glimmer-dsl-libui 0.1.4 → 0.1.8

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.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +55 -0
  3. data/README.md +939 -255
  4. data/VERSION +1 -1
  5. data/examples/area_gallery.rb +72 -38
  6. data/examples/area_gallery2.rb +126 -92
  7. data/examples/area_gallery3.rb +74 -40
  8. data/examples/area_gallery4.rb +132 -98
  9. data/examples/basic_area2.rb +1 -1
  10. data/examples/basic_button.rb +2 -2
  11. data/examples/basic_entry.rb +2 -2
  12. data/examples/basic_transform.rb +27 -0
  13. data/examples/control_gallery.rb +3 -3
  14. data/examples/dynamic_area.rb +1 -1
  15. data/examples/form.rb +2 -2
  16. data/examples/histogram.rb +118 -0
  17. data/examples/meta_example.rb +71 -20
  18. data/examples/midi_player.rb +8 -10
  19. data/glimmer-dsl-libui.gemspec +0 -0
  20. data/lib/glimmer/dsl/libui/control_expression.rb +0 -2
  21. data/lib/glimmer/dsl/libui/file_expression.rb +5 -1
  22. data/lib/glimmer/dsl/libui/shape_expression.rb +2 -4
  23. data/lib/glimmer/dsl/libui/tab_item_expression.rb +2 -2
  24. data/lib/glimmer/libui/control_proxy/area_proxy.rb +166 -0
  25. data/lib/glimmer/libui/{menu_item_proxy.rb → control_proxy/box/horizontal_box_proxy.rb} +9 -12
  26. data/lib/glimmer/libui/{horizontal_box_proxy.rb → control_proxy/box/vertical_box_proxy.rb} +10 -6
  27. data/lib/glimmer/libui/{box.rb → control_proxy/box.rb} +31 -27
  28. data/lib/glimmer/libui/{button_proxy.rb → control_proxy/button_proxy.rb} +14 -12
  29. data/lib/glimmer/libui/{checkbox_proxy.rb → control_proxy/checkbox_proxy.rb} +14 -12
  30. data/lib/glimmer/libui/control_proxy/color_button_proxy.rb +118 -0
  31. data/lib/glimmer/libui/control_proxy/column/button_column_proxy.rb +76 -0
  32. data/lib/glimmer/libui/control_proxy/column/checkbox_column_proxy.rb +46 -0
  33. data/lib/glimmer/libui/control_proxy/column/checkbox_text_column_proxy.rb +80 -0
  34. data/lib/glimmer/libui/{radio_buttons_proxy.rb → control_proxy/column/image_column_proxy.rb} +14 -13
  35. data/lib/glimmer/libui/control_proxy/column/image_text_column_proxy.rb +48 -0
  36. data/lib/glimmer/libui/control_proxy/column/progress_bar_column_proxy.rb +44 -0
  37. data/lib/glimmer/libui/{checkbox_column_proxy.rb → control_proxy/column/text_column_proxy.rb} +17 -13
  38. data/lib/glimmer/libui/control_proxy/column.rb +55 -0
  39. data/lib/glimmer/libui/control_proxy/combobox_proxy.rb +45 -0
  40. data/lib/glimmer/libui/control_proxy/date_time_picker_proxy/date_picker_proxy.rb +43 -0
  41. data/lib/glimmer/libui/control_proxy/date_time_picker_proxy/time_picker_proxy.rb +43 -0
  42. data/lib/glimmer/libui/control_proxy/date_time_picker_proxy.rb +72 -0
  43. data/lib/glimmer/libui/control_proxy/dual_column.rb +40 -0
  44. data/lib/glimmer/libui/control_proxy/editable_column.rb +46 -0
  45. data/lib/glimmer/libui/control_proxy/editable_combobox_proxy.rb +45 -0
  46. data/lib/glimmer/libui/{dual_column.rb → control_proxy/enableable_column.rb} +18 -10
  47. data/lib/glimmer/libui/control_proxy/font_button_proxy.rb +70 -0
  48. data/lib/glimmer/libui/{form_proxy.rb → control_proxy/form_proxy.rb} +26 -24
  49. data/lib/glimmer/libui/{grid_proxy.rb → control_proxy/grid_proxy.rb} +29 -27
  50. data/lib/glimmer/libui/{group_proxy.rb → control_proxy/group_proxy.rb} +24 -22
  51. data/lib/glimmer/libui/{image_part_proxy.rb → control_proxy/image_part_proxy.rb} +20 -18
  52. data/lib/glimmer/libui/{image_proxy.rb → control_proxy/image_proxy.rb} +32 -30
  53. data/lib/glimmer/libui/{combobox_proxy.rb → control_proxy/label_proxy.rb} +13 -13
  54. data/lib/glimmer/libui/control_proxy/matrix_proxy.rb +147 -0
  55. data/lib/glimmer/libui/{editable_column.rb → control_proxy/menu_item_proxy/about_menu_item_proxy.rb} +13 -16
  56. data/lib/glimmer/libui/control_proxy/menu_item_proxy/check_menu_item_proxy.rb +41 -0
  57. data/lib/glimmer/libui/{date_picker_proxy.rb → control_proxy/menu_item_proxy/preferences_menu_item_proxy.rb} +10 -10
  58. data/lib/glimmer/libui/{quit_menu_item_proxy.rb → control_proxy/menu_item_proxy/quit_menu_item_proxy.rb} +33 -29
  59. data/lib/glimmer/libui/control_proxy/menu_item_proxy/separator_menu_item_proxy.rb +41 -0
  60. data/lib/glimmer/libui/control_proxy/menu_item_proxy.rb +45 -0
  61. data/lib/glimmer/libui/{editable_combobox_proxy.rb → control_proxy/menu_proxy.rb} +13 -13
  62. data/lib/glimmer/libui/{vertical_box_proxy.rb → control_proxy/message_box/msg_box_error_proxy.rb} +9 -6
  63. data/lib/glimmer/libui/{multiline_entry_proxy.rb → control_proxy/message_box/msg_box_proxy.rb} +8 -6
  64. data/lib/glimmer/libui/{time_picker_proxy.rb → control_proxy/message_box.rb} +9 -10
  65. data/lib/glimmer/libui/{rectangle.rb → control_proxy/multiline_entry_proxy/non_wrapping_multiline_entry_proxy.rb} +6 -8
  66. data/lib/glimmer/libui/{progress_bar_column_proxy.rb → control_proxy/multiline_entry_proxy.rb} +10 -11
  67. data/lib/glimmer/libui/control_proxy/path_proxy.rb +190 -0
  68. data/lib/glimmer/libui/control_proxy/radio_buttons_proxy.rb +45 -0
  69. data/lib/glimmer/libui/{button_column_proxy.rb → control_proxy/tab_item_proxy.rb} +37 -40
  70. data/lib/glimmer/libui/control_proxy/table_proxy.rb +182 -0
  71. data/lib/glimmer/libui/control_proxy/transformable.rb +74 -0
  72. data/lib/glimmer/libui/control_proxy/window_proxy.rb +128 -0
  73. data/lib/glimmer/libui/control_proxy.rb +72 -21
  74. data/lib/glimmer/libui/{enableable_column.rb → parent.rb} +7 -15
  75. data/lib/glimmer/libui/{arc.rb → shape/arc.rb} +13 -11
  76. data/lib/glimmer/libui/shape/bezier.rb +38 -0
  77. data/lib/glimmer/libui/{figure.rb → shape/figure.rb} +23 -21
  78. data/lib/glimmer/libui/{line.rb → shape/line.rb} +9 -7
  79. data/lib/glimmer/libui/{bezier.rb → shape/rectangle.rb} +9 -7
  80. data/lib/glimmer/libui/{square.rb → shape/square.rb} +9 -7
  81. data/lib/glimmer/libui/shape.rb +16 -15
  82. data/lib/glimmer/libui.rb +92 -0
  83. data/lib/glimmer-dsl-libui.rb +3 -0
  84. metadata +76 -53
  85. data/lib/glimmer/libui/about_menu_item_proxy.rb +0 -37
  86. data/lib/glimmer/libui/area_proxy.rb +0 -105
  87. data/lib/glimmer/libui/check_menu_item_proxy.rb +0 -37
  88. data/lib/glimmer/libui/checkbox_text_column_proxy.rb +0 -76
  89. data/lib/glimmer/libui/color_button_proxy.rb +0 -64
  90. data/lib/glimmer/libui/column.rb +0 -51
  91. data/lib/glimmer/libui/date_time_picker_proxy.rb +0 -68
  92. data/lib/glimmer/libui/font_button_proxy.rb +0 -68
  93. data/lib/glimmer/libui/image_column_proxy.rb +0 -40
  94. data/lib/glimmer/libui/image_text_column_proxy.rb +0 -44
  95. data/lib/glimmer/libui/label_proxy.rb +0 -41
  96. data/lib/glimmer/libui/menu_proxy.rb +0 -41
  97. data/lib/glimmer/libui/non_wrapping_multiline_entry_proxy.rb +0 -32
  98. data/lib/glimmer/libui/path_proxy.rb +0 -169
  99. data/lib/glimmer/libui/preferences_menu_item_proxy.rb +0 -37
  100. data/lib/glimmer/libui/separator_menu_item_proxy.rb +0 -37
  101. data/lib/glimmer/libui/tab_item_proxy.rb +0 -67
  102. data/lib/glimmer/libui/table_proxy.rb +0 -180
  103. data/lib/glimmer/libui/text_column_proxy.rb +0 -42
  104. data/lib/glimmer/libui/window_proxy.rb +0 -119
