glimmer-dsl-swt 4.18.2.4 → 4.18.3.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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +60 -0
  3. data/README.md +240 -30
  4. data/VERSION +1 -1
  5. data/glimmer-dsl-swt.gemspec +13 -7
  6. data/lib/ext/glimmer/config.rb +24 -7
  7. data/lib/glimmer/data_binding/table_items_binding.rb +8 -5
  8. data/lib/glimmer/data_binding/widget_binding.rb +22 -4
  9. data/lib/glimmer/dsl/swt/color_expression.rb +4 -4
  10. data/lib/glimmer/dsl/swt/data_binding_expression.rb +3 -3
  11. data/lib/glimmer/dsl/swt/dsl.rb +1 -0
  12. data/lib/glimmer/dsl/swt/multiply_expression.rb +53 -0
  13. data/lib/glimmer/dsl/swt/property_expression.rb +4 -2
  14. data/lib/glimmer/dsl/swt/shape_expression.rb +2 -4
  15. data/{samples/elaborate/tetris/view/game_over_dialog.rb → lib/glimmer/dsl/swt/transform_expression.rb} +29 -46
  16. data/lib/glimmer/dsl/swt/widget_expression.rb +2 -1
  17. data/lib/glimmer/swt/color_proxy.rb +28 -6
  18. data/lib/glimmer/swt/custom/drawable.rb +8 -0
  19. data/lib/glimmer/swt/custom/shape.rb +66 -26
  20. data/lib/glimmer/swt/directory_dialog_proxy.rb +3 -3
  21. data/lib/glimmer/swt/display_proxy.rb +26 -5
  22. data/lib/glimmer/swt/file_dialog_proxy.rb +3 -3
  23. data/lib/glimmer/swt/shell_proxy.rb +24 -4
  24. data/lib/glimmer/swt/table_proxy.rb +31 -7
  25. data/lib/glimmer/swt/transform_proxy.rb +109 -0
  26. data/lib/glimmer/swt/widget_listener_proxy.rb +14 -5
  27. data/lib/glimmer/swt/widget_proxy.rb +35 -20
  28. data/lib/glimmer/ui/custom_shell.rb +11 -9
  29. data/lib/glimmer/ui/custom_widget.rb +65 -39
  30. data/samples/elaborate/meta_sample.rb +81 -24
  31. data/samples/elaborate/tetris.rb +105 -44
  32. data/samples/elaborate/tetris/model/block.rb +1 -1
  33. data/samples/elaborate/tetris/model/game.rb +233 -137
  34. data/samples/elaborate/tetris/model/past_game.rb +26 -0
  35. data/samples/elaborate/tetris/model/tetromino.rb +46 -30
  36. data/samples/elaborate/tetris/view/block.rb +33 -5
  37. data/samples/elaborate/tetris/view/high_score_dialog.rb +133 -0
  38. data/samples/elaborate/tetris/view/playfield.rb +1 -1
  39. data/samples/elaborate/tetris/view/score_lane.rb +11 -11
  40. data/samples/elaborate/tetris/view/tetris_menu_bar.rb +121 -0
  41. data/samples/elaborate/tic_tac_toe.rb +4 -4
  42. data/samples/hello/hello_canvas_transform.rb +40 -0
  43. data/samples/hello/hello_link.rb +1 -1
  44. metadata +11 -5
@@ -0,0 +1,26 @@
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
+ class Tetris
23
+ module Model
24
+ PastGame = Struct.new(:name, :score, :lines, :level)
25
+ end
26
+ end
@@ -38,31 +38,32 @@ class Tetris
38
38
  Z: :red,
39
39
  }
40
40
 
41
- attr_reader :letter, :preview
41
+ attr_reader :game, :letter, :preview
42
42
  alias preview? preview
43
43
  attr_accessor :orientation, :blocks, :row, :column
44
44
 
45
- def initialize
45
+ def initialize(game)
46
+ @game = game
46
47
  @letter = LETTER_COLORS.keys.sample
47
48
  @orientation = :north
48
49
  @blocks = default_blocks
49
50
  @preview = true
50
51
  new_row = 0
51
- new_column = (PREVIEW_PLAYFIELD_WIDTH - width)/2
52
+ new_column = (Model::Game::PREVIEW_PLAYFIELD_WIDTH - width)/2
52
53
  update_playfield(new_row, new_column)
