glimmer-dsl-libui 0.5.13 → 0.5.16

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3ec966c5f1dcd6315fe94455e2a8257df7ed6274ada0d74419e53b9abeab99bb
4
- data.tar.gz: 5f1f410e8a239735c1188d4a7bdad00f9fed6e34ba71ba5c2ac65f01b8f49037
3
+ metadata.gz: 99ed9eb0a65da64d13717cf3f967ceadc36c4a0eed74fcbb385bd5d19c812db2
4
+ data.tar.gz: f24060ff8ef38a93c1f3174d3e01102d61d2c0c8d8466b1e0d32fedc8972c9ea
5
5
  SHA512:
6
- metadata.gz: 574718284c8933f27d088777e9ffd77a37922f3f51ec90e85026f1d86f572c84bea4d86141fc3b67bb5192769ac03e13dca791b3ce6b8d2ae88235008ba4cbcf
7
- data.tar.gz: 87656800ed048be0ffc15520e5988b911fac60bcdb73db16fd962e5820d2d2c948a08a14c1c22aaef4543623d8f2c803c7d10e0c106bc40c13942bddaf83cfda
6
+ metadata.gz: def5540396a5cffc5f1271d3bb2664d008cf26db2f88207dc75bc23fa9915a2badfa45db8778e59644c99cbb0753147361556de25d118acd51e2dc211abd4bc8
7
+ data.tar.gz: 30ac47a92b4439aca5f3bc244514b400f810d8a9c855f64acf924544b448647d0d573d5218bc2e515ceb7bb469978b1e3e754d74d3ff86aea0b7d7247ee0e433
data/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Change Log
2
2
 
3
+ ## 0.5.16
4
+
5
+ - `refined_table` custom control that renders a `table` with filtering and pagination, thus being able to handle a large data set (e.g. 50,000)
6
+ - New examples/paginated_refined_table.rb
7
+
8
+ ## 0.5.15
9
+
10
+ - Fix an issue with rendering table content changes when the number of rows changes with new content that is not a subset of the old content or a container of it
11
+
12
+ ## 0.5.14
13
+
14
+ - Basic Child Window example
15
+ - Modify default window closing behavior to autodetect if the window is a child window (not the main window), and if closed, ensure that does not quit the app.
16
+ - Accept `true` and `false` as return values for `on_closing` window listener as aliases to `1` and `0`
17
+
3
18
  ## 0.5.13
4
19
 
