glimmer-dsl-swt 4.24.3.2 → 4.24.4.2

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: 89250fa7cc394da176eb7cd91a17c7ad388607c69927a7e22b6b1be62333a0b8
4
- data.tar.gz: e7e09c62b782c682c0abdb8722001c8925268cfe4bab49b536eb58f3f32fb697
3
+ metadata.gz: 3a089b978fce806c9396adb1f971c4ba8f5344f0b927e8781c6a6985d9c234d2
4
+ data.tar.gz: 82e0a576ebf56dd83bafda03378aec4cee32cfa0de1d2bf61bc247c2c33562a1
5
5
  SHA512:
6
- metadata.gz: 136c2625e94629b690a325bdc3c8669a36c612f9f651b04faa5be3dcbcc86b57ae5345c23fcce1b10656b9dca5e8a4e96a318147907785a5bff18eac3dd4b130
7
- data.tar.gz: 6d1c069b332ccd3139cf0c6377dd64b5341ccd84cc4dda3c72f5255c0eaeff0639bbfcdea35cb6b66baa84fb9ed1093143cafb4897f6ba2b2198dd494e0033c3
6
+ metadata.gz: 620cc756c24723f0e7443055b9f156e71a7312e07093142dc606f16692af7cd7f37eb97294586bbf79239ccd9fc3c34c9fb107efd28eb5713a287dd882ad1b48
7
+ data.tar.gz: 6c954efcf5550479ab13ef3f21b1f68f3f1ab32823fe638ef13618c5e3c12292f1d1fbb8ed42d2158a480a344ac64c12f2f87d8b96deeadda7373302e01167fb
data/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # Change Log
2
2
 
3
+ ## 4.24.4.2
4
+
5
+ - `refined_table` filtering support
6
+ - Ensure correct `refined_table` enablement of pagination buttons based on whether on first/last page or not
7
+ - Display vertical scroll bar when setting `refined_table` `per_page` to a height value that results in table exceeding screen height
8
+ - Support `refined_table` nested elements `first_button {}`, `previous_button {}`, `page_text {}`, `next_button {}`, `last_button {}` to customize every widget within.
9
+ - Support `refined_table` attributes for accessing nested elements: `table_proxy`, `first_button_proxy`, `previous_button_proxy`, `page_text_proxy`, `next_button_proxy`, `last_button_proxy` to customize every widget within.
10
+
11
+ ## 4.24.4.1
12
+
13
+ - Optimize `table` data-binding performance (improving paging performance in `refined_table`) by making observer registrations run asynchronously in a separate thread
14
+ - Fix cleaning of old `table` data-binding observers (it was retaining some old observers after table updates)
15
+ - Fix issue with ActiveSupport overriding Facets String `underscore`/`snakecase`/`titlecase`/`camelcase` methods with different incompatible implementations when loaded by a project (now, that is repaired automatically)
16
+
17
+ ## 4.24.4.0
18
+
19
+ - `refined_table` custom widget with pagination support (and future filtering support)
20
+ - Hello, Refined Table! sample demonstrating `refined_table`
21
+ - Save `WidgetBinding` instances on `WidgetProxy` objects via `widget_bindings` attribute
22
+ - Support specifying `@children_owner` in any custom widget for which adding children will not add them under the `body_root` (e.g. a composite wrapping a table will designate the table as the `@children_owner` for adding `table_column`s)
23
+ - Add explicit `shell_proxy` method to `Glimmer::UI::CustomWidget` that delegates to `body_root` to avoid annoying false negative error every time that method is called
24
+ - Optimize `table` data-binding performance for single-model update scenarios
25
+ - Fix issue with data-binding a table as the body root of a custom widget when customizing column_attributes
26
+
3
27
  ## 4.24.3.2
4
28
 
5
29
  - Have `table` data-binding infer model attribute names from column names by convention (no need to specify `column_properties`)
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 SWT 4.24.3.2
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 SWT 4.24.4.2
2
2
  ## JRuby Desktop Development GUI Framework
