glimmer-dsl-opal 0.10.2 → 0.14.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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +252 -0
  3. data/README.md +187 -1301
  4. data/VERSION +1 -1
  5. data/lib/display.rb +3 -0
  6. data/lib/glimmer-dsl-opal.rb +2 -1
  7. data/lib/glimmer-dsl-opal/ext/glimmer/dsl/engine.rb +1 -1
  8. data/lib/glimmer-dsl-opal/samples/elaborate/contact_manager.rb +15 -13
  9. data/lib/glimmer-dsl-opal/samples/elaborate/login.rb +55 -28
  10. data/lib/glimmer-dsl-opal/samples/elaborate/tic_tac_toe.rb +2 -2
  11. data/lib/glimmer-dsl-opal/samples/elaborate/weather.rb +157 -0
  12. data/lib/glimmer-dsl-opal/samples/hello/hello_button.rb +8 -8
  13. data/lib/glimmer-dsl-opal/samples/hello/hello_checkbox.rb +16 -14
  14. data/lib/glimmer-dsl-opal/samples/hello/hello_checkbox_group.rb +14 -9
  15. data/lib/glimmer-dsl-opal/samples/hello/hello_combo.rb +24 -22
  16. data/lib/glimmer-dsl-opal/samples/hello/hello_computed.rb +32 -14
  17. data/lib/glimmer-dsl-opal/samples/hello/hello_custom_shell.rb +16 -12
  18. data/lib/glimmer-dsl-opal/samples/hello/hello_custom_widget.rb +1 -1
  19. data/lib/glimmer-dsl-opal/samples/hello/hello_date_time.rb +4 -4
  20. data/lib/glimmer-dsl-opal/samples/hello/hello_group.rb +6 -6
  21. data/lib/glimmer-dsl-opal/samples/hello/hello_list_multi_selection.rb +1 -1
  22. data/lib/glimmer-dsl-opal/samples/hello/hello_list_single_selection.rb +1 -1
  23. data/lib/glimmer-dsl-opal/samples/hello/hello_radio.rb +18 -16
  24. data/lib/glimmer-dsl-opal/samples/hello/hello_radio_group.rb +17 -12
  25. data/lib/glimmer-dsl-opal/samples/hello/hello_table.rb +4 -4
  26. data/lib/glimmer/data_binding/table_items_binding.rb +3 -2
  27. data/lib/glimmer/dsl/opal/bind_expression.rb +24 -25
  28. data/lib/glimmer/dsl/opal/custom_widget_expression.rb +8 -8
  29. data/lib/glimmer/dsl/opal/dsl.rb +4 -0
  30. data/lib/glimmer/dsl/opal/menu_expression.rb +1 -1
  31. data/lib/glimmer/dsl/opal/property_expression.rb +2 -1
  32. data/lib/glimmer/dsl/opal/shape_expression.rb +26 -0
  33. data/lib/glimmer/dsl/opal/shell_expression.rb +1 -1
  34. data/lib/{glimmer-dsl-opal/samples/hello/hello_computed/contact.rb → glimmer/dsl/opal/shine_data_binding_expression.rb} +24 -17
  35. data/lib/glimmer/dsl/opal/table_items_data_binding_expression.rb +2 -2
  36. data/lib/glimmer/dsl/opal/widget_expression.rb +1 -1
  37. data/lib/glimmer/swt/combo_proxy.rb +1 -0
  38. data/lib/glimmer/swt/composite_proxy.rb +2 -0
  39. data/lib/glimmer/swt/dialog_proxy.rb +1 -1
  40. data/lib/glimmer/swt/display_proxy.rb +63 -1
  41. data/lib/glimmer/swt/grid_layout_proxy.rb +17 -17
  42. data/lib/glimmer/swt/layout_proxy.rb +23 -3
  43. data/lib/glimmer/swt/message_box_proxy.rb +1 -1
  44. data/lib/glimmer/swt/row_layout_proxy.rb +12 -3
  45. data/lib/glimmer/swt/table_proxy.rb +19 -3
  46. data/lib/glimmer/swt/widget_proxy.rb +3 -4
  47. data/lib/glimmer/ui/custom_shell.rb +22 -5
  48. data/lib/glimmer/ui/custom_widget.rb +11 -2
  49. data/lib/net/http.rb +14 -6
  50. metadata +14 -10
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.10.2
1
+ 0.14.0
data/lib/display.rb CHANGED
@@ -24,8 +24,11 @@ class Display
24
24
  def setAppName(app_name)
