glimmer-dsl-tk 0.0.25 → 0.0.29
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 +40 -0
- data/README.md +835 -122
- data/VERSION +1 -1
- data/glimmer-dsl-tk.gemspec +0 -0
- data/lib/glimmer/dsl/tk/built_in_dialog_expression.rb +57 -0
- data/lib/glimmer/dsl/tk/dsl.rb +1 -0
- data/lib/glimmer/tk/drag_and_drop_extension.rb +112 -0
- data/lib/glimmer/tk/label_proxy.rb +0 -9
- data/lib/glimmer/tk/root_proxy.rb +10 -34
- data/lib/glimmer/tk/text_proxy.rb +285 -9
- data/lib/glimmer/tk/widget_proxy.rb +88 -34
- data/samples/elaborate/meta_sample.rb +6 -6
- data/samples/hello/hello_built_in_dialog.rb +68 -0
- 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_message_box.rb +0 -4
- data/samples/hello/hello_separator.rb +69 -0
- data/samples/hello/hello_text.rb +246 -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 +19 -6
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.29
|
data/glimmer-dsl-tk.gemspec
CHANGED
Binary file
|
@@ -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
|
data/lib/glimmer/dsl/tk/dsl.rb
CHANGED
@@ -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,15 +29,6 @@ module Glimmer
|
|
29
29
|
# Follows the Proxy Design Pattern
|
30
30
|
class LabelProxy < WidgetProxy
|
31
31
|
include TextVariableOwner
|
32
|
-
|
33
|
-
def set_attribute(attribute, *args)
|
34
|
-
if attribute.to_s == 'font'
|
35
|
-
args[0] = "tk_#{args[0]}_font".camelcase(:upper) if (args[0].is_a?(Symbol) || args[0].is_a?(String)) && args[0].to_s == args[0].to_s.downcase
|
36
|
-
super
|
37
|
-
else
|
38
|
-
super
|
39
|
-
end
|
40
|
-
end
|
41
32
|
end
|
42
33
|
end
|
43
34
|
end
|
@@ -59,16 +59,6 @@ module Glimmer
|
|
59
59
|
when 'iconphoto'
|
60
60
|
args[0..-1] = [image_argument(args)]
|
61
61
|
super
|
62
|
-
when 'width'
|
63
|
-
@width = args.first.to_i
|
64
|
-
self.geometry = "#{args.first.to_i}x#{@height || DEFAULT_HEIGHT}#{x_sign}#{abs_x}#{y_sign}#{abs_y}"
|
65
|
-
when 'height'
|
66
|
-
@height = args.first.to_i
|
67
|
-
self.geometry = "#{@width || DEFAULT_WIDTH}x#{args.first.to_i}#{x_sign}#{abs_x}#{y_sign}#{abs_y}"
|
68
|
-
when 'x'
|
69
|
-
self.geometry = "#{@width || DEFAULT_WIDTH}x#{@height || DEFAULT_HEIGHT}#{args.first.to_i > 0 ? '+' : '-'}#{args.first.to_i.abs}#{y_sign}#{abs_y}"
|
70
|
-
when 'y'
|
71
|
-
self.geometry = "#{@width || DEFAULT_WIDTH}x#{@height || DEFAULT_HEIGHT}#{x_sign}#{abs_x}#{args.first.to_i > 0 ? '+' : '-'}#{args.first.to_i.abs}"
|
72
62
|
when 'resizable'
|
73
63
|
if args.size == 1 && !args.first.is_a?(Array)
|
74
64
|
self.resizable = [args.first]*2
|
@@ -80,52 +70,38 @@ module Glimmer
|
|
80
70
|
end
|
81
71
|
end
|
82
72
|
|
83
|
-
def get_attribute(attribute)
|
84
|
-
attribute = attribute.to_s
|
85
|
-
case attribute
|
86
|
-
when 'width'
|
87
|
-
geometry.split(REGEX_GEOMETRY)[0].to_i
|
88
|
-
when 'height'
|
89
|
-
geometry.split(REGEX_GEOMETRY)[1].to_i
|
90
|
-
when 'x'
|
91
|
-
sign_number(x_sign, geometry.split(REGEX_GEOMETRY)[2].to_i)
|
92
|
-
when 'y'
|
93
|
-
sign_number(y_sign, geometry.split(REGEX_GEOMETRY)[3].to_i)
|
94
|
-
else
|
95
|
-
super
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
73
|
def width
|
100
|
-
|
74
|
+
geometry.split(REGEX_GEOMETRY)[0].to_i
|
101
75
|
end
|
102
76
|
|
103
77
|
def height
|
104
|
-
|
78
|
+
geometry.split(REGEX_GEOMETRY)[1].to_i
|
105
79
|
end
|
106
80
|
|
107
81
|
def x
|
108
|
-
|
82
|
+
sign_number(x_sign, geometry.split(REGEX_GEOMETRY)[2].to_i)
|
109
83
|
end
|
110
84
|
|
111
85
|
def y
|
112
|
-
|
86
|
+
sign_number(y_sign, geometry.split(REGEX_GEOMETRY)[3].to_i)
|
113
87
|
end
|
114
88
|
|
115
89
|
def width=(value)
|
116
|
-
|
90
|
+
@width = value.to_i
|
91
|
+
self.geometry = "#{value.to_i}x#{@height || DEFAULT_HEIGHT}#{x_sign}#{abs_x}#{y_sign}#{abs_y}"
|
117
92
|
end
|
118
93
|
|
119
94
|
def height=(value)
|
120
|
-
|
95
|
+
@height = value.to_i
|
96
|
+
self.geometry = "#{@width || DEFAULT_WIDTH}x#{value.to_i}#{x_sign}#{abs_x}#{y_sign}#{abs_y}"
|
121
97
|
end
|
122
98
|
|
123
99
|
def x=(value)
|
124
|
-
|
100
|
+
self.geometry = "#{@width || DEFAULT_WIDTH}x#{@height || DEFAULT_HEIGHT}#{value.to_i > 0 ? '+' : '-'}#{value.to_i.abs}#{y_sign}#{abs_y}"
|
125
101
|
end
|
126
102
|
|
127
103
|
def y=(value)
|
128
|
-
|
104
|
+
self.geometry = "#{@width || DEFAULT_WIDTH}x#{@height || DEFAULT_HEIGHT}#{x_sign}#{abs_x}#{value.to_i > 0 ? '+' : '-'}#{value.to_i.abs}"
|
129
105
|
end
|
130
106
|
|
131
107
|
def handle_listener(listener_name, &listener)
|
@@ -43,29 +43,305 @@ module Glimmer
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
-
def
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
46
|
+
def add_selection_format(option, value)
|
47
|
+
process_selection_ranges { |range_start, range_end| add_format(range_start, range_end, option, value) }
|
48
|
+
end
|
49
|
+
|
50
|
+
def remove_selection_format(option, value)
|
51
|
+
process_selection_ranges { |range_start, range_end| remove_format(range_start, range_end, option, value) }
|
52
|
+
end
|
53
|
+
|
54
|
+
def toggle_selection_format(option, value)
|
55
|
+
process_selection_ranges { |range_start, range_end| toggle_format(range_start, range_end, option, value) }
|
56
|
+
end
|
57
|
+
|
58
|
+
def add_selection_font_format(option, value)
|
59
|
+
process_selection_ranges { |range_start, range_end| add_font_format(range_start, range_end, option, value) }
|
60
|
+
end
|
61
|
+
|
62
|
+
def remove_selection_font_format(option, value)
|
63
|
+
process_selection_ranges { |range_start, range_end| remove_font_format(range_start, range_end, option, value) }
|
64
|
+
end
|
65
|
+
|
66
|
+
def toggle_selection_font_format(option, value)
|
67
|
+
process_selection_ranges { |range_start, range_end| toggle_font_format(range_start, range_end, option, value) }
|
68
|
+
end
|
69
|
+
|
70
|
+
def process_selection_ranges(&processor)
|
71
|
+
@tk.tag_ranges('sel').each do |region|
|
72
|
+
range_start = region.first
|
73
|
+
range_end = region.last
|
74
|
+
processor.call(range_start, range_end)
|
51
75
|
end
|
52
76
|
end
|
53
77
|
|
54
|
-
def
|
55
|
-
|
56
|
-
|
78
|
+
def applied_format?(region_start, region_end, option, value)
|
79
|
+
!applied_format_tags(region_start, region_end, option, value).empty?
|
80
|
+
end
|
81
|
+
|
82
|
+
def applied_format_tags(region_start, region_end, option, value)
|
83
|
+
tag_names = @tk.tag_names - ['sel']
|
84
|
+
|
85
|
+
tag_names.select do |tag_name|
|
86
|
+
@tk.tag_ranges(tag_name).any? do |range|
|
87
|
+
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)
|
88
|
+
@tk.tag_cget(tag_name, option) == value
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def add_format(region_start, region_end, option, value)
|
95
|
+
@@tag_number = 0 unless defined?(@@tag_number)
|
96
|
+
tag = "tag#{@@tag_number += 1}"
|
97
|
+
@tk.tag_configure(tag, {option => value})
|
98
|
+
@tk.tag_add(tag, region_start, region_end)
|
99
|
+
tag
|
100
|
+
end
|
101
|
+
|
102
|
+
def remove_format(region_start, region_end, option, value)
|
103
|
+
partial_intersection_option_applied_tags = tag_names.select do |tag_name|
|
104
|
+
@tk.tag_ranges(tag_name).any? do |range|
|
105
|
+
if range.first.to_f.between?(region_start.to_f, region_end.to_f) or
|
106
|
+
range.last.to_f.between?(region_start.to_f, region_end.to_f) or
|
107
|
+
(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))
|
108
|
+
@tk.tag_cget(tag_name, option) == value
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
partial_intersection_option_applied_tags.each do |tag_name|
|
114
|
+
@tk.tag_remove(tag_name, region_start, region_end)
|
115
|
+
end
|
116
|
+
|
117
|
+
nil
|
118
|
+
end
|
119
|
+
|
120
|
+
# toggles option/value tag (removes if already applied)
|
121
|
+
def toggle_format(region_start, region_end, option, value)
|
122
|
+
if applied_format?(region_start, region_end, option, value)
|
123
|
+
remove_format(region_start, region_end, option, value)
|
57
124
|
else
|
58
|
-
|
125
|
+
add_format(region_start, region_end, option, value)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# TODO Algorithm for font option formatting
|
130
|
+
# for a region, grab all the latest tags for each subregion as well as the widget font for subregions without a tag
|
131
|
+
# 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)
|
132
|
+
# Once add and remove are implemented, implement toggle
|
133
|
+
# 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)
|
134
|
+
def applied_font_format?(region_start, region_end, font_option, value)
|
135
|
+
applied_font_format_tags_and_regions(region_start, region_end).all? do |tag, region_start, region_end|
|
136
|
+
if tag.nil?
|
137
|
+
@tk.font.send(font_option) == value
|
138
|
+
else
|
139
|
+
@tk.tag_cget(tag, 'font').send(font_option) == value
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def applied_font_format_tags_and_regions(region_start, region_end)
|
145
|
+
lines = value.split("\n")
|
146
|
+
tags_and_regions = []
|
147
|
+
all_tag_names = @tk.tag_names - ['sel']
|
148
|
+
(region_start.to_i..region_end.to_i).each do |line_number|
|
149
|
+
start_character_index = 0
|
150
|
+
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
|
152
|
+
end_character_index = region_end.to_s.split('.').last.to_i if line_number == region_end.to_i
|
153
|
+
(start_character_index...end_character_index).each do |character_index|
|
154
|
+
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
|
+
region_tag = all_tag_names.reverse.find do |tag|
|
157
|
+
@tk.tag_cget(tag, 'font') && @tk.tag_ranges(tag).any? do |range_start, range_end|
|
158
|
+
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)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end_text_index = add_to_text_index(text_index, 1)
|
162
|
+
if tags_and_regions&.last && region_tag == tags_and_regions.last.first
|
163
|
+
tags_and_regions.last[2] = end_text_index
|
164
|
+
else
|
165
|
+
tags_and_regions << [region_tag, text_index, end_text_index]
|
166
|
+
end
|
167
|
+
end
|
59
168
|
end
|
169
|
+
tags_and_regions
|
170
|
+
end
|
171
|
+
|
172
|
+
def add_font_format(region_start, region_end, font_option, value)
|
173
|
+
applied_font_format_tags_and_regions(region_start, region_end).each do |tag, tag_region_start, tag_region_end|
|
174
|
+
if tag
|
175
|
+
bigger_region_tag = @tk.tag_ranges(tag).any? do |range_start, range_end|
|
176
|
+
text_index_less_than_other_text_index?(range_start, tag_region_start) || text_index_greater_than_other_text_index?(range_end, tag_region_end)
|
177
|
+
end
|
178
|
+
if bigger_region_tag
|
179
|
+
@tk.tag_ranges(tag).each do |range_start, range_end|
|
180
|
+
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)
|
181
|
+
font = @tk.tag_cget(tag, 'font')
|
182
|
+
remove_format(range_start, range_end, 'font', font)
|
183
|
+
add_format(range_start, tag_region_start, 'font', font)
|
184
|
+
font_clone = clone_font(font)
|
185
|
+
font_clone.send("#{font_option}=", value)
|
186
|
+
add_format(tag_region_start, tag_region_end, 'font', font_clone)
|
187
|
+
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)
|
188
|
+
font = @tk.tag_cget(tag, 'font')
|
189
|
+
remove_format(range_start, range_end, 'font', font)
|
190
|
+
add_format(tag_region_end, range_end, 'font', font)
|
191
|
+
font_clone = clone_font(font)
|
192
|
+
font_clone.send("#{font_option}=", value)
|
193
|
+
add_format(tag_region_start, tag_region_end, 'font', font_clone)
|
194
|
+
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)
|
195
|
+
font = @tk.tag_cget(tag, 'font')
|
196
|
+
remove_format(range_start, range_end, 'font', font)
|
197
|
+
add_format(range_start, tag_region_start, 'font', font)
|
198
|
+
remove_format(range_start, range_end, 'font', font)
|
199
|
+
add_format(tag_region_end, range_end, '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
|
+
end
|
204
|
+
end
|
205
|
+
else
|
206
|
+
current_font = @tk.tag_cget(tag, 'font')
|
207
|
+
current_font.send("#{font_option}=", value)
|
208
|
+
end
|
209
|
+
else
|
210
|
+
add_format(tag_region_start, tag_region_end, 'font', default_font_attributes.merge(font_option => value))
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def remove_font_format(region_start, region_end, font_option, value)
|
216
|
+
applied_font_format_tags_and_regions(region_start, region_end).each do |tag, tag_region_start, tag_region_end|
|
217
|
+
if tag
|
218
|
+
bigger_region_tag = @tk.tag_ranges(tag).any? do |range_start, range_end|
|
219
|
+
text_index_less_than_other_text_index?(range_start, tag_region_start) || text_index_greater_than_other_text_index?(range_end, tag_region_end)
|
220
|
+
end
|
221
|
+
if bigger_region_tag
|
222
|
+
@tk.tag_ranges(tag).each do |range_start, range_end|
|
223
|
+
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)
|
224
|
+
font = @tk.tag_cget(tag, 'font')
|
225
|
+
remove_format(range_start, range_end, 'font', font)
|
226
|
+
add_format(range_start, subtract_from_text_index(tag_region_start, 1), 'font', font)
|
227
|
+
font_clone = clone_font(font)
|
228
|
+
font_clone.send("#{font_option}=", default_for_font_option(font_option))
|
229
|
+
add_format(tag_region_start, tag_region_end, 'font', font_clone)
|
230
|
+
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)
|
231
|
+
font = @tk.tag_cget(tag, 'font')
|
232
|
+
remove_format(range_start, range_end, 'font', font)
|
233
|
+
add_format(add_to_text_index(tag_region_end, 1), range_end, 'font', font)
|
234
|
+
font_clone = clone_font(font)
|
235
|
+
font_clone.send("#{font_option}=", default_for_font_option(font_option))
|
236
|
+
add_format(tag_region_start, tag_region_end, 'font', font_clone)
|
237
|
+
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)
|
238
|
+
font = @tk.tag_cget(tag, 'font')
|
239
|
+
remove_format(range_start, range_end, 'font', font)
|
240
|
+
add_format(range_start, subtract_from_text_index(tag_region_start, 1), 'font', font)
|
241
|
+
remove_format(range_start, range_end, 'font', font)
|
242
|
+
add_format(add_to_text_index(tag_region_end, 1), range_end, '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
|
+
end
|
247
|
+
end
|
248
|
+
else
|
249
|
+
current_font = @tk.tag_cget(tag, 'font')
|
250
|
+
current_font.send("#{font_option}=", default_for_font_option(font_option))
|
251
|
+
end
|
252
|
+
else
|
253
|
+
add_format(tag_region_start, tag_region_end, 'font', default_font_attributes.merge(font_option => default_for_font_option(font_option)))
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
# toggles option/value tag (removes if already applied)
|
259
|
+
def toggle_font_format(region_start, region_end, option, value)
|
260
|
+
if applied_font_format?(region_start, region_end, option, value)
|
261
|
+
remove_font_format(region_start, region_end, option, value)
|
262
|
+
else
|
263
|
+
add_font_format(region_start, region_end, option, value)
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
def default_for_font_option(font_option)
|
268
|
+
@tk.font.send(font_option)
|
269
|
+
end
|
270
|
+
|
271
|
+
def default_font_attributes
|
272
|
+
Hash[@tk.font.actual]
|
273
|
+
end
|
274
|
+
|
275
|
+
def add_to_text_index(text_index, addition)
|
276
|
+
text_index_parts = text_index.split('.')
|
277
|
+
line = text_index_parts.first
|
278
|
+
char_index = text_index_parts.last
|
279
|
+
char_index = char_index.to_i + addition
|
280
|
+
"#{line}.#{char_index}"
|
281
|
+
end
|
282
|
+
|
283
|
+
def subtract_from_text_index(text_index, subtraction)
|
284
|
+
add_to_text_index(text_index, -1 * subtraction)
|
285
|
+
end
|
286
|
+
|
287
|
+
def text_index_less_than_other_text_index?(region1, region2)
|
288
|
+
region1_parts = region1.to_s.split('.')
|
289
|
+
region2_parts = region2.to_s.split('.')
|
290
|
+
return true if region1_parts.first.to_i < region2_parts.first.to_i
|
291
|
+
return false if region1_parts.first.to_i > region2_parts.first.to_i
|
292
|
+
region1_parts.last.to_i < region2_parts.last.to_i
|
293
|
+
end
|
294
|
+
|
295
|
+
def text_index_less_than_or_equal_to_other_text_index?(region1, region2)
|
296
|
+
region1_parts = region1.to_s.split('.')
|
297
|
+
region2_parts = region2.to_s.split('.')
|
298
|
+
return true if region1_parts.first.to_i < region2_parts.first.to_i
|
299
|
+
return false if region1_parts.first.to_i > region2_parts.first.to_i
|
300
|
+
region1_parts.last.to_i <= region2_parts.last.to_i
|
301
|
+
end
|
302
|
+
|
303
|
+
def text_index_greater_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_greater_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 insert_image(text_index, *image_args)
|
320
|
+
TkTextImage.new(@tk, 'insert', :image => image_argument(image_args))
|
321
|
+
end
|
322
|
+
|
323
|
+
def get_open_file_to_insert_image(text_index = 'insert')
|
324
|
+
image_filename = Glimmer::DSL::Tk::BuiltInDialogExpression.new.interpret(nil, 'get_open_file', filetypes: {
|
325
|
+
'PNG Images' => '.png',
|
326
|
+
'Gif Images' => '.gif',
|
327
|
+
'PPM Images' => '.ppm'
|
328
|
+
})
|
329
|
+
insert_image('insert', image_filename) unless image_filename.nil? || image_filename.to_s.empty?
|
60
330
|
end
|
61
331
|
|
62
332
|
private
|
63
333
|
|
64
334
|
def initialize_defaults
|
65
335
|
super
|
336
|
+
self.font = {family: 'Courier New'}
|
337
|
+
self.wrap = 'none'
|
66
338
|
self.padx = 5
|
67
339
|
self.pady = 5
|
68
340
|
end
|
341
|
+
|
342
|
+
def clone_font(font)
|
343
|
+
::TkFont.new(Hash[font.actual])
|
344
|
+
end
|
69
345
|
end
|
70
346
|
end
|
71
347
|
end
|