@@ -20,52 +20,49 @@
20
20
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
21
 
22
22
  require 'glimmer/libui/control_proxy'
23
- require 'glimmer/libui/column'
24
- require 'glimmer/libui/enableable_column'
23
+ require 'glimmer/libui/control_proxy/box/horizontal_box_proxy'
25
24
 
26
25
  module Glimmer
27
26
  module LibUI
28
- # Proxy for LibUI button column objects
29
- #
30
- # Follows the Proxy Design Pattern
31
- class ButtonColumnProxy < ControlProxy
32
- include Column
33
- include EnableableColumn
34
-
35
- def on_clicked(&block)
36
- # TODO consider generalizing into custom listeners and moving to ControlProxy
37
- @on_clicked_procs ||= []
38
- if block.nil?
39
- @on_clicked_procs
40
- else
41
- @on_clicked_procs << block
42
- block
27
+ class ControlProxy
28
+ # Proxy for LibUI tab item objects
29
+ #
30
+ # Follows the Proxy Design Pattern
31
+ class TabItemProxy < ControlProxy
32
+ attr_reader :index
33
+
34
+ def initialize(keyword, parent, args, &block)
35
+ @keyword = keyword
36
+ @parent_proxy = parent
37
+ @args = args
38
+ @block = block
39
+ @enabled = 1
40
+ @index = @parent_proxy.num_pages
41
+ @content = @block&.call
42
+ build_control
43
43
  end
44
- end
45
-
46
- def can_handle_listener?(listener_name)
47
- listener_name == 'on_clicked' || super
48
- end
49
-
50
- def handle_listener(listener_name, &listener)
51
- case listener_name
52
- when 'on_clicked'
53
- on_clicked(&listener)
54
- else
55
- super
44
+
45
+ def name
46
+ @args.first
56
47
  end
57
- end
58
-
59
- def notify_listeners(listener_name, *args)
60
- @on_clicked_procs&.each do |on_clicked_proc|
61
- on_clicked_proc.call(*args)
48
+
49
+ def margined(value = nil)
50
+ if value.nil?
51
+ @parent_proxy.margined(@index)
52
+ else
53
+ @parent_proxy.margined(@index, value)
54
+ end
55
+ end
56
+ alias set_margined margined
57
+ alias margined= margined
58
+ alias margined? margined
59
+
60
+ private
61
+
62
+ def build_control
63
+ @content = Box::HorizontalBoxProxy.new('horizontal_box', @libui, []) if @content.nil?
64
+ @libui = @parent_proxy.append(name, @content.libui)
62
65
  end
63
- end
64
-
65
- private
66
-
67
- def build_control
68
- @parent_proxy.append_button_column(name, column_index, enabled_value)
69
66
  end
70
67
  end
71
68
  end
