glimmer-dsl-libui 0.0.1 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -0
  3. data/README.md +948 -21
  4. data/VERSION +1 -1
  5. data/examples/basic_entry.rb +31 -0
  6. data/examples/control_gallery.rb +184 -0
  7. data/examples/midi_player.rb +90 -0
  8. data/examples/simple_notepad.rb +15 -0
  9. data/glimmer-dsl-libui.gemspec +0 -0
  10. data/lib/glimmer/dsl/libui/control_expression.rb +2 -1
  11. data/lib/glimmer/dsl/libui/dsl.rb +1 -1
  12. data/lib/glimmer/dsl/libui/file_expression.rb +33 -0
  13. data/lib/glimmer/dsl/libui/open_file_expression.rb +33 -0
  14. data/lib/glimmer/dsl/libui/save_file_expression.rb +33 -0
  15. data/lib/glimmer/dsl/libui/tab_item_expression.rb +35 -0
  16. data/lib/glimmer/libui/about_menu_item_proxy.rb +37 -0
  17. data/lib/glimmer/libui/box.rb +37 -0
  18. data/lib/glimmer/libui/check_menu_item_proxy.rb +37 -0
  19. data/lib/glimmer/libui/combobox_proxy.rb +42 -0
  20. data/lib/glimmer/libui/control_proxy.rb +90 -24
  21. data/lib/glimmer/libui/editable_combobox_proxy.rb +42 -0
  22. data/lib/glimmer/libui/group_proxy.rb +35 -0
  23. data/lib/glimmer/libui/horizontal_box_proxy.rb +34 -0
  24. data/lib/glimmer/libui/menu_item_proxy.rb +41 -0
  25. data/lib/glimmer/libui/preferences_menu_item_proxy.rb +37 -0
  26. data/lib/glimmer/libui/quit_menu_item_proxy.rb +62 -0
  27. data/lib/glimmer/libui/radio_buttons_proxy.rb +42 -0
  28. data/lib/glimmer/libui/separator_menu_item_proxy.rb +37 -0
  29. data/lib/glimmer/libui/tab_item_proxy.rb +64 -0
  30. data/lib/glimmer/libui/vertical_box_proxy.rb +34 -0
  31. data/lib/glimmer/libui/window_proxy.rb +6 -2
  32. data/lib/glimmer-dsl-libui.rb +2 -0
  33. metadata +27 -5
data/README.md CHANGED
@@ -1,12 +1,16 @@
1
- # [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for LibUI 0.0.1
2
- ## Dependency-Free Ruby Desktop Development GUI Library
1
+ # [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for LibUI 0.0.5
2
+ ## Prerequisite-Free Ruby Desktop Development GUI Library
3
3
  [![Gem Version](https://badge.fury.io/rb/glimmer-dsl-libui.svg)](http://badge.fury.io/rb/glimmer-dsl-libui)
4
4
  [![Maintainability](https://api.codeclimate.com/v1/badges/ce2853efdbecf6ebdc73/maintainability)](https://codeclimate.com/github/AndyObtiva/glimmer-dsl-libui/maintainability)
5
5
  [![Join the chat at https://gitter.im/AndyObtiva/glimmer](https://badges.gitter.im/AndyObtiva/glimmer.svg)](https://gitter.im/AndyObtiva/glimmer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
6
6
 
7
- [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) is a dependency-free Ruby desktop development GUI library. No need to pre-install any pre-requisites. Just install the gem and have platform-independent GUI that just works!
7
+ [Glimmer](https://github.com/AndyObtiva/glimmer) DSL for [LibUI](https://github.com/kojix2/LibUI) is a prerequisite-free Ruby desktop development GUI library. No need to pre-install any prerequisites. Just install the gem and have platform-independent GUI that just works!
8
8
 
9
- [Glimmer DSL for LibUI](https://github.com/AndyObtiva/glimmer-dsl-libui) aims to provide a DSL similar to the [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) to enable more productive desktop development in Ruby with:
9
+ [LibUI](https://github.com/kojix2/LibUI) is a thin [Ruby](https://www.ruby-lang.org/en/) wrapper around [libui](https://github.com/andlabs/libui), a relatively new C GUI library that renders native widgets on every platform (similar to [SWT](https://www.eclipse.org/swt/), but without the heavy weight of the [Java Virtual Machine](https://www.java.com/en/)).
10
+
11
+ The main trade-off in using [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) as opposed to [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) or [Glimmer DSL for Tk](https://github.com/AndyObtiva/glimmer-dsl-tk) is the fact that [SWT](https://www.eclipse.org/swt/) and [Tk](https://www.tcl.tk/) are more mature than mid-alpha [libui](https://github.com/andlabs/libui) as GUI toolkits. Still, if there is only a need to build a small simple application, [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) could be a good convenient choice due to having zero prerequisites beyond the dependencies included in the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui). Also, just like [Glimmer DSL for Tk](https://github.com/AndyObtiva/glimmer-dsl-tk), its apps start instantly and have a small memory footprint. [LibUI](https://github.com/kojix2/LibUI) is a promising new GUI toolkit that might prove quite worthy in the future.
12
+
13
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) aims to provide a DSL similar to the [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) to enable more productive desktop development in Ruby with:
10
14
  - Declarative DSL syntax that visually maps to the GUI widget hierarchy
11
15
  - Convention over configuration via smart defaults and automation of low-level details
12
16
  - Requiring the least amount of syntax possible to build GUI
@@ -15,9 +19,31 @@
15
19
  - Scaffolding for new custom widgets, apps, and gems
16
20
  - Native-Executable packaging on Mac, Windows, and Linux
17
21
 
22
+ Example:
23
+
24
+ ```ruby
25
+ require 'glimmer-dsl-libui'
26
+
27
+ include Glimmer
28
+
29
+ window('hello world', 300, 200, 1).show
30
+ ```
31
+
32
+ ![glimmer-dsl-libui-mac-basic-window.png](images/glimmer-dsl-libui-mac-basic-window.png)
33
+ ![glimmer-dsl-libui-linux-basic-window.png](images/glimmer-dsl-libui-linux-basic-window.png)
34
+
35
+ NOTE: [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) is in early alpha mode (only supports included examples). Please help make better by contributing, adopting for small or low risk projects, and providing feedback. It is still an early alpha, so the more feedback and issues you report the better.
36
+
37
+ Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interested in:
38
+ - [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt): Glimmer DSL for SWT (JRuby Desktop Development GUI Framework)
39
+ - [glimmer-dsl-opal](https://github.com/AndyObtiva/glimmer-dsl-opal): Glimmer DSL for Opal (Pure Ruby Web GUI and Auto-Webifier of Desktop Apps)
40
+ - [glimmer-dsl-xml](https://github.com/AndyObtiva/glimmer-dsl-xml): Glimmer DSL for XML (& HTML)
41
+ - [glimmer-dsl-css](https://github.com/AndyObtiva/glimmer-dsl-css): Glimmer DSL for CSS
42
+ - [glimmer-dsl-tk](https://github.com/AndyObtiva/glimmer-dsl-tk): Glimmer DSL for Tk (MRI Ruby Desktop Development GUI Library)
43
+
18
44
  ## Glimmer GUI DSL Concepts
19
45
 
20
- The Glimmer GUI DSL provides a declarative syntax for [LibUI](https://github.com/kojix2/LibUI) that:
46
+ The Glimmer GUI DSL provides object-oriented declarative hierarchical syntax for [LibUI](https://github.com/kojix2/LibUI) that:
21
47
  - Supports smart defaults (e.g. automatic `on_closing` listener that quits `window`)
22
48
  - Automates wiring of widgets (e.g. `button` is automatically set as child of `window`)
23
49
  - Hides lower-level details (e.g. `LibUI.main` loop is started automatically when triggering `show` on `window`)
@@ -27,8 +53,8 @@ The Glimmer GUI DSL provides a declarative syntax for [LibUI](https://github.com
27
53
  The Glimmer GUI DSL follows these simple concepts in mapping from [LibUI](https://github.com/kojix2/LibUI) syntax:
28
54
  - **Control**: [LibUI](https://github.com/kojix2/LibUI) controls may be declared by lower-case underscored name (aka keyword) (e.g. `window` or `button`). Behind the scenes, they are represented by keyword methods that map to corresponding `LibUI.new_keyword` methods receiving args (e.g. `window('hello world', 300, 200, 1)`).
29
55
  - **Content/Properties/Listeners Block**: Any keyword may be optionally followed by a Ruby curly-brace multi-line-block containing nested controls (content) and/or properties (attributes) (e.g. `window('hello world', 300, 200, 1) {button('greet')}`). It optionally recives one arg representing the control (e.g. `button('greet') {|b| on_clicked { puts b.text}}`)
30
- - **Property**: Control properties may be declared inside keyword blocks with lower-case underscored name followed by property value args (e.g. `title "hello world"` inside `group`). Behind the scenes, they properties correspond to `control_set_property` methods.
31
- - **Listener**: Control listeners may be declared inside keyword blocks with listener lower-case underscored name beginning with `on_` and receiving required block handler (e.g. `on_clicked {puts 'clicked'}` inside `button`). Behind the scenes, they listeners correspond to `control_on_event` methods.
56
+ - **Property**: Control properties may be declared inside keyword blocks with lower-case underscored name followed by property value args (e.g. `title "hello world"` inside `group`). Behind the scenes, properties correspond to `control_set_property` methods.
57
+ - **Listener**: Control listeners may be declared inside keyword blocks with listener lower-case underscored name beginning with `on_` and receiving required block handler (e.g. `on_clicked {puts 'clicked'}` inside `button`). Behind the scenes, listeners correspond to `control_on_event` methods.
32
58
 
33
59
  Example of an app written in [LibUI](https://github.com/kojix2/LibUI)'s procedural imperative syntax:
34
60
 
@@ -41,7 +67,11 @@ UI.init
41
67
 
42
68
  main_window = UI.new_window('hello world', 300, 200, 1)
43
69
 
44
- UI.control_show(main_window)
70
+ button = UI.new_button('Button')
71
+
72
+ UI.button_on_clicked(button) do
73
+ UI.msg_box(main_window, 'Information', 'You clicked the button')
74
+ end
45
75
 
46
76
  UI.window_on_closing(main_window) do
47
77
  puts 'Bye Bye'
@@ -50,6 +80,9 @@ UI.window_on_closing(main_window) do
50
80
  0
51
81
  end
52
82
 
83
+ UI.window_set_child(main_window, button)
84
+ UI.control_show(main_window)
85
+
53
86
  UI.main
54
87
  UI.quit
55
88
  ```
@@ -61,7 +94,13 @@ require 'glimmer-dsl-libui'
61
94
 
62
95
  include Glimmer
63
96
 
64
- window('hello world', 300, 200, 1) {
97
+ window('hello world', 300, 200, 1) { |w|
98
+ button('Button') {
99
+ on_clicked do
100
+ msg_box(w, 'Information', 'You clicked the button')
101
+ end
102
+ }
103
+
65
104
  on_closing do
66
105
  puts 'Bye Bye'
67
106
  end
@@ -70,6 +109,18 @@ window('hello world', 300, 200, 1) {
70
109
 
71
110
  ## Usage
72
111
 
112
+ Install [glimmer-dsl-libui](https://rubygems.org/gems/glimmer-dsl-libui) gem directly:
113
+
114
+ ```
115
+ gem install glimmer-dsl-libui
116
+ ```
117
+
118
+ Or install via Bundler `Gemfile`:
119
+
120
+ ```ruby
121
+ gem 'glimmer-dsl-libui', '~> 0.0.5'
122
+ ```
123
+
73
124
  Add `require 'glimmer-dsl-libui'` at the top, and then `include Glimmer` into the top-level main object for testing or into an actual class for serious usage.
74
125
 
75
126
  Example (you may copy/paste in [`girb`](#girb-glimmer-irb)):
@@ -87,10 +138,6 @@ class Application
87
138
  puts 'Button Clicked'
88
139
  end
89
140
  }
90
-
91
- on_closing do
92
- puts 'Bye Bye'
93
- end
94
141
  }.show
95
142
  end
96
143
  end
@@ -100,7 +147,7 @@ Application.new.launch
100
147
 
101
148
  ## API
102
149
 
103
- Any control returned by a Glimmer GUI DSL keyword declaration can be introspected for its properties and updated via object-oriented attributes (standard Ruby `attr`/`attr=` or `set_attr`).
150
+ Any control returned by a [Glimmer GUI DSL](#glimmer-gui-dsl-concepts) keyword declaration can be introspected for its properties and updated via object-oriented attributes (standard Ruby `attr`/`attr=` or `set_attr`).
104
151
 
105
152
  Example (you may copy/paste in [`girb`](#girb-glimmer-irb)):
106
153
 
@@ -113,6 +160,61 @@ w.set_title 'aloha'
113
160
  puts w.title # => aloha
114
161
  ```
115
162
 
163
+ Controls are wrapped as Ruby proxy objects, having a `#libui` method to obtain the wrapped Fiddle pointer object. Ruby proxy objects rely on composition (via [Proxy Design Pattern](https://en.wikipedia.org/wiki/Proxy_pattern)) instead of inheritance to shield consumers from having to deal with lower-level details unless absolutely needed.
164
+
165
+ Example (you may copy/paste in [`girb`](#girb-glimmer-irb)):
166
+
167
+ ```ruby
168
+ w = window('hello world', 300, 200, 1) # => #<Glimmer::LibUI::WindowProxy:0x00007fde4ea39fb0
169
+ w.libui # => #<Fiddle::Pointer:0x00007fde53997980 ptr=0x00007fde51352a60 size=0 free=0x0000000000000000>
170
+ ```
171
+
172
+ Supported Controls and their Properties / Listeners:
173
+ - `button`: `text` (`String`) / `on_clicked`
174
+ - `combobox`: `items` (`Array` of `String`), `selected` (`1` or `0`) / `on_selected`
175
+ - `color_button`: `color` (r `Numeric`, g `Numeric`, b `Numeric`, a `Numeric`), `selected` (`1` or `0`) / `on_selected`
176
+ - `date_picker`: None / None
177
+ - `date_time_picker`: `time` (`UI::FFI::TM`) / `on_changed`
178
+ - `editable_combobox`: `items` (`Array` of `String`), `text` (`String`) / `on_changed`
179
+ - `entry`: `read_only` (`1` or `0`), `text` (`String`) / `on_changed`
180
+ - `font_button`: `font` (`UI::FFI::FontDescriptor`) / `on_changed`
181
+ - `group`: `margined` (`1` or `0`), `title` (`String`) / None
182
+ - `horizontal_box`: `padded` (`1` or `0`) / None
183
+ - `horizontal_separator`: None / None
184
+ - `label`: `text` (`String`) / None
185
+ - `menu`: None / None
186
+ - `menu_item`: `checked` (`1` or `0`) / `on_clicked`
187
+ - `multiline_entry`: `read_only` (`1` or `0`), `text` (`String`) / None
188
+ - `msg_box`: None / None
189
+ - `non_wrapping_multiline_entry`: None / None
190
+ - `progress_bar`: `value` (`Numeric`) / None
191
+ - `radio_buttons`: `selected` (`1` or `0`) / `on_selected`
192
+ - `slider`: `value` (`Numeric`) / `on_changed`
193
+ - `spinbox`: `value` (`Numeric`) / `on_changed`
194
+ - `tab`: `margined` (`1` or `0`), `num_pages` (`Integer`) / None
195
+ - `tab_item`: `index` [read-only] (`Integer`), `margined` (`1` or `0`), `name` [read-only] (`String`) / None
196
+ - `time_picker`: None / None
197
+ - `vertical_box`: `padded` (`1` or `0`) / None
198
+ - `window`: `borderless` (`1` or `0`), `content_size` (width `Numeric`, height `Numeric`), `fullscreen` (`1` or `0`), `margined` (`1` or `0`), `title` (`String`) / `on_closing`, `on_content_size_changed`
199
+
200
+ Common Control Properties:
201
+ - `enabled` (`1` or `0`)
202
+ - `libui` (`Fiddle::Pointer`): returns wrapped [LibUI](https://github.com/kojix2/LibUI) object
203
+ - `parent_proxy` (`Glimmer::LibUI::ControlProxy` or subclass)
204
+ - `parent` (`Fiddle::Pointer`)
205
+ - `toplevel` [read-only] (`1` or `0`)
206
+ - `visible` (`1` or `0`)
207
+ - `stretchy` [dsl-only] (`1` or `0`): available in [Glimmer GUI DSL](#glimmer-gui-dsl-concepts) when nested under `horizontal_box` or `vertical_box`
208
+
209
+ Common Control Operations:
210
+ - `destroy`
211
+ - `disable`
212
+ - `enable`
213
+ - `hide`
214
+ - `show`
215
+
216
+ To learn more about the [LibUI](https://github.com/kojix2/LibUI) API exposed through [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui), check out the [libui C headers](https://github.com/andlabs/libui/blob/master/ui.h)
217
+
116
218
  ## Girb (Glimmer IRB)
117
219
 
118
220
  You can run the `girb` command (`bin/girb` if you cloned the project locally):
@@ -127,19 +229,31 @@ Gotcha: On the Mac, when you close a window opened in `girb`, it remains open un
127
229
 
128
230
  ## Examples
129
231
 
130
- These examples reimplement the ones in the [LibUI](https://github.com/kojix2/LibUI) project utilizing the shorter Glimmer GUI DSL syntax.
232
+ These examples reimplement the ones in the [LibUI](https://github.com/kojix2/LibUI) project utilizing the [Glimmer GUI DSL](#glimmer-gui-dsl-concepts).
131
233
 
132
234
  ### Basic Window
133
235
 
134
236
  [examples/basic_window.rb](examples/basic_window.rb)
135
237
 
238
+ Run with this command from the root of the project if you cloned the project:
239
+
240
+ ```
241
+ ruby -r './lib/glimmer-dsl-libui' examples/basic_window.rb
242
+ ```
243
+
244
+ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
245
+
246
+ ```
247
+ ruby -r glimmer-dsl-libui -e "require 'examples/basic_window'"
248
+ ```
249
+
136
250
  Mac
137
251
 
138
- ![glimmer-dsl-libui-basic-window-mac.png](images/glimmer-dsl-libui-basic-window-mac.png)
252
+ ![glimmer-dsl-libui-mac-basic-window.png](images/glimmer-dsl-libui-mac-basic-window.png)
139
253
 
140
254
  Linux
141
255
 
142
- ![glimmer-dsl-libui-basic-window-linux.png](images/glimmer-dsl-libui-basic-window-linux.png)
256
+ ![glimmer-dsl-libui-linux-basic-window.png](images/glimmer-dsl-libui-linux-basic-window.png)
143
257
 
144
258
  [LibUI](https://github.com/kojix2/LibUI) Original Version:
145
259
 
@@ -183,15 +297,27 @@ window('hello world', 300, 200, 1) {
183
297
 
184
298
  [examples/basic_button.rb](examples/basic_button.rb)
185
299
 
300
+ Run with this command from the root of the project if you cloned the project:
301
+
302
+ ```
303
+ ruby -r './lib/glimmer-dsl-libui' examples/basic_button.rb
304
+ ```
305
+
306
+ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
307
+
308
+ ```
309
+ ruby -r glimmer-dsl-libui -e "require 'examples/basic_button'"
310
+ ```
311
+
186
312
  Mac
187
313
 
188
- ![glimmer-dsl-libui-basic-button-mac.png](images/glimmer-dsl-libui-basic-button-mac.png)
189
- ![glimmer-dsl-libui-basic-button-msg-box-mac.png](images/glimmer-dsl-libui-basic-button-msg-box-mac.png)
314
+ ![glimmer-dsl-libui-mac-basic-button.png](images/glimmer-dsl-libui-mac-basic-button.png)
315
+ ![glimmer-dsl-libui-mac-basic-button-msg-box.png](images/glimmer-dsl-libui-basic-button-msg-box.png)
190
316
 
191
317
  Linux
192
318
 
193
- ![glimmer-dsl-libui-basic-button-linux.png](images/glimmer-dsl-libui-basic-button-linux.png)
194
- ![glimmer-dsl-libui-basic-button-msg-box-linux.png](images/glimmer-dsl-libui-basic-button-msg-box-linux.png)
319
+ ![glimmer-dsl-libui-linux-basic-button.png](images/glimmer-dsl-libui-linux-basic-button.png)
320
+ ![glimmer-dsl-libui-linux-basic-button-msg-box.png](images/glimmer-dsl-libui-linux-basic-button-msg-box.png)
195
321
 
196
322
  [LibUI](https://github.com/kojix2/LibUI) Original Version:
197
323
 
@@ -244,6 +370,807 @@ window('hello world', 300, 200, 1) { |w|
244
370
  }.show
245
371
  ```
246
372
 
373
+ ### Basic Entry
374
+
375
+ [examples/basic_entry.rb](examples/basic_entry.rb)
376
+
377
+ Run with this command from the root of the project if you cloned the project:
378
+
379
+ ```
380
+ ruby -r './lib/glimmer-dsl-libui' examples/basic_entry.rb
381
+ ```
382
+
383
+ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
384
+
385
+ ```
386
+ ruby -r glimmer-dsl-libui -e "require 'examples/basic_entry'"
387
+ ```
388
+
389
+ Mac
390
+
391
+ ![glimmer-dsl-libui-mac-basic-entry.png](images/glimmer-dsl-libui-mac-basic-entry.png)
392
+ ![glimmer-dsl-libui-mac-basic-entry-msg-box.png](images/glimmer-dsl-libui-mac-basic-entry-msg-box.png)
393
+
394
+ Linux
395
+
396
+ ![glimmer-dsl-libui-linux-basic-entry.png](images/glimmer-dsl-libui-linux-basic-entry.png)
397
+ ![glimmer-dsl-libui-linux-basic-entry-msg-box.png](images/glimmer-dsl-libui-linux-basic-entry-msg-box.png)
398
+
399
+ [LibUI](https://github.com/kojix2/LibUI) Original Version:
400
+
401
+ ```ruby
402
+ require 'libui'
403
+
404
+ UI = LibUI
405
+
406
+ UI.init
407
+
408
+ main_window = UI.new_window('Basic Entry', 300, 50, 1)
409
+ UI.window_on_closing(main_window) do
410
+ puts 'Bye Bye'
411
+ UI.control_destroy(main_window)
412
+ UI.quit
413
+ 0
414
+ end
415
+
416
+ hbox = UI.new_horizontal_box
417
+ UI.window_set_child(main_window, hbox)
418
+
419
+ entry = UI.new_entry
420
+ UI.entry_on_changed(entry) do
421
+ puts UI.entry_text(entry).to_s
422
+ $stdout.flush # For Windows
423
+ end
424
+ UI.box_append(hbox, entry, 1)
425
+
426
+ button = UI.new_button('Button')
427
+ UI.button_on_clicked(button) do
428
+ text = UI.entry_text(entry).to_s
429
+ UI.msg_box(main_window, 'You entered', text)
430
+ 0
431
+ end
432
+
433
+ UI.box_append(hbox, button, 0)
434
+
435
+ UI.control_show(main_window)
436
+ UI.main
437
+ UI.quit
438
+ ```
439
+
440
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
441
+
442
+ ```ruby
443
+ require 'glimmer-dsl-libui'
444
+
445
+ include Glimmer
446
+
447
+ window('Basic Entry', 300, 50, 1) { |w|
448
+ horizontal_box {
449
+ e = entry {
450
+ # stretchy 1 # Smart default option for appending to horizontal_box
451
+
452
+ on_changed do
453
+ puts e.text
454
+ $stdout.flush # For Windows
455
+ end
456
+ }
457
+
458
+ button('Button') {
459
+ stretchy 0
460
+
461
+ on_clicked do
462
+ text = e.text
463
+ msg_box(w, 'You entered', text)
464
+ end
465
+ }
466
+ }
467
+
468
+ on_closing do
469
+ puts 'Bye Bye'
470
+ end
471
+ }.show
472
+ ```
473
+
474
+ ### Simple Notepad
475
+
476
+ [examples/simple_notepad.rb](examples/simple_notepad.rb)
477
+
478
+ Run with this command from the root of the project if you cloned the project:
479
+
480
+ ```
481
+ ruby -r './lib/glimmer-dsl-libui' examples/simple_notepad.rb
482
+ ```
483
+
484
+ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
485
+
486
+ ```
487
+ ruby -r glimmer-dsl-libui -e "require 'examples/simple_notepad'"
488
+ ```
489
+
490
+ Mac
491
+
492
+ ![glimmer-dsl-libui-mac-simple-notepad.png](images/glimmer-dsl-libui-mac-simple-notepad.png)
493
+
494
+ Linux
495
+
496
+ ![glimmer-dsl-libui-linux-simple-notepad.png](images/glimmer-dsl-libui-linux-simple-notepad.png)
497
+
498
+ [LibUI](https://github.com/kojix2/LibUI) Original Version:
499
+
500
+ ```ruby
501
+ require 'libui'
502
+
503
+ UI = LibUI
504
+
505
+ UI.init
506
+
507
+ main_window = UI.new_window('Notepad', 500, 300, 1)
508
+ UI.window_on_closing(main_window) do
509
+ puts 'Bye Bye'
510
+ UI.control_destroy(main_window)
511
+ UI.quit
512
+ 0
513
+ end
514
+
515
+ vbox = UI.new_vertical_box
516
+ UI.window_set_child(main_window, vbox)
517
+
518
+ entry = UI.new_non_wrapping_multiline_entry
519
+ UI.box_append(vbox, entry, 1)
520
+
521
+ UI.control_show(main_window)
522
+ UI.main
523
+ UI.quit
524
+ ```
525
+
526
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
527
+
528
+ ```ruby
529
+ require 'glimmer-dsl-libui'
530
+
531
+ include Glimmer
532
+
533
+ window('Notepad', 500, 300, 1) {
534
+ on_closing do
535
+ puts 'Bye Bye'
536
+ end
537
+
538
+ vertical_box {
539
+ non_wrapping_multiline_entry
540
+ }
541
+ }.show
542
+ ```
543
+
544
+ ### Midi Player
545
+
546
+ This example has prerequisites:
547
+ - Install [TiMidity](http://timidity.sourceforge.net) and ensure `timidity` command is in `PATH` (can be installed via [Homebrew](https://brew.sh) on Mac or [apt-get](https://help.ubuntu.com/community/AptGet/Howto) on Linux).
548
+ - Add `*.mid` files to `~/Music` directory (you may copy the ones included in [sounds](sounds) directory)
549
+
550
+ [examples/midi_player.rb](examples/midi_player.rb)
551
+
552
+ Run with this command from the root of the project if you cloned the project:
553
+
554
+ ```
555
+ ruby -r './lib/glimmer-dsl-libui' examples/midi_player.rb
556
+ ```
557
+
558
+ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
559
+
560
+ ```
561
+ ruby -r glimmer-dsl-libui -e "require 'examples/midi_player'"
562
+ ```
563
+
564
+ Mac
565
+
566
+ ![glimmer-dsl-libui-mac-midi-player.png](images/glimmer-dsl-libui-mac-midi-player.png)
567
+ ![glimmer-dsl-libui-mac-midi-player-version-msg-box.png](images/glimmer-dsl-libui-mac-midi-player-version-msg-box.png)
568
+
569
+ Linux
570
+
571
+ ![glimmer-dsl-libui-linux-midi-player.png](images/glimmer-dsl-libui-linux-midi-player.png)
572
+ ![glimmer-dsl-libui-linux-midi-player-version-msg-box.png](images/glimmer-dsl-libui-linux-midi-player-version-msg-box.png)
573
+
574
+ [LibUI](https://github.com/kojix2/LibUI) Original Version:
575
+
576
+ ```ruby
577
+ require 'libui'
578
+ UI = LibUI
579
+
580
+ class TinyMidiPlayer
581
+ VERSION = '0.0.1'
582
+
583
+ def initialize
584
+ UI.init
585
+ @pid = nil
586
+ @music_directory = File.expand_path(ARGV[0] || '~/Music/')
587
+ @midi_files = Dir.glob(File.join(@music_directory, '**/*.mid'))
588
+ .sort_by { |path| File.basename(path) }
589
+ at_exit { stop_midi }
590
+ create_gui
591
+ end
592
+
593
+ def stop_midi
594
+ if @pid
595
+ if @th.alive?
596
+ Process.kill(:SIGKILL, @pid)
597
+ @pid = nil
598
+ else
599
+ @pid = nil
600
+ end
601
+ end
602
+ end
603
+
604
+ def play_midi
605
+ stop_midi
606
+ if @pid.nil? && @selected_file
607
+ begin
608
+ @pid = spawn "timidity #{@selected_file}"
609
+ @th = Process.detach @pid
610
+ rescue Errno::ENOENT
611
+ warn 'Timidty++ not found. Please install Timidity++.'
612
+ warn 'https://sourceforge.net/projects/timidity/'
613
+ end
614
+ end
615
+ end
616
+
617
+ def show_version(main_window)
618
+ UI.msg_box(main_window,
619
+ 'Tiny Midi Player',
620
+ "Written in Ruby\n" \
621
+ "https://github.com/kojix2/libui\n" \
622
+ "Version #{VERSION}")
623
+ end
624
+
625
+ def create_gui
626
+ help_menu = UI.new_menu('Help')
627
+ version_item = UI.menu_append_item(help_menu, 'Version')
628
+
629
+ UI.new_window('Tiny Midi Player', 200, 50, 1).tap do |main_window|
630
+ UI.menu_item_on_clicked(version_item) { show_version(main_window) }
631
+
632
+ UI.window_on_closing(main_window) do
633
+ UI.control_destroy(main_window)
634
+ UI.quit
635
+ 0
636
+ end
637
+
638
+ UI.new_horizontal_box.tap do |hbox|
639
+ UI.new_vertical_box.tap do |vbox|
640
+ UI.new_button('▶').tap do |button1|
641
+ UI.button_on_clicked(button1) { play_midi }
642
+ UI.box_append(vbox, button1, 1)
643
+ end
644
+ UI.new_button('■').tap do |button2|
645
+ UI.button_on_clicked(button2) { stop_midi }
646
+ UI.box_append(vbox, button2, 1)
647
+ end
648
+ UI.box_append(hbox, vbox, 0)
649
+ end
650
+ UI.window_set_child(main_window, hbox)
651
+
652
+ UI.new_combobox.tap do |cbox|
653
+ @midi_files.each do |path|
654
+ name = File.basename(path)
655
+ UI.combobox_append(cbox, name)
656
+ end
657
+ UI.combobox_on_selected(cbox) do |ptr|
658
+ @selected_file = @midi_files[UI.combobox_selected(ptr)]
659
+ play_midi if @th&.alive?
660
+ 0
661
+ end
662
+ UI.box_append(hbox, cbox, 1)
663
+ end
664
+ end
665
+ UI.control_show(main_window)
666
+ end
667
+ UI.main
668
+ UI.quit
669
+ end
670
+ end
671
+
672
+ TinyMidiPlayer.new
673
+ ```
674
+
675
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
676
+
677
+ ```ruby
678
+ require 'glimmer-dsl-libui'
679
+
680
+ class TinyMidiPlayer
681
+ include Glimmer
682
+
683
+ VERSION = '0.0.1'
684
+
685
+ def initialize
686
+ @pid = nil
687
+ @music_directory = File.expand_path(ARGV[0] || '~/Music/')
688
+ @midi_files = Dir.glob(File.join(@music_directory, '**/*.mid'))
689
+ .sort_by { |path| File.basename(path) }
690
+ at_exit { stop_midi }
691
+ create_gui
692
+ end
693
+
694
+ def stop_midi
695
+ if @pid
696
+ if @th.alive?
697
+ Process.kill(:SIGKILL, @pid)
698
+ @pid = nil
699
+ else
700
+ @pid = nil
701
+ end
702
+ end
703
+ end
704
+
705
+ def play_midi
706
+ stop_midi
707
+ if @pid.nil? && @selected_file
708
+ begin
709
+ @pid = spawn "timidity #{@selected_file}"
710
+ @th = Process.detach @pid
711
+ rescue Errno::ENOENT
712
+ warn 'Timidty++ not found. Please install Timidity++.'
713
+ warn 'https://sourceforge.net/projects/timidity/'
714
+ end
715
+ end
716
+ end
717
+
718
+ def show_version(main_window)
719
+ msg_box(main_window,
720
+ 'Tiny Midi Player',
721
+ "Written in Ruby\n" \
722
+ "https://github.com/kojix2/libui\n" \
723
+ "Version #{VERSION}")
724
+ end
725
+
726
+ def create_gui
727
+ menu('Help') { |m|
728
+ menu_item('Version') {
729
+ on_clicked do
730
+ show_version(@main_window)
731
+ end
732
+ }
733
+ }
734
+ @main_window = window('Tiny Midi Player', 200, 50, 1) {
735
+ horizontal_box {
736
+ vertical_box {
737
+ stretchy 0
738
+
739
+ button('▶') {
740
+ on_clicked do
741
+ play_midi
742
+ end
743
+ }
744
+ button('■') {
745
+ on_clicked do
746
+ stop_midi
747
+ end
748
+ }
749
+ }
750
+
751
+ combobox { |c|
752
+ items @midi_files.map { |path| File.basename(path) }
753
+
754
+ on_selected do
755
+ @selected_file = @midi_files[c.selected]
756
+ play_midi if @th&.alive?
757
+ end
758
+ }
759
+ }
760
+ }
761
+ @main_window.show
762
+ end
763
+ end
764
+
765
+ TinyMidiPlayer.new
766
+ ```
767
+
768
+ ### Control Gallery
769
+
770
+ [examples/control_gallery.rb](examples/control_gallery.rb)
771
+
772
+ Run with this command from the root of the project if you cloned the project:
773
+
774
+ ```
775
+ ruby -r './lib/glimmer-dsl-libui' examples/control_gallery.rb
776
+ ```
777
+
778
+ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
779
+
780
+ ```
781
+ ruby -r glimmer-dsl-libui -e "require 'examples/control_gallery'"
782
+ ```
783
+
784
+ Mac
785
+
786
+ ![glimmer-dsl-libui-mac-control-gallery.png](images/glimmer-dsl-libui-mac-control-gallery.png)
787
+
788
+ Linux
789
+
790
+ ![glimmer-dsl-libui-linux-control-gallery.png](images/glimmer-dsl-libui-linux-control-gallery.png)
791
+
792
+ [LibUI](https://github.com/kojix2/LibUI) Original Version:
793
+
794
+ ```ruby
795
+ require 'libui'
796
+ UI = LibUI
797
+
798
+ UI.init
799
+
800
+ should_quit = proc do
801
+ puts 'Bye Bye'
802
+ UI.control_destroy(MAIN_WINDOW)
803
+ UI.quit
804
+ 0
805
+ end
806
+
807
+ # File menu
808
+ menu = UI.new_menu('File')
809
+ open_menu_item = UI.menu_append_item(menu, 'Open')
810
+ UI.menu_item_on_clicked(open_menu_item) do
811
+ pt = UI.open_file(MAIN_WINDOW)
812
+ puts pt unless pt.null?
813
+ end
814
+ save_menu_item = UI.menu_append_item(menu, 'Save')
815
+ UI.menu_item_on_clicked(save_menu_item) do
816
+ pt = UI.save_file(MAIN_WINDOW)
817
+ puts pt unless pt.null?
818
+ end
819
+
820
+ UI.menu_append_quit_item(menu)
821
+ UI.on_should_quit(should_quit)
822
+
823
+ # Edit menu
824
+ edit_menu = UI.new_menu('Edit')
825
+ UI.menu_append_check_item(edit_menu, 'Checkable Item_')
826
+ UI.menu_append_separator(edit_menu)
827
+ disabled_item = UI.menu_append_item(edit_menu, 'Disabled Item_')
828
+ UI.menu_item_disable(disabled_item)
829
+
830
+ preferences = UI.menu_append_preferences_item(menu)
831
+
832
+ # Help menu
833
+ help_menu = UI.new_menu('Help')
834
+ UI.menu_append_item(help_menu, 'Help')
835
+ UI.menu_append_about_item(help_menu)
836
+
837
+ # Main Window
838
+ MAIN_WINDOW = UI.new_window('Control Gallery', 600, 500, 1)
839
+ UI.window_set_margined(MAIN_WINDOW, 1)
840
+ UI.window_on_closing(MAIN_WINDOW, should_quit)
841
+
842
+ vbox = UI.new_vertical_box
843
+ UI.window_set_child(MAIN_WINDOW, vbox)
844
+ hbox = UI.new_horizontal_box
845
+ UI.box_set_padded(vbox, 1)
846
+ UI.box_set_padded(hbox, 1)
847
+
848
+ UI.box_append(vbox, hbox, 1)
849
+
850
+ # Group - Basic Controls
851
+ group = UI.new_group('Basic Controls')
852
+ UI.group_set_margined(group, 1)
853
+ UI.box_append(hbox, group, 1) # OSX bug?
854
+
855
+ inner = UI.new_vertical_box
856
+ UI.box_set_padded(inner, 1)
857
+ UI.group_set_child(group, inner)
858
+
859
+ # Button
860
+ button = UI.new_button('Button')
861
+ UI.button_on_clicked(button) do
862
+ UI.msg_box(MAIN_WINDOW, 'Information', 'You clicked the button')
863
+ end
864
+ UI.box_append(inner, button, 0)
865
+
866
+ # Checkbox
867
+ checkbox = UI.new_checkbox('Checkbox')
868
+ UI.checkbox_on_toggled(checkbox) do |ptr|
869
+ checked = UI.checkbox_checked(ptr) == 1
870
+ UI.window_set_title(MAIN_WINDOW, "Checkbox is #{checked}")
871
+ UI.checkbox_set_text(ptr, "I am the checkbox (#{checked})")
872
+ end
873
+ UI.box_append(inner, checkbox, 0)
874
+
875
+ # Label
876
+ UI.box_append(inner, UI.new_label('Label'), 0)
877
+
878
+ # Separator
879
+ UI.box_append(inner, UI.new_horizontal_separator, 0)
880
+
881
+ # Date Picker
882
+ UI.box_append(inner, UI.new_date_picker, 0)
883
+
884
+ # Time Picker
885
+ UI.box_append(inner, UI.new_time_picker, 0)
886
+
887
+ # Date Time Picker
888
+ UI.box_append(inner, UI.new_date_time_picker, 0)
889
+
890
+ # Font Button
891
+ UI.box_append(inner, UI.new_font_button, 0)
892
+
893
+ # Color Button
894
+ UI.box_append(inner, UI.new_color_button, 0)
895
+
896
+ inner2 = UI.new_vertical_box
897
+ UI.box_set_padded(inner2, 1)
898
+ UI.box_append(hbox, inner2, 1)
899
+
900
+ # Group - Numbers
901
+ group = UI.new_group('Numbers')
902
+ UI.group_set_margined(group, 1)
903
+ UI.box_append(inner2, group, 0)
904
+
905
+ inner = UI.new_vertical_box
906
+ UI.box_set_padded(inner, 1)
907
+ UI.group_set_child(group, inner)
908
+
909
+ # Spinbox
910
+ spinbox = UI.new_spinbox(0, 100)
911
+ UI.spinbox_set_value(spinbox, 42)
912
+ UI.spinbox_on_changed(spinbox) do |ptr|
913
+ puts "New Spinbox value: #{UI.spinbox_value(ptr)}"
914
+ end
915
+ UI.box_append(inner, spinbox, 0)
916
+
917
+ # Slider
918
+ slider = UI.new_slider(0, 100)
919
+ UI.box_append(inner, slider, 0)
920
+
921
+ # Progressbar
922
+ progressbar = UI.new_progress_bar
923
+ UI.box_append(inner, progressbar, 0)
924
+
925
+ UI.slider_on_changed(slider) do |ptr|
926
+ v = UI.slider_value(ptr)
927
+ puts "New Slider value: #{v}"
928
+ UI.progress_bar_set_value(progressbar, v)
929
+ end
930
+
931
+ # Group - Lists
932
+ group = UI.new_group('Lists')
933
+ UI.group_set_margined(group, 1)
934
+ UI.box_append(inner2, group, 0)
935
+
936
+ inner = UI.new_vertical_box
937
+ UI.box_set_padded(inner, 1)
938
+ UI.group_set_child(group, inner)
939
+
940
+ # Combobox
941
+ cbox = UI.new_combobox
942
+ UI.combobox_append(cbox, 'combobox Item 1')
943
+ UI.combobox_append(cbox, 'combobox Item 2')
944
+ UI.combobox_append(cbox, 'combobox Item 3')
945
+ UI.box_append(inner, cbox, 0)
946
+ UI.combobox_on_selected(cbox) do |ptr|
947
+ puts "New combobox selection: #{UI.combobox_selected(ptr)}"
948
+ end
949
+
950
+ # Editable Combobox
951
+ ebox = UI.new_editable_combobox
952
+ UI.editable_combobox_append(ebox, 'Editable Item 1')
953
+ UI.editable_combobox_append(ebox, 'Editable Item 2')
954
+ UI.editable_combobox_append(ebox, 'Editable Item 3')
955
+ UI.box_append(inner, ebox, 0)
956
+
957
+ # Radio Buttons
958
+ rb = UI.new_radio_buttons
959
+ UI.radio_buttons_append(rb, 'Radio Button 1')
960
+ UI.radio_buttons_append(rb, 'Radio Button 2')
961
+ UI.radio_buttons_append(rb, 'Radio Button 3')
962
+ UI.box_append(inner, rb, 1)
963
+
964
+ # Tab
965
+ tab = UI.new_tab
966
+ hbox1 = UI.new_horizontal_box
967
+ hbox2 = UI.new_horizontal_box
968
+ UI.tab_append(tab, 'Page 1', hbox1)
969
+ UI.tab_append(tab, 'Page 2', hbox2)
970
+ UI.tab_append(tab, 'Page 3', UI.new_horizontal_box)
971
+ UI.box_append(inner2, tab, 1)
972
+
973
+ # Text Entry
974
+ text_entry = UI.new_entry
975
+ UI.entry_set_text text_entry, 'Please enter your feelings'
976
+ UI.entry_on_changed(text_entry) do |ptr|
977
+ puts "Current textbox data: '#{UI.entry_text(ptr)}'"
978
+ end
979
+ UI.box_append(hbox1, text_entry, 1)
980
+
981
+ UI.control_show(MAIN_WINDOW)
982
+
983
+ UI.main
984
+ UI.quit
985
+ ```
986
+
987
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
988
+
989
+ ```ruby
990
+ require 'glimmer-dsl-libui'
991
+
992
+ include Glimmer
993
+
994
+ menu('File') {
995
+ menu_item('Open') {
996
+ on_clicked do
997
+ file = open_file(MAIN_WINDOW)
998
+ puts file unless file.nil?
999
+ end
1000
+ }
1001
+
1002
+ menu_item('Save') {
1003
+ on_clicked do
1004
+ file = save_file(MAIN_WINDOW)
1005
+ puts file unless file.nil?
1006
+ end
1007
+ }
1008
+
1009
+ quit_menu_item {
1010
+ on_clicked do
1011
+ puts 'Bye Bye'
1012
+ end
1013
+ }
1014
+
1015
+ preferences_menu_item # Can optionally contain an on_clicked listener
1016
+ }
1017
+
1018
+ menu('Edit') {
1019
+ check_menu_item('Checkable Item_')
1020
+ separator_menu_item
1021
+ menu_item('Disabled Item_') { |mi|
1022
+ enabled 0
1023
+ }
1024
+ }
1025
+
1026
+ menu('Help') {
1027
+ menu_item('Help')
1028
+
1029
+ about_menu_item # Can optionally contain an on_clicked listener
1030
+ }
1031
+
1032
+ MAIN_WINDOW = window('Control Gallery', 600, 500, 1) {
1033
+ margined 1
1034
+
1035
+ on_closing do
1036
+ puts 'Bye Bye'
1037
+ end
1038
+
1039
+ vertical_box { |vb|
1040
+ padded 1
1041
+
1042
+ horizontal_box {
1043
+ padded 1
1044
+
1045
+ group('Basic Controls') {
1046
+ margined 1
1047
+
1048
+ vertical_box {
1049
+ padded 1
1050
+
1051
+ button('Button') {
1052
+ stretchy 0
1053
+
1054
+ on_clicked do
1055
+ msg_box(MAIN_WINDOW, 'Information', 'You clicked the button')
1056
+ end
1057
+ }
1058
+
1059
+ checkbox('Checkbox') { |c|
1060
+ stretchy 0
1061
+
1062
+ on_toggled do
1063
+ checked = c.checked == 1
1064
+ MAIN_WINDOW.title = "Checkbox is #{checked}"
1065
+ c.text = "I am the checkbox (#{checked})"
1066
+ end
1067
+ }
1068
+
1069
+ label('Label') { stretchy 0 }
1070
+
1071
+ horizontal_separator { stretchy 0 }
1072
+
1073
+ date_picker { stretchy 0 }
1074
+
1075
+ time_picker { stretchy 0 }
1076
+
1077
+ date_time_picker { stretchy 0 }
1078
+
1079
+ font_button { stretchy 0 }
1080
+
1081
+ color_button { stretchy 0 }
1082
+ }
1083
+ }
1084
+
1085
+ vertical_box {
1086
+ padded 1
1087
+
1088
+ group('Numbers') {
1089
+ stretchy 0
1090
+ margined 1
1091
+
1092
+ vertical_box {
1093
+ padded 1
1094
+
1095
+ spinbox(0, 100) { |s|
1096
+ stretchy 0
1097
+ value 42
1098
+
1099
+ on_changed do
1100
+ puts "New Spinbox value: #{s.value}"
1101
+ end
1102
+ }
1103
+
1104
+ slider(0, 100) { |s|
1105
+ stretchy 0
1106
+
1107
+ on_changed do
1108
+ v = s.value
1109
+ puts "New Slider value: #{v}"
1110
+ @progress_bar.value = v
1111
+ end
1112
+ }
1113
+
1114
+ @progress_bar = progress_bar { stretchy 0 }
1115
+ }
1116
+ }
1117
+
1118
+ group('Lists') {
1119
+ stretchy 0
1120
+ margined 1
1121
+
1122
+ vertical_box {
1123
+ padded 1
1124
+
1125
+ combobox { |c|
1126
+ stretchy 0
1127
+ items ['combobox Item 1', 'combobox Item 2', 'combobox Item 3']
1128
+
1129
+ on_selected do
1130
+ puts "New combobox selection: #{c.selected}"
1131
+ end
1132
+ }
1133
+
1134
+ editable_combobox {
1135
+ stretchy 0
1136
+ items ['Editable Item 1', 'Editable Item 2', 'Editable Item 3']
1137
+ }
1138
+
1139
+ radio_buttons {
1140
+ items ['Radio Button 1', 'Radio Button 2', 'Radio Button 3']
1141
+ }
1142
+ }
1143
+ }
1144
+
1145
+ tab {
1146
+ tab_item('Page 1') {
1147
+ horizontal_box {
1148
+ entry { |e|
1149
+ text 'Please enter your feelings'
1150
+
1151
+ on_changed do
1152
+ puts "Current textbox data: '#{e.text}'"
1153
+ end
1154
+ }
1155
+ }
1156
+ }
1157
+
1158
+ tab_item('Page 2') {
1159
+ horizontal_box
1160
+ }
1161
+
1162
+ tab_item('Page 3') {
1163
+ horizontal_box
1164
+ }
1165
+ }
1166
+ }
1167
+ }
1168
+ }
1169
+ }
1170
+
1171
+ MAIN_WINDOW.show
1172
+ ```
1173
+
247
1174
  ## Contributing to glimmer-dsl-libui
248
1175
 
249
1176
  - Check out the latest master to make sure the feature hasn't been