glimmer-dsl-swt 4.20.0.2 → 4.20.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +30 -0
  3. data/README.md +6 -6
  4. data/VERSION +1 -1
  5. data/docs/reference/GLIMMER_SAMPLES.md +128 -49
  6. data/docs/reference/GLIMMER_STYLE_GUIDE.md +4 -3
  7. data/glimmer-dsl-swt.gemspec +0 -0
  8. data/lib/glimmer/data_binding/shine.rb +3 -1
  9. data/lib/glimmer/data_binding/table_items_binding.rb +2 -2
  10. data/lib/glimmer/data_binding/tree_items_binding.rb +2 -2
  11. data/lib/glimmer/dsl/swt/shine_data_binding_expression.rb +6 -3
  12. data/lib/glimmer/dsl/swt/table_items_data_binding_expression.rb +2 -2
  13. data/lib/glimmer/dsl/swt/tree_items_data_binding_expression.rb +17 -12
  14. data/lib/glimmer/dsl/swt/widget_listener_expression.rb +3 -3
  15. data/lib/glimmer/swt/display_proxy.rb +11 -8
  16. data/lib/glimmer/swt/proxy_properties.rb +2 -1
  17. data/lib/glimmer/swt/table_proxy.rb +15 -8
  18. data/lib/glimmer/swt/widget_proxy.rb +2 -2
  19. data/lib/glimmer/ui/custom_shell.rb +3 -3
  20. data/lib/glimmer/ui/custom_widget.rb +5 -2
  21. data/samples/elaborate/calculator.rb +116 -0
  22. data/samples/elaborate/calculator/model/command.rb +105 -0
  23. data/samples/elaborate/calculator/model/command/all_clear.rb +17 -0
  24. data/samples/elaborate/calculator/model/command/command_history.rb +0 -0
  25. data/samples/elaborate/calculator/model/command/equals.rb +18 -0
  26. data/samples/elaborate/calculator/model/command/number.rb +20 -0
  27. data/samples/elaborate/calculator/model/command/operation.rb +27 -0
  28. data/samples/elaborate/calculator/model/command/operation/add.rb +15 -0
  29. data/samples/elaborate/calculator/model/command/operation/divide.rb +15 -0
  30. data/samples/elaborate/calculator/model/command/operation/multiply.rb +15 -0
  31. data/samples/elaborate/calculator/model/command/operation/subtract.rb +15 -0
  32. data/samples/elaborate/calculator/model/command/point.rb +20 -0
  33. data/samples/elaborate/calculator/model/presenter.rb +30 -0
  34. data/samples/elaborate/login.rb +15 -13
  35. data/samples/elaborate/tetris.rb +6 -6
  36. data/samples/elaborate/tetris/view/high_score_dialog.rb +1 -1
  37. data/samples/elaborate/timer.rb +233 -0
  38. data/samples/elaborate/timer/alarm1.wav +0 -0
  39. data/samples/elaborate/timer/sounds/alarm1.wav +0 -0
  40. data/samples/elaborate/user_profile.rb +4 -2
  41. data/samples/elaborate/weather.rb +164 -0
  42. data/samples/hello/hello_cool_bar.rb +4 -4
  43. data/samples/hello/hello_layout.rb +6 -2
  44. data/samples/hello/hello_shell.rb +205 -0
  45. data/samples/hello/hello_table.rb +5 -5
  46. data/samples/hello/hello_text.rb +120 -0
  47. data/samples/hello/hello_tool_bar.rb +4 -4
  48. data/samples/hello/hello_tree.rb +11 -11
  49. metadata +21 -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,15 @@
1
+ class Calculator
2
+ module Model
3
+ class Command
4
+ class Operation < Command
5
+ class Add < Operation
6
+ keyword '+'
7
+
8
+ def operation_method
9
+ :+
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ class Calculator
2
+ module Model
3
+ class Command
4
+ class Operation < Command
5
+ class Divide < Operation
6
+ keywords '÷', '/'
7
+
8
+ def operation_method
9
+ :/
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ class Calculator
2
+ module Model
3
+ class Command
4
+ class Operation < Command
5
+ class Multiply < Operation
6
+ keywords '×', '*'
7
+
8
+ def operation_method
9
+ :*
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ class Calculator
2
+ module Model
3
+ class Command
4
+ class Operation < Command
5
+ class Subtract < Operation
6
+ keywords '−', '-'
7
+
8
+ def operation_method
9
+ :-
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ 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
@@ -35,24 +35,19 @@ class LoginPresenter
35
35
 
