glimmer-dsl-swt 4.18.2.0 → 4.18.2.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -27,6 +27,19 @@ module Glimmer
27
27
  include SuperModule
28
28
  include Glimmer::UI::CustomWidget
29
29
 
30
+ class << self
31
+ attr_reader :custom_shell
32
+
33
+ def launch
34
+ @custom_shell = send(self.name.underscore.gsub('::', '__'))
35
+ @custom_shell.open
36
+ end
37
+
38
+ def shutdown
39
+ @custom_shell.close
40
+ end
41
+ end
42
+
30
43
  def initialize(parent, *swt_constants, options, &content)
31
44
  super
32
45
  @swt_widget.set_data('custom_shell', self)
@@ -55,8 +68,8 @@ module Glimmer
55
68
  body_root.visible?
56
69
  end
57
70
 
58
- def center
59
- body_root.center
71
+ def center_within_display
72
+ body_root.center_within_display
60
73
  end
61
74
 
62
75
  def start_event_loop
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2007-2021 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
5
5
  # "Software"), to deal in the Software without restriction, including
@@ -7,10 +7,10 @@
7
7
  # distribute, sublicense, and/or sell copies of the Software, and to
8
8
  # permit persons to whom the Software is furnished to do so, subject to
9
9
  # the following conditions:
10
- #
10
+ #
11
11
  # The above copyright notice and this permission notice shall be
12
12
  # included in all copies or substantial portions of the Software.
13
- #
13
+ #
14
14
  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
15
  # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
16
  # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@@ -34,37 +34,48 @@ module Glimmer
34
34
  include DataBinding::ObservableModel
35
35
 
36
36
  super_module_included do |klass|
37
+ # TODO clear memoization of WidgetProxy.swt_widget_class_for for a keyword if a custom widget was defined with that keyword
37
38
  klass.include(Glimmer) unless klass.name.include?('Glimmer::UI::CustomShell')
38
39
  Glimmer::UI::CustomWidget.add_custom_widget_namespaces_for(klass) unless klass.name.include?('Glimmer::UI::CustomShell')
39
40
  end
40
41
 
41
42
  class << self
42
43
  def for(underscored_custom_widget_name)
43
- extracted_namespaces = underscored_custom_widget_name.
44
- to_s.
45
- split(/__/).map do |namespace|
46
- namespace.camelcase(:upper)
47
- end
48
- custom_widget_namespaces.each do |base|
49
- extracted_namespaces.reduce(base) do |result, namespace|
50
- if !result.constants.include?(namespace)
51
- namespace = result.constants.detect {|c| c.to_s.upcase == namespace.to_s.upcase } || namespace
52
- end
53
- begin
54
- constant = result.const_get(namespace)
55
- return constant if constant.ancestors.include?(Glimmer::UI::CustomWidget)
56
- constant
57
- rescue => e
58
- # Glimmer::Config.logger.debug {"#{e.message}\n#{e.backtrace.join("\n")}"}
59
- result
44
+ unless flyweight_custom_widget_classes.keys.include?(underscored_custom_widget_name)
45
+ begin
46
+ extracted_namespaces = underscored_custom_widget_name.
47
+ to_s.
48
+ split(/__/).map do |namespace|
49
+ namespace.camelcase(:upper)
50
+ end
51
+ custom_widget_namespaces.each do |base|
52
+ extracted_namespaces.reduce(base) do |result, namespace|
53
+ if !result.constants.include?(namespace)
54
+ namespace = result.constants.detect {|c| c.to_s.upcase == namespace.to_s.upcase } || namespace
55
+ end
56
+ begin
57
+ flyweight_custom_widget_classes[underscored_custom_widget_name] = constant = result.const_get(namespace)
58
+ return constant if constant.ancestors.include?(Glimmer::UI::CustomWidget)
59
+ flyweight_custom_widget_classes[underscored_custom_widget_name] = constant
60
+ rescue => e
61
+ # Glimmer::Config.logger.debug {"#{e.message}\n#{e.backtrace.join("\n")}"}
62
+ flyweight_custom_widget_classes[underscored_custom_widget_name] = result
63
+ end
64
+ end
60
65
  end
