glimmer-dsl-libui 0.7.8 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.7.8
1
+ 0.9.0
data/bin/girb CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- # Copyright (c) 2020-2021 Andy Maleh
3
+ # Copyright (c) 2021-2023 Andy Maleh
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining
6
6
  # a copy of this software and associated documentation files (the
data/bin/glimmer ADDED
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Copyright (c) 2021-2023 Andy Maleh
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining
6
+ # a copy of this software and associated documentation files (the
7
+ # "Software"), to deal in the Software without restriction, including
8
+ # without limitation the rights to use, copy, modify, merge, publish,
9
+ # distribute, sublicense, and/or sell copies of the Software, and to
10
+ # permit persons to whom the Software is furnished to do so, subject to
11
+ # the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be
14
+ # included in all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ require_relative '../lib/glimmer-dsl-libui'
25
+ require_relative '../lib/glimmer/launcher'
26
+
27
+ # Initialize launcher, consuming ARGV args that are for Glimmer only
28
+ launcher = Glimmer::Launcher.new(ARGV)
29
+
30
+ launcher.launch
@@ -25,6 +25,7 @@
25
25
  - [Basic Transform](#basic-transform)
26
26
  - [Basic Draw Text](#basic-draw-text)
27
27
  - [Basic Code Area](#basic-code-area)
28
+ - [Basic Composite Shape](#basic-composite-shape)
28
29
 
29
30
  ## Basic Window
30
31
 
@@ -2344,36 +2345,20 @@ Mac | Windows | Linux
2344
2345
 
2345
2346
  New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
2346
2347
 
2347
- ```ruby
2348
- require 'glimmer-dsl-libui'
2348
+ ## Basic Composite Shape
2349
2349
 
2350
- class BasicCodeArea
2351
- include Glimmer::LibUI::Application
2352
-
2353
- before_body do
2354
- @code = <<~CODE
2355
- # Greets target with greeting
2356
- def greet(greeting: 'Hello', target: 'World')
2357
-
2358
- puts "\#{greeting}, \#{target}!"
2359
- end
2360
-
2361
- greet
2362
- greet(target: 'Robert')
2363
- greet(greeting: 'Aloha')
2364
- greet(greeting: 'Aloha', target: 'Nancy')
2365
- greet(greeting: 'Howdy', target: 'Doodle')
2366
- CODE
2367
- end
2368
-
2369
- body {
2370
- window('Basic Code Area', 400, 300) {
2371
- margined true
2372
-
2373
- code_area(language: 'ruby', code: @code)
2374
- }
2375
- }
2376
- end
2350
+ [examples/basic_composite_shape.rb](/examples/basic_composite_shape.rb)
2351
+
2352
+ Run with this command from the root of the project if you cloned the project:
2377
2353
 
2378
- BasicCodeArea.launch
2379
2354
  ```
2355
+ ruby -r './lib/glimmer-dsl-libui' examples/basic_composite_shape.rb
2356
+ ```
2357
+
2358
+ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
2359
+
2360
+ ```
2361
+ ruby -r glimmer-dsl-libui -e "require 'examples/basic_composite_shape.rb'"
2362
+ ```
2363
+
2364
+ ![glimmer-dsl-libui-mac-basic-composite-shape.gif](/images/glimmer-dsl-libui-mac-basic-composite-shape.gif)
@@ -0,0 +1,145 @@
1
+ require 'glimmer-dsl-libui'
2
+
3
+ class BasicCompositeShape
4
+ include Glimmer::LibUI::Application
5
+
6
+ body {
7
+ window {
8
+ title 'Basic Composite Shape'
9
+ content_size 200, 225
10
+
11
+ @area = area {
12
+ rectangle(0, 0, 200, 225) {
13
+ fill :white
14
+ }
15
+
16
+ 7.times do |n|
17
+ x_location = (rand*125).to_i%200 + (rand*15).to_i
18
+ y_location = (rand*125).to_i%200 + (rand*15).to_i
19
+ shape_color = [rand*125 + 130, rand*125 + 130, rand*125 + 130]
20
+ shape_size = 20+n
21
+
22
+ cube(
23
+ location_x: x_location,
24
+ location_y: y_location,
25
+ rectangle_width: shape_size*2,
26
+ rectangle_height: shape_size,
27
+ cube_height: shape_size*2,
28
+ background_color: shape_color,
29
+ line_thickness: 2
30
+ ) { |the_shape|
31
+ on_mouse_up do |area_mouse_event|
32
+ # Change color on mouse up without dragging
33
+ if @drag_shape.nil?
34
+ background_color = [rand(255), rand(255), rand(255)]
35
+ the_shape.fill = background_color
36
+ end
37
+ end
38
+
39
+ on_mouse_drag_start do |area_mouse_event|
40
+ @drag_shape = the_shape
41
+ @drag_x = area_mouse_event[:x]
42
+ @drag_y = area_mouse_event[:y]
43
+ end
44
+
45
+ on_mouse_drag do |area_mouse_event|
46
+ if @drag_shape && @drag_x && @drag_y
47
+ drag_distance_width = area_mouse_event[:x] - @drag_x
48
+ drag_distance_height = area_mouse_event[:y] - @drag_y
49
+ @drag_shape.x += drag_distance_width
50
+ @drag_shape.y += drag_distance_height
51
+ @drag_x = area_mouse_event[:x]
52
+ @drag_y = area_mouse_event[:y]
53
+ end
54
+ end
55
+
56
+ on_mouse_drop do |area_mouse_event|
57
+ @drag_shape = nil
58
+ @drag_x = nil
59
+ @drag_y = nil
60
+ end
61
+ }
62
+ end
63
+
64
+ # this general area on_mouse_drag listener is needed to ensure that dragging a shape
65
+ # outside of its boundaries would still move the dragged shape
66
+ on_mouse_drag do |area_mouse_event|
67
+ if @drag_shape && @drag_x && @drag_y
68
+ drag_distance_width = area_mouse_event[:x] - @drag_x
69
+ drag_distance_height = area_mouse_event[:y] - @drag_y
70
+ @drag_shape.x += drag_distance_width
71
+ @drag_shape.y += drag_distance_height
72
+ @drag_x = area_mouse_event[:x]
73
+ @drag_y = area_mouse_event[:y]
74
+ end
75
+ end
76
+
77
+ on_mouse_drop do |area_mouse_event|
78
+ @drag_shape = nil
79
+ @drag_x = nil
80
+ @drag_y = nil
81
+ end
82
+ }
83
+ }
84
+ }
85
+
86
+ # method-based custom shape using `shape` keyword as a composite shape containing nested shapes
87
+ # that are declared with relative positioning
88
+ def cube(location_x: 0,
89
+ location_y: 0,
90
+ rectangle_width: nil,
91
+ rectangle_height: nil,
92
+ cube_height: nil,
93
+ background_color: :brown,
94
+ line_thickness: 1,
95
+ &content_block)
96
+ default_size = 28
97
+ rectangle_width ||= rectangle_height || cube_height || default_size
98
+ rectangle_height ||= rectangle_width || cube_height || default_size
99
+ cube_height ||= rectangle_width || rectangle_height || default_size
100
+ foreground_color = [0, 0, 0, thickness: line_thickness]
101
+
102
+ # the shape keyword (alias for composite_shape) enables building a composite shape that is treated as one shape
103
+ # like a cube containing polygons, a polyline, a rectangle, and a line
104
+ # with the fill and stroke colors getting inherited by all children that do not specify them
105
+ shape(location_x, location_y) { |the_shape|
106
+ fill background_color
107
+ stroke foreground_color
108
+
109
+ bottom = polygon(0, cube_height + rectangle_height / 2.0,
110
+ rectangle_width / 2.0, cube_height,
111
+ rectangle_width, cube_height + rectangle_height / 2.0,
112
+ rectangle_width / 2.0, cube_height + rectangle_height) {
113
+ # inherits fill property from parent shape if not set
114
+ # inherits stroke property from parent shape if not set
115
+ }
116
+ body = rectangle(0, rectangle_height / 2.0, rectangle_width, cube_height) {
117
+ # inherits fill property from parent shape if not set
118
+ # stroke is overridden to ensure a different value from parent
119
+ stroke thickness: 0
120
+ }
121
+ polyline(0, rectangle_height / 2.0 + cube_height,
122
+ 0, rectangle_height / 2.0,
123
+ rectangle_width, rectangle_height / 2.0,
124
+ rectangle_width, rectangle_height / 2.0 + cube_height) {
125
+ # inherits stroke property from parent shape if not set
126
+ }
127
+ top = polygon(0, rectangle_height / 2.0,
128
+ rectangle_width / 2.0, 0,
129
+ rectangle_width, rectangle_height / 2.0,
130
+ rectangle_width / 2.0, rectangle_height) {
131
+ # inherits fill property from parent shape if not set
132
+ # inherits stroke property from parent shape if not set
133
+ }
134
+ line(rectangle_width / 2.0, cube_height + rectangle_height,
135
+ rectangle_width / 2.0, rectangle_height) {
136
+ # inherits stroke property from parent shape if not set
137
+ }
138
+
139
+ content_block&.call(the_shape)
140
+ }
141
+ end
142
+ end
143
+
144
+ BasicCompositeShape.launch
145
+
@@ -6,7 +6,7 @@ class ButtonCounter
6
6
  attr_accessor :count
7
7
 
8
8
  def initialize
9
- @count = 0
9
+ self.count = 0
10
10
  end
11
11
 
12
12
  def launch
Binary file
@@ -0,0 +1,26 @@
1
+ # Copyright (c) 2021-2023 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
+ # This Rakefile gets used from Glimmer::Launcher when the current directory for running the `glimmer` command
23
+ # does not have a Rakefile (e.g. during scaffolding).
24
+ # It is not needed when running from inside a scaffolded app or gem, or an app with a Rakefile that has
25
+ # `require 'glimmer/rake_task'`
26
+ require 'glimmer/rake_task'
@@ -33,8 +33,7 @@ module Glimmer
33
33
  end
34
34
 
35
35
  def interpret(parent, keyword, *args, &block)
36
- @@inverted_keyword_aliases = Glimmer::LibUI::ControlProxy::KEYWORD_ALIASES.invert unless defined?(@@inverted_keyword_aliases)
37
- keyword = @@inverted_keyword_aliases[keyword] || keyword
36
+ keyword = Glimmer::LibUI::ControlProxy::KEYWORD_ALIASES[keyword] || keyword
38
37
  Glimmer::LibUI::ControlProxy.create(keyword, parent, args, &block)
39
38
  end
40
39
 
@@ -42,6 +42,7 @@ module Glimmer
42
42
  end
43
43
 
44
44
  def interpret(parent, keyword, *args, &block)
45
+ keyword = Glimmer::LibUI::Shape::KEYWORD_ALIASES[keyword] || keyword
45
46
  args = [args] if args.size > 1 && Glimmer::LibUI::Shape.shape_class(keyword).parameters.size == 1
46
47
  Glimmer::LibUI::Shape.create(keyword, parent, args, &block)
47
48
  end
@@ -0,0 +1,231 @@
1
+ # Copyright (c) 2007-2023 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
+ if ARGV.include?('--bundler') && File.exist?(File.expand_path('./Gemfile'))
23
+ require 'bundler'
24
+ Bundler.setup(:default)
25
+ end
26
+ require 'fileutils'
27
+ require 'os'
28
+
29
+ module Glimmer
30
+ # Launcher of glimmer applications and main entry point for the `glimmer` command.
31
+ class Launcher
32
+ # TODO update the verbiage below for Glimmer DSL for LibUI
33
+ TEXT_USAGE = <<~MULTI_LINE_STRING
34
+ Glimmer DSL for LibUI (Prerequisite-Free Ruby Desktop Development Cross-Platform Native GUI Library) - Ruby Gem: glimmer-dsl-libui v#{File.read(File.expand_path('../../../VERSION', __FILE__))}
35
+ Usage: glimmer [--bundler] [--pd] [--quiet] [--debug] [--log-level=VALUE] [[ENV_VAR=VALUE]...] [[-ruby-option]...] (application.rb or task[task_args])
36
+
37
+ Runs Glimmer applications and tasks.
38
+
39
+ When applications are specified, they are run using Ruby,
40
+ automatically preloading the glimmer-dsl-libui Ruby gem.
41
+
42
+ Optionally, extra Glimmer options, Ruby options, and/or environment variables may be passed in.
43
+
44
+ Glimmer options:
45
+ - "--bundler=GROUP" : Activates gems in Bundler default group in Gemfile
46
+ - "--pd=BOOLEAN" : Requires puts_debuggerer to enable pd method
47
+ - "--quiet=BOOLEAN" : Does not announce file path of Glimmer application being launched
48
+ - "--debug" : Displays extra debugging information and enables debug logging
49
+ - "--log-level=VALUE" : Sets Glimmer's Ruby logger level ("ERROR" / "WARN" / "INFO" / "DEBUG"; default is none)
50
+
51
+ Tasks are run via rake. Some tasks take arguments in square brackets (surround with double-quotes if using Zsh).
52
+
53
+ Available tasks are below (if you do not see any, please add `require 'glimmer/rake_task'` to Rakefile and rerun or run rake -T):
54
+
55
+ MULTI_LINE_STRING
56
+
57
+ GLIMMER_LIB_GEM = 'glimmer-dsl-libui'
58
+ GLIMMER_LIB_LOCAL = File.expand_path(File.join('lib', "#{GLIMMER_LIB_GEM}.rb"))
59
+ GLIMMER_OPTIONS = %w[--log-level --quiet --bundler --pd]
60
+ GLIMMER_OPTION_ENV_VAR_MAPPING = {
61
+ '--log-level' => 'GLIMMER_LOGGER_LEVEL' ,
62
+ '--bundler' => 'GLIMMER_BUNDLER_SETUP' ,
63
+ '--pd' => 'PD' ,
64
+ }
65
+ REGEX_RAKE_TASK_WITH_ARGS = /^([^\[]+)\[?([^\]]*)\]?$/
66
+
67
+ class << self
68
+ def is_arm64?
69
+ host_cpu = OS.host_cpu.downcase
70
+ host_cpu.include?('aarch64') || host_cpu.include?('arm')
71
+ end
72
+
73
+ def glimmer_lib
74
+ unless @glimmer_lib
75
+ @glimmer_lib = GLIMMER_LIB_GEM
76
+ if File.exist?(GLIMMER_LIB_LOCAL)
77
+ @glimmer_lib = GLIMMER_LIB_LOCAL
78
+ puts "[DEVELOPMENT MODE] (detected #{@glimmer_lib})"
79
+ end
80
+ end
81
+ @glimmer_lib
82
+ end
83
+
84
+ def dev_mode?
85
+ glimmer_lib == GLIMMER_LIB_LOCAL
86
+ end
87
+
88
+ def glimmer_option_env_vars(glimmer_options)
89
+ GLIMMER_OPTION_ENV_VAR_MAPPING.reduce({}) do |hash, pair|
90
+ glimmer_options[pair.first] ? hash.merge(GLIMMER_OPTION_ENV_VAR_MAPPING[pair.first] => glimmer_options[pair.first]) : hash
91
+ end
92
+ end
93
+
94
+ def load_env_vars(env_vars)
95
+ env_vars.each do |key, value|
96
+ ENV[key] = value
97
+ end
98
+ end
99
+
100
+ def launch(application, ruby_options: [], env_vars: {}, glimmer_options: {})
101
+ ruby_options_string = ruby_options.join(' ') + ' ' if ruby_options.any?
102
+ env_vars = env_vars.merge(glimmer_option_env_vars(glimmer_options))
103
+ env_vars.each do |k,v|
104
+ ENV[k] = v
105
+ end
106
+ the_glimmer_lib = glimmer_lib
107
+ require 'puts_debuggerer' if the_glimmer_lib == GLIMMER_LIB_LOCAL
108
+ is_rake_task = !application.end_with?('.rb')
109
+ rake_tasks = []
110
+ if is_rake_task
111
+ load File.expand_path('./Rakefile') if File.exist?(File.expand_path('./Rakefile')) && caller.join("\n").include?('/bin/glimmer:')
112
+ require_relative 'rake_task'
113
+ rake_tasks = Rake.application.tasks.map(&:to_s).map {|t| t.sub('glimmer:', '')}
114
+
115
+ potential_rake_task_parts = application.match(REGEX_RAKE_TASK_WITH_ARGS)
116
+ application = potential_rake_task_parts[1]
117
+ rake_task_args = potential_rake_task_parts[2].split(',')
118
+ end
119
+ if rake_tasks.include?(application)
120
+ load_env_vars(glimmer_option_env_vars(glimmer_options))
121
+ rake_task = "glimmer:#{application}"
122
+ puts "Running Glimmer rake task: #{rake_task}" if ruby_options_string.to_s.include?('--debug')
123
+ Rake::Task[rake_task].invoke(*rake_task_args)
124
+ else
125
+ puts "Launching Glimmer Application: #{application}" if ruby_options_string.to_s.include?('--debug') || glimmer_options['--quiet'].to_s.downcase != 'true'
126
+ require the_glimmer_lib
127
+ load File.expand_path(application)
128
+ end
129
+ end
130
+ end
131
+
132
+ attr_reader :application_paths
133
+ attr_reader :env_vars
134
+ attr_reader :glimmer_options
135
+ attr_reader :ruby_options
136
+
137
+ def initialize(raw_options)
138
+ raw_options << '--quiet' if !caller.join("\n").include?('/bin/glimmer:') && !raw_options.join.include?('--quiet=')
139
+ raw_options << '--log-level=DEBUG' if raw_options.join.include?('--debug') && !raw_options.join.include?('--log-level=')
140
+ @application_path = extract_application_path(raw_options)
141
+ @env_vars = extract_env_vars(raw_options)
142
+ @glimmer_options = extract_glimmer_options(raw_options)
143
+ @ruby_options = raw_options
144
+ end
145
+
146
+ def launch
147
+ if @application_path.nil?
148
+ display_usage
149
+ else
150
+ launch_application
151
+ end
152
+ end
153
+
154
+ private
155
+
156
+ def launch_application
157
+ self.class.launch(
158
+ @application_path,
159
+ ruby_options: @ruby_options,
160
+ env_vars: @env_vars,
161
+ glimmer_options: @glimmer_options
162
+ )
163
+ end
164
+
165
+ def display_usage
166
+ puts TEXT_USAGE
167
+ display_tasks
168
+ end
169
+
170
+ def display_tasks
171
+ if OS.windows? || Launcher.is_arm64?
172
+ require 'rake'
173
+ Rake::TaskManager.record_task_metadata = true
174
+ require_relative 'rake_task'
175
+ tasks = Rake.application.tasks
176
+ task_lines = tasks.reject do |task|
177
+ task.comment.nil?
178
+ end.map do |task|
179
+ max_task_size = tasks.map(&:name_with_args).map(&:size).max + 1
180
+ task_name = task.name_with_args.sub('glimmer:', '')
181
+ line = "glimmer #{task_name.ljust(max_task_size)} # #{task.comment}"
182
+ end
183
+ puts task_lines.to_a
184
+ else
185
+ require 'rake-tui'
186
+ require 'tty-screen'
187
+ require_relative 'rake_task'
188
+ Rake::TUI.run(branding_header: nil, prompt_question: 'Select a Glimmer task to run:') do |task, tasks|
189
+ max_task_size = tasks.map(&:name_with_args).map(&:size).max + 1
190
+ task_name = task.name_with_args.sub('glimmer:', '')
191
+ line = "glimmer #{task_name.ljust(max_task_size)} # #{task.comment}"
192
+ bound = TTY::Screen.width - 6
193
+ line.size <= bound ? line : "#{line[0..(bound - 3)]}..."
194
+ end
195
+ end
196
+ end
197
+
198
+ # Extract application path (which can also be a rake task, basically a non-arg)
199
+ def extract_application_path(options)
200
+ application_path = options.detect do |option|
201
+ !option.start_with?('-') && !option.include?('=')
202
+ end.tap do
203
+ options.delete(application_path)
204
+ end
205
+ end
206
+
207
+ def extract_env_vars(options)
208
+ options.select do |option|
209
+ !option.start_with?('-') && option.include?('=')
210
+ end.each do |env_var|
211
+ options.delete(env_var)
212
+ end.reduce({}) do |hash, env_var_string|
213
+ match = env_var_string.match(/^([^=]+)=(.+)$/)
214
+ hash.merge(match[1] => match[2])
215
+ end
216
+ end
217
+
218
+ def extract_glimmer_options(options)
219
+ options.select do |option|
220
+ GLIMMER_OPTIONS.reduce(false) do |result, glimmer_option|
221
+ result || option.include?(glimmer_option)
222
+ end
223
+ end.each do |glimmer_option|
224
+ options.delete(glimmer_option)
225
+ end.reduce({}) do |hash, glimmer_option_string|
226
+ match = glimmer_option_string.match(/^([^=]+)=?(.+)?$/)
227
+ hash.merge(match[1] => (match[2] || 'true'))
228
+ end
229
+ end
230
+ end
231
+ end
@@ -129,6 +129,7 @@ module Glimmer
129
129
 
130
130
  def build_control
131
131
  @libui = ::LibUI::FFI::DrawMatrix.malloc
132
+ # TODO is there a way to free the memory allocated for this? Or does it get garbage collected automatically?
132
133
  if @args.empty?
133
134
  set_identity
134
135
  else
@@ -167,9 +167,9 @@ module Glimmer
167
167
  end
168
168
 
169
169
  def perfect_shape
170
- perfect_shape_dependencies = [draw_fill_mode, children]
171
- if perfect_shape_dependencies != @perfect_shape_dependencies
172
- draw_fill_mode, children = @perfect_shape_dependencies = perfect_shape_dependencies
170
+ the_perfect_shape_dependencies = perfect_shape_dependencies
171
+ if the_perfect_shape_dependencies != @perfect_shape_dependencies
172
+ draw_fill_mode, _ = @perfect_shape_dependencies = the_perfect_shape_dependencies
173
173
  shapes = children.map(&:perfect_shape)
174
174
  new_shapes = []
175
175
  shapes.each do |shape|
@@ -190,6 +190,10 @@ module Glimmer
190
190
  @perfect_shape
191
191
  end
192
192
 
193
+ def perfect_shape_dependencies
194
+ [draw_fill_mode, children.map(&:perfect_shape_dependencies)]
195
+ end
196
+
193
197
  private
194
198
 
195
199
  def build_control
@@ -35,10 +35,10 @@ module Glimmer
35
35
  end
36
36
 
37
37
  def create(keyword, parent, args, &block)
38
- widget_proxy_class(keyword).new(keyword, parent, args, &block).tap {|c| control_proxies << c}
38
+ control_proxy_class(keyword).new(keyword, parent, args, &block).tap {|c| control_proxies << c}
39
39
  end
40
40
 
41
- def widget_proxy_class(keyword)
41
+ def control_proxy_class(keyword)
42
42
  descendant_keyword_constant_map[keyword] || ControlProxy
43
43
  end
44
44
 
@@ -96,7 +96,7 @@ module Glimmer
96
96
  private
97
97
 
98
98
  def add_aliases_to_keyword_constant_map(keyword_constant_map)
99
- KEYWORD_ALIASES.each do |keyword, alias_keyword|
99
+ KEYWORD_ALIASES.each do |alias_keyword, keyword|
100
100
  keyword_constant_map[alias_keyword] = keyword_constant_map[keyword]
101
101
  end
102
102
  keyword_constant_map
@@ -106,8 +106,8 @@ module Glimmer
106
106
  include DataBindable
107
107
 
108
108
  KEYWORD_ALIASES = {
109
- 'msg_box' => 'message_box',
110
- 'msg_box_error' => 'message_box_error',
109
+ 'message_box' => 'msg_box',
110
+ 'message_box_error' => 'msg_box_error',
111
111
  }
112
112
 
113
113
  BOOLEAN_PROPERTIES = %w[
@@ -8,7 +8,7 @@ module Glimmer
8
8
 
9
9
  def_delegators :perfect_shape,
10
10
  :min_x, :min_y, :max_x, :max_y, :center_point, :center_x, :center_y
11
-
11
+
12
12
  # Returns if shape contains point on the inside when outline is false (default)
13
13
  # or if point is on the outline when outline is true
14
14
  # distance_tolerance is used when outline is true to enable a fuzz factor in
@@ -22,6 +22,7 @@ module Glimmer
22
22
  # or if shape includes point on the outline when stroked
23
23
  def include?(*point)
24
24
  if fill.empty?
25
+ # TODO check if distance_tolerance should be half the thickness in case it is checked against both sides of out and in
25
26
  contain?(*point, outline: true, distance_tolerance: ((stroke[:thickness] || 1) - 1))
26
27
  else
27
28
  contain?(*point)
@@ -60,6 +61,13 @@ module Glimmer
60
61
  def perfect_shape
61
62
  # No Op
62
63
  end
64
+
65
+ # Returns PerfectShape object dependencies to determine if the PerfectShape
66
+ # object changed or not for caching purposes.
67
+ # Every shape/path implements this uniquely for its own PerfectShape attribute dependencies
68
+ def perfect_shape_dependencies
69
+ # No Op
70
+ end
63
71
  end
64
72
  end
65
73
  end
@@ -51,21 +51,25 @@ module Glimmer
51
51
  end
52
52
 
53
53
  def perfect_shape
54
- perfect_shape_dependencies = [x_center, y_center, radius, start_angle, sweep, is_negative]
55
- if perfect_shape_dependencies != @perfect_shape_dependencies
56
- x_center, y_center, radius, start_angle, sweep, is_negative = @perfect_shape_dependencies = perfect_shape_dependencies
54
+ the_perfect_shape_dependencies = perfect_shape_dependencies
55
+ if the_perfect_shape_dependencies != @perfect_shape_dependencies
56
+ absolute_x_center, absolute_y_center, radius, start_angle, sweep, is_negative = @perfect_shape_dependencies = the_perfect_shape_dependencies
57
57
  sign = is_negative ? 1 : -1
58
58
  start = is_negative ? (360 - start_angle) : -1*start_angle
59
59
  extent = is_negative ? (360 - sweep) : -1*sweep
60
60
  @perfect_shape = PerfectShape::Arc.new(
61
61
  type: :open,
62
- center_x: x_center, center_y: y_center,
62
+ center_x: absolute_x_center, center_y: absolute_y_center,
63
63
  radius_x: radius, radius_y: radius,
64
64
  start: start, extent: extent
65
65
  )
66
66
  end
67
67
  @perfect_shape
68
68
  end
69
+
70
+ def perfect_shape_dependencies
71
+ [absolute_x_center, absolute_y_center, radius, start_angle, sweep, is_negative]
72
+ end
69
73
  end
70
74
  end
71
75
  end