@@ -0,0 +1,182 @@
1
+ # Copyright (c) 2021 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require 'glimmer/libui/control_proxy'
23
+ require 'glimmer/libui/control_proxy/dual_column'
24
+ require 'glimmer/data_binding/observer'
25
+ require 'glimmer/fiddle_consumer'
26
+
27
+ using ArrayIncludeMethods
28
+
29
+ module Glimmer
30
+ module LibUI
31
+ class ControlProxy
32
+ # Proxy for LibUI table objects
33
+ #
34
+ # Follows the Proxy Design Pattern
35
+ class TableProxy < ControlProxy
36
+ include Glimmer::FiddleConsumer
37
+
38
+ attr_reader :model_handler, :model, :table_params, :columns
39
+
40
+ def initialize(keyword, parent, args, &block)
41
+ @keyword = keyword
42
+ @parent_proxy = parent
43
+ @args = args
44
+ @block = block
45
+ @enabled = true
46
+ @columns = []
47
+ @cell_rows = []
48
+ window_proxy.on_destroy do
49
+ # the following unless condition is an exceptional condition stumbled upon that fails freeing the table model
50
+ ::LibUI.free_table_model(@model) unless @destroyed && parent_proxy.is_a?(Box)
51
+ end
52
+ end
53
+
54
+ def post_add_content
55
+ build_control
56
+ super
57
+ end
58
+
59
+ def post_initialize_child(child)
60
+ @columns << child
61
+ # add an extra complementary nil column if it is a dual column (i.e. ImageTextColumnProxy or CheckboxTextColumnProxy
62
+ @columns << nil if child.is_a?(DualColumn)
63
+ end
64
+
65
+ def destroy
66
+ super
67
+ @destroyed = true
68
+ end
69
+
70
+ def cell_rows(rows = nil)
71
+ if rows.nil?
72
+ @cell_rows
73
+ else
74
+ @cell_rows = rows
75
+ @cell_rows.tap do
76
+ @last_cell_rows = @cell_rows.clone
77
+ Glimmer::DataBinding::Observer.proc do
78
+ if @cell_rows.size < @last_cell_rows.size && @last_cell_rows.include_all?(*@cell_rows)
79
+ @last_cell_rows.array_diff_indexes(@cell_rows).reverse.each do |row|
80
+ ::LibUI.table_model_row_deleted(model, row)
81
+ end
82
+ elsif @cell_rows.size > @last_cell_rows.size && @cell_rows.include_all?(*@last_cell_rows)
83
+ @cell_rows.array_diff_indexes(@last_cell_rows).each do |row|
84
+ ::LibUI.table_model_row_inserted(model, row)
85
+ end
86
+ else
87
+ @cell_rows.each_with_index do |new_row_data, row|
88
+ ::LibUI.table_model_row_changed(model, row) if new_row_data != @last_cell_rows[row]
89
+ end
90
+ end
91
+ @last_cell_rows = @cell_rows.clone
92
+ end.observe(self, :cell_rows)
93
+ end
94
+ end
95
+ end
96
+ alias cell_rows= cell_rows
97
+ alias set_cell_rows cell_rows
98
+
99
+ def expanded_cell_rows
100
+ cell_rows.map do |row|
101
+ row.flatten(1)
102
+ end
103
+ end
104
+
105
+ def editable(value = nil)
106
+ if value.nil?
107
+ @editable
108
+ else
109
+ @editable = !!value
110
+ end
111
+ end
112
+ alias editable= editable
113
+ alias set_editable editable
114
+ alias editable? editable
115
+
116
+ private
117
+
118
+ def build_control
119
+ @model_handler = ::LibUI::FFI::TableModelHandler.malloc
120
+ @model_handler.NumColumns = fiddle_closure_block_caller(4) { @columns.map {|c| c.is_a?(DualColumn) ? 2 : 1}.sum }
121
+ @model_handler.ColumnType = fiddle_closure_block_caller(4, [1, 1, 4]) do |_, _, column|
122
+ case @columns[column]
123
+ when Column::TextColumnProxy, Column::ButtonColumnProxy, NilClass
124
+ 0
125
+ when Column::ImageColumnProxy, Column::ImageTextColumnProxy
126
+ 1
127
+ when Column::CheckboxColumnProxy, Column::CheckboxTextColumnProxy, Column::ProgressBarColumnProxy
128
+ 2
129
+ end
130
+ end
131
+ @model_handler.NumRows = fiddle_closure_block_caller(4) { cell_rows.count }
132
+ @model_handler.CellValue = fiddle_closure_block_caller(1, [1, 1, 4, 4]) do |_, _, row, column|
133
+ the_cell_rows = expanded_cell_rows
134
+ case @columns[column]
135
+ when Column::TextColumnProxy, Column::ButtonColumnProxy, NilClass
136
+ ::LibUI.new_table_value_string((expanded_cell_rows[row] && expanded_cell_rows[row][column]).to_s)
137
+ when Column::ImageColumnProxy, Column::ImageTextColumnProxy
138
+ ::LibUI.new_table_value_image((expanded_cell_rows[row] && (expanded_cell_rows[row][column].respond_to?(:libui) ? expanded_cell_rows[row][column].libui : expanded_cell_rows[row][column])))
139
+ when Column::CheckboxColumnProxy, Column::CheckboxTextColumnProxy
140
+ ::LibUI.new_table_value_int((expanded_cell_rows[row] && (expanded_cell_rows[row][column] == 1 || expanded_cell_rows[row][column].to_s.strip.downcase == 'true' ? 1 : 0)))
141
+ when Column::ProgressBarColumnProxy
142
+ ::LibUI.new_table_value_int((expanded_cell_rows[row] && (expanded_cell_rows[row][column].to_i)))
143
+ end
144
+ end
145
+ @model_handler.SetCellValue = fiddle_closure_block_caller(0, [1, 1, 4, 4, 1]) do |_, _, row, column, val|
146
+ case @columns[column]
147
+ when Column::TextColumnProxy
148
+ column = @columns[column].index
149
+ @cell_rows[row] ||= []
150
+ @cell_rows[row][column] = ::LibUI.table_value_string(val).to_s
151
+ when NilClass
152
+ column = @columns[column - 1].index
153
+ @cell_rows[row][column][1] = ::LibUI.table_value_string(val).to_s
154
+ when Column::ButtonColumnProxy
155
+ @columns[column].notify_listeners(:on_clicked, row)
156
+ when Column::CheckboxColumnProxy, Column::CheckboxTextColumnProxy
157
+ column = @columns[column].index
158
+ @cell_rows[row] ||= []
159
+ @cell_rows[row][column] = ::LibUI.table_value_int(val).to_i == 1
160
+ end
161
+ end
162
+
163
+ @model = ::LibUI.new_table_model(@model_handler)
164
+
165
+ @table_params = ::LibUI::FFI::TableParams.malloc
166
+ @table_params.Model = @model
167
+ @table_params.RowBackgroundColorModelColumn = -1
168
+
169
+ @libui = ControlProxy.new_control(@keyword, [@table_params])
170
+ @libui.tap do
171
+ @columns.each {|column| column&.send(:build_control) }
172
+ end
173
+ end
174
+
175
+ def next_column_index
176
+ @next_column_index ||= -1
177
+ @next_column_index += 1
178
+ end
179
+ end
180
+ end
181
+ end
182
+ end
@@ -0,0 +1,74 @@
1
+ # Copyright (c) 2021 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require 'glimmer/libui/control_proxy/matrix_proxy'
23
+
24
+ module Glimmer
25
+ module LibUI
26
+ # This is meant to be prepended (not included) because it changes behavior of instance draw method
27
+ # Adds transform property to controls/shapes
28
+ # Automatically applies transform property at the beginning of draw operation and undoes it at the end
29
+ # Stacks up with Parent module (must include Parent beforehand)
30
+ # Expects transformable to implement redraw method
31
+ module Transformable
32
+ def post_initialize_child(child, add_child: true)
33
+ if child.is_a?(ControlProxy::MatrixProxy)
34
+ super(child, add_child: false)
35
+ self.transform = child if child.keyword == 'transform'
36
+ else
37
+ super(child, add_child: add_child)
38
+ end
39
+ end
40
+
41
+ # Returns transform or sets it. Expects transformable to implement redraw method (delegating work to area).
42
+ def transform(matrix = nil)
43
+ if matrix.nil?
44
+ @transform
45
+ else
46
+ @transform = matrix
47
+ redraw
48
+ end
49
+ end
50
+ alias transform= transform
51
+ alias set_transform transform
52
+
53
+ # Apply transform matrix to coordinate system
54
+ def apply_transform(area_draw_params)
55
+ ::LibUI.draw_transform(area_draw_params[:context], @transform.libui) unless @transform.nil?
56
+ end
57
+
58
+ # Inverse of apply_transform (applies inverse transformation to undo initial transformation)
59
+ def undo_transform(area_draw_params)
60
+ unless @transform.nil?
61
+ inverse_transform = @transform.clone
62
+ inverse_transform.invert
63
+ ::LibUI.draw_transform(area_draw_params[:context], inverse_transform.libui)
64
+ end
65
+ end
66
+
67
+ def draw(area_draw_params)
68
+ apply_transform(area_draw_params)
69
+ super(area_draw_params)
70
+ undo_transform(area_draw_params)
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,128 @@
1
+ # Copyright (c) 2021 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require 'glimmer/libui/control_proxy'
23
+ require 'glimmer/libui/control_proxy/area_proxy'
24
+
25
+ module Glimmer
26
+ module LibUI
27
+ class ControlProxy
28
+ # Proxy for LibUI window objects
29
+ #
30
+ # Follows the Proxy Design Pattern
31
+ class WindowProxy < ControlProxy
32
+ DEFAULT_TITLE = ''
33
+ DEFAULT_WIDTH = 190
34
+ DEFAULT_HEIGHT = 150
35
+ DEFAULT_HAS_MENUBAR = 1
36
+
37
+ def post_initialize_child(child)
38
+ if child.is_a?(AreaProxy)
39
+ vertical_box_parent = ControlProxy.create('vertical_box', self, [])
40
+ child.instance_variable_set(:@parent_proxy, vertical_box_parent)
41
+ vertical_box_parent.post_initialize_child(child)
42
+ child = vertical_box_parent
43
+ end
44
+ ::LibUI.window_set_child(@libui, child.libui)
45
+ end
46
+
47
+ def destroy_child(child)
48
+ ::LibUI.send("window_set_child", @libui, nil)
49
+ super
50
+ end
51
+
52
+ def destroy
53
+ super
54
+ ControlProxy.image_proxies.each { |image_proxy| ::LibUI.free_image(image_proxy.libui) }
55
+ @on_destroy_procs&.each { |on_destroy_proc| on_destroy_proc.call(self)}
56
+ end
57
+
58
+ def on_destroy(&block)
59
+ # TODO look into a way to generalize this logic for multiple listeners
60
+ @on_destroy_procs ||= []
61
+ if block.nil?
62
+ @on_destroy_procs
63
+ else
64
+ @on_destroy_procs << block
65
+ block
66
+ end
67
+ end
68
+
69
+ def show
70
+ super
71
+ unless @shown_at_least_once
72
+ @shown_at_least_once = true
73
+ ::LibUI.main
74
+ ::LibUI.quit
75
+ end
76
+ end
77
+
78
+ def can_handle_listener?(listener_name)
79
+ listener_name == 'on_destroy' || super
80
+ end
81
+
82
+ def handle_listener(listener_name, &listener)
83
+ case listener_name
84
+ when 'on_destroy'
85
+ on_destroy(&listener)
86
+ else
87
+ if listener_name == 'on_closing'
88
+ default_behavior_listener = Proc.new do
89
+ return_value = listener.call(self)
90
+ if return_value.is_a?(Numeric)
91
+ return_value
92
+ else
93
+ destroy
94
+ ::LibUI.quit
95
+ 0
96
+ end
97
+ end
98
+ end
99
+ super(listener_name, &default_behavior_listener)
100
+ end
101
+ end
102
+
103
+ private
104
+
105
+ def build_control
106
+ if OS.mac? && ControlProxy.menu_proxies.empty?
107
+ menu_proxy = ControlProxy.create('menu', nil, [''])
108
+ quit_menu_item_proxy = ControlProxy.create('quit_menu_item', menu_proxy, [])
109
+ end
110
+ construction_args = @args.dup
111
+ construction_args[0] = DEFAULT_TITLE if construction_args.size == 0
112
+ construction_args[1] = DEFAULT_WIDTH if construction_args.size == 1
113
+ construction_args[2] = DEFAULT_HEIGHT if construction_args.size == 2
114
+ construction_args[3] = DEFAULT_HAS_MENUBAR if construction_args.size == 3
115
+ construction_args[3] = Glimmer::LibUI.boolean_to_integer(construction_args[3]) if construction_args.size == 4 && (construction_args[3].is_a?(TrueClass) || construction_args[3].is_a?(FalseClass))
116
+ @libui = ControlProxy.new_control(@keyword, construction_args)
117
+ @libui.tap do
118
+ handle_listener('on_closing') do
119
+ destroy
120
+ ::LibUI.quit
121
+ 0
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
@@ -29,7 +29,7 @@ module Glimmer
29
29
  def exists?(keyword)