66
+ raise "#{underscored_custom_widget_name} has no custom widget class!"
67
+ rescue => e
68
+ Glimmer::Config.logger.debug {e.message}
69
+ Glimmer::Config.logger.debug {"#{e.message}\n#{e.backtrace.join("\n")}"}
70
+ flyweight_custom_widget_classes[underscored_custom_widget_name] = nil
61
71
  end
62
72
  end
63
- raise "#{underscored_custom_widget_name} has no custom widget class!"
64
- rescue => e
65
- Glimmer::Config.logger.debug {e.message}
66
- Glimmer::Config.logger.debug {"#{e.message}\n#{e.backtrace.join("\n")}"}
67
- nil
73
+ flyweight_custom_widget_classes[underscored_custom_widget_name]
74
+ end
75
+
76
+ # Flyweight Design Pattern memoization cache. Can be cleared if memory is needed.
77
+ def flyweight_custom_widget_classes
78
+ @flyweight_custom_widget_classes ||= {}
68
79
  end
69
80
 
70
81
  def add_custom_widget_namespaces_for(klass)
@@ -141,10 +152,9 @@ module Glimmer
141
152
  end
142
153
  end
143
154
 
144
- attr_reader :body_root, :swt_widget, :parent, :swt_style, :options
155
+ attr_reader :body_root, :swt_widget, :parent, :parent_proxy, :swt_style, :options
145
156
 
146
157
  def initialize(parent, *swt_constants, options, &content)
147
- @parent = parent
148
158
  @swt_style = SWT::SWTProxy[*swt_constants]
149
159
  options ||= {}
150
160
  @options = self.class.options.merge(options)
@@ -156,6 +166,9 @@ module Glimmer
156
166
  raise Glimmer::Error, 'Invalid custom widget for having an empty body! Please fill body block!' if @body_root.nil?
157
167
  @swt_widget = @body_root.swt_widget
158
168
  @swt_widget.set_data('custom_widget', self)
169
+ @parent = parent
170
+ @parent ||= @swt_widget.parent
171
+ @parent_proxy ||= @parent&.get_data('proxy')
159
172
  execute_hooks('after_body')
160
173
  end
161
174
 
@@ -211,10 +224,10 @@ module Glimmer
211
224
 
212
225
  # This method ensures it has an instance method not coming from Glimmer DSL
213
226
  def has_instance_method?(method_name)
214
- respond_to?(method_name) and
227
+ respond_to?(method_name) and
215
228
  !swt_widget&.respond_to?(method_name) and
216
229
  (method(method_name) rescue nil) and
217
- !method(method_name)&.source_location&.first&.include?('glimmer/dsl/engine.rb') and
230
+ !method(method_name)&.source_location&.first&.include?('glimmer/dsl/engine.rb') and
218
231
  !method(method_name)&.source_location&.first&.include?('glimmer/swt/widget_proxy.rb')
219
232
  end
220
233
 
@@ -265,13 +278,13 @@ module Glimmer
265
278
  end
266
279
  end
267
280
 
268
- alias local_respond_to? respond_to?
281
+ alias local_respond_to? respond_to?
269
282
  def respond_to?(method, *args, &block)
270
283
  super or
271
284
  can_handle_observation_request?(method) or
272
285
  body_root.respond_to?(method, *args, &block)
273
286
  end
274
-
287
+
275
288
  private
276
289
 
277
290
  def execute_hooks(hook_name)
@@ -1,3 +1,24 @@
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
+
1
22
  require 'fileutils'
2
23
  require 'etc'
3
24
 
