glimmer-dsl-swt 4.20.0.4 → 4.20.3.0
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 +30 -0
- data/README.md +13 -7
- data/VERSION +1 -1
- data/docs/reference/GLIMMER_GUI_DSL_SYNTAX.md +5 -2
- data/docs/reference/GLIMMER_SAMPLES.md +13 -0
- data/docs/reference/GLIMMER_STYLE_GUIDE.md +4 -3
- data/glimmer-dsl-swt.gemspec +0 -0
- data/lib/glimmer/data_binding/shine.rb +2 -1
- data/lib/glimmer/data_binding/table_items_binding.rb +2 -2
- data/lib/glimmer/data_binding/tree_items_binding.rb +2 -2
- data/lib/glimmer/dsl/swt/custom_widget_expression.rb +1 -1
- data/lib/glimmer/dsl/swt/radio_group_selection_data_binding_expression.rb +1 -1
- data/lib/glimmer/dsl/swt/shine_data_binding_expression.rb +6 -5
- data/lib/glimmer/dsl/swt/table_items_data_binding_expression.rb +2 -2
- data/lib/glimmer/dsl/swt/tree_items_data_binding_expression.rb +17 -12
- data/lib/glimmer/dsl/swt/widget_listener_expression.rb +3 -3
- data/lib/glimmer/rake_task/scaffold.rb +2 -2
- data/lib/glimmer/swt/custom/animation.rb +6 -0
- data/lib/glimmer/swt/custom/checkbox_group.rb +1 -1
- data/lib/glimmer/swt/custom/radio_group.rb +2 -1
- data/lib/glimmer/swt/display_proxy.rb +11 -8
- data/lib/glimmer/swt/proxy_properties.rb +7 -6
- data/lib/glimmer/swt/table_proxy.rb +15 -8
- data/lib/glimmer/swt/widget_proxy.rb +5 -2
- data/lib/glimmer/ui/custom_shape.rb +1 -1
- data/lib/glimmer/ui/custom_shell.rb +3 -3
- data/lib/glimmer/ui/custom_widget.rb +6 -3
- data/samples/elaborate/calculator.rb +116 -0
- data/samples/elaborate/calculator/model/command.rb +105 -0
- data/samples/elaborate/calculator/model/command/all_clear.rb +17 -0
- data/samples/elaborate/calculator/model/command/command_history.rb +0 -0
- data/samples/elaborate/calculator/model/command/equals.rb +18 -0
- data/samples/elaborate/calculator/model/command/number.rb +20 -0
- data/samples/elaborate/calculator/model/command/operation.rb +27 -0
- data/samples/elaborate/calculator/model/command/operation/add.rb +15 -0
- data/samples/elaborate/calculator/model/command/operation/divide.rb +15 -0
- data/samples/elaborate/calculator/model/command/operation/multiply.rb +15 -0
- data/samples/elaborate/calculator/model/command/operation/subtract.rb +15 -0
- data/samples/elaborate/calculator/model/command/point.rb +20 -0
- data/samples/elaborate/calculator/model/presenter.rb +30 -0
- data/samples/elaborate/contact_manager.rb +2 -2
- data/samples/elaborate/meta_sample.rb +5 -5
- data/samples/elaborate/tetris.rb +6 -6
- data/samples/elaborate/tetris/view/bevel.rb +11 -11
- data/samples/elaborate/tetris/view/block.rb +1 -1
- data/samples/elaborate/tetris/view/high_score_dialog.rb +4 -4
- data/samples/elaborate/tetris/view/score_lane.rb +3 -3
- data/samples/elaborate/tetris/view/tetris_menu_bar.rb +9 -9
- data/samples/elaborate/timer.rb +233 -0
- data/samples/elaborate/timer/alarm1.wav +0 -0
- data/samples/elaborate/timer/sounds/alarm1.wav +0 -0
- data/samples/elaborate/user_profile.rb +4 -2
- data/samples/elaborate/weather.rb +1 -1
- data/samples/hello/hello_canvas_animation_data_binding.rb +1 -1
- data/samples/hello/hello_checkbox_group.rb +1 -1
- data/samples/hello/hello_code_text.rb +3 -3
- data/samples/hello/hello_cool_bar.rb +5 -5
- data/samples/hello/hello_cursor.rb +1 -1
- data/samples/hello/hello_custom_shell.rb +1 -1
- data/samples/hello/hello_directory_dialog.rb +1 -1
- data/samples/hello/hello_radio_group.rb +2 -2
- data/samples/hello/hello_table.rb +4 -4
- data/samples/hello/hello_text.rb +120 -0
- data/samples/hello/hello_tool_bar.rb +5 -5
- data/samples/hello/hello_tree.rb +11 -11
- metadata +19 -2
@@ -33,6 +33,7 @@ module Glimmer
|
|
33
33
|
# default implementation of attribute setters/getters
|
34
34
|
# It tries swt_widget, swt_display, swt_image, and swt_dialog by default.
|
35
35
|
def proxy_source_object
|
36
|
+
# TODO the logic here should not be needed if derived with polymorphism. Consider removing.
|
36
37
|
if respond_to?(:swt_widget)
|
37
38
|
swt_widget
|
38
39
|
elsif respond_to?(:swt_display)
|
@@ -62,7 +63,7 @@ module Glimmer
|
|
62
63
|
|
63
64
|
def has_attribute?(attribute_name, *args)
|
64
65
|
Glimmer::SWT::DisplayProxy.instance.auto_exec do
|
65
|
-
proxy_source_object&.respond_to?(attribute_setter(attribute_name), args)
|
66
|
+
proxy_source_object&.respond_to?(attribute_setter(attribute_name), args) or
|
66
67
|
respond_to?(ruby_attribute_setter(attribute_name), args)
|
67
68
|
end
|
68
69
|
end
|
@@ -80,7 +81,7 @@ module Glimmer
|
|
80
81
|
end
|
81
82
|
end
|
82
83
|
unless swt_widget_operation
|
83
|
-
result = send(ruby_attribute_setter(attribute_name), args)
|
84
|
+
result = send(ruby_attribute_setter(attribute_name), args) if respond_to?(ruby_attribute_setter(attribute_name), args)
|
84
85
|
end
|
85
86
|
result
|
86
87
|
end
|
@@ -101,10 +102,10 @@ module Glimmer
|
|
101
102
|
end
|
102
103
|
end
|
103
104
|
unless swt_widget_operation
|
104
|
-
|
105
|
-
send(ruby_attribute_getter(attribute_name))
|
106
|
-
|
107
|
-
send(attribute_name)
|
105
|
+
if respond_to?(ruby_attribute_getter(attribute_name))
|
106
|
+
result = send(ruby_attribute_getter(attribute_name))
|
107
|
+
elsif respond_to?(attribute_name)
|
108
|
+
result = send(attribute_name)
|
108
109
|
end
|
109
110
|
end
|
110
111
|
result
|
@@ -250,19 +250,13 @@ module Glimmer
|
|
250
250
|
alias editable? editable
|
251
251
|
|
252
252
|
def initialize(underscored_widget_name, parent, args)
|
253
|
-
|
253
|
+
editable_style = args.delete(:editable)
|
254
254
|
super
|
255
255
|
@table_editor = TableEditor.new(swt_widget)
|
256
256
|
@table_editor.horizontalAlignment = SWTProxy[:left]
|
257
257
|
@table_editor.grabHorizontal = true
|
258
258
|
@table_editor.minimumHeight = 20
|
259
|
-
|
260
|
-
content {
|
261
|
-
on_mouse_up { |event|
|
262
|
-
edit_table_item(event.table_item, event.column_index)
|
263
|
-
}
|
264
|
-
}
|
265
|
-
end
|
259
|
+
self.editable = editable_style
|
266
260
|
end
|
267
261
|
|
268
262
|
def items
|
@@ -283,6 +277,19 @@ module Glimmer
|
|
283
277
|
end
|
284
278
|
end
|
285
279
|
|
280
|
+
def editable=(value)
|
281
|
+
@editable = value
|
282
|
+
if @editable
|
283
|
+
content {
|
284
|
+
@editable_on_mouse_up = on_mouse_up { |event|
|
285
|
+
edit_table_item(event.table_item, event.column_index)
|
286
|
+
}
|
287
|
+
}
|
288
|
+
else
|
289
|
+
@editable_on_mouse_up.deregister if @editable_on_mouse_up
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
286
293
|
def sort_block=(comparator)
|
287
294
|
@sort_block = comparator
|
288
295
|
end
|
@@ -232,9 +232,12 @@ module Glimmer
|
|
232
232
|
def has_attribute?(attribute_name, *args)
|
233
233
|
# TODO test that attribute getter responds too
|
234
234
|
widget_custom_attribute = widget_custom_attribute_mapping[attribute_name.to_s]
|
235
|
+
property_type_converter = property_type_converters[attribute_name.to_s.to_sym]
|
235
236
|
auto_exec do
|
236
237
|
if widget_custom_attribute
|
237
238
|
@swt_widget.respond_to?(widget_custom_attribute[:setter][:name])
|
239
|
+
elsif property_type_converter
|
240
|
+
true
|
238
241
|
else
|
239
242
|
super
|
240
243
|
end
|
@@ -765,7 +768,7 @@ module Glimmer
|
|
765
768
|
|
766
769
|
def can_add_listener?(underscored_listener_name)
|
767
770
|
auto_exec do
|
768
|
-
!self.class.find_listener(@swt_widget.getClass, underscored_listener_name).empty?
|
771
|
+
@swt_widget && !self.class.find_listener(@swt_widget.getClass, underscored_listener_name).empty?
|
769
772
|
end
|
770
773
|
end
|
771
774
|
|
@@ -838,7 +841,7 @@ module Glimmer
|
|
838
841
|
widget_listener_proxy = nil
|
839
842
|
safe_block = lambda { |*args| block.call(*args) unless @swt_widget.isDisposed }
|
840
843
|
@swt_widget.addListener(event_type, &safe_block)
|
841
|
-
|
844
|
+
WidgetListenerProxy.new(swt_widget: @swt_widget, swt_listener: @swt_widget.getListeners(event_type).last, event_type: event_type, swt_constant: swt_constant)
|
842
845
|
end
|
843
846
|
end
|
844
847
|
|
@@ -248,7 +248,7 @@ module Glimmer
|
|
248
248
|
# Otherwise, if a block is passed, it adds it as content to this custom shape
|
249
249
|
def content(&block)
|
250
250
|
if block_given?
|
251
|
-
|
251
|
+
Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::SWT::CustomShapeExpression.new, self.class.keyword, &block)
|
252
252
|
else
|
253
253
|
@content
|
254
254
|
end
|
@@ -31,9 +31,9 @@ module Glimmer
|
|
31
31
|
class << self
|
32
32
|
def launch(*args, &content)
|
33
33
|
auto_exec do
|
34
|
-
|
35
|
-
|
36
|
-
|
34
|
+
launched_custom_shell = send(keyword, *args, &content)
|
35
|
+
launched_custom_shell.swt_widget.set_data('launched', true)
|
36
|
+
launched_custom_shell.open
|
37
37
|
end
|
38
38
|
end
|
39
39
|
end
|
@@ -186,8 +186,11 @@ module Glimmer
|
|
186
186
|
@swt_widget.set_data('custom_widget', self)
|
187
187
|
end
|
188
188
|
execute_hook('after_body')
|
189
|
-
|
190
|
-
|
189
|
+
auto_exec do
|
190
|
+
@dispose_listener_registration = @body_root.on_widget_disposed do
|
191
|
+
observer_registrations.compact.each(&:deregister)
|
192
|
+
observer_registrations.clear
|
193
|
+
end
|
191
194
|
end
|
192
195
|
end
|
193
196
|
|
@@ -293,7 +296,7 @@ module Glimmer
|
|
293
296
|
# Otherwise, if a block is passed, it adds it as content to this custom widget
|
294
297
|
def content(&block)
|
295
298
|
if block_given?
|
296
|
-
|
299
|
+
Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::SWT::CustomWidgetExpression.new, self.class.keyword, &block)
|
297
300
|
else
|
298
301
|
@content
|
299
302
|
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'glimmer-dsl-swt'
|
2
|
+
require 'bigdecimal'
|
3
|
+
|
4
|
+
require_relative 'calculator/model/presenter'
|
5
|
+
|
6
|
+
# This sample demonstrates use of MVP (Model-View-Presenter) Architectural Pattern
|
7
|
+
# to data-bind View widgets to object-oriented Models taking advantage of design patterns
|
8
|
+
# like Command Design Pattern.
|
9
|
+
class Calculator
|
10
|
+
include Glimmer::UI::CustomShell
|
11
|
+
|
12
|
+
BUTTON_FONT = {height: 14}
|
13
|
+
BUTTON_FONT_OPERATION = {height: 18}
|
14
|
+
BUTTON_FONT_BIG = {height: 28}
|
15
|
+
|
16
|
+
attr_reader :presenter
|
17
|
+
|
18
|
+
before_body {
|
19
|
+
@presenter = Model::Presenter.new
|
20
|
+
|
21
|
+
Display.setAppName('Glimmer Calculator')
|
22
|
+
|
23
|
+
display {
|
24
|
+
on_swt_keydown { |key_event|
|
25
|
+
char = key_event.character.chr rescue nil
|
26
|
+
@presenter.press(char)
|
27
|
+
}
|
28
|
+
|
29
|
+
on_about {
|
30
|
+
display_about_dialog
|
31
|
+
}
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
body {
|
36
|
+
shell {
|
37
|
+
grid_layout 4, true
|
38
|
+
|
39
|
+
minimum_size (OS.mac? ? 320 : (OS.windows? ? 390 : 520)), 240
|
40
|
+
text "Glimmer Calculator"
|
41
|
+
|
42
|
+
on_shell_closed do
|
43
|
+
@presenter.purge_command_history
|
44
|
+
end
|
45
|
+
|
46
|
+
# Setting styled_text to multi in order for alignment options to activate
|
47
|
+
styled_text(:multi, :wrap, :border) {
|
48
|
+
text <= [@presenter, :result]
|
49
|
+
alignment swt(:right)
|
50
|
+
right_margin 5
|
51
|
+
font height: 40
|
52
|
+
layout_data(:fill, :fill, true, true) {
|
53
|
+
horizontal_span 4
|
54
|
+
}
|
55
|
+
editable false
|
56
|
+
caret nil
|
57
|
+
}
|
58
|
+
command_button('AC')
|
59
|
+
operation_button('÷')
|
60
|
+
operation_button('×')
|
61
|
+
operation_button('−')
|
62
|
+
(7..9).each { |number|
|
63
|
+
number_button(number)
|
64
|
+
}
|
65
|
+
operation_button('+', font: BUTTON_FONT_BIG, vertical_span: 2)
|
66
|
+
(4..6).each { |number|
|
67
|
+
number_button(number)
|
68
|
+
}
|
69
|
+
(1..3).each { |number|
|
70
|
+
number_button(number)
|
71
|
+
}
|
72
|
+
command_button('=', font: BUTTON_FONT_BIG, vertical_span: 2)
|
73
|
+
number_button(0, horizontal_span: 2)
|
74
|
+
operation_button('.')
|
75
|
+
}
|
76
|
+
}
|
77
|
+
|
78
|
+
def number_button(number, options = {})
|
79
|
+
command_button(number, options)
|
80
|
+
end
|
81
|
+
|
82
|
+
def operation_button(operation, options = {})
|
83
|
+
command_button(operation, options.merge(font: BUTTON_FONT_OPERATION))
|
84
|
+
end
|
85
|
+
|
86
|
+
def command_button(command, options = {})
|
87
|
+
command = command.to_s
|
88
|
+
options[:font] ||= BUTTON_FONT
|
89
|
+
options[:horizontal_span] ||= 1
|
90
|
+
options[:vertical_span] ||= 1
|
91
|
+
|
92
|
+
button { |proxy|
|
93
|
+
text command
|
94
|
+
font options[:font]
|
95
|
+
|
96
|
+
layout_data(:fill, :fill, true, true) {
|
97
|
+
horizontal_span options[:horizontal_span]
|
98
|
+
vertical_span options[:vertical_span]
|
99
|
+
}
|
100
|
+
|
101
|
+
on_widget_selected {
|
102
|
+
@presenter.press(command)
|
103
|
+
}
|
104
|
+
}
|
105
|
+
end
|
106
|
+
|
107
|
+
def display_about_dialog
|
108
|
+
message_box(body_root) {
|
109
|
+
text 'About'
|
110
|
+
message "Glimmer - Calculator\n\nCopyright (c) 2007-2021 Andy Maleh"
|
111
|
+
}.open
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
Calculator.launch
|
@@ -0,0 +1,105 @@
|
|
1
|
+
class Calculator
|
2
|
+
module Model
|
3
|
+
class Command
|
4
|
+
class << self
|
5
|
+
attr_accessor :number1, :number2, :operation
|
6
|
+
|
7
|
+
# Keyword string representing calculator command (e.g. '+' for Add command)
|
8
|
+
# Subclasses must call to define a single keyword
|
9
|
+
def keyword(keyword_text)
|
10
|
+
Command.keyword_to_command_class_mapping[keyword_text] = self
|
11
|
+
end
|
12
|
+
|
13
|
+
# Keyword string array representing calculator command (e.g. ('0'..'9').to_a)
|
14
|
+
# Subclasses must call to define multiple keywords
|
15
|
+
def keywords(*keyword_text_array)
|
16
|
+
keyword_text_array.flatten.each do |keyword_text|
|
17
|
+
keyword(keyword_text)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def keyword_to_command_class_mapping
|
22
|
+
@keyword_to_command_class_mapping ||= {}
|
23
|
+
end
|
24
|
+
|
25
|
+
def command_history
|
26
|
+
@command_history ||= []
|
27
|
+
end
|
28
|
+
|
29
|
+
def purge_command_history
|
30
|
+
Command.command_history.clear
|
31
|
+
self.number1 = nil
|
32
|
+
self.number2 = nil
|
33
|
+
self.operation = nil
|
34
|
+
end
|
35
|
+
|
36
|
+
def for(button)
|
37
|
+
command_class = keyword_to_command_class_mapping[button]
|
38
|
+
command_class&.new(button)&.tap do |command|
|
39
|
+
command.execute
|
40
|
+
command_history << command
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
attr_reader :button
|
46
|
+
attr_accessor :result
|
47
|
+
|
48
|
+
def initialize(button)
|
49
|
+
@button = button
|
50
|
+
end
|
51
|
+
|
52
|
+
def number1
|
53
|
+
Command.number1
|
54
|
+
end
|
55
|
+
|
56
|
+
def number1=(value)
|
57
|
+
Command.number1 = value.to_f
|
58
|
+
end
|
59
|
+
|
60
|
+
def number2
|
61
|
+
Command.number2
|
62
|
+
end
|
63
|
+
|
64
|
+
def number2=(value)
|
65
|
+
Command.number2 = value.to_f
|
66
|
+
end
|
67
|
+
|
68
|
+
def operation
|
69
|
+
Command.operation
|
70
|
+
end
|
71
|
+
|
72
|
+
def operation=(op)
|
73
|
+
Command.operation = op
|
74
|
+
end
|
75
|
+
|
76
|
+
def last_result
|
77
|
+
last_command&.result
|
78
|
+
end
|
79
|
+
|
80
|
+
def last_command
|
81
|
+
command_history.last
|
82
|
+
end
|
83
|
+
|
84
|
+
def command_history
|
85
|
+
Command.command_history
|
86
|
+
end
|
87
|
+
|
88
|
+
def execute
|
89
|
+
raise 'Not implemented! Please override in a subclass.'
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Dir[File.join(File.dirname(__FILE__), 'command', '**', '*.rb')].each {|f| require(f)} # disabled for Opal compatibility
|
96
|
+
|
97
|
+
require_relative 'command/all_clear'
|
98
|
+
require_relative 'command/equals'
|
99
|
+
require_relative 'command/number'
|
100
|
+
require_relative 'command/operation'
|
101
|
+
require_relative 'command/point'
|
102
|
+
require_relative 'command/operation/add'
|
103
|
+
require_relative 'command/operation/divide'
|
104
|
+
require_relative 'command/operation/multiply'
|
105
|
+
require_relative 'command/operation/subtract'
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class Calculator
|
2
|
+
module Model
|
3
|
+
class Command
|
4
|
+
class AllClear < Command
|
5
|
+
keywords 'AC', 'c', 'C', 8.chr, 27.chr, 127.chr
|
6
|
+
|
7
|
+
def execute
|
8
|
+
self.result = '0'
|
9
|
+
self.number1 = nil
|
10
|
+
self.number2 = nil
|
11
|
+
self.operation = nil
|
12
|
+
command_history.clear
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
File without changes
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class Calculator
|
2
|
+
module Model
|
3
|
+
class Command
|
4
|
+
class Equals < Command
|
5
|
+
keywords '=', "\r"
|
6
|
+
|
7
|
+
def execute
|
8
|
+
if number1 && number2 && operation
|
9
|
+
self.result = operation.calculate.to_s
|
10
|
+
self.number1 = self.result
|
11
|
+
else
|
12
|
+
self.result = last_result || '0'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Calculator
|
2
|
+
module Model
|
3
|
+
class Command
|
4
|
+
class Number < Command
|
5
|
+
keywords ('0'..'9').to_a
|
6
|
+
|
7
|
+
def execute
|
8
|
+
self.result = last_result.nil? || (!last_command.is_a?(Number) && !last_command.is_a?(Point)) ? button : last_result + button
|
9
|
+
if operation.nil? || last_command.is_a?(Equals)
|
10
|
+
self.number1 = self.result
|
11
|
+
self.number2 = nil
|
12
|
+
self.operation = nil
|
13
|
+
else
|
14
|
+
self.number2 = self.result
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|