30
30
  ::LibUI.respond_to?("new_#{keyword}") ||
31
31
  ::LibUI.respond_to?(keyword) ||
32
- Glimmer::LibUI.constants.include?("#{keyword.camelcase(:upper)}Proxy".to_sym)
32
+ descendant_keyword_constant_map.keys.include?(keyword)
33
33
  end
34
34
 
35
35
  def create(keyword, parent, args, &block)
@@ -37,12 +37,8 @@ module Glimmer
37
37
  end
38
38
 
39
39
  def widget_proxy_class(keyword)
40
- begin
41
- class_name = "#{keyword.camelcase(:upper)}Proxy".to_sym
42
- Glimmer::LibUI.const_get(class_name)
43
- rescue
44
- Glimmer::LibUI::ControlProxy
45
- end
40
+ class_name = constant_symbol(keyword)
41
+ descendant_keyword_constant_map[keyword] || ControlProxy
46
42
  end
47
43
 
48
44
  # autosave all controls in this array to avoid garbage collection
@@ -52,15 +48,7 @@ module Glimmer
52
48
  end
53
49
 
54
50
  def main_window_proxy
55
- control_proxies.find {|c| c.is_a?(Glimmer::LibUI::WindowProxy)}
56
- end
57
-
58
- def integer_to_boolean(int, allow_nil: true)
59
- int.nil? ? (allow_nil ? nil : false) : int == 1
60
- end
61
-
62
- def boolean_to_integer(bool, allow_nil: true)
63
- bool.nil? ? (allow_nil ? nil : 0) : (bool ? 1 : 0)
51
+ control_proxies.find {|c| c.is_a?(WindowProxy)}
64
52
  end
