glimmer-dsl-libui 0.1.9 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +34 -0
  3. data/README.md +624 -29
  4. data/VERSION +1 -1
  5. data/examples/area_gallery.rb +7 -1
  6. data/examples/area_gallery2.rb +14 -4
  7. data/examples/area_gallery3.rb +8 -2
  8. data/examples/area_gallery4.rb +15 -5
  9. data/examples/color_the_circles.rb +223 -0
  10. data/examples/control_gallery.rb +1 -1
  11. data/examples/form_table.rb +20 -0
  12. data/examples/grid.rb +38 -2
  13. data/examples/login.rb +45 -0
  14. data/examples/midi_player.rb +1 -1
  15. data/examples/timer.rb +135 -0
  16. data/glimmer-dsl-libui.gemspec +0 -0
  17. data/lib/glimmer/libui/control_proxy/area_proxy/scrolling_area_proxy.rb +40 -0
  18. data/lib/glimmer/libui/control_proxy/area_proxy.rb +9 -5
  19. data/lib/glimmer/libui/control_proxy/entry_proxy/password_entry_proxy.rb +36 -0
  20. data/lib/glimmer/libui/control_proxy/entry_proxy/search_entry_proxy.rb +36 -0
  21. data/lib/glimmer/libui/control_proxy/entry_proxy.rb +39 -0
  22. data/lib/glimmer/libui/control_proxy/grid_proxy.rb +12 -1
  23. data/lib/glimmer/libui/control_proxy/menu_item_proxy/radio_menu_item_proxy.rb +69 -0
  24. data/lib/glimmer/libui/control_proxy/menu_proxy.rb +3 -0
  25. data/lib/glimmer/libui/control_proxy/path_proxy.rb +3 -30
  26. data/lib/glimmer/libui/control_proxy.rb +8 -1
  27. data/lib/glimmer/libui/shape/arc.rb +6 -3
  28. data/lib/glimmer/libui/shape/circle.rb +50 -0
  29. data/lib/glimmer/libui/shape/rectangle.rb +4 -0
  30. data/lib/glimmer/libui/shape/square.rb +4 -0
  31. data/lib/glimmer/libui.rb +76 -2
  32. data/sounds/AlanWalker-Faded.mid +0 -0
  33. data/sounds/AlanWalker-SingMeToSleep.mid +0 -0
  34. data/sounds/CalvinHarris-Blame.mid +0 -0
  35. data/sounds/CalvinHarris-MyWay.mid +0 -0
  36. data/sounds/deadmau5-2448.mid +0 -0
  37. data/sounds/deadmau5-SoThereIWas.mid +0 -0
  38. metadata +19 -4
data/README.md CHANGED
@@ -1,4 +1,4 @@
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.1.9
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.2.1
2
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)
@@ -118,11 +118,17 @@ window('Area Gallery', 400, 400) {
118
118
  stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
119
119
  }
120
120
  path { # declarative stable path
121
- arc(200, 200, 90, 0, 360, false)
121
+ circle(200, 200, 90)
122
122
 
123
123
  fill r: 202, g: 102, b: 204, a: 0.5
124
124
  stroke r: 0, g: 0, b: 0, thickness: 2
125
125
  }
126
+ path { # declarative stable path
127
+ arc(400, 220, 180, 90, 90, false)
128
+
129
+ fill r: 204, g: 102, b: 204, a: 0.5
130
+ stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
131
+ }
126
132
 
127
133
  on_mouse_event do |area_mouse_event|
128
134
  p area_mouse_event
@@ -191,7 +197,7 @@ Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interes
191
197
 
192
198
  ## Table of Contents
193
199
 