25
25
  # No Op in Opal
26
26
  end
27
+ alias app_name= setAppName
28
+
27
29
  def setAppVersion(version)
28
30
  # No Op in Opal
29
31
  end
32
+ alias app_version= setAppVersion
30
33
  end
31
34
  end
@@ -56,6 +56,7 @@ if RUBY_ENGINE == 'opal'
56
56
  # require 'glimmer-dsl-opal/vendor/jquery-ui/jquery-ui.theme.min.css'
57
57
  require 'opal-jquery'
58
58
  require 'opal/jquery/local_storage'
59
+ require 'promise'
59
60
 
60
61
  require 'facets/hash/symbolize_keys'
61
62
  require 'glimmer-dsl-opal/ext/class'
@@ -84,7 +85,7 @@ if RUBY_ENGINE == 'opal'
84
85
  Element.alias_native :select
85
86
  Element.alias_native :dialog
86
87
 
87
- Glimmer::Config.loop_max_count = 10 # TODO disable
88
+ Glimmer::Config.loop_max_count = 250 # TODO disable
88
89
 
89
90
  original_logger_level = Glimmer::Config.logger.level
90
91
  Glimmer::Config.logger = Glimmer::Config::OpalLogger.new(STDOUT)
@@ -31,7 +31,7 @@ module Glimmer
31
31
  def interpret_expression(expression, keyword, *args, &block)
32
32
  work = lambda do
33
33
  expression.interpret(parent, keyword, *args, &block).tap do |ui_object|
34
- add_content(ui_object, expression, &block)
34
+ add_content(ui_object, expression, keyword, *args, &block)
35
35
  dsl_stack.pop
36
36
  end
37
37
  end
@@ -1,14 +1,14 @@
1
1
  require_relative "contact_manager/contact_manager_presenter"
2
2
 
3
3
  class ContactManager
4
- include Glimmer
4
+ include Glimmer::UI::CustomShell
5
5
 
6
- def initialize
6
+ before_body {
7
7
  @contact_manager_presenter = ContactManagerPresenter.new
8
8
  @contact_manager_presenter.list
9
- end
9
+ }
10
10
 
11
- def launch
11
+ body {
12
12
  shell {
13
13
  text "Contact Manager"
14
14
  composite {
@@ -28,7 +28,7 @@ class ContactManager
28
28
  }
29
29
  text {
30
30
  layout_data :fill, :center, true, false
31
- text bind(@contact_manager_presenter, :first_name)
31
+ text <=> [@contact_manager_presenter, :first_name]
32
32
  on_key_pressed {|key_event|
33
33
  @contact_manager_presenter.find if key_event.keyCode == swt(:cr)
34
34
  }
@@ -41,7 +41,7 @@ class ContactManager
41
41
  }
42
42
  text {
43
43
  layout_data :fill, :center, true, false
44
- text bind(@contact_manager_presenter, :last_name)
44
+ text <=> [@contact_manager_presenter, :last_name]
45
45
  on_key_pressed {|key_event|
46
46
  @contact_manager_presenter.find if key_event.keyCode == swt(:cr)
47
47
  }
@@ -54,7 +54,7 @@ class ContactManager
54
54
  }
55
55
  text {
56
56
  layout_data :fill, :center, true, false
57
- text bind(@contact_manager_presenter, :email)
57
+ text <=> [@contact_manager_presenter, :email]
58
58
  on_key_pressed {|key_event|
59
59
  @contact_manager_presenter.find if key_event.keyCode == swt(:cr)
60
60
  }
@@ -87,7 +87,7 @@ class ContactManager
87
87
  }
88
88
  }
89
89
 
90
- table(:multi) { |table_proxy|
90
+ table(:editable, :multi) { |table_proxy|
91
91
  layout_data {
92
92
  horizontal_alignment :fill
93
93
  vertical_alignment :fill
@@ -95,6 +95,7 @@ class ContactManager
95
95
  grab_excess_vertical_space true
96
96
  height_hint 200
97
97
  }
98
+
98
99
  table_column {
99
100
  text "First Name"
100
101
  width 80
@@ -107,15 +108,16 @@ class ContactManager
107
108
  text "Email"
108
109
  width 200
109
110
  }
110
- items bind(@contact_manager_presenter, :results),
111
- column_properties(:first_name, :last_name, :email)
111
+
112
+ items <=> [@contact_manager_presenter, :results, column_attributes: [:first_name, :last_name, :email]]
113
+
112
114
  on_mouse_up { |event|
113
115
  table_proxy.edit_table_item(event.table_item, event.column_index)
114
116
  }
115
117
  }
116
118
  }
117
- }.open
118
- end
119
+ }
120
+ }
119
121
  end
