glimmer-dsl-libui 0.4.19 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -0
  3. data/LICENSE.txt +1 -1
  4. data/README.md +452 -52
  5. data/VERSION +1 -1
  6. data/bin/girb_runner.rb +1 -1
  7. data/examples/area_based_custom_controls.rb +282 -0
  8. data/examples/snake/model/game.rb +18 -1
  9. data/examples/snake.rb +6 -2
  10. data/examples/snake2.rb +6 -2
  11. data/examples/tetris/model/block.rb +1 -1
  12. data/examples/tetris/model/game.rb +26 -24
  13. data/examples/tetris/model/past_game.rb +1 -1
  14. data/examples/tetris/model/tetromino.rb +1 -1
  15. data/examples/tetris.rb +51 -20
  16. data/examples/tic_tac_toe/board.rb +1 -1
  17. data/examples/tic_tac_toe/cell.rb +1 -1
  18. data/glimmer-dsl-libui.gemspec +0 -0
  19. data/lib/glimmer/dsl/libui/bind_expression.rb +1 -1
  20. data/lib/glimmer/dsl/libui/control_expression.rb +1 -1
  21. data/lib/glimmer/dsl/libui/data_binding_expression.rb +1 -1
  22. data/lib/glimmer/dsl/libui/dsl.rb +1 -1
  23. data/lib/glimmer/dsl/libui/file_expression.rb +1 -1
  24. data/lib/glimmer/dsl/libui/listener_expression.rb +1 -1
  25. data/lib/glimmer/dsl/libui/observe_expression.rb +1 -1
  26. data/lib/glimmer/dsl/libui/open_file_expression.rb +1 -1
  27. data/lib/glimmer/dsl/libui/operation_expression.rb +1 -1
  28. data/lib/glimmer/dsl/libui/property_expression.rb +1 -1
  29. data/lib/glimmer/dsl/libui/save_file_expression.rb +1 -1
  30. data/lib/glimmer/dsl/libui/shape_expression.rb +1 -1
  31. data/lib/glimmer/dsl/libui/shine_data_binding_expression.rb +1 -1
  32. data/lib/glimmer/dsl/libui/string_expression.rb +1 -1
  33. data/lib/glimmer/dsl/libui/tab_item_expression.rb +1 -1
  34. data/lib/glimmer/fiddle_consumer.rb +1 -1
  35. data/lib/glimmer/libui/attributed_string.rb +1 -1
  36. data/lib/glimmer/libui/control_proxy/area_proxy/scrolling_area_proxy.rb +1 -1
  37. data/lib/glimmer/libui/control_proxy/area_proxy.rb +1 -1
  38. data/lib/glimmer/libui/control_proxy/box/horizontal_box_proxy.rb +1 -1
  39. data/lib/glimmer/libui/control_proxy/box/vertical_box_proxy.rb +1 -1
  40. data/lib/glimmer/libui/control_proxy/box.rb +2 -1
  41. data/lib/glimmer/libui/control_proxy/button_proxy.rb +1 -1
  42. data/lib/glimmer/libui/control_proxy/checkbox_proxy.rb +1 -1
  43. data/lib/glimmer/libui/control_proxy/color_button_proxy.rb +1 -1
  44. data/lib/glimmer/libui/control_proxy/column/background_color_column_proxy.rb +1 -1
  45. data/lib/glimmer/libui/control_proxy/column/button_column_proxy.rb +1 -1
  46. data/lib/glimmer/libui/control_proxy/column/checkbox_column_proxy.rb +1 -1
  47. data/lib/glimmer/libui/control_proxy/column/checkbox_text_color_column_proxy.rb +1 -1
  48. data/lib/glimmer/libui/control_proxy/column/checkbox_text_column_proxy.rb +1 -1
  49. data/lib/glimmer/libui/control_proxy/column/image_column_proxy.rb +1 -1
  50. data/lib/glimmer/libui/control_proxy/column/image_text_color_column_proxy.rb +1 -1
  51. data/lib/glimmer/libui/control_proxy/column/image_text_column_proxy.rb +1 -1
  52. data/lib/glimmer/libui/control_proxy/column/progress_bar_column_proxy.rb +1 -1
  53. data/lib/glimmer/libui/control_proxy/column/text_color_column_proxy.rb +1 -1
  54. data/lib/glimmer/libui/control_proxy/column/text_column_proxy.rb +1 -1
  55. data/lib/glimmer/libui/control_proxy/column.rb +1 -1
  56. data/lib/glimmer/libui/control_proxy/combobox_proxy.rb +1 -1
  57. data/lib/glimmer/libui/control_proxy/date_time_picker_proxy/date_picker_proxy.rb +1 -1
  58. data/lib/glimmer/libui/control_proxy/date_time_picker_proxy/time_picker_proxy.rb +1 -1
  59. data/lib/glimmer/libui/control_proxy/date_time_picker_proxy.rb +1 -1
  60. data/lib/glimmer/libui/control_proxy/dual_column.rb +1 -1
  61. data/lib/glimmer/libui/control_proxy/editable_column.rb +1 -1
  62. data/lib/glimmer/libui/control_proxy/editable_combobox_proxy.rb +1 -1
  63. data/lib/glimmer/libui/control_proxy/enableable_column.rb +1 -1
  64. data/lib/glimmer/libui/control_proxy/entry_proxy/password_entry_proxy.rb +1 -1
  65. data/lib/glimmer/libui/control_proxy/entry_proxy/search_entry_proxy.rb +1 -1
  66. data/lib/glimmer/libui/control_proxy/entry_proxy.rb +1 -1
  67. data/lib/glimmer/libui/control_proxy/font_button_proxy.rb +1 -1
  68. data/lib/glimmer/libui/control_proxy/form_proxy.rb +2 -1
  69. data/lib/glimmer/libui/control_proxy/grid_proxy.rb +1 -1
  70. data/lib/glimmer/libui/control_proxy/group_proxy.rb +1 -1
  71. data/lib/glimmer/libui/control_proxy/image_part_proxy.rb +1 -1
  72. data/lib/glimmer/libui/control_proxy/image_proxy.rb +1 -1
  73. data/lib/glimmer/libui/control_proxy/label_proxy.rb +1 -1
  74. data/lib/glimmer/libui/control_proxy/matrix_proxy.rb +1 -1
  75. data/lib/glimmer/libui/control_proxy/menu_item_proxy/about_menu_item_proxy.rb +1 -1
  76. data/lib/glimmer/libui/control_proxy/menu_item_proxy/check_menu_item_proxy.rb +1 -1
  77. data/lib/glimmer/libui/control_proxy/menu_item_proxy/preferences_menu_item_proxy.rb +1 -1
  78. data/lib/glimmer/libui/control_proxy/menu_item_proxy/quit_menu_item_proxy.rb +1 -1
  79. data/lib/glimmer/libui/control_proxy/menu_item_proxy/radio_menu_item_proxy.rb +1 -1
  80. data/lib/glimmer/libui/control_proxy/menu_item_proxy/separator_menu_item_proxy.rb +1 -1
  81. data/lib/glimmer/libui/control_proxy/menu_item_proxy.rb +1 -1
  82. data/lib/glimmer/libui/control_proxy/menu_proxy.rb +1 -1
  83. data/lib/glimmer/libui/control_proxy/message_box/msg_box_error_proxy.rb +1 -1
  84. data/lib/glimmer/libui/control_proxy/message_box/msg_box_proxy.rb +1 -1
  85. data/lib/glimmer/libui/control_proxy/message_box.rb +1 -1
  86. data/lib/glimmer/libui/control_proxy/multiline_entry_proxy/non_wrapping_multiline_entry_proxy.rb +1 -1
  87. data/lib/glimmer/libui/control_proxy/multiline_entry_proxy.rb +1 -1
  88. data/lib/glimmer/libui/control_proxy/open_type_features_proxy.rb +1 -1
  89. data/lib/glimmer/libui/control_proxy/open_type_tag_proxy.rb +1 -1
  90. data/lib/glimmer/libui/control_proxy/path_proxy.rb +1 -1
  91. data/lib/glimmer/libui/control_proxy/radio_buttons_proxy.rb +1 -1
  92. data/lib/glimmer/libui/control_proxy/slider_proxy.rb +1 -1
  93. data/lib/glimmer/libui/control_proxy/spinbox_proxy.rb +1 -1
  94. data/lib/glimmer/libui/control_proxy/tab_item_proxy.rb +1 -1
  95. data/lib/glimmer/libui/control_proxy/table_proxy.rb +1 -1
  96. data/lib/glimmer/libui/control_proxy/text_proxy.rb +1 -1
  97. data/lib/glimmer/libui/control_proxy/transformable.rb +1 -1
  98. data/lib/glimmer/libui/control_proxy/triple_column.rb +1 -1
  99. data/lib/glimmer/libui/control_proxy/window_proxy.rb +1 -1
  100. data/lib/glimmer/libui/control_proxy.rb +2 -1
  101. data/lib/glimmer/libui/data_bindable.rb +1 -1
  102. data/lib/glimmer/libui/parent.rb +1 -1
  103. data/lib/glimmer/libui/shape/arc.rb +1 -1
  104. data/lib/glimmer/libui/shape/bezier.rb +1 -1
  105. data/lib/glimmer/libui/shape/circle.rb +1 -1
  106. data/lib/glimmer/libui/shape/figure.rb +1 -1
  107. data/lib/glimmer/libui/shape/line.rb +1 -1
  108. data/lib/glimmer/libui/shape/polybezier.rb +1 -1
  109. data/lib/glimmer/libui/shape/polygon.rb +1 -1
  110. data/lib/glimmer/libui/shape/polyline.rb +1 -1
  111. data/lib/glimmer/libui/shape/rectangle.rb +8 -3
  112. data/lib/glimmer/libui/shape/square.rb +10 -3
  113. data/lib/glimmer/libui/shape.rb +37 -1
  114. data/lib/glimmer/libui.rb +1 -1
  115. data/lib/glimmer-dsl-libui.rb +2 -1
  116. metadata +27 -11
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.19
1
+ 0.5.0
data/bin/girb_runner.rb CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2020-2021 Andy Maleh
1
+ # Copyright (c) 2021-2022 Andy Maleh
2
2
  #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining
4
4
  # a copy of this software and associated documentation files (the
@@ -0,0 +1,282 @@
1
+ require 'glimmer-dsl-libui'
2
+
3
+ class AreaBasedCustomControls
4
+ include Glimmer
5
+
6
+ attr_accessor :label_width, :label_height, :label_font_descriptor,
7
+ :label_text_color, :label_background_fill, :label_border_stroke,
8
+ :label_text_x, :label_text_y,
9
+ :button_width, :button_height, :button_font_descriptor,
10
+ :button_text_color, :button_background_fill, :button_border_stroke,
11
+ :button_text_x, :button_text_y
12
+
13
+ def initialize
14
+ self.label_width = 335
15
+ self.label_height = 50
16
+ self.label_font_descriptor = {family: OS.linux? ? 'Monospace Bold Italic' : 'Courier New', size: 16, weight: :bold, italic: :italic}
17
+ self.label_text_color = :red
18
+ self.label_background_fill = :yellow
19
+ self.label_border_stroke = :limegreen
20
+
21
+ self.button_width = 150
22
+ self.button_height = 50
23
+ self.button_font_descriptor = {family: OS.linux? ? 'Monospace Bold Italic' : 'Courier New', size: 36, weight: OS.linux? ? :normal : :bold, italic: :italic}
24
+ self.button_text_color = :green
25
+ self.button_background_fill = :yellow
26
+ self.button_border_stroke = :limegreen
27
+ end
28
+
29
+ def rebuild_text_label
30
+ @text_label.destroy
31
+ @text_label_vertical_box.content { # re-open vertical box content and shove in a new label
32
+ @text_label = text_label('This is a text label.',
33
+ width: label_width, height: label_height, font_descriptor: label_font_descriptor,
34
+ background_fill: label_background_fill, text_color: label_text_color, border_stroke: label_border_stroke,
35
+ text_x: label_text_x, text_y: label_text_y)
36
+ }
37
+ end
38
+
39
+ def rebuild_push_button
40
+ @push_button.destroy
41
+ @push_button_vertical_box.content { # re-open vertical box content and shove in a new button
42
+ @push_button = push_button('Push',
43
+ width: button_width, height: button_height, font_descriptor: button_font_descriptor,
44
+ background_fill: button_background_fill, text_color: button_text_color, border_stroke: button_border_stroke,
45
+ text_x: button_text_x, text_y: button_text_y) {
46
+ on_mouse_up do
47
+ message_box('Button Pushed', 'Thank you for pushing the button!')
48
+ end
49
+ }
50
+ }
51
+ end
52
+
53
+ def launch
54
+ window('Area-Based Custom Controls', 385, 385) { |w|
55
+ margined true
56
+
57
+ tab {
58
+ tab_item('Text Label') {
59
+ @text_label_vertical_box = vertical_box {
60
+ vertical_box {
61
+ text_label('Text Label Form:', width: 385, height: 30, background_fill: OS.windows? ? :white : {a: 0}, border_stroke: OS.windows? ? :white : {a: 0}, font_descriptor: {size: 16, weight: :bold}, text_x: 0, text_y: OS.windows? ? 0 : 5)
62
+
63
+ horizontal_box {
64
+ label('Width')
65
+ spinbox(1, 1000) {
66
+ value <=> [self, :label_width, after_write: method(:rebuild_text_label)]
67
+ }
68
+ }
69
+
70
+ horizontal_box {
71
+ label('Height')
72
+ spinbox(1, 1000) {
73
+ value <=> [self, :label_height, after_write: method(:rebuild_text_label)]
74
+ }
75
+ }
76
+
77
+ horizontal_box {
78
+ label('Font')
79
+ font_button {
80
+ font <=> [self, :label_font_descriptor, after_write: method(:rebuild_text_label)]
81
+ }
82
+ }
83
+
84
+ horizontal_box {
85
+ label('Text Color')
86
+ color_button {
87
+ color <=> [self, :label_text_color, after_write: method(:rebuild_text_label)]
88
+ }
89
+ }
90
+
91
+ horizontal_box {
92
+ label('Background Color')
93
+ color_button {
94
+ color <=> [self, :label_background_fill, after_write: method(:rebuild_text_label)]
95
+ }
96
+ }
97
+
98
+ horizontal_box {
99
+ label('Border Color')
100
+ color_button {
101
+ color <=> [self, :label_border_stroke, after_write: method(:rebuild_text_label)]
102
+ }
103
+ }
104
+
105
+ horizontal_box {
106
+ label('Text X (0=centered)')
107
+ spinbox(0, 1000) {
108
+ value <=> [self, :label_text_x, on_read: ->(x) {x.nil? ? 0 : x}, on_write: ->(x) {x == 0 ? nil : x}, after_write: method(:rebuild_text_label)]
109
+ }
110
+ }
111
+
112
+ horizontal_box {
113
+ label('Text Y (0=centered)')
114
+ spinbox(0, 1000) {
115
+ value <=> [self, :label_text_y, on_read: ->(y) {y.nil? ? 0 : y}, on_write: ->(y) {y == 0 ? nil : y}, after_write: method(:rebuild_text_label)]
116
+ }
117
+ }
118
+ }
119
+
120
+ @text_label = text_label('This is a text label.',
121
+ width: label_width, height: label_height, font_descriptor: label_font_descriptor,
122
+ background_fill: label_background_fill, text_color: label_text_color, border_stroke: label_border_stroke,
123
+ text_x: label_text_x, text_y: label_text_y)
124
+ }
125
+ }
126
+
127
+ tab_item('Push Button') {
128
+ @push_button_vertical_box = vertical_box {
129
+ vertical_box {
130
+ text_label('Push Button Form:', width: 385, height: 30, background_fill: OS.windows? ? :white : {a: 0}, border_stroke: OS.windows? ? :white : {a: 0}, font_descriptor: {size: 16, weight: :bold}, text_x: 0, text_y: OS.windows? ? 0 : 5)
131
+
132
+ horizontal_box {
133
+ label('Width')
134
+ spinbox(1, 1000) {
135
+ value <=> [self, :button_width, after_write: method(:rebuild_push_button)]
136
+ }
137
+ }
138
+
139
+ horizontal_box {
140
+ label('Height')
141
+ spinbox(1, 1000) {
142
+ value <=> [self, :button_height, after_write: method(:rebuild_push_button)]
143
+ }
144
+ }
145
+
146
+ horizontal_box {
147
+ label('Font')
148
+ font_button {
149
+ font <=> [self, :button_font_descriptor, after_write: method(:rebuild_push_button)]
150
+ }
151
+ }
152
+
153
+ horizontal_box {
154
+ label('Text Color')
155
+ color_button {
156
+ color <=> [self, :button_text_color, after_write: method(:rebuild_push_button)]
157
+ }
158
+ }
159
+
160
+ horizontal_box {
161
+ label('Background Color')
162
+ color_button {
163
+ color <=> [self, :button_background_fill, after_write: method(:rebuild_push_button)]
164
+ }
165
+ }
166
+
167
+ horizontal_box {
168
+ label('Border Color')
169
+ color_button {
170
+ color <=> [self, :button_border_stroke, after_write: method(:rebuild_push_button)]
171
+ }
172
+ }
173
+
174
+ horizontal_box {
175
+ label('Text X (0=centered)')
176
+ spinbox(0, 1000) {
177
+ value <=> [self, :button_text_x, on_read: ->(x) {x.nil? ? 0 : x}, on_write: ->(x) {x == 0 ? nil : x}, after_write: method(:rebuild_push_button)]
178
+ }
179
+ }
180
+
181
+ horizontal_box {
182
+ label('Text Y (0=centered)')
183
+ spinbox(0, 1000) {
184
+ value <=> [self, :button_text_y, on_read: ->(y) {y.nil? ? 0 : y}, on_write: ->(y) {y == 0 ? nil : y}, after_write: method(:rebuild_push_button)]
185
+ }
186
+ }
187
+ }
188
+
189
+ @push_button = push_button('Push',
190
+ width: button_width, height: button_height, font_descriptor: button_font_descriptor,
191
+ background_fill: button_background_fill, text_color: button_text_color, border_stroke: button_border_stroke,
192
+ text_x: button_text_x, text_y: button_text_y) {
193
+ on_mouse_up do
194
+ message_box('Button Pushed', 'Thank you for pushing the button!')
195
+ end
196
+ }
197
+ }
198
+ }
199
+ }
200
+ }.show
201
+ end
202
+
203
+ # text label (area-based custom control) built with vector graphics on top of area.
204
+ #
205
+ # background_fill is transparent by default.
206
+ # background_fill can accept a single color or gradient stops just as per `fill` property in README.
207
+ # border_stroke is transparent by default.
208
+ # border_stroke can accept thickness and dashes in addition to color just as per `stroke` property in README.
209
+ # text_x and text_y are the offset of the label text in relation to its top-left corner.
210
+ # When text_x, text_y are left nil, the text is automatically centered in the label area.
211
+ # Sometimes, the centering calculation is not perfect due to using a custom font, so
212
+ # in that case, pass in text_x, and text_y manually.
213
+ def text_label(label_text,
214
+ width: 80, height: 30, font_descriptor: {},
215
+ background_fill: {a: 0}, text_color: :black, border_stroke: {a: 0},
216
+ text_x: nil, text_y: nil,
217
+ &content)
218
+ area { |the_area|
219
+ rectangle(1, 1, width, height) {
220
+ fill background_fill
221
+ }
222
+ rectangle(1, 1, width, height) {
223
+ stroke border_stroke
224
+ }
225
+
226
+ text_height = (font_descriptor[:size] || 12) * (OS.mac? ? 0.75 : 1.35)
227
+ text_width = (text_height * label_text.size) * (OS.mac? ? 0.75 : 0.60)
228
+ text_x ||= (width - text_width) / 2.0
229
+ text_y ||= (height - 4 - text_height) / 2.0
230
+ text(text_x, text_y, width) {
231
+ string(label_text) {
232
+ color text_color
233
+ font font_descriptor
234
+ }
235
+ }
236
+
237
+ content&.call(the_area)
238
+ }
239
+ end
240
+
241
+ # push button (area-based custom control) built with vector graphics on top of area.
242
+ #
243
+ # background_fill is white by default.
244
+ # background_fill can accept a single color or gradient stops just as per `fill` property in README.
245
+ # border_stroke is black by default.
246
+ # border_stroke can accept thickness and dashes in addition to color just as per `stroke` property in README.
247
+ # text_x and text_y are the offset of the button text in relation to its top-left corner.
248
+ # When text_x, text_y are left nil, the text is automatically centered in the button area.
249
+ # Sometimes, the centering calculation is not perfect due to using a custom font, so
250
+ # in that case, pass in text_x, and text_y manually.
251
+ #
252
+ # reuses the text_label custom control
253
+ def push_button(button_text,
254
+ width: 80, height: 30, font_descriptor: {},
255
+ background_fill: :white, text_color: :black, border_stroke: {r: 201, g: 201, b: 201},
256
+ text_x: nil, text_y: nil,
257
+ &content)
258
+ text_label(button_text,
259
+ width: width, height: height, font_descriptor: font_descriptor,
260
+ background_fill: background_fill, text_color: text_color, border_stroke: border_stroke,
261
+ text_x: text_x, text_y: text_y) { |the_area|
262
+
263
+ # dig into the_area content and grab elements to modify in mouse listeners below
264
+ background_rectangle = the_area.children[0]
265
+ button_string = the_area.children[2].children[0]
266
+
267
+ on_mouse_down do
268
+ background_rectangle.fill = {x0: 0, y0: 0, x1: 0, y1: height, stops: [{pos: 0, r: 72, g: 146, b: 247}, {pos: 1, r: 12, g: 85, b: 214}]}
269
+ button_string.color = :white
270
+ end
271
+
272
+ on_mouse_up do
273
+ background_rectangle.fill = background_fill
274
+ button_string.color = text_color
275
+ end
276
+
277
+ content&.call(the_area)
278
+ }
279
+ end
280
+ end
281
+
282
+ AreaBasedCustomControls.new.launch
@@ -11,8 +11,9 @@ class Snake
11
11
  FILE_HIGH_SCORE = File.expand_path(File.join(Dir.home, '.glimmer-snake'))
12
12
 
13
13
  attr_reader :width, :height
14
- attr_accessor :snake, :apple, :over, :score, :high_score
14
+ attr_accessor :snake, :apple, :over, :score, :high_score, :paused
15
15
  alias over? over
16
+ alias paused? paused
16
17
 
17
18
  def initialize(width = WIDTH_DEFAULT, height = HEIGHT_DEFAULT)
18
19
  @width = width
@@ -42,6 +43,22 @@ class Snake
42
43
  self.apple.generate
43
44
  end
44
45
 
46
+ def pause
47
+ self.paused = true
48
+ end
49
+
50
+ def resume
51
+ self.paused = false
52
+ end
53
+
54
+ def toggle_pause
55
+ unless paused?
56
+ pause
57
+ else
58
+ resume
59
+ end
60
+ end
61
+
45
62
  # inspect is overridden to prevent printing very long stack traces
46
63
  def inspect
47
64
  "#{super[0, 75]}... >"
data/examples/snake.rb CHANGED
@@ -32,7 +32,7 @@ class Snake
32
32
  end
33
33
 
34
34
  Glimmer::LibUI.timer(SNAKE_MOVE_DELAY) do
35
- unless @game.over?
35
+ unless @game.paused? || @game.over?
36
36
  process_queued_keypress
