glimmer-dsl-swt 4.18.3.0 → 4.18.3.5
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +59 -0
- data/README.md +440 -18
- data/VERSION +1 -1
- data/glimmer-dsl-swt.gemspec +11 -7
- data/lib/ext/glimmer/config.rb +24 -7
- data/lib/ext/rouge/themes/glimmer.rb +29 -0
- data/lib/glimmer-dsl-swt.rb +0 -1
- data/lib/glimmer/data_binding/table_items_binding.rb +8 -5
- data/lib/glimmer/data_binding/widget_binding.rb +22 -4
- data/lib/glimmer/dsl/swt/image_expression.rb +14 -6
- data/lib/glimmer/dsl/swt/layout_data_expression.rb +4 -4
- data/lib/glimmer/dsl/swt/layout_expression.rb +5 -3
- data/lib/glimmer/swt/custom/code_text.rb +132 -37
- data/lib/glimmer/swt/custom/drawable.rb +3 -2
- data/lib/glimmer/swt/custom/shape.rb +25 -11
- data/lib/glimmer/swt/date_time_proxy.rb +1 -3
- data/lib/glimmer/swt/directory_dialog_proxy.rb +3 -3
- data/lib/glimmer/swt/display_proxy.rb +26 -5
- data/lib/glimmer/swt/file_dialog_proxy.rb +3 -3
- data/lib/glimmer/swt/font_proxy.rb +1 -0
- data/lib/glimmer/swt/image_proxy.rb +68 -1
- data/lib/glimmer/swt/shell_proxy.rb +23 -3
- data/lib/glimmer/swt/table_proxy.rb +43 -15
- data/lib/glimmer/swt/widget_listener_proxy.rb +14 -5
- data/lib/glimmer/swt/widget_proxy.rb +7 -3
- data/lib/glimmer/ui/custom_shell.rb +11 -9
- data/lib/glimmer/ui/custom_widget.rb +32 -17
- data/samples/elaborate/meta_sample.rb +81 -24
- data/samples/elaborate/tetris.rb +146 -42
- data/samples/elaborate/tetris/model/game.rb +259 -137
- data/samples/elaborate/tetris/{view/game_over_dialog.rb → model/past_game.rb} +11 -44
- data/samples/elaborate/tetris/model/tetromino.rb +45 -29
- data/samples/elaborate/tetris/view/block.rb +8 -13
- data/samples/elaborate/tetris/view/high_score_dialog.rb +131 -0
- data/samples/elaborate/tetris/view/playfield.rb +1 -1
- data/samples/elaborate/tetris/view/score_lane.rb +11 -11
- data/samples/elaborate/tetris/view/tetris_menu_bar.rb +139 -0
- data/samples/elaborate/tic_tac_toe.rb +4 -4
- data/samples/hello/hello_canvas.rb +4 -4
- data/samples/hello/hello_canvas_animation.rb +3 -3
- data/samples/hello/hello_canvas_transform.rb +1 -1
- data/samples/hello/hello_code_text.rb +84 -0
- data/samples/hello/hello_link.rb +1 -1
- metadata +9 -5
@@ -26,10 +26,12 @@ module Glimmer
|
|
26
26
|
# Follows the Proxy Design Pattern
|
27
27
|
class WidgetListenerProxy
|
28
28
|
|
29
|
-
attr_reader :swt_widget, :swt_listener, :widget_add_listener_method, :swt_listener_class, :swt_listener_method, :event_type, :swt_constant
|
29
|
+
attr_reader :swt_widget, :swt_display, :swt_listener, :widget_add_listener_method, :swt_listener_class, :swt_listener_method, :event_type, :swt_constant
|
30
30
|
|
31
|
-
def initialize(swt_widget
|
31
|
+
def initialize(swt_widget:nil, swt_display:nil, swt_listener:, widget_add_listener_method: nil, swt_listener_class: nil, swt_listener_method: nil, event_type: nil, swt_constant: nil, filter: false)
|
32
32
|
@swt_widget = swt_widget
|
33
|
+
@swt_display = swt_display
|
34
|
+
@filter = filter
|
33
35
|
@swt_listener = swt_listener
|
34
36
|
@widget_add_listener_method = widget_add_listener_method
|
35
37
|
@swt_listener_class = swt_listener_class
|
@@ -42,14 +44,21 @@ module Glimmer
|
|
42
44
|
@widget_add_listener_method.sub('add', 'remove')
|
43
45
|
end
|
44
46
|
|
45
|
-
def
|
46
|
-
|
47
|
-
if @
|
47
|
+
def deregister
|
48
|
+
return if @swt_widget&.is_disposed || @swt_display&.is_disposed
|
49
|
+
if @swt_display
|
50
|
+
if @filter
|
51
|
+
@swt_display.removeFilter(@event_type, @swt_listener)
|
52
|
+
else
|
53
|
+
@swt_display.removeListener(@event_type, @swt_listener)
|
54
|
+
end
|
55
|
+
elsif @event_type
|
48
56
|
@swt_widget.removeListener(@event_type, @swt_listener)
|
49
57
|
else
|
50
58
|
@swt_widget.send(widget_remove_listener_method, @swt_listener)
|
51
59
|
end
|
52
60
|
end
|
61
|
+
alias unregister deregister # TODO consider dropping unregister (and in Observer too)
|
53
62
|
end
|
54
63
|
end
|
55
64
|
end
|
@@ -77,7 +77,7 @@ module Glimmer
|
|
77
77
|
composite.layout = GridLayout.new if composite.get_layout.nil?
|
78
78
|
end,
|
79
79
|
canvas: lambda do |canvas|
|
80
|
-
canvas.layout = nil
|
80
|
+
canvas.layout = nil if canvas.respond_to?('layout=') && !canvas.get_layout.nil?
|
81
81
|
end,
|
82
82
|
scrolled_composite: lambda do |scrolled_composite|
|
83
83
|
scrolled_composite.expand_horizontal = true
|
@@ -547,6 +547,10 @@ module Glimmer
|
|
547
547
|
@swt_widget.dispose
|
548
548
|
end
|
549
549
|
|
550
|
+
def disposed?
|
551
|
+
@swt_widget.isDisposed
|
552
|
+
end
|
553
|
+
|
550
554
|
# TODO Consider renaming these methods as they are mainly used for data-binding
|
551
555
|
|
552
556
|
def can_add_observer?(property_name)
|
@@ -663,7 +667,7 @@ module Glimmer
|
|
663
667
|
can_handle_observation_request?(method) ||
|
664
668
|
swt_widget.respond_to?(method, *args, &block)
|
665
669
|
end
|
666
|
-
|
670
|
+
|
667
671
|
private
|
668
672
|
|
669
673
|
def style(underscored_widget_name, styles)
|
@@ -696,7 +700,7 @@ module Glimmer
|
|
696
700
|
safe_block = lambda { |*args| block.call(*args) unless @swt_widget.isDisposed }
|
697
701
|
listener = listener_class.new(listener_method => safe_block)
|
698
702
|
@swt_widget.send(widget_add_listener_method, listener)
|
699
|
-
|
703
|
+
WidgetListenerProxy.new(swt_widget: @swt_widget, swt_listener: listener, widget_add_listener_method: widget_add_listener_method, swt_listener_class: listener_class, swt_listener_method: listener_method)
|
700
704
|
end
|
701
705
|
|
702
706
|
# Looks through SWT class add***Listener methods till it finds one for which
|
@@ -28,15 +28,12 @@ module Glimmer
|
|
28
28
|
include Glimmer::UI::CustomWidget
|
29
29
|
|
30
30
|
class << self
|
31
|
-
attr_reader :
|
31
|
+
attr_reader :launched_custom_shell
|
32
32
|
|
33
|
-
def launch
|
34
|
-
@
|
35
|
-
@
|
36
|
-
|
37
|
-
|
38
|
-
def shutdown
|
39
|
-
@custom_shell.close
|
33
|
+
def launch(*args, &content)
|
34
|
+
@launched_custom_shell = send(keyword, *args, &content) if @launched_custom_shell.nil? || @launched_custom_shell.disposed?
|
35
|
+
@launched_custom_shell.swt_widget.set_data('launched', true)
|
36
|
+
@launched_custom_shell.open
|
40
37
|
end
|
41
38
|
end
|
42
39
|
|
@@ -45,7 +42,7 @@ module Glimmer
|
|
45
42
|
@swt_widget.set_data('custom_shell', self)
|
46
43
|
raise Error, 'Invalid custom shell body root! Must be a shell or another custom shell.' unless body_root.swt_widget.is_a?(org.eclipse.swt.widgets.Shell)
|
47
44
|
end
|
48
|
-
|
45
|
+
|
49
46
|
# Classes may override
|
50
47
|
def open
|
51
48
|
body_root.open
|
@@ -56,6 +53,7 @@ module Glimmer
|
|
56
53
|
open
|
57
54
|
end
|
58
55
|
|
56
|
+
# TODO consider using Forwardable instead
|
59
57
|
def close
|
60
58
|
body_root.close
|
61
59
|
end
|
@@ -68,6 +66,10 @@ module Glimmer
|
|
68
66
|
body_root.visible?
|
69
67
|
end
|
70
68
|
|
69
|
+
def disposed?
|
70
|
+
swt_widget.is_disposed
|
71
|
+
end
|
72
|
+
|
71
73
|
def center_within_display
|
72
74
|
body_root.center_within_display
|
73
75
|
end
|
@@ -77,7 +77,17 @@ module Glimmer
|
|
77
77
|
def flyweight_custom_widget_classes
|
78
78
|
@flyweight_custom_widget_classes ||= {}
|
79
79
|
end
|
80
|
-
|
80
|
+
|
81
|
+
# Returns keyword to use for this custom widget
|
82
|
+
def keyword
|
83
|
+
self.name.underscore.gsub('::', '__')
|
84
|
+
end
|
85
|
+
|
86
|
+
# Returns shortcut keyword to use for this custom widget (keyword minus namespace)
|
87
|
+
def shortcut_keyword
|
88
|
+
self.name.underscore.gsub('::', '__').split('__').last
|
89
|
+
end
|
90
|
+
|
81
91
|
def add_custom_widget_namespaces_for(klass)
|
82
92
|
Glimmer::UI::CustomWidget.namespaces_for_class(klass).drop(1).each do |namespace|
|
83
93
|
Glimmer::UI::CustomWidget.custom_widget_namespaces << namespace
|
@@ -138,8 +148,7 @@ module Glimmer
|
|
138
148
|
end
|
139
149
|
|
140
150
|
def before_body(&block)
|
141
|
-
@
|
142
|
-
@before_body_blocks << block
|
151
|
+
@before_body_block = block
|
143
152
|
end
|
144
153
|
|
145
154
|
def body(&block)
|
@@ -147,29 +156,27 @@ module Glimmer
|
|
147
156
|
end
|
148
157
|
|
149
158
|
def after_body(&block)
|
150
|
-
@
|
151
|
-
@after_body_blocks << block
|
159
|
+
@after_body_block = block
|
152
160
|
end
|
153
161
|
end
|
154
162
|
|
155
163
|
attr_reader :body_root, :swt_widget, :parent, :parent_proxy, :swt_style, :options
|
156
164
|
|
157
165
|
def initialize(parent, *swt_constants, options, &content)
|
166
|
+
@parent_proxy = @parent = parent
|
167
|
+
@parent_proxy = @parent&.get_data('proxy') if @parent.respond_to?(:get_data) && @parent.get_data('proxy')
|
158
168
|
@swt_style = SWT::SWTProxy[*swt_constants]
|
159
169
|
options ||= {}
|
160
170
|
@options = self.class.options.merge(options)
|
161
171
|
@content = Util::ProcTracker.new(content) if content
|
162
|
-
|
172
|
+
execute_hook('before_body')
|
163
173
|
body_block = self.class.instance_variable_get("@body_block")
|
164
174
|
raise Glimmer::Error, 'Invalid custom widget for having no body! Please define body block!' if body_block.nil?
|
165
175
|
@body_root = instance_exec(&body_block)
|
166
176
|
raise Glimmer::Error, 'Invalid custom widget for having an empty body! Please fill body block!' if @body_root.nil?
|
167
177
|
@swt_widget = @body_root.swt_widget
|
168
178
|
@swt_widget.set_data('custom_widget', self)
|
169
|
-
|
170
|
-
@parent ||= @swt_widget.parent
|
171
|
-
@parent_proxy ||= @parent&.get_data('proxy')
|
172
|
-
execute_hooks('after_body')
|
179
|
+
execute_hook('after_body')
|
173
180
|
end
|
174
181
|
|
175
182
|
# Subclasses may override to perform post initialization work on an added child
|
@@ -242,10 +249,18 @@ module Glimmer
|
|
242
249
|
def attribute_setter(attribute_name)
|
243
250
|
"#{attribute_name}="
|
244
251
|
end
|
252
|
+
|
253
|
+
def disposed?
|
254
|
+
swt_widget.isDisposed
|
255
|
+
end
|
245
256
|
|
246
257
|
def has_style?(style)
|
247
258
|
(swt_style & SWT::SWTProxy[style]) == SWT::SWTProxy[style]
|
248
259
|
end
|
260
|
+
|
261
|
+
def pack(*args)
|
262
|
+
body_root.pack(*args)
|
263
|
+
end
|
249
264
|
|
250
265
|
# TODO see if it is worth it to eliminate duplication of async_exec/sync_exec
|
251
266
|
# delegation to DisplayProxy, via a module
|
@@ -287,13 +302,13 @@ module Glimmer
|
|
287
302
|
|
288
303
|
private
|
289
304
|
|
290
|
-
def
|
291
|
-
self.class.instance_variable_get("@#{hook_name}
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
305
|
+
def execute_hook(hook_name)
|
306
|
+
hook_block = self.class.instance_variable_get("@#{hook_name}_block")
|
307
|
+
return if hook_block.nil?
|
308
|
+
temp_method_name = "#{hook_name}_block_#{hook_block.hash.abs}_#{(Time.now.to_f * 1_000_000).to_i}"
|
309
|
+
singleton_class.define_method(temp_method_name, &hook_block)
|
310
|
+
send(temp_method_name)
|
311
|
+
singleton_class.send(:remove_method, temp_method_name)
|
297
312
|
end
|
298
313
|
end
|
299
314
|
end
|
@@ -25,6 +25,24 @@ require 'etc'
|
|
25
25
|
class Sample
|
26
26
|
include Glimmer::DataBinding::ObservableModel
|
27
27
|
|
28
|
+
class << self
|
29
|
+
def glimmer_directory
|
30
|
+
File.expand_path('../../..', __FILE__)
|
31
|
+
end
|
32
|
+
|
33
|
+
def user_glimmer_directory
|
34
|
+
File.join(Etc.getpwuid.dir, '.glimmer-dsl-swt')
|
35
|
+
end
|
36
|
+
|
37
|
+
def ensure_user_glimmer_directory
|
38
|
+
unless @ensured_glimmer_directory
|
39
|
+
FileUtils.rm_rf(user_glimmer_directory)
|
40
|
+
FileUtils.cp_r(glimmer_directory, user_glimmer_directory)
|
41
|
+
@ensured_glimmer_directory = true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
28
46
|
attr_accessor :sample_directory, :file, :selected
|
29
47
|
|
30
48
|
def initialize(file, sample_directory: )
|
@@ -59,25 +77,35 @@ class Sample
|
|
59
77
|
File.basename(file) != 'meta_sample.rb'
|
60
78
|
end
|
61
79
|
alias launchable editable
|
80
|
+
|
81
|
+
def file_relative_path
|
82
|
+
file.sub(self.class.glimmer_directory, '')
|
83
|
+
end
|
84
|
+
|
85
|
+
def user_file
|
86
|
+
File.join(self.class.user_glimmer_directory, file_relative_path)
|
87
|
+
end
|
88
|
+
|
89
|
+
def user_file_parent_directory
|
90
|
+
File.dirname(user_file)
|
91
|
+
end
|
62
92
|
|
93
|
+
def directory
|
94
|
+
file.sub(/\.rb/, '')
|
95
|
+
end
|
96
|
+
|
63
97
|
def launch(modified_code)
|
64
|
-
|
65
|
-
modified_file_parent_directory = File.join(Etc.getpwuid.dir, '.glimmer', 'samples', parent_directory)
|
66
|
-
launch_file = modified_file = File.join(modified_file_parent_directory, File.basename(file))
|
98
|
+
launch_file = user_file
|
67
99
|
begin
|
68
|
-
FileUtils.
|
69
|
-
FileUtils.cp_r(
|
70
|
-
|
71
|
-
File.write(modified_file, modified_code)
|
100
|
+
FileUtils.cp_r(file, user_file_parent_directory)
|
101
|
+
FileUtils.cp_r(directory, user_file_parent_directory) if File.exist?(directory)
|
102
|
+
File.write(user_file, modified_code)
|
72
103
|
rescue => e
|
73
104
|
puts 'Error writing sample modifications. Launching original sample.'
|
74
105
|
puts e.full_message
|
75
106
|
launch_file = file # load original file if failed to write changes
|
76
107
|
end
|
77
108
|
load(launch_file)
|
78
|
-
ensure
|
79
|
-
FileUtils.rm_rf(modified_file)
|
80
|
-
FileUtils.rm_rf(modified_file.sub(/\.rb/, ''))
|
81
109
|
end
|
82
110
|
end
|
83
111
|
|
@@ -162,17 +190,18 @@ class SampleDirectory
|
|
162
190
|
end
|
163
191
|
|
164
192
|
class MetaSampleApplication
|
165
|
-
include Glimmer
|
193
|
+
include Glimmer::UI::CustomShell
|
166
194
|
|
167
|
-
|
195
|
+
before_body {
|
196
|
+
Sample.ensure_user_glimmer_directory
|
168
197
|
selected_sample_directory = SampleDirectory.sample_directories.first
|
169
198
|
selected_sample = selected_sample_directory.samples.first
|
170
199
|
selected_sample_directory.selected_sample_name = selected_sample.name
|
171
|
-
end
|
172
|
-
|
173
|
-
def launch
|
174
200
|
Display.app_name = 'Glimmer Meta-Sample'
|
175
|
-
|
201
|
+
}
|
202
|
+
|
203
|
+
body {
|
204
|
+
shell(:fill_screen) {
|
176
205
|
minimum_size 1280, 768
|
177
206
|
text 'Glimmer Meta-Sample (The Sample of Samples)'
|
178
207
|
image File.expand_path('../../icons/scaffold_app.png', __dir__)
|
@@ -218,11 +247,8 @@ class MetaSampleApplication
|
|
218
247
|
on_widget_selected {
|
219
248
|
begin
|
220
249
|
SampleDirectory.selected_sample.launch(@code_text.text)
|
221
|
-
rescue StandardError, SyntaxError => launch_error
|
222
|
-
|
223
|
-
text 'Error Launching'
|
224
|
-
message launch_error.full_message
|
225
|
-
}.open
|
250
|
+
rescue LoadError, StandardError, SyntaxError => launch_error
|
251
|
+
error_dialog(message: launch_error.full_message).open
|
226
252
|
end
|
227
253
|
}
|
228
254
|
}
|
@@ -243,10 +269,41 @@ class MetaSampleApplication
|
|
243
269
|
editable bind(SampleDirectory, 'selected_sample.editable')
|
244
270
|
}
|
245
271
|
|
246
|
-
weights 4,
|
272
|
+
weights 4, 11
|
273
|
+
}
|
274
|
+
}
|
275
|
+
}
|
276
|
+
|
277
|
+
# Method-based error_dialog custom widget
|
278
|
+
def error_dialog(message:)
|
279
|
+
return if message.nil?
|
280
|
+
dialog(body_root) { |dialog_proxy|
|
281
|
+
row_layout(:vertical) {
|
282
|
+
center true
|
283
|
+
}
|
284
|
+
|
285
|
+
text 'Error Launching'
|
286
|
+
|
287
|
+
styled_text(:border, :h_scroll, :v_scroll) {
|
288
|
+
layout_data {
|
289
|
+
width body_root.bounds.width*0.75
|
290
|
+
height body_root.bounds.height*0.75
|
291
|
+
}
|
292
|
+
|
293
|
+
text message
|
294
|
+
editable false
|
295
|
+
caret nil
|
296
|
+
}
|
297
|
+
|
298
|
+
button {
|
299
|
+
text 'Close'
|
300
|
+
|
301
|
+
on_widget_selected {
|
302
|
+
dialog_proxy.close
|
303
|
+
}
|
247
304
|
}
|
248
|
-
}
|
305
|
+
}
|
249
306
|
end
|
250
307
|
end
|
251
308
|
|
252
|
-
MetaSampleApplication.
|
309
|
+
MetaSampleApplication.launch
|
data/samples/elaborate/tetris.rb
CHANGED
@@ -25,60 +25,88 @@ require_relative 'tetris/model/game'
|
|
25
25
|
|
26
26
|
require_relative 'tetris/view/playfield'
|
27
27
|
require_relative 'tetris/view/score_lane'
|
28
|
-
require_relative 'tetris/view/
|
28
|
+
require_relative 'tetris/view/high_score_dialog'
|
29
|
+
require_relative 'tetris/view/tetris_menu_bar'
|
29
30
|
|
30
31
|
class Tetris
|
31
32
|
include Glimmer::UI::CustomShell
|
32
33
|
|
33
34
|
BLOCK_SIZE = 25
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
35
|
+
FONT_NAME = 'Menlo'
|
36
|
+
FONT_TITLE_HEIGHT = 32
|
37
|
+
FONT_TITLE_STYLE = :bold
|
38
|
+
BEVEL_CONSTANT = 20
|
39
|
+
|
40
|
+
option :playfield_width, default: Model::Game::PLAYFIELD_WIDTH
|
41
|
+
option :playfield_height, default: Model::Game::PLAYFIELD_HEIGHT
|
42
|
+
|
43
|
+
attr_reader :game
|
38
44
|
|
39
45
|
before_body {
|
46
|
+
@mutex = Mutex.new
|
47
|
+
@game = Model::Game.new(playfield_width, playfield_height)
|
48
|
+
|
49
|
+
@game.configure_beeper do
|
50
|
+
display.beep
|
51
|
+
end
|
52
|
+
|
53
|
+
Display.app_name = 'Glimmer Tetris'
|
40
54
|
display {
|
41
|
-
on_swt_keydown { |key_event|
|
55
|
+
@keyboard_listener = on_swt_keydown { |key_event|
|
42
56
|
case key_event.keyCode
|
43
|
-
when swt(:arrow_down)
|
44
|
-
|
45
|
-
when swt(:
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
57
|
+
when swt(:arrow_down), 's'.bytes.first
|
58
|
+
game.down!
|
59
|
+
when swt(:arrow_up)
|
60
|
+
case game.up_arrow_action
|
61
|
+
when :instant_down
|
62
|
+
game.down!(instant: true)
|
63
|
+
when :rotate_right
|
64
|
+
game.rotate!(:right)
|
65
|
+
when :rotate_left
|
66
|
+
game.rotate!(:left)
|
67
|
+
end
|
68
|
+
when swt(:arrow_left), 'a'.bytes.first
|
69
|
+
game.left!
|
70
|
+
when swt(:arrow_right), 'd'.bytes.first
|
71
|
+
game.right!
|
72
|
+
when swt(:shift), swt(:alt)
|
50
73
|
if key_event.keyLocation == swt(:right) # right shift key
|
51
|
-
|
74
|
+
game.rotate!(:right)
|
52
75
|
elsif key_event.keyLocation == swt(:left) # left shift key
|
53
|
-
|
76
|
+
game.rotate!(:left)
|
54
77
|
end
|
55
|
-
when
|
56
|
-
|
57
|
-
when 'a'.bytes.first
|
58
|
-
Model::Game.current_tetromino.rotate(:left)
|
78
|
+
when swt(:ctrl)
|
79
|
+
game.rotate!(:left)
|
59
80
|
end
|
60
81
|
}
|
82
|
+
|
83
|
+
# if running in app mode, set the Mac app about dialog (ignored in platforms)
|
84
|
+
@about_observer = on_about {
|
85
|
+
show_about_dialog
|
86
|
+
}
|
87
|
+
|
88
|
+
@quit_observer = on_quit {
|
89
|
+
exit(0)
|
90
|
+
}
|
61
91
|
}
|
62
|
-
|
63
|
-
Model::Game.configure_beeper do
|
64
|
-
display.beep
|
65
|
-
end
|
66
92
|
}
|
67
93
|
|
68
94
|
after_body {
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
95
|
+
@game_over_observer = observe(@game, :game_over) do |game_over|
|
96
|
+
if game_over
|
97
|
+
show_high_score_dialog
|
98
|
+
else
|
99
|
+
start_moving_tetrominos_down
|
100
|
+
end
|
101
|
+
end
|
102
|
+
@show_high_scores_observer = observe(@game, :show_high_scores) do |show_high_scores|
|
103
|
+
if show_high_scores
|
104
|
+
show_high_score_dialog
|
105
|
+
else
|
106
|
+
@high_score_dialog.close unless @high_score_dialog.nil? || @high_score_dialog.disposed? || !@high_score_dialog.visible?
|
107
|
+
end
|
108
|
+
end
|
109
|
+
@game.start!
|
82
110
|
}
|
83
111
|
|
84
112
|
body {
|
@@ -92,15 +120,91 @@ class Tetris
|
|
92
120
|
}
|
93
121
|
|
94
122
|
text 'Glimmer Tetris'
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
123
|
+
minimum_size 475, 500
|
124
|
+
image tetris_icon
|
125
|
+
|
126
|
+
tetris_menu_bar(game: game)
|
127
|
+
|
128
|
+
playfield(game_playfield: game.playfield, playfield_width: playfield_width, playfield_height: playfield_height, block_size: BLOCK_SIZE)
|
129
|
+
|
130
|
+
score_lane(game: game, block_size: BLOCK_SIZE) {
|
131
|
+
layout_data(:fill, :fill, true, true)
|
132
|
+
}
|
133
|
+
|
134
|
+
on_widget_disposed {
|
135
|
+
deregister_observers
|
101
136
|
}
|
102
137
|
}
|
103
138
|
}
|
139
|
+
|
140
|
+
def tetris_icon
|
141
|
+
icon_block_size = 64
|
142
|
+
icon_bevel_size = icon_block_size.to_f / 25.to_f
|
143
|
+
icon_bevel_pixel_size = 0.16*icon_block_size.to_f
|
144
|
+
icon_size = 8
|
145
|
+
icon_pixel_size = icon_block_size * icon_size
|
146
|
+
image(icon_pixel_size, icon_pixel_size) {
|
147
|
+
icon_size.times { |row|
|
148
|
+
icon_size.times { |column|
|
149
|
+
colored = row >= 1 && column.between?(1, 6)
|
150
|
+
color = colored ? color(([:white] + Model::Tetromino::LETTER_COLORS.values).sample) : color(:white)
|
151
|
+
x = column * icon_block_size
|
152
|
+
y = row * icon_block_size
|
153
|
+
rectangle(x, y, icon_block_size, icon_block_size, fill: true) {
|
154
|
+
background color
|
155
|
+
}
|
156
|
+
polygon(x, y, x + icon_block_size, y, x + icon_block_size - icon_bevel_pixel_size, y + icon_bevel_pixel_size, x + icon_bevel_pixel_size, y + icon_bevel_pixel_size, fill: true) {
|
157
|
+
background rgb(color.red + 4*BEVEL_CONSTANT, color.green + 4*BEVEL_CONSTANT, color.blue + 4*BEVEL_CONSTANT)
|
158
|
+
}
|
159
|
+
polygon(x + icon_block_size, y, x + icon_block_size - icon_bevel_pixel_size, y + icon_bevel_pixel_size, x + icon_block_size - icon_bevel_pixel_size, y + icon_block_size - icon_bevel_pixel_size, x + icon_block_size, y + icon_block_size, fill: true) {
|
160
|
+
background rgb(color.red - BEVEL_CONSTANT, color.green - BEVEL_CONSTANT, color.blue - BEVEL_CONSTANT)
|
161
|
+
}
|
162
|
+
polygon(x + icon_block_size, y + icon_block_size, x, y + icon_block_size, x + icon_bevel_pixel_size, y + icon_block_size - icon_bevel_pixel_size, x + icon_block_size - icon_bevel_pixel_size, y + icon_block_size - icon_bevel_pixel_size, fill: true) {
|
163
|
+
background rgb(color.red - 2*BEVEL_CONSTANT, color.green - 2*BEVEL_CONSTANT, color.blue - 2*BEVEL_CONSTANT)
|
164
|
+
}
|
165
|
+
polygon(x, y, x, y + icon_block_size, x + icon_bevel_pixel_size, y + icon_block_size - icon_bevel_pixel_size, x + icon_bevel_pixel_size, y + icon_bevel_pixel_size, fill: true) {
|
166
|
+
background rgb(color.red - BEVEL_CONSTANT, color.green - BEVEL_CONSTANT, color.blue - BEVEL_CONSTANT)
|
167
|
+
}
|
168
|
+
}
|
169
|
+
}
|
170
|
+
}
|
171
|
+
end
|
172
|
+
|
173
|
+
def start_moving_tetrominos_down
|
174
|
+
Thread.new do
|
175
|
+
@mutex.synchronize do
|
176
|
+
loop do
|
177
|
+
time = Time.now
|
178
|
+
sleep @game.delay
|
179
|
+
break if @game.game_over? || body_root.disposed?
|
180
|
+
sync_exec {
|
181
|
+
@game.down! unless @game.paused?
|
182
|
+
}
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def show_high_score_dialog
|
189
|
+
return if @high_score_dialog&.visible?
|
190
|
+
@high_score_dialog = high_score_dialog(parent_shell: body_root, game: @game) if @high_score_dialog.nil? || @high_score_dialog.disposed?
|
191
|
+
@high_score_dialog.show
|
192
|
+
end
|
193
|
+
|
194
|
+
def show_about_dialog
|
195
|
+
message_box {
|
196
|
+
text 'Glimmer Tetris'
|
197
|
+
message "Glimmer Tetris\n\nGlimmer DSL for SWT Sample\n\nCopyright (c) 2007-2021 Andy Maleh"
|
198
|
+
}.open
|
199
|
+
end
|
200
|
+
|
201
|
+
def deregister_observers
|
202
|
+
@show_high_scores_observer&.deregister
|
203
|
+
@game_over_observer&.deregister
|
204
|
+
@keyboard_listener&.deregister
|
205
|
+
@about_observer&.deregister
|
206
|
+
@quit_observer&.deregister
|
207
|
+
end
|
104
208
|
end
|
105
209
|
|
106
210
|
Tetris.launch
|