glimmer-dsl-libui 0.1.2 → 0.1.6

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 (103) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +55 -0
  3. data/README.md +1127 -65
  4. data/VERSION +1 -1
  5. data/examples/area_gallery.rb +50 -0
  6. data/examples/area_gallery2.rb +111 -0
  7. data/examples/area_gallery3.rb +52 -0
  8. data/examples/area_gallery4.rb +113 -0
  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_table_progress_bar.rb +13 -3
  13. data/examples/basic_transform.rb +27 -0
  14. data/examples/control_gallery.rb +3 -3
  15. data/examples/dynamic_area.rb +1 -1
  16. data/examples/form.rb +2 -2
  17. data/examples/histogram.rb +118 -0
  18. data/examples/meta_example.rb +71 -20
  19. data/examples/midi_player.rb +8 -10
  20. data/glimmer-dsl-libui.gemspec +0 -0
  21. data/lib/glimmer/dsl/libui/control_expression.rb +1 -3
  22. data/lib/glimmer/dsl/libui/dsl.rb +1 -0
  23. data/lib/glimmer/dsl/libui/file_expression.rb +5 -1
  24. data/lib/glimmer/dsl/libui/property_expression.rb +5 -1
  25. data/lib/glimmer/{libui/box.rb → dsl/libui/shape_expression.rb} +26 -29
  26. data/lib/glimmer/dsl/libui/tab_item_expression.rb +2 -2
  27. data/lib/glimmer/libui/control_proxy/area_proxy.rb +117 -0
  28. data/lib/glimmer/libui/{menu_item_proxy.rb → control_proxy/box/horizontal_box_proxy.rb} +9 -12
  29. data/lib/glimmer/libui/{vertical_box_proxy.rb → control_proxy/box/vertical_box_proxy.rb} +10 -6
  30. data/lib/glimmer/libui/control_proxy/box.rb +61 -0
  31. data/lib/glimmer/libui/{button_proxy.rb → control_proxy/button_proxy.rb} +14 -12
  32. data/lib/glimmer/libui/{checkbox_proxy.rb → control_proxy/checkbox_proxy.rb} +14 -12
  33. data/lib/glimmer/libui/control_proxy/color_button_proxy.rb +118 -0
  34. data/lib/glimmer/libui/control_proxy/column/button_column_proxy.rb +76 -0
  35. data/lib/glimmer/libui/control_proxy/column/checkbox_column_proxy.rb +46 -0
  36. data/lib/glimmer/libui/control_proxy/column/checkbox_text_column_proxy.rb +80 -0
  37. data/lib/glimmer/libui/{radio_buttons_proxy.rb → control_proxy/column/image_column_proxy.rb} +14 -13
  38. data/lib/glimmer/libui/control_proxy/column/image_text_column_proxy.rb +48 -0
  39. data/lib/glimmer/libui/{progress_bar_column_proxy.rb → control_proxy/column/progress_bar_column_proxy.rb} +14 -10
  40. data/lib/glimmer/libui/control_proxy/column/text_column_proxy.rb +46 -0
  41. data/lib/glimmer/libui/control_proxy/column.rb +55 -0
  42. data/lib/glimmer/libui/{checkbox_column_proxy.rb → control_proxy/combobox_proxy.rb} +16 -13
  43. data/lib/glimmer/libui/control_proxy/date_time_picker_proxy/date_picker_proxy.rb +43 -0
  44. data/lib/glimmer/libui/control_proxy/date_time_picker_proxy/time_picker_proxy.rb +43 -0
  45. data/lib/glimmer/libui/control_proxy/date_time_picker_proxy.rb +72 -0
  46. data/lib/glimmer/libui/control_proxy/dual_column.rb +40 -0
  47. data/lib/glimmer/libui/control_proxy/editable_column.rb +46 -0
  48. data/lib/glimmer/libui/control_proxy/editable_combobox_proxy.rb +45 -0
  49. data/lib/glimmer/libui/{dual_column.rb → control_proxy/enableable_column.rb} +18 -10
  50. data/lib/glimmer/libui/control_proxy/font_button_proxy.rb +70 -0
  51. data/lib/glimmer/libui/{form_proxy.rb → control_proxy/form_proxy.rb} +26 -24
  52. data/lib/glimmer/libui/{grid_proxy.rb → control_proxy/grid_proxy.rb} +29 -27
  53. data/lib/glimmer/libui/{group_proxy.rb → control_proxy/group_proxy.rb} +24 -22
  54. data/lib/glimmer/libui/{image_part_proxy.rb → control_proxy/image_part_proxy.rb} +20 -18
  55. data/lib/glimmer/libui/{image_proxy.rb → control_proxy/image_proxy.rb} +32 -30
  56. data/lib/glimmer/libui/{combobox_proxy.rb → control_proxy/label_proxy.rb} +13 -13
  57. data/lib/glimmer/libui/control_proxy/matrix_proxy.rb +147 -0
  58. data/lib/glimmer/libui/{editable_column.rb → control_proxy/menu_item_proxy/about_menu_item_proxy.rb} +13 -16
  59. data/lib/glimmer/libui/control_proxy/menu_item_proxy/check_menu_item_proxy.rb +41 -0
  60. data/lib/glimmer/libui/{preferences_menu_item_proxy.rb → control_proxy/menu_item_proxy/preferences_menu_item_proxy.rb} +11 -9
  61. data/lib/glimmer/libui/control_proxy/menu_item_proxy/quit_menu_item_proxy.rb +66 -0
  62. data/lib/glimmer/libui/control_proxy/menu_item_proxy/separator_menu_item_proxy.rb +41 -0
  63. data/lib/glimmer/libui/control_proxy/menu_item_proxy.rb +45 -0
  64. data/lib/glimmer/libui/{editable_combobox_proxy.rb → control_proxy/menu_proxy.rb} +13 -13
  65. data/lib/glimmer/libui/{horizontal_box_proxy.rb → control_proxy/message_box/msg_box_error_proxy.rb} +9 -6
  66. data/lib/glimmer/libui/{multiline_entry_proxy.rb → control_proxy/message_box/msg_box_proxy.rb} +8 -6
  67. data/lib/glimmer/libui/{check_menu_item_proxy.rb → control_proxy/message_box.rb} +11 -10
  68. data/lib/glimmer/libui/control_proxy/multiline_entry_proxy/non_wrapping_multiline_entry_proxy.rb +34 -0
  69. data/lib/glimmer/libui/{image_column_proxy.rb → control_proxy/multiline_entry_proxy.rb} +10 -11
  70. data/lib/glimmer/libui/control_proxy/path_proxy.rb +169 -0
  71. data/lib/glimmer/libui/control_proxy/radio_buttons_proxy.rb +45 -0
  72. data/lib/glimmer/libui/{button_column_proxy.rb → control_proxy/tab_item_proxy.rb} +37 -40
  73. data/lib/glimmer/libui/control_proxy/table_proxy.rb +182 -0
  74. data/lib/glimmer/libui/control_proxy/transformable.rb +74 -0
  75. data/lib/glimmer/libui/control_proxy/window_proxy.rb +128 -0
  76. data/lib/glimmer/libui/control_proxy.rb +41 -22
  77. data/lib/glimmer/libui/{enableable_column.rb → parent.rb} +7 -15
  78. data/lib/glimmer/libui/shape/arc.rb +43 -0
  79. data/lib/glimmer/libui/{date_picker_proxy.rb → shape/bezier.rb} +8 -9
  80. data/lib/glimmer/libui/{quit_menu_item_proxy.rb → shape/figure.rb} +24 -32
  81. data/lib/glimmer/libui/{non_wrapping_multiline_entry_proxy.rb → shape/line.rb} +11 -5
  82. data/lib/glimmer/libui/{time_picker_proxy.rb → shape/rectangle.rb} +8 -9
  83. data/lib/glimmer/libui/{about_menu_item_proxy.rb → shape/square.rb} +9 -8
  84. data/lib/glimmer/libui/shape.rb +145 -0
  85. data/lib/glimmer/libui.rb +92 -0
  86. data/lib/glimmer-dsl-libui.rb +2 -0
  87. metadata +82 -48
  88. data/lib/glimmer/libui/area_proxy.rb +0 -105
  89. data/lib/glimmer/libui/checkbox_text_column_proxy.rb +0 -76
  90. data/lib/glimmer/libui/color_button_proxy.rb +0 -64
  91. data/lib/glimmer/libui/column.rb +0 -51
  92. data/lib/glimmer/libui/date_time_picker_proxy.rb +0 -68
  93. data/lib/glimmer/libui/font_button_proxy.rb +0 -68
  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/path_proxy.rb +0 -167
  98. data/lib/glimmer/libui/rectangle_proxy.rb +0 -100
  99. data/lib/glimmer/libui/separator_menu_item_proxy.rb +0 -37
  100. data/lib/glimmer/libui/tab_item_proxy.rb +0 -67
  101. data/lib/glimmer/libui/table_proxy.rb +0 -180
  102. data/lib/glimmer/libui/text_column_proxy.rb +0 -42
  103. data/lib/glimmer/libui/window_proxy.rb +0 -119
