glimmer-dsl-libui 0.5.3 → 0.5.6
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -0
- data/README.md +345 -50
- data/VERSION +1 -1
- data/examples/class_based_custom_controls.rb +113 -0
- data/examples/{method_based_custom_keyword.rb → method_based_custom_controls.rb} +4 -4
- data/examples/{method_based_custom_keyword2.rb → method_based_custom_controls2.rb} +4 -4
- data/glimmer-dsl-libui.gemspec +0 -0
- data/lib/glimmer/dsl/libui/custom_control_expression.rb +58 -0
- data/lib/glimmer/dsl/libui/dsl.rb +1 -0
- data/lib/glimmer/dsl/libui/property_expression.rb +2 -1
- data/lib/glimmer/libui/control_proxy/path_proxy.rb +5 -1
- data/lib/glimmer/libui/custom_control.rb +249 -0
- data/lib/glimmer/libui/shape/bezier.rb +1 -1
- data/lib/glimmer/libui/shape/figure.rb +12 -0
- data/lib/glimmer/libui/shape/line.rb +1 -1
- data/lib/glimmer/proc_tracker.rb +39 -0
- metadata +26 -8
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'glimmer-dsl-libui'
|
2
|
+
require 'facets'
|
3
|
+
|
4
|
+
include Glimmer
|
5
|
+
|
6
|
+
Address = Struct.new(:street, :p_o_box, :city, :state, :zip_code)
|
7
|
+
|
8
|
+
class FormField
|
9
|
+
include Glimmer::LibUI::CustomControl
|
10
|
+
|
11
|
+
options :model, :attribute
|
12
|
+
|
13
|
+
body {
|
14
|
+
entry { |e|
|
15
|
+
label attribute.to_s.underscore.split('_').map(&:capitalize).join(' ')
|
16
|
+
text <=> [model, attribute]
|
17
|
+
}
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
class AddressForm
|
22
|
+
include Glimmer::LibUI::CustomControl
|
23
|
+
|
24
|
+
options :address
|
25
|
+
|
26
|
+
body {
|
27
|
+
form {
|
28
|
+
form_field(model: address, attribute: :street)
|
29
|
+
form_field(model: address, attribute: :p_o_box)
|
30
|
+
form_field(model: address, attribute: :city)
|
31
|
+
form_field(model: address, attribute: :state)
|
32
|
+
form_field(model: address, attribute: :zip_code)
|
33
|
+
}
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
class LabelPair
|
38
|
+
include Glimmer::LibUI::CustomControl
|
39
|
+
|
40
|
+
options :model, :attribute, :value
|
41
|
+
|
42
|
+
body {
|
43
|
+
horizontal_box {
|
44
|
+
label(attribute.to_s.underscore.split('_').map(&:capitalize).join(' '))
|
45
|
+
label(value.to_s) {
|
46
|
+
text <= [model, attribute]
|
47
|
+
}
|
48
|
+
}
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
class AddressView
|
53
|
+
include Glimmer::LibUI::CustomControl
|
54
|
+
|
55
|
+
options :address
|
56
|
+
|
57
|
+
body {
|
58
|
+
vertical_box {
|
59
|
+
address.each_pair do |attribute, value|
|
60
|
+
label_pair(model: address, attribute: attribute, value: value)
|
61
|
+
end
|
62
|
+
}
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
address1 = Address.new('123 Main St', '23923', 'Denver', 'Colorado', '80014')
|
67
|
+
address2 = Address.new('2038 Park Ave', '83272', 'Boston', 'Massachusetts', '02101')
|
68
|
+
|
69
|
+
window('Class-Based Custom Keyword') {
|
70
|
+
margined true
|
71
|
+
|
72
|
+
horizontal_box {
|
73
|
+
vertical_box {
|
74
|
+
label('Address 1') {
|
75
|
+
stretchy false
|
76
|
+
}
|
77
|
+
|
78
|
+
address_form(address: address1)
|
79
|
+
|
80
|
+
horizontal_separator {
|
81
|
+
stretchy false
|
82
|
+
}
|
83
|
+
|
84
|
+
label('Address 1 (Saved)') {
|
85
|
+
stretchy false
|
86
|
+
}
|
87
|
+
|
88
|
+
address_view(address: address1)
|
89
|
+
}
|
90
|
+
|
91
|
+
vertical_separator {
|
92
|
+
stretchy false
|
93
|
+
}
|
94
|
+
|
95
|
+
vertical_box {
|
96
|
+
label('Address 2') {
|
97
|
+
stretchy false
|
98
|
+
}
|
99
|
+
|
100
|
+
address_form(address: address2)
|
101
|
+
|
102
|
+
horizontal_separator {
|
103
|
+
stretchy false
|
104
|
+
}
|
105
|
+
|
106
|
+
label('Address 2 (Saved)') {
|
107
|
+
stretchy false
|
108
|
+
}
|
109
|
+
|
110
|
+
address_view(address: address2)
|
111
|
+
}
|
112
|
+
}
|
113
|
+
}.show
|
@@ -32,7 +32,7 @@ def label_pair(model, attribute, value)
|
|
32
32
|
}
|
33
33
|
end
|
34
34
|
|
35
|
-
def
|
35
|
+
def address_view(address_model)
|
36
36
|
vertical_box {
|
37
37
|
address_model.each_pair do |attribute, value|
|
38
38
|
label_pair(address_model, attribute, value)
|
@@ -43,7 +43,7 @@ end
|
|
43
43
|
address1 = Address.new('123 Main St', '23923', 'Denver', 'Colorado', '80014')
|
44
44
|
address2 = Address.new('2038 Park Ave', '83272', 'Boston', 'Massachusetts', '02101')
|
45
45
|
|
46
|
-
window('Method-Based Custom
|
46
|
+
window('Method-Based Custom Controls') {
|
47
47
|
margined true
|
48
48
|
|
49
49
|
horizontal_box {
|
@@ -62,7 +62,7 @@ window('Method-Based Custom Keyword') {
|
|
62
62
|
stretchy false
|
63
63
|
}
|
64
64
|
|
65
|
-
|
65
|
+
address_view(address1)
|
66
66
|
}
|
67
67
|
|
68
68
|
vertical_separator {
|
@@ -84,7 +84,7 @@ window('Method-Based Custom Keyword') {
|
|
84
84
|
stretchy false
|
85
85
|
}
|
86
86
|
|
87
|
-
|
87
|
+
address_view(address2)
|
88
88
|
}
|
89
89
|
}
|
90
90
|
}.show
|
@@ -39,7 +39,7 @@ def label_pair(model, attribute, value)
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
-
def
|
42
|
+
def address_view(address_model)
|
43
43
|
vertical_box {
|
44
44
|
address_model.each_pair do |attribute, value|
|
45
45
|
label_pair(address_model, attribute, value)
|
@@ -50,7 +50,7 @@ end
|
|
50
50
|
address1 = Address.new('123 Main St', '23923', 'Denver', 'Colorado', '80014')
|
51
51
|
address2 = Address.new('2038 Park Ave', '83272', 'Boston', 'Massachusetts', '02101')
|
52
52
|
|
53
|
-
window('Method-Based Custom
|
53
|
+
window('Method-Based Custom Controls') {
|
54
54
|
margined true
|
55
55
|
|
56
56
|
horizontal_box {
|
@@ -69,7 +69,7 @@ window('Method-Based Custom Keyword') {
|
|
69
69
|
stretchy false
|
70
70
|
}
|
71
71
|
|
72
|
-
|
72
|
+
address_view(address1)
|
73
73
|
}
|
74
74
|
|
75
75
|
vertical_separator {
|
@@ -91,7 +91,7 @@ window('Method-Based Custom Keyword') {
|
|
91
91
|
stretchy false
|
92
92
|
}
|
93
93
|
|
94
|
-
|
94
|
+
address_view(address2)
|
95
95
|
}
|
96
96
|
}
|
97
97
|
}.show
|
data/glimmer-dsl-libui.gemspec
CHANGED
Binary file
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# Copyright (c) 2021-2022 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_control'
|
27
|
+
|
28
|
+
module Glimmer
|
29
|
+
module DSL
|
30
|
+
module Libui
|
31
|
+
class CustomControlExpression < Expression
|
32
|
+
# TODO Consider making custom controls automatically generate static expressions
|
33
|
+
include ParentExpression
|
34
|
+
include TopLevelExpression
|
35
|
+
|
36
|
+
def can_interpret?(parent, keyword, *args, &block)
|
37
|
+
LibUI::CustomControl.for(keyword)
|
38
|
+
end
|
39
|
+
|
40
|
+
def interpret(parent, keyword, *args, &block)
|
41
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
42
|
+
LibUI::CustomControl.for(keyword).new(keyword, parent, args, options, &block)
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_content(custom_control, 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_control.content&.__getobj__&.source_location
|
49
|
+
custom_control.content.call(custom_control) unless custom_control.content.called?
|
50
|
+
else
|
51
|
+
super
|
52
|
+
end
|
53
|
+
custom_control.post_add_content if options[:post_add_content]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -32,7 +32,8 @@ module Glimmer
|
|
32
32
|
(
|
33
33
|
parent.is_a?(Glimmer::LibUI::ControlProxy) or
|
34
34
|
parent.is_a?(Glimmer::LibUI::Shape) or
|
35
|
-
parent.is_a?(Glimmer::LibUI::AttributedString)
|
35
|
+
parent.is_a?(Glimmer::LibUI::AttributedString) or
|
36
|
+
parent.is_a?(Glimmer::LibUI::CustomControl)
|
36
37
|
) and
|
37
38
|
block.nil? and
|
38
39
|
parent.respond_to?("#{keyword}=", *args)
|
@@ -60,6 +60,10 @@ module Glimmer
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def draw_fill_mode
|
63
|
+
@args[0].is_a?(Integer) ? (@args[0] == 0 ? :winding : :alternate ) : ((@args[0].is_a?(String) || @args[0].is_a?(Symbol)) ? @args[0].to_sym : :winding)
|
64
|
+
end
|
65
|
+
|
66
|
+
def draw_fill_mode_value
|
63
67
|
@args[0].is_a?(Integer) ? @args[0] : @args[0].to_s == 'alternate' ? 1 : 0
|
64
68
|
end
|
65
69
|
|
@@ -159,7 +163,7 @@ module Glimmer
|
|
159
163
|
private
|
160
164
|
|
161
165
|
def build_control
|
162
|
-
@libui = ::LibUI.draw_new_path(
|
166
|
+
@libui = ::LibUI.draw_new_path(draw_fill_mode_value)
|
163
167
|
end
|
164
168
|
|
165
169
|
def init_draw_brush(draw_brush, draw_brush_args)
|
@@ -0,0 +1,249 @@
|
|
1
|
+
# Copyright (c) 2021-2022 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 'super_module'
|
23
|
+
require 'glimmer'
|
24
|
+
require 'glimmer/proc_tracker'
|
25
|
+
require 'glimmer/data_binding/observer'
|
26
|
+
require 'glimmer/data_binding/observable_model'
|
27
|
+
|
28
|
+
module Glimmer
|
29
|
+
module LibUI
|
30
|
+
module CustomControl
|
31
|
+
include SuperModule
|
32
|
+
include DataBinding::ObservableModel
|
33
|
+
|
34
|
+
super_module_included do |klass|
|
35
|
+
# TODO clear memoization of WidgetProxy.libui_class_for for a keyword if a custom control was defined with that keyword
|
36
|
+
klass.include(Glimmer)
|
37
|
+
Glimmer::LibUI::CustomControl.add_custom_control_namespaces_for(klass)
|
38
|
+
end
|
39
|
+
|
40
|
+
class << self
|
41
|
+
def for(keyword)
|
42
|
+
unless flyweight_custom_control_classes.keys.include?(keyword)
|
43
|
+
begin
|
44
|
+
extracted_namespaces = keyword.
|
45
|
+
to_s.
|
46
|
+
split(/__/).map do |namespace|
|
47
|
+
namespace.camelcase(:upper)
|
48
|
+
end
|
49
|
+
custom_control_namespaces.each do |base|
|
50
|
+
extracted_namespaces.reduce(base) do |result, namespace|
|
51
|
+
if !result.constants.include?(namespace)
|
52
|
+
namespace = result.constants.detect {|c| c.to_s.upcase == namespace.to_s.upcase } || namespace
|
53
|
+
end
|
54
|
+
begin
|
55
|
+
flyweight_custom_control_classes[keyword] = constant = result.const_get(namespace)
|
56
|
+
return constant if constant.ancestors.include?(Glimmer::LibUI::CustomControl)
|
57
|
+
flyweight_custom_control_classes[keyword] = constant
|
58
|
+
rescue => e
|
59
|
+
# Glimmer::Config.logger.debug {"#{e.message}\n#{e.backtrace.join("\n")}"}
|
60
|
+
flyweight_custom_control_classes[keyword] = result
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
raise "#{keyword} has no custom control class!"
|
65
|
+
rescue => e
|
66
|
+
Glimmer::Config.logger.debug {e.message}
|
67
|
+
Glimmer::Config.logger.debug {"#{e.message}\n#{e.backtrace.join("\n")}"}
|
68
|
+
flyweight_custom_control_classes[keyword] = nil
|
69
|
+
end
|
70
|
+
end
|
71
|
+
flyweight_custom_control_classes[keyword]
|
72
|
+
end
|
73
|
+
|
74
|
+
# Flyweight Design Pattern memoization cache. Can be cleared if memory is needed.
|
75
|
+
def flyweight_custom_control_classes
|
76
|
+
@flyweight_custom_control_classes ||= {}
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns keyword to use for this custom control
|
80
|
+
def keyword
|
81
|
+
self.name.underscore.gsub('::', '__')
|
82
|
+
end
|
83
|
+
|
84
|
+
# Returns shortcut keyword to use for this custom control (keyword minus namespace)
|
85
|
+
def shortcut_keyword
|
86
|
+
self.name.underscore.gsub('::', '__').split('__').last
|
87
|
+
end
|
88
|
+
|
89
|
+
def add_custom_control_namespaces_for(klass)
|
90
|
+
Glimmer::LibUI::CustomControl.namespaces_for_class(klass).drop(1).each do |namespace|
|
91
|
+
Glimmer::LibUI::CustomControl.custom_control_namespaces << namespace
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def namespaces_for_class(m)
|
96
|
+
return [m] if m.name.nil?
|
97
|
+
namespace_constants = m.name.split(/::/).map(&:to_sym)
|
98
|
+
namespace_constants.reduce([Object]) do |output, namespace_constant|
|
99
|
+
output += [output.last.const_get(namespace_constant)]
|
100
|
+
end[1..-1].uniq.reverse
|
101
|
+
end
|
102
|
+
|
103
|
+
def custom_control_namespaces
|
104
|
+
@custom_control_namespaces ||= reset_custom_control_namespaces
|
105
|
+
end
|
106
|
+
|
107
|
+
def reset_custom_control_namespaces
|
108
|
+
@custom_control_namespaces = Set[Object, Glimmer::LibUI]
|
109
|
+
end
|
110
|
+
|
111
|
+
# Allows defining convenience option accessors for an array of option names
|
112
|
+
# Example: `options :color1, :color2` defines `#color1` and `#color2`
|
113
|
+
# where they return the instance values `options[:color1]` and `options[:color2]`
|
114
|
+
# respectively.
|
115
|
+
# Can be called multiple times to set more options additively.
|
116
|
+
# When passed no arguments, it returns list of all option names captured so far
|
117
|
+
def options(*new_options)
|
118
|
+
new_options = new_options.compact.map(&:to_s).map(&:to_sym)
|
119
|
+
if new_options.empty?
|
120
|
+
@options ||= {} # maps options to defaults
|
121
|
+
else
|
122
|
+
new_options = new_options.reduce({}) {|new_options_hash, new_option| new_options_hash.merge(new_option => nil)}
|
123
|
+
@options = options.merge(new_options)
|
124
|
+
def_option_attr_accessors(new_options)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def option(new_option, default: nil)
|
129
|
+
new_option = new_option.to_s.to_sym
|
130
|
+
new_options = {new_option => default}
|
131
|
+
@options = options.merge(new_options)
|
132
|
+
def_option_attr_accessors(new_options)
|
133
|
+
end
|
134
|
+
|
135
|
+
def def_option_attr_accessors(new_options)
|
136
|
+
new_options.each do |option, default|
|
137
|
+
class_eval <<-end_eval, __FILE__, __LINE__
|
138
|
+
def #{option}
|
139
|
+
options[:#{option}]
|
140
|
+
end
|
141
|
+
|
142
|
+
def #{option}=(option_value)
|
143
|
+
self.options[:#{option}] = option_value
|
144
|
+
end
|
145
|
+
end_eval
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def before_body(&block)
|
150
|
+
@before_body_block = block
|
151
|
+
end
|
152
|
+
|
153
|
+
def body(&block)
|
154
|
+
@body_block = block
|
155
|
+
end
|
156
|
+
|
157
|
+
def after_body(&block)
|
158
|
+
@after_body_block = block
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
attr_reader :body_root, :libui, :parent, :parent_proxy, :args, :keyword, :content, :options
|
163
|
+
|
164
|
+
def initialize(keyword, parent, args, options, &content)
|
165
|
+
@parent_proxy = @parent = parent
|
166
|
+
options ||= {}
|
167
|
+
@options = self.class.options.merge(options)
|
168
|
+
@content = ProcTracker.new(content) if content
|
169
|
+
execute_hook('before_body')
|
170
|
+
body_block = self.class.instance_variable_get("@body_block")
|
171
|
+
raise Glimmer::Error, 'Invalid custom control for having no body! Please define body block!' if body_block.nil?
|
172
|
+
@body_root = instance_exec(&body_block)
|
173
|
+
raise Glimmer::Error, 'Invalid custom control for having an empty body! Please fill body block!' if @body_root.nil?
|
174
|
+
@libui = @body_root.libui
|
175
|
+
execute_hook('after_body')
|
176
|
+
# TODO deregister all observer_registrations on destroy of the control once that listener is supported
|
177
|
+
# (on_destroy) unless it is the last window closing, in which case exit faster
|
178
|
+
post_add_content if content.nil?
|
179
|
+
end
|
180
|
+
|
181
|
+
# Subclasses may override to perform post initialization work on an added child
|
182
|
+
def post_initialize_child(child)
|
183
|
+
# No Op by default
|
184
|
+
end
|
185
|
+
|
186
|
+
def post_add_content
|
187
|
+
# No Op by default
|
188
|
+
end
|
189
|
+
|
190
|
+
def observer_registrations
|
191
|
+
@observer_registrations ||= []
|
192
|
+
end
|
193
|
+
|
194
|
+
def can_handle_listener?(listener)
|
195
|
+
body_root&.can_handle_listener?(listener.to_s)
|
196
|
+
end
|
197
|
+
|
198
|
+
def handle_listener(listener, &block)
|
199
|
+
body_root.handle_listener(listener.to_s, &block)
|
200
|
+
end
|
201
|
+
|
202
|
+
# This method ensures it has an instance method not coming from Glimmer DSL
|
203
|
+
def has_instance_method?(method_name)
|
204
|
+
respond_to?(method_name) and
|
205
|
+
!@body_root.respond_to_libui?(method_name) and
|
206
|
+
(method(method_name) rescue nil) and
|
207
|
+
!method(method_name)&.source_location&.first&.include?('glimmer/dsl/engine.rb') and
|
208
|
+
!method(method_name)&.source_location&.first&.include?('glimmer/libui/control_proxy.rb')
|
209
|
+
end
|
210
|
+
|
211
|
+
# Returns content block if used as an attribute reader (no args)
|
212
|
+
# Otherwise, if a block is passed, it adds it as content to this custom control
|
213
|
+
def content(&block)
|
214
|
+
if block_given?
|
215
|
+
Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::Libui::CustomControlExpression.new, self.class.keyword, &block)
|
216
|
+
else
|
217
|
+
@content
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def method_missing(method_name, *args, &block)
|
222
|
+
# TODO Consider supporting a glimmer error silencing option for methods defined here
|
223
|
+
# but fail the glimmer DSL for the right reason to avoid seeing noise in the log output
|
224
|
+
if block && can_handle_listener?(method_name)
|
225
|
+
handle_listener(method_name, &block)
|
226
|
+
else
|
227
|
+
@body_root.send(method_name, *args, &block)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def respond_to?(method_name, *args, &block)
|
232
|
+
super or
|
233
|
+
can_handle_listener?(method_name) or
|
234
|
+
@body_root.respond_to?(method_name, *args, &block)
|
235
|
+
end
|
236
|
+
|
237
|
+
private
|
238
|
+
|
239
|
+
def execute_hook(hook_name)
|
240
|
+
hook_block = self.class.instance_variable_get("@#{hook_name}_block")
|
241
|
+
return if hook_block.nil?
|
242
|
+
temp_method_name = "#{hook_name}_block_#{hook_block.hash.abs}_#{(Time.now.to_f * 1_000_000).to_i}"
|
243
|
+
singleton_class.define_method(temp_method_name, &hook_block)
|
244
|
+
send(temp_method_name)
|
245
|
+
singleton_class.send(:remove_method, temp_method_name)
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
@@ -54,7 +54,7 @@ module Glimmer
|
|
54
54
|
perfect_shape_dependencies = [x, y, c1_x, c1_y, c2_x, c2_y, end_x, end_y]
|
55
55
|
if perfect_shape_dependencies != @perfect_shape_dependencies
|
56
56
|
x, y, c1_x, c1_y, c2_x, c2_y, end_x, end_y = @perfect_shape_dependencies = perfect_shape_dependencies
|
57
|
-
@perfect_shape = PerfectShape::CubicBezierCurve.new(points: [
|
57
|
+
@perfect_shape = PerfectShape::CubicBezierCurve.new(points: [x, y, c1_x, c1_y, c2_x, c2_y, end_x, end_y].compact)
|
58
58
|
end
|
59
59
|
@perfect_shape
|
60
60
|
end
|
@@ -50,6 +50,18 @@ module Glimmer
|
|
50
50
|
alias closed= closed
|
51
51
|
alias set_closed closed
|
52
52
|
alias closed? closed
|
53
|
+
|
54
|
+
def perfect_shape
|
55
|
+
perfect_shape_dependencies = [x, y, closed, parent.draw_fill_mode, children]
|
56
|
+
if perfect_shape_dependencies != @perfect_shape_dependencies
|
57
|
+
x, y, closed, draw_fill_mode, children = @perfect_shape_dependencies = perfect_shape_dependencies
|
58
|
+
path_shapes = [[x, y]]
|
59
|
+
path_shapes += children.map(&:perfect_shape)
|
60
|
+
winding_rule = draw_fill_mode == :winding ? :wind_non_zero : :wind_even_odd
|
61
|
+
@perfect_shape = PerfectShape::Path.new(closed: closed, winding_rule: winding_rule, shapes: path_shapes)
|
62
|
+
end
|
63
|
+
@perfect_shape
|
64
|
+
end
|
53
65
|
end
|
54
66
|
end
|
55
67
|
end
|
@@ -57,7 +57,7 @@ module Glimmer
|
|
57
57
|
perfect_shape_dependencies = [x, y, end_x, end_y]
|
58
58
|
if perfect_shape_dependencies != @perfect_shape_dependencies
|
59
59
|
x, y, end_x, end_y = @perfect_shape_dependencies = perfect_shape_dependencies
|
60
|
-
@perfect_shape = PerfectShape::Line.new(points: [
|
60
|
+
@perfect_shape = PerfectShape::Line.new(points: [x, y, end_x, end_y].compact)
|
61
61
|
end
|
62
62
|
@perfect_shape
|
63
63
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# Copyright (c) 2007-2022 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 'delegate'
|
23
|
+
|
24
|
+
module Glimmer
|
25
|
+
class ProcTracker < DelegateClass(Proc)
|
26
|
+
def initialize(proc)
|
27
|
+
super(proc)
|
28
|
+
end
|
29
|
+
|
30
|
+
def call(*args)
|
31
|
+
__getobj__.call(*args)
|
32
|
+
@called = true
|
33
|
+
end
|
34
|
+
|
35
|
+
def called?
|
36
|
+
!!@called
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|