glimmer-dsl-swt 4.18.2.0 → 4.18.2.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.
@@ -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
+