3
3
  [![Gem Version](https://badge.fury.io/rb/glimmer-dsl-swt.svg)](http://badge.fury.io/rb/glimmer-dsl-swt)
4
4
  [![Travis CI](https://travis-ci.com/AndyObtiva/glimmer-dsl-swt.svg?branch=master)](https://travis-ci.com/github/AndyObtiva/glimmer-dsl-swt)
@@ -21,7 +21,7 @@ Featured in JRuby Cookbook](http://shop.oreilly.com/product/9780596519650.do) an
21
21
 
22
22
  ![Eclipse SWT RCP NASA Mars Rover](/images/glimmer-eclipse-swt-rcp-nasa-mars-rover.png)
23
23
 
24
- [Glimmer DSL for SWT](https://rubygems.org/gems/glimmer-dsl-swt) 4.24.3.2 includes [SWT 4.24](https://download.eclipse.org/eclipse/downloads/drops4/R-4.24-202206070700/), which was released on June 7, 2022. Gem version numbers are in sync with the SWT library versions. The first two digits represent the SWT version number. The last two digits represent the minor and patch versions of Glimmer DSL for SWT.
24
+ [Glimmer DSL for SWT](https://rubygems.org/gems/glimmer-dsl-swt) 4.24.4.2 includes [SWT 4.24](https://download.eclipse.org/eclipse/downloads/drops4/R-4.24-202206070700/), which was released on June 7, 2022. Gem version numbers are in sync with the SWT library versions. The first two digits represent the SWT version number. The last two digits represent the minor and patch versions of Glimmer DSL for SWT.
25
25
 
26
26
  **Starting in version 4.20.0.0, [Glimmer DSL for SWT](https://rubygems.org/gems/glimmer-dsl-swt) comes with the new [***Shine***](/docs/reference/GLIMMER_GUI_DSL_SYNTAX.md#shine) syntax** for highly intuitive and visually expressive View/Model Attribute Mapping, relying on `<=>` for bidirectional (two-way) data-binding and `<=` for unidirectional (one-way) data-binding, providing an alternative to the `bind` keyword. That was [originally conceived back in 2007](https://andymaleh.blogspot.com/2007/12/data-shining-in-glimmer.html).
27
27
 
@@ -338,7 +338,7 @@ jgem install glimmer-dsl-swt
338
338
 
339
339
  Or this command if you want a specific version:
340
340
  ```
341
- jgem install glimmer-dsl-swt -v 4.24.3.2
341
+ jgem install glimmer-dsl-swt -v 4.24.4.2
342
342
  ```
343
343
 
344
344
  `jgem` is JRuby's version of `gem` command.
@@ -366,7 +366,7 @@ Note: if you're using activerecord or activesupport, keep in mind that Glimmer u
366
366
 
367
367
  Add the following to `Gemfile`:
368
368
  ```
369
- gem 'glimmer-dsl-swt', '~> 4.24.3.2'
369
+ gem 'glimmer-dsl-swt', '~> 4.24.4.2'
370
370
  ```
371
371
 
372
372
  And, then run:
@@ -389,7 +389,7 @@ glimmer
389
389
  ```
390
390
 
391
391
  ```
392
- Glimmer (JRuby Desktop Development GUI Framework) - JRuby Gem: glimmer-dsl-swt v4.24.3.2
392
+ Glimmer (JRuby Desktop Development GUI Framework) - JRuby Gem: glimmer-dsl-swt v4.24.4.2
393
393
 
394
394
  Usage: glimmer [--bundler] [--pd] [--quiet] [--debug] [--log-level=VALUE] [[ENV_VAR=VALUE]...] [[-jruby-option]...] (application.rb or task[task_args]) [[application2.rb]...]
395
395
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 4.24.3.2
1
+ 4.24.4.2
@@ -54,9 +54,11 @@ This guide should help you get started with Glimmer DSL for SWT. For more advanc
54
54
  - [Combo](#combo)
55
55
  - [List](#list)
56
56
  - [Table](#table)
57
+ - [Table Item Properties](#table-item-properties)
57
58
  - [Table Selection](#table-selection)
58
59
  - [Table Editing](#table-editing)
59
60
  - [Table Sorting](#table-sorting)
61
+ - [Refined Table](#refined-table)
60
62
  - [Tree](#tree)
61
63
  - [DateTime](#datetime)
62
64
  - [Observer](#observer)
@@ -4109,6 +4111,9 @@ This automatically leverages the SWT TableEditor custom class behind the scenes,
4109
4111
  passed table item text into something else.
4110
4112
  It automatically persists the change to `items` data-bound model on ENTER/FOCUS-OUT or cancels on ESC/NO-CHANGE.
4111
4113
 
4114
+ Note that `table` is useful with a maximum of about 100 rows only, not more than that, or otherwise it will not offer a user-friendly experience due to requiring users to scroll through a lot of data.
4115
+ If you need to display a table with more than 100 rows, then you need to employ pagination. That is already supported in the [Refined Table (`refined_table`)](#refined-table) custom widget documented below.
4116
+
4112
4117
  ##### Table Item Properties
4113
4118
 
4114
4119
  When data-binding a `table`'s `items`, extra [`TableItem` properties](https://help.eclipse.org/latest/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/widgets/TableItem.html) are data-bound automatically by convention for `background` color, `foreground` color, `font`, and `image` if corresponding properties (attributes) are available on the model.
@@ -4236,7 +4241,7 @@ shell {
4236
4241
 
4237
4242
  Check out [Hello, Table!](/docs/reference/GLIMMER_SAMPLES.md#hello-table) for an actual example including table editors.
4238
4243
 
4239
- [Are We There Yet?](#are-we-there-yet) is an actual production Glimmer application that takes full advantage of table capabilities, storing model data in a database via ActiveRecord. As such, it's an excellent demonstration of how to use Glimmer DSL for SWT with a database.
4244
+ [Are We There Yet?](#are-we-there-yet) is an actual production Glimmer application that takes full advantage of table capabilities, storing model data in a database via ActiveRecord and SQLite DB. As such, it's an excellent demonstration of how to use Glimmer DSL for SWT with a database. [Contact Manager](https://github.com/AndyObtiva/contact_manager) is an external sample application that also utilizes a table with ActiveRecord and SQLite DB. It comes with a [blog post](https://andymaleh.blogspot.com/2022/06/using-activerecord-with-sqlite-db-in.html?m=0) that provides a step by step guide on how to build such an application.
4240
4245
 
4241
4246
  ##### Table Sorting
4242
4247
 
@@ -4302,6 +4307,52 @@ Here is an explanation of the example above:
4302
4307
 
4303
4308
  `<= [model, :property, read_only_sort: true]` could be used with items to make sorting not propagate sorting changes to model.
4304
4309
 
4310
+ ##### Refined Table
4311
+
4312
+ **(ALPHA FEATURE)**
4313
+
4314
+ `refined_table` is a custom widget that can handle very large amounts of data by applying pagination and filtering.
4315
+
4316
+ Just use like a standard `table`, but data-bind models to the `model_array` property instead of `items`. `refined_table` will take care of the rest.
4317
+
4318
+ Options:
4319
+ - `per_page` (default: `10`): specifies how many rows to display per page
4320
+ - `page` (default: `1` if table is filled and `0` otherwise): specifies initial page
4321
+ - `query` (default: `''`): specifies filter query term (empty shows all results)
4322
+
4323
+ Note that currently `refined_table` only supports displaying a **read-only** table (meaning it can read updates from the model, but it cannot write back to the model through `TableEditor` cells).
4324
+
4325
+ Example taken from [Hello, Refined Table!](/docs/reference/GLIMMER_SAMPLES.md#hello-refined-table):
4326
+
4327
+ ![hello refined table](/images/glimmer-hello-refined-table.png)
4328
+
4329
+ ```ruby
4330
+ #... more code precedes
4331
+
4332
+ refined_table(per_page: 20) {
4333
+ table_column {
4334
+ width 100
4335
+ text 'Date'
4336
+ }
4337
+ table_column {
4338
+ width 200
4339
+ text 'Ballpark'
4340
+ }
4341
+ table_column {
4342
+ width 150
4343
+ text 'Home Team'
4344
+ }
4345
+ table_column {
4346
+ width 150
4347
+ text 'Away Team'
4348
+ }
4349
+
4350
+ model_array <= [@baseball_season, :games, column_attributes: {'Home Team' => :home_team_name, 'Away Team' => :away_team_name}]
4351
+ }
4352
+
4353
+ #...more code follows
4354
+ ```
4355
+
4305
4356
  #### Tree
4306
4357
 
4307
4358
  The SWT Tree widget visualizes a tree data-structure, such as an employment or composition hierarchy.
@@ -41,6 +41,7 @@
41
41
  - [Hello, Slider!](#hello-slider)
42
42
  - [Hello, Spinner!](#hello-spinner)
43
43
  - [Hello, Table!](#hello-table)
44
+ - [Hello, Refined Table!](#hello-refined-table)
44
45
  - [Hello, Link!](#hello-link)
45
46
  - [Hello, Dialog!](#hello-dialog)
46
47
  - [Hello, Code Text!](#hello-code-text)
@@ -780,6 +781,18 @@ Hello, Table! Game Booked Rows
780
781
 
781
782
  ![Hello Table game booked rows](/images/glimmer-hello-table-game-booked-rows.png)
782
783
 
784
+ #### Hello, Refined Table!
785
+
786
+ This sample demonstrates the use of the [`refined_table` widget](/docs/reference/GLIMMER_GUI_DSL_SYNTAX.md#refined-table), which provides a paginated `table` that can handle very large amounts of data.
787
+
788
+ Code:
789
+
790
+ [samples/hello/hello_refined_table.rb](/samples/hello/hello_refined_table.rb)
791
+
792
+ Hello, Refined Table!
793
+
794
+ ![Hello Refined Table](/images/glimmer-hello-refined-table.png)
795
+
783
796
  #### Hello, Link!
784
797
 
785
798
  This sample demonstrates the use of the `link` widget in Glimmer, including identifying which link was clicked and performing an action (displaying help) based on its location.
Binary file
data/lib/ext/glimmer.rb CHANGED
@@ -27,6 +27,11 @@ module Glimmer
27
27
  if Object.const_defined?(:ActiveSupport) && ActiveSupport.const_defined?(:Dependencies)
28
28
  begin
29
29
  ActiveSupport::Dependencies.unhook!
30
+ # override activesupport string method implementations if already loaded
31
+ gem 'facets'
32
+ load 'facets/string/snakecase.rb'
33
+ load 'facets/string/titlecase.rb'
34
+ load 'facets/string/camelcase.rb'
30
35
  rescue => e
31
36
  # noop TODO support logging unimportant details below debug level
32
37
  end
@@ -37,20 +37,17 @@ module Glimmer
37
37
  include_package 'org.eclipse.swt.widgets'
38
38
 
39
39
  TABLE_ITEM_PROPERTIES = %w[background foreground font image]
40
+
41
+ attr_reader :data_binding_done
40
42
 
41
43
  def initialize(parent, model_binding, column_properties = nil)
42
- @table = parent
44
+ @table = parent.is_a?(Glimmer::SWT::TableProxy) ? parent : parent.body_root # assume custom widget in latter case
45
+ @table.table_items_binding = self
43
46
  @model_binding = model_binding
44
47
  @read_only_sort = @model_binding.binding_options[:read_only_sort]
45
48
  @table.editable = false if @model_binding.binding_options[:read_only]
46
- column_properties = @model_binding.binding_options[:column_properties] || @model_binding.binding_options[:column_attributes] || column_properties
47
- if @table.is_a?(Glimmer::SWT::TableProxy)
48
- @table.column_properties = column_properties
49
- @column_properties = @table.column_properties # normalized column properties
50
- else # assume custom widget
51
- @table.body_root.column_properties = @column_properties
52
- @column_properties = @table.body_root.column_properties # normalized column properties
53
- end
49
+ @table.column_properties = @model_binding.binding_options[:column_properties] || @model_binding.binding_options[:column_attributes] || column_properties
50
+ @column_properties = @table.column_properties # normalized column properties
54
51
  Glimmer::SWT::DisplayProxy.instance.auto_exec(override_sync_exec: @model_binding.binding_options[:sync_exec], override_async_exec: @model_binding.binding_options[:async_exec]) do
55
52
  @table.swt_widget.data = @model_binding
56
53
  @table.swt_widget.set_data('table_items_binding', self)
@@ -69,6 +66,7 @@ module Glimmer
69
66
  Glimmer::SWT::DisplayProxy.instance.auto_exec(override_sync_exec: @model_binding.binding_options[:sync_exec], override_async_exec: @model_binding.binding_options[:async_exec]) do
70
67
  new_model_collection = model_binding_evaluated_property = @model_binding.evaluate_property unless internal_sort # this ensures applying converters (e.g. :on_read)
71
68
  return if same_table_data?(new_model_collection)
69
+
72
70
  if same_model_collection?(new_model_collection)
73
71
  new_model_collection_attribute_values = model_collection_attribute_values(new_model_collection)
74
72
  @table.swt_widget.items.each_with_index do |table_item, row_index|
@@ -85,24 +83,44 @@ module Glimmer
85
83
  @last_model_collection_attribute_values = new_model_collection_attribute_values
86
84
  else
87
85
  if new_model_collection and new_model_collection.is_a?(Array)
88
- remove_dependent(@table_observer_registration => @table_items_observer_registration) if @table_items_observer_registration
89
- @table_items_observer_registration&.unobserve
90
- # TODO observe and update table items piecemeal per model instead of passing @column_properties
91
- # TODO ensure unobserving models when they are no longer part of the table
92
- @table_items_observer_registration = observe(new_model_collection, @column_properties)
93
- add_dependent(@table_observer_registration => @table_items_observer_registration)
94
- @table_items_property_observer_registration ||= {}
95
- if !same_model_collection_with_different_sort?(new_model_collection)
96
- TABLE_ITEM_PROPERTIES.each do |table_item_property|
97
- remove_dependent(@table_observer_registration => @table_items_property_observer_registration[table_item_property]) if @table_items_property_observer_registration[table_item_property]
98
- @table_items_property_observer_registration[table_item_property]&.unobserve
99
- property_properties = @column_properties.map {|property| "#{property}_#{table_item_property}" }
100
- @table_items_property_observer_registration[table_item_property] = observe(new_model_collection, property_properties)
101
- add_dependent(@table_observer_registration => @table_items_property_observer_registration[table_item_property])
86
+ @model_observer_registrations ||= {}
87
+ @table_item_property_observation_mutex ||= Mutex.new
88
+
89
+ Thread.new do
90
+ @data_binding_done = false
91
+ @table_item_property_observation_mutex.synchronize do
92
+ deregister_model_observer_registrations
93
+
94
+ new_model_collection.each_with_index do |model, model_index|
95
+ @model_observer_registrations[model_index] ||= {}
96
+ @column_properties.each do |column_property|
97
+ model_observer_registration = observe(model, column_property)
98
+ @model_observer_registrations[model_index][column_property] = model_observer_registration
99
+ add_dependent(@table_observer_registration => model_observer_registration)
100
+ end
101
+ end
102
+
103
+ if !same_model_collection_with_different_sort?(new_model_collection)
104
+ new_model_collection.each_with_index do |model, model_index|
105
+ TABLE_ITEM_PROPERTIES.each do |table_item_property|
106
+ @column_properties.each do |column_property|
107
+ column_property = "#{column_property}_#{table_item_property}"
108
+ model_observer_registration = observe(model, column_property)
109
+ @model_observer_registrations[model_index][column_property] = model_observer_registration
110
+ add_dependent(@table_observer_registration => model_observer_registration)
111
+ end
112
+ end
113
+ end
114
+ end
115
+ @data_binding_done = true
102
116
  end
103
117
  end
118
+
104
119
  @model_collection = new_model_collection
120
+ else
121
+ @model_collection = []
105
122
  end
123
+
106
124
  populate_table(@model_collection, @table, @column_properties, internal_sort: internal_sort)
107
125
  end
108
126
  end
@@ -202,7 +220,24 @@ module Glimmer
202
220
  end
203
221
 
204
222
  def table_item_model_collection
205
- @table.swt_widget.items.map(&:get_data)
223
+ Glimmer::SWT::DisplayProxy.instance.auto_exec do
224
+ if @table.disposed?
225
+ []
226
+ else
227
+ @table.swt_widget.items.map(&:get_data)
228
+ end
229
+ end
230
+ end
231
+
232
+ def deregister_model_observer_registrations
233
+ @model_observer_registrations&.dup&.each do |model_index, model_column_properties|
234
+ model_column_properties.dup.each do |column_property, model_observer_registration|
235
+ remove_dependent(@table_observer_registration => model_observer_registration) if model_observer_registration
236
+ model_observer_registration&.unobserve
237
+ model_column_properties.delete(column_property)
238
+ end
239
+ @model_observer_registrations.delete(model_index)
240
+ end
206
241
  end
207
242
 
208
243
  def model_collection_attribute_values(model_collection)
@@ -31,7 +31,8 @@ module Glimmer
31
31
  include Observable
32
32
  include Observer
33
33
 
34
- attr_reader :widget, :property
34
+ attr_reader :widget, :property, :model_binding
35
+
35
36
  def initialize(widget, property, sync_exec: nil, async_exec: nil)
36
37
  @widget = widget
37
38
  @property = property
@@ -40,17 +41,35 @@ module Glimmer
40
41
  SWT::DisplayProxy.instance.auto_exec(override_sync_exec: @sync_exec, override_async_exec: @async_exec) do
41
42
  if @widget.is_a?(Glimmer::SWT::WidgetProxy) && @widget.respond_to?(:on_widget_disposed)
42
43
  @widget.on_widget_disposed do |dispose_event|
43
- deregister_all_observables unless @widget.shell_proxy.last_shell_closing?
44
+ unless @widget.shell_proxy.last_shell_closing?
45
+ @widget_proxy.widget_bindings.delete(self)
46
+ deregister_all_observables
47
+ end
44
48
  end
45
49
  end
46
50
  # TODO look into hooking on_shape_disposed without slowing down shapes in samples like Tetris
47
51
  end
52
+ # TODO try to come up with a more comprehensive and cleaner solution to miscallenous objects like MessageBoxProxy with regards to @widget_proxy
53
+ @widget_proxy = widget.is_a?(Glimmer::SWT::WidgetProxy) ? widget : (widget.respond_to?(:body_root) ? widget.body_root : widget)
54
+ end
55
+
56
+ def observe(*args)
57
+ if @widget_proxy.respond_to?(:widget_bindings)
58
+ # TODO try to come up with a more comprehensive and cleaner solution to miscallenous objects like MessageBoxProxy with regards to the following code
59
+ # assumes only one observation
60
+ @model_binding = args.first if args.size == 1
61
+ @widget_proxy.widget_bindings << self
62
+ end
63
+ super
48
64
  end
49
65
 
50
66
  def call(value)
51
67
  SWT::DisplayProxy.instance.auto_exec(override_sync_exec: @sync_exec, override_async_exec: @async_exec) do
52
68
  if @widget.respond_to?(:disposed?) && @widget.disposed?
53
- deregister_all_observables unless @widget.shell_proxy.last_shell_closing?
69
+ unless @widget.shell_proxy.last_shell_closing?
70
+ @widget_proxy.widget_bindings.delete(self)
71
+ deregister_all_observables
72
+ end
54
73
  return
55
74
  end
56
75
  # need the rescue false for a scenario with tree items not being equal to model objects raising an exception
@@ -62,7 +81,10 @@ module Glimmer
62
81
 
63
82
  def evaluate_property
64
83
  if @widget.respond_to?(:disposed?) && @widget.disposed?
65
- deregister_all_observables unless @widget.shell_proxy.last_shell_closing?
84
+ unless @widget.shell_proxy.last_shell_closing?
85
+ @widget_proxy.widget_bindings.delete(self)
86
+ deregister_all_observables
87
+ end
66
88
  return
67
89
  end
68
90
  @widget.get_attribute(@property)
@@ -26,6 +26,7 @@ require 'glimmer/dsl/top_level_expression'
26
26
  require 'glimmer/ui/custom_widget'
27
27
  require 'glimmer/ui/custom_shell'
28
28
  require 'glimmer/swt/custom/code_text'
29
+ require 'glimmer/swt/custom/refined_table'
29
30
  require 'glimmer/swt/custom/radio_group'
30
31
  require 'glimmer/swt/custom/checkbox_group'
31
32
 
@@ -0,0 +1,235 @@
1
+ # Copyright (c) 2007-2022 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require 'glimmer/ui/custom_widget'
23
+
24
+ module Glimmer
25
+ module SWT
26
+ module Custom
27
+ # RefinedTable is a customization of Table with support for Filtering/Pagination
28
+ class RefinedTable
29
+ include Glimmer::UI::CustomWidget
30
+
31
+ option :per_page, default: 10
32
+ option :page, default: 0
33
+ option :model_array
34
+ option :query, default: ''
35
+
36
+ attr_accessor :filtered_model_array
37
+ attr_accessor :refined_model_array
38
+ attr_reader :table_proxy, :page_text_proxy, :first_button_proxy, :previous_button_proxy, :next_button_proxy, :last_button_proxy
39
+
40
+ before_body do
41
+ self.query ||= ''
42
+ self.model_array ||= []
43
+ self.filtered_model_array = []
44
+ self.refined_model_array = []
45
+ end
46
+
47
+ after_body do
48
+ Glimmer::DataBinding::Observer.proc do |new_widget_bindings|
49
+ new_widget_bindings.each do |new_widget_binding|
50
+ if new_widget_binding.property.to_s == 'model_array' && !@data_bound
51
+ @data_bound = true
52
+ model_binding = new_widget_binding.model_binding
53
+ observe(self, :model_array) do
54
+ filter_and_paginate
55
+ end
56
+ @table_proxy.content {
57
+ items(dsl: true) <=> [self, :refined_model_array, model_binding.binding_options.merge(read_only: true)]
58
+ }
59
+ filter_and_paginate
60
+ end
61
+ end
62
+ end.observe(body_root.widget_bindings)
63
+ end
64
+
65
+ body {
66
+ composite {
67
+ text(:search, :border) {
68
+ layout_data(:fill, :center, true, false)
69
+
70
+ text <=> [self, :query, after_write: -> { filter_and_paginate }]
71
+ }
72
+
73
+ pagination
74
+
75
+ @children_owner = @table_proxy = table(swt_style) {
76
+ layout_data(:fill, :fill, true, true)
77
+ }
78
+ }
79
+ }
80
+
81
+ def pagination
82
+ composite {
83
+ layout_data(:fill, :center, true, false)
84
+
85
+ fill_layout(:horizontal)
86
+
87
+ @first_button_proxy = button {
88
+ text '<<'
89
+ enabled <= [self, :page, on_read: ->(value) {value > first_page}]
90
+
91
+ on_widget_selected do
92
+ self.page = first_page
93
+ filter_and_paginate
94
+ end
95
+ }
96
+
97
+ @previous_button_proxy = button {
98
+ text '<'
99
+ enabled <= [self, :page, on_read: ->(value) {value > first_page}]
100
+
101
+ on_widget_selected do
102
+ self.page -= 1
103
+ filter_and_paginate
104
+ end
105
+ }
106
+
107
+ @page_text_proxy = text(:border, :center) {
108
+ text <= [self, :page, on_read: ->(value) { "#{value} of #{page_count}" }]
109
+
110
+ on_focus_gained do
111
+ @page_text_proxy.select_all
112
+ end
113
+
114
+ on_focus_lost do
115
+ self.page = @page_text_proxy.text.to_i
116
+ filter_and_paginate
117
+ end
118
+
119
+ on_key_pressed do |key_event|
120
+ if key_event.keyCode == swt(:cr)
121
+ self.page = @page_text_proxy.text.to_i
122
+ filter_and_paginate
123
+ end
124
+ end
125
+ }
126
+
127
+ @next_button_proxy = button {
128
+ text '>'
129
+ enabled <= [self, :page, on_read: ->(value) {value < last_page}]
130
+
131
+ on_widget_selected do
132
+ self.page += 1
133
+ filter_and_paginate
134
+ end
135
+ }
136
+
137
+ @last_button_proxy = button {
138
+ text '>>'
139
+ enabled <= [self, :page, on_read: ->(value) {value < last_page}]
140
+
141
+ on_widget_selected do
142
+ self.page = last_page
143
+ filter_and_paginate
144
+ end
145
+ }
146
+ }
147
+ end
148
+
149
+ def table_block=(block)
150
+ @table_proxy.content(&block)
151
+ end
152
+
153
+ def page_text_block=(block)
154
+ @page_text_proxy.content(&block)
155
+ end
156
+
157
+ def first_button_block=(block)
158
+ @first_button_proxy.content(&block)
159
+ end
160
+
161
+ def previous_button_block=(block)
162
+ @previous_button_proxy.content(&block)
163
+ end
164
+
165
+ def next_button_block=(block)
166
+ @next_button_proxy.content(&block)
167
+ end
168
+
169
+ def last_button_block=(block)
170
+ @last_button_proxy.content(&block)
171
+ end
172
+
173
+ def method_missing(method_name, *args, &block)
174
+ dsl_mode = @dsl_mode || args.last.is_a?(Hash) && args.last[:dsl]
175
+ if dsl_mode
176
+ args.pop if args.last.is_a?(Hash) && args.last[:dsl]
177
+ super(method_name, *args, &block)
178
+ elsif @table_proxy&.respond_to?(method_name, *args, &block)
179
+ @table_proxy&.send(method_name, *args, &block)
180
+ else
181
+ super
182
+ end
183
+ end
184
+
185
+ def respond_to?(method_name, *args, &block)
186
+ dsl_mode = @dsl_mode || args.last.is_a?(Hash) && args.last[:dsl]
187
+ if dsl_mode
188
+ args = args[0...-1] if args.last.is_a?(Hash) && args.last[:dsl]
189
+ super(method_name, *args, &block)
190
+ else
191
+ super || @table_proxy&.respond_to?(method_name, *args, &block)
192
+ end
193
+ end
194
+
195
+ def page_count
196
+ (filtered_model_array && (filtered_model_array.count / per_page.to_f).ceil) || 0
197
+ end
198
+
199
+ def corrected_page(initial_page_value = nil)
200
+ correct_page = initial_page_value || page
201
+ correct_page = [correct_page, page_count].min
202
+ correct_page = [correct_page, 1].max
203
+ correct_page = (filtered_model_array&.count.to_i > 0) ? (correct_page > 0 ? correct_page : 1) : 0
204
+ correct_page
205
+ end
206
+
207
+ def first_page
208
+ (filtered_model_array&.count.to_i > 0) ? 1 : 0
209
+ end
210
+
211
+ def last_page
212
+ page_count
213
+ end
214
+
215
+ def filter_and_paginate
216
+ filter
217
+ paginate
218
+ end
219
+
220
+ def filter
221
+ self.filtered_model_array = model_array.select do |model|
222
+ @table_proxy.cells_for(model).any? do |cell_text|
223
+ cell_text.to_s.downcase.include?(query.to_s.downcase)
224
+ end
225
+ end
226
+ end
227
+
228
+ def paginate
229
+ self.page = corrected_page(page)
230
+ self.refined_model_array = filtered_model_array[(page - 1) * per_page, per_page]
231
+ end
232
+ end
233
+ end
234
+ end
235
+ end
@@ -247,6 +247,7 @@ module Glimmer
247
247
 
248
248
  attr_reader :table_editor, :table_editor_widget_proxy, :sort_property, :sort_direction, :sort_block, :sort_type, :sort_by_block, :additional_sort_properties, :editor, :editable
249
249
  attr_writer :column_properties
250
+ attr_accessor :table_items_binding
250
251
  alias column_attributes= column_properties=
251
252
  alias editable? editable
252
253
 
@@ -159,7 +159,8 @@ module Glimmer
159
159
  @parent_proxy = parent
160
160
  styles, extra_options = extract_args(underscored_widget_name, args)
161
161
  swt_widget_class = self.class.swt_widget_class_for(underscored_widget_name)
162
- @swt_widget = swt_widget_class.new(@parent_proxy.swt_widget, style(underscored_widget_name, styles), *extra_options)
162
+ swt_widget_producer = @parent_proxy.respond_to?(:children_owner) ? @parent_proxy.children_owner : @parent_proxy
163
+ @swt_widget = swt_widget_class.new(swt_widget_producer.swt_widget, style(underscored_widget_name, styles), *extra_options)
163
164
  else
164
165
  @swt_widget = swt_widget
165
166
  underscored_widget_name = self.class.underscored_widget_name(@swt_widget)
@@ -769,6 +770,10 @@ module Glimmer
769
770
  end
770
771
  end
771
772
  end
773
+
774
+ def widget_bindings
775
+ @widget_bindings ||= []
776
+ end
772
777
 
773
778
  def content(&block)
774
779
  auto_exec do
@@ -271,6 +271,14 @@ module Glimmer
271
271
  "#{attribute_name}="
272
272
  end
273
273
 
274
+ def shell_proxy
275
+ @body_root.shell_proxy
276
+ end
277
+
278
+ def children_owner
279
+ @children_owner || @body_root
280
+ end
281
+
274
282
  def disposed?
275
283
  swt_widget.isDisposed
276
284
  end
@@ -122,7 +122,7 @@ class ContactManager
122
122
  }
123
123
  }
124
124
 
125
- table(:editable, :multi) { |table_proxy|
125
+ table(:editable, :border) { |table_proxy|
126
126
  layout_data {
127
127
  horizontal_alignment :fill
128
128
  vertical_alignment :fill
@@ -30,3 +30,4 @@ Hello, Custom Widget!: aJHLo5yLDZc
30
30
  Hello, Custom Shell!: c8Eb8GWM_XQ
31
31
  Hello, Custom Shape!: H3J8ecp30Ak
32
32
  Battleship: b00OWeLZOt8
33
+ Klondike Solitaire: qOzgiz9X3sI
@@ -0,0 +1,159 @@
1
+ # Copyright (c) 2007-2022 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require 'glimmer-dsl-swt'
23
+ require 'date'
24
+
25
+ class HelloRefinedTable
26
+ BaseballTeam = Struct.new(:name, :town, :ballpark, keyword_init: true) do
27
+ class << self
28
+ def all
29
+ @all ||= [
30
+ {town: 'Chicago', name: 'White Sox', ballpark: 'Guaranteed Rate Field'},
31
+ {town: 'Cleveland', name: 'Indians', ballpark: 'Progressive Field'},
32
+ {town: 'Detroit', name: 'Tigers', ballpark: 'Comerica Park'},
33
+ {town: 'Kansas City', name: 'Royals', ballpark: 'Kauffman Stadium'},
34
+ {town: 'Minnesota', name: 'Twins', ballpark: 'Target Field'},
35
+ {town: 'Baltimore', name: 'Orioles', ballpark: 'Oriole Park at Camden Yards'},
36
+ {town: 'Boston', name: 'Red Sox', ballpark: 'Fenway Park'},
37
+ {town: 'New York', name: 'Yankees', ballpark: 'Comerica Park'},
38
+ {town: 'Tampa Bay', name: 'Rays', ballpark: 'Tropicana Field'},
39
+ {town: 'Toronto', name: 'Blue Jays', ballpark: 'Rogers Centre'},
40
+ {town: 'Houston', name: 'Astros', ballpark: 'Minute Maid Park'},
41
+ {town: 'Los Angeles', name: 'Angels', ballpark: 'Angel Stadium'},
42
+ {town: 'Oakland', name: 'Athletics', ballpark: 'RingCentral Coliseum'},
43
+ {town: 'Seattle', name: 'Mariners', ballpark: 'T-Mobile Park'},
44
+ {town: 'Texas', name: 'Rangers', ballpark: 'Globe Life Field'},
45
+ {town: 'Chicago', name: 'Cubs', ballpark: 'Wrigley Field'},
46
+ {town: 'Cincinnati', name: 'Reds', ballpark: 'Great American Ball Park'},
47
+ {town: 'Milwaukee', name: 'Brewers', ballpark: 'American Family Field'},
48
+ {town: 'Pittsburgh', name: 'Pirates', ballpark: 'PNC Park'},
49
+ {town: 'St. Louis', name: 'Cardinals', ballpark: 'Busch Stadium'},
50
+ {town: 'Atlanta', name: 'Braves', ballpark: 'Truist Park'},
51
+ {town: 'Miami', name: 'Marlins', ballpark: 'LoanDepot Park'},
52
+ {town: 'New York', name: 'Mets', ballpark: 'Citi Field'},
53
+ {town: 'Philadelphia', name: 'Phillies', ballpark: 'Citizens Bank Park'},
54
+ {town: 'Washington', name: 'Nationals', ballpark: 'Nationals Park'},
55
+ {town: 'Arizona', name: 'Diamondbacks', ballpark: 'Chase Field'},
56
+ {town: 'Colorado', name: 'Rockies', ballpark: 'Coors Field'},
57
+ {town: 'Los Angeles', name: 'Dodgers', ballpark: 'Dodger Stadium'},
58
+ {town: 'San Diego', name: 'Padres', ballpark: 'Petco Park'},
59
+ {town: 'San Francisco', name: 'Giants', ballpark: 'Oracle Park'},
60
+ ].map {|team_kwargs| new(team_kwargs)}
61
+ end
62
+ end
63
+
64
+ def complete_name
65
+ "#{town} #{name}"
66
+ end
67
+ end
68
+
69
+ BaseballGame = Struct.new(:date, :home_team, :away_team, keyword_init: true) do
70
+ def home_team_name
71
+ home_team.complete_name
72
+ end
73
+
74
+ def away_team_name
75
+ away_team.complete_name
76
+ end
77
+
78
+ def ballpark
79
+ home_team.ballpark
80
+ end
81
+ end
82
+
83
+ BaseballSeason = Struct.new(:year) do
84
+ def games
85
+ if @games.nil?
86
+ @games = []
87
+ baseball_team_combinations = BaseballTeam.all.combination(2).to_a
88
+ current_day = first_day
89
+ day_offset = 0
90
+ begin
91
+ if (day_offset % 7 != 6)
92
+ day_games = []
93
+ half_teams_count = BaseballTeam.all.count / 2
94
+ while day_games.uniq.count < half_teams_count
95
+ baseball_team_pair = baseball_team_combinations.sample
96
+ teams_played_so_far = day_games.map {|game| [game.home_team, game.away_team]}.flatten
97
+ unless teams_played_so_far.include?(baseball_team_pair.first) || teams_played_so_far.include?(baseball_team_pair.last)
98
+ baseball_game = BaseballGame.new(
99
+ date: current_day,
100
+ home_team: baseball_team_pair.first,
101
+ away_team: baseball_team_pair.last,
102
+ )
103
+ day_games << baseball_game
104
+ @games << baseball_game
105
+ end
106
+ end
107
+ end
108
+ day_offset += 1
109
+ current_day += 1
110
+ end while current_day != first_day_of_playoffs
111
+ end
112
+ @games
113
+ end
114
+
115
+ def first_day
116
+ @first_day ||= Date.new(year, 04, 01)
117
+ end
118
+
119
+ def first_day_of_playoffs
120
+ @last_day ||= Date.new(year, 10, 01)
121
+ end
122
+ end
123
+
124
+ include Glimmer::UI::Application
125
+
126
+ before_body do
127
+ @baseball_season = BaseballSeason.new(Time.now.year)
128
+ end
129
+
130
+ body {
131
+ shell {
132
+ text 'Hello, Refined Table!'
133
+
134
+ refined_table(per_page: 20) { # also `page: 1` by default
135
+ table_column {
136
+ width 100
137
+ text 'Date'
138
+ }
139
+ table_column {
140
+ width 200
141
+ text 'Ballpark'
142
+ }
143
+ table_column {
144
+ width 150
145
+ text 'Home Team'
146
+ }
147
+ table_column {
148
+ width 150
149
+ text 'Away Team'
150
+ }
151
+
152
+ model_array <= [@baseball_season, :games, column_attributes: {'Home Team' => :home_team_name, 'Away Team' => :away_team_name}]
153
+ }
154
+ }
155
+ }
156
+
157
+ end
158
+
159
+ HelloRefinedTable.launch
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: glimmer-dsl-swt
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.24.3.2
4
+ version: 4.24.4.2
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-09-02 00:00:00.000000000 Z
11
+ date: 2022-09-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -344,17 +344,17 @@ dependencies:
344
344
  version: 0.14.1.cr2
345
345
  description: Glimmer DSL for SWT (JRuby Desktop Development GUI Framework) is a native-GUI
346
346
  cross-platform desktop development library written in JRuby, an OS-threaded faster
347
- JVM version of Ruby. It includes SWT 4.22 (released November 24, 2021). Glimmer's
348
- main innovation is a declarative Ruby DSL that enables productive and efficient
349
- authoring of desktop application user-interfaces by relying on the robust Eclipse
350
- SWT library. Glimmer additionally innovates by having built-in data-binding support,
351
- which greatly facilitates synchronizing the GUI with domain models, thus achieving
352
- true decoupling of object oriented components and enabling developers to solve business
353
- problems (test-first) without worrying about GUI concerns, or alternatively drive
354
- development GUI-first, and then write clean business models (test-first) afterwards.
355
- Not only does Glimmer provide a large set of GUI widgets, but it also supports drawing
356
- Canvas Graphics like Shapes and Animations. To get started quickly, Glimmer offers
357
- scaffolding options for Apps, Gems, and Custom Widgets. Glimmer also includes native-executable
347
+ JVM version of Ruby. It includes SWT 4.24 (released June 7, 2022). Glimmer's main
348
+ innovation is a declarative Ruby DSL that enables productive and efficient authoring
349
+ of desktop application user-interfaces by relying on the robust Eclipse SWT library.
350
+ Glimmer additionally innovates by having built-in data-binding support, which greatly
351
+ facilitates synchronizing the GUI with domain models, thus achieving true decoupling
352
+ of object oriented components and enabling developers to solve business problems
353
+ (test-first) without worrying about GUI concerns, or alternatively drive development
354
+ GUI-first, and then write clean business models (test-first) afterwards. Not only
355
+ does Glimmer provide a large set of GUI widgets, but it also supports drawing Canvas
356
+ Graphics like Shapes and Animations. To get started quickly, Glimmer offers scaffolding
357
+ options for Apps, Gems, and Custom Widgets. Glimmer also includes native-executable
358
358
  packaging support, sorely lacking in other libraries, thus enabling the delivery
359
359
  of desktop apps written in Ruby as truly native DMG/PKG/APP files on the Mac, MSI/EXE
360
360
  files on Windows, and DEB/RPM files on Linux. Glimmer was the first Ruby gem to
@@ -472,6 +472,7 @@ files:
472
472
  - lib/glimmer/swt/custom/code_text.rb
473
473
  - lib/glimmer/swt/custom/drawable.rb
474
474
  - lib/glimmer/swt/custom/radio_group.rb
475
+ - lib/glimmer/swt/custom/refined_table.rb
475
476
  - lib/glimmer/swt/custom/shape.rb
476
477
  - lib/glimmer/swt/custom/shape/arc.rb
477
478
  - lib/glimmer/swt/custom/shape/cubic.rb
@@ -670,6 +671,7 @@ files:
670
671
  - samples/hello/hello_progress_bar.rb
671
672
  - samples/hello/hello_radio.rb
672
673
  - samples/hello/hello_radio_group.rb
674
+ - samples/hello/hello_refined_table.rb
673
675
  - samples/hello/hello_sash_form.rb
674
676
  - samples/hello/hello_scale.rb
675
677
  - samples/hello/hello_scrolled_composite.rb