53
54
  end
54
55
 
55
56
  def playfield
56
- @preview ? Game.preview_playfield : Game.playfield
57
+ @preview ? game.preview_playfield : game.playfield
57
58
  end
58
59
 
59
60
  def launch!
60
61
  remove_from_playfield
61
62
  @preview = false
62
63
  new_row = 1 - height
63
- new_column = (PLAYFIELD_WIDTH - width)/2
64
+ new_column = (game.playfield_width - width)/2
64
65
  update_playfield(new_row, new_column)
65
- Game.tetrominoes << self
66
+ game.tetrominoes << self
66
67
  end
67
68
 
68
69
  def update_playfield(new_row = nil, new_column = nil)
@@ -76,7 +77,7 @@ class Tetris
76
77
 
77
78
  def add_to_playfield
78
79
  update_playfield_block do |playfield_row, playfield_column, row_index, column_index|
79
- playfield[playfield_row][playfield_column].color = blocks[row_index][column_index].color if playfield_row >= 0 && playfield[playfield_row][playfield_column]&.clear? && !blocks[row_index][column_index].clear?
80
+ playfield[playfield_row][playfield_column].color = blocks[row_index][column_index].color if playfield_row >= 0 && playfield[playfield_row][playfield_column]&.clear? && !blocks[row_index][column_index].clear? && playfield[playfield_row][playfield_column].color != blocks[row_index][column_index].color
80
81
  end
81
82
  end
82
83
 
@@ -89,16 +90,16 @@ class Tetris
89
90
 
90
91
  def stopped?
91
92
  return true if @stopped || @preview
92
- playfield_remaining_heights = Game.playfield_remaining_heights(self)
93
+ playfield_remaining_heights = game.playfield_remaining_heights(self)
93
94
  result = bottom_most_blocks.any? do |bottom_most_block|
94
95
  playfield_column = @column + bottom_most_block[:column_index]
95
- playfield_remaining_heights[playfield_column] &&
96
- @row + bottom_most_block[:row] >= playfield_remaining_heights[playfield_column] - 1
96
+ playfield_remaining_heights[playfield_column] &&
97
+ @row + bottom_most_block[:row_index] >= playfield_remaining_heights[playfield_column] - 1
97
98
  end
98
- if result && !Game.hypothetical?
99
+ if result && !game.hypothetical?
99
100
  @stopped = result
100
- Game.consider_eliminating_lines
101
- Model::Game.consider_adding_tetromino
101
+ game.consider_eliminating_lines
102
+ @game.consider_adding_tetromino
102
103
  end
103
104
  result
104
105
  end
@@ -113,7 +114,7 @@ class Tetris
113
114
  bottom_most_block_row = row_blocks_with_row_index[1]
114
115
  {
115
116
  block: bottom_most_block,
116
- row: bottom_most_block_row,
117
+ row_index: bottom_most_block_row,
117
118
  column_index: column_index
118
119
  }
119
120
  end
@@ -124,9 +125,10 @@ class Tetris
124
125
  end
125
126
 
126
127
  def right_blocked?
127
- (@column == PLAYFIELD_WIDTH - width) ||
128
+ (@column == game.playfield_width - width) ||
128
129
  right_most_blocks.any? { |right_most_block|
129
- playfield[@row + right_most_block[:row_index]][@column + right_most_block[:column_index] + 1].occupied?
130
+ (@row + right_most_block[:row_index]) >= 0 &&
131
+ playfield[@row + right_most_block[:row_index]][@column + right_most_block[:column_index] + 1].occupied?
130
132
  }
131
133
  end
132
134
 
@@ -150,7 +152,8 @@ class Tetris
150
152
  def left_blocked?
151
153
  (@column == 0) ||
152
154
  left_most_blocks.any? { |left_most_block|
153
- playfield[@row + left_most_block[:row_index]][@column + left_most_block[:column_index] - 1].occupied?
155
+ (@row + left_most_block[:row_index]) >= 0 &&
156
+ playfield[@row + left_most_block[:row_index]][@column + left_most_block[:column_index] - 1].occupied?
154
157
  }
155
158
  end
156
159
 
@@ -179,22 +182,27 @@ class Tetris
179
182
  @blocks.size
180
183
  end
181
184
 
182
- def down
185
+ def down!(immediate: false)
183
186
  launch! if preview?
