glimmer-dsl-swt 4.18.3.4 → 4.18.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +55 -0
  3. data/README.md +600 -331
  4. data/VERSION +1 -1
  5. data/glimmer-dsl-swt.gemspec +8 -3
  6. data/lib/ext/rouge/themes/glimmer.rb +29 -0
  7. data/lib/glimmer-dsl-swt.rb +0 -1
  8. data/lib/glimmer/data_binding/widget_binding.rb +1 -1
  9. data/lib/glimmer/swt/custom/code_text.rb +200 -51
  10. data/lib/glimmer/swt/custom/drawable.rb +1 -4
  11. data/lib/glimmer/swt/custom/shape.rb +50 -6
  12. data/lib/glimmer/swt/date_time_proxy.rb +1 -3
  13. data/lib/glimmer/swt/display_proxy.rb +11 -0
  14. data/lib/glimmer/swt/font_proxy.rb +1 -0
  15. data/lib/glimmer/swt/image_proxy.rb +11 -0
  16. data/lib/glimmer/swt/layout_proxy.rb +4 -1
  17. data/lib/glimmer/swt/shell_proxy.rb +4 -1
  18. data/lib/glimmer/swt/table_proxy.rb +17 -13
  19. data/lib/glimmer/swt/widget_proxy.rb +11 -3
  20. data/samples/elaborate/meta_sample.rb +15 -4
  21. data/samples/elaborate/meta_sample/meta_sample_logo.png +0 -0
  22. data/samples/elaborate/tetris.rb +16 -6
  23. data/samples/elaborate/tetris/model/game.rb +3 -3
  24. data/samples/elaborate/tetris/model/past_game.rb +14 -1
  25. data/samples/elaborate/tetris/view/block.rb +1 -1
  26. data/samples/elaborate/tetris/view/high_score_dialog.rb +1 -10
  27. data/samples/elaborate/tetris/view/playfield.rb +1 -1
  28. data/samples/elaborate/tetris/view/tetris_menu_bar.rb +13 -11
  29. data/samples/hello/hello_canvas.rb +7 -6
  30. data/samples/hello/hello_canvas_animation.rb +3 -3
  31. data/samples/hello/hello_canvas_transform.rb +1 -1
  32. data/samples/hello/hello_canvas_transform/hello_canvas_transform_image.png +0 -0
  33. data/samples/hello/hello_code_text.rb +104 -0
  34. data/samples/hello/hello_table.rb +7 -4
  35. data/samples/hello/hello_table/baseball_park.png +0 -0
  36. metadata +7 -2
@@ -43,9 +43,7 @@ module Glimmer
43
43
  end
44
44
 
45
45
  def date=(date_value)
46
- self.year = date_value.year
47
- self.month = date_value.month
48
- self.day = date_value.day
46
+ swt_widget.setDate(date_value.year, date_value.month - 1, date_value.day)
49
47
  end
50
48
 
51
49
  def time
@@ -71,6 +71,9 @@ module Glimmer
71
71
  Display.app_name ||= 'Glimmer'
72
72
  @swt_display = Display.new(*args)
73
73
  @swt_display.set_data('proxy', self)
74
+ on_swt_Dispose {
75
+ clear_shapes
76
+ }
74
77
  end
75
78
 
76
79
  def content(&block)
@@ -88,6 +91,14 @@ module Glimmer
88
91
  def timer_exec(&block)
89
92
  @swt_display.timer_exec(&block)
90
93
  end
94
+
95
+ def on_widget_disposed(&block)
96
+ on_swt_Dispose(&block)
97
+ end
98
+
99
+ def disposed?
100
+ @swt_display.isDisposed
101
+ end
91
102
 
92
103
  def method_missing(method, *args, &block)
93
104
  if can_handle_observation_request?(method)
@@ -43,6 +43,7 @@ module Glimmer
43
43
 
44
44
  attr_reader :widget_proxy, :swt_font, :font_properties
45
45
 
46
+
46
47
  # Builds a new font proxy from passed in widget_proxy and font_properties hash,
47
48
  #
48
49
  # It begins with existing SWT widget font and amends it with font properties.
@@ -83,6 +83,13 @@ module Glimmer
83
83
  @swt_image = Image.new(*@args)
84
84
  @original_image_data = @image_data = @swt_image.image_data
85
85
  end
86
+ @swt_image.singleton_class.alias_method(:dispose_without_glimmer, :dispose)
87
+ proxy = self
88
+ # TODO consider adding a get_data/set_data method to conform with other SWT widgets
89
+ @swt_image.singleton_class.define_method(:dispose) do
90
+ proxy.clear_shapes
91
+ dispose_without_glimmer
92
+ end
86
93
  post_add_content if content.nil?