37
37
  @game.snake.move
38
38
  end
@@ -73,7 +73,11 @@ class Snake
73
73
  }
74
74
 
75
75
  on_key_up do |area_key_event|
76
- @keypress_queue << area_key_event[:ext_key]
76
+ if area_key_event[:key] == ' '
77
+ @game.toggle_pause
78
+ else
79
+ @keypress_queue << area_key_event[:ext_key]
80
+ end
77
81
  end
78
82
  }
79
83
  end
data/examples/snake2.rb CHANGED
@@ -40,7 +40,7 @@ class Snake
40
40
  end
41
41
 
42
42
  Glimmer::LibUI.timer(SNAKE_MOVE_DELAY) do
43
- unless @game.over?
43
+ unless @game.paused? || @game.over?
44
44
  process_queued_keypress
45
45
  @game.snake.move
46
46
  end
@@ -83,7 +83,11 @@ class Snake
83
83
  }
84
84
 
85
85
  on_key_up do |area_key_event|
86
- @keypress_queue << area_key_event[:ext_key]
86
+ if area_key_event[:key] == ' '
87
+ @game.toggle_pause
88
+ else
89
+ @keypress_queue << area_key_event[:ext_key]
90
+ end
87
91
  end
88
92
  }
89
93
  end
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2007-2021 Andy Maleh
1
+ # Copyright (c) 2021-2022 Andy Maleh
2
2
  #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining
4
4
  # a copy of this software and associated documentation files (the
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2007-2021 Andy Maleh
1
+ # Copyright (c) 2021-2022 Andy Maleh
2
2
  #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining
4
4
  # a copy of this software and associated documentation files (the
@@ -36,21 +36,27 @@ class Tetris
36
36
  PREVIEW_PLAYFIELD_WIDTH = 4
37
37
  PREVIEW_PLAYFIELD_HEIGHT = 2
38
38
  SCORE_MULTIPLIER = {1 => 40, 2 => 100, 3 => 300, 4 => 1200}
39
+ UP_ARROW_ACTIONS = %i[instant_down rotate_right rotate_left]
40
+ SPEEDS = %i[snail sloth turtle rabbit gorilla bear horse gazelle cheetah falcon]
41
+ SPEED_INITIAL_DELAYS = SPEEDS.each_with_index.inject({}) {|hash, speed_index_pair| hash.merge(speed_index_pair.first => 1.1 - speed_index_pair.last*(0.1)) }
39
42
 
40
43
  attr_reader :playfield_width, :playfield_height
41
- attr_accessor :game_over, :paused, :preview_tetromino, :lines, :score, :level, :high_scores, :beeping, :added_high_score, :show_high_scores, :up_arrow_action
44
+ attr_accessor :game_over, :paused, :preview_tetromino, :lines, :score, :level, :high_scores, :beeping, :added_high_score, :show_high_scores, :up_arrow_action, :show_preview_tetromino, :initial_delay
42
45
  alias game_over? game_over
43
46
  alias paused? paused
44
47
  alias beeping? beeping
45
48
  alias added_high_score? added_high_score
49
+ alias show_preview_tetromino? show_preview_tetromino
46
50
 
47
51
  def initialize(playfield_width = PLAYFIELD_WIDTH, playfield_height = PLAYFIELD_HEIGHT)
52
+ @initial_delay = SPEED_INITIAL_DELAYS[:snail]
48
53
  @playfield_width = playfield_width
49
54
  @playfield_height = playfield_height
50
55
  @high_scores = []
51
56
  @show_high_scores = false
52
57
  @beeping = true
53
58
  @up_arrow_action = :rotate_left
59
+ @show_preview_tetromino = true
54
60
  load_high_scores!
55
61
  end
56
62
 
@@ -200,35 +206,31 @@ class Tetris
200
206
  end
201
207
 
202
208
  def delay
203
- [1.1 - (level.to_i * 0.1), 0.001].max
209
+ [@initial_delay - (level.to_i * 0.1), 0.001].max
204
210
  end
205
211
 
206
212
  def beep
207
213
  @beeper&.call if beeping
208
214
  end
209
-
210
- def instant_down_on_up=(value)
211
- self.up_arrow_action = :instant_down if value
212
- end
213
-
214
- def instant_down_on_up
215
- self.up_arrow_action == :instant_down
216
- end
217
-
218
- def rotate_right_on_up=(value)
219
- self.up_arrow_action = :rotate_right if value
220
- end
221
-
222
- def rotate_right_on_up
223
- self.up_arrow_action == :rotate_right
224
- end
225
-
226
- def rotate_left_on_up=(value)
227
- self.up_arrow_action = :rotate_left if value
215
+
216
+ SPEED_INITIAL_DELAYS.each do |speed, speed_initial_day|
217
+ define_method("speed_#{speed}=") do |is_true|
218
+ self.initial_delay = speed_initial_day if is_true
219
+ end
220
+
221
+ define_method("speed_#{speed}") do
222
+ self.initial_delay == speed_initial_day
223
+ end
228
224
  end
229
225
 
230
- def rotate_left_on_up
231
- self.up_arrow_action == :rotate_left
226
+ UP_ARROW_ACTIONS.each do |up_arrow_action_symbol|
227
+ define_method("#{up_arrow_action_symbol}_on_up=") do |is_true|
228
+ self.up_arrow_action = up_arrow_action_symbol if is_true
229
+ end
230
+
231
+ define_method("#{up_arrow_action_symbol}_on_up") do
232
+ self.up_arrow_action == up_arrow_action_symbol
233
+ end
232
234
  end
233
235
 
234
236
  def reset_tetrominoes
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2007-2021 Andy Maleh
1
+ # Copyright (c) 2021-2022 Andy Maleh
2
2
  #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining
4
4
  # a copy of this software and associated documentation files (the
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2007-2021 Andy Maleh
1
+ # Copyright (c) 2021-2022 Andy Maleh
2
2
  #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining
4
4
  # a copy of this software and associated documentation files (the
data/examples/tetris.rb CHANGED
@@ -5,7 +5,7 @@ require_relative 'tetris/model/game'
5
5
  class Tetris
6
6
  include Glimmer
7
7
 
8
- BLOCK_SIZE = 25
8
+ BLOCK_SIZE = OS.linux? ? 28 : 25
9
9
  BEVEL_CONSTANT = 20
10
10
  COLOR_GRAY = {r: 192, g: 192, b: 192}
11
11
 
@@ -71,18 +71,31 @@ class Tetris
71
71
 
72
72
  Model::Game::PREVIEW_PLAYFIELD_HEIGHT.times do |row|
73
73
  Model::Game::PREVIEW_PLAYFIELD_WIDTH.times do |column|
74
- observe(@game.preview_playfield[row][column], :color) do |new_color|
74
+ preview_updater = proc do
75
75
  Glimmer::LibUI.queue_main do
76
+ new_color = @game.preview_playfield[row][column].color
76
77
  color = Glimmer::LibUI.interpret_color(new_color)
77
78
  block = @preview_playfield_blocks[row][column]
