glimmer-dsl-libui 0.4.12 → 0.4.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +26 -0
- data/README.md +111 -48
- data/VERSION +1 -1
- data/examples/basic_image.rb +5 -3
- data/examples/basic_image2.rb +1 -3
- data/examples/basic_image3.rb +3 -3
- data/examples/basic_image4.rb +0 -3
- data/examples/basic_image5.rb +0 -2
- data/examples/basic_table_button.rb +0 -1
- data/examples/cpu_percentage.rb +1 -1
- data/examples/editable_column_table.rb +5 -0
- data/examples/form_table.rb +10 -4
- data/examples/form_table2.rb +12 -6
- data/examples/form_table3.rb +9 -3
- data/examples/form_table4.rb +9 -3
- data/examples/form_table5.rb +9 -3
- data/examples/meta_example.rb +3 -1
- data/glimmer-dsl-libui.gemspec +0 -0
- data/icons/blank.png +0 -0
- data/lib/glimmer/libui/attributed_string.rb +17 -8
- data/lib/glimmer/libui/control_proxy/area_proxy.rb +17 -17
- data/lib/glimmer/libui/control_proxy/box.rb +1 -0
- data/lib/glimmer/libui/control_proxy/column/button_column_proxy.rb +1 -29
- data/lib/glimmer/libui/control_proxy/form_proxy.rb +1 -0
- data/lib/glimmer/libui/control_proxy/image_proxy.rb +92 -10
- data/lib/glimmer/libui/control_proxy/menu_item_proxy/quit_menu_item_proxy.rb +18 -8
- data/lib/glimmer/libui/control_proxy/open_type_features_proxy.rb +11 -2
- data/lib/glimmer/libui/control_proxy/open_type_tag_proxy.rb +2 -0
- data/lib/glimmer/libui/control_proxy/path_proxy.rb +2 -0
- data/lib/glimmer/libui/control_proxy/table_proxy.rb +13 -11
- data/lib/glimmer/libui/control_proxy/text_proxy.rb +2 -0
- data/lib/glimmer/libui/control_proxy/window_proxy.rb +34 -35
- data/lib/glimmer/libui/control_proxy.rb +45 -9
- data/lib/glimmer/libui/shape.rb +1 -0
- data/lib/glimmer/libui.rb +1 -0
- metadata +5 -4
    
        data/examples/form_table.rb
    CHANGED
    
    | @@ -18,7 +18,7 @@ class FormTable | |
| 18 18 | 
             
              end
         | 
| 19 19 |  | 
| 20 20 | 
             
              def launch
         | 
