glimmer-dsl-swt 4.18.2.4 → 4.18.3.3

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