120
122
 
121
- ContactManager.new.launch
123
+ ContactManager.launch
@@ -1,4 +1,25 @@
1
- require "observer"
1
+ # Copyright (c) 2020-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 'glimmer-dsl-swt'
2
23
 
3
24
  class LoginPresenter
4
25
 
@@ -14,29 +35,26 @@ class LoginPresenter
14
35
 
15
36
  def status=(status)
16
37
  @status = status
17
-
18
- notify_observers("logged_in")
19
- notify_observers("logged_out")
20
38
  end
21
39
 
22
40
  def valid?
23
41
  !@user_name.to_s.strip.empty? && !@password.to_s.strip.empty?
24
42
  end
25
43
 
26
- def logged_in
44
+ def logged_in?
27
45
  self.status == "Logged In"
28
46
  end
29
47
 
30
- def logged_out
31
- !self.logged_in
48
+ def logged_out?
49
+ !self.logged_in?
32
50
  end
33
51
 
34
- def login
52
+ def login!
35
53
  return unless valid?
36
54
  self.status = "Logged In"
37
55
  end
38
56
 
39
- def logout
57
+ def logout!
40
58
  self.user_name = ""
41
59
  self.password = ""
42
60
  self.status = "Logged Out"
@@ -45,19 +63,24 @@ class LoginPresenter
45
63
  end
46
64
 
47
65
  class Login
48
- include Glimmer
66
+ include Glimmer::UI::CustomShell
67
+
68
+ before_body {
69
+ @presenter = LoginPresenter.new
70
+ }
49
71
 
