glimmer-dsl-wx 0.0.2
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 +7 -0
- data/CHANGELOG.md +9 -0
- data/LICENSE.txt +20 -0
- data/README.md +120 -0
- data/VERSION +1 -0
- data/bin/girb +30 -0
- data/bin/girb_runner.rb +47 -0
- data/glimmer-dsl-wx.gemspec +0 -0
- data/icons/glimmer.png +0 -0
- data/lib/glimmer/dsl/wx/bind_expression.rb +36 -0
- data/lib/glimmer/dsl/wx/control_expression.rb +63 -0
- data/lib/glimmer/dsl/wx/data_binding_expression.rb +45 -0
- data/lib/glimmer/dsl/wx/dsl.rb +50 -0
- data/lib/glimmer/dsl/wx/listener_expression.rb +41 -0
- data/lib/glimmer/dsl/wx/observe_expression.rb +35 -0
- data/lib/glimmer/dsl/wx/operation_expression.rb +47 -0
- data/lib/glimmer/dsl/wx/property_expression.rb +48 -0
- data/lib/glimmer/dsl/wx/shine_data_binding_expression.rb +42 -0
- data/lib/glimmer/proc_tracker.rb +39 -0
- data/lib/glimmer/wx/control_proxy.rb +409 -0
- data/lib/glimmer/wx/data_bindable.rb +69 -0
- data/lib/glimmer/wx/parent.rb +36 -0
- data/lib/glimmer-dsl-wx/ext/glimmer.rb +7 -0
- data/lib/glimmer-dsl-wx.rb +53 -0
- data/samples/art/wxruby-128x128.png +0 -0
- data/samples/art/wxruby-256x256.png +0 -0
- data/samples/art/wxruby-64x64.png +0 -0
- data/samples/art/wxruby.ico +0 -0
- data/samples/art/wxruby.png +0 -0
- data/samples/minimal/mondrian.ico +0 -0
- data/samples/minimal/mondrian.png +0 -0
- data/samples/minimal/nothing.rb +24 -0
- data/samples/minimal/tn_minimal.png +0 -0
- data/samples/minimal/tn_nothing.png +0 -0
- metadata +280 -0
@@ -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
|
@@ -0,0 +1,409 @@
|
|
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/libui/data_bindable'
|
23
|
+
|
24
|
+
module Glimmer
|
25
|
+
module LibUI
|
26
|
+
# Proxy for LibUI control objects
|
27
|
+
#
|
28
|
+
# Follows the Proxy Design Pattern
|
29
|
+
class ControlProxy
|
30
|
+
class << self
|
31
|
+
def exists?(keyword)
|
32
|
+
::LibUI.respond_to?("new_#{keyword}") or
|
33
|
+
::LibUI.respond_to?(keyword) or
|
34
|
+
descendant_keyword_constant_map.keys.include?(keyword)
|
35
|
+
end
|
36
|
+
|
37
|
+
def create(keyword, parent, args, &block)
|
38
|
+
widget_proxy_class(keyword).new(keyword, parent, args, &block).tap {|c| control_proxies << c}
|
39
|
+
end
|
40
|
+
|
41
|
+
def widget_proxy_class(keyword)
|
42
|
+
descendant_keyword_constant_map[keyword] || ControlProxy
|
43
|
+
end
|
44
|
+
|
45
|
+
# autosave all controls in this array to avoid garbage collection
|
46
|
+
def control_proxies
|
47
|
+
@@control_proxies = [] unless defined?(@@control_proxies)
|
48
|
+
@@control_proxies
|
49
|
+
end
|
50
|
+
|
51
|
+
def main_window_proxy
|
52
|
+
control_proxies.find {|c| c.is_a?(WindowProxy)}
|
53
|
+
end
|
54
|
+
|
55
|
+
def menu_proxies
|
56
|
+
control_proxies.select {|c| c.keyword == 'menu' }
|
57
|
+
end
|
58
|
+
|
59
|
+
def image_proxies
|
60
|
+
control_proxies.select {|c| c.keyword == 'image' }
|
61
|
+
end
|
62
|
+
|
63
|
+
def new_control(keyword, args)
|
64
|
+
::LibUI.send("new_#{keyword}", *args)
|
65
|
+
end
|
66
|
+
|
67
|
+
def constant_symbol(keyword)
|
68
|
+
"#{keyword.camelcase(:upper)}Proxy".to_sym
|
69
|
+
end
|
70
|
+
|
71
|
+
def keyword(constant_symbol)
|
72
|
+
constant_symbol.to_s.underscore.sub(/_proxy$/, '')
|
73
|
+
end
|
74
|
+
|
75
|
+
def descendant_keyword_constant_map
|
76
|
+
@descendant_keyword_constant_map ||= add_aliases_to_keyword_constant_map(map_descendant_keyword_constants_for(self))
|
77
|
+
end
|
78
|
+
|
79
|
+
def reset_descendant_keyword_constant_map
|
80
|
+
@descendant_keyword_constant_map = nil
|
81
|
+
descendant_keyword_constant_map
|
82
|
+
end
|
83
|
+
|
84
|
+
def map_descendant_keyword_constants_for(klass, accumulator: {})
|
85
|
+
klass.constants.map do |constant_symbol|
|
86
|
+
[constant_symbol, klass.const_get(constant_symbol)]
|
87
|
+
end.select do |constant_symbol, constant|
|
88
|
+
constant.is_a?(Module) && !accumulator.values.include?(constant)
|
89
|
+
end.each do |constant_symbol, constant|
|
90
|
+
accumulator[keyword(constant_symbol)] = constant
|
91
|
+
map_descendant_keyword_constants_for(constant, accumulator: accumulator)
|
92
|
+
end
|
93
|
+
accumulator
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
def add_aliases_to_keyword_constant_map(keyword_constant_map)
|
99
|
+
KEYWORD_ALIASES.each do |keyword, alias_keyword|
|
100
|
+
keyword_constant_map[alias_keyword] = keyword_constant_map[keyword]
|
101
|
+
end
|
102
|
+
keyword_constant_map
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
include DataBindable
|
107
|
+
|
108
|
+
KEYWORD_ALIASES = {
|
109
|
+
'msg_box' => 'message_box',
|
110
|
+
'msg_box_error' => 'message_box_error',
|
111
|
+
}
|
112
|
+
|
113
|
+
BOOLEAN_PROPERTIES = %w[
|
114
|
+
padded
|
115
|
+
checked
|
116
|
+
enabled toplevel visible
|
117
|
+
read_only
|
118
|
+
margined
|
119
|
+
borderless fullscreen
|
120
|
+
stretchy
|
121
|
+
]
|
122
|
+
|
123
|
+
STRING_PROPERTIES = %w[
|
124
|
+
text
|
125
|
+
title
|
126
|
+
]
|
127
|
+
|
128
|
+
# libui returns the contained LibUI object
|
129
|
+
attr_reader :parent_proxy, :libui, :args, :keyword, :block, :content_added
|
130
|
+
alias content_added? content_added
|
131
|
+
|
132
|
+
def initialize(keyword, parent, args, &block)
|
133
|
+
@keyword = keyword
|
134
|
+
@parent_proxy = parent
|
135
|
+
@args = args
|
136
|
+
@block = block
|
137
|
+
@enabled = true
|
138
|
+
build_control
|
139
|
+
post_add_content if @block.nil?
|
140
|
+
end
|
141
|
+
|
142
|
+
# Subclasses may override to perform post add_content work (normally must call super)
|
143
|
+
def post_add_content
|
144
|
+
unless @content_added
|
145
|
+
@parent_proxy&.post_initialize_child(self)
|
146
|
+
@content_added = true
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# Subclasses may override to perform post initialization work on an added child
|
151
|
+
def post_initialize_child(child)
|
152
|
+
# No Op by default
|
153
|
+
end
|
154
|
+
|
155
|
+
def window_proxy
|
156
|
+
found_proxy = self
|
157
|
+
until found_proxy.nil? || found_proxy.is_a?(WindowProxy)
|
158
|
+
found_proxy = found_proxy.parent_proxy
|
159
|
+
end
|
160
|
+
found_proxy
|
161
|
+
end
|
162
|
+
|
163
|
+
def can_handle_listener?(listener_name)
|
164
|
+
::LibUI.respond_to?("#{libui_api_keyword}_#{listener_name}") ||
|
165
|
+
::LibUI.respond_to?("control_#{listener_name}") ||
|
166
|
+
has_custom_listener?(listener_name)
|
167
|
+
end
|
168
|
+
|
169
|
+
def handle_listener(listener_name, &listener)
|
170
|
+
# replace first listener argument (control libui pointer) with actual Ruby libui object
|
171
|
+
safe_listener = Proc.new { |*args| listener.call(self, *args[1..-1]) }
|
172
|
+
if ::LibUI.respond_to?("#{libui_api_keyword}_#{listener_name}")
|
173
|
+
if listeners[listener_name].nil?
|
174
|
+
::LibUI.send("#{libui_api_keyword}_#{listener_name}", libui) do |*args|
|
175
|
+
listeners_for(listener_name).map { |listener| listener.call(*args) }.last
|
176
|
+
end
|
177
|
+
end
|
178
|
+
listeners_for(listener_name) << safe_listener
|
179
|
+
elsif ::LibUI.respond_to?("control_#{listener_name}")
|
180
|
+
if listeners[listener_name].nil?
|
181
|
+
::LibUI.send("control_#{listener_name}", libui) do |*args|
|
182
|
+
listeners_for(listener_name).map { |listener| listener.call(*args) }.last
|
183
|
+
end
|
184
|
+
end
|
185
|
+
listeners_for(listener_name) << safe_listener
|
186
|
+
elsif has_custom_listener?(listener_name)
|
187
|
+
handle_custom_listener(listener_name, &listener)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def listeners
|
192
|
+
@listeners ||= {}
|
193
|
+
end
|
194
|
+
|
195
|
+
def listeners_for(listener_name)
|
196
|
+
listeners[listener_name] ||= []
|
197
|
+
end
|
198
|
+
|
199
|
+
def has_custom_listener?(listener_name)
|
200
|
+
listener_name = listener_name.to_s
|
201
|
+
custom_listener_names.include?(listener_name) || custom_listener_name_aliases.stringify_keys.keys.include?(listener_name)
|
202
|
+
end
|
203
|
+
|
204
|
+
def custom_listener_names
|
205
|
+
self.class.constants.include?(:CUSTOM_LISTENER_NAMES) ? self.class::CUSTOM_LISTENER_NAMES : []
|
206
|
+
end
|
207
|
+
|
208
|
+
def custom_listener_name_aliases
|
209
|
+
self.class.constants.include?(:CUSTOM_LISTENER_NAME_ALIASES) ? self.class::CUSTOM_LISTENER_NAME_ALIASES : {}
|
210
|
+
end
|
211
|
+
|
212
|
+
def handle_custom_listener(listener_name, &listener)
|
213
|
+
listener_name = listener_name.to_s
|
214
|
+
listener_name = custom_listener_name_aliases.stringify_keys[listener_name] || listener_name
|
215
|
+
instance_variable_name = "@#{listener_name}_procs" # TODO ensure clearing custom listeners on destroy of a control
|
216
|
+
instance_variable_set(instance_variable_name, []) if instance_variable_get(instance_variable_name).nil?
|
217
|
+
if listener.nil?
|
218
|
+
instance_variable_get(instance_variable_name)
|
219
|
+
else
|
220
|
+
instance_variable_get(instance_variable_name) << listener
|
221
|
+
listener
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def notify_custom_listeners(listener_name, *args)
|
226
|
+
handle_custom_listener(listener_name).each do |listener|
|
227
|
+
listener.call(*args)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def deregister_custom_listeners(listener_name)
|
232
|
+
handle_custom_listener(listener_name).clear
|
233
|
+
end
|
234
|
+
|
235
|
+
# deregisters all custom listeners except on_destroy, which can only be deregistered after destruction of a control, using deregister_custom_listeners
|
236
|
+
def deregister_all_custom_listeners
|
237
|
+
(custom_listener_names - ['on_destroy']).each { |listener_name| deregister_custom_listeners(listener_name) }
|
238
|
+
end
|
239
|
+
|
240
|
+
def respond_to?(method_name, *args, &block)
|
241
|
+
respond_to_libui?(method_name, *args, &block) ||
|
242
|
+
(
|
243
|
+
append_properties.include?(method_name.to_s) ||
|
244
|
+
(append_properties.include?(method_name.to_s.sub(/\?$/, '')) && BOOLEAN_PROPERTIES.include?(method_name.to_s.sub(/\?$/, ''))) ||
|
245
|
+
append_properties.include?(method_name.to_s.sub(/=$/, ''))
|
246
|
+
) ||
|
247
|
+
can_handle_listener?(method_name.to_s) ||
|
248
|
+
super(method_name, true)
|
249
|
+
end
|
250
|
+
|
251
|
+
def respond_to_libui?(method_name, *args, &block)
|
252
|
+
::LibUI.respond_to?("control_#{method_name}") or
|
253
|
+
(::LibUI.respond_to?("control_#{method_name.to_s.sub(/\?$/, '')}") && BOOLEAN_PROPERTIES.include?(method_name.to_s.sub(/\?$/, '')) ) or
|
254
|
+
::LibUI.respond_to?("control_set_#{method_name.to_s.sub(/=$/, '')}") or
|
255
|
+
::LibUI.respond_to?("#{libui_api_keyword}_#{method_name}") or
|
256
|
+
(::LibUI.respond_to?("#{libui_api_keyword}_#{method_name.to_s.sub(/\?$/, '')}") && BOOLEAN_PROPERTIES.include?(method_name.to_s.sub(/\?$/, '')) ) or
|
257
|
+
::LibUI.respond_to?("#{libui_api_keyword}_set_#{method_name.to_s.sub(/=$/, '')}")
|
258
|
+
end
|
259
|
+
|
260
|
+
def method_missing(method_name, *args, &block)
|
261
|
+
if respond_to_libui?(method_name, *args, &block)
|
262
|
+
send_to_libui(method_name, *args, &block)
|
263
|
+
elsif append_properties.include?(method_name.to_s) ||
|
264
|
+
append_properties.include?(method_name.to_s.sub(/(=|\?)$/, ''))
|
265
|
+
append_property(method_name, *args)
|
266
|
+
elsif can_handle_listener?(method_name.to_s)
|
267
|
+
handle_listener(method_name.to_s, &block)
|
268
|
+
else
|
269
|
+
super
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
def send_to_libui(method_name, *args, &block)
|
274
|
+
if ::LibUI.respond_to?("#{libui_api_keyword}_#{method_name.to_s.sub(/\?$/, '')}") && args.empty?
|
275
|
+
property = method_name.to_s.sub(/\?$/, '')
|
276
|
+
value = ::LibUI.send("#{libui_api_keyword}_#{property}", libui, *args)
|
277
|
+
handle_string_property(property, handle_boolean_property(property, value))
|
278
|
+
elsif ::LibUI.respond_to?("#{libui_api_keyword}_get_#{method_name.to_s.sub(/\?$/, '')}") && args.empty?
|
279
|
+
property = method_name.to_s.sub(/\?$/, '')
|
280
|
+
value = ::LibUI.send("#{libui_api_keyword}_get_#{property}", libui, *args)
|
281
|
+
handle_string_property(property, handle_boolean_property(property, value))
|
282
|
+
elsif ::LibUI.respond_to?("#{libui_api_keyword}_set_#{method_name.to_s.sub(/=$/, '')}") && !args.empty?
|
283
|
+
property = method_name.to_s.sub(/=$/, '')
|
284
|
+
args[0] = Glimmer::LibUI.boolean_to_integer(args.first) if BOOLEAN_PROPERTIES.include?(property) && (args.first.is_a?(TrueClass) || args.first.is_a?(FalseClass))
|
285
|
+
args[0] = '' if STRING_PROPERTIES.include?(property) && args.first == nil
|
286
|
+
if property.to_s == 'checked'
|
287
|
+
current_value = Glimmer::LibUI.integer_to_boolean(::LibUI.send("#{libui_api_keyword}_checked", libui), allow_nil: false)
|
288
|
+
new_value = Glimmer::LibUI.integer_to_boolean(args[0], allow_nil: false)
|
289
|
+
::LibUI.send("#{libui_api_keyword}_set_#{property}", libui, *args) if new_value != current_value
|
290
|
+
else
|
291
|
+
::LibUI.send("#{libui_api_keyword}_set_#{property}", libui, *args)
|
292
|
+
end
|
293
|
+
elsif ::LibUI.respond_to?("#{libui_api_keyword}_#{method_name}") && !args.empty?
|
294
|
+
::LibUI.send("#{libui_api_keyword}_#{method_name}", libui, *args)
|
295
|
+
elsif ::LibUI.respond_to?("control_#{method_name.to_s.sub(/\?$/, '')}") && args.empty?
|
296
|
+
property = method_name.to_s.sub(/\?$/, '')
|
297
|
+
value = ::LibUI.send("control_#{property}", libui, *args)
|
298
|
+
handle_string_property(property, handle_boolean_property(property, value))
|
299
|
+
elsif ::LibUI.respond_to?("control_set_#{method_name.to_s.sub(/=$/, '')}")
|
300
|
+
property = method_name.to_s.sub(/=$/, '')
|
301
|
+
args[0] = Glimmer::LibUI.boolean_to_integer(args.first) if BOOLEAN_PROPERTIES.include?(property) && (args.first.is_a?(TrueClass) || args.first.is_a?(FalseClass))
|
302
|
+
args[0] = '' if STRING_PROPERTIES.include?(property) && args.first == nil
|
303
|
+
::LibUI.send("control_set_#{method_name.to_s.sub(/=$/, '')}", libui, *args)
|
304
|
+
elsif ::LibUI.respond_to?("control_#{method_name}") && !args.empty?
|
305
|
+
::LibUI.send("control_#{method_name}", libui, *args)
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
def append_properties
|
310
|
+
@parent_proxy&.class&.constants&.include?(:APPEND_PROPERTIES) ? @parent_proxy.class::APPEND_PROPERTIES : []
|
311
|
+
end
|
312
|
+
|
313
|
+
def append_property(property, value = nil)
|
314
|
+
property = property.to_s.sub(/(=|\?)$/, '')
|
315
|
+
@append_property_hash ||= {}
|
316
|
+
if value.nil?
|
317
|
+
value = @append_property_hash[property]
|
318
|
+
handle_string_property(property, handle_boolean_property(property, value))
|
319
|
+
else
|
320
|
+
value = Glimmer::LibUI.boolean_to_integer(value) if BOOLEAN_PROPERTIES.include?(property) && (value.is_a?(TrueClass) || value.is_a?(FalseClass))
|
321
|
+
@append_property_hash[property] = value
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
def libui_api_keyword
|
326
|
+
@keyword
|
327
|
+
end
|
328
|
+
|
329
|
+
def destroy
|
330
|
+
# TODO exclude menus from this initial return
|
331
|
+
return if !is_a?(ControlProxy::WindowProxy) && ControlProxy.main_window_proxy&.destroying?
|
332
|
+
data_binding_model_attribute_observer_registrations.each(&:deregister)
|
333
|
+
if parent_proxy.nil?
|
334
|
+
default_destroy
|
335
|
+
else
|
336
|
+
parent_proxy.destroy_child(self)
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
def destroy_child(child)
|
341
|
+
child.default_destroy
|
342
|
+
children.delete(child)
|
343
|
+
end
|
344
|
+
|
345
|
+
def default_destroy
|
346
|
+
deregister_all_custom_listeners
|
347
|
+
send_to_libui('destroy')
|
348
|
+
ControlProxy.control_proxies.delete(self)
|
349
|
+
end
|
350
|
+
|
351
|
+
def enabled(value = nil)
|
352
|
+
if value.nil?
|
353
|
+
@enabled
|
354
|
+
elsif value != @enabled
|
355
|
+
@enabled = value == 1 || value
|
356
|
+
if value == 1 || value
|
357
|
+
send_to_libui('enable')
|
358
|
+
else
|
359
|
+
send_to_libui('disable')
|
360
|
+
end
|
361
|
+
end
|
362
|
+
end
|
363
|
+
alias enabled? enabled
|
364
|
+
alias set_enabled enabled
|
365
|
+
alias enabled= enabled
|
366
|
+
|
367
|
+
def visible(value = nil)
|
368
|
+
current_value = send_to_libui('visible')
|
369
|
+
if value.nil?
|
370
|
+
current_value
|
371
|
+
elsif value != current_value
|
372
|
+
if value == 1 || value
|
373
|
+
send_to_libui('show')
|
374
|
+
else
|
375
|
+
send_to_libui('hide')
|
376
|
+
end
|
377
|
+
end
|
378
|
+
end
|
379
|
+
alias visible? visible
|
380
|
+
alias set_visible visible
|
381
|
+
alias visible= visible
|
382
|
+
|
383
|
+
def content(&block)
|
384
|
+
Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::Libui::ControlExpression.new, @keyword, {post_add_content: @content_added}, &block)
|
385
|
+
end
|
386
|
+
|
387
|
+
private
|
388
|
+
|
389
|
+
def build_control
|
390
|
+
@libui = if ::LibUI.respond_to?("new_#{keyword}")
|
391
|
+
ControlProxy.new_control(@keyword, @args)
|
392
|
+
elsif ::LibUI.respond_to?(keyword)
|
393
|
+
@args[0] = @args.first.libui if @args.first.is_a?(ControlProxy)
|
394
|
+
::LibUI.send(@keyword, *@args)
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
def handle_boolean_property(property, value)
|
399
|
+
BOOLEAN_PROPERTIES.include?(property) ? Glimmer::LibUI.integer_to_boolean(value) : value
|
400
|
+
end
|
401
|
+
|
402
|
+
def handle_string_property(property, value)
|
403
|
+
STRING_PROPERTIES.include?(property) ? value.to_s : value
|
404
|
+
end
|
405
|
+
end
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
Dir[File.expand_path("./#{File.basename(__FILE__, '.rb')}/*.rb", __dir__)].each {|f| require f}
|
@@ -0,0 +1,69 @@
|
|
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
|
+
module Glimmer
|
23
|
+
module LibUI
|
24
|
+
# Parent controls and shapes who have children and add child post_initialize_child
|
25
|
+
module DataBindable
|
26
|
+
# Sets up read/write (bidirectional) data-binding
|
27
|
+
#
|
28
|
+
# classes are expected to implement `data_bind_write(property, model_binding)` to setup write data-binding
|
29
|
+
# by observing view property for changes and writing to model attribute via model binding accordingly
|
30
|
+
#
|
31
|
+
# classes can override data_bind_read to disable read data-binding in rare scenarios that might need it
|
32
|
+
#
|
33
|
+
# returns model attribute reading observer registration by default
|
34
|
+
def data_bind(property, model_binding)
|
35
|
+
data_bind_read(property, model_binding).tap do
|
36
|
+
data_bind_write(property, model_binding) unless model_binding.binding_options[:read_only]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Sets up read data-binding (reading from model to update view)
|
41
|
+
#
|
42
|
+
# Default implementation observes model attribute for changes via model binding
|
43
|
+
# and updates view property accordingly
|
44
|
+
def data_bind_read(property, model_binding)
|
45
|
+
model_attribute_observer = Glimmer::DataBinding::Observer.proc do
|
46
|
+
new_value = model_binding.evaluate_property
|
47
|
+
send("#{property}=", new_value) unless send(property) == new_value
|
48
|
+
end
|
49
|
+
observer_registration = model_attribute_observer.observe(model_binding, attribute_writer_type: [:attribute=, :set_attribute])
|
50
|
+
model_attribute_observer.call # initial update
|
51
|
+
data_binding_model_attribute_observer_registrations << observer_registration
|
52
|
+
observer_registration
|
53
|
+
end
|
54
|
+
|
55
|
+
# Sets up write data-binding (writing to model from view)
|
56
|
+
#
|
57
|
+
# Has no implementation by default. Classes are expected
|
58
|
+
# to implement this method by observing view property
|
59
|
+
# for changes and writing them to model accordingly via model binding
|
60
|
+
def data_bind_write(property, model_binding)
|
61
|
+
# No Op by default
|
62
|
+
end
|
63
|
+
|
64
|
+
def data_binding_model_attribute_observer_registrations
|
65
|
+
@data_binding_model_attribute_observer_registrations ||= []
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,36 @@
|
|
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
|
+
module Glimmer
|
23
|
+
module LibUI
|
24
|
+
# Parent controls and shapes who have children and add child post_initialize_child
|
25
|
+
module Parent
|
26
|
+
# Subclasses can override and must call super (passing add_child: false to cancel adding child to children)
|
27
|
+
def post_initialize_child(child, add_child: true)
|
28
|
+
children << child if add_child
|
29
|
+
end
|
30
|
+
|
31
|
+
def children
|
32
|
+
@children ||= []
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,53 @@
|
|
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
|
+
$LOAD_PATH.unshift(File.expand_path('..', __FILE__))
|
23
|
+
|
24
|
+
# External requires
|
25
|
+
require 'glimmer'
|
26
|
+
# require 'perfect-shape'
|
27
|
+
# require 'logging'
|
28
|
+
# require 'puts_debuggerer' if ENV['pd'].to_s.downcase == 'true'
|
29
|
+
# require 'super_module'
|
30
|
+
# require 'color'
|
31
|
+
require 'os'
|
32
|
+
# require 'equalizer'
|
33
|
+
require 'array_include_methods'
|
34
|
+
require 'facets/hash/stringify_keys'
|
35
|
+
require 'facets/string/underscore'
|
36
|
+
require 'wx'
|
37
|
+
|
38
|
+
# Internal requires
|
39
|
+
# require 'ext/glimmer/config'
|
40
|
+
require 'glimmer-dsl-wx/ext/glimmer'
|
41
|
+
require 'glimmer/dsl/wx/dsl'
|
42
|
+
Glimmer::Config.loop_max_count = -1
|
43
|
+
Glimmer::Config.excluded_keyword_checkers << lambda do |method_symbol, *args|
|
44
|
+
method = method_symbol.to_s
|
45
|
+
result = false
|
46
|
+
result ||= method == 'load_iseq'
|
47
|
+
end
|
48
|
+
|
49
|
+
# begin
|
50
|
+
# PutsDebuggerer.printer = lambda { |m| puts m; $stdout.flush}
|
51
|
+
# rescue
|
52
|
+
##### No Op if puts_debuggerer is not loaded
|
53
|
+
# end
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# wxRuby2 Sample Code. Copyright (c) 2004-2008 wxRuby development team
|
3
|
+
# Adapted for wxRuby3
|
4
|
+
# Copyright (c) M.J.N. Corino, The Netherlands
|
5
|
+
###
|
6
|
+
|
7
|
+
# require 'wx'
|
8
|
+
|
9
|
+
# This is the minimum code to start a WxRuby app - create a Frame, and
|
10
|
+
# show it.
|
11
|
+
# Wx::App.run do
|
12
|
+
### self.app_name = 'Nothing'
|
13
|
+
### frame = Wx::Frame.new(nil, title: "Empty wxRuby App")
|
14
|
+
# frame = Wx::Frame.new(nil)
|
15
|
+
# frame.title = "Empty wxRuby App"
|
16
|
+
# frame.show
|
17
|
+
# frame
|
18
|
+
# end
|
19
|
+
|
20
|
+
require './lib/glimmer-dsl-wx'
|
21
|
+
|
22
|
+
include Glimmer
|
23
|
+
|
24
|
+
frame
|
Binary file
|
Binary file
|