glimmer 0.5.3 → 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7e54e98c5ffbe3d68414f3d50103d1d4871ba6a7d9a620b78c57850df8cd7247
4
- data.tar.gz: 445cccf31afff2be026317731bd08d3cf51fe79fc3ae166ffd75ed7d93291f5e
3
+ metadata.gz: e13a3990377287df2bf023f72f4c6ab4b1cb8a58b4dad96321f4ac59e9efde11
4
+ data.tar.gz: 829597311c7719af97c326fd6f456038c1df94a76d8f140dd8acc430a8a951d4
5
5
  SHA512:
6
- metadata.gz: 712e885e76e573b302a961e00a21b680d236aa4d63567e57c6d4dd014f3f57cddb82f72259769695bea4f4a7eb3da768153827f0554fe73002605944f6e60701
7
- data.tar.gz: 93152749050dd124163b074260dc1c587fff26726d157808436922cb99aca5acd2156377b8e32344ee9a53cc686bac5ada5127ee96161e81622e159b2bd999cc
6
+ metadata.gz: 344b90942d68a6bd994b09eb0e0848f996d57da0526cf54a6fc11f9718aae850b3e53419ba8bb418470f303fda278283ae99420d6a04bbd2cbab5b22e9ad250e
7
+ data.tar.gz: 486bbbf6de699a7d3985eea76a5588a83fe32c8a1484648d68b026237390427c2e14b8083b59874f0e0a87454c8d0a05449ca12016ba950c2271d7ac0da7e960
@@ -1,4 +1,4 @@
1
- # Glimmer 0.5.3 Beta (JRuby Desktop UI DSL + Data-Binding)
1
+ # Glimmer 0.5.4 Beta (JRuby Desktop UI DSL + Data-Binding)
2
2
  [![Coverage Status](https://coveralls.io/repos/github/AndyObtiva/glimmer/badge.svg?branch=master)](https://coveralls.io/github/AndyObtiva/glimmer?branch=master)
3
3
 
4
4
  Glimmer is a native-UI cross-platform desktop development library written in Ruby. Glimmer's main innovation is a JRuby DSL that enables productive and efficient authoring of desktop application user-interfaces while relying on the robust platform-native Eclipse SWT library. Glimmer additionally innovates by having built-in data-binding support to greatly facilitate synchronizing the UI with domain models. As a result, that achieves true decoupling of object oriented components, enabling developers to solve business problems without worrying about UI concerns, or alternatively drive development UI-first, and then write clean business components test-first afterwards.
@@ -111,19 +111,19 @@ Please follow these instructions to make the `glimmer` command available on your
111
111
 
112
112
  Run this command to install directly:
113
113
  ```
114
- jgem install glimmer -v 0.5.3
114
+ jgem install glimmer -v 0.5.4
115
115
  ```
116
116
 
117
117
  ### Option 2: Bundler
118
118
 
119
119
  Add the following to `Gemfile`:
120
120
  ```
121
- gem 'glimmer', '~> 0.5.3'
121
+ gem 'glimmer', '~> 0.5.4'
122
122
  ```
123
123
 
124
124
  And, then run:
125
125
  ```
126
- bundle install
126
+ jruby -S bundle install
127
127
  ```
128
128
 
129
129
  ## Glimmer Command
@@ -153,7 +153,7 @@ bin/glimmer samples/hello_world.rb
153
153
  ### Advanced Usage
154
154
 
155
155
  ```
156
- glimmer [[-jruby-option]...] application.rb [[application2.rb]...]
156
+ glimmer [--log-level=VALUE] [[ENV_VAR=VALUE]...] [[-jruby-option]...] application.rb [[application2.rb]...]
157
157
  ```
158
158
 
159
159
  Accepts JRuby options and multiple Glimmer applications to run simultaneously, each in a JRuby thread.
@@ -356,7 +356,7 @@ Example (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
356
356
  button {
357
357
  text "Press Me"
358
358
  on_widget_selected {
359
- message_box = MessageBox.new(@shell.widget) # passing SWT Shell widget
359
+ message_box = MessageBox.new(@shell.swt_widget) # passing SWT Shell widget
360
360
  message_box.setText("Surprise")
361
361
  message_box.setMessage("You have won $1,000,000!")
362
362
  message_box.open
@@ -481,7 +481,7 @@ In the above example, the `text` widget `enabled` property was data-bound to `#e
481
481
 
482
482
  #### Colors
483
483
 
484
- Colors make up a subset of widget properties. SWT accepts color objects created with RGB (Red Green Blue) or RGBA (Red Green Blue Alpha). Glimmer supports constructing color objects using the `rgb` and `rgba` DSL methods.
484
+ Colors make up a subset of widget properties. SWT accepts color objects created with RGB (Red Green Blue) or RGBA (Red Green Blue Alpha). Glimmer supports constructing color objects using the `rgb` and `rgba` DSL keywords.
485
485
 
486
486
  Example:
487
487
 
@@ -494,21 +494,21 @@ label {
494
494
  # ...
495
495
  ```
496
496
 
497
- SWT also supports standard colors available as constants under the `SWT` namespace with the `COLOR_` prefix (e.g. `SWT::COLOR_BLUE`, `SWT::COLOR_WHITE`, `SWT::COLOR_RED`)
497
+ SWT also supports standard colors available as constants under the `SWT` namespace with the `COLOR_` prefix (e.g. `SWT::COLOR_BLUE`)
498
498
 
499
- Glimmer accepts these constants as lowercase Ruby symbols with or without `color_` prefix.
499
+ Glimmer supports constructing colors for these constants as lowercase Ruby symbols (with or without `color_` prefix) passed to `color` DSL keyword
500
500
 
501
501
  Example:
502
502
 
503
503
  ```ruby
504
504
  # ...
505
505
  label {
506
- background :black
507
- foreground :yellow
506
+ background color(:black)
507
+ foreground color(:yellow)
508
508
  }
509
509
  label {
510
- background :color_white
511
- foreground :color_red
510
+ background color(:color_white)
511
+ foreground color(:color_red)
512
512
  }
513
513
  # ...
514
514
  ```
@@ -522,9 +522,11 @@ https://help.eclipse.org/2019-12/nftopic/org.eclipse.platform.doc.isv/reference/
522
522
 
523
523
  Glimmer color objects come with an instance method `#swt_color` that returns the actual SWT `Color` object wrapped by the Glimmer color object. It is useful in cases you'd like to do some custom SWT programming outside of Glimmer.
524
524
 
525
- ##### `Glimmer::SWT::ColorProxy.new(display = nil, standard_color).swt_color`
525
+ Example:
526
526
 
527
- Glimmer `ColorProxy` class comes with `.color_for` method that builds an actual SWT `Color` object from a standard color string or symbol. Passing a `display` is optional. It is useful in cases you'd like to do some custom SWT programming outside of Glimmer.
527
+ ```ruby
528
+ color(:black).swt_color # returns SWT Color object
529
+ ```
528
530
 
529
531
  #### Fonts
530
532
 
@@ -976,20 +978,20 @@ shell {
976
978
  text "Show 2nd Button"
977
979
  visible true
978
980
  on_event_show {
979
- @button2.widget.setVisible(false)
981
+ @button2.swt_widget.setVisible(false)
980
982
  }
981
983
  on_widget_selected {
982
- @button2.widget.setVisible(true)
984
+ @button2.swt_widget.setVisible(true)
983
985
  }
984
986
  }
985
987
  @button2 = button {
986
988
  text "Show 1st Button"
987
989
  visible false
988
990
  on_event_show {
989
- @button1.widget.setVisible(false)
991
+ @button1.swt_widget.setVisible(false)
990
992
  }
991
993
  on_widget_selected {
992
- @button1.widget.setVisible(true)
994
+ @button1.swt_widget.setVisible(true)
993
995
  }
994
996
  }
995
997
  }.open
@@ -1077,7 +1079,7 @@ class TicTacToe
1077
1079
  end
1078
1080
 
1079
1081
  def display_game_over_message(message)
1080
- message_box = MessageBox.new(@shell.widget)
1082
+ message_box = MessageBox.new(@shell.swt_widget)
1081
1083
  message_box.setText("Game Over")
1082
1084
  message_box.setMessage(message)
1083
1085
  message_box.open
@@ -1313,7 +1315,7 @@ shell { |app_shell|
1313
1315
 
1314
1316
  Glimmer comes with a video widget not in SWT. It comes with very basic video functionality at the moment, such as autoplay by default, displaying controls, looping, and setting background.
1315
1317
 
1316
- Attributes (passed in an options hash as arguments to video widget):
1318
+ Options (passed in an options hash as arguments to video widget):
1317
1319
  - `autoplay` (true [default] or false): plays video automatically as soon as loaded
1318
1320
  - `controls` (true [default] or false): displays controls
1319
1321
  - `looped` (true or false [default]): plays video in looped mode
@@ -1324,8 +1326,22 @@ Attributes (passed in an options hash as arguments to video widget):
1324
1326
  - `offset_y` (integer [default: 0]): offset from top border. Could be a negative number if you want to show only an area of the video. Useful when fit_to_height is false to pick an area of the video to display.
1325
1327
 
1326
1328
  Methods:
1327
- - `play`: plays video
1328
- - `pause`: pauses video
1329
+ - `#play`: plays video
1330
+ - `#pause`: pauses video
1331
+ - `#reload`: reloads video restarting from beginning
1332
+ - `#position`: position in seconds (and fractions)
1333
+ - `#position=`: seeks a new position in video
1334
+ - `#duration`: length of video, maximum video position possible
1335
+ - `#loaded?`: returns true when video has been initially loaded or reloaded
1336
+ - `#playing?`: returns true when video is actively playing
1337
+ - `#paused?`: returns true when video is not playing
1338
+ - `#ended?`: returns true when video has reached the end (position == duration)
1339
+
1340
+ Events (to observe):
1341
+ - `on_loaded`: invoked when video `#loaded?` becomes true
1342
+ - `on_ended`: invoked when video `#ended?` becomes true
1343
+ - `on_playing`: invoked when video `#playing?` becomes true
1344
+ - `on_paused`: invoked when video `#paused?` becomes true
1329
1345
 
1330
1346
  Example ([samples/video/hello_video.rb](samples/video/hello_video.rb)):
1331
1347
 
@@ -1346,6 +1362,36 @@ shell {
1346
1362
  }.open
1347
1363
  ```
1348
1364
 
1365
+ Example ([samples/video/hello_video_observers.rb](samples/video/hello_video_observers.rb)):
1366
+
1367
+ ```ruby
1368
+ # ...
1369
+ def display_video_status(video, status)
1370
+ message_box = MessageBox.new(video.swt_widget.getShell)
1371
+ message_box.setText(status)
1372
+ message = "Video Position: #{video.position} seconds\n"
1373
+ message += "Video Duration: #{video.duration} seconds"
1374
+ message_box.setMessage(message)
1375
+ message_box.open
1376
+ end
1377
+
1378
+ @shell = shell {
1379
+ minimum_size 800, 500
1380
+ @video = video(file: video_file, background: :black) {
1381
+ on_playing {
1382
+ display_video_status(@video, 'Playing')
1383
+ }
1384
+ on_paused {
1385
+ display_video_status(@video, 'Paused')
1386
+ }
1387
+ on_ended {
1388
+ display_video_status(@video, 'Ended')
1389
+ }
1390
+ }
1391
+ }
1392
+ @shell.open
1393
+ ```
1394
+
1349
1395
  #### Browser Widget
1350
1396
 
1351
1397
  Glimmer supports SWT Browser widget, which can load URLs or render HTML. It can even be instrumented with JavaScript when needed (though highly discouraged in Glimmer except for rare cases when leveraging a pre-existing web codebase in a desktop app).
@@ -1377,7 +1423,7 @@ shell {
1377
1423
  </html>
1378
1424
  HTML
1379
1425
  on_completed { # on load of the page execute this JavaScript
1380
- @browser.widget.execute("alert('Hello, World!');")
1426
+ @browser.swt_widget.execute("alert('Hello, World!');")
1381
1427
  }
1382
1428
  }
1383
1429
  }.open
@@ -1539,10 +1585,8 @@ Glimmer simplifies the process for general packaging on the Mac by providing a r
1539
1585
 
1540
1586
  - Create `Rakefile` in your app root directory
1541
1587
  - Add the following line to it: `require 'glimmer/rake_task'`
1542
- - Create a Ruby script under bin to launch your application (e.g. `bin/math_bowling`) with the following content (replacing `'../app/my_application.rb'` with your application path):
1588
+ - Create a Ruby script under bin (e.g. `bin/math_bowling`) to require the application file that uses Glimmer (e.g. `'../app/my_application.rb'`):
1543
1589
  ```ruby
1544
- require 'glimmer/launcher'
1545
- require Glimmer::Launcher.swt_jar_file
1546
1590
  require_relative '../app/my_application.rb'
1547
1591
  ```
1548
1592
 
@@ -58,7 +58,7 @@ module Glimmer
58
58
  if method_symbol.to_s.match(REGEX_METHODS_EXCLUDED)
59
59
  raise InvalidKeywordError, "Glimmer excluded keyword: #{method_symbol}"
60
60
  end
61
- Glimmer.logger&.debug "keyword: " + method_symbol.to_s + " and args: " + args.to_s
61
+ Glimmer.logger&.debug "Interpreting keyword: #{method_symbol}"
62
62
  Glimmer::DSL::Engine.interpret(method_symbol, *args, &block)
63
63
  rescue InvalidKeywordError => e
64
64
  if !method_symbol.to_s.match(REGEX_METHODS_EXCLUDED)
@@ -69,8 +69,15 @@ module Glimmer
69
69
  end
70
70
  end
71
71
 
72
+ if ENV['GLIMMER_LOGGER_LEVEL']
73
+ Glimmer.enable_logging
74
+ Glimmer.logger.level = ENV['GLIMMER_LOGGER_LEVEL'].downcase
75
+ end
76
+
72
77
  $LOAD_PATH.unshift(File.expand_path('..', __FILE__))
73
78
 
79
+ require 'glimmer/launcher'
80
+ require Glimmer::Launcher.swt_jar_file
74
81
  require 'glimmer/swt/packages'
75
82
  require 'glimmer/dsl'
76
83
  require 'glimmer/error'
@@ -203,7 +203,10 @@ module Glimmer
203
203
  end
204
204
 
205
205
  def evaluate_property
206
- invoke_property_reader(model, property_name) unless model.nil?
206
+ unless model.nil?
207
+ value = invoke_property_reader(model, property_name)
208
+ convert_on_read(value)
209
+ end
207
210
  end
208
211
 
209
212
  def evaluate_options_property
@@ -219,16 +222,14 @@ module Glimmer
219
222
  end
220
223
 
221
224
  def invoke_property_reader(object, property_expression)
222
- value = nil
223
225
  if property_indexed?(property_expression)
224
226
  property_method = '[]'
225
227
  property_argument = property_expression[1...-1]
226
228
  property_argument = property_argument.to_i if property_argument.match(/\d+/)
227
- value = object.send(property_method, property_argument)
229
+ object.send(property_method, property_argument)
228
230
  else
229
- value = object.send(property_expression)
231
+ object.send(property_expression)
230
232
  end
231
- convert_on_read(value)
232
233
  end
233
234
 
234
235
  def invoke_property_writer(object, property_expression, value)
@@ -15,13 +15,10 @@ module Glimmer
15
15
  @property = property
16
16
  @translator = translator || proc {|value| value}
17
17
 
18
- begin
18
+ if @widget.respond_to?(:dispose)
19
19
  @widget.on_widget_disposed do |dispose_event|
20
20
  unregister_all_observables
21
21
  end
22
- rescue => e
23
- # No Op
24
- Glimmer.logger&.debug("#{e.message}\n#{e.backtrace.join("\n")}")
25
22
  end
26
23
  end
27
24
  def call(value)
@@ -18,7 +18,6 @@ module Glimmer
18
18
 
19
19
  def interpret(parent, keyword, *args, &block)
20
20
  options = args.last.is_a?(Hash) ? args.pop : {}
21
- Glimmer.logger&.debug "Custom widget #{keyword} styles are: [" + args.inspect + "] and options are: #{options}"
22
21
  UI::CustomWidget.for(keyword).new(parent, *args, options, &block)
23
22
  end
24
23
 
@@ -58,9 +58,9 @@ module Glimmer
58
58
  #
59
59
  # For example, a shell widget would get properties set and children added
60
60
  def add_content(parent, expression, &block)
61
- parent_stack.push(parent)
61
+ parent_stack.push(parent) if expression.is_a?(ParentExpression)
62
62
  expression.add_content(parent, &block) if block_given?
63
- parent_stack.pop
63
+ parent_stack.pop if expression.is_a?(ParentExpression)
64
64
  end
65
65
 
66
66
  # Current parent while evaluating Glimmer DSL (nil if just started or done evaluatiing)
@@ -23,16 +23,16 @@ module Glimmer
23
23
  # Otherwise, it forwards to the next handler configured via `#next=` method
24
24
  # If there is no handler next, then it raises an error
25
25
  def handle(parent, keyword, *args, &block)
26
- Glimmer.logger&.debug "Attempting to handle #{keyword}(#{args}) with #{@expression.class.name.split(":").last}"
26
+ Glimmer.logger&.debug "Attempting to handle #{keyword} with #{@expression.class.name.split(":").last}"
27
27
  if @expression.can_interpret?(parent, keyword, *args, &block)
28
- Glimmer.logger&.debug "#{@expression.class.name} will handle expression keyword #{keyword} with arguments #{args}"
28
+ Glimmer.logger&.debug "#{@expression.class.name} will handle expression keyword #{keyword}"
29
29
  return @expression
30
30
  elsif @next_expression_handler
31
31
  return @next_expression_handler.handle(parent, keyword, *args, &block)
32
32
  else
33
33
  # TODO see if we need a better response here (e.g. dev mode error raising vs production mode silent failure)
34
34
  message = "Glimmer keyword #{keyword} with args #{args} cannot be handled"
35
- message += " inside parent #{parent.inspect}" if parent
35
+ message += " inside parent #{parent}" if parent
36
36
  message += "! Check the validity of the code."
37
37
  # Glimmer.logger&.error message
38
38
  raise InvalidKeywordError, message
@@ -16,7 +16,6 @@ module Glimmer
16
16
  end
17
17
 
18
18
  def interpret(parent, keyword, *args, &block)
19
- Glimmer.logger&.debug "Layout Data args are: #{args.inspect}"
20
19
  SWT::LayoutDataProxy.new(parent, args)
21
20
  end
22
21
  end
@@ -18,7 +18,6 @@ module Glimmer
18
18
  end
19
19
 
20
20
  def interpret(parent, keyword, *args, &block)
21
- Glimmer.logger&.debug "Layout #{keyword} args are: #{args.inspect}"
22
21
  SWT::LayoutProxy.new(keyword, parent, args)
23
22
  end
24
23
  end
@@ -19,9 +19,9 @@ module Glimmer
19
19
  Glimmer.define_method(keyword) do |*args, &block|
20
20
  parent = Glimmer::DSL::Engine.current_parent
21
21
  if !static_expression.can_interpret?(parent, keyword, *args, &block)
22
- raise Error, "Invalid use of Glimmer keyword #{keyword} with args #{args.inspect} under parent #{parent.inspect}"
22
+ raise Error, "Invalid use of Glimmer keyword #{keyword} with args #{args} under parent #{parent}"
23
23
  else
24
- Glimmer.logger&.debug "#{base.name} will handle expression keyword #{keyword} with arguments #{args}"
24
+ Glimmer.logger&.debug "#{base.name} will handle expression keyword #{keyword}"
25
25
  static_expression.interpret(parent, keyword, *args, &block).tap do |ui_object|
26
26
  Glimmer::DSL::Engine.add_content(ui_object, static_expression, &block) unless block.nil?
27
27
  end
@@ -6,17 +6,16 @@ module Glimmer
6
6
  module DSL
7
7
  class WidgetExpression < Expression
8
8
  include ParentExpression
9
-
9
+
10
10
  EXCLUDED_KEYWORDS = %w[shell display tab_item]
11
11
 
12
12
  def can_interpret?(parent, keyword, *args, &block)
13
13
  !EXCLUDED_KEYWORDS.include?(keyword) and
14
- widget?(parent) and
14
+ widget?(parent) and #TODO change to composite?(parent)
15
15
  SWT::WidgetProxy.widget_exists?(keyword)
16
16
  end
17
17
 
18
18
  def interpret(parent, keyword, *args, &block)
19
- Glimmer.logger&.debug "widget styles are: " + args.inspect
20
19
  SWT::WidgetProxy.new(keyword, parent, args)
21
20
  end
22
21
  end
@@ -14,7 +14,7 @@ module Glimmer
14
14
  Glimmer.logger&.debug "block exists?: #{!block.nil?}"
15
15
  raise Glimmer::Error, "Listener is missing block for keyword: #{keyword}" unless block_given?
16
16
  Glimmer.logger&.debug "args are empty?: #{args.empty?}"
17
- raise Glimmer::Error, "Invalid listener arguments for keyword: #{keyword}(#{args.inspect})" unless args.empty?
17
+ raise Glimmer::Error, "Invalid listener arguments for keyword: #{keyword}(#{args})" unless args.empty?
18
18
  result = parent.can_handle_observation_request?(keyword)
19
19
  Glimmer.logger&.debug "can add listener? #{result}"
20
20
  raise Glimmer::Error, "Invalid listener keyword: #{keyword}" unless result
@@ -4,7 +4,7 @@ module Glimmer
4
4
  class Launcher
5
5
  OPERATING_SYSTEMS_SUPPORTED = ["mac", "windows", "linux"]
6
6
  TEXT_USAGE = <<-MULTILINE
7
- Usage: glimmer [[-jruby-option]...] application.rb [[application2.rb]...]
7
+ Usage: glimmer [--log-level=VALUE] [[ENV_VAR=VALUE]...] [[-jruby-option]...] application.rb [[application2.rb]...]
8
8
 
9
9
  Runs Glimmer applications using JRuby, automatically preloading
10
10
  the glimmer ruby gem and SWT jar dependency.
@@ -16,6 +16,10 @@ module Glimmer
16
16
  MULTILINE
17
17
  GLIMMER_LIB_LOCAL = File.expand_path(File.join(__FILE__, '..', '..', 'glimmer.rb'))
18
18
  GLIMMER_LIB_GEM = 'glimmer'
19
+ GLIMMER_OPTIONS = %w[--log-level]
20
+ GLIMMER_OPTION_ENV_VAR_MAPPING = {
21
+ '--log-level' => 'GLIMMER_LOGGER_LEVEL'
22
+ }
19
23
 
20
24
  @@mutex = Mutex.new
21
25
 
@@ -50,18 +54,25 @@ module Glimmer
50
54
  @glimmer_lib
51
55
  end
52
56
 
53
- def launch(application, options = [])
54
- options_string = options.join(' ') + ' ' if options.any?
55
- system "jruby #{options_string}#{jruby_swt_options} -r #{glimmer_lib} -S #{application}"
57
+ def glimmer_option_env_vars(glimmer_options)
58
+ glimmer_options.map do |k, v|
59
+ "#{GLIMMER_OPTION_ENV_VAR_MAPPING[k]}=#{v}"
60
+ end.join(' ')
56
61
  end
57
- end
58
62
 
59
- def initialize(options)
60
- @application_paths = options.select {|option| !option.start_with?('-')}
61
- @application_paths.each do |application_path|
62
- options.delete(application_path)
63
+ def launch(application, jruby_options: [], env_vars: {}, glimmer_options: {})
64
+ jruby_options_string = jruby_options.join(' ') + ' ' if jruby_options.any?
65
+ env_vars_string = env_vars.map {|k,v| "#{k}=#{v}"}.join(' ')
66
+ env_vars_string = [env_vars_string, glimmer_option_env_vars(glimmer_options)].join(' ')
67
+ system "#{env_vars_string} jruby #{jruby_options_string}#{jruby_os_specific_options} -r #{glimmer_lib} -S #{application}"
63
68
  end
64
- @options = options
69
+ end
70
+
71
+ def initialize(raw_options)
72
+ @application_paths = extract_application_paths(raw_options)
73
+ @env_vars = extract_env_vars(raw_options)
74
+ @glimmer_options = extract_glimmer_options(raw_options)
75
+ @jruby_options = raw_options
65
76
  end
66
77
 
67
78
  def launch
@@ -78,7 +89,12 @@ module Glimmer
78
89
  threads = @application_paths.map do |application_path|
79
90
  puts "Launching Glimmer Application: #{application_path}" unless application_path.to_s.include?('irb')
80
91
  Thread.new do
81
- self.class.launch(application_path, @options)
92
+ self.class.launch(
93
+ application_path,
94
+ jruby_options: @jruby_options,
95
+ env_vars: @env_vars,
96
+ glimmer_options: @glimmer_options
97
+ )
82
98
  end
83
99
  end
84
100
  threads.each(&:join)
@@ -87,5 +103,37 @@ module Glimmer
87
103
  def display_usage
88
104
  puts TEXT_USAGE
89
105
  end
106
+
107
+ def extract_application_paths(options)
108
+ options.select do |option|
109
+ !option.start_with?('-') && !option.include?('=')
110
+ end.each do |application_path|
111
+ options.delete(application_path)
112
+ end
113
+ end
114
+
115
+ def extract_env_vars(options)
116
+ options.select do |option|
117
+ !option.start_with?('-') && option.include?('=')
118
+ end.each do |env_var|
119
+ options.delete(env_var)
120
+ end.reduce({}) do |hash, env_var_string|
121
+ match = env_var_string.match(/^([^=]+)=(.+)$/)
122
+ hash.merge(match[1] => match[2])
123
+ end
124
+ end
125
+
126
+ def extract_glimmer_options(options)
127
+ options.select do |option|
128
+ GLIMMER_OPTIONS.reduce(false) do |result, glimmer_option|
129
+ result || option.include?(glimmer_option)
130
+ end
131
+ end.each do |glimmer_option|
132
+ options.delete(glimmer_option)
133
+ end.reduce({}) do |hash, glimmer_option_string|
134
+ match = glimmer_option_string.match(/^([^=]+)=?(.*)$/)
135
+ hash.merge(match[1] => match[2])
136
+ end
137
+ end
90
138
  end
91
139
  end
@@ -18,7 +18,6 @@ namespace :glimmer do
18
18
  end
19
19
  system('mkdir -p dist')
20
20
  system('warble')
21
- puts("javapackager -deploy -native -outdir packages -outfile #{project_name} -srcdir dist -srcfiles #{project_name}.jar -appclass JarMain -name \"#{project_name}\" -title \"#{project_name}\" -BjvmOptions=-XstartOnFirstThread")
22
21
  system("javapackager -deploy -native -outdir packages -outfile #{project_name} -srcdir dist -srcfiles #{project_name}.jar -appclass JarMain -name \"#{project_name}\" -title \"#{project_name}\" -BjvmOptions=-XstartOnFirstThread")
23
22
  end
24
23
  end
@@ -25,6 +25,7 @@ module Glimmer
25
25
  split(/__/).map do |namespace|
26
26
  namespace.camelcase(:upper)
27
27
  end
28
+ #TODO update code to avoid using reduce and going through all of them, yet stop right when it finds something
28
29
  custom_widget_class = [Object, Glimmer::UI].reduce([]) do |found, base|
29
30
  if found.empty?
30
31
  found << namespaces.reduce(base) do |result, namespace|
@@ -135,7 +136,7 @@ module Glimmer
135
136
  end
136
137
 
137
138
  def can_add_observer?(attribute_name)
138
- respond_to?(attribute_name) || @body_root.can_add_observer?(attribute_name)
139
+ respond_to?(attribute_name) || respond_to?("#{attribute_name}?") || @body_root.can_add_observer?(attribute_name)
139
140
  end
140
141
 
141
142
  def add_observer(observer, attribute_name)
@@ -10,15 +10,6 @@ module Glimmer
10
10
 
11
11
  include_package 'org.eclipse.swt.browser'
12
12
 
13
- PROPERTIES_OBSERVED = [
14
- 'playing',
15
- 'paused',
16
- 'ended',
17
- 'started',
18
- 'remaining',
19
- 'current_time',
20
- ]
21
-
22
13
  options :file, :url
23
14
  option :autoplay, true
24
15
  option :controls, true
@@ -70,6 +61,9 @@ module Glimmer
70
61
  </body>
71
62
  </html>
72
63
  HTML
64
+ on_completed {
65
+ @completed = true
66
+ }
73
67
  }
74
68
  }
75
69
 
@@ -103,10 +97,137 @@ module Glimmer
103
97
  video_action('pause')
104
98
  end
105
99
 
100
+ def reload
101
+ video_action('load')
102
+ end
103
+
104
+ def paused?
105
+ video_attribute('paused')
106
+ end
107
+
108
+ def playing?
109
+ !paused?
110
+ end
111
+
112
+ def ended?
113
+ video_attribute('ended')
114
+ end
115
+
116
+ # Video fully loaded and ready for playback
117
+ def loaded?
118
+ !!@completed
119
+ end
120
+
121
+ def position
122
+ video_attribute('currentTime')
123
+ end
124
+
125
+ def position=(new_position)
126
+ video_attribute_set('currentTime', new_position)
127
+ end
128
+
129
+ def duration
130
+ video_attribute('duration')
131
+ end
132
+
133
+ def can_handle_observation_request?(observation_request)
134
+ result = false
135
+ if observation_request.start_with?('on_')
136
+ attribute = observation_request.sub(/^on_/, '')
137
+ result = OBSERVED_ATTRIBUTE_TO_PROPERTY_MAPPING.keys.include?(attribute)
138
+ end
139
+ result || super
140
+ end
141
+
142
+ def handle_observation_request(observation_request, &block)
143
+ if observation_request.start_with?('on_')
144
+ attribute = observation_request.sub(/^on_/, '')
145
+ if attribute == 'loaded' && !@completed
146
+ super('on_completed', &block)
147
+ elsif OBSERVED_ATTRIBUTE_TO_PROPERTY_MAPPING.keys.include?(attribute)
148
+ add_video_observer(block, OBSERVED_ATTRIBUTE_TO_PROPERTY_MAPPING[attribute])
149
+ else
150
+ super
151
+ end
152
+ end
153
+ end
154
+
106
155
  private
107
156
 
157
+ class VideoObserverBrowserFunction < BrowserFunction
158
+ def initialize(video, observer_proc, attribute)
159
+ @observer_proc = observer_proc
160
+ @attribute = attribute
161
+ name = self.class.generate_name(@attribute)
162
+ super(video.swt_widget, name)
163
+ end
164
+
165
+ def function(arguments)
166
+ @observer_proc.call
167
+ rescue => e
168
+ Glimmer.logger&.error "#{e.message}\n#{e.backtrace.join("\n")}"
169
+ ensure
170
+ nil
171
+ end
172
+
173
+ private
174
+
175
+ class << self
176
+ def generate_name(attribute)
177
+ "video#{attribute}#{generate_attribute_id(attribute)}"
178
+ end
179
+
180
+ def generate_attribute_id(attribute)
181
+ attribute_max_ids[attribute] = attribute_max_id(attribute) + 1
182
+ end
183
+
184
+ def attribute_max_id(attribute)
185
+ attribute_max_ids[attribute] ||= 0
186
+ end
187
+
188
+ def attribute_max_ids
189
+ @attribute_max_ids ||= {}
190
+ end
191
+ end
192
+ end
193
+
194
+ OBSERVED_ATTRIBUTE_TO_PROPERTY_MAPPING = {
195
+ 'playing' => 'play',
196
+ 'paused' => 'pause',
197
+ 'ended' => 'ended',
198
+ 'loaded' => 'canplay',
199
+ }
200
+
108
201
  def video_action(action)
109
- swt_widget.execute("document.getElementById('video').#{action}()")
202
+ run_on_completed do
203
+ swt_widget.execute("document.getElementById('video').#{action}()")
204
+ end
205
+ end
206
+
207
+ def video_attribute(attribute)
208
+ swt_widget.evaluate("return document.getElementById('video').#{attribute}") if @completed
209
+ end
210
+
211
+ def video_attribute_set(attribute, value)
212
+ value = "'#{value}'" if value.is_a?(String) || value.is_a?(Symbol)
213
+ run_on_completed do
214
+ swt_widget.execute("document.getElementById('video').#{attribute} = #{value}")
215
+ end
216
+ end
217
+
218
+ def add_video_observer(observer_proc, attribute)
219
+ run_on_completed do
220
+ video_observer_browser_function = VideoObserverBrowserFunction.new(self, observer_proc, attribute)
221
+ swt_widget.execute("document.getElementById('video').addEventListener('#{attribute}', function() {#{video_observer_browser_function.getName}()})")
222
+ end
223
+ end
224
+
225
+ def run_on_completed(&block)
226
+ if @completed
227
+ block.call
228
+ else
229
+ on_completed(&block)
230
+ end
110
231
  end
111
232
 
112
233
  def browser_video_autoplay
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: glimmer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.3
4
+ version: 0.5.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - AndyMaleh
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-14 00:00:00.000000000 Z
11
+ date: 2020-04-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement