glimmer-dsl-swt 4.18.3.1 → 4.18.3.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: 0d88ee3cdf02d4610332fd450379b43ab5231e0a2e5e9941c746ea7b65bcad00
4
- data.tar.gz: db869412b61dd6c45002cd11ebf7cdb046d02b4de9b74769ec8cd90122eafbd9
3
+ metadata.gz: ed3de759196522f355d23f94734175e7bd1f5957b25637005d2b266f0868cd3d
4
+ data.tar.gz: 4cf0d426d9ab01d53de2c52246be5521bce14e963b1269568d8ecee6f0d01326
5
5
  SHA512:
6
- metadata.gz: 520388927363b967965f3afbbafdd753daf3c399547b87e5c60535620b12ffc694b05e14b5176b600fd1dcf7eecb4ee9c00ca41f4cbdda79ad138d2e593c836a
7
- data.tar.gz: 5d52780657cac19b47ccec883b163b56fd1823f1701abef6e01e8b9b3109479648999506eb3dea2cb831826ab88eb562faf1c1a961f8d9c85620cfc956da7432
6
+ metadata.gz: f2a0569e88e07cedbd989aae075e1bf03721f88dc647906e1ce7c16492a59d129f282dd8ce53f535e76027cb402b3ea07a5b56d1ac8d2303dbdc468c1cd17741
7
+ data.tar.gz: d58989c1839bbe0d15e8fd8a96cdfb917a4c2a2bc9cc0382e2271ef40445b0b891d3ddd06e8f05b361535cfa57915e57e8cbbca0e2d19c2b2425670addfdbfba
@@ -1,5 +1,16 @@
1
1
  # Change Log
2
2
 
3
+ ### 4.18.3.2
4
+
5
+ - Tetris High Scores
6
+ - Tetris Modify High Score Player Name
7
+ - Tetris Show High Scores (Menu Item + Accelerator)
8
+ - Tetris add a menu item with beep enablement option
9
+ - Tetris Clear High Scores
10
+ - Tetris Add left and right alt (option) buttons as alternative to shift for rotation. Use left ctrl as rotate left. Use a, s, d as left, down, right.
11
+ - Fix issues relating to setting parenthood with custom widgets before building their body (instead of after)
12
+ - Fix issues relating to not respecting arity of passed in table editing callbacks: before_write, after_write, and after_cancel
13
+
3
14
  ### 4.18.3.1
4
15
 
5
16
  - Provide an auto_sync_exec all data-binding config option to automatically sync_exec GUI calls from other threads instead of requiring users to use sync_exec on model attribute-change logic. Default value to false.
@@ -10,9 +21,9 @@
10
21
  - Supporting deregistering Display listeners just like standard listeners via deregister
11
22
  - Enhance performance of excluded keyword check
12
23
  - Remove CustomWidget support for multiple before_body/after_body blocks instead of one each since it is not needed.
24
+ - Add new :fill_screen style for `shell` to start app filling the screen size (not full screen mode though)
13
25
  - Tetris Menu Bar with Game Menu -> Start, Pause, Restart, and Exit
14
26
  - Tetris refactor mutation methods to end with bangs
15
- - Add new :fill_screen style for `shell` to start app filling the screen size (not full screen mode though)
16
27
  - Tetris Stop game if user does not play again in the end (instead of closing it)
17
28
  - End Tetris Thread loop gracefully if game over is encountered