184
187
  unless stopped?
185
- new_row = @row + 1
188
+ block_count = 1
189
+ if immediate
190
+ remaining_height, bottom_touching_block = remaining_height_and_bottom_touching_block
191
+ block_count = remaining_height - @row
192
+ end
193
+ new_row = @row + block_count
186
194
  update_playfield(new_row, @column)
187
195
  end
188
196
  end
189
197
 
190
- def left
198
+ def left!
191
199
  unless left_blocked?
192
200
  new_column = @column - 1
193
201
  update_playfield(@row, new_column)
194
202
  end
195
203
  end
196
204
 
197
- def right
205
+ def right!
198
206
  unless right_blocked?
199
207
  new_column = @column + 1
200
208
  update_playfield(@row, new_column)
@@ -202,11 +210,11 @@ class Tetris
202
210
  end
203
211
 
204
212
  # Rotate in specified direcation, which can be :right (clockwise) or :left (counterclockwise)
205
- def rotate(direction)
213
+ def rotate!(direction)
206
214
  return if stopped?
207
215
  can_rotate = nil
208
216
  new_blocks = nil
209
- Game.hypothetical do
217
+ game.hypothetical do
210
218
  hypothetical_rotated_tetromino = hypothetical_tetromino
211
219
  new_blocks = hypothetical_rotated_tetromino.rotate_blocks(direction)
212
220
  can_rotate = !hypothetical_rotated_tetromino.stopped? && !hypothetical_rotated_tetromino.right_blocked? && !hypothetical_rotated_tetromino.left_blocked?
@@ -222,13 +230,13 @@ class Tetris
222
230
  end
223
231
 
224
232
  def rotate_blocks(direction)
225
- new_blocks = Matrix[*@blocks].transpose.to_a
226
- if direction == :right
227
- new_blocks = new_blocks.map(&:reverse)
228
- else
229
- new_blocks = new_blocks.reverse
230
- end
231
- Matrix[*new_blocks].to_a
233
+ new_blocks = Matrix[*@blocks].transpose.to_a
234
+ if direction == :right
235
+ new_blocks = new_blocks.map(&:reverse)
236
+ else
237
+ new_blocks = new_blocks.reverse
238
+ end
239
+ Matrix[*new_blocks].to_a
232
240
  end
233
241
 
234
242
  def hypothetical_tetromino
@@ -242,6 +250,14 @@ class Tetris
242
250
  end
243
251
  end
244
252
 
253
+ def remaining_height_and_bottom_touching_block
254
+ playfield_remaining_heights = game.playfield_remaining_heights(self)
255
+ bottom_most_blocks.map do |bottom_most_block|
256
+ playfield_column = @column + bottom_most_block[:column_index]
257
+ [playfield_remaining_heights[playfield_column] - (bottom_most_block[:row_index] + 1), bottom_most_block]
258
+ end.min_by(&:first)
259
+ end
260
+
245
261
  def default_blocks
246
262
  case @letter
247
263
  when :I
@@ -26,14 +26,42 @@ class Tetris
26
26
 
27
27
  options :game_playfield, :block_size, :row, :column
28
28
 