194
- - [Glimmer DSL for LibUI 0.1.9](#-glimmer-dsl-for-libui-019)
200
+ - [Glimmer DSL for LibUI 0.2.1](#-glimmer-dsl-for-libui-021)
195
201
  - [Glimmer GUI DSL Concepts](#glimmer-gui-dsl-concepts)
196
202
  - [Usage](#usage)
197
203
  - [Girb (Glimmer IRB)](#girb-glimmer-irb)
@@ -199,6 +205,7 @@ Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interes
199
205
  - [Supported Controls](#supported-controls)
200
206
  - [Common Control Properties](#common-control-properties)
201
207
  - [Common Control Operations](#common-control-operations)
208
+ - [LibUI Operations](#libui-operations)
202
209
  - [Extra Dialogs](#extra-dialogs)
203
210
  - [Extra Operations](#extra-operations)
204
211
  - [Table API](#table-api)
@@ -234,6 +241,9 @@ Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interes
234
241
  - [Area Gallery](#area-gallery)
235
242
  - [Histogram](#histogram)
236
243
  - [Basic Transform](#basic-transform)
244
+ - [Login](#login)
245
+ - [Timer](#timer)
246
+ - [Color The Circles](#color-the-circles)
237
247
  - [Contributing to glimmer-dsl-libui](#contributing-to-glimmer-dsl-libui)
238
248
  - [Help](#help)
239
249
  - [Issues](#issues)
@@ -321,7 +331,7 @@ gem install glimmer-dsl-libui
321
331
  Or install via Bundler `Gemfile`:
322
332
 
323
333
  ```ruby
324
- gem 'glimmer-dsl-libui', '~> 0.1.9'
334
+ gem 'glimmer-dsl-libui', '~> 0.2.1'
325
335
  ```
326
336
 
327
337
  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.
@@ -429,6 +439,7 @@ Control(Args) | Properties | Listeners
429
439
  `msg_box(window = main_window as Glimmer::LibUI::WindowProxy, title as String, description as String)` | None | None
430
440
  `msg_box_error(window = main_window as Glimmer::LibUI::WindowProxy, title as String, description as String)` | None | None
431
441
  `non_wrapping_multiline_entry` | `read_only` (Boolean), `text` (`String`) | `on_changed`
442
+ `password_entry` | `read_only` (Boolean), `text` (`String`) | `on_changed`
432
443
  `path(draw_fill_mode = :winding)` | `fill` (`Hash` of `:r` as `0`-`255`, `:g` as `0`-`255`, `:b` as `0`-`255`, `:a` as `0.0`-`1.0`), `stroke` (`Hash` of `:r` as `0`-`255`, `:g` as `0`-`255`, `:b` as `0`-`255`, `:a` as `0.0`-`1.0`, `:cap` as (`:round`, `:square`, `:flat`), `:join` as (`:miter`, `:round`, `:bevel`), `:thickness` as `Numeric`, `:miter_limit` as `Numeric`, `:dashes` as `Array` of `Numeric` ) | None
433
444
  `preferences_menu_item` | None | `on_clicked`
434
445
  `progress_bar` | `value` (`Numeric`) | None
@@ -436,6 +447,7 @@ Control(Args) | Properties | Listeners
436
447
  `quit_menu_item` | None | `on_clicked`
437
448
  `radio_buttons` | `selected` (`Integer`) | `on_selected`
438
449
  `rectangle(x as Numeric, y as Numeric, width as Numeric, height as Numeric)` | `x` (`Numeric`), `y` (`Numeric`), `width` (`Numeric`), `height` (`Numeric`) | None
450
+ `search_entry` | `read_only` (Boolean), `text` (`String`) | `on_changed`
439
451
  `slider(min as Numeric, max as Numeric)` | `value` (`Numeric`) | `on_changed`
440
452
  `spinbox(min as Numeric, max as Numeric)` | `value` (`Numeric`) | `on_changed`
441
453
  `square(x as Numeric, y as Numeric, length as Numeric)` | `x` (`Numeric`), `y` (`Numeric`), `length` (`Numeric`) | None
@@ -445,6 +457,7 @@ Control(Args) | Properties | Listeners
445
457
  `text_column(name as String)` | `editable` (Boolean) | None
446
458
  `time_picker` | `time` (`Hash` of keys: `sec` as `Integer`, `min` as `Integer`, `hour` as `Integer`) | `on_changed`
447
459
  `vertical_box` | `padded` (Boolean) | None
460
+ `vertical_separator` | None | None
448
461
  `window(title as String, width as Integer, height as Integer, has_menubar as Boolean)` | `borderless` (Boolean), `content_size` (width `Numeric`, height `Numeric`), `fullscreen` (Boolean), `margined` (Boolean), `title` (`String`) | `on_closing`, `on_content_size_changed`, `on_destroy`
449
462
 
450
463
  ### Common Control Properties
@@ -460,9 +473,9 @@ Control(Args) | Properties | Listeners
460
473
  - `xspan` [dsl-only] (`Integer`) [default=`1`]: available in [Glimmer GUI DSL](#glimmer-gui-dsl-concepts) when nested under `grid`
461
474
  - `yspan` [dsl-only] (`Integer`) [default=`1`]: available in [Glimmer GUI DSL](#glimmer-gui-dsl-concepts) when nested under `grid`
462
475
  - `hexpand` [dsl-only] (Boolean) [default=`false`]: available in [Glimmer GUI DSL](#glimmer-gui-dsl-concepts) when nested under `grid`
463
- - `halign` [dsl-only] (`Integer`) [default=`0`]: available in [Glimmer GUI DSL](#glimmer-gui-dsl-concepts) when nested under `grid`
476
+ - `halign` [dsl-only] (`:fill`, `:start`, `:center`, or `:end`) [default=`:fill`]: available in [Glimmer GUI DSL](#glimmer-gui-dsl-concepts) when nested under `grid`
464
477
  - `vexpand` [dsl-only] (Boolean) [default=`false`]: available in [Glimmer GUI DSL](#glimmer-gui-dsl-concepts) when nested under `grid`
465
- - `valign` [dsl-only] (`Integer`) [default=`0`]: available in [Glimmer GUI DSL](#glimmer-gui-dsl-concepts) when nested under `grid`
478
+ - `valign` [dsl-only] (`:fill`, `:start`, `:center`, or `:end`) [default=`:fill`]: available in [Glimmer GUI DSL](#glimmer-gui-dsl-concepts) when nested under `grid`
466
479
 
467
480
  ### Common Control Operations
468
481
  - `destroy`
@@ -471,6 +484,13 @@ Control(Args) | Properties | Listeners
471
484
  - `hide`
472
485
  - `show`
473
486
 
487
+ ### LibUI Operations
488
+
489
+ All operations that could normally be called on `LibUI` can also be called on `Glimmer::LibUI`, but some have enhancements as detailed below.
490
+
491
+ - `Glimmer::LibUI::queue_main(&block)`: queues an operation to be run on the main event loop at the earliest opportunity possible
492
+ - `Glimmer::LibUI::timer(time_in_seconds=0.1, repeat: true, &block)`: calls block after time_in_seconds has elapsed, repeating indefinitely unless repeat is `false` or an `Integer` for finite number of repeats. Block can return `false` or `true` to override next repetition.
493
+
474
494
  ### Extra Dialogs
475
495
 
476
496
  - `open_file(window as Glimmer::LibUI::WindowProxy)`: returns selected file (`String`) or `nil` if cancelled
@@ -684,7 +704,9 @@ The `area_mouse_event` `Hash` argument for mouse events that receive it (e.g. `o
684
704
 
685
705
  The `area_key_event` `Hash` argument for keyboard events that receive it (e.g. `on_key_up`, `on_key_down`) consist of the following hash keys:
686
706
  - `:key`: key character (`String`)
687
- - `:ext_key`: non-character extra key (`Symbol`) such as `:left`, `:right`, `:escape`, `:insert`
707
+ - `:key_value`: key value (`Integer`). Useful in rare cases for numeric processing of keys instead of dealing with as `:key` character `String`
708
+ - `:ext_key`: non-character extra key (`Symbol`) from `Glimmer::LibUI.enum_symbols(:ext_key)` such as `:left`, `:right`, `:escape`, `:insert`
709
+ - `:ext_key_value`: non-character extra key value (`Integer`). Useful in rare cases for numeric processing of extra keys instead of dealing with as `:ext_key` `Symbol`
688
710
  - `:modifier`: modifier key pressed alone (e.g. `:shift` or `:control`)
689
711
  - `:modifiers`: modifier keys pressed simultaneously with `:key`, `:ext_key`, or `:modifier`
690
712
  - `:up`: indicates if key has been released or not (Boolean)
@@ -760,6 +782,8 @@ Note that `area`, `path`, and nested shapes are all truly declarative, meaning t
760
782
 
761
783
  `fill` and `stroke` accept [X11](https://en.wikipedia.org/wiki/X11_color_names) color `Symbol`s/`String`s like `:skyblue` and `'sandybrown'` or 6-number hex or 3-number hex-shorthand (as `Integer` or `String` with or without `0x` prefix)
762
784
 
785
+ Available [X11](https://en.wikipedia.org/wiki/X11_color_names) colors can be obtained through `Glimmer::LibUI.x11_colors` method.
786
+
763
787
  Check [Basic Transform](#basic-transform) example for use of [X11](https://en.wikipedia.org/wiki/X11_color_names) colors.
764
788
 
765
789
  Check [Histogram](#histogram) example for use of hex colors.
@@ -786,7 +810,7 @@ Check [Histogram](#histogram) example for use of hex colors.
786
810
  - When destroying a control nested under a `form`, it is automatically deleted from the form's children
787
811
  - When destroying a control nested under a `window` or `group`, it is automatically unset as their child to allow successful destruction
788
812
  - For `date_time_picker`, `date_picker`, and `time_picker`, make sure `time` hash values for `mon`, `wday`, and `yday` are 1-based instead of [libui](https://github.com/andlabs/libui) original 0-based values, and return `dst` as Boolean instead of `isdst` as `1`/`0`
789
- - Smart defaults for `grid` child attributes are `left` (`0`), `top` (`0`), `xspan` (`1`), `yspan` (`1`), `hexpand` (`false`), `halign` (`0`), `vexpand` (`false`), and `valign` (`0`)
813
+ - Smart defaults for `grid` child attributes are `left` (`0`), `top` (`0`), `xspan` (`1`), `yspan` (`1`), `hexpand` (`false`), `halign` (`:fill`), `vexpand` (`false`), and `valign` (`:fill`)
790
814
  - The `table` control automatically constructs required `TableModelHandler`, `TableModel`, and `TableParams`, calculating all their arguments from `cell_rows` and `editable` properties (e.g. `NumRows`) as well as nested columns (e.g. `text_column`)
791
815
  - Table model instances are automatically freed from memory after `window` is destroyed.
792
816
  - Table `cell_rows` data has implicit data-binding to table cell values for deletion, insertion, and change (done by diffing `cell_rows` value before and after change and auto-informing `table` of deletions [`LibUI.table_model_row_deleted`], insertions [`LibUI.table_model_row_deleted`], and changes [`LibUI.table_model_row_changed`]). When deleting data rows from `cell_rows` array, then actual rows from the `table` are automatically deleted. When inserting data rows into `cell_rows` array, then actual `table` rows are automatically inserted. When updating data rows in `cell_rows` array, then actual `table` rows are automatically updated.
@@ -1432,7 +1456,7 @@ class TinyMidiPlayer
1432
1456
  end
1433
1457
 
1434
1458
  def create_gui
1435
- menu('Help') { |m|
1459
+ menu('Help') {
1436
1460
  menu_item('Version') {
1437
1461
  on_clicked do
1438
1462
  show_version
@@ -2097,13 +2121,15 @@ ruby -r glimmer-dsl-libui -e "require 'examples/grid'"
2097
2121
 
2098
2122
  Mac
2099
2123
 
2100
- ![glimmer-dsl-libui-mac-grid-spanning.png](images/glimmer-dsl-libui-mac-grid-spanning.png)
2101
- ![glimmer-dsl-libui-mac-grid-expanding.png](images/glimmer-dsl-libui-mac-grid-expanding.png)
2124
+ ![glimmer-dsl-libui-mac-grid-span.png](images/glimmer-dsl-libui-mac-grid-span.png)
2125
+ ![glimmer-dsl-libui-mac-grid-expand.png](images/glimmer-dsl-libui-mac-grid-expand.png)
2126
+ ![glimmer-dsl-libui-mac-grid-align.png](images/glimmer-dsl-libui-mac-grid-align.png)
2102
2127
 
2103
2128
  Linux
2104
2129
 
2105
- ![glimmer-dsl-libui-linux-grid-spanning.png](images/glimmer-dsl-libui-linux-grid-spanning.png)
2106
- ![glimmer-dsl-libui-linux-grid-expanding.png](images/glimmer-dsl-libui-linux-grid-expanding.png)
2130
+ ![glimmer-dsl-libui-linux-grid-span.png](images/glimmer-dsl-libui-linux-grid-span.png)
2131
+ ![glimmer-dsl-libui-linux-grid-expand.png](images/glimmer-dsl-libui-linux-grid-expand.png)
2132
+ ![glimmer-dsl-libui-linux-grid-align.png](images/glimmer-dsl-libui-linux-grid-align.png)
2107
2133
 
2108
2134
  New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
2109
2135
 
@@ -2114,7 +2140,7 @@ include Glimmer
2114
2140
 
2115
2141
  window('Grid') {
2116
2142
  tab {
2117
- tab_item('Spanning') {
2143
+ tab_item('Span') {
2118
2144
  grid {
2119
2145
  4.times { |top_value|
2120
2146
  4.times { |left_value|
@@ -2154,7 +2180,7 @@ window('Grid') {
2154
2180
  }
2155
2181
  }
2156
2182
  }
2157
- tab_item('Expanding') {
2183
+ tab_item('Expand') {
2158
2184
  grid {
2159
2185
  label("(0, 0) hexpand/vexpand\nall available horizontal space is taken\nand\nall\navailable\nvertical\nspace\nis\ntaken") {
2160
2186
  left 0
@@ -2176,6 +2202,42 @@ window('Grid') {
2176
2202
  }
2177
2203
  }
2178
2204
  }
2205
+ tab_item('Align') {
2206
+ grid {
2207
+ label("(0, 0) halign/valign fill\nall available horizontal space is taken\nand\nall\navailable\nvertical\nspace\nis\ntaken") {
2208
+ left 0
2209
+ top 0
2210
+ hexpand true unless OS.mac? # on Mac, only the first label is given all space, so avoid expanding
2211
+ vexpand true unless OS.mac? # on Mac, only the first label is given all space, so avoid expanding
2212
+ halign :fill
2213
+ valign :fill
2214
+ }
2215
+ label("(1, 0) halign/valign start") {
2216
+ left 1
2217
+ top 0
2218
+ hexpand true unless OS.mac? # on Mac, only the first label is given all space, so avoid expanding
2219
+ vexpand true unless OS.mac? # on Mac, only the first label is given all space, so avoid expanding
2220
+ halign :start
2221
+ valign :start
2222
+ }
2223
+ label("(0, 1) halign/valign center") {
2224
+ left 0
2225
+ top 1
2226
+ hexpand true unless OS.mac? # on Mac, only the first label is given all space, so avoid expanding
2227
+ vexpand true unless OS.mac? # on Mac, only the first label is given all space, so avoid expanding
2228
+ halign :center
2229
+ valign :center
2230
+ }
2231
+ label("(1, 1) halign/valign end") {
2232
+ left 1
2233
+ top 1
2234
+ hexpand true unless OS.mac? # on Mac, only the first label is given all space, so avoid expanding
2235
+ vexpand true unless OS.mac? # on Mac, only the first label is given all space, so avoid expanding
2236
+ halign :end
2237
+ valign :end
2238
+ }
2239
+ }
2240
+ }
2179
2241
  }
2180
2242
  }.show
2181
2243
  ```
@@ -2955,15 +3017,19 @@ Mac
2955
3017
 
2956
3018
  ![glimmer-dsl-libui-mac-form-table.png](images/glimmer-dsl-libui-mac-form-table.png)
2957
3019
  ![glimmer-dsl-libui-mac-form-table-contact-entered.png](images/glimmer-dsl-libui-mac-form-table-contact-entered.png)
3020
+ ![glimmer-dsl-libui-mac-form-table-filtered.png](images/glimmer-dsl-libui-mac-form-table-filtered.png)
2958
3021
 
2959
3022
  Linux
2960
3023
 
2961
3024
  ![glimmer-dsl-libui-linux-form-table.png](images/glimmer-dsl-libui-linux-form-table.png)
2962
3025
  ![glimmer-dsl-libui-linux-form-table-contact-entered.png](images/glimmer-dsl-libui-linux-form-table-contact-entered.png)
3026
+ ![glimmer-dsl-libui-linux-form-table-filtered.png](images/glimmer-dsl-libui-linux-form-table-filtered.png)
2963
3027
 
2964
3028
  New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
2965
3029
 
2966
3030
  ```ruby
3031
+ # frozen_string_literal: true
3032
+
2967
3033
  require 'glimmer-dsl-libui'
2968
3034
 
2969
3035
  include Glimmer
@@ -3009,6 +3075,7 @@ window('Contacts', 600, 600) { |w|
3009
3075
  msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
3010
3076
  else
3011
3077
  data << new_row # automatically inserts a row into the table due to implicit data-binding
3078
+ @unfiltered_data = data.dup
3012
3079
  @name_entry.text = ''
3013
3080
  @email_entry.text = ''
3014
3081
  @phone_entry.text = ''
@@ -3018,6 +3085,25 @@ window('Contacts', 600, 600) { |w|
3018
3085
  end
3019
3086
  }
3020
3087
 
3088
+ search_entry { |se|
3089
+ stretchy false
3090
+
3091
+ on_changed do
3092
+ filter_value = se.text
3093
+ @unfiltered_data ||= data.dup
3094
+ # Unfilter first to remove any previous filters
3095
+ data.replace(@unfiltered_data) # affects table indirectly through implicit data-binding
3096
+ # Now, apply filter if entered
3097
+ unless filter_value.empty?
3098
+ data.filter! do |row_data| # affects table indirectly through implicit data-binding
3099
+ row_data.any? do |cell|
3100
+ cell.to_s.downcase.include?(filter_value.downcase)
3101
+ end
3102
+ end
3103
+ end
3104
+ end
3105
+ }
3106
+
3021
3107
  table {
3022
3108
  text_column('Name')
3023
3109
  text_column('Email')
@@ -3455,11 +3541,17 @@ window('Area Gallery', 400, 400) {
3455
3541
  stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
3456
3542
  }
3457
3543
  path { # declarative stable path
3458
- arc(200, 200, 90, 0, 360, false)
3544
+ circle(200, 200, 90)
3459
3545
 
3460
3546
  fill r: 202, g: 102, b: 204, a: 0.5
3461
3547
  stroke r: 0, g: 0, b: 0, thickness: 2
3462
3548
  }
3549
+ path { # declarative stable path
3550
+ arc(400, 220, 180, 90, 90, false)
3551
+
3552
+ fill r: 204, g: 102, b: 204, a: 0.5
3553
+ stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
3554
+ }
3463
3555
 
3464
3556
  on_mouse_event do |area_mouse_event|
3465
3557
  p area_mouse_event
@@ -3612,18 +3704,28 @@ window('Area Gallery', 400, 400) {
3612
3704
  stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
3613
3705
  }
3614
3706
  path { # declarative stable path
3615
- arc {
3707
+ circle {
3616
3708
  x_center 200
3617
3709
  y_center 200
3618
3710
  radius 90
3619
- start_angle 0
3620
- sweep 360
3621
- is_negative false
3622
3711
  }
3623
3712
 
3624
3713
  fill r: 202, g: 102, b: 204, a: 0.5
3625
3714
  stroke r: 0, g: 0, b: 0, thickness: 2
3626
3715
  }
3716
+ path { # declarative stable path
3717
+ arc {
3718
+ x_center 400
3719
+ y_center 220
3720
+ radius 180
3721
+ start_angle 90
3722
+ sweep 90
3723
+ is_negative false
3724
+ }
3725
+
3726
+ fill r: 204, g: 102, b: 204, a: 0.5
3727
+ stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
3728
+ }
3627
3729
 
3628
3730
  on_mouse_event do |area_mouse_event|
3629
3731
  p area_mouse_event
@@ -3723,13 +3825,19 @@ window('Area Gallery', 400, 400) {
3723
3825
  stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
3724
3826
  }
3725
3827
  path { # a dynamic path is added semi-declaratively inside on_draw block
3726
- arc(200, 200, 90, 0, 360, false)
3727
-
3828
+ circle(200, 200, 90)
3829
+
3728
3830
  fill r: 202, g: 102, b: 204, a: 0.5
3729
3831
  stroke r: 0, g: 0, b: 0, thickness: 2
3730
3832
  }
3833
+ path { # a dynamic path is added semi-declaratively inside on_draw block
3834
+ arc(400, 220, 180, 90, 90, false)
3835
+
3836
+ fill r: 204, g: 102, b: 204, a: 0.5
3837
+ stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
3838
+ }
3731
3839
  end
3732
-
3840
+
3733
3841
  on_mouse_event do |area_mouse_event|
3734
3842
  p area_mouse_event
3735
3843
  end
@@ -3882,20 +3990,30 @@ window('Area Gallery', 400, 400) {
3882
3990
  stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
3883
3991
  }
3884
3992
  path { # a dynamic path is added semi-declaratively inside on_draw block
3885
- arc {
3993
+ circle {
3886
3994
  x_center 200
3887
3995
  y_center 200
3888
3996
  radius 90
3889
- start_angle 0
3890
- sweep 360
3891
- is_negative false
3892
3997
  }
3893
-
3998
+
3894
3999
  fill r: 202, g: 102, b: 204, a: 0.5
3895
4000
  stroke r: 0, g: 0, b: 0, thickness: 2
3896
4001
  }
4002
+ path { # a dynamic path is added semi-declaratively inside on_draw block
4003
+ arc {
4004
+ x_center 400
4005
+ y_center 220
4006
+ radius 180
4007
+ start_angle 90
4008
+ sweep 90
4009
+ is_negative false
4010
+ }
4011
+
4012
+ fill r: 204, g: 102, b: 204, a: 0.5
4013
+ stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
4014
+ }
3897
4015
  end
3898
-
4016
+
3899
4017
  on_mouse_event do |area_mouse_event|
3900
4018
  p area_mouse_event
3901
4019
  end
@@ -4344,6 +4462,483 @@ window('Basic Transform', 350, 350) {
4344
4462
  }.show
4345
4463
  ```
4346
4464
 
4465
+ ### Login
4466
+
4467
+ [examples/login.rb](examples/login.rb)
4468
+
4469
+ Run with this command from the root of the project if you cloned the project:
4470
+
4471
+ ```
4472
+ ruby -r './lib/glimmer-dsl-libui' examples/login.rb
4473
+ ```
4474
+
4475
+ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
4476
+
4477
+ ```
4478
+ ruby -r glimmer-dsl-libui -e "require 'examples/login'"
4479
+ ```
4480
+
4481
+ Mac
4482
+
4483
+ ![glimmer-dsl-libui-mac-login.png](images/glimmer-dsl-libui-mac-login.png)
4484
+ ![glimmer-dsl-libui-mac-login-logged-in.png](images/glimmer-dsl-libui-mac-login-logged-in.png)
4485
+
4486
+ Linux
4487
+
4488
+ ![glimmer-dsl-libui-linux-login.png](images/glimmer-dsl-libui-linux-login.png)
4489
+ ![glimmer-dsl-libui-linux-login-logged-in.png](images/glimmer-dsl-libui-linux-login-logged-in.png)
4490
+
4491
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
4492
+
4493
+ ```ruby
4494
+ require 'glimmer-dsl-libui'
4495
+
4496
+ include Glimmer
4497
+
4498
+ window('Login') {
4499
+ margined true
4500
+
4501
+ vertical_box {
4502
+ form {
4503
+ @username_entry = entry {
4504
+ label 'Username:'
4505
+ }
4506
+
4507
+ @password_entry = password_entry {
4508
+ label 'Password:'
4509
+ }
4510
+ }
4511
+
4512
+ horizontal_box {
4513
+ @login_button = button('Login') {
4514
+ on_clicked do
4515
+ @username_entry.enabled = false
4516
+ @password_entry.enabled = false
4517
+ @login_button.enabled = false
4518
+ @logout_button.enabled = true
4519
+ end
4520
+ }
4521
+
4522
+ @logout_button = button('Logout') {
4523
+ enabled false
4524
+
4525
+ on_clicked do
4526
+ @username_entry.text = ''
4527
+ @password_entry.text = ''
4528
+ @username_entry.enabled = true
4529
+ @password_entry.enabled = true
4530
+ @login_button.enabled = true
4531
+ @logout_button.enabled = false
4532
+ end
4533
+ }
4534
+ }
4535
+ }
4536
+ }.show
4537
+ ```
4538
+
4539
+ ### Timer
4540
+
4541
+ [examples/timer.rb](examples/timer.rb)
4542
+
4543
+ Run with this command from the root of the project if you cloned the project:
4544
+
4545
+ ```
4546
+ ruby -r './lib/glimmer-dsl-libui' examples/timer.rb
4547
+ ```
4548
+
4549
+ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
4550
+
4551
+ ```
4552
+ ruby -r glimmer-dsl-libui -e "require 'examples/timer'"
4553
+ ```
4554
+
4555
+ Mac
4556
+
4557
+ ![glimmer-dsl-libui-mac-timer.png](images/glimmer-dsl-libui-mac-timer.png)
4558
+ ![glimmer-dsl-libui-mac-timer-in-progress.png](images/glimmer-dsl-libui-mac-timer-in-progress.png)
4559
+
4560
+ Linux
4561
+
4562
+ ![glimmer-dsl-libui-linux-timer.png](images/glimmer-dsl-libui-linux-timer.png)
4563
+ ![glimmer-dsl-libui-linux-timer-in-progress.png](images/glimmer-dsl-libui-linux-timer-in-progress.png)
4564
+
4565
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
4566
+
4567
+ ```ruby
4568
+ # frozen_string_literal: true
4569
+
4570
+ require 'glimmer-dsl-libui'
4571
+
4572
+ class Timer
4573
+ include Glimmer
4574
+
4575
+ SECOND_MAX = 59
4576
+ MINUTE_MAX = 59
4577
+ HOUR_MAX = 23
4578
+
4579
+ def initialize
4580
+ @pid = nil
4581
+ @midi_file = File.expand_path('../sounds/AlanWalker-Faded.mid', __dir__)
4582
+ at_exit { stop_midi }
4583
+ setup_timer
4584
+ create_gui
4585
+ end
4586
+
4587
+ def stop_midi
4588
+ if @pid
4589
+ if @th.alive?
4590
+ Process.kill(:SIGKILL, @pid)
4591
+ @pid = nil
4592
+ else
4593
+ @pid = nil
4594
+ end
4595
+ end
4596
+ end
4597
+
4598
+ def play_midi
4599
+ stop_midi
4600
+ if @pid.nil?
4601
+ begin
4602
+ @pid = spawn "timidity -G 0.0-10.0 #{@midi_file}"
4603
+ @th = Process.detach @pid
4604
+ rescue Errno::ENOENT
4605
+ warn 'Timidty++ not found. Please install Timidity++.'
4606
+ warn 'https://sourceforge.net/projects/timidity/'
4607
+ end
4608
+ end
4609
+ end
4610
+
4611
+ def setup_timer
4612
+ unless @setup_timer
4613
+ Glimmer::LibUI.timer(1) do
4614
+ if @started
4615
+ seconds = @sec_spinbox.value
4616
+ minutes = @min_spinbox.value
4617
+ hours = @hour_spinbox.value
4618
+ if seconds > 0
4619
+ @sec_spinbox.value = seconds -= 1
4620
+ end
4621
+ if seconds == 0
4622
+ if minutes > 0
4623
+ @min_spinbox.value = minutes -= 1
4624
+ @sec_spinbox.value = seconds = SECOND_MAX
4625
+ end
4626
+ if minutes == 0
4627
+ if hours > 0
4628
+ @hour_spinbox.value = hours -= 1
4629
+ @min_spinbox.value = minutes = MINUTE_MAX
4630
+ @sec_spinbox.value = seconds = SECOND_MAX
4631
+ end
4632
+ if hours == 0 && minutes == 0 && seconds == 0
4633
+ @start_button.enabled = true
4634
+ @stop_button.enabled = false
4635
+ @started = false
4636
+ unless @played
4637
+ play_midi
4638
+ @played = true
4639
+ end
4640
+ end
4641
+ end
4642
+ end
4643
+ end
4644
+ end
4645
+ @setup_timer = true
4646
+ end
4647
+ end
4648
+
4649
+ def create_gui
4650
+ window('Timer') {
4651
+ margined true
4652
+
4653
+ group('Countdown') {
4654
+ vertical_box {
4655
+ horizontal_box {
4656
+ @hour_spinbox = spinbox(0, HOUR_MAX) {
4657
+ stretchy false
4658
+ value 0
4659
+ }
4660
+ label(':') {
4661
+ stretchy false
4662
+ }
4663
+ @min_spinbox = spinbox(0, MINUTE_MAX) {
4664
+ stretchy false
4665
+ value 0
4666
+ }
4667
+ label(':') {
4668
+ stretchy false
4669
+ }
4670
+ @sec_spinbox = spinbox(0, SECOND_MAX) {
4671
+ stretchy false
4672
+ value 0
4673
+ }
4674
+ }
4675
+ horizontal_box {
4676
+ @start_button = button('Start') {
4677
+ on_clicked do
4678
+ @start_button.enabled = false
4679
+ @stop_button.enabled = true
4680
+ @started = true
4681
+ @played = false
4682
+ end
4683
+ }
4684
+
4685
+ @stop_button = button('Stop') {
4686
+ enabled false
4687
+
4688
+ on_clicked do
4689
+ @start_button.enabled = true
4690
+ @stop_button.enabled = false
4691
+ @started = false
4692
+ end
4693
+ }
4694
+ }
4695
+ }
4696
+ }
4697
+ }.show
4698
+ end
4699
+ end
4700
+
4701
+ Timer.new
4702
+ ```
4703
+
4704
+ ### Color The Circles
4705
+
4706
+ [examples/color_the_circles.rb](examples/color_the_circles.rb)
4707
+
4708
+ Run with this command from the root of the project if you cloned the project:
4709
+
4710
+ ```
4711
+ ruby -r './lib/glimmer-dsl-libui' examples/color_the_circles.rb
4712
+ ```
4713
+
4714
+ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
4715
+
4716
+ ```
4717
+ ruby -r glimmer-dsl-libui -e "require 'examples/color_the_circles'"
4718
+ ```
4719
+
4720
+ Mac
4721
+
4722
+ ![glimmer-dsl-libui-mac-color-the-circles.png](images/glimmer-dsl-libui-mac-color-the-circles.png)
4723
+ ![glimmer-dsl-libui-mac-color-the-circles-lost.png](images/glimmer-dsl-libui-mac-color-the-circles-lost.png)
4724
+ ![glimmer-dsl-libui-mac-color-the-circles-won.png](images/glimmer-dsl-libui-mac-color-the-circles-won.png)
4725
+
4726
+ Linux
4727
+
4728
+ ![glimmer-dsl-libui-linux-color-the-circles.png](images/glimmer-dsl-libui-linux-color-the-circles.png)
4729
+ ![glimmer-dsl-libui-linux-color-the-circles-lost.png](images/glimmer-dsl-libui-linux-color-the-circles-lost.png)
4730
+ ![glimmer-dsl-libui-linux-color-the-circles-won.png](images/glimmer-dsl-libui-linux-color-the-circles-won.png)
4731
+
4732
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
4733
+
4734
+ ```ruby
4735
+ require 'glimmer-dsl-libui'
4736
+
4737
+ class ColorTheCircles
4738
+ include Glimmer
4739
+
4740
+ WINDOW_WIDTH = 800
4741
+ WINDOW_HEIGHT = 600
4742
+ CIRCLE_MIN_RADIUS = 15
4743
+ CIRCLE_MAX_RADIUS = 50
4744
+ MARGIN_WIDTH = 55
4745
+ MARGIN_HEIGHT = 155
4746
+ TIME_MAX_EASY = 4
4747
+ TIME_MAX_MEDIUM = 3
4748
+ TIME_MAX_HARD = 2
4749
+ TIME_MAX_INSANE = 1
4750
+
4751
+ attr_accessor :score
4752
+
4753
+ def initialize
4754
+ @circles_data = []
4755
+ @score = 0
4756
+ @time_max = TIME_MAX_HARD
4757
+ register_observers
4758
+ setup_circle_factory
4759
+ end
4760
+
4761
+ def register_observers
4762
+ observer = Glimmer::DataBinding::Observer.proc do |new_score|
4763
+ @score_label.text = new_score.to_s
4764
+ if new_score == -20
4765
+ msg_box('You Lost!', 'Sorry! Your score reached -20')
4766
+ restart_game
4767
+ elsif new_score == 0
4768
+ msg_box('You Won!', 'Congratulations! Your score reached 0')
4769
+ restart_game
4770
+ end
4771
+ end
4772
+ observer.observe(self, :score) # automatically enhances self to become Glimmer::DataBinding::ObservableModel and notify observer on score attribute changes
4773
+ end
4774
+
4775
+ def setup_circle_factory
4776
+ consumer = Proc.new do
4777
+ if @circles_data.empty?
4778
+ # start with 3 circles to make more challenging
4779
+ add_circle until @circles_data.size > 3
4780
+ else
4781
+ add_circle
4782
+ end
4783
+ delay = rand * @time_max
4784
+ Glimmer::LibUI.timer(delay, repeat: false, &consumer)
4785
+ end
4786
+ Glimmer::LibUI.queue_main(&consumer)
4787
+ end
4788
+
4789
+ def add_circle
4790
+ circle_x_center = rand * (WINDOW_WIDTH - MARGIN_WIDTH - CIRCLE_MAX_RADIUS) + CIRCLE_MAX_RADIUS
4791
+ circle_y_center = rand * (WINDOW_HEIGHT - MARGIN_HEIGHT - CIRCLE_MAX_RADIUS) + CIRCLE_MAX_RADIUS
4792
+ circle_radius = rand * (CIRCLE_MAX_RADIUS - CIRCLE_MIN_RADIUS) + CIRCLE_MIN_RADIUS
4793
+ stroke_color = Glimmer::LibUI.x11_colors.sample
4794
+ @circles_data << {
4795
+ args: [circle_x_center, circle_y_center, circle_radius],
4796
+ fill: nil,
4797
+ stroke: stroke_color
4798
+ }
4799
+ @area.queue_redraw_all
4800
+ self.score -= 1 # notifies score observers automatically of change
4801
+ end
4802
+
4803
+ def restart_game
4804
+ @score = 0 # update variable directly to avoid notifying observers
4805
+ @circles_data.clear
4806
+ end
4807
+
4808
+ def launch
4809
+ menu('Actions') {
4810
+ menu_item('Restart') {
4811
+ on_clicked do
4812
+ restart_game
4813
+ end
4814
+ }
4815
+
4816
+ quit_menu_item
4817
+ }
4818
+
4819
+ menu('Difficulty') {
4820
+ radio_menu_item('Easy') {
4821
+ on_clicked do
4822
+ @time_max = TIME_MAX_EASY
4823
+ end
4824
+ }
4825
+
4826
+ radio_menu_item('Medium') {
4827
+ on_clicked do
4828
+ @time_max = TIME_MAX_MEDIUM
4829
+ end
4830
+ }
4831
+
4832
+ radio_menu_item('Hard') {
4833
+ checked true
4834
+
4835
+ on_clicked do
4836
+ @time_max = TIME_MAX_HARD
4837
+ end
4838
+ }
4839
+
4840
+ radio_menu_item('Insane') {
4841
+ on_clicked do
4842
+ @time_max = TIME_MAX_INSANE
4843
+ end
4844
+ }
4845
+ }
4846
+
4847
+ menu('Help') {
4848
+ menu_item('Instructions') {
4849
+ on_clicked do
4850
+ msg_box('Instructions', "Score goes down as circles are added.\nIf it reaches -20, you lose!\n\nClick circles to color and score!\nOnce score reaches 0, you win!\n\nBeware of concealed light-colored circles!\nThey are revealed once darker circles intersect them.\n\nThere are four levels of difficulty.\nChange via difficulty menu if the game gets too tough.")
4851
+ end
4852
+ }
4853
+ }
4854
+
4855
+ window('Color The Circles', WINDOW_WIDTH, WINDOW_HEIGHT) {
4856
+ margined true
4857
+
4858
+ grid {
4859
+ button('Restart') {
4860
+ left 0
4861
+ top 0
4862
+ halign :center
4863
+
4864
+ on_clicked do
4865
+ restart_game
4866
+ end
4867
+ }
4868
+
4869
+ label('Score goes down as circles are added. If it reaches -20, you lose!') {
4870
+ left 0
4871
+ top 1
4872
+ halign :center
4873
+ }
4874
+
4875
+ label('Click circles to color and score! Once score reaches 0, you win!') {
4876
+ left 0
4877
+ top 2
4878
+ halign :center
4879
+ }
4880
+
4881
+ horizontal_box {
4882
+ left 0
4883
+ top 3
4884
+ halign :center
4885
+
4886
+ label('Score:') {
4887
+ stretchy false
4888
+ }
4889
+
4890
+ @score_label = label(@score.to_s) {
4891
+ stretchy false
4892
+ }
4893
+ }
4894
+
4895
+ vertical_box {
4896
+ left 0
4897
+ top 4
4898
+ hexpand true
4899
+ vexpand true
4900
+ halign :fill
4901
+ valign :fill
4902
+
4903
+ @area = area {
4904
+ on_draw do |area_draw_params|
4905
+ path {
4906
+ rectangle(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT)
4907
+
4908
+ fill :white
4909
+ }
4910
+
4911
+ @circles_data.each do |circle_data|
4912
+ path {
4913
+ circle_data[:circle] = circle(*circle_data[:args])
4914
+
4915
+ fill circle_data[:fill]
4916
+ stroke circle_data[:stroke]
4917
+ }
4918
+ end
4919
+ end
4920
+
4921
+ on_mouse_down do |area_mouse_event|
4922
+ clicked_circle_data = @circles_data.find do |circle_data|
4923
+ circle_data[:fill].nil? && circle_data[:circle].include?(area_mouse_event[:x], area_mouse_event[:y])
4924
+ end
4925
+ if clicked_circle_data
4926
+ clicked_circle_data[:fill] = clicked_circle_data[:stroke]
4927
+ @area.queue_redraw_all
4928
+ self.score += 1 # notifies score observers automatically of change
4929
+ end
4930
+ end
4931
+ }
4932
+ }
4933
+
4934
+ }
4935
+ }.show
4936
+ end
4937
+ end
4938
+
4939
+ ColorTheCircles.new.launch
4940
+ ```
4941
+
4347
4942
  ## Contributing to glimmer-dsl-libui
4348
4943
 
4349
4944
  - Check out the latest master to make sure the feature hasn't been