87
94
  end
88
95
 
@@ -120,6 +127,10 @@ module Glimmer
120
127
  @gc = org.eclipse.swt.graphics.GC.new(swt_image)
121
128
  end
122
129
 
130
+ def disposed?
131
+ @swt_image.isDisposed
132
+ end
133
+
123
134
  def has_attribute?(attribute_name, *args)
124
135
  @swt_image.respond_to?(attribute_setter(attribute_name), args) || respond_to?(ruby_attribute_setter(attribute_name), args)
125
136
  end
@@ -72,7 +72,9 @@ module Glimmer
72
72
  @swt_layout.marginRight = 0 if @swt_layout.respond_to?(:marginRight)
73
73
  @swt_layout.marginBottom = 0 if @swt_layout.respond_to?(:marginBottom)
74
74
  @swt_layout.marginLeft = 0 if @swt_layout.respond_to?(:marginLeft)
75
+ old_layout = @widget_proxy.swt_widget.getLayout
75
76
  @widget_proxy.swt_widget.setLayout(@swt_layout)
77
+ @widget_proxy.swt_widget.layout if old_layout
76
78
  end
77
79
 
78
80
  def has_attribute?(attribute_name, *args)
@@ -83,7 +85,8 @@ module Glimmer
83
85
  apply_property_type_converters(attribute_name, args)
84
86
  if args.first != @swt_layout.send(attribute_getter(attribute_name))
85
87
  @swt_layout.send(attribute_setter(attribute_name), *args)
86
- @widget_proxy.swt_widget.getShell.pack
88
+ @widget_proxy.swt_widget.layout
89
+ @widget_proxy.swt_widget.getShell.layout
87
90
  end
88
91
  end
89
92
 
@@ -82,6 +82,9 @@ module Glimmer
82
82
  end
83
83
  end
84
84
  end
85
+ on_widget_disposed {
86
+ clear_shapes
87
+ }
85
88
  @display ||= @swt_widget.getDisplay
86
89
  end
87
90
 
@@ -161,7 +164,7 @@ module Glimmer
161
164
  minimum_size = @swt_widget.getMinimumSize
162
165
  @swt_widget.setMinimumSize(bounds.width, bounds.height)
163
166
  listener = on_control_resized { @swt_widget.setBounds(bounds) }
164
- @swt_widget.pack
167
+ @swt_widget.layout(true, true)
165
168
  @swt_widget.removeControlListener(listener.swt_listener)
166
169
  @swt_widget.setMinimumSize(minimum_size)
167
170
  elsif OS.linux?
@@ -457,7 +457,7 @@ module Glimmer
457
457
  edit_table_item(swt_widget.getSelection.first, column_index, before_write: before_write, after_write: after_write, after_cancel: after_cancel)
458
458
  end
459
459
 
460
- def edit_table_item(table_item, column_index, before_write: nil, after_write: nil, after_cancel: nil)
460
+ def edit_table_item(table_item, column_index, before_write: nil, after_write: nil, after_cancel: nil, write_on_cancel: false)
461
461
  return if table_item.nil?
462
462
  model = table_item.data
463
463
  property = column_properties[column_index]
@@ -476,29 +476,33 @@ module Glimmer
476
476
  widget_value_property = TableProxy::editors.symbolize_keys[editor_widget][:widget_value_property]
477
477
 
478
478
  @cancel_edit = lambda do |event=nil|
479
- @cancel_in_progress = true
480
- @table_editor_widget_proxy&.swt_widget&.dispose
481
- @table_editor_widget_proxy = nil
482
- if after_cancel&.arity == 0
483
- after_cancel&.call
479
+ if write_on_cancel
480
+ @finish_edit.call(event)
484
481
  else
485
- after_cancel&.call(table_item)
482
+ @cancel_in_progress = true
483
+ @table_editor_widget_proxy&.swt_widget&.dispose
484
+ @table_editor_widget_proxy = nil
485
+ if after_cancel&.arity == 0
486
+ after_cancel&.call
487
+ else
488
+ after_cancel&.call(table_item)
489
+ end
490
+ @edit_in_progress = false
491
+ @cancel_in_progress = false
492
+ @cancel_edit = nil
493
+ @edit_mode = false
486
494
  end
487
- @edit_in_progress = false
488
- @cancel_in_progress = false
489
- @cancel_edit = nil
490
- @edit_mode = false
491
495
  end