@@ -0,0 +1,106 @@
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
+ # Tetris App View Custom Shell (represents `tetris` keyword)
23
+
24
+ require_relative 'tetris/model/game'
25
+
26
+ require_relative 'tetris/view/playfield'
27
+ require_relative 'tetris/view/score_lane'
28
+ require_relative 'tetris/view/game_over_dialog'
29
+
30
+ class Tetris
31
+ include Glimmer::UI::CustomShell
32
+
33
+ BLOCK_SIZE = 25
34
+ PLAYFIELD_WIDTH = 10
35
+ PLAYFIELD_HEIGHT = 20
36
+ PREVIEW_PLAYFIELD_WIDTH = 4
37
+ PREVIEW_PLAYFIELD_HEIGHT = 2
38
+
39
+ before_body {
40
+ display {
41
+ on_swt_keydown { |key_event|
42
+ case key_event.keyCode
43
+ when swt(:arrow_down)
44
+ Model::Game.current_tetromino.down
45
+ when swt(:arrow_left)
46
+ Model::Game.current_tetromino.left
47
+ when swt(:arrow_right)
48
+ Model::Game.current_tetromino.right
49
+ when swt(:shift)
50
+ if key_event.keyLocation == swt(:right) # right shift key
51
+ Model::Game.current_tetromino.rotate(:right)
52
+ elsif key_event.keyLocation == swt(:left) # left shift key
53
+ Model::Game.current_tetromino.rotate(:left)
54
+ end
55
+ when 'd'.bytes.first, swt(:arrow_up)
56
+ Model::Game.current_tetromino.rotate(:right)
57
+ when 'a'.bytes.first
58
+ Model::Game.current_tetromino.rotate(:left)
59
+ end
60
+ }
61
+ }
62
+
63
+ Model::Game.configure_beeper do
64
+ display.beep
65
+ end
66
+ }
67
+
68
+ after_body {
69
+ Model::Game.start
70
+
71
+ Thread.new {
72
+ loop {
73
+ sleep(Model::Game.delay)
74
+ sync_exec {
75
+ unless Model::Game.game_over?
76
+ Model::Game.current_tetromino.down
77
+ game_over_dialog(parent_shell: body_root).open if Model::Game.current_tetromino.row <= 0 && Model::Game.current_tetromino.stopped?
78
+ end
79
+ }
80
+ }
81
+ }
82
+ }
83
+
84
+ body {
85
+ shell(:no_resize) {
86
+ grid_layout {
87
+ num_columns 2
88
+ make_columns_equal_width false
89
+ margin_width 0
90
+ margin_height 0
91
+ horizontal_spacing 0
92
+ }
93
+
94
+ text 'Glimmer Tetris'
95
+ background :gray
96
+
97
+ playfield(game_playfield: Model::Game.playfield, playfield_width: PLAYFIELD_WIDTH, playfield_height: PLAYFIELD_HEIGHT, block_size: BLOCK_SIZE)
98
+
99
+ score_lane(block_size: BLOCK_SIZE) {
100
+ layout_data(:fill, :fill, false, true)
101
+ }
102
+ }
103
+ }
104
+ end
105
+
106
+ Tetris.launch
@@ -0,0 +1,48 @@
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
+ class Block
25
+ COLOR_CLEAR = :white
26
+
27
+ attr_accessor :color
28
+
29
+ # Initializes with color. Default color (gray) signifies an empty block
30
+ def initialize(color = COLOR_CLEAR)
31
+ @color = color
32
+ end
33
+
34
+ # Clears block color. `quietly` option indicates if it should not notify observers by setting value quietly via variable not attribute writer.
35
+ def clear
36
+ self.color = COLOR_CLEAR unless self.color == COLOR_CLEAR
37
+ end
38
+
39
+ def clear?
40
+ self.color == COLOR_CLEAR
41
+ end
42
+
43
+ def occupied?
44
+ !clear?
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,185 @@
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 'block'
23
+ require_relative 'tetromino'
24
+
25
+ class Tetris
26
+ module Model
27
+ class Game
28
+ SCORE_MULTIPLIER = {1 => 40, 2 => 100, 3 => 300, 4 => 1200}
29
+
30
+ class << self
31
+ attr_accessor :game_over, :preview_tetromino, :lines, :score, :level
32
+ alias game_over? game_over
33
+
34
+ def consider_adding_tetromino
35
+ if tetrominoes.empty? || Game.current_tetromino.stopped?
36
+ preview_tetromino.launch!
37
+ preview_next_tetromino!
38
+ end
39
+ end
40
+
41
+ def current_tetromino
42
+ tetrominoes.last
43
+ end
44
+
45
+ def tetrominoes
46
+ @tetrominoes ||= reset_tetrominoes
47
+ end
48
+
49
+ # Returns blocks in the playfield
50
+ def playfield
51
+ @playfield ||= @original_playfield = PLAYFIELD_HEIGHT.times.map {
52
+ PLAYFIELD_WIDTH.times.map {
53
+ Block.new
54
+ }
55
+ }
56
+ end
57
+
58
+ def hypothetical(&block)
59
+ @playfield = hypothetical_playfield
60
+ block.call
61
+ @playfield = @original_playfield
62
+ end
63
+
64
+ def hypothetical?
65
+ @playfield != @original_playfield
66
+ end
67
+
68
+ def hypothetical_playfield
69
+ PLAYFIELD_HEIGHT.times.map { |row|
70
+ PLAYFIELD_WIDTH.times.map { |column|
71
+ playfield[row][column].clone
72
+ }
73
+ }
74
+ end
75
+
76
+ def preview_playfield
77
+ @preview_playfield ||= PREVIEW_PLAYFIELD_HEIGHT.times.map {|row|
78
+ PREVIEW_PLAYFIELD_WIDTH.times.map {|column|
79
+ Block.new
80
+ }
81
+ }
82
+ end
83
+
84
+ def preview_next_tetromino!
85
+ self.preview_tetromino = Tetromino.new
86
+ end
87
+
88
+ def calculate_score!(eliminated_lines)
89
+ new_score = SCORE_MULTIPLIER[eliminated_lines] * (level + 1)
90
+ self.score += new_score
91
+ end
92
+
93
+ def level_up!
94
+ self.level += 1 if lines >= self.level*10
95
+ end
96
+
97
+ def delay
98
+ [1.1 - (level.to_i * 0.1), 0.001].max
99
+ end
100
+
101
+ def consider_eliminating_lines
102
+ eliminated_lines = 0
103
+ playfield.each_with_index do |row, playfield_row|
104
+ if row.all? {|block| !block.clear?}
105
+ eliminated_lines += 1
106
+ shift_blocks_down_above_row(playfield_row)
107
+ end
108
+ end
109
+ if eliminated_lines > 0
110
+ beep
111
+ self.lines += eliminated_lines
112
+ level_up!
113
+ calculate_score!(eliminated_lines)
114
+ end
115
+ end
116
+
117
+ def beep
118
+ @beeper&.call
119
+ end
120
+
121
+ def configure_beeper(&beeper)
122
+ @beeper = beeper
123
+ end
124
+
125
+ def shift_blocks_down_above_row(row)
126
+ row.downto(0) do |playfield_row|
127
+ playfield[playfield_row].each_with_index do |block, playfield_column|
128
+ previous_row = playfield[playfield_row - 1]
129
+ previous_block = previous_row[playfield_column]
130
+ block.color = previous_block.color unless block.color == previous_block.color
131
+ end
132
+ end
133
+ playfield[0].each(&:clear)
134
+ end
135
+
136
+ def start
137
+ self.level = 1
138
+ self.score = 0
139
+ self.lines = @previoius_lines = 0
140
+ reset_playfield
141
+ reset_preview_playfield
142
+ reset_tetrominoes
143
+ preview_next_tetromino!
144
+ consider_adding_tetromino
145
+ self.game_over = false
146
+ end
147
+ alias restart start
148
+
149
+ def reset_tetrominoes
150
+ @tetrominoes = []
151
+ end
152
+
153
+ def reset_playfield
154
+ playfield.each do |row|
155
+ row.each do |block|
156
+ block.clear
157
+ end
158
+ end
159
+ end
160
+
161
+ def reset_preview_playfield
162
+ preview_playfield.each do |row|
163
+ row.each do |block|
164
+ block.clear
165
+ end
166
+ end
167
+ end
168
+
169
+ def playfield_remaining_heights(tetromino = nil)
170
+ PLAYFIELD_WIDTH.times.map do |playfield_column|
171
+ (playfield.each_with_index.detect do |row, playfield_row|
172
+ !row[playfield_column].clear? &&
173
+ (
174
+ tetromino.nil? ||
175
+ (bottom_most_block = tetromino.bottom_most_block_for_column(playfield_column)).nil? ||
176
+ (playfield_row > tetromino.row + bottom_most_block[:row])
177
+ )
178
+ end || [nil, PLAYFIELD_HEIGHT])[1]
179
+ end.to_a
180
+ end
181
+ end
182
+ end
183
+ end
184
+ end
185
+