glimmer-dsl-swt 4.18.3.4 → 4.18.4.3

Sign up to get free protection for your applications and to get access to all the features.
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