78
- block[:background_square].fill = color
79
- block[:top_bevel_edge].fill = {r: color[:r] + 4*BEVEL_CONSTANT, g: color[:g] + 4*BEVEL_CONSTANT, b: color[:b] + 4*BEVEL_CONSTANT}
80
- block[:right_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
81
- block[:bottom_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
82
- block[:left_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
83
- block[:border_square].stroke = new_color == Model::Block::COLOR_CLEAR ? COLOR_GRAY : color
79
+ if @game.show_preview_tetromino?
80
+ block[:background_square].fill = color
81
+ block[:top_bevel_edge].fill = {r: color[:r] + 4*BEVEL_CONSTANT, g: color[:g] + 4*BEVEL_CONSTANT, b: color[:b] + 4*BEVEL_CONSTANT}
82
+ block[:right_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
83
+ block[:bottom_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
84
+ block[:left_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
85
+ block[:border_square].stroke = new_color == Model::Block::COLOR_CLEAR ? COLOR_GRAY : color
86
+ else
87
+ transparent_color = {r: 255, g: 255, b: 255, a: 0}
88
+ block[:background_square].fill = transparent_color
89
+ block[:top_bevel_edge].fill = transparent_color
90
+ block[:right_bevel_edge].fill = transparent_color
91
+ block[:bottom_bevel_edge].fill = transparent_color
92
+ block[:left_bevel_edge].fill = transparent_color
93
+ block[:border_square].stroke = transparent_color
94
+ end
84
95
  end
85
96
  end
97
+ observe(@game.preview_playfield[row][column], :color, &preview_updater)
98
+ observe(@game, :show_preview_tetromino, &preview_updater)
86
99
  end
87
100
  end
88
101
 
@@ -130,6 +143,12 @@ class Tetris
130
143
  }
131
144
 
132
145
  menu('View') {
146
+ check_menu_item('Show Next Block Preview') {
147
+ checked <=> [@game, :show_preview_tetromino]
148
+ }
149
+
150
+ separator_menu_item
151
+
133
152
  menu_item('Show High Scores') {
134
153
  on_clicked do
135
154
  show_high_scores
@@ -141,18 +160,28 @@ class Tetris
141
160
  @game.clear_high_scores!
142
161
  }
143
162
  }
163
+
164
+ separator_menu_item
165
+ }
166
+
167
+ menu('Speed') {
168
+ Model::Game::SPEEDS.each do |speed|
169
+ radio_menu_item(speed.to_s.capitalize) {
170
+ checked <=> [@game, "speed_#{speed}"]
171
+ }
172
+ end
144
173
  }
145
174
 
146
175
  menu('Options') {
147
- radio_menu_item('Instant Down on Up Arrow') { |r|
176
+ radio_menu_item('Instant Down on Up Arrow') {
148
177
  checked <=> [@game, :instant_down_on_up]
149
178
  }
150
179
 
151
- radio_menu_item('Rotate Right on Up Arrow') { |r|
180
+ radio_menu_item('Rotate Right on Up Arrow') {
152
181
  checked <=> [@game, :rotate_right_on_up]
153
182
  }
154
183
 
155
- radio_menu_item('Rotate Left on Up Arrow') { |r|
184
+ radio_menu_item('Rotate Left on Up Arrow') {
156
185
  checked <=> [@game, :rotate_left_on_up]
157
186
  }
158
187
  }
@@ -199,7 +228,7 @@ class Tetris
199
228
  block = {}
200
229
  bevel_pixel_size = 0.16 * block_size.to_f
201
230
  color = Glimmer::LibUI.interpret_color(Model::Block::COLOR_CLEAR)
202
- area {
231
+ block[:area] = area {
203
232
  block[:background_square] = square(0, 0, block_size) {
204
233
  fill color
205
234
  }
@@ -275,12 +304,6 @@ class Tetris
275
304
 
276
305
  def score_board(block_size: , &extra_content)
277
306
  vertical_box {
278
- horizontal_box {
279
- label # filler
280
- @preview_playfield_blocks = playfield(playfield_width: Model::Game::PREVIEW_PLAYFIELD_WIDTH, playfield_height: Model::Game::PREVIEW_PLAYFIELD_HEIGHT, block_size: block_size)
281
- label # filler
282
- }
283
-
284
307
  horizontal_box {
285
308
  label # filler
286
309
  grid {
@@ -321,6 +344,12 @@ class Tetris
321
344
  }
322
345
  label # filler
323
346
  }
347
+
348
+ horizontal_box {
349
+ label # filler
350
+ @preview_playfield_blocks = playfield(playfield_width: Model::Game::PREVIEW_PLAYFIELD_WIDTH, playfield_height: Model::Game::PREVIEW_PLAYFIELD_HEIGHT, block_size: block_size)
351
+ label # filler
352
+ }
324
353
 
325
354
  extra_content&.call
326
355
  }
@@ -329,9 +358,11 @@ class Tetris
329
358
  def start_moving_tetrominos_down
330
359
  unless @tetrominos_start_moving_down
331
360
  @tetrominos_start_moving_down = true
332
- Glimmer::LibUI.timer(@game.delay) do
361
+ tetromino_move = proc do
333
362
  @game.down! if !@game.game_over? && !@game.paused?
363
+ Glimmer::LibUI.timer(@game.delay, repeat: false, &tetromino_move)
334
364
  end
365
+ Glimmer::LibUI.timer(@game.delay, repeat: false, &tetromino_move)
335
366
  end
336
367
  end
337
368
 
@@ -360,7 +391,7 @@ class Tetris
360
391
 
361
392
  def show_about_dialog
362
393
  Glimmer::LibUI.queue_main do
363
- msg_box('About', 'Glimmer Tetris - Glimmer DSL for LibUI Example - Copyright (c) 2021 Andy Maleh')
394
+ msg_box('About', 'Glimmer Tetris - Glimmer DSL for LibUI Example - Copyright (c) 2021-2022 Andy Maleh')
364
395
  end
365
396
  end
366
397
  end
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2007-2021 Andy Maleh
1
+ # Copyright (c) 2021-2022 Andy Maleh
2
2
  #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining
4
4
  # a copy of this software and associated documentation files (the
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2007-2021 Andy Maleh
1
+ # Copyright (c) 2021-2022 Andy Maleh
2
2
  #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining
4
4
  # a copy of this software and associated documentation files (the
Binary file
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2021 Andy Maleh
1
+ # Copyright (c) 2021-2022 Andy Maleh
2
2
  #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining
4
4
  # a copy of this software and associated documentation files (the
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2021 Andy Maleh
1
+ # Copyright (c) 2021-2022 Andy Maleh
2
2
  #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining
4
4
  # a copy of this software and associated documentation files (the
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2021 Andy Maleh
1
+ # Copyright (c) 2021-2022 Andy Maleh
2
2
  #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining
4
4
  # a copy of this software and associated documentation files (the
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2021 Andy Maleh
1
+ # Copyright (c) 2021-2022 Andy Maleh
2
2
  #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining
4
4
  # a copy of this software and associated documentation files (the