glimmer-dsl-libui 0.9.7 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +392 -216
- data/VERSION +1 -1
- data/docs/examples/GLIMMER-DSL-LIBUI-ADVANCED-EXAMPLES.md +5 -5
- data/docs/examples/GLIMMER-DSL-LIBUI-BASIC-EXAMPLES.md +23 -0
- data/examples/basic_composite_shape.rb +2 -0
- data/examples/basic_custom_shape.rb +168 -0
- data/glimmer-dsl-libui.gemspec +0 -0
- data/lib/glimmer/dsl/libui/custom_shape_expression.rb +58 -0
- data/lib/glimmer/dsl/libui/dsl.rb +1 -0
- data/lib/glimmer/dsl/libui/listener_expression.rb +6 -1
- data/lib/glimmer/launcher.rb +2 -5
- data/lib/glimmer/libui/custom_control.rb +25 -19
- data/lib/glimmer/libui/custom_shape.rb +258 -0
- data/lib/glimmer/rake_task/scaffold.rb +1 -2
- data/lib/glimmer-dsl-libui.rb +1 -1
- metadata +5 -2
@@ -0,0 +1,258 @@
|
|
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 'super_module'
|
23
|
+
require 'glimmer'
|
24
|
+
require 'glimmer/error'
|
25
|
+
require 'glimmer/proc_tracker'
|
26
|
+
require 'glimmer/data_binding/observer'
|
27
|
+
require 'glimmer/data_binding/observable_model'
|
28
|
+
|
29
|
+
module Glimmer
|
30
|
+
module LibUI
|
31
|
+
module CustomShape
|
32
|
+
include SuperModule
|
33
|
+
include DataBinding::ObservableModel
|
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
|
+
|
57
|
+
super_module_included do |klass|
|
58
|
+
# TODO clear memoization of Shape.libui_class_for for a keyword if a custom shape was defined with that keyword
|
59
|
+
klass.include(Glimmer)
|
60
|
+
klass.include(GlimmerSupersedable) # prevent Glimmer from running method_missing first
|
61
|
+
Glimmer::LibUI::CustomShape.add_custom_shape_namespaces_for(klass)
|
62
|
+
end
|
63
|
+
|
64
|
+
class << self
|
65
|
+
def for(keyword)
|
66
|
+
unless flyweight_custom_shape_classes.keys.include?(keyword)
|
67
|
+
begin
|
68
|
+
extracted_namespaces = keyword.
|
69
|
+
to_s.
|
70
|
+
split(/__/).map do |namespace|
|
71
|
+
namespace.camelcase(:upper)
|
72
|
+
end
|
73
|
+
custom_shape_namespaces.each do |base|
|
74
|
+
extracted_namespaces.reduce(base) do |result, namespace|
|
75
|
+
if !result.constants.include?(namespace)
|
76
|
+
namespace = result.constants.detect {|c| c.to_s.upcase == namespace.to_s.upcase } || namespace
|
77
|
+
end
|
78
|
+
begin
|
79
|
+
flyweight_custom_shape_classes[keyword] = constant = result.const_get(namespace)
|
80
|
+
return constant if constant.ancestors.include?(Glimmer::LibUI::CustomShape)
|
81
|
+
flyweight_custom_shape_classes[keyword] = constant
|
82
|
+
rescue => e
|
83
|
+
# Glimmer::Config.logger.debug {"#{e.message}\n#{e.backtrace.join("\n")}"}
|
84
|
+
flyweight_custom_shape_classes[keyword] = result
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
raise "#{keyword} has no custom shape class!"
|
89
|
+
rescue => e
|
90
|
+
Glimmer::Config.logger.debug {e.message}
|
91
|
+
Glimmer::Config.logger.debug {"#{e.message}\n#{e.backtrace.join("\n")}"}
|
92
|
+
flyweight_custom_shape_classes[keyword] = nil
|
93
|
+
end
|
94
|
+
end
|
95
|
+
flyweight_custom_shape_classes[keyword]
|
96
|
+
end
|
97
|
+
|
98
|
+
# Flyweight Design Pattern memoization cache. Can be cleared if memory is needed.
|
99
|
+
def flyweight_custom_shape_classes
|
100
|
+
@flyweight_custom_shape_classes ||= {}
|
101
|
+
end
|
102
|
+
|
103
|
+
# Returns keyword to use for this custom shape
|
104
|
+
def keyword
|
105
|
+
self.name.underscore.gsub('::', '__')
|
106
|
+
end
|
107
|
+
|
108
|
+
# Returns shortcut keyword to use for this custom shape (keyword minus namespace)
|
109
|
+
def shortcut_keyword
|
110
|
+
self.name.underscore.gsub('::', '__').split('__').last
|
111
|
+
end
|
112
|
+
|
113
|
+
def add_custom_shape_namespaces_for(klass)
|
114
|
+
Glimmer::LibUI::CustomShape.namespaces_for_class(klass).drop(1).each do |namespace|
|
115
|
+
Glimmer::LibUI::CustomShape.custom_shape_namespaces << namespace
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def namespaces_for_class(m)
|
120
|
+
return [m] if m.name.nil?
|
121
|
+
namespace_constants = m.name.split(/::/).map(&:to_sym)
|
122
|
+
namespace_constants.reduce([Object]) do |output, namespace_constant|
|
123
|
+
output += [output.last.const_get(namespace_constant)]
|
124
|
+
end[1..-1].uniq.reverse
|
125
|
+
end
|
126
|
+
|
127
|
+
def custom_shape_namespaces
|
128
|
+
@custom_shape_namespaces ||= reset_custom_shape_namespaces
|
129
|
+
end
|
130
|
+
|
131
|
+
def reset_custom_shape_namespaces
|
132
|
+
@custom_shape_namespaces = Set[Object, Glimmer::LibUI]
|
133
|
+
end
|
134
|
+
|
135
|
+
# Allows defining convenience option accessors for an array of option names
|
136
|
+
# Example: `options :color1, :color2` defines `#color1` and `#color2`
|
137
|
+
# where they return the instance values `options[:color1]` and `options[:color2]`
|
138
|
+
# respectively.
|
139
|
+
# Can be called multiple times to set more options additively.
|
140
|
+
# When passed no arguments, it returns list of all option names captured so far
|
141
|
+
def options(*new_options)
|
142
|
+
new_options = new_options.compact.map(&:to_s).map(&:to_sym)
|
143
|
+
if new_options.empty?
|
144
|
+
@options ||= {} # maps options to defaults
|
145
|
+
else
|
146
|
+
new_options = new_options.reduce({}) {|new_options_hash, new_option| new_options_hash.merge(new_option => nil)}
|
147
|
+
@options = options.merge(new_options)
|
148
|
+
def_option_attr_accessors(new_options)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def option(new_option, default: nil)
|
153
|
+
new_option = new_option.to_s.to_sym
|
154
|
+
new_options = {new_option => default}
|
155
|
+
@options = options.merge(new_options)
|
156
|
+
def_option_attr_accessors(new_options)
|
157
|
+
end
|
158
|
+
|
159
|
+
def def_option_attr_accessors(new_options)
|
160
|
+
new_options.each do |option, default|
|
161
|
+
class_eval <<-end_eval, __FILE__, __LINE__
|
162
|
+
def #{option}
|
163
|
+
options[:#{option}]
|
164
|
+
end
|
165
|
+
|
166
|
+
def #{option}=(option_value)
|
167
|
+
self.options[:#{option}] = option_value
|
168
|
+
end
|
169
|
+
end_eval
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def before_body(&block)
|
174
|
+
@before_body_block = block
|
175
|
+
end
|
176
|
+
|
177
|
+
def body(&block)
|
178
|
+
@body_block = block
|
179
|
+
end
|
180
|
+
|
181
|
+
def after_body(&block)
|
182
|
+
@after_body_block = block
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
attr_reader :body_root, :parent, :parent_proxy, :args, :keyword, :content, :options
|
187
|
+
|
188
|
+
def initialize(keyword, parent, args, options, &content)
|
189
|
+
@parent_proxy = @parent = parent
|
190
|
+
options ||= {}
|
191
|
+
@options = self.class.options.merge(options)
|
192
|
+
@content = ProcTracker.new(content) if content
|
193
|
+
execute_hook('before_body')
|
194
|
+
body_block = self.class.instance_variable_get("@body_block")
|
195
|
+
raise Glimmer::Error, 'Invalid custom shape for having no body! Please define body block!' if body_block.nil?
|
196
|
+
@body_root = instance_exec(&body_block)
|
197
|
+
raise Glimmer::Error, 'Invalid custom shape for having an empty body! Please fill body block!' if @body_root.nil?
|
198
|
+
execute_hook('after_body')
|
199
|
+
# TODO deregister all observer_registrations on destroy of the shape once that listener is supported
|
200
|
+
# (on_destroy) unless it is the last window closing, in which case exit faster
|
201
|
+
post_add_content if content.nil?
|
202
|
+
end
|
203
|
+
|
204
|
+
# Subclasses may override to perform post initialization work on an added child
|
205
|
+
def post_initialize_child(child)
|
206
|
+
# No Op by default
|
207
|
+
end
|
208
|
+
|
209
|
+
def post_add_content
|
210
|
+
# No Op by default
|
211
|
+
end
|
212
|
+
|
213
|
+
def observer_registrations
|
214
|
+
@observer_registrations ||= []
|
215
|
+
end
|
216
|
+
|
217
|
+
def can_handle_listener?(listener)
|
218
|
+
body_root&.can_handle_listener?(listener.to_s)
|
219
|
+
end
|
220
|
+
|
221
|
+
def handle_listener(listener, &block)
|
222
|
+
body_root.handle_listener(listener.to_s, &block)
|
223
|
+
end
|
224
|
+
|
225
|
+
# This method ensures it has an instance method not coming from Glimmer DSL
|
226
|
+
def has_instance_method?(method_name)
|
227
|
+
respond_to?(method_name) and
|
228
|
+
!@body_root.respond_to_libui?(method_name) and
|
229
|
+
(method(method_name) rescue nil) and
|
230
|
+
!method(method_name)&.source_location&.first&.include?('glimmer/dsl/engine.rb') and
|
231
|
+
!method(method_name)&.source_location&.first&.include?('glimmer/libui/shape.rb')
|
232
|
+
end
|
233
|
+
|
234
|
+
# Returns content block if used as an attribute reader (no args)
|
235
|
+
# Otherwise, if a block is passed, it adds it as content to this custom shape
|
236
|
+
def content(&block)
|
237
|
+
if block_given?
|
238
|
+
Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::Libui::CustomShapeExpression.new, self.class.keyword, &block)
|
239
|
+
else
|
240
|
+
@content
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
private
|
245
|
+
|
246
|
+
def execute_hook(hook_name)
|
247
|
+
hook_block = self.class.instance_variable_get("@#{hook_name}_block")
|
248
|
+
return if hook_block.nil?
|
249
|
+
temp_method_name = "#{hook_name}_block_#{hook_block.hash.abs}_#{(Time.now.to_f * 1_000_000).to_i}"
|
250
|
+
singleton_class.define_method(temp_method_name, &hook_block)
|
251
|
+
send(temp_method_name)
|
252
|
+
singleton_class.send(:remove_method, temp_method_name)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
Dir[File.expand_path("./#{File.basename(__FILE__, '.rb')}/*.rb", __dir__)].each {|f| require f}
|
@@ -432,8 +432,6 @@ module Glimmer
|
|
432
432
|
end
|
433
433
|
|
434
434
|
class #{class_name(app_name)}
|
435
|
-
include Glimmer
|
436
|
-
|
437
435
|
APP_ROOT = File.expand_path('../..', __FILE__)
|
438
436
|
VERSION = File.read(File.join(APP_ROOT, 'VERSION'))
|
439
437
|
LICENSE = File.read(File.join(APP_ROOT, 'LICENSE.txt'))
|
@@ -835,6 +833,7 @@ end
|
|
835
833
|
namespace_type = class_name(namespace) == class_name(current_dir_name) ? 'class' : 'module'
|
836
834
|
|
837
835
|
<<-MULTI_LINE_STRING
|
836
|
+
# Delete this example model and replace with your own model
|
838
837
|
#{namespace_type} #{class_name(namespace)}
|
839
838
|
module Model
|
840
839
|
class #{class_name(model_name)}
|
data/lib/glimmer-dsl-libui.rb
CHANGED
@@ -25,7 +25,7 @@ $LOAD_PATH.unshift(File.expand_path('..', __FILE__))
|
|
25
25
|
require 'glimmer'
|
26
26
|
require 'perfect-shape'
|
27
27
|
# require 'logging'
|
28
|
-
|
28
|
+
require 'puts_debuggerer' if (ENV['PD'] || ENV['pd']).to_s.downcase == 'true'
|
29
29
|
# require 'super_module'
|
30
30
|
require 'color'
|
31
31
|
require 'os'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: glimmer-dsl-libui
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andy Maleh
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-10-
|
11
|
+
date: 2023-10-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: glimmer
|
@@ -389,6 +389,7 @@ files:
|
|
389
389
|
- examples/basic_child_window.rb
|
390
390
|
- examples/basic_code_area.rb
|
391
391
|
- examples/basic_composite_shape.rb
|
392
|
+
- examples/basic_custom_shape.rb
|
392
393
|
- examples/basic_draw_text.rb
|
393
394
|
- examples/basic_draw_text2.rb
|
394
395
|
- examples/basic_entry.rb
|
@@ -501,6 +502,7 @@ files:
|
|
501
502
|
- lib/glimmer/dsl/libui/bind_expression.rb
|
502
503
|
- lib/glimmer/dsl/libui/control_expression.rb
|
503
504
|
- lib/glimmer/dsl/libui/custom_control_expression.rb
|
505
|
+
- lib/glimmer/dsl/libui/custom_shape_expression.rb
|
504
506
|
- lib/glimmer/dsl/libui/data_binding_expression.rb
|
505
507
|
- lib/glimmer/dsl/libui/dsl.rb
|
506
508
|
- lib/glimmer/dsl/libui/file_expression.rb
|
@@ -587,6 +589,7 @@ files:
|
|
587
589
|
- lib/glimmer/libui/custom_control.rb
|
588
590
|
- lib/glimmer/libui/custom_control/code_area.rb
|
589
591
|
- lib/glimmer/libui/custom_control/refined_table.rb
|
592
|
+
- lib/glimmer/libui/custom_shape.rb
|
590
593
|
- lib/glimmer/libui/custom_window.rb
|
591
594
|
- lib/glimmer/libui/data_bindable.rb
|
592
595
|
- lib/glimmer/libui/image_path_renderer.rb
|