glimmer-dsl-tk 0.0.27 → 0.0.31
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +34 -0
- data/README.md +734 -59
- 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/built_in_dialog_expression.rb +57 -0
- data/lib/glimmer/dsl/tk/data_binding_expression.rb +3 -2
- data/lib/glimmer/dsl/tk/dsl.rb +1 -0
- data/lib/glimmer/dsl/tk/widget_expression.rb +5 -1
- data/lib/glimmer/tk/drag_and_drop_extension.rb +112 -0
- data/lib/glimmer/tk/label_proxy.rb +0 -10
- data/lib/glimmer/tk/root_proxy.rb +2 -77
- data/lib/glimmer/tk/text_proxy.rb +31 -23
- data/lib/glimmer/tk/toplevel_proxy.rb +119 -0
- data/lib/glimmer/tk/widget_proxy.rb +68 -15
- data/samples/elaborate/meta_sample.rb +6 -6
- data/samples/hello/hello_built_in_dialog.rb +68 -0
- data/samples/hello/hello_drag_and_drop.rb +159 -0
- data/samples/hello/hello_message_box.rb +0 -4
- data/samples/hello/hello_separator.rb +69 -0
- data/samples/hello/hello_text.rb +236 -37
- data/samples/hello/images/align-center.png +0 -0
- data/samples/hello/images/align-left.png +0 -0
- data/samples/hello/images/align-right.png +0 -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/picture.png +0 -0
- data/samples/hello/images/redo.png +0 -0
- data/samples/hello/images/search.png +0 -0
- data/samples/hello/images/undo.png +0 -0
- metadata +22 -5
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.31
|
data/glimmer-dsl-tk.gemspec
CHANGED
Binary file
|
@@ -0,0 +1,39 @@
|
|
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/data_binding/observer'
|
23
|
+
|
24
|
+
module Glimmer
|
25
|
+
module DataBinding
|
26
|
+
module Tk
|
27
|
+
# Decorator for Observer that ensures it is only called once
|
28
|
+
# (subsequent invocations of call method do nothing)
|
29
|
+
module OneTimeObserver
|
30
|
+
def call(value)
|
31
|
+
unless @called
|
32
|
+
super
|
33
|
+
@called = true
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,57 @@
|
|
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'
|
23
|
+
require 'glimmer/dsl/expression'
|
24
|
+
|
25
|
+
module Glimmer
|
26
|
+
module DSL
|
27
|
+
module Tk
|
28
|
+
class BuiltInDialogExpression < Expression
|
29
|
+
def can_interpret?(parent, keyword, *args, &block)
|
30
|
+
keyword = "get_#{keyword}" if keyword.start_with?('open')
|
31
|
+
(keyword.start_with?('get') or keyword.start_with?('choose')) and
|
32
|
+
(
|
33
|
+
(block.nil? and ::Tk.respond_to?(keyword.camelcase)) or
|
34
|
+
keyword == 'choose_font'
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
def interpret(parent, keyword, *args, &block)
|
39
|
+
if args.first.is_a?(Hash)
|
40
|
+
options = args.first.symbolize_keys
|
41
|
+
options[:filetypes] = options.delete(:file_types) if options.keys.include?(:file_types)
|
42
|
+
options[:filetypes] = options[:filetypes].map { |key, value| "{#{key}} {#{value}}" } if options[:filetypes].is_a?(Hash)
|
43
|
+
options[:parent] = options[:parent].tk if options[:parent].is_a?(Glimmer::Tk::RootProxy)
|
44
|
+
args[0] = options
|
45
|
+
end
|
46
|
+
keyword = "get_#{keyword}" if keyword.start_with?('open') || keyword.start_with?('save') || keyword.start_with?('multiple')
|
47
|
+
if keyword == 'choose_font'
|
48
|
+
TkFont::Fontchooser.configure(:font => args, :command => block)
|
49
|
+
TkFont::Fontchooser.show
|
50
|
+
else
|
51
|
+
::Tk.send(keyword.camelcase, *args)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -38,12 +38,13 @@ module Glimmer
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def interpret(parent, keyword, *args, &block)
|
41
|
-
parent.class
|
42
41
|
model_binding = args[0]
|
43
42
|
widget_binding_parameters = [parent, keyword]
|
44
43
|
widget_binding = DataBinding::Tk::WidgetBinding.new(*widget_binding_parameters)
|
45
44
|
#TODO make this options observer dependent and all similar observers in widget specific data binding handlers
|
46
|
-
widget_binding.observe(model_binding)
|
45
|
+
registration = widget_binding.observe(model_binding)
|
46
|
+
parent.on('destroy') { registration.deregister }
|
47
|
+
|
47
48
|
# TODO simplify this logic and put it where it belongs
|
48
49
|
parent.add_observer(model_binding, keyword) if parent.respond_to?(:add_observer, [model_binding, keyword])
|
49
50
|
widget_binding.call(model_binding.evaluate_property)
|
data/lib/glimmer/dsl/tk/dsl.rb
CHANGED
@@ -33,11 +33,15 @@ module Glimmer
|
|
33
33
|
|
34
34
|
def can_interpret?(parent, keyword, *args, &block)
|
35
35
|
!EXCLUDED_KEYWORDS.include?(keyword) and
|
36
|
-
parent.respond_to?(:tk) and
|
37
36
|
Glimmer::Tk::WidgetProxy.widget_exists?(keyword)
|
37
|
+
(parent.respond_to?(:tk) or args.first.respond_to?(:tk))
|
38
38
|
end
|
39
39
|
|
40
40
|
def interpret(parent, keyword, *args, &block)
|
41
|
+
if keyword == 'toplevel' && args.first.respond_to?(:tk)
|
42
|
+
parent = args.first
|
43
|
+
args[0] = args.first.tk
|
44
|
+
end
|
41
45
|
Glimmer::Tk::WidgetProxy.create(keyword, parent, args, &block)
|
42
46
|
end
|
43
47
|
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require "json"
|
2
|
+
|
3
|
+
module Glimmer
|
4
|
+
module Tk
|
5
|
+
class WidgetProxy
|
6
|
+
attr_accessor :on_drag_motion_block
|
7
|
+
|
8
|
+
def drag_source=(value)
|
9
|
+
@drag_source = value
|
10
|
+
if @drag_source
|
11
|
+
make_draggable
|
12
|
+
else
|
13
|
+
make_non_draggable
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def on_drag_start_block=(block)
|
18
|
+
@on_drag_start_block = block
|
19
|
+
make_draggable
|
20
|
+
end
|
21
|
+
|
22
|
+
def on_drop_block=(value)
|
23
|
+
@on_drop_block = value
|
24
|
+
self.tk.bind("<DropEvent>", proc { |tk_event|
|
25
|
+
drop_event = DragAndDropEvent.json_create(JSON.parse("{" + tk_event.detail + "}"))
|
26
|
+
@on_drop_block.call(drop_event) if self.tk == drop_event.target
|
27
|
+
})
|
28
|
+
self.tk.bind("<DropCheckEvent>", proc { |tk_event|
|
29
|
+
drop_check_event = DragAndDropEvent.json_create(JSON.parse("{" + tk_event.detail + "}"))
|
30
|
+
drop_check_event.source.event_generate("<DropAcceptedEvent>")
|
31
|
+
})
|
32
|
+
end
|
33
|
+
|
34
|
+
def textvariable_defined?
|
35
|
+
tk_widget_has_attribute_setter?(:textvariable) && !tk.textvariable.nil?
|
36
|
+
end
|
37
|
+
|
38
|
+
def make_draggable
|
39
|
+
drag_event = nil
|
40
|
+
bind("<DropAcceptedEvent>", proc { |event| drag_event.drop_accepted = true })
|
41
|
+
bind("B1-Motion", proc { |tk_event|
|
42
|
+
if drag_event.nil?
|
43
|
+
tooltip = TkToplevel.new(root).overrideredirect(1) #create tooltip window to display dragged data
|
44
|
+
tooltip.geometry("+#{tk_event.x_root + 10}+#{tk_event.y_root - 2}")
|
45
|
+
drag_event = DragAndDropEvent.new(self.tk, nil, tooltip, tk_event.x_root, tk_event.y_root, nil, false)
|
46
|
+
if @drag_source
|
47
|
+
tk_event.widget.configure(:cursor => "hand2")
|
48
|
+
# Default data to drag is text
|
49
|
+
drag_event.data = if textvariable_defined? then tk.textvariable.value elsif has_attribute?(:text) then tk.text end
|
50
|
+
TkLabel.new(tooltip) { text drag_event.data }.pack
|
51
|
+
elsif !@on_drag_start_block.nil?
|
52
|
+
@on_drag_start_block.call(drag_event)
|
53
|
+
TkLabel.new(tooltip) { text drag_event.data }.pack if tooltip.winfo_children().length == 0
|
54
|
+
end
|
55
|
+
else
|
56
|
+
drag_event.x_root, drag_event.y_root = tk_event.x_root, tk_event.y_root
|
57
|
+
drag_event.drop_accepted = false
|
58
|
+
move_over_widget = tk_event.widget.winfo_containing(tk_event.x_root, tk_event.y_root)
|
59
|
+
drag_event.target = move_over_widget
|
60
|
+
move_over_widget.event_generate("<DropCheckEvent>", :data => drag_event.to_json)
|
61
|
+
if @on_drag_motion_block.nil?
|
62
|
+
# Default motion behavior:
|
63
|
+
# 1.Change cursor to show whether text can be dropped.
|
64
|
+
# 2.Move tooltip with dragged data.
|
65
|
+
if drag_event.drop_accepted
|
66
|
+
tk_event.widget.configure(:cursor => "hand1")
|
67
|
+
else
|
68
|
+
tk_event.widget.configure(:cursor => "hand2")
|
69
|
+
end
|
70
|
+
drag_event.tooltip.geometry("+#{tk_event.x_root + 10}+#{tk_event.y_root - 2}")
|
71
|
+
else
|
72
|
+
@on_drag_motion_block.call(drag_event)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
})
|
76
|
+
bind("ButtonRelease-1", proc { |tk_event|
|
77
|
+
if drag_event
|
78
|
+
drag_event.target = tk_event.widget.winfo_containing(tk_event.x_root, tk_event.y_root)
|
79
|
+
drag_event.source.configure(:cursor => "")
|
80
|
+
drag_event.target.event_generate("<DropEvent>", :data => drag_event.to_json)
|
81
|
+
drag_event.tooltip.destroy
|
82
|
+
drag_event = nil
|
83
|
+
end
|
84
|
+
})
|
85
|
+
end
|
86
|
+
|
87
|
+
def make_non_draggable
|
88
|
+
@tk.bind_remove("B1-Motion")
|
89
|
+
@tk.bind_remove("ButtonRelease-1")
|
90
|
+
@tk.bind_remove("<DropAcceptedEvent>")
|
91
|
+
end
|
92
|
+
|
93
|
+
DragAndDropEvent = Struct.new(:source, :target, :tooltip, :x_root, :y_root, :data, :drop_accepted) do
|
94
|
+
def as_json(*)
|
95
|
+
klass = self.class.name
|
96
|
+
{
|
97
|
+
JSON.create_id => klass,
|
98
|
+
"v" => [values[0].object_id, values[1].object_id, values[2].object_id].concat(values.drop 3),
|
99
|
+
}
|
100
|
+
end
|
101
|
+
|
102
|
+
def to_json(*args)
|
103
|
+
as_json.to_json(*args)
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.json_create(object)
|
107
|
+
new(*[ObjectSpace._id2ref(object["v"][0]), ObjectSpace._id2ref(object["v"][1]), ObjectSpace._id2ref(object["v"][2])].concat(object["v"].drop 3))
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -29,16 +29,6 @@ module Glimmer
|
|
29
29
|
# Follows the Proxy Design Pattern
|
30
30
|
class LabelProxy < WidgetProxy
|
31
31
|
include TextVariableOwner
|
32
|
-
|
33
|
-
FONTS_PREDEFINED = %w[default text fixed menu heading caption small_caption icon tooltip]
|
34
|
-
|
35
|
-
def font=(value)
|
36
|
-
if (value.is_a?(Symbol) || value.is_a?(String)) && FONTS_PREDEFINED.include?(value.to_s.downcase)
|
37
|
-
@tk.font = "tk_#{value}_font".camelcase(:upper)
|
38
|
-
else
|
39
|
-
super
|
40
|
-
end
|
41
|
-
end
|
42
32
|
end
|
43
33
|
end
|
44
34
|
end
|
@@ -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
|
@@ -28,8 +28,7 @@ module Glimmer
|
|
28
28
|
# Follows the Proxy Design Pattern
|
29
29
|
class TextProxy < WidgetProxy
|
30
30
|
def handle_listener(listener_name, &listener)
|
31
|
-
|
32
|
-
case listener_name
|
31
|
+
case listener_name.to_s.downcase
|
33
32
|
when '<<modified>>', '<modified>', 'modified'
|
34
33
|
modified_listener = Proc.new do |*args|
|
35
34
|
listener.call(*args)
|
@@ -38,27 +37,23 @@ module Glimmer
|
|
38
37
|
bind('<Modified>', modified_listener)
|
39
38
|
when '<<selection>>', '<selection>', 'selection'
|
40
39
|
bind('<Selection>', listener)
|
41
|
-
|
40
|
+
when 'destroy'
|
42
41
|
super
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
42
|
+
else
|
43
|
+
@tk.tag_add('__all__', '1.0', 'end') unless @tk.tag_names.include?('__all__')
|
44
|
+
# 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
|
45
|
+
begin
|
46
|
+
@tk.tag_bind('__all__', listener_name, &listener)
|
47
|
+
rescue => e
|
48
|
+
Glimmer::Config.logger.debug {"Unable to bind to #{listener_name} .. attempting to surround with <>"}
|
49
|
+
Glimmer::Config.logger.debug {e.full_message}
|
50
|
+
listener_name = "<#{listener_name}" if !listener_name.start_with?('<')
|
51
|
+
listener_name = "#{listener_name}>" if !listener_name.end_with?('>')
|
52
|
+
@tk.tag_bind('__all__', listener_name, &listener)
|
53
53
|
end
|
54
|
-
@text = value
|
55
54
|
end
|
56
55
|
end
|
57
|
-
|
58
|
-
def text
|
59
|
-
@text = get("1.0", 'end')
|
60
|
-
end
|
61
|
-
|
56
|
+
|
62
57
|
def add_selection_format(option, value)
|
63
58
|
process_selection_ranges { |range_start, range_end| add_format(range_start, range_end, option, value) }
|
64
59
|
end
|
@@ -158,7 +153,7 @@ module Glimmer
|
|
158
153
|
end
|
159
154
|
|
160
155
|
def applied_font_format_tags_and_regions(region_start, region_end)
|
161
|
-
lines =
|
156
|
+
lines = value.split("\n")
|
162
157
|
tags_and_regions = []
|
163
158
|
all_tag_names = @tk.tag_names - ['sel']
|
164
159
|
(region_start.to_i..region_end.to_i).each do |line_number|
|
@@ -332,10 +327,19 @@ module Glimmer
|
|
332
327
|
region1_parts.last.to_i >= region2_parts.last.to_i
|
333
328
|
end
|
334
329
|
|
335
|
-
def
|
336
|
-
|
330
|
+
def insert_image(text_index, *image_args)
|
331
|
+
TkTextImage.new(@tk, 'insert', :image => image_argument(image_args))
|
337
332
|
end
|
338
|
-
|
333
|
+
|
334
|
+
def get_open_file_to_insert_image(text_index = 'insert')
|
335
|
+
image_filename = Glimmer::DSL::Tk::BuiltInDialogExpression.new.interpret(nil, 'get_open_file', filetypes: {
|
336
|
+
'PNG Images' => '.png',
|
337
|
+
'Gif Images' => '.gif',
|
338
|
+
'PPM Images' => '.ppm'
|
339
|
+
})
|
340
|
+
insert_image('insert', image_filename) unless image_filename.nil? || image_filename.to_s.empty?
|
341
|
+
end
|
342
|
+
|
339
343
|
private
|
340
344
|
|
341
345
|
def initialize_defaults
|
@@ -345,6 +349,10 @@ module Glimmer
|
|
345
349
|
self.padx = 5
|
346
350
|
self.pady = 5
|
347
351
|
end
|
352
|
+
|
353
|
+
def clone_font(font)
|
354
|
+
::TkFont.new(Hash[font.actual])
|
355
|
+
end
|
348
356
|
end
|
349
357
|
end
|
350
358
|
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
|