glimmer-dsl-libui 0.9.7 → 0.10.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.9.7
1
+ 0.10.0
@@ -1271,11 +1271,11 @@ window('Login') {
1271
1271
 
1272
1272
  ## Method-Based Custom Controls
1273
1273
 
1274
- [Custom keywords](#custom-keywords) can be defined to represent custom controls (components) that provide new features or act as composites of existing controls that need to be reused multiple times in an application or across multiple applications. Custom keywords save a lot of development time, improving productivity and maintainability immensely.
1274
+ [Custom components](#custom-components) can be defined to represent custom controls (components) that provide new features or act as composites of existing controls that need to be reused multiple times in an application or across multiple applications. Custom components save a lot of development time, improving productivity and maintainability immensely.
1275
1275
 
1276
1276
  This example defines `form_field`, `address_form`, `label_pair`, and `address` as custom controls (keywords).
1277
1277
 
1278
- The custom keywords are defined via methods (thus are "method-based").
1278
+ The Custom components are defined via methods (thus are "method-based").
1279
1279
 
1280
1280
  [examples/method_based_custom_controls.rb](/examples/method_based_custom_controls.rb)
1281
1281
 
@@ -1494,11 +1494,11 @@ window('Method-Based Custom Controls') {
1494
1494
 
1495
1495
  ## Class-Based Custom Controls
1496
1496
 
1497
- [Custom keywords](#custom-keywords) can be defined to represent custom controls (components) that provide new features or act as composites of existing controls that need to be reused multiple times in an application or across multiple applications. Custom keywords save a lot of development time, improving productivity and maintainability immensely.
1497
+ [Custom components](#custom-components) can be defined to represent custom controls (components) that provide new features or act as composites of existing controls that need to be reused multiple times in an application or across multiple applications. Custom components save a lot of development time, improving productivity and maintainability immensely.
1498
1498
 
1499
1499
  This example defines `form_field`, `address_form`, `label_pair`, and `address` as custom controls (keywords).
1500
1500
 
1501
- The custom keywords are defined via classes that include `Glimmer::LibUI::CustomControl` (thus are "class-based"), thus enabling offloading each custom control into its own file when needed for better code organization.
1501
+ The Custom components are defined via classes that include `Glimmer::LibUI::CustomControl` (thus are "class-based"), thus enabling offloading each custom control into its own file when needed for better code organization.
1502
1502
 
1503
1503
  [examples/class_based_custom_controls.rb](/examples/class_based_custom_controls.rb)
1504
1504
 
@@ -1646,7 +1646,7 @@ ClassBasedCustomControls.launch
1646
1646
 
1647
1647
  ## Area-Based Custom Controls
1648
1648
 
1649
- [Custom keywords](#custom-keywords) can be defined for graphical custom controls (components) built completely from scratch as vector-graphics on top of the [`area`](#area-api) control while leveraging keyboard and mouse listeners.
1649
+ [Custom components](#custom-components) can be defined for graphical custom controls (components) built completely from scratch as vector-graphics on top of the [`area`](#area-api) control while leveraging keyboard and mouse listeners.
1650
1650
 
1651
1651
  This example defines `text_label` and `push_button` as [`area`](#area-api)-based graphical custom controls that can have width, height, font, fill, stroke, border, and custom text location.
1652
1652
 
@@ -26,6 +26,7 @@
26
26
  - [Basic Draw Text](#basic-draw-text)
27
27
  - [Basic Code Area](#basic-code-area)
28
28
  - [Basic Composite Shape](#basic-composite-shape)
29
+ - [Basic Custom Shape](#basic-custom-shape)
29
30
 
30
31
  ## Basic Window
31
32
 
@@ -2347,6 +2348,8 @@ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version
2347
2348
 
2348
2349
  ## Basic Composite Shape
2349
2350
 
2351
+ (this is the method-based custom shape version of Basic Custom Shape)
2352
+
2350
2353
  [examples/basic_composite_shape.rb](/examples/basic_composite_shape.rb)
2351
2354
 
2352
2355
  Run with this command from the root of the project if you cloned the project:
@@ -2362,3 +2365,23 @@ ruby -r glimmer-dsl-libui -e "require 'examples/basic_composite_shape.rb'"
2362
2365
  ```
2363
2366
 
2364
2367
  ![glimmer-dsl-libui-mac-basic-composite-shape.gif](/images/glimmer-dsl-libui-mac-basic-composite-shape.gif)
2368
+
2369
+ ## Basic Custom Shape
2370
+
2371
+ (this is the class-based custom shape version of Basic Composite Shape)
2372
+
2373
+ [examples/basic_custom_shape.rb](/examples/basic_custom_shape.rb)
2374
+
2375
+ Run with this command from the root of the project if you cloned the project:
2376
+
2377
+ ```
2378
+ ruby -r './lib/glimmer-dsl-libui' examples/basic_custom_shape.rb
2379
+ ```
2380
+
2381
+ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
2382
+
2383
+ ```
2384
+ ruby -r glimmer-dsl-libui -e "require 'examples/basic_custom_shape.rb'"
2385
+ ```
2386
+
2387
+ ![glimmer-dsl-libui-mac-basic-custom-shape.gif](/images/glimmer-dsl-libui-mac-basic-composite-shape.gif)
@@ -1,5 +1,7 @@
1
1
  require 'glimmer-dsl-libui'
2
2
 
3
+ # This is the method-based custom shape version of basic_custom_shape
4
+
3
5
  class BasicCompositeShape
4
6
  include Glimmer::LibUI::Application
5
7
 
@@ -0,0 +1,168 @@
1
+ require 'glimmer-dsl-libui'
2
+
3
+ # This is the class-based custom shape version of basic_composite_shape
4
+
5
+ # class-based custom shape using Glimmer::LibUI::CustomShape mixin, which automatically
6
+ # augments the Glimmer GUI DSL with the underscored version of the class name: `cube`
7
+ # while accepting hash options matching the options declared on the class.
8
+ # (e.g. `cube(location_x: 50, location_y: 100)` )
9
+ class Cube
10
+ include Glimmer::LibUI::CustomShape
11
+
12
+ DEFAULT_SIZE = 28
13
+
14
+ option :location_x, default: 0
15
+ option :location_y, default: 0
16
+ option :rectangle_width, default: nil
17
+ option :rectangle_height, default: nil
18
+ option :cube_height, default: 75
19
+ option :background_color, default: :brown
20
+ option :foreground_color
21
+ option :line_thickness, default: 1
22
+
23
+ # The before_body block executes before building the body
24
+ before_body do
25
+ self.rectangle_width ||= rectangle_height || cube_height || DEFAULT_SIZE
26
+ self.rectangle_height ||= rectangle_width || cube_height || DEFAULT_SIZE
27
+ self.cube_height ||= rectangle_width || rectangle_height || DEFAULT_SIZE
28
+ if foreground_color
29
+ self.foreground_color = Glimmer::LibUI.interpret_color(foreground_color)
30
+ self.foreground_color[:thickness] ||= line_thickness
31
+ else
32
+ self.foreground_color = [0, 0, 0, thickness: line_thickness]
33
+ end
34
+ end
35
+
36
+ # Optionally, after_body could be defined to perform operations after building the body
37
+ # like setting up observers.
38
+ #
39
+ # after_body do
40
+ # end
41
+
42
+ body {
43
+ # the shape keyword (alias for composite_shape) enables building a composite shape that is treated as one shape
44
+ # like a cube containing polygons, a polyline, a rectangle, and a line
45
+ # with the fill and stroke colors getting inherited by all children that do not specify them
46
+ shape(location_x, location_y) {
47
+ fill background_color
48
+ stroke foreground_color
49
+
50
+ bottom = polygon(0, cube_height + rectangle_height / 2.0,
51
+ rectangle_width / 2.0, cube_height,
52
+ rectangle_width, cube_height + rectangle_height / 2.0,
53
+ rectangle_width / 2.0, cube_height + rectangle_height) {
54
+ # inherits fill property from parent shape if not set
55
+ # inherits stroke property from parent shape if not set
56
+ }
57
+ body = rectangle(0, rectangle_height / 2.0, rectangle_width, cube_height) {
58
+ # inherits fill property from parent shape if not set
59
+ # stroke is overridden to ensure a different value from parent
60
+ stroke thickness: 0
61
+ }
62
+ polyline(0, rectangle_height / 2.0 + cube_height,
63
+ 0, rectangle_height / 2.0,
64
+ rectangle_width, rectangle_height / 2.0,
65
+ rectangle_width, rectangle_height / 2.0 + cube_height) {
66
+ # inherits stroke property from parent shape if not set
67
+ }
68
+ top = polygon(0, rectangle_height / 2.0,
69
+ rectangle_width / 2.0, 0,
70
+ rectangle_width, rectangle_height / 2.0,
71
+ rectangle_width / 2.0, rectangle_height) {
72
+ # inherits fill property from parent shape if not set
73
+ # inherits stroke property from parent shape if not set
74
+ }
75
+ line(rectangle_width / 2.0, cube_height + rectangle_height,
76
+ rectangle_width / 2.0, rectangle_height) {
77
+ # inherits stroke property from parent shape if not set
78
+ }
79
+ }
80
+ }
81
+ end
82
+
83
+ class BasicCustomShape
84
+ include Glimmer::LibUI::Application
85
+
86
+ body {
87
+ window {
88
+ title 'Basic Custom Shape'
89
+ content_size 200, 225
90
+
91
+ @area = area {
92
+ rectangle(0, 0, 200, 225) {
93
+ fill :white
94
+ }
95
+
96
+ 7.times do |n|
97
+ x_location = (rand*125).to_i%200 + (rand*15).to_i
98
+ y_location = (rand*125).to_i%200 + (rand*15).to_i
99
+ shape_color = [rand*125 + 130, rand*125 + 130, rand*125 + 130]
100
+ shape_size = 20+n
101
+
102
+ cube(
103
+ location_x: x_location,
104
+ location_y: y_location,
105
+ rectangle_width: shape_size*2,
106
+ rectangle_height: shape_size,
107
+ cube_height: shape_size*2,
108
+ background_color: shape_color,
109
+ line_thickness: 2
110
+ ) { |the_shape|
111
+ on_mouse_up do |area_mouse_event|
112
+ # Change color on mouse up without dragging
113
+ if @drag_shape.nil?
114
+ background_color = [rand(255), rand(255), rand(255)]
115
+ the_shape.fill = background_color
116
+ end
117
+ end
118
+
119
+ on_mouse_drag_start do |area_mouse_event|
120
+ @drag_shape = the_shape
121
+ @drag_x = area_mouse_event[:x]
122
+ @drag_y = area_mouse_event[:y]
123
+ end
124
+
125
+ on_mouse_drag do |area_mouse_event|
126
+ if @drag_shape && @drag_x && @drag_y
127
+ drag_distance_width = area_mouse_event[:x] - @drag_x
128
+ drag_distance_height = area_mouse_event[:y] - @drag_y
129
+ @drag_shape.x += drag_distance_width
130
+ @drag_shape.y += drag_distance_height
131
+ @drag_x = area_mouse_event[:x]
132
+ @drag_y = area_mouse_event[:y]
133
+ end
134
+ end
135
+
136
+ on_mouse_drop do |area_mouse_event|
137
+ @drag_shape = nil
138
+ @drag_x = nil
139
+ @drag_y = nil
140
+ end
141
+ }
142
+ end
143
+
144
+ # this general area on_mouse_drag listener is needed to ensure that dragging a shape
145
+ # outside of its boundaries would still move the dragged shape
146
+ on_mouse_drag do |area_mouse_event|
147
+ if @drag_shape && @drag_x && @drag_y
148
+ drag_distance_width = area_mouse_event[:x] - @drag_x
149
+ drag_distance_height = area_mouse_event[:y] - @drag_y
150
+ @drag_shape.x += drag_distance_width
151
+ @drag_shape.y += drag_distance_height
152
+ @drag_x = area_mouse_event[:x]
153
+ @drag_y = area_mouse_event[:y]
154
+ end
155
+ end
156
+
157
+ on_mouse_drop do |area_mouse_event|
158
+ @drag_shape = nil
159
+ @drag_x = nil
160
+ @drag_y = nil
161
+ end
162
+ }
163
+ }
164
+ }
165
+ end
166
+
167
+ BasicCustomShape.launch
168
+
Binary file
@@ -0,0 +1,58 @@
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
+ require 'glimmer'
23
+ require 'glimmer/dsl/expression'
24
+ require 'glimmer/dsl/parent_expression'
25
+ require 'glimmer/dsl/top_level_expression'
26
+ require 'glimmer/libui/custom_shape'
27
+
28
+ module Glimmer
29
+ module DSL
30
+ module Libui
31
+ class CustomShapeExpression < Expression
32
+ # TODO Consider making custom shapes automatically generate static expressions
33
+ include ParentExpression
34
+ include TopLevelExpression
35
+
36
+ def can_interpret?(parent, keyword, *args, &block)
37
+ LibUI::CustomShape.for(keyword)
38
+ end
39
+
40
+ def interpret(parent, keyword, *args, &block)
41
+ options = args.last.is_a?(Hash) ? args.pop : {}
42
+ LibUI::CustomShape.for(keyword).new(keyword, parent, args, options, &block)
43
+ end
44
+
45
+ def add_content(custom_shape, keyword, *args, &block)
46
+ options = args.last.is_a?(Hash) ? args.last : {post_add_content: true}
47
+ # TODO consider avoiding source_location
48
+ if block.source_location == custom_shape.content&.__getobj__&.source_location
49
+ custom_shape.content.call(custom_shape) unless custom_shape.content.called?
50
+ else
51
+ super
52
+ end
53
+ custom_shape.post_add_content if options[:post_add_content]
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -45,6 +45,7 @@ module Glimmer
45
45
  control
46
46
  custom_control
47
47
  shape
48
+ custom_shape
48
49
  ]
49
50
  )
50
51
  end
@@ -27,7 +27,12 @@ module Glimmer
27
27
  module Libui
28
28
  class ListenerExpression < Expression
29
29
  def can_interpret?(parent, keyword, *args, &block)
30
- (parent.is_a?(Glimmer::LibUI::ControlProxy) or parent.is_a?(Glimmer::LibUI::Shape)) and
30
+ (
31
+ parent.is_a?(Glimmer::LibUI::ControlProxy) or
32
+ parent.is_a?(Glimmer::LibUI::Shape) or
33
+ parent.is_a?(Glimmer::LibUI::CustomControl) or
34
+ parent.is_a?(Glimmer::LibUI::CustomShape)
35
+ ) and
31
36
  block_given? and
32
37
  parent.can_handle_listener?(keyword)
33
38
  end
@@ -100,11 +100,9 @@ module Glimmer
100
100
  def launch(application, ruby_options: [], env_vars: {}, glimmer_options: {})
101
101
  ruby_options_string = ruby_options.join(' ') + ' ' if ruby_options.any?
102
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
103
+ load_env_vars(env_vars)
106
104
  the_glimmer_lib = glimmer_lib
107
- require 'puts_debuggerer' if the_glimmer_lib == GLIMMER_LIB_LOCAL
105
+ require 'puts_debuggerer' if (ENV['PD'] || ENV['pd']).to_s.downcase == 'true' || the_glimmer_lib == GLIMMER_LIB_LOCAL
108
106
  is_rake_task = !application.end_with?('.rb')
109
107
  rake_tasks = []
110
108
  if is_rake_task
@@ -117,7 +115,6 @@ module Glimmer
117
115
  rake_task_args = potential_rake_task_parts[2].split(',')
118
116
  end
119
117
  if rake_tasks.include?(application)
120
- load_env_vars(glimmer_option_env_vars(glimmer_options))
121
118
  rake_task = "glimmer:#{application}"
122
119
  puts "Running Glimmer rake task: #{rake_task}" if ruby_options_string.to_s.include?('--debug')
123
120
  Rake::Task[rake_task].invoke(*rake_task_args)
@@ -31,11 +31,34 @@ module Glimmer
31
31
  module CustomControl
32
32
  include SuperModule
33
33
  include DataBinding::ObservableModel
34
-
34
+
35
+ # This module was only created to prevent Glimmer from checking method_missing first
36
+ module GlimmerSupersedable
37
+ def method_missing(method_name, *args, &block)
38
+ # TODO Consider supporting a glimmer error silencing option for methods defined here
39
+ # but fail the glimmer DSL for the right reason to avoid seeing noise in the log output
40
+ if block && can_handle_listener?(method_name)
41
+ handle_listener(method_name, &block)
42
+ elsif @body_root.respond_to?(method_name, true)
43
+ @body_root.send(method_name, *args, &block)
44
+ else
45
+ super
46
+ end
47
+ end
48
+
49
+ def respond_to?(method_name, *args, &block)
50
+ result = false
51
+ result ||= super
52
+ result ||= can_handle_listener?(method_name)
53
+ result ||= @body_root.respond_to?(method_name, *args, &block)
54
+ end
55
+ end
56
+
35
57
  super_module_included do |klass|
36
- # TODO clear memoization of WidgetProxy.libui_class_for for a keyword if a custom control was defined with that keyword
58
+ # TODO clear memoization of ControlProxy.libui_class_for for a keyword if a custom control was defined with that keyword
37
59
  unless klass.name.include?('Glimmer::LibUI::CustomWindow')
38
60
  klass.include(Glimmer)
61
+ klass.include(GlimmerSupersedable) # prevent Glimmer from running method_missing first
39
62
  Glimmer::LibUI::CustomControl.add_custom_control_namespaces_for(klass)
40
63
  end
41
64
  end
@@ -220,23 +243,6 @@ module Glimmer
220
243
  @content
221
244
  end
222
245
  end
223
-
224
- def method_missing(method_name, *args, &block)
225
- # TODO Consider supporting a glimmer error silencing option for methods defined here
226
- # but fail the glimmer DSL for the right reason to avoid seeing noise in the log output
227
- if block && can_handle_listener?(method_name)
228
- handle_listener(method_name, &block)
229
- else
230
- @body_root.send(method_name, *args, &block)
231
- end
232
- end
233
-
234
- def respond_to?(method_name, *args, &block)
235
- result = false
236
- result ||= super
237
- result ||= can_handle_listener?(method_name)
238
- result ||= @body_root.respond_to?(method_name, *args, &block)
239
- end
240
246
 
241
247
  private
242
248