65
53
 
66
54
  def menu_proxies
@@ -74,6 +62,35 @@ module Glimmer
74
62
  def new_control(keyword, args)
75
63
  ::LibUI.send("new_#{keyword}", *args)
76
64
  end
65
+
66
+ def constant_symbol(keyword)
67
+ "#{keyword.camelcase(:upper)}Proxy".to_sym
68
+ end
69
+
70
+ def keyword(constant_symbol)
71
+ constant_symbol.to_s.underscore.sub(/_proxy$/, '')
72
+ end
73
+
74
+ def descendant_keyword_constant_map
75
+ @descendant_keyword_constant_map ||= map_descendant_keyword_constants_for(self)
76
+ end
77
+
78
+ def reset_descendant_keyword_constant_map
79
+ @descendant_keyword_constant_map = nil
80
+ descendant_keyword_constant_map
81
+ end
82
+
83
+ def map_descendant_keyword_constants_for(klass, accumulator: {})
84
+ klass.constants.map do |constant_symbol|
85
+ [constant_symbol, klass.const_get(constant_symbol)]
86
+ end.select do |constant_symbol, constant|
87
+ constant.is_a?(Module) && !accumulator.values.include?(constant)
88
+ end.each do |constant_symbol, constant|
89
+ accumulator[keyword(constant_symbol)] = constant
90
+ map_descendant_keyword_constants_for(constant, accumulator: accumulator)
91
+ end
92
+ accumulator
93
+ end
77
94
  end
