glimmer-dsl-tk 0.0.24 → 0.0.28
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 +37 -0
- data/README.md +548 -100
- data/VERSION +1 -1
- data/glimmer-dsl-tk.gemspec +0 -0
- data/lib/glimmer/dsl/tk/format_expression.rb +39 -0
- data/lib/glimmer/dsl/tk/root_expression.rb +1 -1
- data/lib/glimmer/tk/drag_and_drop_extension.rb +112 -0
- data/lib/glimmer/tk/frame_proxy.rb +8 -0
- data/lib/glimmer/tk/label_proxy.rb +0 -9
- data/lib/glimmer/tk/radiobutton_proxy.rb +1 -1
- data/lib/glimmer/tk/root_proxy.rb +17 -35
- data/lib/glimmer/tk/spinbox_proxy.rb +52 -0
- data/lib/glimmer/tk/text_proxy.rb +350 -0
- data/lib/glimmer/tk/widget_proxy.rb +109 -35
- data/samples/elaborate/meta_sample.rb +138 -0
- data/samples/hello/hello_checkbutton.rb +0 -1
- data/samples/hello/hello_combobox.rb +2 -1
- data/samples/hello/hello_drag_and_drop.rb +159 -0
- data/samples/hello/hello_entry.rb +2 -2
- data/samples/hello/hello_radiobutton.rb +21 -1
- data/samples/hello/hello_spinbox.rb +75 -0
- data/samples/hello/hello_text.rb +222 -0
- data/samples/hello/images/copy.png +0 -0
- data/samples/hello/images/cut.png +0 -0
- data/samples/hello/images/paste.png +0 -0
- data/samples/hello/images/redo.png +0 -0
- data/samples/hello/images/undo.png +0 -0
- metadata +19 -6
@@ -0,0 +1,350 @@
|
|
1
|
+
# Copyright (c) 2020-2021 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/tk/widget_proxy'
|
23
|
+
|
24
|
+
module Glimmer
|
25
|
+
module Tk
|
26
|
+
# Proxy for Tk::Text
|
27
|
+
#
|
28
|
+
# Follows the Proxy Design Pattern
|
29
|
+
class TextProxy < WidgetProxy
|
30
|
+
def handle_listener(listener_name, &listener)
|
31
|
+
listener_name = listener_name.to_s.downcase
|
32
|
+
case listener_name
|
33
|
+
when '<<modified>>', '<modified>', 'modified'
|
34
|
+
modified_listener = Proc.new do |*args|
|
35
|
+
listener.call(*args)
|
36
|
+
@tk.modified = false
|
37
|
+
end
|
38
|
+
bind('<Modified>', modified_listener)
|
39
|
+
when '<<selection>>', '<selection>', 'selection'
|
40
|
+
bind('<Selection>', listener)
|
41
|
+
else
|
42
|
+
super
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def text=(value)
|
47
|
+
if value != @text
|
48
|
+
if @text && value.start_with?(@text)
|
49
|
+
insert('end', value[@text.size..-1])
|
50
|
+
else
|
51
|
+
delete('1.0', 'end')
|
52
|
+
insert('end', value)
|
53
|
+
end
|
54
|
+
@text = value
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def text
|
59
|
+
@text = get("1.0", 'end')
|
60
|
+
end
|
61
|
+
|
62
|
+
def add_selection_format(option, value)
|
63
|
+
process_selection_ranges { |range_start, range_end| add_format(range_start, range_end, option, value) }
|
64
|
+
end
|
65
|
+
|
66
|
+
def remove_selection_format(option, value)
|
67
|
+
process_selection_ranges { |range_start, range_end| remove_format(range_start, range_end, option, value) }
|
68
|
+
end
|
69
|
+
|
70
|
+
def toggle_selection_format(option, value)
|
71
|
+
process_selection_ranges { |range_start, range_end| toggle_format(range_start, range_end, option, value) }
|
72
|
+
end
|
73
|
+
|
74
|
+
def add_selection_font_format(option, value)
|
75
|
+
process_selection_ranges { |range_start, range_end| add_font_format(range_start, range_end, option, value) }
|
76
|
+
end
|
77
|
+
|
78
|
+
def remove_selection_font_format(option, value)
|
79
|
+
process_selection_ranges { |range_start, range_end| remove_font_format(range_start, range_end, option, value) }
|
80
|
+
end
|
81
|
+
|
82
|
+
def toggle_selection_font_format(option, value)
|
83
|
+
process_selection_ranges { |range_start, range_end| toggle_font_format(range_start, range_end, option, value) }
|
84
|
+
end
|
85
|
+
|
86
|
+
def process_selection_ranges(&processor)
|
87
|
+
@tk.tag_ranges('sel').each do |region|
|
88
|
+
range_start = region.first
|
89
|
+
range_end = region.last
|
90
|
+
processor.call(range_start, range_end)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def applied_format?(region_start, region_end, option, value)
|
95
|
+
!applied_format_tags(region_start, region_end, option, value).empty?
|
96
|
+
end
|
97
|
+
|
98
|
+
def applied_format_tags(region_start, region_end, option, value)
|
99
|
+
tag_names = @tk.tag_names - ['sel']
|
100
|
+
|
101
|
+
tag_names.select do |tag_name|
|
102
|
+
@tk.tag_ranges(tag_name).any? do |range|
|
103
|
+
if text_index_less_than_or_equal_to_other_text_index?(range.first, region_start) && text_index_greater_than_or_equal_to_other_text_index?(range.last, region_end)
|
104
|
+
@tk.tag_cget(tag_name, option) == value
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def add_format(region_start, region_end, option, value)
|
111
|
+
@@tag_number = 0 unless defined?(@@tag_number)
|
112
|
+
tag = "tag#{@@tag_number += 1}"
|
113
|
+
@tk.tag_configure(tag, {option => value})
|
114
|
+
@tk.tag_add(tag, region_start, region_end)
|
115
|
+
tag
|
116
|
+
end
|
117
|
+
|
118
|
+
def remove_format(region_start, region_end, option, value)
|
119
|
+
partial_intersection_option_applied_tags = tag_names.select do |tag_name|
|
120
|
+
@tk.tag_ranges(tag_name).any? do |range|
|
121
|
+
if range.first.to_f.between?(region_start.to_f, region_end.to_f) or
|
122
|
+
range.last.to_f.between?(region_start.to_f, region_end.to_f) or
|
123
|
+
(text_index_less_than_or_equal_to_other_text_index?(range.first, region_start) && text_index_greater_than_or_equal_to_other_text_index?(range.last, region_end))
|
124
|
+
@tk.tag_cget(tag_name, option) == value
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
partial_intersection_option_applied_tags.each do |tag_name|
|
130
|
+
@tk.tag_remove(tag_name, region_start, region_end)
|
131
|
+
end
|
132
|
+
|
133
|
+
nil
|
134
|
+
end
|
135
|
+
|
136
|
+
# toggles option/value tag (removes if already applied)
|
137
|
+
def toggle_format(region_start, region_end, option, value)
|
138
|
+
if applied_format?(region_start, region_end, option, value)
|
139
|
+
remove_format(region_start, region_end, option, value)
|
140
|
+
else
|
141
|
+
add_format(region_start, region_end, option, value)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# TODO Algorithm for font option formatting
|
146
|
+
# for a region, grab all the latest tags for each subregion as well as the widget font for subregions without a tag
|
147
|
+
# for each part of the region covered by a tag, augment its font with new font option (or remove if that is what is needed)
|
148
|
+
# Once add and remove are implemented, implement toggle
|
149
|
+
# Also, there is a need for a method that checks if a font option value applies to an entire region (to decide which way to toggle with toggle method)
|
150
|
+
def applied_font_format?(region_start, region_end, font_option, value)
|
151
|
+
applied_font_format_tags_and_regions(region_start, region_end).all? do |tag, region_start, region_end|
|
152
|
+
if tag.nil?
|
153
|
+
@tk.font.send(font_option) == value
|
154
|
+
else
|
155
|
+
@tk.tag_cget(tag, 'font').send(font_option) == value
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def applied_font_format_tags_and_regions(region_start, region_end)
|
161
|
+
lines = text.split("\n")
|
162
|
+
tags_and_regions = []
|
163
|
+
all_tag_names = @tk.tag_names - ['sel']
|
164
|
+
(region_start.to_i..region_end.to_i).each do |line_number|
|
165
|
+
start_character_index = 0
|
166
|
+
start_character_index = region_start.to_s.split('.').last.to_i if line_number == region_start.to_i
|
167
|
+
end_character_index = lines[line_number - 1].size
|
168
|
+
end_character_index = region_end.to_s.split('.').last.to_i if line_number == region_end.to_i
|
169
|
+
(start_character_index...end_character_index).each do |character_index|
|
170
|
+
text_index = "#{line_number}.#{character_index}"
|
171
|
+
# TODO reimplement the following using @tk.tag_names without arg since passing an arg seems broken and returns inaccurate results
|
172
|
+
region_tag = all_tag_names.reverse.find do |tag|
|
173
|
+
@tk.tag_cget(tag, 'font') && @tk.tag_ranges(tag).any? do |range_start, range_end|
|
174
|
+
text_index_less_than_or_equal_to_other_text_index?(range_start, text_index) && text_index_greater_than_or_equal_to_other_text_index?(range_end, text_index)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end_text_index = add_to_text_index(text_index, 1)
|
178
|
+
if tags_and_regions&.last && region_tag == tags_and_regions.last.first
|
179
|
+
tags_and_regions.last[2] = end_text_index
|
180
|
+
else
|
181
|
+
tags_and_regions << [region_tag, text_index, end_text_index]
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
tags_and_regions
|
186
|
+
end
|
187
|
+
|
188
|
+
def add_font_format(region_start, region_end, font_option, value)
|
189
|
+
applied_font_format_tags_and_regions(region_start, region_end).each do |tag, tag_region_start, tag_region_end|
|
190
|
+
if tag
|
191
|
+
bigger_region_tag = @tk.tag_ranges(tag).any? do |range_start, range_end|
|
192
|
+
text_index_less_than_other_text_index?(range_start, tag_region_start) || text_index_greater_than_other_text_index?(range_end, tag_region_end)
|
193
|
+
end
|
194
|
+
if bigger_region_tag
|
195
|
+
@tk.tag_ranges(tag).each do |range_start, range_end|
|
196
|
+
if text_index_less_than_other_text_index?(range_start, tag_region_start) && text_index_less_than_or_equal_to_other_text_index?(range_end, tag_region_end) && text_index_greater_than_or_equal_to_other_text_index?(range_end, tag_region_start)
|
197
|
+
font = @tk.tag_cget(tag, 'font')
|
198
|
+
remove_format(range_start, range_end, 'font', font)
|
199
|
+
add_format(range_start, tag_region_start, 'font', font)
|
200
|
+
font_clone = clone_font(font)
|
201
|
+
font_clone.send("#{font_option}=", value)
|
202
|
+
add_format(tag_region_start, tag_region_end, 'font', font_clone)
|
203
|
+
elsif text_index_greater_than_other_text_index?(range_end, tag_region_end) && text_index_greater_than_or_equal_to_other_text_index?(range_start, tag_region_start) && text_index_less_than_or_equal_to_other_text_index?(range_start, tag_region_end)
|
204
|
+
font = @tk.tag_cget(tag, 'font')
|
205
|
+
remove_format(range_start, range_end, 'font', font)
|
206
|
+
add_format(tag_region_end, range_end, 'font', font)
|
207
|
+
font_clone = clone_font(font)
|
208
|
+
font_clone.send("#{font_option}=", value)
|
209
|
+
add_format(tag_region_start, tag_region_end, 'font', font_clone)
|
210
|
+
elsif text_index_less_than_other_text_index?(range_start, tag_region_start) && text_index_greater_than_other_text_index?(range_end, tag_region_end)
|
211
|
+
font = @tk.tag_cget(tag, 'font')
|
212
|
+
remove_format(range_start, range_end, 'font', font)
|
213
|
+
add_format(range_start, tag_region_start, 'font', font)
|
214
|
+
remove_format(range_start, range_end, 'font', font)
|
215
|
+
add_format(tag_region_end, range_end, 'font', font)
|
216
|
+
font_clone = clone_font(font)
|
217
|
+
font_clone.send("#{font_option}=", value)
|
218
|
+
add_format(tag_region_start, tag_region_end, 'font', font_clone)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
else
|
222
|
+
current_font = @tk.tag_cget(tag, 'font')
|
223
|
+
current_font.send("#{font_option}=", value)
|
224
|
+
end
|
225
|
+
else
|
226
|
+
add_format(tag_region_start, tag_region_end, 'font', default_font_attributes.merge(font_option => value))
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def remove_font_format(region_start, region_end, font_option, value)
|
232
|
+
applied_font_format_tags_and_regions(region_start, region_end).each do |tag, tag_region_start, tag_region_end|
|
233
|
+
if tag
|
234
|
+
bigger_region_tag = @tk.tag_ranges(tag).any? do |range_start, range_end|
|
235
|
+
text_index_less_than_other_text_index?(range_start, tag_region_start) || text_index_greater_than_other_text_index?(range_end, tag_region_end)
|
236
|
+
end
|
237
|
+
if bigger_region_tag
|
238
|
+
@tk.tag_ranges(tag).each do |range_start, range_end|
|
239
|
+
if text_index_less_than_other_text_index?(range_start, tag_region_start) && text_index_less_than_or_equal_to_other_text_index?(range_end, tag_region_end) && text_index_greater_than_or_equal_to_other_text_index?(range_end, tag_region_start)
|
240
|
+
font = @tk.tag_cget(tag, 'font')
|
241
|
+
remove_format(range_start, range_end, 'font', font)
|
242
|
+
add_format(range_start, subtract_from_text_index(tag_region_start, 1), 'font', font)
|
243
|
+
font_clone = clone_font(font)
|
244
|
+
font_clone.send("#{font_option}=", default_for_font_option(font_option))
|
245
|
+
add_format(tag_region_start, tag_region_end, 'font', font_clone)
|
246
|
+
elsif text_index_greater_than_other_text_index?(range_end, tag_region_end) && text_index_greater_than_or_equal_to_other_text_index?(range_start, tag_region_start) && text_index_less_than_or_equal_to_other_text_index?(range_start, tag_region_end)
|
247
|
+
font = @tk.tag_cget(tag, 'font')
|
248
|
+
remove_format(range_start, range_end, 'font', font)
|
249
|
+
add_format(add_to_text_index(tag_region_end, 1), range_end, 'font', font)
|
250
|
+
font_clone = clone_font(font)
|
251
|
+
font_clone.send("#{font_option}=", default_for_font_option(font_option))
|
252
|
+
add_format(tag_region_start, tag_region_end, 'font', font_clone)
|
253
|
+
elsif text_index_less_than_other_text_index?(range_start, tag_region_start) && text_index_greater_than_other_text_index?(range_end, tag_region_end)
|
254
|
+
font = @tk.tag_cget(tag, 'font')
|
255
|
+
remove_format(range_start, range_end, 'font', font)
|
256
|
+
add_format(range_start, subtract_from_text_index(tag_region_start, 1), 'font', font)
|
257
|
+
remove_format(range_start, range_end, 'font', font)
|
258
|
+
add_format(add_to_text_index(tag_region_end, 1), range_end, 'font', font)
|
259
|
+
font_clone = clone_font(font)
|
260
|
+
font_clone.send("#{font_option}=", default_for_font_option(font_option))
|
261
|
+
add_format(tag_region_start, tag_region_end, 'font', font_clone)
|
262
|
+
end
|
263
|
+
end
|
264
|
+
else
|
265
|
+
current_font = @tk.tag_cget(tag, 'font')
|
266
|
+
current_font.send("#{font_option}=", default_for_font_option(font_option))
|
267
|
+
end
|
268
|
+
else
|
269
|
+
add_format(tag_region_start, tag_region_end, 'font', default_font_attributes.merge(font_option => default_for_font_option(font_option)))
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
# toggles option/value tag (removes if already applied)
|
275
|
+
def toggle_font_format(region_start, region_end, option, value)
|
276
|
+
if applied_font_format?(region_start, region_end, option, value)
|
277
|
+
remove_font_format(region_start, region_end, option, value)
|
278
|
+
else
|
279
|
+
add_font_format(region_start, region_end, option, value)
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
def default_for_font_option(font_option)
|
284
|
+
@tk.font.send(font_option)
|
285
|
+
end
|
286
|
+
|
287
|
+
def default_font_attributes
|
288
|
+
Hash[@tk.font.actual]
|
289
|
+
end
|
290
|
+
|
291
|
+
def add_to_text_index(text_index, addition)
|
292
|
+
text_index_parts = text_index.split('.')
|
293
|
+
line = text_index_parts.first
|
294
|
+
char_index = text_index_parts.last
|
295
|
+
char_index = char_index.to_i + addition
|
296
|
+
"#{line}.#{char_index}"
|
297
|
+
end
|
298
|
+
|
299
|
+
def subtract_from_text_index(text_index, subtraction)
|
300
|
+
add_to_text_index(text_index, -1 * subtraction)
|
301
|
+
end
|
302
|
+
|
303
|
+
def text_index_less_than_other_text_index?(region1, region2)
|
304
|
+
region1_parts = region1.to_s.split('.')
|
305
|
+
region2_parts = region2.to_s.split('.')
|
306
|
+
return true if region1_parts.first.to_i < region2_parts.first.to_i
|
307
|
+
return false if region1_parts.first.to_i > region2_parts.first.to_i
|
308
|
+
region1_parts.last.to_i < region2_parts.last.to_i
|
309
|
+
end
|
310
|
+
|
311
|
+
def text_index_less_than_or_equal_to_other_text_index?(region1, region2)
|
312
|
+
region1_parts = region1.to_s.split('.')
|
313
|
+
region2_parts = region2.to_s.split('.')
|
314
|
+
return true if region1_parts.first.to_i < region2_parts.first.to_i
|
315
|
+
return false if region1_parts.first.to_i > region2_parts.first.to_i
|
316
|
+
region1_parts.last.to_i <= region2_parts.last.to_i
|
317
|
+
end
|
318
|
+
|
319
|
+
def text_index_greater_than_other_text_index?(region1, region2)
|
320
|
+
region1_parts = region1.to_s.split('.')
|
321
|
+
region2_parts = region2.to_s.split('.')
|
322
|
+
return true if region1_parts.first.to_i > region2_parts.first.to_i
|
323
|
+
return false if region1_parts.first.to_i < region2_parts.first.to_i
|
324
|
+
region1_parts.last.to_i > region2_parts.last.to_i
|
325
|
+
end
|
326
|
+
|
327
|
+
def text_index_greater_than_or_equal_to_other_text_index?(region1, region2)
|
328
|
+
region1_parts = region1.to_s.split('.')
|
329
|
+
region2_parts = region2.to_s.split('.')
|
330
|
+
return true if region1_parts.first.to_i > region2_parts.first.to_i
|
331
|
+
return false if region1_parts.first.to_i < region2_parts.first.to_i
|
332
|
+
region1_parts.last.to_i >= region2_parts.last.to_i
|
333
|
+
end
|
334
|
+
|
335
|
+
def clone_font(font)
|
336
|
+
::TkFont.new(Hash[font.actual])
|
337
|
+
end
|
338
|
+
|
339
|
+
private
|
340
|
+
|
341
|
+
def initialize_defaults
|
342
|
+
super
|
343
|
+
self.font = {family: 'Courier New'}
|
344
|
+
self.wrap = 'none'
|
345
|
+
self.padx = 5
|
346
|
+
self.pady = 5
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
@@ -34,7 +34,9 @@ module Glimmer
|
|
34
34
|
begin
|
35
35
|
class_name = "#{keyword.camelcase(:upper)}Proxy".to_sym
|
36
36
|
Glimmer::Tk.const_get(class_name)
|
37
|
-
rescue
|
37
|
+
rescue => e
|
38
|
+
Glimmer::Config.logger.debug {"Unable to instantiate custom class name for #{keyword} ... defaulting to Glimmer::Tk::WidgetProxy"}
|
39
|
+
Glimmer::Config.logger.debug {e.full_message}
|
38
40
|
Glimmer::Tk::WidgetProxy
|
39
41
|
end
|
40
42
|
end
|
@@ -54,13 +56,15 @@ module Glimmer
|
|
54
56
|
tk_widget_class = eval(tk_widget_name)
|
55
57
|
break
|
56
58
|
rescue RuntimeError, SyntaxError, NameError => e
|
57
|
-
Glimmer::Config.logger.debug e.full_message
|
59
|
+
Glimmer::Config.logger.debug {e.full_message}
|
58
60
|
end
|
59
61
|
end
|
60
|
-
tk_widget_class
|
62
|
+
tk_widget_class if tk_widget_class.respond_to?(:new)
|
61
63
|
end
|
62
64
|
end
|
63
65
|
|
66
|
+
FONTS_PREDEFINED = %w[default text fixed menu heading caption small_caption icon tooltip]
|
67
|
+
|
64
68
|
attr_reader :parent_proxy, :tk, :args, :keyword, :children
|
65
69
|
|
66
70
|
# Initializes a new Tk Widget
|
@@ -74,8 +78,8 @@ module Glimmer
|
|
74
78
|
tk_widget_class = self.class.tk_widget_class_for(underscored_widget_name)
|
75
79
|
@tk = tk_widget_class.new(@parent_proxy.tk, *args)
|
76
80
|
# a common widget initializer
|
77
|
-
initialize_defaults
|
78
81
|
@parent_proxy.post_initialize_child(self)
|
82
|
+
initialize_defaults
|
79
83
|
post_add_content if @block.nil?
|
80
84
|
end
|
81
85
|
|
@@ -104,6 +108,8 @@ module Glimmer
|
|
104
108
|
@tk.send(attribute_setter(attribute), @tk.send(attribute))
|
105
109
|
result = true
|
106
110
|
rescue => e
|
111
|
+
Glimmer::Config.logger.debug { "No tk attribute setter for #{attribute}" }
|
112
|
+
Glimmer::Config.logger.debug { e.full_message }
|
107
113
|
result = false
|
108
114
|
end
|
109
115
|
result
|
@@ -114,7 +120,8 @@ module Glimmer
|
|
114
120
|
# TK Widget currently doesn't support respond_to? properly, so I have to resort to this trick for now
|
115
121
|
@tk.send(attribute)
|
116
122
|
true
|
117
|
-
rescue
|
123
|
+
rescue => e
|
124
|
+
Glimmer::Config.logger.debug { "No tk attribute getter setter for #{attribute}" }
|
118
125
|
false
|
119
126
|
end
|
120
127
|
end
|
@@ -125,7 +132,8 @@ module Glimmer
|
|
125
132
|
begin
|
126
133
|
@tk.tile_instate(attribute)
|
127
134
|
true
|
128
|
-
rescue
|
135
|
+
rescue => e
|
136
|
+
Glimmer::Config.logger.debug { "No tk state for #{attribute}" }
|
129
137
|
false
|
130
138
|
end
|
131
139
|
else
|
@@ -144,45 +152,55 @@ module Glimmer
|
|
144
152
|
tk_widget_has_attribute_getter_setter?(attribute) or
|
145
153
|
has_state?(attribute) or
|
146
154
|
has_attributes_attribute?(attribute) or
|
147
|
-
respond_to?(attribute_setter(attribute), args)
|
155
|
+
respond_to?(attribute_setter(attribute), args) or
|
156
|
+
respond_to?(attribute_setter(attribute), *args, super_only: true) or
|
157
|
+
respond_to?(attribute, *args, super_only: true)
|
148
158
|
end
|
149
159
|
|
150
160
|
def set_attribute(attribute, *args)
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
widget_custom_attribute
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
161
|
+
begin
|
162
|
+
widget_custom_attribute = widget_custom_attribute_mapping[tk.class] && widget_custom_attribute_mapping[tk.class][attribute.to_s]
|
163
|
+
if respond_to?(attribute_setter(attribute), super_only: true)
|
164
|
+
send(attribute_setter(attribute), *args)
|
165
|
+
elsif respond_to?(attribute, super_only: true) && self.class.instance_method(attribute).parameters.size > 0
|
166
|
+
send(attribute, *args)
|
167
|
+
elsif widget_custom_attribute
|
168
|
+
widget_custom_attribute[:setter][:invoker].call(@tk, args)
|
169
|
+
elsif tk_widget_has_attribute_setter?(attribute)
|
170
|
+
unless args.size == 1 && @tk.send(attribute) == args.first
|
171
|
+
if args.size == 1
|
172
|
+
@tk.send(attribute_setter(attribute), *args)
|
173
|
+
else
|
174
|
+
@tk.send(attribute_setter(attribute), args)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
elsif tk_widget_has_attribute_getter_setter?(attribute)
|
178
|
+
@tk.send(attribute, *args)
|
179
|
+
elsif has_state?(attribute)
|
180
|
+
attribute = attribute.sub(/=$/, '')
|
181
|
+
if !!args.first
|
182
|
+
@tk.tile_state(attribute)
|
162
183
|
else
|
163
|
-
@tk.
|
184
|
+
@tk.tile_state("!#{attribute}")
|
164
185
|
end
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
elsif has_state?(attribute)
|
169
|
-
attribute = attribute.sub(/=$/, '')
|
170
|
-
if !!args.first
|
171
|
-
@tk.tile_state(attribute)
|
186
|
+
elsif has_attributes_attribute?(attribute)
|
187
|
+
attribute = attribute.sub(/=$/, '')
|
188
|
+
@tk.attributes(attribute, args.first)
|
172
189
|
else
|
173
|
-
|
190
|
+
raise "#{self} cannot handle attribute #{attribute} with args #{args.inspect}"
|
174
191
|
end
|
175
|
-
|
176
|
-
attribute
|
177
|
-
|
178
|
-
|
179
|
-
send(attribute_setter(attribute), args)
|
192
|
+
rescue => e
|
193
|
+
Glimmer::Config.logger.debug {"Failed to set attribute #{attribute} with args #{args.inspect}. Attempting to set through style instead..."}
|
194
|
+
Glimmer::Config.logger.debug {e.full_message}
|
195
|
+
apply_style(attribute => args.first)
|
180
196
|
end
|
181
197
|
end
|
182
198
|
|
183
199
|
def get_attribute(attribute)
|
184
200
|
widget_custom_attribute = widget_custom_attribute_mapping[tk.class] && widget_custom_attribute_mapping[tk.class][attribute.to_s]
|
185
|
-
if
|
201
|
+
if respond_to?(attribute, super_only: true)
|
202
|
+
send(attribute)
|
203
|
+
elsif widget_custom_attribute
|
186
204
|
widget_custom_attribute[:getter][:invoker].call(@tk, args)
|
187
205
|
elsif tk_widget_has_attribute_getter_setter?(attribute)
|
188
206
|
@tk.send(attribute)
|
@@ -201,6 +219,12 @@ module Glimmer
|
|
201
219
|
"#{attribute}="
|
202
220
|
end
|
203
221
|
|
222
|
+
def style=(styles)
|
223
|
+
styles.each do |attribute, value|
|
224
|
+
apply_style(attribute => value)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
204
228
|
def grid(options = {})
|
205
229
|
options = options.stringify_keys
|
206
230
|
index_in_parent = @parent_proxy.children.index(self)
|
@@ -221,6 +245,25 @@ module Glimmer
|
|
221
245
|
@tk.grid(options)
|
222
246
|
end
|
223
247
|
|
248
|
+
def font=(value)
|
249
|
+
if (value.is_a?(Symbol) || value.is_a?(String)) && FONTS_PREDEFINED.include?(value.to_s.downcase)
|
250
|
+
@tk.font = "tk_#{value}_font".camelcase(:upper)
|
251
|
+
else
|
252
|
+
@tk.font = value.is_a?(TkFont) ? value : TkFont.new(value)
|
253
|
+
end
|
254
|
+
rescue => e
|
255
|
+
Glimmer::Config.logger.debug {"Failed to set attribute #{attribute} with args #{args.inspect}. Attempting to set through style instead..."}
|
256
|
+
Glimmer::Config.logger.debug {e.full_message}
|
257
|
+
apply_style({"font" => value})
|
258
|
+
end
|
259
|
+
|
260
|
+
def apply_style(options)
|
261
|
+
@@style_number = 0 unless defined?(@@style_number)
|
262
|
+
style = "style#{@@style_number += 1}.#{@tk.class.name.split('::').last}"
|
263
|
+
::Tk::Tile::Style.configure(style, options)
|
264
|
+
@tk.style = style
|
265
|
+
end
|
266
|
+
|
224
267
|
def widget_custom_attribute_mapping
|
225
268
|
# TODO consider extracting to modules/subclasses
|
226
269
|
@widget_custom_attribute_mapping ||= {
|
@@ -276,6 +319,12 @@ module Glimmer
|
|
276
319
|
setter: {name: 'text=', invoker: lambda { |widget, args| @tk.textvariable&.value = args.first }},
|
277
320
|
},
|
278
321
|
},
|
322
|
+
::Tk::Tile::TSpinbox => {
|
323
|
+
'text' => {
|
324
|
+
getter: {name: 'text', invoker: lambda { |widget, args| @tk.textvariable&.value }},
|
325
|
+
setter: {name: 'text=', invoker: lambda { |widget, args| @tk.textvariable&.value = args.first }},
|
326
|
+
},
|
327
|
+
},
|
279
328
|
::Tk::Root => {
|
280
329
|
'text' => {
|
281
330
|
getter: {name: 'text', invoker: lambda { |widget, args| @tk.title }},
|
@@ -309,11 +358,33 @@ module Glimmer
|
|
309
358
|
},
|
310
359
|
::Tk::Tile::TEntry => {
|
311
360
|
'text' => lambda do |observer|
|
312
|
-
tk.textvariable.trace('write') {
|
361
|
+
@tk.textvariable.trace('write') {
|
313
362
|
observer.call(@tk.textvariable.value)
|
314
363
|
}
|
315
364
|
end,
|
316
365
|
},
|
366
|
+
::Tk::Tile::TSpinbox => {
|
367
|
+
'text' => lambda do |observer|
|
368
|
+
@tk.command {
|
369
|
+
observer.call(@tk.textvariable&.value)
|
370
|
+
}
|
371
|
+
@tk.validate('key')
|
372
|
+
@tk.validatecommand { |validate_args|
|
373
|
+
observer.call(validate_args.value)
|
374
|
+
new_icursor = validate_args.index
|
375
|
+
new_icursor += validate_args.string.size if validate_args.action == 1
|
376
|
+
@tk.icursor = new_icursor
|
377
|
+
true
|
378
|
+
}
|
379
|
+
end,
|
380
|
+
},
|
381
|
+
::Tk::Text => {
|
382
|
+
'text' => lambda do |observer|
|
383
|
+
handle_listener('modified') do
|
384
|
+
observer.call(text)
|
385
|
+
end
|
386
|
+
end,
|
387
|
+
},
|
317
388
|
::Tk::Tile::TRadiobutton => {
|
318
389
|
'variable' => lambda do |observer|
|
319
390
|
@tk.command {
|
@@ -384,7 +455,10 @@ module Glimmer
|
|
384
455
|
private
|
385
456
|
|
386
457
|
def initialize_defaults
|
387
|
-
|
458
|
+
options = {}
|
459
|
+
options[:sticky] = 'nsew'
|
460
|
+
options[:column_weight] = 1 if @parent_proxy.children.count == 1
|
461
|
+
grid(options) unless @tk.is_a?(::Tk::Toplevel)
|
388
462
|
end
|
389
463
|
end
|
390
464
|
end
|