492
496
 
493
497
  @finish_edit = lambda do |event=nil|
494
498
  new_value = @table_editor_widget_proxy&.send(widget_value_property)
495
499
  if table_item.isDisposed
496
- @cancel_edit.call
500
+ @cancel_edit.call unless write_on_cancel
497
501
  elsif !new_value.nil? && !action_taken && !@edit_in_progress && !@cancel_in_progress
498
502
  action_taken = true
499
503
  @edit_in_progress = true
500
504
  if new_value == model.send(model_editing_property)
501
- @cancel_edit.call
505
+ @cancel_edit.call unless write_on_cancel
502
506
  else
503
507
  if before_write&.arity == 0
504
508
  before_write&.call
@@ -165,6 +165,12 @@ module Glimmer
165
165
  DEFAULT_INITIALIZERS[underscored_widget_name.to_s.to_sym]&.call(@swt_widget)
166
166
  @parent_proxy.post_initialize_child(self)
167
167
  end
168
+ @keyword = underscored_widget_name.to_s
169
+ if respond_to?(:on_widget_disposed)
170
+ on_widget_disposed {
171
+ clear_shapes
172
+ }
173
+ end
168
174
  end
169
175
 
170
176
  # Subclasses may override to perform post initialization work on an added child
@@ -559,9 +565,11 @@ module Glimmer
559
565
 
560
566
  # Used for data-binding only. Consider renaming or improving to avoid the confusion it causes
561
567
  def add_observer(observer, property_name)
562
- property_listener_installers = @swt_widget.class.ancestors.map {|ancestor| widget_property_listener_installers[ancestor]}.compact
563
- widget_listener_installers = property_listener_installers.map{|installer| installer[property_name.to_s.to_sym]}.compact if !property_listener_installers.empty?
564
- widget_listener_installers.to_a.first&.call(observer)
568
+ if !observer.respond_to?(:binding_options) || !observer.binding_options[:read_only]
569
+ property_listener_installers = @swt_widget.class.ancestors.map {|ancestor| widget_property_listener_installers[ancestor]}.compact
570
+ widget_listener_installers = property_listener_installers.map{|installer| installer[property_name.to_s.to_sym]}.compact if !property_listener_installers.empty?
571
+ widget_listener_installers.to_a.first&.call(observer)
572
+ end
565
573
  end
566
574
 
567
575
  def remove_observer(observer, property_name)
@@ -20,7 +20,6 @@
20
20
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
21
 
22
22
  require 'fileutils'
23
- require 'etc'
24
23
 
25
24
  class Sample
26
25
  include Glimmer::DataBinding::ObservableModel
@@ -31,7 +30,7 @@ class Sample
31
30
  end
32
31
 
33
32
  def user_glimmer_directory
34
- File.join(Etc.getpwuid.dir, '.glimmer-dsl-swt')
33
+ File.join(File.expand_path('~'), '.glimmer-dsl-swt')
35
34
  end
36
35
 
37
36
  def ensure_user_glimmer_directory
