glimmer-dsl-libui 0.4.4 → 0.4.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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +36 -1
  3. data/README.md +932 -160
  4. data/VERSION +1 -1
  5. data/examples/button_counter.rb +2 -1
  6. data/examples/color_button.rb +18 -13
  7. data/examples/color_button2.rb +14 -0
  8. data/examples/date_time_picker.rb +19 -14
  9. data/examples/date_time_picker2.rb +20 -0
  10. data/examples/dynamic_area.rb +77 -90
  11. data/examples/dynamic_area2.rb +14 -12
  12. data/examples/dynamic_area3.rb +90 -0
  13. data/examples/dynamic_area4.rb +95 -0
  14. data/examples/font_button.rb +17 -12
  15. data/examples/font_button2.rb +18 -0
  16. data/examples/form_table.rb +0 -2
  17. data/examples/form_table2.rb +0 -2
  18. data/examples/histogram.rb +93 -91
  19. data/examples/histogram2.rb +109 -0
  20. data/examples/meta_example.rb +17 -6
  21. data/examples/midi_player.rb +5 -6
  22. data/examples/midi_player2.rb +83 -0
  23. data/examples/midi_player3.rb +84 -0
  24. data/examples/tetris.rb +15 -18
  25. data/examples/timer.rb +28 -31
  26. data/examples/timer2.rb +129 -0
  27. data/glimmer-dsl-libui.gemspec +0 -0
  28. data/lib/glimmer/libui/control_proxy/checkbox_proxy.rb +5 -0
  29. data/lib/glimmer/libui/control_proxy/color_button_proxy.rb +5 -0
  30. data/lib/glimmer/libui/control_proxy/combobox_proxy.rb +18 -2
  31. data/lib/glimmer/libui/control_proxy/date_time_picker_proxy.rb +5 -0
  32. data/lib/glimmer/libui/control_proxy/editable_combobox_proxy.rb +5 -0
  33. data/lib/glimmer/libui/control_proxy/font_button_proxy.rb +4 -0
  34. data/lib/glimmer/libui/control_proxy/menu_item_proxy/check_menu_item_proxy.rb +5 -0
  35. data/lib/glimmer/libui/control_proxy/menu_item_proxy/radio_menu_item_proxy.rb +17 -4
  36. data/lib/glimmer/libui/control_proxy/radio_buttons_proxy.rb +20 -0
  37. data/lib/glimmer/libui/control_proxy/slider_proxy.rb +38 -0
  38. data/lib/glimmer/libui/control_proxy/spinbox_proxy.rb +38 -0
  39. data/lib/glimmer/libui/control_proxy.rb +2 -2
  40. metadata +13 -2
data/examples/timer.rb CHANGED
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  require 'glimmer-dsl-libui'
4
2
 
5
3
  class Timer
@@ -9,9 +7,12 @@ class Timer
9
7
  MINUTE_MAX = 59
10
8
  HOUR_MAX = 23
11
9
 
10
+ attr_accessor :hour, :min, :sec, :started, :played
11
+
12
12
  def initialize
13
13
  @pid = nil
14
14
  @alarm_file = File.expand_path('../sounds/AlanWalker-Faded.mid', __dir__)
15
+ @hour = @min = @sec = 0
15
16
  at_exit { stop_alarm }
16
17
  setup_timer
17
18
  create_gui
@@ -41,31 +42,29 @@ class Timer
41
42
  unless @setup_timer
42
43
  Glimmer::LibUI.timer(1) do
43
44
  if @started
44
- seconds = @sec_spinbox.value
45
- minutes = @min_spinbox.value
46
- hours = @hour_spinbox.value
45
+ seconds = @sec
46
+ minutes = @min
47
+ hours = @hour
47
48
  if seconds > 0
48
- @sec_spinbox.value = seconds -= 1
49
+ self.sec = seconds -= 1
49
50
  end
50
51
  if seconds == 0
51
52
  if minutes > 0
52
- @min_spinbox.value = minutes -= 1
53
- @sec_spinbox.value = seconds = SECOND_MAX
53
+ self.min = minutes -= 1
54
+ self.sec = seconds = SECOND_MAX
54
55
  end
55
56
  if minutes == 0
56
57
  if hours > 0