5
20
  - Fix issue with rendering table content changes when having many rows
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.5.13
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.5.16
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
  [![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)
@@ -385,6 +385,7 @@ DSL | Platforms | Native? | Vector Graphics? | Pros | Cons | Prereqs
385
385
  - [Examples](#examples)
386
386
  - [Basic Examples](#basic-examples)
387
387
  - [Basic Window](#basic-window)
388
+ - [Basic Child Window](#basic-child-window)
388
389
  - [Basic Button](#basic-button)
389
390
  - [Basic Entry](#basic-entry)
390
391
  - [Simple Notepad](#simple-notepad)
@@ -417,6 +418,7 @@ DSL | Platforms | Native? | Vector Graphics? | Pros | Cons | Prereqs
417
418
  - [Editable Column Table](#editable-column-table)
418
419
  - [Editable Table](#editable-table)
419
420
  - [Form Table](#form-table)
421
+ - [Paginated Refined Table](#paginated-refined-table)
420
422
  - [Grid](#grid)
421
423
  - [Histogram](#histogram)
422
424
  - [Login](#login)
@@ -435,6 +437,7 @@ DSL | Platforms | Native? | Vector Graphics? | Pros | Cons | Prereqs
435
437
  - [i3off Gtk Ruby](#i3off-gtk-ruby)
436
438
  - [Chess](#chess)
437
439
  - [RubyCrumbler](#rubycrumbler)
440
+ - [Rubio-Radio](#rubio-radio)
438
441
  - [Process](#process)
439
442
  - [Resources](#resources)
440
443
  - [Help](#help)
@@ -581,7 +584,7 @@ gem install glimmer-dsl-libui
581
584
  Or install via Bundler `Gemfile`:
582
585
 
583
586
  ```ruby
584
- gem 'glimmer-dsl-libui', '~> 0.5.13'
587
+ gem 'glimmer-dsl-libui', '~> 0.5.16'
585
588
  ```
586
589
 
587
590
  Test that installation worked by running the [Meta-Example](#examples):
@@ -763,6 +766,7 @@ Keyword(Args) | Properties | Listeners
763
766
  `quit_menu_item` | None | `on_clicked`
764
767
  `radio_buttons` | `selected` (`Integer`) | `on_selected`
765
768
  `rectangle(x as Numeric, y as Numeric, width as Numeric, height as Numeric)` | `x` (`Numeric`), `y` (`Numeric`), `width` (`Numeric`), `height` (`Numeric`) | None
769
+ `refined_table` | (EARLY ALPHA UNSTABLE API / CHECK SOURCE CODE FOR DETAILS) | (EARLY ALPHA UNSTABLE API / CHECK SOURCE CODE FOR DETAILS)
766
770
  `scrolling_area(width = main_window.width, height = main_window.height)` | `auto_draw_enabled` (Boolean), `size` (`Array` of `width` (`Numeric`) and `height` (`Numeric`)), `width` (`Numeric`), `height` (`Numeric`) | `on_draw(area_draw_params)`, `on_mouse_event(area_mouse_event)`, `on_mouse_down(area_mouse_event)`, `on_mouse_up(area_mouse_event)`, `on_mouse_drag_started(area_mouse_event)`, `on_mouse_dragged(area_mouse_event)`, `on_mouse_dropped(area_mouse_event)`, `on_mouse_entered`, `on_mouse_exited`, `on_key_event(area_key_event)`, `on_key_down(area_key_event)`, `on_key_up(area_key_event)`
767
771
  `search_entry` | `read_only` (Boolean), `text` (`String`) | `on_changed`
768
772
  `separator_menu_item` | None | None
@@ -990,6 +994,40 @@ Mac | Windows | Linux
990
994
 
991
995
  Learn more by checking out [examples](#examples).
992
996
 
997
+ #### Refined Table
998
+
999
+ [EARLY ALPHA FEATURE]
1000
+
1001
+ `refined_table` is a custom control provided exclusively by Glimmer DSL for LibUI that includes filtering and pagination support out of the box and can handle very large amounts of data (e.g. 50,000 rows).
1002
+
1003
+ It is currently an early alpha feature, so please test-drive and report issues if you encounter any.
1004
+
1005
+ Also, the API might undergo big changes, so please keep that in mind.
1006
+
1007
+ Example code:
1008
+
1009
+ ```ruby
1010
+ refined_table(
1011
+ model_array: contacts,
1012
+ table_columns: {
1013
+ 'Name' => {button: {on_clicked: ->(row) {puts row}}},
1014
+ 'Colored Email' => :text_color,
1015
+ 'Phone' => {text: {editable: true}},
1016
+ 'City' => :text,
1017
+ 'State' => :text,
1018
+ },
1019
+ table_editable: true,
1020
+ per_page: 20, # row count per page
1021
+ page: 5, # initial page
1022
+ )
1023
+ ```
1024
+
1025
+ Mac | Windows | Linux
1026
+ ----|---------|------
1027
+ ![glimmer-dsl-libui-mac-paginated-refined-table.png](images/glimmer-dsl-libui-mac-paginated-refined-table.png)| ![glimmer-dsl-libui-windows-paginated-refined-table.png](images/glimmer-dsl-libui-windows-paginated-refined-table.png)| ![glimmer-dsl-libui-linux-paginated-refined-table.png](images/glimmer-dsl-libui-linux-paginated-refined-table.png)
1028
+
1029
+ Learn more in the [Paginated Refined Table](#paginated-refined-table) example.
1030
+
993
1031
  ### Area API
994
1032
 
995
1033
  The `area` control is a canvas-like control for drawing paths that can be used in one of two ways:
@@ -1615,8 +1653,7 @@ SpinnerExample.new.launch
1615
1653
  - All controls nested under a `horizontal_box`, `vertical_box`, and `form` have `stretchy` property (fill maximum space) as `true` by default (passed to `box_append`/`form_append` method)
1616
1654
  - If an event listener is repeated under a control (e.g. two `on_clicked {}` listeners under `button`), it does not overwrite the previous listener, yet it is added to an `Array` of listeners for the event. [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) provides multiple-event-listener support unlike [LibUI](https://github.com/kojix2/LibUI)
1617
1655
  - `window` instatiation args can be left off, having the following defaults when unspecified: `title` as `''`, `width` as `190`, `height` as `150`, and `has_menubar` as `true`)
1618
- - `window` has an `on_closing` listener by default that quits application upon hitting the close button (can be overridden with a manual `on_closing` implementation that returns integer `1` for closing and `0` for remaining open). When creating/showing a child window other than the main window and then closing it, the entire app closes by default unless you implement an `on_closing` listener on the secondary child window that returns `1` at the end.
1619
-
1656
+ - `window` has an `on_closing` listener by default that quits application upon hitting close button if window is the main window, not a child window (the behavior can be overridden with a manual `on_closing` implementation that returns integer `1`/`true` for closing and `0`/`false` for remaining open).
1620
1657
  - `group` has `title` property default to `''` if not specified in instantiation args, so it can be instantiated without args with `title` property specified in nested block (e.g. `group {title 'Address'; ...}`)
1621
1658
  - `button`, `checkbox`, and `label` have `text` default to `''` if not specified in instantiation args, so they can be instantiated without args with `text` property specified in nested block (e.g. `button {text 'Greet'; on_clicked {puts 'Hello'}}`)
1622
1659
  - `quit_menu_item` has an `on_clicked` listener by default that quits application upon selecting the quit menu item (can be overridden with a manual `on_clicked` implementation that returns integer `0` for success)
@@ -1905,7 +1942,7 @@ Learn more from custom keyword usage in [Method-Based Custom Keyword](#method-ba
1905
1942
 
1906
1943
  The [Observer Design Pattern](https://en.wikipedia.org/wiki/Observer_pattern) (a.k.a. Observer Pattern) is fundamental to building GUIs (Graphical User Interfaces) following the [MVC (Model View Controller) Architectural Pattern](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) or any of its variations like [MVP (Model View Presenter)](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93presenter). In the original Smalltalk-MVC, the View observes the Model for changes and updates itself accordingly.
1907
1944
 
1908
- ![MVC - Model View Controller](https://www.researchgate.net/profile/Danny-Weyns/publication/269303611/figure/fig2/AS:858133056462866@1581606272800/Smalltalk80-MVC-pattern-View-and-Controller-work-as-a-pair-allowing-the-user-to-interact.ppm)
1945
+ ![MVC - Model View Controller](https://developer.mozilla.org/en-US/docs/Glossary/MVC/model-view-controller-light-blue.png)
1909
1946
 
1910
1947
  [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) supports the [Observer Design Pattern](https://en.wikipedia.org/wiki/Observer_pattern) via the `observe(model, attribute_or_key=nil)` keyword, which can observe `Object` models with attributes, `Hash`es with keys, and `Array`s. It automatically enhances objects as needed to support automatically notifying observers of changes via `observable#notify_observers(attribute_or_key = nil)` method:
1911
1948
  - `Object` becomes `Glimmer::DataBinding::ObservableModel`, which supports observing specified `Object` model attributes.
@@ -1936,7 +1973,7 @@ Data-binding automatically takes advantage of the [Observer Pattern](#observer-p
1936
1973
 
1937
1974
  Data-binding supports utilizing the [MVP (Model View Presenter)](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93presenter) flavor of [MVC](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) by observing both the View and a Presenter for changes and updating the opposite side upon encountering them. This enables writing more decoupled cleaner code that keeps View code and Model code disentangled and highly maintainable. For example, check out the Snake game presenters for [Grid](/examples/snake/presenter/grid.rb) and [Cell](/examples/snake/presenter/cell.rb), which act as proxies for the actual Snake game models [Snake](/examples/snake/model/snake.rb) and [Apple](/examples/snake/model/apple.rb), mediating synchronization of data between them and the [Snake View GUI](/examples/snake.rb).
1938
1975
 
1939
- ![MVP](https://www.researchgate.net/profile/Gilles-Perrouin/publication/320249584/figure/fig8/AS:668260987068418@1536337243385/Model-view-presenter-architecture.png)
1976
+ ![MVP](https://upload.wikimedia.org/wikipedia/commons/d/dc/Model_View_Presenter_GUI_Design_Pattern.png)
1940
1977
 
1941
1978
  #### Bidirectional (Two-Way) Data-Binding
1942
1979
 
@@ -2643,6 +2680,50 @@ window { # first 3 args can be set via properties with 4th arg has_menubar=true
2643
2680
  }.show
2644
2681
  ```
2645
2682
 
2683
+ #### Basic Child Window
2684
+
2685
+ [examples/basic_child_window.rb](examples/basic_child_window.rb)
2686
+
2687
+ Run with this command from the root of the project if you cloned the project:
2688
+
2689
+ ```
2690
+ ruby -r './lib/glimmer-dsl-libui' examples/basic_child_window.rb
2691
+ ```
2692
+
2693
+ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
2694
+
2695
+ ```
2696
+ ruby -r glimmer-dsl-libui -e "require 'examples/basic_child_window.rb'"
2697
+ ```
2698
+
2699
+ Mac | Windows | Linux
2700
+ ----|---------|------
2701
+ ![glimmer-dsl-libui-mac-basic-child-window.png](images/glimmer-dsl-libui-mac-basic-child-window.png) ![glimmer-dsl-libui-mac-basic-child-window-open.png](images/glimmer-dsl-libui-mac-basic-child-window-open.png) | ![glimmer-dsl-libui-windows-basic-child-window.png](images/glimmer-dsl-libui-windows-basic-child-window.png) ![glimmer-dsl-libui-windows-basic-child-window-open.png](images/glimmer-dsl-libui-windows-basic-child-window-open.png) | ![glimmer-dsl-libui-linux-basic-child-window.png](images/glimmer-dsl-libui-linux-basic-child-window.png) ![glimmer-dsl-libui-linux-basic-child-window-open.png](images/glimmer-dsl-libui-linux-basic-child-window-open.png)
2702
+
2703
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
2704
+
2705
+ ```ruby
2706
+ require 'glimmer-dsl-libui'
2707
+
2708
+ include Glimmer
2709
+
2710
+ window('Main Window') {
2711
+ button('Spawn Child Window') {
2712
+ on_clicked do
2713
+ window('Child Window') {
2714
+ on_closing do
2715
+ puts 'Child window is closing'
2716
+ end
2717
+ }.show
2718
+ end
2719
+ }
2720
+
2721
+ on_closing do
2722
+ puts 'Main window is closing'
2723
+ end
2724
+ }.show
2725
+ ```
2726
+
2646
2727
  #### Basic Button
2647
2728
 
2648
2729
  [examples/basic_button.rb](examples/basic_button.rb)
@@ -7934,6 +8015,273 @@ window('Contacts', 600, 600) {
7934
8015
  }.show
7935
8016
  ```
7936
8017
 
8018
+ #### Paginated Refined Table
8019
+
8020
+ [examples/paginated_refined_table.rb](examples/paginated_refined_table.rb)
8021
+
8022
+ Run with this command from the root of the project if you cloned the project:
8023
+
8024
+ ```
8025
+ ruby -r './lib/glimmer-dsl-libui' examples/paginated_refined_table.rb
8026
+ ```
8027
+
8028
+ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
8029
+
8030
+ ```
8031
+ ruby -r glimmer-dsl-libui -e "require 'examples/paginated_refined_table'"
8032
+ ```
8033
+
8034
+ Mac | Windows | Linux
8035
+ ----|---------|------
8036
+ ![glimmer-dsl-libui-mac-paginated-refined-table.png](images/glimmer-dsl-libui-mac-paginated-refined-table.png)| ![glimmer-dsl-libui-windows-paginated-refined-table.png](images/glimmer-dsl-libui-windows-paginated-refined-table.png)| ![glimmer-dsl-libui-linux-paginated-refined-table.png](images/glimmer-dsl-libui-linux-paginated-refined-table.png)
8037
+
8038
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
8039
+
8040
+ ```ruby
8041
+ require 'glimmer-dsl-libui'
8042
+
8043
+ class PaginatedRefinedTable
8044
+ Contact = Struct.new(:name, :email, :phone, :city, :state)
8045
+
8046
+ include Glimmer::LibUI::Application
8047
+
8048
+ NAMES_FIRST = %w[
8049
+ Liam
8050
+ Noah
8051
+ William
8052
+ James
8053
+ Oliver
8054
+ Benjamin
8055
+ Elijah
8056
+ Lucas
8057
+ Mason
8058
+ Logan
8059
+ Alexander
8060
+ Ethan
8061
+ Jacob
8062
+ Michael
8063
+ Daniel
8064
+ Henry
8065
+ Jackson
8066
+ Sebastian
8067
+ Aiden
8068
+ Matthew
8069
+ Samuel
8070
+ David
8071
+ Joseph
8072
+ Carter
8073
+ Owen
8074
+ Wyatt
8075
+ John
8076
+ Jack
8077
+ Luke
8078
+ Jayden
8079
+ Dylan
8080
+ Grayson
8081
+ Levi
8082
+ Isaac
8083
+ Gabriel
8084
+ Julian
8085
+ Mateo
8086
+ Anthony
8087
+ Jaxon
8088
+ Lincoln
8089
+ Joshua
8090
+ Christopher
8091
+ Andrew
8092
+ Theodore
8093
+ Caleb
8094
+ Ryan
8095
+ Asher
8096
+ Nathan
8097
+ Thomas
8098
+ Leo
8099
+ Isaiah
8100
+ Charles
8101
+ Josiah
8102
+ Hudson
8103
+ Christian
8104
+ Hunter
8105
+ Connor
8106
+ Eli
8107
+ Ezra
8108
+ Aaron
8109
+ Landon
8110
+ Adrian
8111
+ Jonathan
8112
+ Nolan
8113
+ Jeremiah
8114
+ Easton
8115
+ Elias
8116
+ Colton
8117
+ Cameron
8118
+ Carson
8119
+ Robert
8120
+ Angel
8121
+ Maverick
8122
+ Nicholas
8123
+ Dominic
8124
+ Jaxson
8125
+ Greyson
8126
+ Adam
8127
+ Ian
8128
+ Austin
8129
+ Santiago
8130
+ Jordan
8131
+ Cooper
8132
+ Brayden
8133
+ Roman
8134
+ Evan
8135
+ Ezekiel
8136
+ Xaviar
8137
+ Jose
8138
+ Jace
8139
+ Jameson
8140
+ Leonardo
8141
+ Axel
8142
+ Everett
8143
+ Kayden
8144
+ Miles
8145
+ Sawyer
8146
+ Jason
8147
+ Emma
8148
+ Olivia
8149
+ Bartholomew
8150
+ Corey
8151
+ Danielle
8152
+ Eva
8153
+ Felicity
8154
+ ]
8155
+
8156
+ NAMES_LAST = %w[
8157
+ Smith
8158
+ Johnson
8159
+ Williams
8160
+ Brown
8161
+ Jones
8162
+ Miller
8163
+ Davis
8164
+ Wilson
8165
+ Anderson
8166
+ Taylor
8167
+ George
8168
+ Harrington
8169
+ Iverson
8170
+ Jackson
8171
+ Korby
8172
+ Levinson
8173
+ ]
8174
+
8175
+ CITIES = [
8176
+ 'Bellesville',
8177
+ 'Lombardia',
8178
+ 'Steepleton',
8179
+ 'Deerenstein',
8180
+ 'Schwartz',
8181
+ 'Hollandia',
8182
+ 'Saint Pete',
8183
+ 'Grandville',
8184
+ 'London',
8185
+ 'Berlin',
8186
+ 'Elktown',
8187
+ 'Paris',
8188
+ 'Garrison',
8189
+ 'Muncy',
8190
+ 'St Louis',
8191
+ ]
8192
+
8193
+ STATES = [ 'AK', 'AL', 'AR', 'AZ', 'CA', 'CO', 'CT', 'DC', 'DE', 'FL', 'GA',
8194
+ 'HI', 'IA', 'ID', 'IL', 'IN', 'KS', 'KY', 'LA', 'MA', 'MD', 'ME',
8195
+ 'MI', 'MN', 'MO', 'MS', 'MT', 'NC', 'ND', 'NE', 'NH', 'NJ', 'NM',
8196
+ 'NV', 'NY', 'OH', 'OK', 'OR', 'PA', 'RI', 'SC', 'SD', 'TN', 'TX',
8197
+ 'UT', 'VA', 'VT', 'WA', 'WI', 'WV', 'WY']
8198
+
8199
+ attr_accessor :contacts, :name, :email, :phone, :city, :state, :filter_value, :index
8200
+
8201
+ before_body do
8202
+ @contacts = 50_000.times.map do |n|
8203
+ n += 1
8204
+ first_name = NAMES_FIRST.sample
8205
+ last_name = NAMES_LAST.sample
8206
+ city = CITIES.sample
8207
+ state = STATES.sample
8208
+ Contact.new("#{first_name} #{last_name}", "#{first_name.downcase}#{n}@#{last_name.downcase}.com", '555-555-5555', city, state)
8209
+ end
8210
+ end
8211
+
8212
+ body {
8213
+ window("50,000 Paginated Contacts", 600, 700) {
8214
+ margined true
8215
+
8216
+ vertical_box {
8217
+ form {
8218
+ stretchy false
8219
+
8220
+ entry {
8221
+ label 'Name'
8222
+ text <=> [self, :name] # bidirectional data-binding between entry text and self.name
8223
+ }
8224
+
8225
+ entry {
8226
+ label 'Email'
8227
+ text <=> [self, :email]
8228
+ }
8229
+
8230
+ entry {
8231
+ label 'Phone'
8232
+ text <=> [self, :phone]
8233
+ }
8234
+
8235
+ entry {
8236
+ label 'City'
8237
+ text <=> [self, :city]
8238
+ }
8239
+
8240
+ entry {
8241
+ label 'State'
8242
+ text <=> [self, :state]
8243
+ }
8244
+ }
8245
+
8246
+ button('Save Contact') {
8247
+ stretchy false
8248
+
8249
+ on_clicked do
8250
+ new_row = [name, email, phone, city, state]
8251
+ if new_row.map(&:to_s).include?('')
8252
+ msg_box_error('Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
8253
+ else
8254
+ @contacts << Contact.new(*new_row) # automatically inserts a row into the table due to explicit data-binding
8255
+ @unfiltered_contacts = @contacts.dup
8256
+ self.name = '' # automatically clears name entry through explicit data-binding
8257
+ self.email = ''
8258
+ self.phone = ''
8259
+ self.city = ''
8260
+ self.state = ''
8261
+ end
8262
+ end
8263
+ }
8264
+
8265
+ refined_table(
8266
+ model_array: contacts,
8267
+ table_columns: {
8268
+ 'Name' => :text,
8269
+ 'Email' => :text,
8270
+ 'Phone' => :text,
8271
+ 'City' => :text,
8272
+ 'State' => :text,
8273
+ },
8274
+ table_editable: true,
8275
+ per_page: 20,
8276
+ )
8277
+ }
8278
+ }
8279
+ }
8280
+ end
8281
+
8282
+ PaginatedRefinedTable.launch
8283
+ ```
8284
+
7937
8285
  #### Grid
7938
8286
 
7939
8287
  [examples/grid.rb](examples/grid.rb)
@@ -11239,6 +11587,12 @@ MacOS | Windows | Linux
11239
11587
  | :---: | :---: | :---:
11240
11588
  ![mac_31](https://user-images.githubusercontent.com/72874215/159339948-b7ae1bf2-60c1-4dae-ac1a-4e13a6048ef0.gif)|![windows_4](https://user-images.githubusercontent.com/72874215/160242473-c38439be-0955-4e89-9f3f-b3d0567531fd.gif)|![rubycrumbler_linux](https://user-images.githubusercontent.com/72874215/160242460-99af1c8c-b43f-458d-bd69-1274a0bd9814.gif)
11241
11589
 
11590
+ ### Rubio-Radio
11591
+
11592
+ https://github.com/kojix2/rubio-radio
11593
+
11594
+ ![rubio radio screenshot](https://user-images.githubusercontent.com/5798442/171986696-24bedc38-3811-4c62-a5ad-89c09d015c8a.png)
11595
+
11242
11596
  ## Process
11243
11597
 
11244
11598
  [Glimmer Process](https://github.com/AndyObtiva/glimmer/blob/master/PROCESS.md)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.13
1
+ 0.5.16
@@ -0,0 +1,19 @@
1
+ require 'glimmer-dsl-libui'
2
+
3
+ include Glimmer
4
+
5
+ window('Main Window') {
6
+ button('Spawn Child Window') {
7
+ on_clicked do
8
+ window('Child Window') {
9
+ on_closing do
10
+ puts 'Child window is closing'
11
+ end
12
+ }.show
13
+ end
14
+ }
15
+
16
+ on_closing do
17
+ puts 'Main window is closing'
18
+ end
19
+ }.show
@@ -0,0 +1,242 @@
1
+ require 'glimmer-dsl-libui'
2
+
3
+ class PaginatedRefinedTable
4
+ Contact = Struct.new(:name, :email, :phone, :city, :state)
5
+
6
+ include Glimmer::LibUI::Application
7
+
8
+ NAMES_FIRST = %w[
9
+ Liam
10
+ Noah
11
+ William
12
+ James
13
+ Oliver
14
+ Benjamin
15
+ Elijah
16
+ Lucas
17
+ Mason
18
+ Logan
19
+ Alexander
20
+ Ethan
21
+ Jacob
22
+ Michael
23
+ Daniel
24
+ Henry
25
+ Jackson
26
+ Sebastian
27
+ Aiden
28
+ Matthew
29
+ Samuel
30
+ David
31
+ Joseph
32
+ Carter
33
+ Owen
34
+ Wyatt
35
+ John
36
+ Jack
37
+ Luke
38
+ Jayden
39
+ Dylan
40
+ Grayson
41
+ Levi
42
+ Isaac
43
+ Gabriel
44
+ Julian
45
+ Mateo
46
+ Anthony
47
+ Jaxon
48
+ Lincoln
49
+ Joshua
50
+ Christopher
51
+ Andrew
52
+ Theodore
53
+ Caleb
54
+ Ryan
55
+ Asher
56
+ Nathan
57
+ Thomas
58
+ Leo
59
+ Isaiah
60
+ Charles
61
+ Josiah
62
+ Hudson
63
+ Christian
64
+ Hunter
65
+ Connor
66
+ Eli
67
+ Ezra
68
+ Aaron
69
+ Landon
70
+ Adrian
71
+ Jonathan
72
+ Nolan
73
+ Jeremiah
74
+ Easton
75
+ Elias
76
+ Colton
77
+ Cameron
78
+ Carson
79
+ Robert
80
+ Angel
81
+ Maverick
82
+ Nicholas
83
+ Dominic
84
+ Jaxson
85
+ Greyson
86
+ Adam
87
+ Ian
88
+ Austin
89
+ Santiago
90
+ Jordan
91
+ Cooper
92
+ Brayden
93
+ Roman
94
+ Evan
95
+ Ezekiel
96
+ Xaviar
97
+ Jose
98
+ Jace
99
+ Jameson
100
+ Leonardo
101
+ Axel
102
+ Everett
103
+ Kayden
104
+ Miles
105
+ Sawyer
106
+ Jason
107
+ Emma
108
+ Olivia
109
+ Bartholomew
110
+ Corey
111
+ Danielle
112
+ Eva
113
+ Felicity
114
+ ]
115
+
116
+ NAMES_LAST = %w[
117
+ Smith
118
+ Johnson
119
+ Williams
120
+ Brown
121
+ Jones
122
+ Miller
123
+ Davis
124
+ Wilson
125
+ Anderson
126
+ Taylor
127
+ George
128
+ Harrington
129
+ Iverson
130
+ Jackson
131
+ Korby
132
+ Levinson
133
+ ]
134
+
135
+ CITIES = [
136
+ 'Bellesville',
137
+ 'Lombardia',
138
+ 'Steepleton',
139
+ 'Deerenstein',
140
+ 'Schwartz',
141
+ 'Hollandia',
142
+ 'Saint Pete',
143
+ 'Grandville',
144
+ 'London',
145
+ 'Berlin',
146
+ 'Elktown',
147
+ 'Paris',
148
+ 'Garrison',
149
+ 'Muncy',
150
+ 'St Louis',
151
+ ]
152
+
153
+ STATES = [ 'AK', 'AL', 'AR', 'AZ', 'CA', 'CO', 'CT', 'DC', 'DE', 'FL', 'GA',
154
+ 'HI', 'IA', 'ID', 'IL', 'IN', 'KS', 'KY', 'LA', 'MA', 'MD', 'ME',
155
+ 'MI', 'MN', 'MO', 'MS', 'MT', 'NC', 'ND', 'NE', 'NH', 'NJ', 'NM',
156
+ 'NV', 'NY', 'OH', 'OK', 'OR', 'PA', 'RI', 'SC', 'SD', 'TN', 'TX',
157
+ 'UT', 'VA', 'VT', 'WA', 'WI', 'WV', 'WY']
158
+
159
+ attr_accessor :contacts, :name, :email, :phone, :city, :state, :filter_value, :index
160
+
161
+ before_body do
162
+ @contacts = 50_000.times.map do |n|
163
+ n += 1
164
+ first_name = NAMES_FIRST.sample
165
+ last_name = NAMES_LAST.sample
166
+ city = CITIES.sample
167
+ state = STATES.sample
168
+ Contact.new("#{first_name} #{last_name}", "#{first_name.downcase}#{n}@#{last_name.downcase}.com", '555-555-5555', city, state)
169
+ end
170
+ end
171
+
172
+ body {
173
+ window("50,000 Paginated Contacts", 600, 700) {
174
+ margined true
175
+
176
+ vertical_box {
177
+ form {
178
+ stretchy false
179
+
180
+ entry {
181
+ label 'Name'
182
+ text <=> [self, :name] # bidirectional data-binding between entry text and self.name
183
+ }
184
+
185
+ entry {
186
+ label 'Email'
187
+ text <=> [self, :email]
188
+ }
189
+
190
+ entry {
191
+ label 'Phone'
192
+ text <=> [self, :phone]
193
+ }
194
+
195
+ entry {
196
+ label 'City'
197
+ text <=> [self, :city]
198
+ }
199
+
200
+ entry {
201
+ label 'State'
202
+ text <=> [self, :state]
203
+ }
204
+ }
205
+
206
+ button('Save Contact') {
207
+ stretchy false
208
+
209
+ on_clicked do
210
+ new_row = [name, email, phone, city, state]
211
+ if new_row.map(&:to_s).include?('')
212
+ msg_box_error('Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
213
+ else
214
+ @contacts << Contact.new(*new_row) # automatically inserts a row into the table due to explicit data-binding
215
+ @unfiltered_contacts = @contacts.dup
216
+ self.name = '' # automatically clears name entry through explicit data-binding
217
+ self.email = ''
218
+ self.phone = ''
219
+ self.city = ''
220
+ self.state = ''
221
+ end
222
+ end
223
+ }
224
+
225
+ refined_table(
226
+ model_array: contacts,
227
+ table_columns: {
228
+ 'Name' => {text: {editable: false}},
229
+ 'Email' => :text,
230
+ 'Phone' => :text,
231
+ 'City' => :text,
232
+ 'State' => :text,
233
+ },
234
+ table_editable: true,
235
+ per_page: 20,
236
+ )
237
+ }
238
+ }
239
+ }
240
+ end
241
+
242
+ PaginatedRefinedTable.launch
Binary file
@@ -129,7 +129,7 @@ module Glimmer
129
129
  model_attribute_observer = Glimmer::DataBinding::Observer.proc do
130
130
  new_value = model_binding.evaluate_property
131
131
  new_value = new_value.to_a if new_value.is_a?(Enumerator)
132
- if model_binding.binding_options[:column_attributes] || (!new_value.empty? && !new_value.first.is_a?(Array))
132
+ if model_binding.binding_options[:column_attributes] || (!new_value.nil? && (!new_value.is_a?(String) || !new_value.empty?) && (!new_value.is_a?(Array) || !new_value.first.is_a?(Array)))
133
133
  @model_attribute_array_observer_registration&.deregister
134
134
  @model_attribute_array_observer_registration = model_attribute_observer.observe(new_value, @column_attributes, ignore_frozen: true, attribute_writer_type: [:attribute=, :set_attribute])
135
135
  model_attribute_observer.add_dependent(model_attribute_observer_registration => @model_attribute_array_observer_registration)
@@ -302,34 +302,38 @@ module Glimmer
302
302
 
303
303
  def register_cell_rows_observer
304
304
  @cell_rows_observer = Glimmer::DataBinding::Observer.proc do |new_cell_rows|
305
- if @cell_rows.size < @last_cell_rows.size && @last_cell_rows.include_all?(*@cell_rows)
305
+ if @cell_rows.size < @last_cell_rows.size
306
306
  @last_cell_rows.each_with_index do |old_row_data, row|
307
- if old_row_data != @cell_rows[row]
308
- ::LibUI.table_model_row_changed(model, row) if model && row
307
+ if old_row_data != @cell_rows[row] && model
308
+ ::LibUI.table_model_row_changed(model, row)
309
309
  notify_custom_listeners('on_changed', row, :changed, @cell_rows[row])
310
310
  end
311
311
  end
312
312
  (@last_cell_rows.size - @cell_rows.size).times do |n|
313
313
  row = @last_cell_rows.size - n - 1
314
- ::LibUI.table_model_row_deleted(model, row) if model && row
315
- notify_custom_listeners('on_changed', row, :deleted, @last_cell_rows[row])
314
+ if model
315
+ ::LibUI.table_model_row_deleted(model, row)
316
+ notify_custom_listeners('on_changed', row, :deleted, @last_cell_rows[row])
317
+ end
316
318
  end
317
- elsif @cell_rows.size > @last_cell_rows.size && @cell_rows.include_all?(*@last_cell_rows)
319
+ elsif @cell_rows.size > @last_cell_rows.size
318
320
  (@cell_rows.size - @last_cell_rows.size).times do |n|
319
321
  row = @last_cell_rows.size + n
320
- ::LibUI.table_model_row_inserted(model, row) if model && row
321
- notify_custom_listeners('on_changed', row, :inserted, @cell_rows[row])
322
+ if model
323
+ ::LibUI.table_model_row_inserted(model, row)
324
+ notify_custom_listeners('on_changed', row, :inserted, @cell_rows[row])
325
+ end
322
326
  end
323
327
  @cell_rows.each_with_index do |new_row_data, row|
324
- if new_row_data != @last_cell_rows[row]
325
- ::LibUI.table_model_row_changed(model, row) if model && row
328
+ if new_row_data != @last_cell_rows[row] && model
329
+ ::LibUI.table_model_row_changed(model, row)
326
330
  notify_custom_listeners('on_changed', row, :changed, @cell_rows[row])
327
331
  end
328
332
  end
329
333
  elsif @cell_rows != @last_cell_rows
330
334
  @cell_rows.each_with_index do |new_row_data, row|
331
- if new_row_data != @last_cell_rows[row]
332
- ::LibUI.table_model_row_changed(model, row) if model && row
335
+ if new_row_data != @last_cell_rows[row] && model
336
+ ::LibUI.table_model_row_changed(model, row)
333
337
  notify_custom_listeners('on_changed', row, :changed, @cell_rows[row])
334
338
  end
335
339
  end
@@ -80,10 +80,14 @@ module Glimmer
80
80
  return_value = nil
81
81
  @on_closing_listeners.each do |l|
82
82
  return_value = l.call(self)
83
- break if return_value.is_a?(Numeric)
83
+ break if return_value.is_a?(Numeric) || return_value.is_a?(TrueClass) || return_value.is_a?(FalseClass)
84
84
  end
85
85
  if return_value.is_a?(Numeric)
86
86
  return_value
87
+ elsif return_value.is_a?(TrueClass) || return_value.is_a?(FalseClass)
88
+ return_value ? 1 : 0
89
+ elsif self != ControlProxy.main_window_proxy
90
+ 1
87
91
  else
88
92
  destroy
89
93
  ::LibUI.quit
@@ -0,0 +1,153 @@
1
+ class RefinedTable
2
+ include Glimmer::LibUI::CustomControl
3
+
4
+ option :model_array, default: []
5
+ option :table_columns, default: []
6
+ option :table_editable, default: false
7
+ option :per_page, default: 10
8
+ option :page, default: 1
9
+
10
+ attr_accessor :filtered_model_array, :filter_query, :filter_query_page_stack, :paginated_model_array
11
+
12
+ before_body do
13
+ @filter_query = ''
14
+ @filter_query_page_stack = {}
15
+ @filtered_model_array = model_array.dup
16
+ @filtered_model_array_stack = {@filter_query => @filtered_model_array}
17
+ paginate_model_array
18
+ end
19
+
20
+ body {
21
+ vertical_box {
22
+ table_filter
23
+
24
+ table_paginator
25
+
26
+ @table = table {
27
+ table_columns.each do |column_name, column_details|
28
+ editable_value = on_clicked_value = nil
29
+ if column_details.is_a?(Symbol) || column_details.is_a?(String)
30
+ column_type = column_details
31
+ elsif column_details.is_a?(Hash)
32
+ column_type = column_details.keys.first
33
+ editable_value = column_details.values.first[:editable] || column_details.values.first['editable']
34
+ on_clicked_value = column_details.values.first[:on_clicked] || column_details.values.first['on_clicked']
35
+ end
36
+
37
+ send("#{column_type}_column", column_name) {
38
+ editable editable_value unless editable_value.nil?
39
+ on_clicked(&on_clicked_value) unless on_clicked_value.nil?
40
+ }
41
+ end
42
+
43
+ editable table_editable
44
+ cell_rows <=> [self, :paginated_model_array]
45
+ }
46
+ }
47
+ }
48
+
49
+ def table_filter
50
+ search_entry {
51
+ stretchy false
52
+ text <=> [self, :filter_query,
53
+ before_write: ->(new_filter_query) {
54
+ if new_filter_query != filter_query
55
+ if !@filtered_model_array_stack.key?(new_filter_query)
56
+ @filtered_model_array_stack[new_filter_query] = model_array.dup.filter do |model|
57
+ @table.expand([model])[0].any? do |attribute_value|
58
+ attribute_value.to_s.downcase.include?(new_filter_query.downcase)
59
+ end
60
+ end
61
+ end
62
+ @filtered_model_array = @filtered_model_array_stack[new_filter_query]
63
+ if new_filter_query.size > filter_query.size
64
+ @filter_query_page_stack[filter_query] = page
65
+ end
66
+ self.page = @filter_query_page_stack[new_filter_query] || correct_page(page)
67
+ paginate_model_array
68
+ end
69
+ }
70
+ ]
71
+ }
72
+ end
73
+
74
+ def table_paginator
75
+ horizontal_box {
76
+ stretchy false
77
+
78
+ button('<<') {
79
+ on_clicked do
80
+ unless self.page == 0
81
+ self.page = 1
82
+ paginate_model_array
83
+ end
84
+ end
85
+ }
86
+ button('<') {
87
+ on_clicked do
88
+ unless self.page == 0
89
+ self.page = [page - 1, 1].max
90
+ paginate_model_array
91
+ end
92
+ end
93
+ }
94
+ entry {
95
+ text <=> [self, :page,
96
+ on_read: :to_s,
97
+ on_write: ->(val) { correct_page(val.to_i) },
98
+ after_write: ->(val) { paginate_model_array },
99
+ ]
100
+ }
101
+ button('>') {
102
+ on_clicked do
103
+ unless self.page == 0
104
+ self.page = [page + 1, page_count].min
105
+ paginate_model_array
106
+ end
107
+ end
108
+ }
109
+ button('>>') {
110
+ on_clicked do
111
+ unless self.page == 0
112
+ self.page = page_count
113
+ paginate_model_array
114
+ end
115
+ end
116
+ }
117
+ }
118
+ end
119
+
120
+ def paginate_model_array
121
+ self.paginated_model_array = filtered_model_array[index, limit]
122
+ end
123
+
124
+ def index
125
+ [per_page * (page - 1), 0].max
126
+ end
127
+
128
+ def limit
129
+ [(filtered_model_array.count - index), per_page].min
130
+ end
131
+
132
+ def page_count
133
+ (filtered_model_array.count.to_f / per_page.to_f).ceil
134
+ end
135
+
136
+ def correct_page(page)
137
+ [[page, 1].max, page_count].min
138
+ end
139
+
140
+ # Ensure proxying properties to @table if body_root (vertical_box) doesn't support them
141
+
142
+ def respond_to?(method_name, *args, &block)
143
+ super || @table&.respond_to?(method_name, *args, &block)
144
+ end
145
+
146
+ def method_missing(method_name, *args, &block)
147
+ if @table&.respond_to?(method_name, *args, &block)
148
+ @table&.send(method_name, *args, &block)
149
+ else
150
+ super
151
+ end
152
+ end
153
+ end
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.5.13
4
+ version: 0.5.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Maleh
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-06-17 00:00:00.000000000 Z
11
+ date: 2022-07-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: glimmer
@@ -285,6 +285,7 @@ files:
285
285
  - examples/basic_area3.rb
286
286
  - examples/basic_area4.rb
287
287
  - examples/basic_button.rb
288
+ - examples/basic_child_window.rb
288
289
  - examples/basic_code_area.rb
289
290
  - examples/basic_draw_text.rb
290
291
  - examples/basic_draw_text2.rb
@@ -355,6 +356,7 @@ files:
355
356
  - examples/midi_player.rb
356
357
  - examples/midi_player2.rb
357
358
  - examples/midi_player3.rb
359
+ - examples/paginated_refined_table.rb
358
360
  - examples/shape_coloring.rb
359
361
  - examples/simple_notepad.rb
360
362
  - examples/snake.rb
@@ -468,6 +470,7 @@ files:
468
470
  - lib/glimmer/libui/control_proxy/window_proxy.rb
469
471
  - lib/glimmer/libui/custom_control.rb
470
472
  - lib/glimmer/libui/custom_control/code_area.rb
473
+ - lib/glimmer/libui/custom_control/refined_table.rb
471
474
  - lib/glimmer/libui/custom_window.rb
472
475
  - lib/glimmer/libui/data_bindable.rb
473
476
  - lib/glimmer/libui/image_path_renderer.rb
@@ -511,7 +514,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
511
514
  - !ruby/object:Gem::Version
512
515
  version: '0'
513
516
  requirements: []
514
- rubygems_version: 3.3.3
517
+ rubygems_version: 3.3.16
515
518
  signing_key:
516
519
  specification_version: 4
517
520
  summary: Glimmer DSL for LibUI