@@ -204,7 +203,7 @@ class MetaSampleApplication
204
203
  shell(:fill_screen) {
205
204
  minimum_size 1280, 768
206
205
  text 'Glimmer Meta-Sample (The Sample of Samples)'
207
- image File.expand_path('../../icons/scaffold_app.png', __dir__)
206
+ image File.expand_path('meta_sample/meta_sample_logo.png', __dir__)
208
207
 
209
208
  sash_form {
210
209
  composite {
@@ -264,9 +263,21 @@ class MetaSampleApplication
264
263
  }
265
264
  }
266
265
 
267
- @code_text = code_text {
266
+ @code_text = code_text(lines: {width: 3}) {
267
+ root {
268
+ grid_layout(2, false) {
269
+ horizontal_spacing 0
270
+ margin_width 0
271
+ margin_height 0
272
+ }
273
+ }
274
+ line_numbers {
275
+ background :white
276
+ }
268
277
  text bind(SampleDirectory, 'selected_sample.code', read_only: true)
269
278
  editable bind(SampleDirectory, 'selected_sample.editable')
279
+ left_margin 7
280
+ right_margin 7
270
281
  }
271
282
 
272
283
  weights 4, 11
@@ -51,11 +51,12 @@ class Tetris
51
51
  end
52
52
 
53
53
  Display.app_name = 'Glimmer Tetris'
54
+
54
55
  display {
55
- @keyboard_listener = on_swt_keydown { |key_event|
56
+ @keyboard_down_listener = on_swt_keydown { |key_event|
56
57
  case key_event.keyCode
57
58
  when swt(:arrow_down), 's'.bytes.first
58
- game.down!
59
+ game.down! if OS.mac?
59
60
  when swt(:arrow_up)
60
61
  case game.up_arrow_action
61
62
  when :instant_down
@@ -75,10 +76,18 @@ class Tetris
75
76
  elsif key_event.keyLocation == swt(:left) # left shift key
76
77
  game.rotate!(:left)
77
78
  end
78
- when swt(:ctrl)
79
- game.rotate!(:left)
80
79
  end
81
80
  }
81
+
82
+ # invoke game.down! on keyup with Windows/Linux since they seem to group-render similar events, preventing intermediate renders (causing invisiblity while holding keys)
83
+ if !OS.mac?
84
+ @keyboard_up_listener = on_swt_keyup { |key_event|
85
+ case key_event.keyCode
86
+ when swt(:arrow_down), 's'.bytes.first
87
+ game.down!
88
+ end
89
+ }
90
+ end
82
91
 
83
92
  # if running in app mode, set the Mac app about dialog (ignored in platforms)
84
93
  @about_observer = on_about {
@@ -169,7 +178,7 @@ class Tetris
169
178
  }
170
179
  }
171
180
  end
172
-
181
+
173
182
  def start_moving_tetrominos_down
174
183
  Thread.new do
175
184
  @mutex.synchronize do
@@ -201,7 +210,8 @@ class Tetris
201
210
  def deregister_observers
202
211
  @show_high_scores_observer&.deregister
203
212
  @game_over_observer&.deregister
204
- @keyboard_listener&.deregister
213
+ @keyboard_down_listener&.deregister
214
+ @keyboard_up_listener&.deregister
205
215
  @about_observer&.deregister
206
216
  @quit_observer&.deregister
207
217
  end
@@ -20,7 +20,7 @@
20
20
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
21
 
22
22
  require 'fileutils'
23
- require 'etc'
23
+ require 'json'
24
24
  require 'glimmer/data_binding/observer'
25
25
  require 'glimmer/config'
26
26
 
@@ -50,7 +50,7 @@ class Tetris
50
50
  @high_scores = []
51
51
  @show_high_scores = false
52
52
  @beeping = true
53
- @up_arrow_action = :instant_down
53
+ @up_arrow_action = :rotate_left
54
54
  load_high_scores!
55
55
  end
56
56
 
@@ -111,7 +111,7 @@ class Tetris
111
111
  end
112
112
 
113
113
  def tetris_dir
114
- @tetris_dir ||= File.join(Etc.getpwuid.dir, '.glimmer-tetris')
114
+ @tetris_dir ||= File.join(File.expand_path('~'), '.glimmer-tetris')
115
115
  end
116
116
 
117
117
  def tetris_high_score_file
@@ -21,6 +21,19 @@
21
21
 
22
22
  class Tetris
23
23
  module Model
24
- PastGame = Struct.new(:name, :score, :lines, :level)
24
+ class PastGame
25
+ attr_accessor :name, :score, :lines, :level
26
+
27
+ def initialize(name, score, lines, level)
28
+ @name = name
29
+ @score = score.to_i
30
+ @lines = lines.to_i
31
+ @level = level.to_i
32
+ end
33
+
34
+ def to_a
35
+ [@name, @score, @lines, @level]
36
+ end
37
+ end
25
38
  end
26
39
  end
@@ -27,7 +27,7 @@ class Tetris
27
27
  options :game_playfield, :block_size, :row, :column
28
28
 
29
29
  body {
30
- canvas {
30
+ canvas(:double_buffered) {
31
31
  background bind(game_playfield[row][column], :color)
32
32
  polygon(0, 0, block_size, 0, block_size - 4, 4, 4, 4) {
33
33
  background bind(game_playfield[row][column], :color) { |color_value|
@@ -71,13 +71,6 @@ class Tetris
71
71
  composite {
72
72
  row_layout :horizontal
73
73
 
74
- button {
75
- text 'Clear'
76
-
77
- on_widget_selected {
78
- game.clear_high_scores!
79
- }
80
- }
81
74
  @play_close_button = button {
82
75
  text bind(game, :game_over) {|game_over| game_over ? 'Play Again?' : 'Close'}
83
76
  focus true # initial focus
@@ -99,13 +92,11 @@ class Tetris
99
92
  @high_score_table.edit_table_item(
100
93
  @high_score_table.items.first, # row item
101
94
  0, # column
95
+ write_on_cancel: true,
102
96
  after_write: -> {
103
97
  game.save_high_scores!
104
98
  @play_close_button.set_focus
105
99
  },
106
- after_cancel: -> {
107
- @play_close_button.set_focus
108
- },
109
100
  )
110
101
  end
111
102
  }
@@ -29,7 +29,7 @@ class Tetris
29
29
  options :game_playfield, :playfield_width, :playfield_height, :block_size
30
30
 
31
31
  body {
32
- composite {
32
+ composite(:double_buffered) {
33
33
  grid_layout {
34
34
  num_columns playfield_width
35
35
  make_columns_equal_width true
@@ -23,6 +23,8 @@ class Tetris
23
23
  module View
24
24
  class TetrisMenuBar
25
25
  include Glimmer::UI::CustomWidget
26
+
27
+ COMMAND_KEY = OS.mac? ? :command : :ctrl
26
28
 
27
29
  options :game
28
30
 
@@ -34,7 +36,7 @@ class Tetris
34
36
  menu_item {
35
37
  text '&Start'
36
38
  enabled bind(game, :game_over)
37
- accelerator :command, :s
39
+ accelerator COMMAND_KEY, :s
38
40
 
39
41
  on_widget_selected {
40
42
  game.start!
@@ -42,14 +44,14 @@ class Tetris
42
44
  }
43
45
  menu_item(:check) {
44
46
  text '&Pause'
45
- accelerator :command, :p
47
+ accelerator COMMAND_KEY, :p
46
48
  enabled bind(game, :game_over, on_read: :!) {|value| value && !game.show_high_scores}
47
49
  enabled bind(game, :show_high_scores, on_read: :!) {|value| value && !game.game_over}
48
50
  selection bind(game, :paused)
49
51
  }
50
52
  menu_item {
51
53
  text '&Restart'
52
- accelerator :command, :r
54
+ accelerator COMMAND_KEY, :r
53
55
 
54
56
  on_widget_selected {
55
57
  game.restart!
@@ -58,7 +60,7 @@ class Tetris
58
60
  menu_item(:separator)
59
61
  menu_item {
60
62
  text '&Exit'
61
- accelerator :command, :x
63
+ accelerator COMMAND_KEY, :x
62
64
 
63
65
  on_widget_selected {
64
66
  parent_proxy.close
@@ -73,12 +75,12 @@ class Tetris
73
75
  text '&High Scores'
74
76
  menu_item(:check) {
75
77
  text '&Show'
76
- accelerator :command, :shift, :h
78
+ accelerator COMMAND_KEY, :shift, :h
77
79
  selection bind(game, :show_high_scores)
78
80
  }
79
81
  menu_item {
80
82
  text '&Clear'
81
- accelerator :command, :shift, :c
83
+ accelerator COMMAND_KEY, :shift, :c
82
84
 
83
85
  on_widget_selected {
84
86
  game.clear_high_scores!
@@ -91,24 +93,24 @@ class Tetris
91
93
  text '&Options'
92
94
  menu_item(:check) {
93
95
  text '&Beeping'
94
- accelerator :command, :b
96
+ accelerator COMMAND_KEY, :b
95
97
  selection bind(game, :beeping)
96
98
  }
97
99
  menu {
98
100
  text 'Up Arrow'
99
101
  menu_item(:radio) {
100
102
  text '&Instant Down'
101
- accelerator :command, :shift, :i
103
+ accelerator COMMAND_KEY, :shift, :i
102
104
  selection bind(game, :instant_down_on_up, computed_by: :up_arrow_action)
103
105
  }
104
106
  menu_item(:radio) {
105
107
  text 'Rotate &Right'
106
- accelerator :command, :shift, :r
108
+ accelerator COMMAND_KEY, :shift, :r
107
109
  selection bind(game, :rotate_right_on_up, computed_by: :up_arrow_action)
108
110
  }
109
111
  menu_item(:radio) {
110
112
  text 'Rotate &Left'
111
- accelerator :command, :shift, :l
113
+ accelerator COMMAND_KEY, :shift, :l
112
114
  selection bind(game, :rotate_left_on_up, computed_by: :up_arrow_action)
113
115
  }
114
116
  }
@@ -119,7 +121,7 @@ class Tetris
119
121
 
120
122
  menu_item {
121
123
  text '&About'
122
- accelerator :command, :shift, :a
124
+ accelerator COMMAND_KEY, :shift, :a
123
125
 
124
126
  on_widget_selected {
125
127
  parent_custom_shell&.show_about_dialog