18
29
  - Tetris use more observers instead of callbacks to Game
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.18.3.1
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.18.3.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)
@@ -241,6 +241,7 @@ If you see anything that needs to be improved, please do not hesitate to contact
241
241
  - [Desktop Apps Built with Glimmer DSL for SWT](#desktop-apps-built-with-glimmer-dsl-for-swt)
242
242
  - [Table of contents](#table-of-contents)
243
243
  - [Background](#background)
244
+ - [Software Architecture](#software-architecture)
244
245
  - [Platform Support](#platform-support)
245
246
  - [Pre-requisites](#pre-requisites)
246
247
  - [Setup](#setup)
@@ -407,6 +408,38 @@ If you see anything that needs to be improved, please do not hesitate to contact
407
408
 
408
409
  [Ruby](https://www.ruby-lang.org) is a dynamically-typed object-oriented language, which provides great productivity gains due to its powerful expressive syntax and dynamic nature. While it is proven by the [Ruby](https://www.ruby-lang.org) on Rails framework for web development, it currently lacks a robust platform-independent framework for building desktop applications. Given that Java libraries can now be utilized in Ruby code through JRuby, Eclipse technologies, such as [SWT](https://www.eclipse.org/swt/), JFace, and RCP can help fill the gap of desktop application development with Ruby.
409
410
 
411
+ ## Software Architecture
412
+
413
+ There are several requirements for building enterprise-level/consumer-level desktop GUI applications:
414
+ - Cross-Platform Support (Mac, Windows, Linux) without compilation/recompilation
415
+ - OS Native Look & Feel
416
+ - High Performance
417
+ - Productivity
418
+ - Maintainability
419
+ - Extensibility
420
+ - Native Executable Packaging
421
+ - Multi-Threading / Parallel Programming
422
+ - Arbitrary Graphics Painting
423
+ - Audio Support
424
+
425
+ Glimmer provides cross-platform support that does not require Ruby compilation (like Tk does), thanks to JRuby, a JVM (Java Virtual Machine) faster OS-threaded version of Ruby.
426
+
427
+ Glimmer leverages SWT (Standard Widget Toolkit), which provides cross-platform widgets that automatically use the native GUI libraries under each operating system, such as Win32 on Windows, Cocoa on Mac, and GTK on Linux.
428
+
429
+ Furthermore, what is special about SWT regarding "High Performance" is that it does all the GUI painting natively outside of Java, thus producing GUI that runs at maximum performance even in Ruby. As such, you do not need to worry about Ruby dynamic typing getting in the way of GUI performance. It has ZERO effect on it and since SWT supports making asynchronous calls for GUI rendering, you could avoid blocking the GUI completely with any computations happening in Ruby no matter how complex, thus never affecting the responsiveness of GUI of applications while taking full advantage of the productivity benefits of Ruby dynamic typing.
430
+
431
+ Glimmer takes this further by providing a very programmer friendly DSL (Domain Specific Language) that visually maps lightweight Ruby syntax to the containment hierarchy of GUI widgets (meaning Ruby blocks nested within each other map to GUI widgets nested within each other). This provides maximum productivity and maintainability.
432
+
433
+ Extensibility has never been simpler in desktop GUI application development than with Glimmer, which provides the ability to support any new custom keywords through custom widgets and custom shells (windows). Basically, you map a keyword by declaring a view class matching its name by convention with a GUI body that simply consists of reusable Glimmer GUI syntax. They can be passive views or smart views with additional logic. This provides the ultimate realization of Object Oriented Programming and micro-level MVC pattern.
434
+
435
+ Thanks to Java and JRuby, Glimmer apps can be packaged as cross-platform JAR files (with JRuby Warbler) and native executables (with Java Packager) as Mac APP/DMG/PACKAGE or Windows EXE/MSI.
436
+
437
+ The Java Virtual Machine already supports OS-native threads, so Glimmer apps can have multiple things running in parallel with no problem.
438
+
439
+ SWT supports Canvas graphics drawing, and Glimmer takes that further by provding a Canvas Shape/Transform/Animation DSL, making it very simple to decorate any existing widgets or add new widgets with a completely custom look and feel if needed for branding or entertainment (gaming) purposes.
440
+
441
+ Audio is supported via the Java Sound library in a cross-platform approach and video is supported via a Glimmer custom widget, so any Glimmer app can be enhanced with audio and video where needed.
442
+
410
443
  ## Platform Support
411
444
 
412
445
  Glimmer runs on the following platforms:
@@ -460,7 +493,7 @@ jgem install glimmer-dsl-swt
460
493
 
461
494
  Or this command if you want a specific version:
462
495
  ```
463
- jgem install glimmer-dsl-swt -v 4.18.3.1
496
+ jgem install glimmer-dsl-swt -v 4.18.3.2
464
497
 
465
498
 
466
499
  ```
@@ -480,7 +513,7 @@ Note: if you're using activerecord or activesupport, keep in mind that Glimmer u
480
513
 
481
514
  Add the following to `Gemfile`:
482
515
  ```
483
- gem 'glimmer-dsl-swt', '~> 4.18.3.1
516
+ gem 'glimmer-dsl-swt', '~> 4.18.3.2
484
517
  '
485
518
  ```
486
519
 
@@ -539,7 +572,7 @@ bin/glimmer samples
539
572
  Below are the full usage instructions that come up when running `glimmer` without args.
540
573
 
541
574
  ```
542
- Glimmer (JRuby Desktop Development GUI Framework) - JRuby Gem: glimmer-dsl-swt v4.18.3.1
575
+ Glimmer (JRuby Desktop Development GUI Framework) - JRuby Gem: glimmer-dsl-swt v4.18.3.2
543
576
 
544
577
 
545
578
 
@@ -815,7 +848,7 @@ glimmer run
815
848
 
816
849
  #### Custom Shell
817
850
 
818
- To scaffold a Glimmer custom shell (full window view) for an existing Glimmer app, run the following command:
851
+ To scaffold a Glimmer [custom shell](#custom-shells) (full window view) for an existing Glimmer app, run the following command:
819
852
 
820
853
  ```
821
854
  glimmer scaffold:customshell[name]
@@ -829,7 +862,7 @@ glimmer scaffold:cs[name]
829
862
 
830
863
  #### Custom Widget
831
864
 
832
- To scaffold a Glimmer custom widget (part of a view) for an existing Glimmer app, run the following command:
865
+ To scaffold a Glimmer [custom widget](#custom-widgets) (part of a view) for an existing Glimmer app, run the following command:
833
866
 
834
867
  ```
835
868
  glimmer scaffold:customwidget[name]
@@ -843,7 +876,7 @@ glimmer scaffold:cw[name]
843
876
 
844
877
  #### Custom Shell Gem
845
878
 
846
- Custom shell gems are self-contained Glimmer apps as well as reusable custom shells.
879
+ Custom shell gems are self-contained Glimmer apps as well as reusable [custom shells](#custom-shells).
847
880
  They have everything scaffolded Glimmer apps come with in addition to gem content like a [Juwelier](https://rubygems.org/gems/juwelier) Rakefile that can build gemspec and release gems.
848
881
  Unlike scaffolded Glimmer apps, custom shell gem content lives under the `lib` directory (not `app`).
849
882
  They can be packaged as both a native executable (e.g. Mac DMG/PKG/APP) and a Ruby gem.
@@ -878,7 +911,7 @@ Examples:
878
911
 
879
912
  #### Custom Widget Gem
880
913
 
881
- To scaffold a Glimmer custom widget gem (part of a view distributed as a Ruby gem), run the following command:
914
+ To scaffold a Glimmer [custom widget](#custom-widgets) gem (part of a view distributed as a Ruby gem), run the following command:
882
915
 
883
916
  ```
884
917
  glimmer scaffold:gem:customwidget[name,namespace]
@@ -904,7 +937,7 @@ Examples:
904
937
 
905
938
  ### Gem Listing
906
939
 
907
- The `glimmer` command comes with tasks for listing Glimmer related gems to make it easy to find Glimmer Custom Shells, Custom Widgets, and DSLs published by others in the Glimmer community on [rubygems.org](http://www.rubygems.org).
940
+ The `glimmer` command comes with tasks for listing Glimmer related gems to make it easy to find Glimmer [Custom Shells](#custom-shells), [Custom Widgets](#custom-widgets), and DSLs published by others in the Glimmer community on [rubygems.org](http://www.rubygems.org).
908
941
 
909
942
  #### Listing Custom Shell Gems
910
943
 
@@ -1020,7 +1053,7 @@ Output:
1020
1053
 
1021
1054
  Css glimmer-dsl-css 1.1.0 AndyMaleh Glimmer DSL for CSS
1022
1055
  Opal glimmer-dsl-opal 0.10.2 AndyMaleh Glimmer DSL for Opal
1023
- Swt glimmer-dsl-swt 4.18.3.1
1056
+ Swt glimmer-dsl-swt 4.18.3.2
1024
1057
 
1025
1058
  AndyMaleh Glimmer DSL for SWT
1026
1059
  Tk glimmer-dsl-tk 0.0.6 AndyMaleh Glimmer DSL for Tk
@@ -1098,9 +1131,12 @@ bin/girb
1098
1131
 
1099
1132
  Watch out for hands-on examples in this README indicated by "you may copy/paste in [`girb`](#girb-glimmer-irb-command)"
1100
1133
 
1101
- Keep in mind that all samples live under [https://github.com/AndyObtiva/glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt)
1134
+ Keep in mind that all samples live under [https://github.com/AndyObtiva/glimmer-dsl-swt/samples](https://github.com/AndyObtiva/glimmer-dsl-swt/samples)
1102
1135
 
1103
- If you need a more GUI interactive option to experiement with Glimmer GUI DSL Syntax, you may try the ["Ugliest Editor Ever"](https://github.com/AndyObtiva/glimmer-cs-gladiator) or just build your own using the [Glimmer DSL for SWT Ruby Gem](https://rubygems.org/gems/glimmer-dsl-swt).
1136
+ If you need a more GUI interactive option to experiement with Glimmer GUI DSL Syntax, you may try:
1137
+ - [Glimmer Meta-Sample (The Sample of Samples)](#samples): allows launching Glimmer samples and viewing/editing code to learn/experiment too.
1138
+ - ["Ugliest Editor Ever"](https://github.com/AndyObtiva/glimmer-cs-gladiator)
1139
+ - Just build your own GUI editor using the [Glimmer DSL for SWT Ruby Gem](https://rubygems.org/gems/glimmer-dsl-swt).
1104
1140
 
1105
1141
  ## Glimmer GUI DSL Syntax
1106
1142
 
@@ -3496,6 +3532,8 @@ shell { |app_shell|
3496
3532
  }.open
3497
3533
  ```
3498
3534
 
3535
+ If you use a Custom Shell as the top-level app shell, you may invoke the class method `::launch` instead to avoid building an app class yourself or including Glimmer into the top-level namespace (e.g. `Tetris.launch` instead of `include Glimmer; tetris.open`)
3536
+
3499
3537
  You may check out [Hello, Custom Shell!](#hello-custom-shell) for another example.
3500
3538
 
3501
3539
  ### Drag and Drop
@@ -4147,8 +4185,18 @@ end
4147
4185
 
4148
4186
  ### log_excluded_keywords
4149
4187
 
4188
+ (default = false)
4189
+
4150
4190
  This just tells Glimmer whether to log excluded keywords or not (at the debug level). It is off by default.
4151
4191
 
4192
+ ### auto_sync_exec
4193
+
4194
+ (default = false)
4195
+
4196
+ This automatically uses sync_exec on GUI calls from threads other than the main GUI thread instead of requiring users to manually use sync_exec. Default value to false.
4197
+
4198
+ Keep in mind the caveat that it would force redraws on every minor changein the models instead of applying large scope changes all together, thus causing too much drawing/stutter in the GUI. As such, this is a good fit for simpler GUIs, not ones used with highly sophisticated 2D graphics. It may be mitigated in the future by introducing the idea of large-scale observation events that wrap around smaller events. Until then, keep the caveat in mind or just use sync_exec manually as usually done with Java SWT apps.
4199
+
4152
4200
  ## Glimmer Style Guide
4153
4201
 
4154
4202
  - Widgets are declared with underscored lowercase versions of their SWT names minus the SWT package name.
@@ -4227,7 +4275,7 @@ You may edit the code of any sample before launching it by clicking on the "Laun
4227
4275
  Note that if you fail to run any sample through the Glimmer Meta-Sample for whatever reason, you could always run directly by cloning the project, running `bundle`, and then this command (drop the "bin" if you install the glimmer-dsl-swt gem instead):
4228
4276
 
4229
4277
  ```ruby
4230
- bin/glimmer samples/hello/hello_canvas_transform.rb
4278
+ bin/glimmer samples/hello/hello_canvas_transform.rb
4231
4279
  ```
4232
4280
 
4233
4281
  ### Hello Samples
@@ -4812,6 +4860,16 @@ Code:
4812
4860
 
4813
4861
  ![Tetris Game Over](images/glimmer-tetris-game-over.png)
4814
4862
 
4863
+ ![Tetris High Scores](images/glimmer-tetris-high-score-dialog.png)
4864
+
4865
+ ![Tetris Game Menu](images/glimmer-tetris-game-menu.png)
4866
+
4867
+ ![Tetris View Menu](images/glimmer-tetris-view-menu.png)
4868
+
4869
+ ![Tetris Options Menu](images/glimmer-tetris-options-menu.png)
4870
+
4871
+ ![Tetris Help Menu](images/glimmer-tetris-help-menu.png)
4872
+
4815
4873
  ### External Samples
4816
4874
 
4817
4875
  #### Glimmer Calculator
data/VERSION CHANGED
@@ -1 +1 @@
1
- 4.18.3.1
1
+ 4.18.3.2
@@ -2,16 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: glimmer-dsl-swt 4.18.3.1 ruby lib
5
+ # stub: glimmer-dsl-swt 4.18.3.2 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "glimmer-dsl-swt".freeze
9
- s.version = "4.18.3.1"
9
+ s.version = "4.18.3.2"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib".freeze]
13
13
  s.authors = ["AndyMaleh".freeze]
14
- s.date = "2021-01-27"
14
+ s.date = "2021-01-28"
15
15
  s.description = "Glimmer DSL for SWT (JRuby Desktop Development GUI Framework) is a native-GUI cross-platform desktop development library written in JRuby, an OS-threaded faster JVM version of Ruby. Glimmer's main innovation is a declarative Ruby DSL that enables productive and efficient authoring of desktop application user-interfaces by relying on the robust Eclipse SWT library. Glimmer additionally innovates by having built-in data-binding support, which greatly facilitates synchronizing the GUI with domain models, thus achieving true decoupling of object oriented components and enabling developers to solve business problems (test-first) without worrying about GUI concerns, or alternatively drive development GUI-first, and then write clean business models (test-first) afterwards. Not only does Glimmer provide a large set of GUI widgets, but it also supports drawing Canvas Graphics like Shapes and Animations. To get started quickly, Glimmer offers scaffolding options for Apps, Gems, and Custom Widgets. Glimmer also includes native-executable packaging support, sorely lacking in other libraries, thus enabling the delivery of desktop apps written in Ruby as truly native DMG/PKG/APP files on the Mac + App Store, MSI/EXE files on Windows, and Gem Packaged Shell Scripts on Linux.".freeze
16
16
  s.email = "andy.am@gmail.com".freeze
17
17
  s.executables = ["glimmer".freeze, "girb".freeze]
@@ -138,9 +138,10 @@ Gem::Specification.new do |s|
138
138
  "samples/elaborate/tetris.rb",
139
139
  "samples/elaborate/tetris/model/block.rb",
140
140
  "samples/elaborate/tetris/model/game.rb",
141
+ "samples/elaborate/tetris/model/past_game.rb",
141
142
  "samples/elaborate/tetris/model/tetromino.rb",
142
143
  "samples/elaborate/tetris/view/block.rb",
143
- "samples/elaborate/tetris/view/game_over_dialog.rb",
144
+ "samples/elaborate/tetris/view/high_score_dialog.rb",
144
145
  "samples/elaborate/tetris/view/playfield.rb",
145
146
  "samples/elaborate/tetris/view/score_lane.rb",
146
147
  "samples/elaborate/tetris/view/tetris_menu_bar.rb",
@@ -117,10 +117,12 @@ module Glimmer
117
117
  def nested?
118
118
  !swt_widget&.parent.nil?
119
119
  end
120
+ alias nested nested?
120
121
 
121
122
  def disposed?
122
123
  swt_widget.isDisposed
123
124
  end
125
+ alias disposed disposed?
124
126
 
125
127
  def hide
126
128
  @swt_widget.setVisible(false)
@@ -129,6 +131,7 @@ module Glimmer
129
131
  def visible?
130
132
  @swt_widget.isDisposed ? false : @swt_widget.isVisible
131
133
  end
134
+ alias visible visible?
132
135
 
133
136
  # Setting to true opens/shows shell. Setting to false hides the shell.
134
137
  def visible=(visibility)
@@ -265,6 +265,10 @@ module Glimmer
265
265
  end
266
266
  end
267
267
 
268
+ def items
269
+ swt_widget.get_items
270
+ end
271
+
268
272
  def model_binding
269
273
  swt_widget.data
270
274
  end
@@ -445,7 +449,6 @@ module Glimmer
445
449
  end
446
450
 
447
451
  def edit_table_item(table_item, column_index, before_write: nil, after_write: nil, after_cancel: nil)
448
- require 'facets/hash/symbolize_keys'
449
452
  return if table_item.nil?
450
453
  model = table_item.data
451
454
  property = column_properties[column_index]
@@ -467,7 +470,11 @@ module Glimmer
467
470
  @cancel_in_progress = true
468
471
  @table_editor_widget_proxy&.swt_widget&.dispose
469
472
  @table_editor_widget_proxy = nil
470
- after_cancel&.call
473
+ if after_cancel&.arity == 0
474
+ after_cancel&.call
475
+ else
476
+ after_cancel&.call(table_item)
477
+ end
471
478
  @edit_in_progress = false
472
479
  @cancel_in_progress = false
473
480
  @cancel_edit = nil
@@ -484,14 +491,22 @@ module Glimmer
484
491
  if new_value == model.send(model_editing_property)
485
492
  @cancel_edit.call
486
493
  else
487
- before_write&.call
494
+ if before_write&.arity == 0
495
+ before_write&.call
496
+ else
497
+ before_write&.call(edited_table_item)
498
+ end
488
499
  model.send("#{model_editing_property}=", new_value) # makes table update itself, so must search for selected table item again
489
500
  # Table refresh happens here because of model update triggering observers, so must retrieve table item again
490
501
  edited_table_item = search { |ti| ti.getData == model }.first
491
502
  swt_widget.showItem(edited_table_item)
492
503
  @table_editor_widget_proxy&.swt_widget&.dispose
493
504
  @table_editor_widget_proxy = nil
494
- after_write&.call(edited_table_item)
505
+ if after_write&.arity == 0
506
+ after_write&.call
507
+ else
508
+ after_write&.call(edited_table_item)
509
+ end
495
510
  @edit_in_progress = false
496
511
  end
497
512
  end
@@ -32,6 +32,7 @@ module Glimmer
32
32
 
33
33
  def launch(*args, &content)
34
34
  @launched_custom_shell = send(keyword, *args, &content) if @launched_custom_shell.nil? || @launched_custom_shell.disposed?
35
+ @launched_custom_shell.swt_widget.set_data('launched', true)
35
36
  @launched_custom_shell.open
36
37
  end
37
38
  end
@@ -163,6 +163,8 @@ module Glimmer
163
163
  attr_reader :body_root, :swt_widget, :parent, :parent_proxy, :swt_style, :options
164
164
 
165
165
  def initialize(parent, *swt_constants, options, &content)
166
+ @parent = parent
167
+ @parent_proxy = @parent&.get_data('proxy')
166
168
  @swt_style = SWT::SWTProxy[*swt_constants]
167
169
  options ||= {}
168
170
  @options = self.class.options.merge(options)
@@ -174,9 +176,6 @@ module Glimmer
174
176
  raise Glimmer::Error, 'Invalid custom widget for having an empty body! Please fill body block!' if @body_root.nil?
175
177
  @swt_widget = @body_root.swt_widget
176
178
  @swt_widget.set_data('custom_widget', self)
177
- @parent = parent
178
- @parent ||= @swt_widget.parent
179
- @parent_proxy ||= @parent&.get_data('proxy')
180
179
  execute_hook('after_body')
181
180
  end
182
181
 
@@ -254,6 +253,10 @@ module Glimmer
254
253
  def has_style?(style)
255
254
  (swt_style & SWT::SWTProxy[style]) == SWT::SWTProxy[style]
256
255
  end
256
+
257
+ def pack(*args)
258
+ body_root.pack(*args)
259
+ end
257
260
 
258
261
  # TODO see if it is worth it to eliminate duplication of async_exec/sync_exec
259
262
  # delegation to DisplayProxy, via a module
@@ -25,13 +25,16 @@ require_relative 'tetris/model/game'
25
25
 
26
26
  require_relative 'tetris/view/playfield'
27
27
  require_relative 'tetris/view/score_lane'
28
- require_relative 'tetris/view/game_over_dialog'
28
+ require_relative 'tetris/view/high_score_dialog'
29
29
  require_relative 'tetris/view/tetris_menu_bar'
30
30
 
31
31
  class Tetris
32
32
  include Glimmer::UI::CustomShell
33
33
 
34
34
  BLOCK_SIZE = 25
35
+ FONT_NAME = 'Menlo'
36
+ FONT_TITLE_HEIGHT = 32
37
+ FONT_TITLE_STYLE = :bold
35
38
 
36
39
  option :playfield_width, default: Model::Game::PLAYFIELD_WIDTH
37
40
  option :playfield_height, default: Model::Game::PLAYFIELD_HEIGHT
@@ -50,32 +53,36 @@ class Tetris
50
53
  display {
51
54
  @keyboard_listener = on_swt_keydown { |key_event|
52
55
  case key_event.keyCode
53
- when swt(:arrow_down)
56
+ when swt(:arrow_down), 's'.bytes.first
54
57
  game.down!
55
- when swt(:arrow_left)
58
+ when swt(:arrow_left), 'a'.bytes.first
56
59
  game.left!
57
- when swt(:arrow_right)
60
+ when swt(:arrow_right), 'd'.bytes.first
58
61
  game.right!
59
- when swt(:shift)
62
+ when swt(:shift), swt(:alt)
60
63
  if key_event.keyLocation == swt(:right) # right shift key
61
64
  game.rotate!(:right)
62
65
  elsif key_event.keyLocation == swt(:left) # left shift key
63
66
  game.rotate!(:left)
64
67
  end
65
- when 'd'.bytes.first, swt(:arrow_up)
68
+ when swt(:arrow_up)
66
69
  game.rotate!(:right)
67
- when 'a'.bytes.first
70
+ when swt(:ctrl)
68
71
  game.rotate!(:left)
69
72
  end
70
73
  }
74
+
75
+ # if running in app mode, set the Mac app about dialog (ignored in platforms)
76
+ @about_observer = on_about {
77
+ show_about_dialog
78
+ }
71
79
  }
72
80
  }
73
81
 
74
82
  after_body {
75
83
  @game_over_observer = observe(@game, :game_over) do |game_over|
76
84
  if game_over
77
- @game_over_dialog = game_over_dialog(parent_shell: body_root, game: @game) if @game_over_dialog.nil? || @game_over_dialog.disposed?
78
- @game_over_dialog.show
85
+ show_high_score_dialog
79
86
  else
80
87
  start_moving_tetrominos_down
81
88
  end
@@ -92,19 +99,19 @@ class Tetris
92
99
  margin_height 0
93
100
  horizontal_spacing 0
94
101
  }
95
-
102
+
96
103
  text 'Glimmer Tetris'
97
104
  minimum_size 475, 500
98
105
  background :gray
99
-
106
+
100
107
  tetris_menu_bar(game: game)
101
-
108
+
102
109
  playfield(game_playfield: game.playfield, playfield_width: playfield_width, playfield_height: playfield_height, block_size: BLOCK_SIZE)
103
-
110
+
104
111
  score_lane(game: game, block_size: BLOCK_SIZE) {
105
112
  layout_data(:fill, :fill, true, true)
106
113
  }
107
-
114
+
108
115
  on_widget_disposed {
109
116
  deregister_observers
110
117
  }
@@ -126,11 +133,24 @@ class Tetris
126
133
  end
127
134
  end
128
135
 
136
+ def show_high_score_dialog
137
+ return if @high_score_dialog&.visible?
138
+ @high_score_dialog = high_score_dialog(parent_shell: body_root, game: @game) if @high_score_dialog.nil? || @high_score_dialog.disposed?
139
+ @high_score_dialog.show
140
+ end
141
+
142
+ def show_about_dialog
143
+ message_box {
144
+ text 'Glimmer Tetris'
145
+ message "Glimmer Tetris\n\nGlimmer DSL for SWT Sample\n\nCopyright (c) 2007-2021 Andy Maleh"
146
+ }.open
147
+ end
148
+
129
149
  def deregister_observers
130
- @game_over_observer&.deregister
131
- @game_over_observer = nil
132
- @keyboard_listener&.deregister
133
- @keyboard_listener = nil
150
+ @show_high_scores_observer.deregister
151
+ @game_over_observer.deregister
152
+ @keyboard_listener.deregister
153
+ @about_observer&.deregister
134
154
  end
135
155
  end
136
156
 
@@ -19,8 +19,14 @@
19
19
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
20
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
21
 
22
+ require 'fileutils'
23
+ require 'etc'
24
+ require 'glimmer/data_binding/observer'
25
+ require 'glimmer/config'
26
+
22
27
  require_relative 'block'
23
28
  require_relative 'tetromino'
29
+ require_relative 'past_game'
24
30
 
25
31
  class Tetris
26
32
  module Model
@@ -32,13 +38,18 @@ class Tetris
32
38
  SCORE_MULTIPLIER = {1 => 40, 2 => 100, 3 => 300, 4 => 1200}
33
39
 
34
40
  attr_reader :playfield_width, :playfield_height
35
- attr_accessor :game_over, :paused, :preview_tetromino, :lines, :score, :level
41
+ attr_accessor :game_over, :paused, :preview_tetromino, :lines, :score, :level, :high_scores, :beeping, :added_high_score
36
42
  alias game_over? game_over
37
43
  alias paused? paused
44
+ alias beeping? beeping
45
+ alias added_high_score? added_high_score
38
46
 
39
47
  def initialize(playfield_width = PLAYFIELD_WIDTH, playfield_height = PLAYFIELD_HEIGHT)
40
48
  @playfield_width = playfield_width
41
49
  @playfield_height = playfield_height
50
+ @high_scores = []
51
+ @beeping = true
52
+ load_high_scores!
42
53
  end
43
54
 
44
55
  def configure_beeper(&beeper)
@@ -63,10 +74,51 @@ class Tetris
63
74
  end
64
75
  alias restart! start!
65
76
 
77
+ def game_over!
78
+ add_high_score!
79
+ beep
80
+ self.game_over = true
81
+ end
82
+
83
+ def clear_high_scores!
84
+ high_scores.clear
85
+ end
86
+
87
+ def add_high_score!
88
+ self.added_high_score = true
89
+ high_scores.prepend(PastGame.new("Player #{high_scores.count + 1}", score))
90
+ end
91
+
92
+ def save_high_scores!
93
+ high_score_file_content = @high_scores.map {|past_game| past_game.to_a.join("\t") }.join("\n")
94
+ FileUtils.mkdir_p(tetris_dir)
95
+ File.write(tetris_high_score_file, high_score_file_content)
96
+ rescue => e
97
+ # Fail safely by keeping high scores in memory if unable to access disk
98
+ Glimmer::Config.logger.error {"Failed to save high scores in: #{tetris_high_score_file}\n#{e.full_message}"}
99
+ end
100
+
101
+ def load_high_scores!
102
+ if File.exist?(tetris_high_score_file)
103
+ self.high_scores = File.read(tetris_high_score_file).split("\n").map {|line| PastGame.new(*line.split("\t")) }
104
+ end
105
+ rescue => e
106
+ # Fail safely by keeping high scores in memory if unable to access disk
107
+ Glimmer::Config.logger.error {"Failed to load high scores from: #{tetris_high_score_file}\n#{e.full_message}"}
108
+ end
109
+
110
+ def tetris_dir
111
+ @tetris_dir ||= File.join(Etc.getpwuid.dir, '.glimmer-tetris')
112
+ end
113
+
114
+ def tetris_high_score_file
115
+ File.join(tetris_dir, "high_scores.txt")
116
+ end
117
+
66
118
  def down!
67
119
  return unless game_in_progress?
68
120
  current_tetromino.down!
69
- self.game_over = true if current_tetromino.row <= 0 && current_tetromino.stopped?
121
+ game_over! if current_tetromino.row <= 0 && current_tetromino.stopped?
70
122
  end
71
123
 
72
124
  def right!
@@ -147,7 +199,7 @@ class Tetris
147
199
  end
148
200
 
149
201
  def beep
150
- @beeper&.call
202
+ @beeper&.call if beeping
151
203
  end
152
204
 
153
205
  def reset_tetrominoes
@@ -19,50 +19,8 @@
19
19
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
20
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
21
 
22
- require_relative 'tetris_menu_bar'
23
-
24
22
  class Tetris
25
- module View
26
- class GameOverDialog
27
- include Glimmer::UI::CustomShell
28
-
29
- options :parent_shell, :game
30
-
31
- after_body {
32
- observe(game, :game_over) do |game_over|
33
- hide if !game_over
34
- end
35
- }
36
-
37
- body {
38
- dialog(parent_shell) {
39
- row_layout {
40
- type :vertical
41
- center true
42
- }
43
- text 'Tetris'
44
-
45
- tetris_menu_bar(game: game)
46
-
47
- label(:center) {
48
- text 'Game Over!'
49
- font name: 'Menlo', height: 30, style: :bold
50
- }
51
- label # filler
52
- button {
53
- text 'Play Again?'
54
-
55
- on_widget_selected {
56
- hide
57
- game.restart!
58
- }
59
- }
60
-
61
- on_shell_activated {
62
- display.beep
63
- }
64
- }
65
- }
66
- end
23
+ module Model
24
+ PastGame = Struct.new(:name, :score)
67
25
  end
68
26
  end
@@ -0,0 +1,114 @@
1
+ # Copyright (c) 2007-2021 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require_relative 'tetris_menu_bar'
23
+
24
+ class Tetris
25
+ module View
26
+ class HighScoreDialog
27
+ include Glimmer::UI::CustomShell
28
+
29
+ options :parent_shell, :game
30
+
31
+ after_body {
32
+ @game_over_observer = observe(game, :game_over) do |game_over|
33
+ close if !game_over
34
+ end
35
+ }
36
+
37
+ body {
38
+ dialog(parent_shell) {
39
+ row_layout {
40
+ type :vertical
41
+ center true
42
+ }
43
+ text 'Tetris'
44
+
45
+ tetris_menu_bar(game: game)
46
+
47
+ label(:center) {
48
+ text bind(game, :game_over) {|game_over| game_over ? 'Game Over!' : 'High Scores'}
49
+ font name: FONT_NAME, height: FONT_TITLE_HEIGHT, style: FONT_TITLE_STYLE
50
+ }
51
+ @high_score_table = table {
52
+ layout_data {
53
+ height 100
54
+ }
55
+
56
+ table_column {
57
+ text 'Name'
58
+ }
59
+ table_column {
60
+ text 'Score'
61
+ }
62
+
63
+ items bind(game, :high_scores), column_properties(:name, :score)
64
+ }
65
+ composite {
66
+ row_layout :horizontal
67
+
68
+ button {
69
+ text 'Clear'
70
+
71
+ on_widget_selected {
72
+ game.clear_high_scores!
73
+ }
74
+ }
75
+ @play_close_button = button {
76
+ text bind(game, :game_over) {|game_over| game_over ? 'Play Again?' : 'Close'}
77
+ focus true # initial focus
78
+
79
+ on_widget_selected {
80
+ close
81
+ game.restart! if game.game_over?
82
+ }
83
+ }
84
+ }
85
+
86
+ on_swt_show {
87
+ if game.game_over? && game.added_high_score?
88
+ game.added_high_score = false
89
+ @high_score_table.edit_table_item(
90
+ @high_score_table.items.first, # row item
91
+ 0, # column
92
+ after_write: -> {
93
+ game.save_high_scores!
94
+ @play_close_button.set_focus
95
+ },
96
+ after_cancel: -> {
97
+ @play_close_button.set_focus
98
+ },
99
+ )
100
+ end
101
+ }
102
+
103
+ on_shell_closed {
104
+ @high_score_table.cancel_edit!
105
+ }
106
+
107
+ on_widget_disposed {
108
+ @game_over_observer.deregister
109
+ }
110
+ }
111
+ }
112
+ end
113
+ end
114
+ end
@@ -30,8 +30,8 @@ class Tetris
30
30
  options :block_size, :game
31
31
 
32
32
  before_body {
33
- @font_name = 'Menlo'
34
- @font_height = 32
33
+ @font_name = FONT_NAME
34
+ @font_height = FONT_TITLE_HEIGHT
35
35
  }
36
36
 
37
37
  body {
@@ -46,13 +46,13 @@ class Tetris
46
46
  }
47
47
  label(:center) {
48
48
  text 'Next'
49
- font name: @font_name, height: @font_height, style: :bold
49
+ font name: @font_name, height: @font_height, style: FONT_TITLE_STYLE
50
50
  }
51
51
  playfield(game_playfield: game.preview_playfield, playfield_width: Model::Game::PREVIEW_PLAYFIELD_WIDTH, playfield_height: Model::Game::PREVIEW_PLAYFIELD_HEIGHT, block_size: block_size)
52
52
 
53
53
  label(:center) {
54
54
  text 'Score'
55
- font name: @font_name, height: @font_height, style: :bold
55
+ font name: @font_name, height: @font_height, style: FONT_TITLE_STYLE
56
56
  }
57
57
  label(:center) {
58
58
  text bind(game, :score)
@@ -63,7 +63,7 @@ class Tetris
63
63
 
64
64
  label(:center) {
65
65
  text 'Lines'
66
- font name: @font_name, height: @font_height, style: :bold
66
+ font name: @font_name, height: @font_height, style: FONT_TITLE_STYLE
67
67
  }
68
68
  label(:center) {
69
69
  text bind(game, :lines)
@@ -74,7 +74,7 @@ class Tetris
74
74
 
75
75
  label(:center) {
76
76
  text 'Level'
77
- font name: @font_name, height: @font_height, style: :bold
77
+ font name: @font_name, height: @font_height, style: FONT_TITLE_STYLE
78
78
  }
79
79
  label(:center) {
80
80
  text bind(game, :level)
@@ -49,7 +49,6 @@ class Tetris
49
49
  menu_item {
50
50
  text '&Restart'
51
51
  accelerator :command, :r
52
- enabled bind(game, :game_over, on_read: :!)
53
52
 
54
53
  on_widget_selected {
55
54
  game.restart!
@@ -64,9 +63,61 @@ class Tetris
64
63
  parent_proxy.close
65
64
  }
66
65
  }
67
- }
66
+ } # end of menu
67
+
68
+ menu {
69
+ text '&View'
70
+
71
+ menu {
72
+ text '&High Scores'
73
+ menu_item {
74
+ text '&Show'
75
+ accelerator :command, :shift, :h
76
+
77
+ on_widget_selected {
78
+ parent_custom_shell&.show_high_score_dialog
79
+ }
80
+ }
81
+ menu_item {
82
+ text '&Clear'
83
+ accelerator :command, :shift, :c
84
+
85
+ on_widget_selected {
86
+ game.clear_high_scores!
87
+ }
88
+ }
89
+ }
90
+ } # end of menu
91
+
92
+ menu {
93
+ text '&Options'
94
+ menu_item(:check) {
95
+ text '&Beeping'
96
+ accelerator :command, :b
97
+ selection bind(game, :beeping)
98
+ }
99
+ } # end of menu
100
+
101
+ menu {
102
+ text '&Help'
103
+
104
+ menu_item {
105
+ text '&About'
106
+ accelerator :command, :shift, :a
107
+
108
+ on_widget_selected {
109
+ parent_custom_shell&.show_about_dialog
110
+ }
111
+ }
112
+ } # end of menu
68
113
  }
69
114
  }
115
+
116
+ def parent_custom_shell
117
+ # grab custom shell widget wrapping parent widget proxy (i.e. Tetris) and invoke method on it
118
+ the_parent_custom_shell = parent_proxy&.get_data('custom_shell')
119
+ the_parent_custom_shell if the_parent_custom_shell&.visible?
120
+ end
70
121
  end
71
122
  end
72
123
  end
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.18.3.1
4
+ version: 4.18.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - AndyMaleh
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-01-27 00:00:00.000000000 Z
11
+ date: 2021-01-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -471,9 +471,10 @@ files:
471
471
  - samples/elaborate/tetris.rb
472
472
  - samples/elaborate/tetris/model/block.rb
473
473
  - samples/elaborate/tetris/model/game.rb
474
+ - samples/elaborate/tetris/model/past_game.rb
474
475
  - samples/elaborate/tetris/model/tetromino.rb
475
476
  - samples/elaborate/tetris/view/block.rb
476
- - samples/elaborate/tetris/view/game_over_dialog.rb
477
+ - samples/elaborate/tetris/view/high_score_dialog.rb
477
478
  - samples/elaborate/tetris/view/playfield.rb
478
479
  - samples/elaborate/tetris/view/score_lane.rb
479
480
  - samples/elaborate/tetris/view/tetris_menu_bar.rb