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
@@ -0,0 +1,27 @@
|
|
1
|
+
class Calculator
|
2
|
+
module Model
|
3
|
+
class Command
|
4
|
+
class Operation < Command
|
5
|
+
def execute
|
6
|
+
if number1 && number2 && operation && !last_command.is_a?(Equals)
|
7
|
+
self.result = operation.calculate.to_s
|
8
|
+
self.number1 = self.result
|
9
|
+
else
|
10
|
+
self.result = last_result || '0'
|
11
|
+
self.operation = self
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def calculate
|
16
|
+
calculation_result = BigDecimal.new(number1.to_s).send(operation_method, BigDecimal.new(number2.to_s)).to_f
|
17
|
+
calculation_result.to_s.match(/\.0+$/) ? calculation_result.to_i : calculation_result
|
18
|
+
end
|
19
|
+
|
20
|
+
# Subclasses must implement to indicate operation method on number (e.g. :+ for addition)
|
21
|
+
def operation_method
|
22
|
+
raise 'Not implemented!'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Calculator
|
2
|
+
module Model
|
3
|
+
class Command
|
4
|
+
class Point < Command
|
5
|
+
keyword '.'
|
6
|
+
|
7
|
+
def execute
|
8
|
+
self.result = last_result.nil? || !last_command.is_a?(Number) ? '0.' : "#{last_result}."
|
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
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require_relative 'command'
|
2
|
+
|
3
|
+
class Calculator
|
4
|
+
module Model
|
5
|
+
class Presenter
|
6
|
+
FORMATTER = {
|
7
|
+
nil => '0',
|
8
|
+
'NaN' => 'Not a number'
|
9
|
+
}
|
10
|
+
|
11
|
+
attr_accessor :result
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
self.result = '0'
|
15
|
+
end
|
16
|
+
|
17
|
+
def press(button)
|
18
|
+
command = Command.for(button)
|
19
|
+
if command
|
20
|
+
new_result = command.result
|
21
|
+
self.result = FORMATTER[new_result] || new_result
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def purge_command_history
|
26
|
+
Command.purge_command_history
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -110,7 +110,7 @@ class ContactManager
|
|
110
110
|
}
|
111
111
|
}
|
112
112
|
|
113
|
-
table(:multi) { |table_proxy|
|
113
|
+
table(:editable, :multi) { |table_proxy|
|
114
114
|
layout_data {
|
115
115
|
horizontal_alignment :fill
|
116
116
|
vertical_alignment :fill
|
@@ -132,7 +132,7 @@ class ContactManager
|
|
132
132
|
width 200
|
133
133
|
}
|
134
134
|
|
135
|
-
items
|
135
|
+
items <=> [@contact_manager_presenter, :results, column_properties: [:first_name, :last_name, :email]]
|
136
136
|
|
137
137
|
on_mouse_up { |event|
|
138
138
|
table_proxy.edit_table_item(event.table_item, event.column_index)
|
@@ -226,7 +226,7 @@ class MetaSampleApplication
|
|
226
226
|
row_layout(:vertical) {
|
227
227
|
fill true
|
228
228
|
}
|
229
|
-
selection
|
229
|
+
selection <=> [sample_directory, :selected_sample_name]
|
230
230
|
font height: 20
|
231
231
|
}
|
232
232
|
}
|
@@ -242,7 +242,7 @@ class MetaSampleApplication
|
|
242
242
|
button {
|
243
243
|
text 'Launch'
|
244
244
|
font height: 25
|
245
|
-
enabled
|
245
|
+
enabled <= [SampleDirectory, 'selected_sample.launchable']
|
246
246
|
|
247
247
|
on_widget_selected {
|
248
248
|
begin
|
@@ -255,7 +255,7 @@ class MetaSampleApplication
|
|
255
255
|
button {
|
256
256
|
text 'Reset'
|
257
257
|
font height: 25
|
258
|
-
enabled
|
258
|
+
enabled <= [SampleDirectory, 'selected_sample.editable']
|
259
259
|
|
260
260
|
on_widget_selected {
|
261
261
|
SampleDirectory.selected_sample.reset_code!
|
@@ -277,8 +277,8 @@ class MetaSampleApplication
|
|
277
277
|
line_numbers {
|
278
278
|
background :white
|
279
279
|
}
|
280
|
-
text
|
281
|
-
editable
|
280
|
+
text <=> [SampleDirectory, 'selected_sample.code']
|
281
|
+
editable <= [SampleDirectory, 'selected_sample.editable']
|
282
282
|
left_margin 7
|
283
283
|
right_margin 7
|
284
284
|
}
|
data/samples/elaborate/tetris.rb
CHANGED
@@ -54,7 +54,7 @@ class Tetris
|
|
54
54
|
Display.app_name = 'Glimmer Tetris'
|
55
55
|
|
56
56
|
display {
|
57
|
-
|
57
|
+
on_swt_keydown { |key_event|
|
58
58
|
case key_event.keyCode
|
59
59
|
when swt(:arrow_down), 's'.bytes.first
|
60
60
|
game.down! if OS.mac?
|
@@ -82,7 +82,7 @@ class Tetris
|
|
82
82
|
|
83
83
|
# invoke game.down! on keyup with Windows/Linux since they seem to group-render similar events, preventing intermediate renders (causing invisiblity while holding keys)
|
84
84
|
if !OS.mac?
|
85
|
-
|
85
|
+
on_swt_keyup { |key_event|
|
86
86
|
case key_event.keyCode
|
87
87
|
when swt(:arrow_down), 's'.bytes.first
|
88
88
|
game.down!
|
@@ -91,25 +91,25 @@ class Tetris
|
|
91
91
|
end
|
92
92
|
|
93
93
|
# if running in app mode, set the Mac app about dialog (ignored in platforms)
|
94
|
-
|
94
|
+
on_about {
|
95
95
|
show_about_dialog
|
96
96
|
}
|
97
97
|
|
98
|
-
|
98
|
+
on_quit {
|
99
99
|
exit(0)
|
100
100
|
}
|
101
101
|
}
|
102
102
|
}
|
103
103
|
|
104
104
|
after_body {
|
105
|
-
|
105
|
+
observe(@game, :game_over) do |game_over|
|
106
106
|
if game_over
|
107
107
|
show_high_score_dialog
|
108
108
|
else
|
109
109
|
start_moving_tetrominos_down
|
110
110
|
end
|
111
111
|
end
|
112
|
-
|
112
|
+
observe(@game, :show_high_scores) do |show_high_scores|
|
113
113
|
if show_high_scores
|
114
114
|
show_high_score_dialog
|
115
115
|
else
|
@@ -35,44 +35,44 @@ class Tetris
|
|
35
35
|
|
36
36
|
body {
|
37
37
|
rectangle(x, y, size, size) {
|
38
|
-
background
|
38
|
+
background <= [self, :base_color]
|
39
39
|
polygon(0, 0, size, 0, size - bevel_pixel_size, bevel_pixel_size, bevel_pixel_size, bevel_pixel_size) {
|
40
|
-
background
|
40
|
+
background <= [self, :base_color, on_read: ->(color_value) {
|
41
41
|
unless color_value.nil?
|
42
42
|
color = color(color_value)
|
43
43
|
rgb(color.red + 4*BEVEL_CONSTANT, color.green + 4*BEVEL_CONSTANT, color.blue + 4*BEVEL_CONSTANT)
|
44
44
|
end
|
45
|
-
}
|
45
|
+
}]
|
46
46
|
}
|
47
47
|
polygon(size, 0, size - bevel_pixel_size, bevel_pixel_size, size - bevel_pixel_size, size - bevel_pixel_size, size, size) {
|
48
|
-
background
|
48
|
+
background <= [self, :base_color, on_read: ->(color_value) {
|
49
49
|
unless color_value.nil?
|
50
50
|
color = color(color_value)
|
51
51
|
rgb(color.red - BEVEL_CONSTANT, color.green - BEVEL_CONSTANT, color.blue - BEVEL_CONSTANT)
|
52
52
|
end
|
53
|
-
}
|
53
|
+
}]
|
54
54
|
}
|
55
55
|
polygon(size, size, 0, size, bevel_pixel_size, size - bevel_pixel_size, size - bevel_pixel_size, size - bevel_pixel_size) {
|
56
|
-
background
|
56
|
+
background <= [self, :base_color, on_read: ->(color_value) {
|
57
57
|
unless color_value.nil?
|
58
58
|
color = color(color_value)
|
59
59
|
rgb(color.red - 2*BEVEL_CONSTANT, color.green - 2*BEVEL_CONSTANT, color.blue - 2*BEVEL_CONSTANT)
|
60
60
|
end
|
61
|
-
}
|
61
|
+
}]
|
62
62
|
}
|
63
63
|
polygon(0, 0, 0, size, bevel_pixel_size, size - bevel_pixel_size, bevel_pixel_size, bevel_pixel_size) {
|
64
|
-
background
|
64
|
+
background <= [self, :base_color, on_read: ->(color_value) {
|
65
65
|
unless color_value.nil?
|
66
66
|
color = color(color_value)
|
67
67
|
rgb(color.red - BEVEL_CONSTANT, color.green - BEVEL_CONSTANT, color.blue - BEVEL_CONSTANT)
|
68
68
|
end
|
69
|
-
}
|
69
|
+
}]
|
70
70
|
}
|
71
71
|
rectangle(0, 0, size, size) {
|
72
|
-
foreground
|
72
|
+
foreground <= [self, :base_color, on_read: ->(color_value) {
|
73
73
|
# use gray instead of white for the border
|
74
74
|
color_value == Model::Block::COLOR_CLEAR ? :gray : color_value
|
75
|
-
}
|
75
|
+
}]
|
76
76
|
}
|
77
77
|
}
|
78
78
|
}
|
@@ -40,12 +40,12 @@ class Tetris
|
|
40
40
|
type :vertical
|
41
41
|
center true
|
42
42
|
}
|
43
|
-
text 'Tetris'
|
43
|
+
text 'Glimmer Tetris'
|
44
44
|
|
45
45
|
tetris_menu_bar(game: game)
|
46
46
|
|
47
47
|
label(:center) {
|
48
|
-
text
|
48
|
+
text <= [game, :game_over, on_read: ->(game_over) { game_over ? 'Game Over!' : 'High Scores' }]
|
49
49
|
font name: FONT_NAME, height: FONT_TITLE_HEIGHT, style: FONT_TITLE_STYLE
|
50
50
|
}
|
51
51
|
@high_score_table = table {
|
@@ -66,13 +66,13 @@ class Tetris
|
|
66
66
|
text 'Level'
|
67
67
|
}
|
68
68
|
|
69
|
-
items
|
69
|
+
items <=> [game, :high_scores, read_only_sort: true, column_properties: [:name, :score, :lines, :level]]
|
70
70
|
}
|
71
71
|
composite {
|
72
72
|
row_layout :horizontal
|
73
73
|
|
74
74
|
@play_close_button = button {
|
75
|
-
text
|
75
|
+
text <= [game, :game_over, on_read: ->(game_over) { game_over ? 'Play Again?' : 'Close'}]
|
76
76
|
focus true # initial focus
|
77
77
|
|
78
78
|
on_widget_selected {
|
@@ -55,7 +55,7 @@ class Tetris
|
|
55
55
|
font name: @font_name, height: @font_height, style: FONT_TITLE_STYLE
|
56
56
|
}
|
57
57
|
label(:center) {
|
58
|
-
text
|
58
|
+
text <= [game, :score]
|
59
59
|
font height: @font_height
|
60
60
|
}
|
61
61
|
|
@@ -66,7 +66,7 @@ class Tetris
|
|
66
66
|
font name: @font_name, height: @font_height, style: FONT_TITLE_STYLE
|
67
67
|
}
|
68
68
|
label(:center) {
|
69
|
-
text
|
69
|
+
text <= [game, :lines]
|
70
70
|
font height: @font_height
|
71
71
|
}
|
72
72
|
|
@@ -77,7 +77,7 @@ class Tetris
|
|
77
77
|
font name: @font_name, height: @font_height, style: FONT_TITLE_STYLE
|
78
78
|
}
|
79
79
|
label(:center) {
|
80
|
-
text
|
80
|
+
text <= [game, :level]
|
81
81
|
font height: @font_height
|
82
82
|
}
|
83
83
|
}
|
@@ -35,7 +35,7 @@ class Tetris
|
|
35
35
|
|
36
36
|
menu_item {
|
37
37
|
text '&Start'
|
38
|
-
enabled
|
38
|
+
enabled <= [game, :game_over]
|
39
39
|
accelerator COMMAND_KEY, :s
|
40
40
|
|
41
41
|
on_widget_selected {
|
@@ -45,9 +45,9 @@ class Tetris
|
|
45
45
|
menu_item(:check) {
|
46
46
|
text '&Pause'
|
47
47
|
accelerator COMMAND_KEY, :p
|
48
|
-
enabled
|
49
|
-
enabled
|
50
|
-
selection
|
48
|
+
enabled <= [game, :game_over, on_read: ->(value) { value && !game.show_high_scores }]
|
49
|
+
enabled <= [game, :show_high_scores, on_read: ->(value) { value && !game.game_over }]
|
50
|
+
selection <=> [game, :paused]
|
51
51
|
}
|
52
52
|
menu_item {
|
53
53
|
text '&Restart'
|
@@ -76,7 +76,7 @@ class Tetris
|
|
76
76
|
menu_item(:check) {
|
77
77
|
text '&Show'
|
78
78
|
accelerator COMMAND_KEY, :shift, :h
|
79
|
-
selection
|
79
|
+
selection <=> [game, :show_high_scores]
|
80
80
|
}
|
81
81
|
menu_item {
|
82
82
|
text '&Clear'
|
@@ -94,24 +94,24 @@ class Tetris
|
|
94
94
|
menu_item(:check) {
|
95
95
|
text '&Beeping'
|
96
96
|
accelerator COMMAND_KEY, :b
|
97
|
-
selection
|
97
|
+
selection <=> [game, :beeping]
|
98
98
|
}
|
99
99
|
menu {
|
100
100
|
text 'Up Arrow'
|
101
101
|
menu_item(:radio) {
|
102
102
|
text '&Instant Down'
|
103
103
|
accelerator COMMAND_KEY, :shift, :i
|
104
|
-
selection
|
104
|
+
selection <=> [game, :instant_down_on_up, computed_by: :up_arrow_action]
|
105
105
|
}
|
106
106
|
menu_item(:radio) {
|
107
107
|
text 'Rotate &Right'
|
108
108
|
accelerator COMMAND_KEY, :shift, :r
|
109
|
-
selection
|
109
|
+
selection <=> [game, :rotate_right_on_up, computed_by: :up_arrow_action]
|
110
110
|
}
|
111
111
|
menu_item(:radio) {
|
112
112
|
text 'Rotate &Left'
|
113
113
|
accelerator COMMAND_KEY, :shift, :l
|
114
|
-
selection
|
114
|
+
selection <=> [game, :rotate_left_on_up, computed_by: :up_arrow_action]
|
115
115
|
}
|
116
116
|
}
|
117
117
|
} # end of menu
|
@@ -0,0 +1,233 @@
|
|
1
|
+
require 'glimmer-dsl-swt'
|
2
|
+
require 'os'
|
3
|
+
|
4
|
+
class Timer
|
5
|
+
include Glimmer::UI::CustomShell
|
6
|
+
|
7
|
+
import 'javax.sound.sampled'
|
8
|
+
|
9
|
+
FILE_SOUND_ALARM = File.expand_path(File.join('timer', 'alarm1.wav'), __dir__)
|
10
|
+
COMMAND_KEY = OS.mac? ? :command : :ctrl
|
11
|
+
|
12
|
+
## Add options like the following to configure CustomShell by outside consumers
|
13
|
+
#
|
14
|
+
# options :title, :background_color
|
15
|
+
# option :width, default: 320
|
16
|
+
# option :height, default: 240
|
17
|
+
|
18
|
+
attr_accessor :countdown, :min, :sec
|
19
|
+
|
20
|
+
## Use before_body block to pre-initialize variables to use in body
|
21
|
+
#
|
22
|
+
#
|
23
|
+
before_body {
|
24
|
+
Display.setAppName('Glimmer Timer')
|
25
|
+
|
26
|
+
@display = display {
|
27
|
+
on_about {
|
28
|
+
display_about_dialog
|
29
|
+
}
|
30
|
+
on_preferences {
|
31
|
+
display_about_dialog
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
@min = 0
|
36
|
+
@sec = 0
|
37
|
+
}
|
38
|
+
|
39
|
+
## Use after_body block to setup observers for widgets in body
|
40
|
+
#
|
41
|
+
after_body {
|
42
|
+
Thread.new {
|
43
|
+
loop {
|
44
|
+
sleep(1)
|
45
|
+
if @countdown
|
46
|
+
sync_exec {
|
47
|
+
@countdown_time = Time.new(1, 1, 1, 0, min, sec)
|
48
|
+
@countdown_time -= 1
|
49
|
+
self.min = @countdown_time.min
|
50
|
+
self.sec = @countdown_time.sec
|
51
|
+
if @countdown_time.min <= 0 && @countdown_time.sec <= 0
|
52
|
+
stop_countdown
|
53
|
+
play_countdown_done_sound
|
54
|
+
end
|
55
|
+
}
|
56
|
+
end
|
57
|
+
}
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
61
|
+
## Add widget content inside custom shell body
|
62
|
+
## Top-most widget must be a shell or another custom shell
|
63
|
+
#
|
64
|
+
body {
|
65
|
+
shell {
|
66
|
+
grid_layout # 1 column
|
67
|
+
|
68
|
+
# Replace example content below with custom shell content
|
69
|
+
minimum_size (OS.windows? ? 214 : 200), 114
|
70
|
+
image File.join(APP_ROOT, 'package', 'windows', "Timer.ico") if OS.windows?
|
71
|
+
text "Glimmer Timer"
|
72
|
+
|
73
|
+
timer_menu_bar
|
74
|
+
|
75
|
+
countdown_group
|
76
|
+
}
|
77
|
+
}
|
78
|
+
|
79
|
+
def timer_menu_bar
|
80
|
+
menu_bar {
|
81
|
+
menu {
|
82
|
+
text '&Action'
|
83
|
+
|
84
|
+
menu_item {
|
85
|
+
text '&Start'
|
86
|
+
accelerator COMMAND_KEY, 's'
|
87
|
+
enabled <= [self, :countdown, on_read: :!]
|
88
|
+
|
89
|
+
on_widget_selected {
|
90
|
+
start_countdown
|
91
|
+
}
|
92
|
+
}
|
93
|
+
menu_item {
|
94
|
+
text 'St&op'
|
95
|
+
enabled <= [self, :countdown]
|
96
|
+
accelerator COMMAND_KEY, 'o'
|
97
|
+
|
98
|
+
on_widget_selected {
|
99
|
+
stop_countdown
|
100
|
+
}
|
101
|
+
}
|
102
|
+
unless OS.mac?
|
103
|
+
menu_item(:separator)
|
104
|
+
menu_item {
|
105
|
+
text 'E&xit'
|
106
|
+
accelerator :alt, :f4
|
107
|
+
|
108
|
+
on_widget_selected {
|
109
|
+
exit(0)
|
110
|
+
}
|
111
|
+
}
|
112
|
+
end
|
113
|
+
}
|
114
|
+
menu {
|
115
|
+
text '&Help'
|
116
|
+
|
117
|
+
menu_item {
|
118
|
+
text '&About'
|
119
|
+
accelerator COMMAND_KEY, :shift, 'a'
|
120
|
+
|
121
|
+
on_widget_selected {
|
122
|
+
display_about_dialog
|
123
|
+
}
|
124
|
+
}
|
125
|
+
}
|
126
|
+
}
|
127
|
+
end
|
128
|
+
|
129
|
+
def countdown_group
|
130
|
+
group {
|
131
|
+
# has grid layout with 1 column by default
|
132
|
+
text 'Countdown'
|
133
|
+
font height: 20
|
134
|
+
|
135
|
+
countdown_group_field_composite
|
136
|
+
|
137
|
+
countdown_group_button_composite
|
138
|
+
}
|
139
|
+
end
|
140
|
+
|
141
|
+
def countdown_group_field_composite
|
142
|
+
composite {
|
143
|
+
row_layout {
|
144
|
+
margin_width 0
|
145
|
+
margin_height 0
|
146
|
+
}
|
147
|
+
@min_spinner = spinner {
|
148
|
+
text_limit 2
|
149
|
+
digits 0
|
150
|
+
maximum 59
|
151
|
+
selection <=> [self, :min]
|
152
|
+
enabled <= [self, :countdown, on_read: :!]
|
153
|
+
on_widget_default_selected {
|
154
|
+
start_countdown
|
155
|
+
}
|
156
|
+
}
|
157
|
+
label {
|
158
|
+
text ':'
|
159
|
+
font(height: 18) if OS.mac?
|
160
|
+
}
|
161
|
+
@sec_spinner = spinner {
|
162
|
+
text_limit 2
|
163
|
+
digits 0
|
164
|
+
maximum 59
|
165
|
+
selection <=> [self, :sec]
|
166
|
+
enabled <= [self, :countdown, on_read: :!]
|
167
|
+
on_widget_default_selected {
|
168
|
+
start_countdown
|
169
|
+
}
|
170
|
+
}
|
171
|
+
}
|
172
|
+
end
|
173
|
+
|
174
|
+
def countdown_group_button_composite
|
175
|
+
composite {
|
176
|
+
row_layout {
|
177
|
+
margin_width 0
|
178
|
+
margin_height 0
|
179
|
+
}
|
180
|
+
@start_button = button {
|
181
|
+
text '&Start'
|
182
|
+
enabled <= [self, :countdown, on_read: :!]
|
183
|
+
on_widget_selected {
|
184
|
+
start_countdown
|
185
|
+
}
|
186
|
+
on_key_pressed { |event|
|
187
|
+
start_countdown if event.keyCode == swt(:cr)
|
188
|
+
}
|
189
|
+
}
|
190
|
+
@stop_button = button {
|
191
|
+
text 'St&op'
|
192
|
+
enabled <= [self, :countdown]
|
193
|
+
on_widget_selected {
|
194
|
+
stop_countdown
|
195
|
+
}
|
196
|
+
on_key_pressed { |event|
|
197
|
+
stop_countdown if event.keyCode == swt(:cr)
|
198
|
+
}
|
199
|
+
}
|
200
|
+
}
|
201
|
+
end
|
202
|
+
|
203
|
+
def display_about_dialog
|
204
|
+
message_box(body_root) {
|
205
|
+
text 'About'
|
206
|
+
message "Glimmer Timer\n\nCopyright (c) 2007-2021 Andy Maleh"
|
207
|
+
}.open
|
208
|
+
end
|
209
|
+
|
210
|
+
def start_countdown
|
211
|
+
self.countdown = true
|
212
|
+
@stop_button.swt_widget.set_focus
|
213
|
+
end
|
214
|
+
|
215
|
+
def stop_countdown
|
216
|
+
self.countdown = false
|
217
|
+
@min_spinner.swt_widget.set_focus
|
218
|
+
end
|
219
|
+
|
220
|
+
def play_countdown_done_sound
|
221
|
+
begin
|
222
|
+
file_or_stream = java.io.File.new(FILE_SOUND_ALARM)
|
223
|
+
audio_stream = AudioSystem.get_audio_input_stream(file_or_stream)
|
224
|
+
clip = AudioSystem.clip
|
225
|
+
clip.open(audio_stream)
|
226
|
+
clip.start
|
227
|
+
rescue => e
|
228
|
+
Glimmer::Config.logger.error e.full_message
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
Timer.launch
|