78
95
 
79
96
  BOOLEAN_PROPERTIES = %w[
@@ -124,7 +141,8 @@ module Glimmer
124
141
 
125
142
  def can_handle_listener?(listener_name)
126
143
  ::LibUI.respond_to?("#{libui_api_keyword}_#{listener_name}") ||
127
- ::LibUI.respond_to?("control_#{listener_name}")
144
+ ::LibUI.respond_to?("control_#{listener_name}") ||
145
+ has_custom_listener?(listener_name)
128
146
  end
129
147
 
130
148
  def handle_listener(listener_name, &listener)
@@ -133,6 +151,34 @@ module Glimmer
133
151
  ::LibUI.send("#{libui_api_keyword}_#{listener_name}", @libui, &safe_listener)
134
152
  elsif ::LibUI.respond_to?("control_#{listener_name}")
135
153
  ::LibUI.send("control_#{listener_name}", @libui, &safe_listener)
154
+ elsif has_custom_listener?(listener_name)
155
+ handle_custom_listener(listener_name, &listener)
156
+ end
157
+ end
158
+
159
+ def has_custom_listener?(listener_name)
160
+ listener_name = listener_name.to_s
161
+ custom_listeners.include?(listener_name) || custom_listener_aliases.stringify_keys.keys.include?(listener_name)
162
+ end
163
+
164
+ def custom_listeners
165
+ self.class.constants.include?(:LISTENERS) ? self.class::LISTENERS : []
166
+ end
167
+
168
+ def custom_listener_aliases
169
+ self.class.constants.include?(:LISTENER_ALIASES) ? self.class::LISTENER_ALIASES : {}
170
+ end
171
+
172
+ def handle_custom_listener(listener_name, &listener)
173
+ listener_name = listener_name.to_s
174
+ listener_name = custom_listener_aliases.stringify_keys[listener_name] || listener_name
175
+ instance_variable_name = "@#{listener_name}_procs"
176
+ instance_variable_set(instance_variable_name, []) if instance_variable_get(instance_variable_name).nil?
177
+ if listener.nil?
178
+ instance_variable_get(instance_variable_name)
179
+ else
180
+ instance_variable_get(instance_variable_name) << listener
181
+ listener
136
182
  end
137
183
  end
138
184
 
@@ -143,6 +189,7 @@ module Glimmer
143
189
  (append_properties.include?(method_name.to_s.sub(/\?$/, '')) && BOOLEAN_PROPERTIES.include?(method_name.to_s.sub(/\?$/, ''))) ||
144
190
  append_properties.include?(method_name.to_s.sub(/=$/, ''))
145
191
  ) ||
192
+ can_handle_listener?(method_name.to_s) ||
146
193
  super(method_name, true)
147
194
  end
148
195
 
@@ -161,6 +208,8 @@ module Glimmer
161
208
  elsif append_properties.include?(method_name.to_s) ||
162
209
  append_properties.include?(method_name.to_s.sub(/(=|\?)$/, ''))
163
210
  append_property(method_name, *args)