57
- @hour_spinbox.value = hours -= 1
58
- @min_spinbox.value = minutes = MINUTE_MAX
59
- @sec_spinbox.value = seconds = SECOND_MAX
58
+ self.hour = hours -= 1
59
+ self.min = minutes = MINUTE_MAX
60
+ self.sec = seconds = SECOND_MAX
60
61
  end
61
62
  if hours == 0 && minutes == 0 && seconds == 0
62
- @start_button.enabled = true
63
- @stop_button.enabled = false
64
- @started = false
63
+ self.started = false
65
64
  unless @played
66
65
  play_alarm
67
66
  msg_box('Alarm', 'Countdown Is Finished!')
68
- @played = true
67
+ self.played = true
69
68
  end
70
69
  end
71
70
  end
@@ -83,42 +82,40 @@ class Timer
83
82
  group('Countdown') {
84
83
  vertical_box {
85
84
  horizontal_box {
86
- @hour_spinbox = spinbox(0, HOUR_MAX) {
85
+ spinbox(0, HOUR_MAX) {
87
86
  stretchy false
88
- value 0
87
+ value <=> [self, :hour]
89
88
  }
90
89
  label(':') {
91
90
  stretchy false
92
91
  }
93
- @min_spinbox = spinbox(0, MINUTE_MAX) {
92
+ spinbox(0, MINUTE_MAX) {
94
93
  stretchy false
95
- value 0
94
+ value <=> [self, :min]
96
95
  }
97
96
  label(':') {
98
97
  stretchy false
99
98
  }
100
- @sec_spinbox = spinbox(0, SECOND_MAX) {
99
+ spinbox(0, SECOND_MAX) {
101
100
  stretchy false
102
- value 0
101
+ value <=> [self, :sec]
103
102
  }
104
103
  }
105
104
  horizontal_box {
106
- @start_button = button('Start') {
105
+ button('Start') {
106
+ enabled <= [self, :started, on_read: :!]
107
+
107
108
  on_clicked do
108
- @start_button.enabled = false
109
- @stop_button.enabled = true
110
- @started = true
111
- @played = false
109
+ self.started = true
110
+ self.played = false
112
111
  end
113
112
  }
114
113
 
115
- @stop_button = button('Stop') {
116
- enabled false
114
+ button('Stop') {
115
+ enabled <= [self, :started]
117
116
 
118
117
  on_clicked do
119
- @start_button.enabled = true
120
- @stop_button.enabled = false
121
- @started = false
118
+ self.started = false
122
119
  end
123
120
  }
124
121
  }
@@ -0,0 +1,129 @@
1
+ require 'glimmer-dsl-libui'
2
+
3
+ class Timer
4
+ include Glimmer
5
+
6
+ SECOND_MAX = 59
7
+ MINUTE_MAX = 59
8
+ HOUR_MAX = 23
9
+
10
+ def initialize
11
+ @pid = nil
12
+ @alarm_file = File.expand_path('../sounds/AlanWalker-Faded.mid', __dir__)
13
+ at_exit { stop_alarm }
14
+ setup_timer
15
+ create_gui
16
+ end
17
+
18
+ def stop_alarm
19
+ if @pid
20
+ Process.kill(:SIGKILL, @pid) if @th.alive?
21
+ @pid = nil
22
+ end
23
+ end
24
+
25
+ def play_alarm
26
+ stop_alarm
27
+ if @pid.nil?
28
+ begin
29
+ @pid = spawn "timidity -G 0.0-10.0 #{@alarm_file}"
30
+ @th = Process.detach @pid
31
+ rescue Errno::ENOENT
32
+ warn 'Timidty++ not found. Please install Timidity++.'
33
+ warn 'https://sourceforge.net/projects/timidity/'
34
+ end
35
+ end
36
+ end
37
+
38
+ def setup_timer
39
+ unless @setup_timer
40
+ Glimmer::LibUI.timer(1) do
41
+ if @started
42
+ seconds = @sec_spinbox.value
43
+ minutes = @min_spinbox.value
44
+ hours = @hour_spinbox.value
45
+ if seconds > 0
46
+ @sec_spinbox.value = seconds -= 1
47
+ end
48
+ if seconds == 0
49
+ if minutes > 0
50
+ @min_spinbox.value = minutes -= 1
51
+ @sec_spinbox.value = seconds = SECOND_MAX
52
+ end
53
+ if minutes == 0
54
+ if hours > 0
55
+ @hour_spinbox.value = hours -= 1
56
+ @min_spinbox.value = minutes = MINUTE_MAX
57
+ @sec_spinbox.value = seconds = SECOND_MAX
58
+ end
59
+ if hours == 0 && minutes == 0 && seconds == 0
60
+ @start_button.enabled = true
61
+ @stop_button.enabled = false
62
+ @started = false
63
+ unless @played
64
+ play_alarm
65
+ msg_box('Alarm', 'Countdown Is Finished!')
66
+ @played = true
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ @setup_timer = true
74
+ end
75
+ end
76
+
77
+ def create_gui
78
+ window('Timer') {
79
+ margined true
80
+
81
+ group('Countdown') {
82
+ vertical_box {
83
+ horizontal_box {
84
+ @hour_spinbox = spinbox(0, HOUR_MAX) {
85
+ stretchy false
86
+ value 0
87
+ }
88
+ label(':') {
89
+ stretchy false
90
+ }
91
+ @min_spinbox = spinbox(0, MINUTE_MAX) {
92
+ stretchy false
93
+ value 0
94
+ }
95
+ label(':') {
96
+ stretchy false
97
+ }
98
+ @sec_spinbox = spinbox(0, SECOND_MAX) {
99
+ stretchy false
100
+ value 0
101
+ }
102
+ }
103
+ horizontal_box {
104
+ @start_button = button('Start') {
105
+ on_clicked do
106
+ @start_button.enabled = false
107
+ @stop_button.enabled = true
108
+ @started = true
109
+ @played = false
110
+ end
111
+ }
112
+
113
+ @stop_button = button('Stop') {
114
+ enabled false
115
+
116
+ on_clicked do
117
+ @start_button.enabled = true
118
+ @stop_button.enabled = false
119
+ @started = false
120
+ end
121
+ }
122
+ }
123
+ }
124
+ }
125
+ }.show
126
+ end
127
+ end
128
+
129
+ Timer.new
Binary file
@@ -29,6 +29,11 @@ module Glimmer
29
29
  # Follows the Proxy Design Pattern
30
30
  class CheckboxProxy < ControlProxy
31
31
  DEFAULT_TEXT = ''
32
+
33
+ def data_bind(property, model_binding)
34
+ super
35
+ handle_listener('on_toggled') { model_binding.call(checked) } if property == 'checked'
36
+ end
32
37
 
33
38
  private
34
39
 
@@ -113,6 +113,11 @@ module Glimmer
113
113
  Fiddle.free @alpha unless @alpha.nil?
114
114
  super
115
115
  end
116
+
117
+ def data_bind(property, model_binding)
118
+ super
119
+ handle_listener('on_changed') { model_binding.call(color) } if property == 'color'
120
+ end
116
121
  end
117
122
  end
118
123
  end
@@ -54,8 +54,24 @@ module Glimmer
54
54
  alias set_items items
55
55
  alias items= items
56
56
 
57
- def selected_item
58
- items[selected]
57
+ def selected_item(value = nil)
58
+ if value.nil?
59
+ items[selected]
60
+ else
61
+ self.selected = items.index(value)
62
+ end
63
+ end
64
+ alias set_selected_item selected_item
65
+ alias selected_item= selected_item
66
+
67
+ def data_bind(property, model_binding)
68
+ super # model to view data-binding
69
+ case property
70
+ when 'selected'
71
+ handle_listener('on_selected') { model_binding.call(selected) }
72
+ when 'selected_item'
73
+ handle_listener('on_selected') { model_binding.call(selected_item) }
74
+ end
59
75
  end
60
76
  end
61
77
  end
@@ -64,6 +64,11 @@ module Glimmer
64
64
  Fiddle.free @time unless @time.nil?
65
65
  super
66
66
  end
67
+
68
+ def data_bind(property, model_binding)
69
+ super
70
+ handle_listener('on_changed') { model_binding.call(time) } if property == 'time'
71
+ end
67
72
  end
68
73
  end
69
74
  end
@@ -39,6 +39,11 @@ module Glimmer
39
39
  end
40
40
  alias set_items items
41
41
  alias items= items
42
+
43
+ def data_bind(property, model_binding)
44
+ super
45
+ handle_listener('on_changed') { model_binding.call(text) } if property == 'text'
46
+ end
42
47
  end
43
48
  end
44
49
  end
@@ -64,6 +64,10 @@ module Glimmer
64
64
  ::LibUI.free_font_button_font(@font_descriptor) unless @font_descriptor.nil?
65
65
  super
66
66
  end
67
+
68
+ def data_bind(property, model_binding)
69
+ handle_listener('on_changed') { model_binding.call(font) } if property == 'font'
70
+ end
67
71
  end
68
72
  end
69
73
  end
@@ -29,6 +29,11 @@ module Glimmer
29
29
  #
30
30
  # Follows the Proxy Design Pattern
31
31
  class CheckMenuItemProxy < MenuItemProxy
32
+ def data_bind(property, model_binding)
33
+ super
34
+ handle_listener('on_clicked') { model_binding.call(checked) } if property == 'checked'
35
+ end
36
+
32
37
  private
33
38
 
34
39
  def build_control
@@ -29,15 +29,23 @@ module Glimmer
29
29
  #
30
30
  # Follows the Proxy Design Pattern
31
31
  class RadioMenuItemProxy < MenuItemProxy
32
+ def initialize(keyword, parent, args, &block)
33
+ @last_checked = nil
34
+ super
35
+ end
36
+
32
37
  def checked(value = nil)
33
- if !value.nil?
34
- if Glimmer::LibUI.integer_to_boolean(value) != checked?
35
- super
38
+ if value.nil?
39
+ super()
40
+ else
41
+ super
42
+ if Glimmer::LibUI.integer_to_boolean(value, allow_nil: false) != Glimmer::LibUI.integer_to_boolean(@last_checked, allow_nil: false)
36
43
  if Glimmer::LibUI.integer_to_boolean(value)
37
44
  (@parent_proxy.children - [self]).select {|c| c.is_a?(MenuItemProxy)}.each do |menu_item|
38
45
  menu_item.checked = false
39
46
  end
40
47
  end
48
+ @last_checked = checked
41
49
  end
42
50
  end
43
51
  end
@@ -48,7 +56,7 @@ module Glimmer
48
56
  def handle_listener(listener_name, &listener)
49
57
  if listener_name.to_s == 'on_clicked'
50
58
  radio_listener = Proc.new do
51
- self.checked = true if !checked?
59
+ self.checked = true
52
60
  listener.call(self)
53
61
  end
54
62
  super(listener_name, &radio_listener)
@@ -57,6 +65,11 @@ module Glimmer
57
65
  end
58
66
  end
59
67
 
68
+ def data_bind(property, model_binding)
69
+ super
70
+ handle_listener('on_clicked') { model_binding.call(checked) } if property == 'checked'
71
+ end
72
+
60
73
  private
61
74
 
62
75
  def build_control
@@ -39,6 +39,26 @@ module Glimmer
39
39
  end
40
40
  alias set_items items
41
41
  alias items= items
42
+
43
+ def selected_item(value = nil)
44
+ if value.nil?
45
+ items[selected]
46
+ else
47
+ self.selected = items.index(value) || -1
48
+ end
49
+ end
50
+ alias set_selected_item selected_item
51
+ alias selected_item= selected_item
52
+
53
+ def data_bind(property, model_binding)
54
+ super # model to view data-binding
55
+ case property
56
+ when 'selected'
57
+ handle_listener('on_selected') { model_binding.call(selected) }
58
+ when 'selected_item'
59
+ handle_listener('on_selected') { model_binding.call(selected_item) }
60
+ end
61
+ end
42
62
  end
43
63
  end
44
64
  end
@@ -0,0 +1,38 @@
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 slider objects
28
+ #
29
+ # Follows the Proxy Design Pattern
30
+ class SliderProxy < ControlProxy
31
+ def data_bind(property, model_binding)
32
+ super
33
+ handle_listener('on_changed') { model_binding.call(value) } if property == 'value'
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,38 @@
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 spinbox objects
28
+ #
29
+ # Follows the Proxy Design Pattern
30
+ class SpinboxProxy < ControlProxy
31
+ def data_bind(property, model_binding)
32
+ super
33
+ handle_listener('on_changed') { model_binding.call(value) } if property == 'value'
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -245,8 +245,8 @@ module Glimmer
245
245
  args[0] = Glimmer::LibUI.boolean_to_integer(args.first) if BOOLEAN_PROPERTIES.include?(property) && (args.first.is_a?(TrueClass) || args.first.is_a?(FalseClass))
246
246
  args[0] = '' if STRING_PROPERTIES.include?(property) && args.first == nil
247
247
  if property.to_s == 'checked'
248
- current_value = Glimmer::LibUI.integer_to_boolean(::LibUI.send("#{libui_api_keyword}_checked", @libui))
249
- new_value = Glimmer::LibUI.integer_to_boolean(args[0])
248
+ current_value = Glimmer::LibUI.integer_to_boolean(::LibUI.send("#{libui_api_keyword}_checked", @libui), allow_nil: false)
249
+ new_value = Glimmer::LibUI.integer_to_boolean(args[0], allow_nil: false)
250
250
  ::LibUI.send("#{libui_api_keyword}_set_#{property}", @libui, *args) if new_value != current_value
251
251
  else
252
252
  ::LibUI.send("#{libui_api_keyword}_set_#{property}", @libui, *args)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: glimmer-dsl-libui
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.4
4
+ version: 0.4.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Maleh
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-11-28 00:00:00.000000000 Z
11
+ date: 2021-11-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: glimmer
@@ -247,22 +247,28 @@ files:
247
247
  - examples/basic_window2.rb
248
248
  - examples/button_counter.rb
249
249
  - examples/color_button.rb
250
+ - examples/color_button2.rb
250
251
  - examples/color_the_circles.rb
251
252
  - examples/control_gallery.rb
252
253
  - examples/custom_draw_text.rb
253
254
  - examples/custom_draw_text2.rb
254
255
  - examples/date_time_picker.rb
256
+ - examples/date_time_picker2.rb
255
257
  - examples/dynamic_area.rb
256
258
  - examples/dynamic_area2.rb
259
+ - examples/dynamic_area3.rb
260
+ - examples/dynamic_area4.rb
257
261
  - examples/editable_column_table.rb
258
262
  - examples/editable_table.rb
259
263
  - examples/font_button.rb
264
+ - examples/font_button2.rb
260
265
  - examples/form.rb
261
266
  - examples/form2.rb
262
267
  - examples/form_table.rb
263
268
  - examples/form_table2.rb
264
269
  - examples/grid.rb
265
270
  - examples/histogram.rb
271
+ - examples/histogram2.rb
266
272
  - examples/login.rb
267
273
  - examples/login2.rb
268
274
  - examples/login3.rb
@@ -272,6 +278,8 @@ files:
272
278
  - examples/method_based_custom_keyword.rb
273
279
  - examples/method_based_custom_keyword2.rb
274
280
  - examples/midi_player.rb
281
+ - examples/midi_player2.rb
282
+ - examples/midi_player3.rb
275
283
  - examples/simple_notepad.rb
276
284
  - examples/snake.rb
277
285
  - examples/snake/model/apple.rb
@@ -289,6 +297,7 @@ files:
289
297
  - examples/tic_tac_toe/board.rb
290
298
  - examples/tic_tac_toe/cell.rb
291
299
  - examples/timer.rb
300
+ - examples/timer2.rb
292
301
  - glimmer-dsl-libui.gemspec
293
302
  - icons/glimmer.png
294
303
  - lib/glimmer-dsl-libui.rb
@@ -366,6 +375,8 @@ files:
366
375
  - lib/glimmer/libui/control_proxy/open_type_tag_proxy.rb
367
376
  - lib/glimmer/libui/control_proxy/path_proxy.rb
368
377
  - lib/glimmer/libui/control_proxy/radio_buttons_proxy.rb
378
+ - lib/glimmer/libui/control_proxy/slider_proxy.rb
379
+ - lib/glimmer/libui/control_proxy/spinbox_proxy.rb
369
380
  - lib/glimmer/libui/control_proxy/tab_item_proxy.rb
370
381
  - lib/glimmer/libui/control_proxy/table_proxy.rb
371
382
  - lib/glimmer/libui/control_proxy/text_proxy.rb