glimmer-dsl-tk 0.0.30 → 0.0.34
Sign up to get free protection for your applications and to get access to all the features.
- 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
|