211
+ elsif can_handle_listener?(method_name.to_s)
212
+ handle_listener(method_name.to_s, &block)
164
213
  else
165
214
  super
166
215
  end
@@ -173,7 +222,7 @@ module Glimmer
173
222
  handle_string_property(property, handle_boolean_property(property, value))
174
223
  elsif ::LibUI.respond_to?("#{libui_api_keyword}_set_#{method_name.to_s.sub(/=$/, '')}") && !args.empty?
175
224
  property = method_name.to_s.sub(/=$/, '')
176
- args[0] = ControlProxy.boolean_to_integer(args.first) if BOOLEAN_PROPERTIES.include?(property) && (args.first.is_a?(TrueClass) || args.first.is_a?(FalseClass))
225
+ args[0] = Glimmer::LibUI.boolean_to_integer(args.first) if BOOLEAN_PROPERTIES.include?(property) && (args.first.is_a?(TrueClass) || args.first.is_a?(FalseClass))
177
226
  ::LibUI.send("#{libui_api_keyword}_set_#{property}", @libui, *args)
178
227
  elsif ::LibUI.respond_to?("#{libui_api_keyword}_#{method_name}") && !args.empty?
179
228
  ::LibUI.send("#{libui_api_keyword}_#{method_name}", @libui, *args)
@@ -183,7 +232,7 @@ module Glimmer
183
232
  handle_string_property(property, handle_boolean_property(property, value))
184
233
  elsif ::LibUI.respond_to?("control_set_#{method_name.to_s.sub(/=$/, '')}")
185
234
  property = method_name.to_s.sub(/=$/, '')
186
- args[0] = ControlProxy.boolean_to_integer(args.first) if BOOLEAN_PROPERTIES.include?(property) && (args.first.is_a?(TrueClass) || args.first.is_a?(FalseClass))
235
+ args[0] = Glimmer::LibUI.boolean_to_integer(args.first) if BOOLEAN_PROPERTIES.include?(property) && (args.first.is_a?(TrueClass) || args.first.is_a?(FalseClass))
187
236
  ::LibUI.send("control_set_#{method_name.to_s.sub(/=$/, '')}", @libui, *args)
188
237
  elsif ::LibUI.respond_to?("control_#{method_name}") && !args.empty?
189
238
  ::LibUI.send("control_#{method_name}", @libui, *args)
@@ -201,7 +250,7 @@ module Glimmer
201
250
  value = @append_property_hash[property]
202
251
  handle_string_property(property, handle_boolean_property(property, value))
203
252
  else
204
- value = ControlProxy.boolean_to_integer(value) if BOOLEAN_PROPERTIES.include?(property) && (value.is_a?(TrueClass) || value.is_a?(FalseClass))
253
+ value = Glimmer::LibUI.boolean_to_integer(value) if BOOLEAN_PROPERTIES.include?(property) && (value.is_a?(TrueClass) || value.is_a?(FalseClass))
205
254
  @append_property_hash[property] = value
206
255
  end
207
256
  end
@@ -274,7 +323,7 @@ module Glimmer
274
323
  end
275
324
 
276
325
  def handle_boolean_property(property, value)
277
- BOOLEAN_PROPERTIES.include?(property) ? ControlProxy.integer_to_boolean(value) : value
326
+ BOOLEAN_PROPERTIES.include?(property) ? Glimmer::LibUI.integer_to_boolean(value) : value
278
327
  end
279
328
 
280
329
  def handle_string_property(property, value)
@@ -283,3 +332,5 @@ module Glimmer
283
332
  end
284
333
  end
285
334
  end
335
+
336
+ Dir[File.expand_path('./control_proxy/*.rb', __dir__)].each {|f| require f}
@@ -21,23 +21,15 @@
21
21
 
22
22
  module Glimmer
23
23
  module LibUI
24
- module EnableableColumn
25
- def enabled(value = nil)
26
- if value.nil?
27
- @enabled = true if @enabled.nil?
28
- @enabled
29
- else
30
- @enabled = !!value
31
- end
24
+ # Parent controls and shapes who have children and add child post_initialize_child
25
+ module Parent
26
+ # Subclasses can override and must call super (passing add_child: false to cancel adding child to children)
27
+ def post_initialize_child(child, add_child: true)
28
+ children << child if add_child
32
29
  end
33
- alias enabled= enabled
34
- alias set_enabled enabled
35
- alias enabled? enabled
36
-
37
- private
38
30
 
39
- def enabled_value
40
- enabled? ? -2 : -1
31
+ def children
32
+ @children ||= []
41
33
  end
42
34
  end
43
35
  end