| 21 | 
            -
                window('Contacts', 600, 600) { | 
| 21 | 
            +
                window('Contacts', 600, 600) {
         | 
| 22 22 | 
             
                  margined true
         | 
| 23 23 |  | 
| 24 24 | 
             
                  vertical_box {
         | 
| @@ -56,8 +56,8 @@ class FormTable | |
| 56 56 |  | 
| 57 57 | 
             
                      on_clicked do
         | 
| 58 58 | 
             
                        new_row = [name, email, phone, city, state]
         | 
| 59 | 
            -
                        if new_row.include?('')
         | 
| 60 | 
            -
                          msg_box_error( | 
| 59 | 
            +
                        if new_row.map(&:to_s).include?('')
         | 
| 60 | 
            +
                          msg_box_error('Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
         | 
| 61 61 | 
             
                        else
         | 
| 62 62 | 
             
                          @contacts << Contact.new(*new_row) # automatically inserts a row into the table due to explicit data-binding
         | 
| 63 63 | 
             
                          @unfiltered_contacts = @contacts.dup
         | 
| @@ -98,10 +98,16 @@ class FormTable | |
| 98 98 | 
             
                      text_column('State')
         | 
| 99 99 |  | 
| 100 100 | 
             
                      editable true
         | 
| 101 | 
            -
                      cell_rows <=> [self, :contacts] # explicit data-binding to Model Array
         | 
| 101 | 
            +
                      cell_rows <=> [self, :contacts] # explicit data-binding to self.contacts Model Array, auto-inferring model attribute names from underscored table column names by convention
         | 
| 102 102 |  | 
| 103 103 | 
             
                      on_changed do |row, type, row_data|
         | 
| 104 104 | 
             
                        puts "Row #{row} #{type}: #{row_data}"
         | 
| 105 | 
            +
                        $stdout.flush # for Windows
         | 
| 106 | 
            +
                      end
         | 
| 107 | 
            +
                  
         | 
| 108 | 
            +
                      on_edited do |row, row_data| # only fires on direct table editing
         | 
| 109 | 
            +
                        puts "Row #{row} edited: #{row_data}"
         | 
| 110 | 
            +
                        $stdout.flush # for Windows
         | 
| 105 111 | 
             
                      end
         | 
| 106 112 | 
             
                    }
         | 
| 107 113 | 
             
                  }
         | 
    
        data/examples/form_table2.rb
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            require 'glimmer-dsl-libui'
         | 
| 2 2 |  | 
| 3 3 | 
             
            class FormTable
         | 
| 4 | 
            -
              Contact = Struct.new(:name, :email, :phone, :city, : | 
| 4 | 
            +
              Contact = Struct.new(:name, :email, :phone, :city, :state_province)
         | 
| 5 5 |  | 
| 6 6 | 
             
              include Glimmer
         | 
| 7 7 |  | 
| @@ -18,7 +18,7 @@ class FormTable | |
| 18 18 | 
             
              end
         | 
| 19 19 |  | 
| 20 20 | 
             
              def launch
         | 
| 21 | 
            -
                window('Contacts', 600, 600) { | 
| 21 | 
            +
                window('Contacts', 600, 600) {
         | 
| 22 22 | 
             
                  margined true
         | 
| 23 23 |  | 
| 24 24 | 
             
                  vertical_box {
         | 
| @@ -56,8 +56,8 @@ class FormTable | |
| 56 56 |  | 
| 57 57 | 
             
                      on_clicked do
         | 
| 58 58 | 
             
                        new_row = [name, email, phone, city, state]
         | 
| 59 | 
            -
                        if new_row.include?('')
         | 
| 60 | 
            -
                          msg_box_error( | 
| 59 | 
            +
                        if new_row.map(&:to_s).include?('')
         | 
| 60 | 
            +
                          msg_box_error('Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
         | 
| 61 61 | 
             
                        else
         | 
| 62 62 | 
             
                          @contacts << Contact.new(*new_row) # automatically inserts a row into the table due to implicit data-binding
         | 
| 63 63 | 
             
                          @unfiltered_contacts = @contacts.dup
         | 
| @@ -95,13 +95,19 @@ class FormTable | |
| 95 95 | 
             
                      text_column('Email')
         | 
| 96 96 | 
             
                      text_column('Phone')
         | 
| 97 97 | 
             
                      text_column('City')
         | 
| 98 | 
            -
                      text_column('State | 
| 98 | 
            +
                      text_column('State')
         | 
| 99 99 |  | 
| 100 100 | 
             
                      editable true
         | 
| 101 | 
            -
                      cell_rows <=> [self, :contacts, column_attributes: {'State | 
| 101 | 
            +
                      cell_rows <=> [self, :contacts, column_attributes: {'State' => :state_province}] # explicit data-binding to Model Array with column_attributes mapping for a specific column
         | 
| 102 102 |  | 
| 103 103 | 
             
                      on_changed do |row, type, row_data|
         | 
| 104 104 | 
             
                        puts "Row #{row} #{type}: #{row_data}"
         | 
| 105 | 
            +
                        $stdout.flush # for Windows
         | 
| 106 | 
            +
                      end
         | 
| 107 | 
            +
                      
         | 
| 108 | 
            +
                      on_edited do |row, row_data| # only fires on direct table editing
         | 
| 109 | 
            +
                        puts "Row #{row} edited: #{row_data}"
         | 
| 110 | 
            +
                        $stdout.flush # for Windows
         | 
| 105 111 | 
             
                      end
         | 
| 106 112 | 
             
                    }
         | 
| 107 113 | 
             
                  }
         | 
    
        data/examples/form_table3.rb
    CHANGED
    
    | @@ -19,7 +19,7 @@ class FormTable | |
| 19 19 | 
             
              end
         | 
| 20 20 |  | 
| 21 21 | 
             
              def launch
         | 
| 22 | 
            -
                window('Contacts', 600, 600) { | 
| 22 | 
            +
                window('Contacts', 600, 600) {
         | 
| 23 23 | 
             
                  margined true
         | 
| 24 24 |  | 
| 25 25 | 
             
                  vertical_box {
         | 
| @@ -57,8 +57,8 @@ class FormTable | |
| 57 57 |  | 
| 58 58 | 
             
                      on_clicked do
         | 
| 59 59 | 
             
                        new_row = [name, email, phone, city, state]
         | 
| 60 | 
            -
                        if new_row.include?('')
         | 
| 61 | 
            -
                          msg_box_error( | 
| 60 | 
            +
                        if new_row.map(&:to_s).include?('')
         | 
| 61 | 
            +
                          msg_box_error('Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
         | 
| 62 62 | 
             
                        else
         | 
| 63 63 | 
             
                          @contacts << Contact.new(*new_row) # automatically inserts a row into the table due to implicit data-binding
         | 
| 64 64 | 
             
                          @unfiltered_contacts = @contacts.dup
         | 
| @@ -103,6 +103,12 @@ class FormTable | |
| 103 103 |  | 
| 104 104 | 
             
                      on_changed do |row, type, row_data|
         | 
| 105 105 | 
             
                        puts "Row #{row} #{type}: #{row_data}"
         | 
| 106 | 
            +
                        $stdout.flush # for Windows
         | 
| 107 | 
            +
                      end
         | 
| 108 | 
            +
                      
         | 
| 109 | 
            +
                      on_edited do |row, row_data| # only fires on direct table editing
         | 
| 110 | 
            +
                        puts "Row #{row} edited: #{row_data}"
         | 
| 111 | 
            +
                        $stdout.flush # for Windows
         | 
| 106 112 | 
             
                      end
         | 
| 107 113 | 
             
                    }
         | 
| 108 114 | 
             
                  }
         | 
    
        data/examples/form_table4.rb
    CHANGED
    
    | @@ -16,7 +16,7 @@ class FormTable | |
| 16 16 | 
             
              end
         | 
| 17 17 |  | 
| 18 18 | 
             
              def launch
         | 
| 19 | 
            -
                window('Contacts', 600, 600) { | 
| 19 | 
            +
                window('Contacts', 600, 600) {
         | 
| 20 20 | 
             
                  margined true
         | 
| 21 21 |  | 
| 22 22 | 
             
                  vertical_box {
         | 
| @@ -54,8 +54,8 @@ class FormTable | |
| 54 54 |  | 
| 55 55 | 
             
                      on_clicked do
         | 
| 56 56 | 
             
                        new_row = [name, email, phone, city, state]
         | 
| 57 | 
            -
                        if new_row.include?('')
         | 
| 58 | 
            -
                          msg_box_error( | 
| 57 | 
            +
                        if new_row.map(&:to_s).include?('')
         | 
| 58 | 
            +
                          msg_box_error('Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
         | 
| 59 59 | 
             
                        else
         | 
| 60 60 | 
             
                          data << new_row # automatically inserts a row into the table due to implicit data-binding
         | 
| 61 61 | 
             
                          @unfiltered_data = data.dup
         | 
| @@ -100,6 +100,12 @@ class FormTable | |
| 100 100 |  | 
| 101 101 | 
             
                      on_changed do |row, type, row_data|
         | 
| 102 102 | 
             
                        puts "Row #{row} #{type}: #{row_data}"
         | 
| 103 | 
            +
                        $stdout.flush # for Windows
         | 
| 104 | 
            +
                      end
         | 
| 105 | 
            +
                      
         | 
| 106 | 
            +
                      on_edited do |row, row_data| # only fires on direct table editing
         | 
| 107 | 
            +
                        puts "Row #{row} edited: #{row_data}"
         | 
| 108 | 
            +
                        $stdout.flush # for Windows
         | 
| 103 109 | 
             
                      end
         | 
| 104 110 | 
             
                    }
         | 
| 105 111 | 
             
                  }
         | 
    
        data/examples/form_table5.rb
    CHANGED
    
    | @@ -10,7 +10,7 @@ data = [ | |
| 10 10 | 
             
              ['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'],
         | 
| 11 11 | 
             
            ]
         | 
| 12 12 |  | 
| 13 | 
            -
            window('Contacts', 600, 600) { | 
| 13 | 
            +
            window('Contacts', 600, 600) {
         | 
| 14 14 | 
             
              margined true
         | 
| 15 15 |  | 
| 16 16 | 
             
              vertical_box {
         | 
| @@ -43,8 +43,8 @@ window('Contacts', 600, 600) { |w| | |
| 43 43 |  | 
| 44 44 | 
             
                  on_clicked do
         | 
| 45 45 | 
             
                    new_row = [@name_entry.text, @email_entry.text, @phone_entry.text, @city_entry.text, @state_entry.text]
         | 
| 46 | 
            -
                    if new_row.include?('')
         | 
| 47 | 
            -
                      msg_box_error( | 
| 46 | 
            +
                    if new_row.map(&:to_s).include?('')
         | 
| 47 | 
            +
                      msg_box_error('Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
         | 
| 48 48 | 
             
                    else
         | 
| 49 49 | 
             
                      data << new_row # automatically inserts a row into the table due to implicit data-binding
         | 
| 50 50 | 
             
                      @unfiltered_data = data.dup
         | 
| @@ -88,6 +88,12 @@ window('Contacts', 600, 600) { |w| | |
| 88 88 |  | 
| 89 89 | 
             
                  on_changed do |row, type, row_data|
         | 
| 90 90 | 
             
                    puts "Row #{row} #{type}: #{row_data}"
         | 
| 91 | 
            +
                    $stdout.flush # for Windows
         | 
| 92 | 
            +
                  end
         | 
| 93 | 
            +
                  
         | 
| 94 | 
            +
                  on_edited do |row, row_data| # only fires on direct table editing
         | 
| 95 | 
            +
                    puts "Row #{row} edited: #{row_data}"
         | 
| 96 | 
            +
                    $stdout.flush # for Windows
         | 
| 91 97 | 
             
                  end
         | 
| 92 98 | 
             
                }
         | 
| 93 99 | 
             
              }
         | 
    
        data/examples/meta_example.rb
    CHANGED
    
    | @@ -180,7 +180,9 @@ class MetaExample | |
| 180 180 | 
             
                        }
         | 
| 181 181 | 
             
                        button('Reset') {
         | 
| 182 182 | 
             
                          on_clicked do
         | 
| 183 | 
            -
                             | 
| 183 | 
            +
                            version_number = @version_spinbox.value == 1 ? '' : @version_spinbox.value
         | 
| 184 | 
            +
                            example = "#{selected_example}#{version_number}"
         | 
| 185 | 
            +
                            self.code_text = File.read(file_path_for(example))
         | 
| 184 186 | 
             
                          end
         | 
| 185 187 | 
             
                        }
         | 
| 186 188 | 
             
                      }
         | 
    
        data/glimmer-dsl-libui.gemspec
    CHANGED
    
    | Binary file | 
    
        data/icons/blank.png
    ADDED
    
    | Binary file | 
| @@ -22,7 +22,6 @@ | |
| 22 22 | 
             
            require 'glimmer/libui/control_proxy'
         | 
| 23 23 | 
             
            require 'glimmer/libui/control_proxy/area_proxy'
         | 
| 24 24 | 
             
            require 'glimmer/libui/parent'
         | 
| 25 | 
            -
            require 'glimmer/libui/control_proxy/transformable'
         | 
| 26 25 | 
             
            require 'glimmer/libui/data_bindable'
         | 
| 27 26 |  | 
| 28 27 | 
             
            module Glimmer
         | 
| @@ -48,7 +47,7 @@ module Glimmer | |
| 48 47 | 
             
                      @string
         | 
| 49 48 | 
             
                    else
         | 
| 50 49 | 
             
                      @string = value
         | 
| 51 | 
            -
                       | 
| 50 | 
            +
                      request_auto_redraw
         | 
| 52 51 | 
             
                    end
         | 
| 53 52 | 
             
                  end
         | 
| 54 53 | 
             
                  alias string= string
         | 
| @@ -59,7 +58,7 @@ module Glimmer | |
| 59 58 | 
             
                      @font
         | 
| 60 59 | 
             
                    else
         | 
| 61 60 | 
             
                      @font = value
         | 
| 62 | 
            -
                       | 
| 61 | 
            +
                      request_auto_redraw
         | 
| 63 62 | 
             
                    end
         | 
| 64 63 | 
             
                  end
         | 
| 65 64 | 
             
                  alias font= font
         | 
| @@ -70,7 +69,7 @@ module Glimmer | |
| 70 69 | 
             
                      @color
         | 
| 71 70 | 
             
                    else
         | 
| 72 71 | 
             
                      @color = Glimmer::LibUI.interpret_color(value)
         | 
| 73 | 
            -
                       | 
| 72 | 
            +
                      request_auto_redraw
         | 
| 74 73 | 
             
                    end
         | 
| 75 74 | 
             
                  end
         | 
| 76 75 | 
             
                  alias color= color
         | 
| @@ -81,7 +80,7 @@ module Glimmer | |
| 81 80 | 
             
                      @background
         | 
| 82 81 | 
             
                    else
         | 
| 83 82 | 
             
                      @background = Glimmer::LibUI.interpret_color(value)
         | 
| 84 | 
            -
                       | 
| 83 | 
            +
                      request_auto_redraw
         | 
| 85 84 | 
             
                    end
         | 
| 86 85 | 
             
                  end
         | 
| 87 86 | 
             
                  alias background= background
         | 
| @@ -92,7 +91,7 @@ module Glimmer | |
| 92 91 | 
             
                      @underline
         | 
| 93 92 | 
             
                    else
         | 
| 94 93 | 
             
                      @underline = value
         | 
| 95 | 
            -
                       | 
| 94 | 
            +
                      request_auto_redraw
         | 
| 96 95 | 
             
                    end
         | 
| 97 96 | 
             
                  end
         | 
| 98 97 | 
             
                  alias underline= underline
         | 
| @@ -103,7 +102,7 @@ module Glimmer | |
| 103 102 | 
             
                      @underline_color
         | 
| 104 103 | 
             
                    else
         | 
| 105 104 | 
             
                      @underline_color = value
         | 
| 106 | 
            -
                       | 
| 105 | 
            +
                      request_auto_redraw
         | 
| 107 106 | 
             
                    end
         | 
| 108 107 | 
             
                  end
         | 
| 109 108 | 
             
                  alias underline_color= underline_color
         | 
| @@ -114,12 +113,21 @@ module Glimmer | |
| 114 113 | 
             
                      @open_type_features
         | 
| 115 114 | 
             
                    else
         | 
| 116 115 | 
             
                      @open_type_features = value
         | 
| 117 | 
            -
                       | 
| 116 | 
            +
                      request_auto_redraw
         | 
| 118 117 | 
             
                    end
         | 
| 119 118 | 
             
                  end
         | 
| 120 119 | 
             
                  alias open_type_features= open_type_features
         | 
| 121 120 | 
             
                  alias set_open_type_features open_type_features
         | 
| 122 121 |  | 
| 122 | 
            +
                  def remove_open_type_features
         | 
| 123 | 
            +
                    return if @removing_open_type_features
         | 
| 124 | 
            +
                    @removing_open_type_features = true
         | 
| 125 | 
            +
                    @open_type_features&.destroy
         | 
| 126 | 
            +
                    @open_type_features = nil
         | 
| 127 | 
            +
                    request_auto_redraw
         | 
| 128 | 
            +
                    @removing_open_type_features = false
         | 
| 129 | 
            +
                  end
         | 
| 130 | 
            +
                  
         | 
| 123 131 | 
             
                  def post_add_content(block = nil)
         | 
| 124 132 | 
             
                    block ||= @block
         | 
| 125 133 | 
             
                    block_result = block&.call
         | 
| @@ -189,6 +197,7 @@ module Glimmer | |
| 189 197 | 
             
                  end
         | 
| 190 198 |  | 
| 191 199 | 
             
                  def destroy
         | 
| 200 | 
            +
                    return if ControlProxy.main_window_proxy&.destroying?
         | 
| 192 201 | 
             
                    open_type_features.destroy unless open_type_features.nil?
         | 
| 193 202 | 
             
                    @parent_proxy&.children&.delete(self)
         | 
| 194 203 | 
             
                  end
         | 
| @@ -36,9 +36,9 @@ module Glimmer | |
| 36 36 | 
             
                      attr_accessor :current_area_draw_params
         | 
| 37 37 | 
             
                    end
         | 
| 38 38 |  | 
| 39 | 
            -
                     | 
| 39 | 
            +
                    CUSTOM_LISTENER_NAMES = ['on_draw', 'on_mouse_event', 'on_mouse_move', 'on_mouse_down', 'on_mouse_up', 'on_mouse_drag_start', 'on_mouse_drag', 'on_mouse_drop', 'on_mouse_crossed', 'on_mouse_enter', 'on_mouse_exit', 'on_drag_broken', 'on_key_event', 'on_key_down', 'on_key_up']
         | 
| 40 40 |  | 
| 41 | 
            -
                     | 
| 41 | 
            +
                    CUSTOM_LISTENER_NAME_ALIASES = {
         | 
| 42 42 | 
             
                      on_drawn: 'on_draw',
         | 
| 43 43 | 
             
                      on_mouse_moved: 'on_mouse_move',
         | 
| 44 44 | 
             
                      on_mouse_drag_started: 'on_mouse_drag_start',
         | 
| @@ -105,7 +105,7 @@ module Glimmer | |
| 105 105 |  | 
| 106 106 | 
             
                    def draw(area_draw_params)
         | 
| 107 107 | 
             
                      children.dup.each {|child| child.draw(area_draw_params)}
         | 
| 108 | 
            -
                      on_draw | 
| 108 | 
            +
                      notify_custom_listeners('on_draw', area_draw_params)
         | 
| 109 109 | 
             
                    end
         | 
| 110 110 |  | 
| 111 111 | 
             
                    def redraw
         | 
| @@ -156,37 +156,37 @@ module Glimmer | |
| 156 156 | 
             
                        @area_handler.MouseEvent   = fiddle_closure_block_caller(0, [1, 1, 1]) do |_, _, area_mouse_event|
         | 
| 157 157 | 
             
                          area_mouse_event = ::LibUI::FFI::AreaMouseEvent.new(area_mouse_event)
         | 
| 158 158 | 
             
                          area_mouse_event = area_mouse_event_hash(area_mouse_event)
         | 
| 159 | 
            -
                          on_mouse_event | 
| 160 | 
            -
                          on_mouse_move | 
| 159 | 
            +
                          notify_custom_listeners('on_mouse_event', area_mouse_event)
         | 
| 160 | 
            +
                          notify_custom_listeners('on_mouse_move', area_mouse_event) if area_mouse_event[:x].between?(0, area_mouse_event[:area_width]) && area_mouse_event[:y].between?(0, area_mouse_event[:area_height])
         | 
| 161 161 | 
             
                          unless @last_area_mouse_event.nil?
         | 
| 162 | 
            -
                            on_mouse_down | 
| 163 | 
            -
                            on_mouse_up | 
| 164 | 
            -
                            on_mouse_drag_start | 
| 165 | 
            -
                            on_mouse_drag | 
| 166 | 
            -
                            on_mouse_drop | 
| 162 | 
            +
                            notify_custom_listeners('on_mouse_down', area_mouse_event) if area_mouse_event[:down] > 0 && @last_area_mouse_event[:down] == 0
         | 
| 163 | 
            +
                            notify_custom_listeners('on_mouse_up', area_mouse_event) if area_mouse_event[:up] > 0 && @last_area_mouse_event[:up] == 0
         | 
| 164 | 
            +
                            notify_custom_listeners('on_mouse_drag_start', area_mouse_event) if area_mouse_event[:held] > 0 && @last_area_mouse_event[:held] == 0
         | 
| 165 | 
            +
                            notify_custom_listeners('on_mouse_drag', area_mouse_event) if area_mouse_event[:held] > 0
         | 
| 166 | 
            +
                            notify_custom_listeners('on_mouse_drop', area_mouse_event) if area_mouse_event[:held] == 0 && @last_area_mouse_event[:held] > 0
         | 
| 167 167 | 
             
                          end
         | 
| 168 168 | 
             
                          @last_area_mouse_event = area_mouse_event
         | 
| 169 169 | 
             
                        end
         | 
| 170 170 | 
             
                        @area_handler.MouseCrossed = fiddle_closure_block_caller(0, [1, 1, 4]) do |_, _, left|
         | 
| 171 171 | 
             
                          left = Glimmer::LibUI.integer_to_boolean(left)
         | 
| 172 | 
            -
                          on_mouse_crossed | 
| 172 | 
            +
                          notify_custom_listeners('on_mouse_crossed', left)
         | 
| 173 173 | 
             
                          if left
         | 
| 174 | 
            -
                            on_mouse_exit | 
| 174 | 
            +
                            notify_custom_listeners('on_mouse_exit', left)
         | 
| 175 175 | 
             
                          else
         | 
| 176 | 
            -
                            on_mouse_enter | 
| 176 | 
            +
                            notify_custom_listeners('on_mouse_enter', left)
         | 
| 177 177 | 
             
                          end
         | 
| 178 178 | 
             
                        end
         | 
| 179 179 | 
             
                        @area_handler.DragBroken   = fiddle_closure_block_caller(0, [1, 1]) do |_, _|
         | 
| 180 | 
            -
                          on_drag_broken | 
| 180 | 
            +
                          notify_custom_listeners('on_drag_broken')
         | 
| 181 181 | 
             
                        end
         | 
| 182 182 | 
             
                        @area_handler.KeyEvent     = fiddle_closure_block_caller(0, [1, 1, 1]) do |_, _, area_key_event|
         | 
| 183 183 | 
             
                          area_key_event = ::LibUI::FFI::AreaKeyEvent.new(area_key_event)
         | 
| 184 184 | 
             
                          area_key_event = area_key_event_hash(area_key_event)
         | 
| 185 | 
            -
                          on_key_event | 
| 185 | 
            +
                          notify_custom_listeners('on_key_event', area_key_event)
         | 
| 186 186 | 
             
                          if area_key_event[:up]
         | 
| 187 | 
            -
                            on_key_up | 
| 187 | 
            +
                            notify_custom_listeners('on_key_up', area_key_event)
         | 
| 188 188 | 
             
                          else
         | 
| 189 | 
            -
                            on_key_down | 
| 189 | 
            +
                            notify_custom_listeners('on_key_down', area_key_event)
         | 
| 190 190 | 
             
                          end
         | 
| 191 191 | 
             
                        end
         | 
| 192 192 | 
             
                        @listeners_installed = true
         | 
| @@ -34,36 +34,8 @@ module Glimmer | |
| 34 34 | 
             
                      include Column
         | 
| 35 35 | 
             
                      include EnableableColumn
         | 
| 36 36 |  | 
| 37 | 
            -
                       | 
| 38 | 
            -
                        # TODO consider generalizing into custom listeners and moving to ControlProxy
         | 
| 39 | 
            -
                        @on_clicked_procs ||= []
         | 
| 40 | 
            -
                        if block.nil?
         | 
| 41 | 
            -
                          @on_clicked_procs
         | 
| 42 | 
            -
                        else
         | 
| 43 | 
            -
                          @on_clicked_procs << block
         | 
| 44 | 
            -
                          block
         | 
| 45 | 
            -
                        end
         | 
| 46 | 
            -
                      end
         | 
| 47 | 
            -
                      
         | 
| 48 | 
            -
                      def can_handle_listener?(listener_name)
         | 
| 49 | 
            -
                        listener_name == 'on_clicked' || super
         | 
| 50 | 
            -
                      end
         | 
| 37 | 
            +
                      CUSTOM_LISTENER_NAMES = ['on_clicked']
         | 
| 51 38 |  | 
| 52 | 
            -
                      def handle_listener(listener_name, &listener)
         | 
| 53 | 
            -
                        case listener_name
         | 
| 54 | 
            -
                        when 'on_clicked'
         | 
| 55 | 
            -
                          on_clicked(&listener)
         | 
| 56 | 
            -
                        else
         | 
| 57 | 
            -
                          super
         | 
| 58 | 
            -
                        end
         | 
| 59 | 
            -
                      end
         | 
| 60 | 
            -
                      
         | 
| 61 | 
            -
                      def notify_listeners(listener_name, *args)
         | 
| 62 | 
            -
                        @on_clicked_procs&.each do |on_clicked_proc|
         | 
| 63 | 
            -
                          on_clicked_proc.call(*args)
         | 
| 64 | 
            -
                        end
         | 
| 65 | 
            -
                      end
         | 
| 66 | 
            -
                          
         | 
| 67 39 | 
             
                      private
         | 
| 68 40 |  | 
| 69 41 | 
             
                      def build_control
         | 
| @@ -34,14 +34,31 @@ module Glimmer | |
| 34 34 | 
             
                  #
         | 
| 35 35 | 
             
                  # Follows the Proxy Design Pattern
         | 
| 36 36 | 
             
                  class ImageProxy < ControlProxy
         | 
| 37 | 
            +
                    class << self
         | 
| 38 | 
            +
                      # creates or returns existing instance for passed in arguments if parent is nil and block is nil
         | 
| 39 | 
            +
                      def create(keyword, parent, args, &block)
         | 
| 40 | 
            +
                        if parent.nil? && block.nil?
         | 
| 41 | 
            +
                          instances[args] ||= new(keyword, parent, args.dup, &block)
         | 
| 42 | 
            +
                        else
         | 
| 43 | 
            +
                          new(keyword, parent, args, &block)
         | 
| 44 | 
            +
                        end
         | 
| 45 | 
            +
                      end
         | 
| 46 | 
            +
                      
         | 
| 47 | 
            +
                      def instances
         | 
| 48 | 
            +
                        @@instances = {} unless defined? @@instances
         | 
| 49 | 
            +
                        @@instances
         | 
| 50 | 
            +
                      end
         | 
| 51 | 
            +
                    end
         | 
| 52 | 
            +
                    
         | 
| 37 53 | 
             
                    include Parent
         | 
| 38 54 | 
             
                    prepend Transformable
         | 
| 39 55 |  | 
| 40 | 
            -
                    attr_reader :data, :pixels, :shapes
         | 
| 56 | 
            +
                    attr_reader :data, :pixels, :shapes, :options
         | 
| 41 57 |  | 
| 42 58 | 
             
                    def initialize(keyword, parent, args, &block)
         | 
| 43 59 | 
             
                      @keyword = keyword
         | 
| 44 60 | 
             
                      @parent_proxy = parent
         | 
| 61 | 
            +
                      @options = args.last.is_a?(Hash) ? args.pop : {}
         | 
| 45 62 | 
             
                      @args = args
         | 
| 46 63 | 
             
                      @block = block
         | 
| 47 64 | 
             
                      @enabled = true
         | 
| @@ -76,11 +93,47 @@ module Glimmer | |
| 76 93 | 
             
                    alias file= file
         | 
| 77 94 | 
             
                    alias set_file file
         | 
| 78 95 |  | 
| 96 | 
            +
                    def x(value = nil)
         | 
| 97 | 
            +
                      if value.nil?
         | 
| 98 | 
            +
                        @args.size > 3 ? @args[1] : (@options[:x] || 0)
         | 
| 99 | 
            +
                      else
         | 
| 100 | 
            +
                        if @args.size > 3
         | 
| 101 | 
            +
                          @args[1] = value
         | 
| 102 | 
            +
                        else
         | 
| 103 | 
            +
                          @options[:x] = value
         | 
| 104 | 
            +
                        end
         | 
| 105 | 
            +
                        if area_image? && @content_added
         | 
| 106 | 
            +
                          post_add_content
         | 
| 107 | 
            +
                          request_auto_redraw
         | 
| 108 | 
            +
                        end
         | 
| 109 | 
            +
                      end
         | 
| 110 | 
            +
                    end
         | 
| 111 | 
            +
                    alias x= x
         | 
| 112 | 
            +
                    alias set_x x
         | 
| 113 | 
            +
                  
         | 
| 114 | 
            +
                    def y(value = nil)
         | 
| 115 | 
            +
                      if value.nil?
         | 
| 116 | 
            +
                        @args.size > 3 ? @args[2] : (@options[:y] || 0)
         | 
| 117 | 
            +
                      else
         | 
| 118 | 
            +
                        if @args.size > 3
         | 
| 119 | 
            +
                          @args[2] = value
         | 
| 120 | 
            +
                        else
         | 
| 121 | 
            +
                          @options[:y] = value
         | 
| 122 | 
            +
                        end
         | 
| 123 | 
            +
                        if area_image? && @content_added
         | 
| 124 | 
            +
                          post_add_content
         | 
| 125 | 
            +
                          request_auto_redraw
         | 
| 126 | 
            +
                        end
         | 
| 127 | 
            +
                      end
         | 
| 128 | 
            +
                    end
         | 
| 129 | 
            +
                    alias y= y
         | 
| 130 | 
            +
                    alias set_y y
         | 
| 131 | 
            +
                    
         | 
| 79 132 | 
             
                    def width(value = nil)
         | 
| 80 133 | 
             
                      if value.nil?
         | 
| 81 | 
            -
                        @args[1]
         | 
| 134 | 
            +
                        @args.size > 3 ? @args[3] : (@options[:width] || @args[1])
         | 
| 82 135 | 
             
                      else
         | 
| 83 | 
            -
                         | 
| 136 | 
            +
                        set_width_variable(value)
         | 
| 84 137 | 
             
                        if area_image? && @content_added
         | 
| 85 138 | 
             
                          post_add_content
         | 
| 86 139 | 
             
                          request_auto_redraw
         | 
| @@ -92,9 +145,9 @@ module Glimmer | |
| 92 145 |  | 
| 93 146 | 
             
                    def height(value = nil)
         | 
| 94 147 | 
             
                      if value.nil?
         | 
| 95 | 
            -
                        @args[2]
         | 
| 148 | 
            +
                        @args.size > 3 ? @args[4] : (@options[:height] || @args[2])
         | 
| 96 149 | 
             
                      else
         | 
| 97 | 
            -
                         | 
| 150 | 
            +
                        set_height_variable(value)
         | 
| 98 151 | 
             
                        if area_image? && @content_added
         | 
| 99 152 | 
             
                          post_add_content
         | 
| 100 153 | 
             
                          request_auto_redraw
         | 
| @@ -126,12 +179,34 @@ module Glimmer | |
| 126 179 | 
             
                    end
         | 
| 127 180 |  | 
| 128 181 | 
             
                    def destroy
         | 
| 182 | 
            +
                      return if ControlProxy.main_window_proxy&.destroying?
         | 
| 183 | 
            +
                      deregister_all_custom_listeners
         | 
| 129 184 | 
             
                      @parent_proxy&.children&.delete(self)
         | 
| 130 185 | 
             
                      ControlProxy.control_proxies.delete(self)
         | 
| 131 186 | 
             
                    end
         | 
| 132 187 |  | 
| 133 188 | 
             
                    private
         | 
| 134 189 |  | 
| 190 | 
            +
                    def set_width_variable(value)
         | 
| 191 | 
            +
                      if @args.size > 3
         | 
| 192 | 
            +
                        @args[3] = value
         | 
| 193 | 
            +
                      elsif @options[:width]
         | 
| 194 | 
            +
                        @options[:width] = value
         | 
| 195 | 
            +
                      else
         | 
| 196 | 
            +
                        @args[1] = value
         | 
| 197 | 
            +
                      end
         | 
| 198 | 
            +
                    end
         | 
| 199 | 
            +
                    
         | 
| 200 | 
            +
                    def set_height_variable(value)
         | 
| 201 | 
            +
                      if @args.size > 3
         | 
| 202 | 
            +
                        @args[4] = value
         | 
| 203 | 
            +
                      elsif @options[:height]
         | 
| 204 | 
            +
                        @options[:height] = value
         | 
| 205 | 
            +
                      else
         | 
| 206 | 
            +
                        @args[2] = value
         | 
| 207 | 
            +
                      end
         | 
| 208 | 
            +
                    end
         | 
| 209 | 
            +
                    
         | 
| 135 210 | 
             
                    def build_control
         | 
| 136 211 | 
             
                      unless area_image? # image object
         | 
| 137 212 | 
             
                        if file
         | 
| @@ -164,10 +239,15 @@ module Glimmer | |
| 164 239 | 
             
                        canvas = ChunkyPNG::Canvas.from_io(f)
         | 
| 165 240 | 
             
                        f.close
         | 
| 166 241 | 
             
                      end
         | 
| 167 | 
            -
                      canvas. | 
| 242 | 
            +
                      original_width = canvas.width
         | 
| 243 | 
            +
                      original_height = canvas.height
         | 
| 244 | 
            +
                      require 'bigdecimal'
         | 
| 245 | 
            +
                      calculated_width = ((BigDecimal(height)/BigDecimal(original_height))*original_width).to_i if height && !width
         | 
| 246 | 
            +
                      calculated_height = ((BigDecimal(width)/BigDecimal(original_width))*original_height).to_i if width && !height
         | 
| 247 | 
            +
                      canvas.resample_nearest_neighbor!(calculated_width || width, calculated_height || height) if width || height
         | 
| 168 248 | 
             
                      @data = canvas.to_rgba_stream
         | 
| 169 | 
            -
                       | 
| 170 | 
            -
                       | 
| 249 | 
            +
                      set_width_variable(canvas.width) unless width
         | 
| 250 | 
            +
                      set_height_variable(canvas.height) unless height
         | 
| 171 251 | 
             
                      [@data, width, height]
         | 
| 172 252 | 
             
                    end
         | 
| 173 253 |  | 
| @@ -187,6 +267,8 @@ module Glimmer | |
| 187 267 | 
             
                      @shapes = []
         | 
| 188 268 | 
             
                      original_pixels = @pixels.dup
         | 
| 189 269 | 
             
                      indexed_original_pixels = Hash[original_pixels.each_with_index.to_a]
         | 
| 270 | 
            +
                      x_offset = x
         | 
| 271 | 
            +
                      y_offset = y
         | 
| 190 272 | 
             
                      @pixels.each do |pixel|
         | 
| 191 273 | 
             
                        index = indexed_original_pixels[pixel]
         | 
| 192 274 | 
             
                        @rectangle_start_x ||= pixel[:x]
         | 
| @@ -195,9 +277,9 @@ module Glimmer | |
| 195 277 | 
             
                          @rectangle_width += 1
         | 
| 196 278 | 
             
                        else
         | 
| 197 279 | 
             
                          if pixel[:x] > 0 && pixel[:color] == original_pixels[index - 1][:color]
         | 
| 198 | 
            -
                            @shapes << {x: @rectangle_start_x, y: pixel[:y], width: @rectangle_width, height: 1, color: pixel[:color]}
         | 
| 280 | 
            +
                            @shapes << {x: x_offset + @rectangle_start_x, y: y_offset + pixel[:y], width: @rectangle_width, height: 1, color: pixel[:color]}
         | 
| 199 281 | 
             
                          else
         | 
| 200 | 
            -
                            @shapes << {x: pixel[:x], y: pixel[:y], width: 1, height: 1, color: pixel[:color]}
         | 
| 282 | 
            +
                            @shapes << {x: x_offset + pixel[:x], y: y_offset + pixel[:y], width: 1, height: 1, color: pixel[:color]}
         | 
| 201 283 | 
             
                          end
         | 
| 202 284 | 
             
                          @rectangle_width = 1
         | 
| 203 285 | 
             
                          @rectangle_start_x = pixel[:x] == width - 1 ? 0 : pixel[:x] + 1
         | 
| @@ -35,29 +35,39 @@ module Glimmer | |
| 35 35 |  | 
| 36 36 | 
             
                      def handle_listener(listener_name, &listener)
         | 
| 37 37 | 
             
                        if listener_name == 'on_clicked'
         | 
| 38 | 
            -
                          @ | 
| 39 | 
            -
             | 
| 38 | 
            +
                          @on_clicked_listeners ||= []
         | 
| 39 | 
            +
                          @on_clicked_listeners << listener
         | 
| 40 | 
            +
                          @default_behavior_listener ||= Proc.new do
         | 
| 41 | 
            +
                            return_value = nil
         | 
| 42 | 
            +
                            @on_clicked_listeners.each do |l|
         | 
| 43 | 
            +
                              return_value = l.call(self)
         | 
| 44 | 
            +
                              break if return_value.is_a?(Numeric)
         | 
| 45 | 
            +
                            end
         | 
| 40 46 | 
             
                            if return_value.is_a?(Numeric)
         | 
| 41 47 | 
             
                              return_value
         | 
| 42 48 | 
             
                            else
         | 
| 43 49 | 
             
                              destroy
         | 
| 50 | 
            +
                              ControlProxy.main_window_proxy&.destroy
         | 
| 44 51 | 
             
                              ::LibUI.quit
         | 
| 45 52 | 
             
                              0
         | 
| 46 53 | 
             
                            end
         | 
| 54 | 
            +
                          end.tap do |default_behavior_listener|
         | 
| 55 | 
            +
                            ::LibUI.on_should_quit(&default_behavior_listener)
         | 
| 47 56 | 
             
                          end
         | 
| 48 | 
            -
                          ::LibUI.on_should_quit(&@default_behavior_listener)
         | 
| 49 57 | 
             
                        end
         | 
| 50 58 | 
             
                      end
         | 
| 59 | 
            +
                      
         | 
| 60 | 
            +
                      def destroy
         | 
| 61 | 
            +
                        @on_clicked_listeners&.clear
         | 
| 62 | 
            +
                        super
         | 
| 63 | 
            +
                      end
         | 
| 51 64 |  | 
| 52 65 | 
             
                      private
         | 
| 53 66 |  | 
| 54 67 | 
             
                      def build_control
         | 
| 55 68 | 
             
                        @libui = @parent_proxy.append_quit_item(*@args)
         | 
| 56 | 
            -
                         | 
| 57 | 
            -
             | 
| 58 | 
            -
                          ::LibUI.quit
         | 
| 59 | 
            -
                          0
         | 
| 60 | 
            -
                        end
         | 
| 69 | 
            +
                        # setup default on_clicked listener if no on_clicked listeners are setup
         | 
| 70 | 
            +
                        handle_listener('on_clicked') {} if @on_clicked_listeners.nil? || @on_clicked_listeners.empty?
         | 
| 61 71 | 
             
                      end
         | 
| 62 72 | 
             
                    end
         | 
| 63 73 | 
             
                  end
         | 
| @@ -33,15 +33,24 @@ module Glimmer | |
| 33 33 | 
             
                    include Parent
         | 
| 34 34 |  | 
| 35 35 | 
             
                    def destroy
         | 
| 36 | 
            +
                      return if ControlProxy.main_window_proxy&.destroying?
         | 
| 37 | 
            +
                      return if @destroying
         | 
| 38 | 
            +
                      @destroying = true
         | 
| 39 | 
            +
                      deregister_all_custom_listeners
         | 
| 36 40 | 
             
                      ::LibUI.free_open_type_features(@libui)
         | 
| 37 | 
            -
                      @parent_proxy&. | 
| 41 | 
            +
                      @parent_proxy&.remove_open_type_features
         | 
| 38 42 | 
             
                      ControlProxy.control_proxies.delete(self)
         | 
| 43 | 
            +
                      @destroying = false
         | 
| 39 44 | 
             
                    end
         | 
| 40 45 |  | 
| 41 46 | 
             
                    def redraw
         | 
| 42 | 
            -
                      @parent_proxy | 
| 47 | 
            +
                      @parent_proxy&.redraw
         | 
| 43 48 | 
             
                    end
         | 
| 44 49 |  | 
| 50 | 
            +
                    def request_auto_redraw
         | 
| 51 | 
            +
                      @parent_proxy&.request_auto_redraw
         | 
| 52 | 
            +
                    end
         | 
| 53 | 
            +
                                    
         | 
| 45 54 | 
             
                    private
         | 
| 46 55 |  | 
| 47 56 | 
             
                    def build_control
         |