36
36
  def status=(status)
37
37
  @status = status
38
-
39
- notify_observers("logged_in")
40
- notify_observers("logged_out")
41
38
  end
42
39
 
43
40
  def valid?
44
41
  !@user_name.to_s.strip.empty? && !@password.to_s.strip.empty?
45
42
  end
46
43
 
47
- def logged_in
44
+ def logged_in?
48
45
  self.status == "Logged In"
49
46
  end
50
- alias logged_in? logged_in
51
47
 
52
- def logged_out
53
- !self.logged_in
48
+ def logged_out?
49
+ !self.logged_in?
54
50
  end
55
- alias logged_out? logged_out
56
51
 
57
52
  def login!
58
53
  return unless valid?
@@ -77,13 +72,15 @@ class Login
77
72
  body {
78
73
  shell {
79
74
  text "Login"
75
+
80
76
  composite {
81
77
  grid_layout 2, false #two columns with differing widths
82
78
 
83
79
  label { text "Username:" } # goes in column 1
84
80
  @user_name_text = text { # goes in column 2
85
81
  text <=> [@presenter, :user_name]
86
- enabled <= [@presenter, :logged_out]
82
+ enabled <= [@presenter, :logged_out?, computed_by: :status]
83
+
87
84
  on_key_pressed { |event|
88
85
  @password_text.set_focus if event.keyCode == swt(:cr)
89
86
  }
@@ -92,7 +89,8 @@ class Login
92
89
  label { text "Password:" }
93
90
  @password_text = text(:password, :border) {
94
91
  text <=> [@presenter, :password]
95
- enabled <= [@presenter, :logged_out]
92
+ enabled <= [@presenter, :logged_out?, computed_by: :status]
93
+
96
94
  on_key_pressed { |event|
97
95
  @presenter.login! if event.keyCode == swt(:cr)
98
96
  }
@@ -103,16 +101,20 @@ class Login
103
101
 
104
102
  button {
105
103
  text "Login"
106
- enabled <= [@presenter, :logged_out]
104
+ enabled <= [@presenter, :logged_out?, computed_by: :status]
105
+
107
106
  on_widget_selected { @presenter.login! }
108
107
  on_key_pressed { |event|
109
- @presenter.login! if event.keyCode == swt(:cr)
108
+ if event.keyCode == swt(:cr)
109
+ @presenter.login!
110
+ end
110
111
  }
111
112
  }
112
113
 
113
114
  button {
114
115
  text "Logout"
115
- enabled <= [@presenter, :logged_in]
116
+ enabled <= [@presenter, :logged_in?, computed_by: :status]
117
+
116
118
  on_widget_selected { @presenter.logout! }
117
119
  on_key_pressed { |event|
118
120
  if event.keyCode == swt(:cr)
@@ -54,7 +54,7 @@ class Tetris
54
54
  Display.app_name = 'Glimmer Tetris'
55
55
 
56
56
  display {
57
- @keyboard_down_listener = on_swt_keydown { |key_event|
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
- @keyboard_up_listener = on_swt_keyup { |key_event|
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
- @about_observer = on_about {
94
+ on_about {
95
95
  show_about_dialog
96
96
  }
97
97
 
98
- @quit_observer = on_quit {
98
+ on_quit {
99
99
  exit(0)
100
100
  }
101
101
  }
102
102
  }
103
103
 
104
104
  after_body {
105
- @game_over_observer = observe(@game, :game_over) do |game_over|
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
- @show_high_scores_observer = observe(@game, :show_high_scores) do |show_high_scores|
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
@@ -40,7 +40,7 @@ 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
 
@@ -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 bind(self, :countdown, on_read: :!)
88
+
89
+ on_widget_selected {
90
+ start_countdown
91
+ }
92
+ }
93
+ menu_item {
94
+ text 'St&op'
95
+ enabled bind(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 bind(self, :min)
152
+ enabled bind(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 bind(self, :sec)
166
+ enabled bind(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 bind(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 bind(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