@@ -0,0 +1,169 @@
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
+ require 'glimmer/libui/parent'
25
+ require 'glimmer/libui/control_proxy/transformable'
26
+
27
+ module Glimmer
28
+ module LibUI
29
+ class ControlProxy
30
+ # Proxy for LibUI path objects
31
+ #
32
+ # Follows the Proxy Design Pattern
33
+ class PathProxy < ControlProxy
34
+ include Parent
35
+ prepend Transformable
36
+
37
+ # TODO support mode without parent proxy
38
+ def initialize(keyword, parent, args, &block)
39
+ @keyword = keyword
40
+ @parent_proxy = parent
41
+ @args = args
42
+ @block = block
43
+ @enabled = true
44
+ post_add_content if @block.nil?
45
+ end
46
+
47
+ def post_add_content
48
+ super
49
+ if @parent_proxy.nil? && AreaProxy.current_area_draw_params
50
+ draw(AreaProxy.current_area_draw_params)
51
+ destroy
52
+ end
53
+ end
54
+
55
+ def draw(area_draw_params)
56
+ build_control
57
+ children.dup.each {|child| child.draw(area_draw_params)}
58
+ ::LibUI.draw_path_end(@libui)
59
+ ::LibUI.draw_fill(area_draw_params[:context], @libui, fill_draw_brush.to_ptr) unless fill.empty?
60
+ ::LibUI.draw_stroke(area_draw_params[:context], @libui, stroke_draw_brush, draw_stroke_params) unless stroke.empty?
61
+ ::LibUI.draw_free_path(@libui)
62
+ end
63
+
64
+ def draw_fill_mode
65
+ @args[0].is_a?(Integer) ? @args[0] : @args[0].to_s == 'alternate' ? 1 : 0
66
+ end
67
+
68
+ def fill(*args)
69
+ args = args.first if args.size == 1 && (args.first.is_a?(Array) || args.first.is_a?(Hash) || args.first.is_a?(String) || args.first.is_a?(Symbol))
70
+ if args.empty?
71
+ @fill ||= {}
72
+ else
73
+ @fill = Glimmer::LibUI.interpret_color(args)
74
+ @fill[:a] = 1.0 if @fill.is_a?(Hash) && @fill[:a].nil?
75
+ @parent_proxy&.queue_redraw_all
76
+ end
77
+ @fill.tap do
78
+ @fill_observer ||= Glimmer::DataBinding::Observer.proc do
79
+ @parent_proxy&.queue_redraw_all
80
+ end
81
+ @fill_observer.observe(@fill)
82
+ end
83
+ end
84
+ alias fill= fill
85
+ alias set_fill fill
86
+
87
+ def fill_draw_brush
88
+ @fill_draw_brush ||= ::LibUI::FFI::DrawBrush.malloc
89
+ init_draw_brush(@fill_draw_brush, @fill)
90
+ @fill_draw_brush
91
+ end
92
+
93
+ def stroke(*args)
94
+ args = args.first if args.size == 1 && (args.first.is_a?(Array) || args.first.is_a?(Hash) || args.first.is_a?(String) || args.first.is_a?(Symbol))
95
+ if args.empty?
96
+ @stroke ||= {}
97
+ else
98
+ @stroke = Glimmer::LibUI.interpret_color(args)
99
+ @stroke[:a] = 1.0 if @stroke.is_a?(Hash) && @stroke[:a].nil?
100
+ @parent_proxy&.queue_redraw_all
101
+ end
102
+ @stroke.tap do
103
+ @stroke_observer ||= Glimmer::DataBinding::Observer.proc do
104
+ @parent_proxy&.queue_redraw_all
105
+ end
106
+ @stroke_observer.observe(@stroke)
107
+ end
108
+ end
109
+ alias stroke= stroke
110
+ alias set_stroke stroke
111
+
112
+ def stroke_draw_brush
113
+ @stroke_draw_brush ||= ::LibUI::FFI::DrawBrush.malloc
114
+ init_draw_brush(@stroke_draw_brush, @stroke)
115
+ @stroke_draw_brush
116
+ end
117
+
118
+ def draw_stroke_params
119
+ @draw_stroke_params ||= ::LibUI::FFI::DrawStrokeParams.malloc
120
+ @draw_stroke_params.Cap = @stroke[:cap] || 0 # flat
121
+ @draw_stroke_params.Join = @stroke[:join] || 0 # miter
122
+ @draw_stroke_params.Thickness = @stroke[:thickness] || 1
123
+ @draw_stroke_params.MiterLimit = @stroke[:miter_limit] || 10 # DEFAULT_MITER_LIMIT
124
+ @draw_stroke_params_dashes ||= Fiddle::Pointer.malloc(8)
125
+ @draw_stroke_params.Dashes = @draw_stroke_params_dashes
126
+ @draw_stroke_params.NumDashes = @stroke[:num_dashes] || 0 # TODO reimplement this line correctly (perhaps no need to pass num dashes, yet dashes themselves and use their count here)
127
+ @draw_stroke_params.DashPhase = @stroke[:dash_phase] || 0
128
+ @draw_stroke_params
129
+ end
130
+
131
+ def destroy
132
+ @parent_proxy&.children&.delete(self)
133
+ ControlProxy.control_proxies.delete(self)
134
+ end
135
+
136
+ def redraw
137
+ @parent_proxy&.queue_redraw_all
138
+ end
139
+
140
+ private
141
+
142
+ def build_control
143
+ @libui = ::LibUI.draw_new_path(draw_fill_mode)
144
+ end
145
+
146
+ def init_draw_brush(draw_brush, draw_brush_args)
147
+ case draw_brush_args[:type]
148
+ when Integer
149
+ draw_brush.Type = draw_brush_args[:type]
150
+ when :solid, 'solid'
151
+ draw_brush.Type = 0
152
+ when :linear_gradient, 'linear_gradient'
153
+ draw_brush.Type = 1
154
+ when :radial_gradient, 'radial_gradient'
155
+ draw_brush.Type = 2
156
+ when :image, 'image'
157
+ draw_brush.Type = 3
158
+ else
159
+ draw_brush.Type = 0
160
+ end
161
+ draw_brush.R = (draw_brush_args[:r] || draw_brush_args[:red]).to_f / 255.0
162
+ draw_brush.G = (draw_brush_args[:g] || draw_brush_args[:green]).to_f / 255.0
163
+ draw_brush.B = (draw_brush_args[:b] || draw_brush_args[:blue]).to_f / 255.0
164
+ draw_brush.A = (draw_brush_args[:a] || draw_brush_args[:alpha])
165
+ end
166
+ end
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,45 @@
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
+
24
+ module Glimmer
25
+ module LibUI
26
+ class ControlProxy
27
+ # Proxy for LibUI radio buttons objects
28
+ #
29
+ # Follows the Proxy Design Pattern
30
+ class RadioButtonsProxy < ControlProxy
31
+ def items(*values)
32
+ values = values.first if values.first.is_a?(Array)
33
+ if values.empty?
34
+ @values
35
+ else
36
+ @values = values
37
+ @values.each { |value| append value }
38
+ end
39
+ end
40
+ alias set_items items
41
+ alias items= items
42
+ end
43
+ end
44
+ end
45
+ end
@@ -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