glimmer-dsl-tk 0.0.30 → 0.0.34
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 +32 -2
- data/README.md +143 -57
- data/VERSION +1 -1
- data/glimmer-dsl-tk.gemspec +0 -0
- data/lib/glimmer/data_binding/tk/one_time_observer.rb +39 -0
- data/lib/glimmer/dsl/tk/data_binding_expression.rb +3 -2
- data/lib/glimmer/dsl/tk/widget_expression.rb +5 -1
- data/lib/glimmer/tk/entry_proxy.rb +1 -1
- data/lib/glimmer/tk/root_proxy.rb +2 -77
- data/lib/glimmer/tk/text_proxy.rb +137 -25
- data/lib/glimmer/tk/toplevel_proxy.rb +119 -0
- data/lib/glimmer/tk/widget_proxy.rb +57 -13
- data/samples/hello/hello_text.rb +110 -38
- metadata +4 -2
@@ -19,18 +19,14 @@
|
|
19
19
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
20
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
21
|
|
22
|
-
require 'glimmer/tk/
|
22
|
+
require 'glimmer/tk/toplevel_proxy'
|
23
23
|
|
24
24
|
module Glimmer
|
25
25
|
module Tk
|
26
26
|
# Proxy for TkRoot
|
27
27
|
#
|
28
28
|
# Follows the Proxy Design Pattern
|
29
|
-
class RootProxy <
|
30
|
-
REGEX_GEOMETRY = /[x+-]/
|
31
|
-
DEFAULT_WIDTH = 190
|
32
|
-
DEFAULT_HEIGHT = 95
|
33
|
-
|
29
|
+
class RootProxy < ToplevelProxy
|
34
30
|
def initialize(*args, &block)
|
35
31
|
@tk = ::TkRoot.new
|
36
32
|
@tk.minsize = DEFAULT_WIDTH, DEFAULT_HEIGHT
|
@@ -50,10 +46,6 @@ module Glimmer
|
|
50
46
|
Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::Tk::RootExpression.new, keyword, *args, &block)
|
51
47
|
end
|
52
48
|
|
53
|
-
def has_attribute?(attribute, *args)
|
54
|
-
%w[width height x y].include?(attribute.to_s) || super
|
55
|
-
end
|
56
|
-
|
57
49
|
def set_attribute(attribute, *args)
|
58
50
|
case attribute.to_s
|
59
51
|
when 'iconphoto'
|
@@ -70,45 +62,8 @@ module Glimmer
|
|
70
62
|
end
|
71
63
|
end
|
72
64
|
|
73
|
-
def width
|
74
|
-
geometry.split(REGEX_GEOMETRY)[0].to_i
|
75
|
-
end
|
76
|
-
|
77
|
-
def height
|
78
|
-
geometry.split(REGEX_GEOMETRY)[1].to_i
|
79
|
-
end
|
80
|
-
|
81
|
-
def x
|
82
|
-
sign_number(x_sign, geometry.split(REGEX_GEOMETRY)[2].to_i)
|
83
|
-
end
|
84
|
-
|
85
|
-
def y
|
86
|
-
sign_number(y_sign, geometry.split(REGEX_GEOMETRY)[3].to_i)
|
87
|
-
end
|
88
|
-
|
89
|
-
def width=(value)
|
90
|
-
@width = value.to_i
|
91
|
-
self.geometry = "#{value.to_i}x#{@height || DEFAULT_HEIGHT}#{x_sign}#{abs_x}#{y_sign}#{abs_y}"
|
92
|
-
end
|
93
|
-
|
94
|
-
def height=(value)
|
95
|
-
@height = value.to_i
|
96
|
-
self.geometry = "#{@width || DEFAULT_WIDTH}x#{value.to_i}#{x_sign}#{abs_x}#{y_sign}#{abs_y}"
|
97
|
-
end
|
98
|
-
|
99
|
-
def x=(value)
|
100
|
-
self.geometry = "#{@width || DEFAULT_WIDTH}x#{@height || DEFAULT_HEIGHT}#{value.to_i > 0 ? '+' : '-'}#{value.to_i.abs}#{y_sign}#{abs_y}"
|
101
|
-
end
|
102
|
-
|
103
|
-
def y=(value)
|
104
|
-
self.geometry = "#{@width || DEFAULT_WIDTH}x#{@height || DEFAULT_HEIGHT}#{x_sign}#{abs_x}#{value.to_i > 0 ? '+' : '-'}#{value.to_i.abs}"
|
105
|
-
end
|
106
|
-
|
107
65
|
def handle_listener(listener_name, &listener)
|
108
66
|
case listener_name.to_s.upcase
|
109
|
-
when 'WM_DELETE_WINDOW', 'DELETE_WINDOW'
|
110
|
-
listener_name = 'WM_DELETE_WINDOW'
|
111
|
-
@tk.protocol(listener_name, &listener)
|
112
67
|
when 'WM_OPEN_WINDOW', 'OPEN_WINDOW'
|
113
68
|
@on_open_window_procs ||= []
|
114
69
|
@on_open_window_procs << listener
|
@@ -128,36 +83,6 @@ module Glimmer
|
|
128
83
|
end
|
129
84
|
::Tk.mainloop
|
130
85
|
end
|
131
|
-
|
132
|
-
private
|
133
|
-
|
134
|
-
def sign_number(sign, number)
|
135
|
-
"#{sign}1".to_i * number
|
136
|
-
end
|
137
|
-
|
138
|
-
def abs_x
|
139
|
-
geometry.split(REGEX_GEOMETRY)[2].to_i
|
140
|
-
end
|
141
|
-
|
142
|
-
def abs_y
|
143
|
-
geometry.split(REGEX_GEOMETRY)[3].to_i
|
144
|
-
end
|
145
|
-
|
146
|
-
def x_sign
|
147
|
-
geometry_signs[0]
|
148
|
-
end
|
149
|
-
|
150
|
-
def y_sign
|
151
|
-
geometry_signs[1]
|
152
|
-
end
|
153
|
-
|
154
|
-
def geometry_signs
|
155
|
-
geometry.chars.select {|char| char.match(/[+-]/)}
|
156
|
-
end
|
157
|
-
|
158
|
-
def initialize_defaults
|
159
|
-
self.background = '#ececec' if OS.mac?
|
160
|
-
end
|
161
86
|
end
|
162
87
|
end
|
163
88
|
end
|
@@ -27,48 +27,118 @@ module Glimmer
|
|
27
27
|
#
|
28
28
|
# Follows the Proxy Design Pattern
|
29
29
|
class TextProxy < WidgetProxy
|
30
|
+
ALL_TAG = '__all__'
|
31
|
+
FORMAT_DEFAULT_MAP = {
|
32
|
+
'justify' => 'left',
|
33
|
+
}
|
34
|
+
|
30
35
|
def handle_listener(listener_name, &listener)
|
31
|
-
|
32
|
-
case listener_name
|
36
|
+
case listener_name.to_s.downcase
|
33
37
|
when '<<modified>>', '<modified>', 'modified'
|
34
38
|
modified_listener = Proc.new do |*args|
|
39
|
+
@modified_count ||= 0
|
40
|
+
@modified_count += 1
|
35
41
|
listener.call(*args)
|
42
|
+
apply_all_tag
|
43
|
+
@insert_mark_moved_proc&.call
|
36
44
|
@tk.modified = false
|
37
45
|
end
|
38
|
-
bind('<Modified>', modified_listener)
|
46
|
+
@tk.bind('<Modified>', modified_listener)
|
39
47
|
when '<<selection>>', '<selection>', 'selection'
|
40
|
-
bind('<Selection>', listener)
|
41
|
-
|
48
|
+
@tk.bind('<Selection>', listener)
|
49
|
+
when 'destroy'
|
42
50
|
super
|
51
|
+
when 'insertmarkmove', 'insertmarkmoved', 'insert_mark_move', 'insert_mark_moved'
|
52
|
+
if @insert_mark_moved_proc.nil?
|
53
|
+
handle_listener('KeyPress') do |event|
|
54
|
+
@insert_mark_moved_proc&.call
|
55
|
+
end
|
56
|
+
handle_listener('KeyRelease') do |event|
|
57
|
+
@insert_mark_moved_proc&.call
|
58
|
+
end
|
59
|
+
handle_listener('ButtonPress') do |event|
|
60
|
+
@insert_mark_moved_proc&.call
|
61
|
+
end
|
62
|
+
handle_listener('ButtonRelease') do |event|
|
63
|
+
@insert_mark_moved_proc&.call
|
64
|
+
end
|
65
|
+
end
|
66
|
+
@insert_mark = @tk.index('insert')
|
67
|
+
@insert_mark_moved_proc = Proc.new do
|
68
|
+
new_insert_mark = @tk.index('insert')
|
69
|
+
if new_insert_mark != @insert_mark
|
70
|
+
@insert_mark = new_insert_mark
|
71
|
+
listener.call(new_insert_mark)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
else
|
75
|
+
apply_all_tag
|
76
|
+
# TODO make listener pass an event that has a modifiers attribute for easy representation of :shift, :meta, :control, etc... while a letter button is pressed
|
77
|
+
@listeners ||= {}
|
78
|
+
begin
|
79
|
+
@listeners[listener_name] ||= []
|
80
|
+
@tk.tag_bind(ALL_TAG, listener_name) { |event| @listeners[listener_name].each {|l| l.call(event)} } if @listeners[listener_name].empty?
|
81
|
+
@listeners[listener_name] << listener
|
82
|
+
rescue => e
|
83
|
+
@listeners.delete(listener_name)
|
84
|
+
Glimmer::Config.logger.debug {"Unable to bind to #{listener_name} .. attempting to surround with <>"}
|
85
|
+
Glimmer::Config.logger.debug {e.full_message}
|
86
|
+
listener_name = "<#{listener_name}" if !listener_name.start_with?('<')
|
87
|
+
listener_name = "#{listener_name}>" if !listener_name.end_with?('>')
|
88
|
+
@listeners[listener_name] ||= []
|
89
|
+
@tk.tag_bind(ALL_TAG, listener_name) { |event| @listeners[listener_name].each {|l| l.call(event)} } if @listeners[listener_name].empty?
|
90
|
+
@listeners[listener_name] << listener
|
91
|
+
end
|
43
92
|
end
|
44
93
|
end
|
45
|
-
|
46
|
-
def
|
47
|
-
|
94
|
+
|
95
|
+
def edit_undo
|
96
|
+
@tk.edit_undo if @modified_count.to_i > 2 # <Modified> fires twice the first time, which is equivalent to one change.
|
48
97
|
end
|
49
98
|
|
50
|
-
def
|
51
|
-
|
99
|
+
def edit_redo
|
100
|
+
begin
|
101
|
+
@tk.edit_redo
|
102
|
+
rescue => e
|
103
|
+
# No Op
|
104
|
+
end
|
52
105
|
end
|
53
106
|
|
54
|
-
def
|
55
|
-
process_selection_ranges { |range_start, range_end|
|
107
|
+
def add_selection_format(option, value, no_selection_default: :insert_word)
|
108
|
+
process_selection_ranges(no_selection_default: no_selection_default) { |range_start, range_end| add_format(range_start, range_end, option, value) }
|
56
109
|
end
|
57
110
|
|
58
|
-
def
|
59
|
-
process_selection_ranges { |range_start, range_end|
|
111
|
+
def remove_selection_format(option, value, no_selection_default: :insert_word)
|
112
|
+
process_selection_ranges(no_selection_default: no_selection_default) { |range_start, range_end| remove_format(range_start, range_end, option, value) }
|
60
113
|
end
|
61
114
|
|
62
|
-
def
|
63
|
-
process_selection_ranges { |range_start, range_end|
|
115
|
+
def toggle_selection_format(option, value, no_selection_default: :insert_word)
|
116
|
+
process_selection_ranges(no_selection_default: no_selection_default) { |range_start, range_end| toggle_format(range_start, range_end, option, value) }
|
64
117
|
end
|
65
118
|
|
66
|
-
def
|
67
|
-
process_selection_ranges { |range_start, range_end|
|
119
|
+
def add_selection_font_format(option, value, no_selection_default: :insert_word)
|
120
|
+
process_selection_ranges(no_selection_default: no_selection_default) { |range_start, range_end| add_font_format(range_start, range_end, option, value) }
|
68
121
|
end
|
69
122
|
|
70
|
-
def
|
71
|
-
|
123
|
+
def remove_selection_font_format(option, value, no_selection_default: :insert_word)
|
124
|
+
process_selection_ranges(no_selection_default: no_selection_default) { |range_start, range_end| remove_font_format(range_start, range_end, option, value) }
|
125
|
+
end
|
126
|
+
|
127
|
+
def toggle_selection_font_format(option, value, no_selection_default: :insert_word)
|
128
|
+
process_selection_ranges(no_selection_default: no_selection_default) { |range_start, range_end| toggle_font_format(range_start, range_end, option, value) }
|
129
|
+
end
|
130
|
+
|
131
|
+
def process_selection_ranges(no_selection_default: :insert_word, &processor)
|
132
|
+
regions = @tk.tag_ranges('sel')
|
133
|
+
if regions.empty?
|
134
|
+
case no_selection_default
|
135
|
+
when :insert_word
|
136
|
+
regions = [[@tk.index('insert wordstart'), @tk.index('insert wordend + 1 char')]]
|
137
|
+
when :insert_letter
|
138
|
+
regions = [[@tk.index('insert'), @tk.index('insert + 1 char')]]
|
139
|
+
end
|
140
|
+
end
|
141
|
+
regions.each do |region|
|
72
142
|
range_start = region.first
|
73
143
|
range_end = region.last
|
74
144
|
processor.call(range_start, range_end)
|
@@ -80,7 +150,7 @@ module Glimmer
|
|
80
150
|
end
|
81
151
|
|
82
152
|
def applied_format_tags(region_start, region_end, option, value)
|
83
|
-
tag_names = @tk.tag_names - ['sel']
|
153
|
+
tag_names = @tk.tag_names - ['sel', ALL_TAG]
|
84
154
|
|
85
155
|
tag_names.select do |tag_name|
|
86
156
|
@tk.tag_ranges(tag_name).any? do |range|
|
@@ -91,9 +161,26 @@ module Glimmer
|
|
91
161
|
end
|
92
162
|
end
|
93
163
|
|
164
|
+
def applied_format_value(text_index = nil, option)
|
165
|
+
text_index ||= @tk.index('insert')
|
166
|
+
region_start = text_index
|
167
|
+
region_end = text_index
|
168
|
+
tag_names = @tk.tag_names - ['sel', ALL_TAG]
|
169
|
+
|
170
|
+
values = tag_names.map do |tag_name|
|
171
|
+
@tk.tag_ranges(tag_name).map do |range|
|
172
|
+
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)
|
173
|
+
@tk.tag_cget(tag_name, option)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end.flatten.reject {|value| value.to_s.empty?}
|
177
|
+
|
178
|
+
values.last || (@tk.send(option) rescue FORMAT_DEFAULT_MAP[option])
|
179
|
+
end
|
180
|
+
|
94
181
|
def add_format(region_start, region_end, option, value)
|
95
182
|
@@tag_number = 0 unless defined?(@@tag_number)
|
96
|
-
tag = "
|
183
|
+
tag = "tag_#{option}_#{@@tag_number += 1}"
|
97
184
|
@tk.tag_configure(tag, {option => value})
|
98
185
|
@tk.tag_add(tag, region_start, region_end)
|
99
186
|
tag
|
@@ -144,15 +231,14 @@ module Glimmer
|
|
144
231
|
def applied_font_format_tags_and_regions(region_start, region_end)
|
145
232
|
lines = value.split("\n")
|
146
233
|
tags_and_regions = []
|
147
|
-
all_tag_names = @tk.tag_names - ['sel']
|
234
|
+
all_tag_names = (@tk.tag_names - ['sel', ALL_TAG]).select {|tag_name| tag_name.include?('_font_')}
|
148
235
|
(region_start.to_i..region_end.to_i).each do |line_number|
|
149
236
|
start_character_index = 0
|
150
237
|
start_character_index = region_start.to_s.split('.').last.to_i if line_number == region_start.to_i
|
151
|
-
end_character_index = lines[line_number - 1].size
|
238
|
+
end_character_index = lines[line_number - 1].to_s.size
|
152
239
|
end_character_index = region_end.to_s.split('.').last.to_i if line_number == region_end.to_i
|
153
240
|
(start_character_index...end_character_index).each do |character_index|
|
154
241
|
text_index = "#{line_number}.#{character_index}"
|
155
|
-
# TODO reimplement the following using @tk.tag_names without arg since passing an arg seems broken and returns inaccurate results
|
156
242
|
region_tag = all_tag_names.reverse.find do |tag|
|
157
243
|
@tk.tag_cget(tag, 'font') && @tk.tag_ranges(tag).any? do |range_start, range_end|
|
158
244
|
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)
|
@@ -168,6 +254,27 @@ module Glimmer
|
|
168
254
|
end
|
169
255
|
tags_and_regions
|
170
256
|
end
|
257
|
+
|
258
|
+
def applied_font_format_value(text_index = nil, font_option)
|
259
|
+
text_index ||= @tk.index('insert')
|
260
|
+
region_start = text_index
|
261
|
+
region_end = @tk.index("#{text_index} + 1 chars")
|
262
|
+
tag_names = applied_font_format_tags_and_regions(region_start, region_end).map(&:first)
|
263
|
+
|
264
|
+
values = tag_names.map do |tag_name|
|
265
|
+
@tk.tag_ranges(tag_name).map do |range|
|
266
|
+
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)
|
267
|
+
@tk.tag_cget(tag_name, 'font')
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end.flatten.reject {|value| value.to_s.empty?}
|
271
|
+
|
272
|
+
font = values.last
|
273
|
+
|
274
|
+
value = font && font.send(font_option)
|
275
|
+
|
276
|
+
value || Hash[@tk.font.actual][font_option]
|
277
|
+
end
|
171
278
|
|
172
279
|
def add_font_format(region_start, region_end, font_option, value)
|
173
280
|
applied_font_format_tags_and_regions(region_start, region_end).each do |tag, tag_region_start, tag_region_end|
|
@@ -337,11 +444,16 @@ module Glimmer
|
|
337
444
|
self.wrap = 'none'
|
338
445
|
self.padx = 5
|
339
446
|
self.pady = 5
|
447
|
+
on('Modified') { apply_all_tag }
|
340
448
|
end
|
341
449
|
|
342
450
|
def clone_font(font)
|
343
451
|
::TkFont.new(Hash[font.actual])
|
344
452
|
end
|
453
|
+
|
454
|
+
def apply_all_tag
|
455
|
+
@tk.tag_add(ALL_TAG, '1.0', 'end')
|
456
|
+
end
|
345
457
|
end
|
346
458
|
end
|
347
459
|
end
|
@@ -0,0 +1,119 @@
|
|
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 TkToplevel
|
27
|
+
#
|
28
|
+
# Follows the Proxy Design Pattern
|
29
|
+
class ToplevelProxy < WidgetProxy
|
30
|
+
REGEX_GEOMETRY = /[x+-]/
|
31
|
+
DEFAULT_WIDTH = 190
|
32
|
+
DEFAULT_HEIGHT = 95
|
33
|
+
|
34
|
+
attr_reader :tk
|
35
|
+
|
36
|
+
def has_attribute?(attribute, *args)
|
37
|
+
%w[width height x y].include?(attribute.to_s) || super
|
38
|
+
end
|
39
|
+
|
40
|
+
def width
|
41
|
+
geometry.split(REGEX_GEOMETRY)[0].to_i
|
42
|
+
end
|
43
|
+
|
44
|
+
def height
|
45
|
+
geometry.split(REGEX_GEOMETRY)[1].to_i
|
46
|
+
end
|
47
|
+
|
48
|
+
def x
|
49
|
+
sign_number(x_sign, geometry.split(REGEX_GEOMETRY)[2].to_i)
|
50
|
+
end
|
51
|
+
|
52
|
+
def y
|
53
|
+
sign_number(y_sign, geometry.split(REGEX_GEOMETRY)[3].to_i)
|
54
|
+
end
|
55
|
+
|
56
|
+
def width=(value)
|
57
|
+
@width = value.to_i
|
58
|
+
self.geometry = "#{value.to_i}x#{@height || DEFAULT_HEIGHT}#{x_sign}#{abs_x}#{y_sign}#{abs_y}"
|
59
|
+
end
|
60
|
+
|
61
|
+
def height=(value)
|
62
|
+
@height = value.to_i
|
63
|
+
self.geometry = "#{@width || DEFAULT_WIDTH}x#{value.to_i}#{x_sign}#{abs_x}#{y_sign}#{abs_y}"
|
64
|
+
end
|
65
|
+
|
66
|
+
def x=(value)
|
67
|
+
self.geometry = "#{@width || DEFAULT_WIDTH}x#{@height || DEFAULT_HEIGHT}#{value.to_i > 0 ? '+' : '-'}#{value.to_i.abs}#{y_sign}#{abs_y}"
|
68
|
+
end
|
69
|
+
|
70
|
+
def y=(value)
|
71
|
+
self.geometry = "#{@width || DEFAULT_WIDTH}x#{@height || DEFAULT_HEIGHT}#{x_sign}#{abs_x}#{value.to_i > 0 ? '+' : '-'}#{value.to_i.abs}"
|
72
|
+
end
|
73
|
+
|
74
|
+
def handle_listener(listener_name, &listener)
|
75
|
+
case listener_name.to_s.upcase
|
76
|
+
when 'WM_DELETE_WINDOW', 'DELETE_WINDOW'
|
77
|
+
listener_name = 'WM_DELETE_WINDOW'
|
78
|
+
@tk.protocol(listener_name, &listener)
|
79
|
+
else
|
80
|
+
super
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def sign_number(sign, number)
|
87
|
+
"#{sign}1".to_i * number
|
88
|
+
end
|
89
|
+
|
90
|
+
def abs_x
|
91
|
+
geometry.split(REGEX_GEOMETRY)[2].to_i
|
92
|
+
end
|
93
|
+
|
94
|
+
def abs_y
|
95
|
+
geometry.split(REGEX_GEOMETRY)[3].to_i
|
96
|
+
end
|
97
|
+
|
98
|
+
def x_sign
|
99
|
+
geometry_signs[0]
|
100
|
+
end
|
101
|
+
|
102
|
+
def y_sign
|
103
|
+
geometry_signs[1]
|
104
|
+
end
|
105
|
+
|
106
|
+
def geometry_signs
|
107
|
+
geometry.chars.select {|char| char.match(/[+-]/)}
|
108
|
+
end
|
109
|
+
|
110
|
+
def initialize_defaults
|
111
|
+
self.background = '#ececec' if OS.mac?
|
112
|
+
on('DELETE_WINDOW') do
|
113
|
+
grab_release
|
114
|
+
destroy
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -19,6 +19,8 @@
|
|
19
19
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
20
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
21
|
|
22
|
+
require 'glimmer/data_binding/tk/one_time_observer'
|
23
|
+
|
22
24
|
module Glimmer
|
23
25
|
module Tk
|
24
26
|
# Proxy for Tk Widget objects
|
@@ -83,6 +85,27 @@ module Glimmer
|
|
83
85
|
post_add_content if @block.nil?
|
84
86
|
end
|
85
87
|
|
88
|
+
def root_parent_proxy
|
89
|
+
current = self
|
90
|
+
current = current.parent_proxy while current.parent_proxy
|
91
|
+
current
|
92
|
+
end
|
93
|
+
|
94
|
+
def toplevel_parent_proxy
|
95
|
+
ancestor_proxies.find {|widget_proxy| widget_proxy.is_a?(ToplevelProxy)}
|
96
|
+
end
|
97
|
+
|
98
|
+
# returns list of ancestors ordered from direct parent to root parent
|
99
|
+
def ancestor_proxies
|
100
|
+
ancestors = []
|
101
|
+
current = self
|
102
|
+
while current.parent_proxy
|
103
|
+
ancestors << current.parent_proxy
|
104
|
+
current = current.parent_proxy
|
105
|
+
end
|
106
|
+
ancestors
|
107
|
+
end
|
108
|
+
|
86
109
|
def children
|
87
110
|
@children ||= []
|
88
111
|
end
|
@@ -227,7 +250,7 @@ module Glimmer
|
|
227
250
|
|
228
251
|
def grid(options = {})
|
229
252
|
options = options.stringify_keys
|
230
|
-
index_in_parent = @parent_proxy
|
253
|
+
index_in_parent = @parent_proxy&.children&.index(self)
|
231
254
|
options['rowspan'] = options.delete('row_span') if options.keys.include?('row_span')
|
232
255
|
options['columnspan'] = options.delete('column_span') if options.keys.include?('column_span')
|
233
256
|
options['rowweight'] = options.delete('row_weight') if options.keys.include?('row_weight')
|
@@ -240,10 +263,12 @@ module Glimmer
|
|
240
263
|
options['columnminsize'] = options.delete('minwidth') if options.keys.include?('minwidth')
|
241
264
|
options['columnminsize'] = options.delete('min_width') if options.keys.include?('min_width')
|
242
265
|
options['columnminsize'] = options['rowminsize'] = options.delete('minsize') if options.keys.include?('minsize')
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
266
|
+
if index_in_parent
|
267
|
+
TkGrid.rowconfigure(@parent_proxy.tk, index_in_parent, 'weight'=> options.delete('rowweight')) if options.keys.include?('rowweight')
|
268
|
+
TkGrid.rowconfigure(@parent_proxy.tk, index_in_parent, 'minsize'=> options.delete('rowminsize')) if options.keys.include?('rowminsize')
|
269
|
+
TkGrid.columnconfigure(@parent_proxy.tk, index_in_parent, 'weight'=> options.delete('columnweight')) if options.keys.include?('columnweight')
|
270
|
+
TkGrid.columnconfigure(@parent_proxy.tk, index_in_parent, 'minsize'=> options.delete('columnminsize')) if options.keys.include?('columnminsize')
|
271
|
+
end
|
247
272
|
@tk.grid(options)
|
248
273
|
end
|
249
274
|
|
@@ -259,6 +284,11 @@ module Glimmer
|
|
259
284
|
apply_style({"font" => value})
|
260
285
|
end
|
261
286
|
|
287
|
+
def destroy
|
288
|
+
@tk.destroy
|
289
|
+
@on_destroy_procs&.each {|p| p.call(@tk)}
|
290
|
+
end
|
291
|
+
|
262
292
|
def apply_style(options)
|
263
293
|
@@style_number = 0 unless defined?(@@style_number)
|
264
294
|
style = "style#{@@style_number += 1}.#{@tk.class.name.split('::').last}"
|
@@ -421,17 +451,31 @@ module Glimmer
|
|
421
451
|
|
422
452
|
def handle_listener(listener_name, &listener)
|
423
453
|
listener_name = listener_name.to_s
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
Glimmer::
|
428
|
-
|
429
|
-
|
430
|
-
listener_name
|
431
|
-
|
454
|
+
if listener_name == 'destroy'
|
455
|
+
# 'destroy' is a more reliable alternative listener binding to '<Destroy>'
|
456
|
+
@on_destroy_procs ||= []
|
457
|
+
listener.singleton_class.include(Glimmer::DataBinding::Tk::OneTimeObserver) unless listener.is_a?(Glimmer::DataBinding::Tk::OneTimeObserver)
|
458
|
+
@on_destroy_procs << listener
|
459
|
+
@tk.bind('<Destroy>', listener)
|
460
|
+
parent_proxy.handle_listener(listener_name, &listener) if parent_proxy
|
461
|
+
# TODO return a listener registration object that has a deregister method
|
462
|
+
else
|
463
|
+
begin
|
464
|
+
@tk.bind(listener_name, &listener)
|
465
|
+
rescue => e
|
466
|
+
Glimmer::Config.logger.debug {"Unable to bind to #{listener_name} .. attempting to surround with <>"}
|
467
|
+
Glimmer::Config.logger.debug {e.full_message}
|
468
|
+
listener_name = "<#{listener_name}" if !listener_name.start_with?('<')
|
469
|
+
listener_name = "#{listener_name}>" if !listener_name.end_with?('>')
|
470
|
+
@tk.bind(listener_name, &listener)
|
471
|
+
end
|
432
472
|
end
|
433
473
|
end
|
434
474
|
|
475
|
+
def on(listener_name, &listener)
|
476
|
+
handle_listener(listener_name, &listener)
|
477
|
+
end
|
478
|
+
|
435
479
|
def content(&block)
|
436
480
|
Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::Tk::WidgetExpression.new, keyword, *args, &block)
|
437
481
|
end
|