50
- def launch
51
- presenter = LoginPresenter.new
52
- @shell = shell {
72
+ body {
73
+ shell {
53
74
  text "Login"
75
+
54
76
  composite {
55
77
  grid_layout 2, false #two columns with differing widths
56
78
 
57
79
  label { text "Username:" } # goes in column 1
58
80
  @user_name_text = text { # goes in column 2
59
- text bind(presenter, :user_name)
60
- enabled bind(presenter, :logged_out)
81
+ text <=> [@presenter, :user_name]
82
+ enabled <= [@presenter, :logged_out?, computed_by: :status]
83
+
61
84
  on_key_pressed { |event|
62
85
  @password_text.set_focus if event.keyCode == swt(:cr)
63
86
  }
@@ -65,40 +88,44 @@ class Login
65
88
 
66
89
  label { text "Password:" }
67
90
  @password_text = text(:password, :border) {
68
- text bind(presenter, :password)
69
- enabled bind(presenter, :logged_out)
91
+ text <=> [@presenter, :password]
92
+ enabled <= [@presenter, :logged_out?, computed_by: :status]
93
+
70
94
  on_key_pressed { |event|
71
- presenter.login if event.keyCode == swt(:cr)
95
+ @presenter.login! if event.keyCode == swt(:cr)
72
96
  }
73
97
  }
74
98
 
75
99
  label { text "Status:" }
76
- label { text bind(presenter, :status) }
100
+ label { text <= [@presenter, :status] }
77
101
 
78
102
  button {
79
103
  text "Login"
80
- enabled bind(presenter, :logged_out)
81
- on_widget_selected { presenter.login }
104
+ enabled <= [@presenter, :logged_out?, computed_by: :status]
105
+
106
+ on_widget_selected { @presenter.login! }
82
107
  on_key_pressed { |event|
83
- presenter.login if event.keyCode == swt(:cr)
108
+ if event.keyCode == swt(:cr)
109
+ @presenter.login!
110
+ end
84
111
  }
85
112
  }
86
113
 
87
114
  button {
88
115
  text "Logout"
89
- enabled bind(presenter, :logged_in)
90
- on_widget_selected { presenter.logout }
116
+ enabled <= [@presenter, :logged_in?, computed_by: :status]
117
+
118
+ on_widget_selected { @presenter.logout! }
91
119
  on_key_pressed { |event|
92
120
  if event.keyCode == swt(:cr)
93
- presenter.logout
121
+ @presenter.logout!
94
122
  @user_name_text.set_focus
95
123
  end
96
124
  }
97
125
  }
98
126
  }
99
127
  }
100
- @shell.open
101
- end
128
+ }
102
129
  end
103
130
 
104
- Login.new.launch
131
+ Login.launch
@@ -35,8 +35,8 @@ class TicTacToe
35
35
  (1..3).each { |column|
36
36
  button {
37
37
  layout_data :fill, :fill, true, true
38
- text bind(@tic_tac_toe_board[row, column], :sign)
39
- enabled bind(@tic_tac_toe_board[row, column], :empty)
38
+ text <= [@tic_tac_toe_board[row, column], :sign]
39
+ enabled <= [@tic_tac_toe_board[row, column], :empty]
40
40
  font style: :bold, height: 20
41
41
  on_widget_selected {
42
42
  @tic_tac_toe_board.mark(row, column)
@@ -0,0 +1,157 @@
1
+ # Copyright (c) 2020-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 'net/http'
23
+ require 'json'
24
+ require 'facets/string/titlecase'
25
+
26
+ class Weather
27
+ include Glimmer::UI::CustomShell
28
+
29
+ DEFAULT_FONT_HEIGHT = 30
30
+ DEFAULT_FOREGROUND = :white
31
+ DEFAULT_BACKGROUND = rgb(135, 176, 235)
32
+
33
+ attr_accessor :city, :temp, :temp_min, :temp_max, :feels_like, :humidity
34
+
35
+ before_body {
36
+ @weather_mutex = Mutex.new
37
+ self.city = 'Montreal, QC, CA'
38
+ fetch_weather!
39
+ }
40
+
41
+ body {
42
+ shell(:no_resize) {
43
+ grid_layout
44
+
45
+ text 'Glimmer Weather'
46
+ minimum_size 400, 300
47
+ background DEFAULT_BACKGROUND
48
+
49
+ text {
50
+ layout_data(:center, :center, true, true)
51
+
52
+ text <=> [self, :city]
53
+
54
+ on_key_pressed {|event|
55
+ if event.keyCode == swt(:cr) # carriage return
56
+ Thread.new do
57
+ fetch_weather!
58
+ end
59
+ end
60
+ }
61
+ }
62
+
63
+ tab_folder {
64
+ layout_data(:center, :center, true, true)
65
+
66
+ ['℃', '℉'].each do |temp_unit|
67
+ tab_item {
68
+ grid_layout 2, false
69
+
70
+ text temp_unit
71
+ background DEFAULT_BACKGROUND
72
+
73
+ rectangle(0, 0, [:default, -2], [:default, -2], 15, 15) {
74
+ foreground DEFAULT_FOREGROUND
75
+ }
76
+
77
+ %w[temp temp_min temp_max feels_like].each do |field_name|
78
+ temp_field(field_name, temp_unit)
79
+ end
80
+
81
+ humidity_field
82
+ }
83
+ end
84
+ }
85
+ }
86
+ }
87
+
88
+ def temp_field(field_name, temp_unit)
89
+ name_label(field_name)
90
+ label {
91
+ layout_data(:fill, :center, true, false)
92
+ text <= [self, field_name, on_read: ->(t) { "#{kelvin_to_temp_unit(t, temp_unit).to_f.round}°" }]
93
+ font height: DEFAULT_FONT_HEIGHT
94
+ foreground DEFAULT_FOREGROUND
95
+ }
96
+ end
97
+
98
+ def humidity_field
99
+ name_label('humidity')
100
+ label {
101
+ layout_data(:fill, :center, true, false)
102
+ text <= [self, 'humidity', on_read: ->(h) { "#{h.to_f.round}%" }]
103
+ font height: DEFAULT_FONT_HEIGHT
104
+ foreground DEFAULT_FOREGROUND
105
+ }
106
+ end
107
+
108
+ def name_label(field_name)
109
+ label {
110
+ layout_data :fill, :center, false, false
111
+ text field_name.titlecase
112
+ font height: DEFAULT_FONT_HEIGHT
113
+ foreground DEFAULT_FOREGROUND
114
+ }
115
+ end
116
+
117
+ def fetch_weather!
118
+ @weather_mutex.synchronize do
119
+ self.weather_data = JSON.parse(Net::HTTP.get('api.openweathermap.org', "/data/2.5/weather?q=#{city}&appid=1d16d70a9aec3570b5cbd27e6b421330"))
120
+ end
121
+ rescue => e
122
+ Glimmer::Config.logger.error "Unable to fetch weather due to error: #{e.full_message}"
123
+ end
124
+
125
+ def weather_data=(data)
126
+ @weather_data = data
127
+ main_data = data['main']
128
+ # temps come back in Kelvin
129
+ self.temp = main_data['temp']
130
+ self.temp_min = main_data['temp_min']
131
+ self.temp_max = main_data['temp_max']
132
+ self.feels_like = main_data['feels_like']
133
+ self.humidity = main_data['humidity']
134
+ end
135
+
136
+ def kelvin_to_temp_unit(kelvin, temp_unit)
137
+ temp_unit == '℃' ? kelvin_to_celsius(kelvin) : kelvin_to_fahrenheit(kelvin)
138
+ end
139
+
140
+ def kelvin_to_celsius(kelvin)
141
+ return nil if kelvin.nil?
142
+ kelvin - 273.15
143
+ end
144
+
145
+ def celsius_to_fahrenheit(celsius)
146
+ return nil if celsius.nil?
147
+ (celsius * 9 / 5 ) + 32
148
+ end
149
+
150
+ def kelvin_to_fahrenheit(kelvin)
151
+ return nil if kelvin.nil?
152
+ celsius_to_fahrenheit(kelvin_to_celsius(kelvin))
153
+ end
154
+
155
+ end
156
+
157
+ Weather.launch
@@ -20,27 +20,27 @@
20
20
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
21
 
22
22
  class HelloButton
23
- include Glimmer
23
+ include Glimmer::UI::CustomShell
24
24
 
25
25
  attr_accessor :count
26
26
 
27
- def initialize
27
+ before_body {
28
28
  @count = 0
29
- end
29
+ }
30
30
 
31
- def launch
31
+ body {
32
32
  shell {
33
33
  text 'Hello, Button!'
34
34
 
35
35
  button {
36
- text bind(self, :count) {|value| "Click To Increment: #{value} "}
36
+ text <= [self, :count, on_read: ->(value) { "Click To Increment: #{value} " }]
37
37
 
38
38
  on_widget_selected {
39
39
  self.count += 1
40
40
  }
41
41
  }
42
- }.open
43
- end
42
+ }
43
+ }
44
44
  end
45
45
 
46
- HelloButton.new.launch
46
+ HelloButton.launch