29
+ before_body {
30
+ @bevel_constant = 20
31
+ }
32
+
29
33
  body {
30
- composite {
34
+ canvas {
31
35
  layout nil
32
36
  background bind(game_playfield[row][column], :color)
33
- # TODO improve shapes to have a bevel look
34
- rectangle(0, 0, block_size, block_size)
35
- rectangle(3, 3, block_size - 6, block_size - 6) {
36
- foreground :gray
37
+ polygon(0, 0, block_size, 0, block_size - 4, 4, 4, 4, fill: true) {
38
+ background bind(game_playfield[row][column], :color) { |color_value|
39
+ color = color(color_value)
40
+ rgb(color.red + 4*@bevel_constant, color.green + 4*@bevel_constant, color.blue + 4*@bevel_constant)
41
+ }
42
+ }
43
+ polygon(block_size, 0, block_size - 4, 4, block_size - 4, block_size - 4, block_size, block_size, fill: true) {
44
+ background bind(game_playfield[row][column], :color) { |color_value|
45
+ color = color(color_value)
46
+ rgb(color.red - @bevel_constant, color.green - @bevel_constant, color.blue - @bevel_constant)
47
+ }
48
+ }
49
+ polygon(block_size, block_size, 0, block_size, 4, block_size - 4, block_size - 4, block_size - 4, fill: true) {
50
+ background bind(game_playfield[row][column], :color) { |color_value|
51
+ color = color(color_value)
52
+ rgb(color.red - 2*@bevel_constant, color.green - 2*@bevel_constant, color.blue - 2*@bevel_constant)
53
+ }
54
+ }
55
+ polygon(0, 0, 0, block_size, 4, block_size - 4, 4, 4, fill: true) {
56
+ background bind(game_playfield[row][column], :color) { |color_value|
57
+ color = color(color_value)
58
+ rgb(color.red - @bevel_constant, color.green - @bevel_constant, color.blue - @bevel_constant)
59
+ }
60
+ }
61
+ rectangle(0, 0, block_size, block_size) {
62
+ foreground bind(game_playfield[row][column], :color) { |color_value|
63
+ color_value == Model::Block::COLOR_CLEAR ? :gray : color_value
64
+ }
37
65
  }
38
66
  }
39
67
  }
@@ -0,0 +1,133 @@
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
+ table_column {
63
+ text 'Lines'
64
+ }
65
+ table_column {
66
+ text 'Level'
67
+ }
68
+
69
+ items bind(game, :high_scores, read_only_sort: true), column_properties(:name, :score, :lines, :level)
70
+ }
71
+ composite {
72
+ row_layout :horizontal
73
+
74
+ button {
75
+ text 'Clear'
76
+
77
+ on_widget_selected {
78
+ game.clear_high_scores!
79
+ }
80
+ }
81
+ @play_close_button = button {
82
+ text bind(game, :game_over) {|game_over| game_over ? 'Play Again?' : 'Close'}
83
+ focus true # initial focus
84
+
85
+ on_widget_selected {
86
+ async_exec { close }
87
+ game.paused = @game_paused
88
+ game.restart! if game.game_over?
89
+ }
90
+ }
91
+ }
92
+
93
+ on_swt_show {
94
+ @game_paused = game.paused?
95
+ game.paused = true
96
+ if game.game_over? && game.added_high_score?
97
+ game.added_high_score = false
98
+ game.save_high_scores!
99
+ @high_score_table.edit_table_item(
100
+ @high_score_table.items.first, # row item
101
+ 0, # column
102
+ after_write: -> {
103
+ game.save_high_scores!
104
+ @play_close_button.set_focus
105
+ },
106
+ after_cancel: -> {
107
+ @play_close_button.set_focus
108
+ },
109
+ )
110
+ end
111
+ }
112
+
113
+ on_shell_closed {
114
+ # guard is needed because there is an observer in Tetris closing on
115
+ # game.show_high_scores change, which gets set below
116
+ unless @closing
117
+ @closing = true
118
+ @high_score_table.cancel_edit!
119
+ game.paused = @game_paused
120
+ game.show_high_scores = false
121
+ else
122
+ @closing = false
123
+ end
124
+ }
125
+
126
+ on_widget_disposed {
127
+ @game_over_observer.deregister
128
+ }
129
+ }
130
+ }
131
+ end
132
+ end
133
+ end
@@ -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
+ canvas {
33
33
  grid_layout {
34
34
  num_columns playfield_width
35
35
  make_columns_equal_width true
@@ -27,11 +27,11 @@ class Tetris
27
27
  class ScoreLane
28
28
  include Glimmer::UI::CustomWidget
29
29
 
30
- options :block_size
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,16 +46,16 @@ 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
- playfield(game_playfield: Model::Game.preview_playfield, playfield_width: PREVIEW_PLAYFIELD_WIDTH, playfield_height: PREVIEW_PLAYFIELD_HEIGHT, block_size: BLOCK_SIZE)
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
- text bind(Model::Game, :score)
58
+ text bind(game, :score)
59
59
  font height: @font_height
60
60
  }
61
61
 
@@ -63,10 +63,10 @@ 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
- text bind(Model::Game, :lines)
69
+ text bind(game, :lines)
70
70
  font height: @font_height
71
71
  }
72
72
 
@@ -74,10 +74,10 @@ 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
- text bind(Model::Game, :level)
80
+ text bind(game, :level)
81
81
  font height: @font_height
82
82
  }
83
83
  }