tkwrapper 1.2.0 → 1.6.0
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/lib/tk_extensions.rb +7 -6
- data/lib/tkwrapper.rb +7 -1
- data/lib/util/tk/cell.rb +37 -0
- data/lib/util/tk/finder.rb +66 -0
- data/lib/util/tk/font.rb +51 -0
- data/lib/util/tk/tk.rb +8 -0
- data/lib/widgets/auto_resize_entry.rb +8 -42
- data/lib/widgets/base/base.rb +1 -0
- data/lib/widgets/base/comparator_item_store.rb +50 -0
- data/lib/widgets/base/configuration.rb +15 -7
- data/lib/widgets/base/manager.rb +32 -38
- data/lib/widgets/base/match.rb +18 -0
- data/lib/widgets/base/matcher.rb +58 -0
- data/lib/widgets/base/matches.rb +38 -0
- data/lib/widgets/base/widget.rb +35 -98
- data/lib/widgets/base/widget_store.rb +38 -0
- data/lib/widgets/base/window_info.rb +13 -0
- data/lib/widgets/button.rb +7 -0
- data/lib/widgets/grid.rb +2 -2
- data/lib/widgets/menu.rb +8 -5
- data/lib/widgets/mount_point.rb +25 -0
- data/lib/widgets/widgets.rb +2 -0
- metadata +29 -4
- data/lib/widgets/base/widgets.rb +0 -31
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 25ebb6c8831b18ae3b55f4d98896cc5cf824d628fd4bc245b6fc1ed9fd8c5ef0
|
|
4
|
+
data.tar.gz: b90ca62d87faa02e1a07f5f1145e175d55dfcfeacd71782c709a484038701dfc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b4d1820254e60b3d9ce4be52a8875b0acbda213dac7aa200352d794ff1146611e84ac1f9bb16589f6fa6d61ba3b65468c940cf27d965ead1e3913fbc5b1cce7e
|
|
7
|
+
data.tar.gz: fd00a3c5ae1c29aa66762f554a43223a5ce32ef538c340a8549ba56fb12853743f80ae90862aac30f99383be61497fe1744bbb85e7ca7ef1e83d32785e4f5133
|
data/lib/tk_extensions.rb
CHANGED
|
@@ -26,12 +26,13 @@ module TkExtensions
|
|
|
26
26
|
# the class names may be aliases (e.g. Tk::Tile::Entry is an alias for
|
|
27
27
|
# Tk::Tile::TEntry)
|
|
28
28
|
tk_class_names = [
|
|
29
|
-
'TkRoot',
|
|
30
|
-
'TkText',
|
|
31
|
-
'TkMenu',
|
|
32
|
-
'Tk::Tile::Entry',
|
|
33
|
-
'Tk::Tile::Frame',
|
|
34
|
-
'Tk::Tile::Label' # becomes TkExtensions::Label
|
|
29
|
+
'TkRoot', # becomes TkExtensions::TkWidgets::TkRoot
|
|
30
|
+
'TkText', # becomes TkExtensions::TkWidgets::TkText
|
|
31
|
+
'TkMenu', # becomes TkExtensions::TkWidgets::TkMenu
|
|
32
|
+
'Tk::Tile::Entry', # becomes TkExtensions::TkWidgets::Entry
|
|
33
|
+
'Tk::Tile::Frame', # becomes TkExtensions::TkWidgets::Frame
|
|
34
|
+
'Tk::Tile::Label', # becomes TkExtensions::TkWidgets::Label
|
|
35
|
+
'Tk::Tile::Button' # becomes TkExtensions::TkWidgets::Button
|
|
35
36
|
]
|
|
36
37
|
tk_class_names.each do |tk_class_name|
|
|
37
38
|
# extract last part of the name (e.g. Tk::Tile::Entry => Entry)
|
data/lib/tkwrapper.rb
CHANGED
|
@@ -2,10 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
LIB_DIR = __dir__
|
|
4
4
|
|
|
5
|
-
module TkWrapper
|
|
5
|
+
module TkWrapper
|
|
6
|
+
module Util
|
|
7
|
+
module Tk
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
6
11
|
|
|
7
12
|
require_relative 'widgets/widgets'
|
|
8
13
|
|
|
9
14
|
module TkWrapper
|
|
15
|
+
Manager = TkWrapper::Widgets::Base::Manager
|
|
10
16
|
Widget = TkWrapper::Widgets::Base::Widget
|
|
11
17
|
end
|
data/lib/util/tk/cell.rb
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'tk'
|
|
4
|
+
|
|
5
|
+
class TkWrapper::Util::Tk::Cell
|
|
6
|
+
def initialize(widget)
|
|
7
|
+
@widget = widget
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# returns the bounding box of the tk_widget
|
|
11
|
+
def bbox
|
|
12
|
+
return unless (container = container_parent)
|
|
13
|
+
|
|
14
|
+
grid_info = TkGrid.info(@widget.tk_widget)
|
|
15
|
+
start_col = grid_info['column']
|
|
16
|
+
end_col = start_col + grid_info['columnspan'] - 1
|
|
17
|
+
start_row = grid_info['row']
|
|
18
|
+
end_row = start_row + grid_info['rowspan'] - 1
|
|
19
|
+
|
|
20
|
+
container.tk_widget.update
|
|
21
|
+
TkGrid.bbox(container.tk_widget, start_col, start_row, end_col, end_row)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
# the first parent, which contains a tk_widget, which is really different
|
|
27
|
+
# from self.tk_widget
|
|
28
|
+
def container_parent
|
|
29
|
+
container = @widget.parent
|
|
30
|
+
while container.tk_widget == @widget.tk_widget
|
|
31
|
+
return unless container.parent # not in a grid?
|
|
32
|
+
|
|
33
|
+
container = container.parent
|
|
34
|
+
end
|
|
35
|
+
container
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "#{LIB_DIR}/widgets/base/manager"
|
|
4
|
+
require "#{LIB_DIR}/widgets/base/matcher"
|
|
5
|
+
require "#{LIB_DIR}/widgets/base/matches"
|
|
6
|
+
|
|
7
|
+
class TkWrapper::Util::Tk::Finder
|
|
8
|
+
Match = TkWrapper::Widgets::Base::Match
|
|
9
|
+
Matcher = TkWrapper::Widgets::Base::Matcher
|
|
10
|
+
Matches = TkWrapper::Widgets::Base::Matches
|
|
11
|
+
|
|
12
|
+
def initialize(widgets: nil, lookup: nil)
|
|
13
|
+
@lookup = lookup
|
|
14
|
+
@widgets = widgets
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def iter(comparators, widgets = @widgets, lookup = @lookup)
|
|
18
|
+
comparators = [comparators] unless comparators.is_a?(Array)
|
|
19
|
+
Enumerator.new do |y|
|
|
20
|
+
comparators = each_widget_lookup_match(lookup, comparators) { |m| y << m }
|
|
21
|
+
each_widget_comparator_match(widgets, comparators) { |m| y << m }
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def find(comparators, widgets = @widgets, lookup = @lookup)
|
|
26
|
+
iter(comparators, widgets, lookup, &:itself).first
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def find_all(comparators, widgets = @widgets, lookup = @lookup)
|
|
30
|
+
it = iter(comparators, widgets, lookup, &:itself)
|
|
31
|
+
it.each_with_object(Matches.new) { |match, matches| matches.push(match) }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def create_value_matchers(comparators)
|
|
37
|
+
comparators.map { |comparator| Matcher.new(comparator: comparator) }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def each_widget_lookup_match(lookup, comparators, &block)
|
|
41
|
+
return comparators unless lookup
|
|
42
|
+
|
|
43
|
+
comparators.filter do |comparator|
|
|
44
|
+
next true unless [String, Symbol].include?(comparator.class)
|
|
45
|
+
|
|
46
|
+
(lookup[comparator] || []).each do |widget|
|
|
47
|
+
block.call(Match.new(comparator, widget: widget))
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
false
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def each_widget_comparator_match(widgets, comparators, &block)
|
|
55
|
+
matchers = create_value_matchers(comparators)
|
|
56
|
+
|
|
57
|
+
widgets.each do |widget|
|
|
58
|
+
ids = widget.ids.empty? ? [nil] : widget.ids
|
|
59
|
+
ids.each do |id|
|
|
60
|
+
matchers.each do |matcher|
|
|
61
|
+
(match = matcher.match(id, widget)) && block.call(match)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
data/lib/util/tk/font.rb
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'tk'
|
|
4
|
+
require 'forwardable'
|
|
5
|
+
|
|
6
|
+
require_relative 'tk'
|
|
7
|
+
|
|
8
|
+
class TkWrapper::Util::Tk::Font
|
|
9
|
+
def initialize(tk_widget)
|
|
10
|
+
@tk_widget = tk_widget
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
%i[family size weight slant underline overstrike].each do |option|
|
|
14
|
+
define_method("#{option}=") do |value, **args|
|
|
15
|
+
load unless @config
|
|
16
|
+
args[:update] ||= false
|
|
17
|
+
@config[option] = value
|
|
18
|
+
update if args[:update]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
define_method(option) do
|
|
22
|
+
load unless @config
|
|
23
|
+
@config[option]
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def with_update(&block)
|
|
28
|
+
block.call(self)
|
|
29
|
+
update
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def method_missing(method, *args)
|
|
33
|
+
if TkFont.respond_to?(method)
|
|
34
|
+
TkFont.send(method, @tk_widget.font, *args)
|
|
35
|
+
else
|
|
36
|
+
super
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def respond_to_missing?(method, *)
|
|
41
|
+
TkFont.respond_to?(method) || super
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def load
|
|
45
|
+
@config = TkFont.actual(@tk_widget.font).to_h.transform_keys(&:to_sym)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def update
|
|
49
|
+
@tk_widget.font = TkFont.new(@config)
|
|
50
|
+
end
|
|
51
|
+
end
|
data/lib/util/tk/tk.rb
ADDED
|
@@ -4,14 +4,14 @@ class TkWrapper::Widgets::AutoResizeEntry < TkWrapper::Widgets::Entry
|
|
|
4
4
|
# auto resizes on user input, only works if in the grid geometry manager of tk
|
|
5
5
|
attr_accessor :min_width, :add_width
|
|
6
6
|
|
|
7
|
-
def initialize(
|
|
8
|
-
@min_width = config
|
|
9
|
-
@add_width = config
|
|
10
|
-
super(
|
|
7
|
+
def initialize(**args)
|
|
8
|
+
@min_width = args.dig(:config, :min_width) || 0
|
|
9
|
+
@add_width = args.dig(:config, :add_width) || 0
|
|
10
|
+
super(**args)
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
def build(parent,
|
|
14
|
-
super(parent,
|
|
13
|
+
def build(parent, **args)
|
|
14
|
+
super(parent, **args)
|
|
15
15
|
parent.tk_widget.bind('Configure') { resize }
|
|
16
16
|
tk_widget.textvariable = TkVariable.new unless tk_widget.textvariable
|
|
17
17
|
tk_widget.textvariable.trace('write') { resize }
|
|
@@ -26,43 +26,9 @@ class TkWrapper::Widgets::AutoResizeEntry < TkWrapper::Widgets::Entry
|
|
|
26
26
|
tk_widget.textvariable.value
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
def config_for_dummy_label
|
|
30
|
-
grid_info = TkGrid.info(tk_widget)
|
|
31
|
-
{ config: { grid: {
|
|
32
|
-
row: grid_info['row'],
|
|
33
|
-
column: grid_info['column'],
|
|
34
|
-
columnspan: grid_info['columnspan'],
|
|
35
|
-
sticky: 'nw'
|
|
36
|
-
} } }
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def create_dummy_label_with_same_size(&block)
|
|
40
|
-
label = TkWrapper::Widgets::Label.new(**config_for_dummy_label)
|
|
41
|
-
label.build(@parent)
|
|
42
|
-
label.tk_widget.text = value
|
|
43
|
-
label.tk_widget.lower
|
|
44
|
-
result = block.call(label)
|
|
45
|
-
label.tk_widget.destroy
|
|
46
|
-
result
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def text_width_in_pixel
|
|
50
|
-
create_dummy_label_with_same_size do |label|
|
|
51
|
-
@parent.tk_widget.update
|
|
52
|
-
label.tk_widget.winfo_width
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def textwidth_and_maxwidth_in_pixel
|
|
57
|
-
create_dummy_frame_with_same_size_label do |frame, label|
|
|
58
|
-
{ text_width: label.tk_widget.winfo_width,
|
|
59
|
-
max_width: frame.tk_widget.winfo_width }
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
|
|
63
29
|
def resize
|
|
64
|
-
max_width =
|
|
65
|
-
text_width =
|
|
30
|
+
max_width = [@cell.bbox[2], 0].max
|
|
31
|
+
text_width = @font.measure(value)
|
|
66
32
|
new_width = [[@min_width, text_width + @add_width].max, max_width].min
|
|
67
33
|
tk_widget.width = 0
|
|
68
34
|
tk_widget.grid(ipadx: new_width / 2.0)
|
data/lib/widgets/base/base.rb
CHANGED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'matcher'
|
|
4
|
+
|
|
5
|
+
class TkWrapper::Widgets::Base::ComparatorItemStore
|
|
6
|
+
Matcher = TkWrapper::Widgets::Base::Matcher
|
|
7
|
+
|
|
8
|
+
def initialize
|
|
9
|
+
@key_map = {} # for fast lookup
|
|
10
|
+
@comparator_map = {} # for lookup using comparisons by Matcher class
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def map_key?(key)
|
|
14
|
+
[String, Symbol].include?(key)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def push(key, *items)
|
|
18
|
+
if map_key?(key)
|
|
19
|
+
(@key_map[key] ||= []).concat(items)
|
|
20
|
+
else
|
|
21
|
+
(@comparator_map[key] ||= []).concat(items)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# returns [{items: [...], match: Match}, {items: [...]}, ...]
|
|
26
|
+
def items_and_matches_for_widget(widget)
|
|
27
|
+
widget.ids.reduce([]) do |items, id|
|
|
28
|
+
items + items_from_key_map(id) + items_from_comparator_map(id, widget)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def [](key)
|
|
33
|
+
items = map_key?(key) ? @key_map[key] : @comparator_map[key]
|
|
34
|
+
items&.length == 1 ? items.first : items
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def items_from_key_map(id)
|
|
40
|
+
(items = @key_map[id]) ? [{ items: items }] : []
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def items_from_comparator_map(id, widget)
|
|
44
|
+
matcher = Matcher.new(value: id)
|
|
45
|
+
|
|
46
|
+
@comparator_map.filter_map do |(comparator, items)|
|
|
47
|
+
(m = matcher.match(comparator, widget)) && { items: items, match: m }
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -21,7 +21,7 @@ class TkWrapper::Widgets::Base::Configuration
|
|
|
21
21
|
}.freeze
|
|
22
22
|
|
|
23
23
|
NON_TK_OPTIONS = %i[
|
|
24
|
-
|
|
24
|
+
tk_class tearoff weights menu min_width add_width
|
|
25
25
|
].freeze
|
|
26
26
|
|
|
27
27
|
def initialize(config)
|
|
@@ -77,17 +77,23 @@ class TkWrapper::Widgets::Base::Configuration
|
|
|
77
77
|
@config[:grid].reject { |option, _| NON_TK_OPTIONS.include?(option) }
|
|
78
78
|
end
|
|
79
79
|
|
|
80
|
+
def configure_grid(tk_widget)
|
|
81
|
+
grid = grid(only_tk_options: true)
|
|
82
|
+
return if grid.empty?
|
|
83
|
+
|
|
84
|
+
tk_widget.grid(grid)
|
|
85
|
+
end
|
|
86
|
+
|
|
80
87
|
def configure_tk_widget(tk_widget)
|
|
81
88
|
@config.each do |option, value|
|
|
82
89
|
next if NON_TK_OPTIONS.include?(option)
|
|
83
90
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
91
|
+
case option
|
|
92
|
+
when :grid then configure_grid(tk_widget)
|
|
93
|
+
when :pack then tk_widget.pack(value)
|
|
94
|
+
when :place then tk_widget.place(value)
|
|
95
|
+
else tk_widget[option] = value
|
|
88
96
|
end
|
|
89
|
-
|
|
90
|
-
tk_widget[option] = value
|
|
91
97
|
end
|
|
92
98
|
|
|
93
99
|
configure_weights(tk_widget)
|
|
@@ -112,6 +118,8 @@ class TkWrapper::Widgets::Base::Configuration
|
|
|
112
118
|
end
|
|
113
119
|
|
|
114
120
|
def merge_global_configurations(manager, widget)
|
|
121
|
+
return unless manager
|
|
122
|
+
|
|
115
123
|
merge(*manager.configurations(widget))
|
|
116
124
|
end
|
|
117
125
|
end
|
data/lib/widgets/base/manager.rb
CHANGED
|
@@ -1,62 +1,56 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
require_relative 'comparator_item_store'
|
|
4
|
+
require_relative 'widget_store'
|
|
5
|
+
|
|
4
6
|
class TkWrapper::Widgets::Base::Manager
|
|
7
|
+
ComparatorItemStore = TkWrapper::Widgets::Base::ComparatorItemStore
|
|
8
|
+
WidgetStore = TkWrapper::Widgets::Base::WidgetStore
|
|
9
|
+
|
|
10
|
+
attr_reader :widgets
|
|
11
|
+
|
|
5
12
|
def initialize
|
|
6
|
-
@
|
|
7
|
-
@
|
|
13
|
+
@configurations = ComparatorItemStore.new
|
|
14
|
+
@modifications = ComparatorItemStore.new
|
|
15
|
+
@widgets = WidgetStore.new
|
|
8
16
|
end
|
|
9
17
|
|
|
10
|
-
def add_configurations(matcher = nil,
|
|
11
|
-
|
|
18
|
+
def add_configurations(matcher = nil, configuration = nil, **configurations)
|
|
19
|
+
add_configuration(matcher, configuration) if configuration
|
|
12
20
|
|
|
13
|
-
|
|
21
|
+
configurations.each { |mat, cfg| add_configuration(mat, cfg) }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def add_configuration(comparator, configuration)
|
|
25
|
+
@configurations.push(comparator, configuration)
|
|
14
26
|
end
|
|
15
27
|
|
|
16
28
|
def add_modification(matcher, &callback)
|
|
17
|
-
|
|
29
|
+
@modifications.push(matcher, callback)
|
|
18
30
|
end
|
|
19
31
|
|
|
20
32
|
def configurations(widget)
|
|
21
|
-
|
|
22
|
-
|
|
33
|
+
config_list = @configurations.items_and_matches_for_widget(widget)
|
|
34
|
+
config_list.map { |configs| configs[:items] }.flatten(1)
|
|
23
35
|
end
|
|
24
36
|
|
|
25
37
|
def execute_modifications(widget)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
private
|
|
34
|
-
|
|
35
|
-
def find_matching_items(keys, container)
|
|
36
|
-
keys.each_with_object([]) do |key, items|
|
|
37
|
-
items.concat(
|
|
38
|
-
items_from_map(key, container),
|
|
39
|
-
items_by_regex(key, container)
|
|
40
|
-
)
|
|
38
|
+
item_list = @modifications.items_and_matches_for_widget(widget)
|
|
39
|
+
item_list.each do |items|
|
|
40
|
+
items[:items].each do |callback|
|
|
41
|
+
callback.call(widget, items[:match])
|
|
42
|
+
end
|
|
41
43
|
end
|
|
42
44
|
end
|
|
43
45
|
|
|
44
|
-
def
|
|
45
|
-
(
|
|
46
|
+
def configure(widget)
|
|
47
|
+
widget.config.merge(*configurations(widget))
|
|
46
48
|
end
|
|
47
49
|
|
|
48
|
-
def
|
|
49
|
-
|
|
50
|
-
match = matcher.match(key)
|
|
51
|
-
merged_items.concat(items.map { |item| [item, match] }) if match
|
|
52
|
-
end
|
|
50
|
+
def tk_widget(id)
|
|
51
|
+
@widgets[id].tk_widget
|
|
53
52
|
end
|
|
54
53
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
(container[:regex][matcher] ||= []).push(item)
|
|
58
|
-
else
|
|
59
|
-
(container[:map][matcher] ||= []).push(item)
|
|
60
|
-
end
|
|
61
|
-
end
|
|
54
|
+
alias modify add_modification
|
|
55
|
+
alias config add_configurations
|
|
62
56
|
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# single 'match' as part of a Matches object
|
|
4
|
+
class TkWrapper::Widgets::Base::Match
|
|
5
|
+
attr_reader :key, :widget, :match
|
|
6
|
+
|
|
7
|
+
def initialize(value, cls: nil, match: nil, widget: nil)
|
|
8
|
+
@key = match&.[](0) || value
|
|
9
|
+
@widget = widget
|
|
10
|
+
@match = match
|
|
11
|
+
@cls = cls
|
|
12
|
+
@value = value
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def tk_widget
|
|
16
|
+
@widget&.tk_widget
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "#{LIB_DIR}/widgets/base/match"
|
|
4
|
+
|
|
5
|
+
class TkWrapper::Widgets::Base::Matcher
|
|
6
|
+
Match = TkWrapper::Widgets::Base::Match
|
|
7
|
+
|
|
8
|
+
def initialize(value: nil, comparator: nil)
|
|
9
|
+
@match_function = curry_match_function(value, comparator)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# args:
|
|
13
|
+
# [] if widget and matcher were provided on initialization
|
|
14
|
+
# [widget] if matcher was provided on initialization
|
|
15
|
+
# [matcher] if widget was provided on initialization
|
|
16
|
+
# [widget, matcher] if neither widget nor matcher were provided on initial.
|
|
17
|
+
def match(*args)
|
|
18
|
+
@match_function.call(*args)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def match_regex(value, comparator, widget)
|
|
24
|
+
(match = comparator.match(value)) &&
|
|
25
|
+
Match.new(value, match: match, widget: widget)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def match_string(value, comparator, widget)
|
|
29
|
+
value == comparator && Match.new(value, widget: widget)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def match_class(value, comparator, widget)
|
|
33
|
+
widget.is_a?(comparator) &&
|
|
34
|
+
Match.new(value, cls: comparator, widget: widget)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def match_f(value, comparator, widget)
|
|
38
|
+
case comparator
|
|
39
|
+
when String, Symbol then match_string(value, comparator, widget)
|
|
40
|
+
when Regexp then match_regex(value, comparator, widget)
|
|
41
|
+
when Class then match_class(value, comparator, widget)
|
|
42
|
+
when nil then Match(value, { widget: widget })
|
|
43
|
+
else false
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def curry_match_function(value, comparator)
|
|
48
|
+
if value && comparator
|
|
49
|
+
->(widget) { match_f(value, comparator, widget) }
|
|
50
|
+
elsif value
|
|
51
|
+
->(x_comparator, widget) { match_f(value, x_comparator, widget) }
|
|
52
|
+
elsif comparator
|
|
53
|
+
->(x_value, widget) { match_f(x_value, comparator, widget) }
|
|
54
|
+
else
|
|
55
|
+
->(x_value, x_comparator, widget) { match_f(x_value, x_comparator, widget) }
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require_relative 'match'
|
|
2
|
+
|
|
3
|
+
# matches widgets against conditions and stores matching widgets and matches
|
|
4
|
+
class TkWrapper::Widgets::Base::Matches
|
|
5
|
+
include Enumerable
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
@matches = {}
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def push(match)
|
|
12
|
+
(@matches[match.key] ||= []).push(match)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def concat(matches)
|
|
16
|
+
matches.each { |match| push(match) }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def each(&block)
|
|
20
|
+
@matches.each do |(key, matches_for_key)|
|
|
21
|
+
matches_for_key.each do |match|
|
|
22
|
+
block.call([match.widget, key, match.match, match])
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def [](key)
|
|
28
|
+
case @matches[key].size
|
|
29
|
+
when 0 then nil
|
|
30
|
+
when 1 then @matches[key][0]
|
|
31
|
+
else @matches[key]
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def first
|
|
36
|
+
@matches.first&.[](1)
|
|
37
|
+
end
|
|
38
|
+
end
|
data/lib/widgets/base/widget.rb
CHANGED
|
@@ -1,47 +1,30 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
require 'tk'
|
|
4
|
-
|
|
1
|
+
require "#{LIB_DIR}/util/tk/cell"
|
|
2
|
+
require "#{LIB_DIR}/util/tk/finder"
|
|
5
3
|
require "#{LIB_DIR}/tk_extensions"
|
|
6
4
|
|
|
7
5
|
require_relative 'base'
|
|
6
|
+
require_relative 'window_info'
|
|
8
7
|
|
|
9
8
|
class TkWrapper::Widgets::Base::Widget
|
|
9
|
+
extend Forwardable
|
|
10
10
|
include TkExtensions
|
|
11
|
+
include Enumerable
|
|
12
|
+
|
|
13
|
+
def_delegators :@finder, :find, :find_all
|
|
11
14
|
|
|
12
15
|
attr_accessor :config
|
|
13
|
-
attr_reader :parent, :childs
|
|
16
|
+
attr_reader :parent, :ids, :cell, :childs, :manager, :winfo
|
|
14
17
|
|
|
15
18
|
def tk_class() end
|
|
16
19
|
|
|
17
|
-
def
|
|
18
|
-
@
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def self.config(matcher = nil, configuration = nil, **configurations)
|
|
22
|
-
manager.add_configurations(matcher, configuration, **configurations)
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def self.modify(matcher, &callback)
|
|
26
|
-
manager.add_modification(matcher, &callback)
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def ids
|
|
30
|
-
case @id
|
|
31
|
-
when Array then @id
|
|
32
|
-
when nil then []
|
|
33
|
-
else [@id]
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def manager
|
|
38
|
-
TkWrapper::Widgets::Base::Widget.manager
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def initialize(config: {}, childs: [])
|
|
20
|
+
def initialize(config: {}, childs: [], manager: nil, ids: [])
|
|
21
|
+
@cell = TkWrapper::Util::Tk::Cell.new(self)
|
|
22
|
+
@winfo = TkWrapper::Widgets::Base::WindowInfo.new(self)
|
|
23
|
+
@finder = TkWrapper::Util::Tk::Finder.new(widgets: self)
|
|
42
24
|
@config = TkWrapper::Widgets::Base::Configuration.new(config)
|
|
43
25
|
@childs = childs.is_a?(Array) ? childs : [childs]
|
|
44
|
-
@
|
|
26
|
+
@manager = manager
|
|
27
|
+
@ids = ids.is_a?(Array) ? ids : [ids]
|
|
45
28
|
end
|
|
46
29
|
|
|
47
30
|
def create_tk_widget(parent)
|
|
@@ -49,7 +32,7 @@ class TkWrapper::Widgets::Base::Widget
|
|
|
49
32
|
|
|
50
33
|
return unless tk_class
|
|
51
34
|
|
|
52
|
-
|
|
35
|
+
tk_class.new(parent&.tk_widget)
|
|
53
36
|
end
|
|
54
37
|
|
|
55
38
|
# if parent is provided and self has no tk_class, the tk_widget of the
|
|
@@ -60,84 +43,38 @@ class TkWrapper::Widgets::Base::Widget
|
|
|
60
43
|
(@tk_widget = create_tk_widget(parent)) || parent&.tk_widget
|
|
61
44
|
end
|
|
62
45
|
|
|
63
|
-
def
|
|
46
|
+
def build_childs
|
|
47
|
+
@childs.each { |child| child.build(self, manager: @manager) }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def build(parent, configure: true, manager: nil)
|
|
64
51
|
@parent = parent
|
|
65
52
|
tk_widget # creates the widget if possible and not yet created
|
|
53
|
+
@font = TkWrapper::Util::Tk::Font.new(tk_widget)
|
|
54
|
+
@manager ||= manager
|
|
55
|
+
@config.merge(*@manager.configurations(self)) if @manager
|
|
66
56
|
self.configure if configure
|
|
67
|
-
manager
|
|
68
|
-
@
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
def push(child)
|
|
72
|
-
@childs.push(child)
|
|
73
|
-
child.build(self)
|
|
57
|
+
@manager&.execute_modifications(self)
|
|
58
|
+
@manager&.widgets&.push(self)
|
|
59
|
+
build_childs
|
|
74
60
|
end
|
|
75
61
|
|
|
76
62
|
def configure
|
|
77
|
-
@config.merge_global_configurations(manager, self)
|
|
78
63
|
@config.configure_tk_widget(tk_widget)
|
|
79
64
|
@config.configure_tearoff
|
|
80
65
|
end
|
|
81
66
|
|
|
82
|
-
def
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
when nil
|
|
89
|
-
true
|
|
90
|
-
else
|
|
91
|
-
is_a?(matcher)
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
def find(matcher)
|
|
96
|
-
nodes_to_scan = [self]
|
|
97
|
-
until nodes_to_scan.empty?
|
|
98
|
-
node = nodes_to_scan.pop
|
|
99
|
-
return node if node.check_match(matcher)
|
|
100
|
-
|
|
101
|
-
nodes_to_scan = node.childs + nodes_to_scan
|
|
67
|
+
def each(&block)
|
|
68
|
+
nodes_to_walk = [self]
|
|
69
|
+
until nodes_to_walk.empty?
|
|
70
|
+
node = nodes_to_walk.pop
|
|
71
|
+
block.call(node)
|
|
72
|
+
nodes_to_walk = node.childs + nodes_to_walk
|
|
102
73
|
end
|
|
103
74
|
end
|
|
104
75
|
|
|
105
|
-
def
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
until nodes_to_scan.empty?
|
|
110
|
-
node = nodes_to_scan.pop
|
|
111
|
-
found_nodes.push(node) if node.check_match(matcher)
|
|
112
|
-
|
|
113
|
-
nodes_to_scan = node.childs + nodes_to_scan
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
found_nodes
|
|
117
|
-
end
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
# the first parent, which contains a tk_widget, which is really different
|
|
121
|
-
# from self.tk_widget
|
|
122
|
-
def get_container_parent
|
|
123
|
-
container = @parent
|
|
124
|
-
while container.tk_widget == tk_widget
|
|
125
|
-
return unless container.parent # not in a grid?
|
|
126
|
-
|
|
127
|
-
container = container.parent
|
|
76
|
+
def push(child)
|
|
77
|
+
@childs.push(child)
|
|
78
|
+
child.build(self, manager: @manager)
|
|
128
79
|
end
|
|
129
|
-
container
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
# returns the bounding box of the tk_widget
|
|
133
|
-
def cell_bbox
|
|
134
|
-
return unless (container = get_container_parent)
|
|
135
|
-
|
|
136
|
-
grid_info = TkGrid.info(tk_widget)
|
|
137
|
-
start_col = grid_info['column']
|
|
138
|
-
end_col = start_col + grid_info['columnspan'] - 1
|
|
139
|
-
start_row = grid_info['row']
|
|
140
|
-
end_row = start_row + grid_info['rowspan'] - 1
|
|
141
|
-
|
|
142
|
-
TkGrid.bbox(container.tk_widget, start_col, start_row, end_col, end_row)
|
|
143
80
|
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "#{LIB_DIR}/util/tk/finder"
|
|
4
|
+
require_relative 'comparator_item_store'
|
|
5
|
+
|
|
6
|
+
class TkWrapper::Widgets::Base::WidgetStore
|
|
7
|
+
extend Forwardable
|
|
8
|
+
|
|
9
|
+
def_delegators :@finder, :find, :find_all, :iter
|
|
10
|
+
|
|
11
|
+
def initialize
|
|
12
|
+
@lookup = {}
|
|
13
|
+
@finder = TkWrapper::Util::Tk::Finder.new(widgets: self, lookup: @lookup)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def push(widget)
|
|
17
|
+
widget.ids.each do |id|
|
|
18
|
+
(@lookup[id] ||= []).push(widget)
|
|
19
|
+
end
|
|
20
|
+
(@lookup[nil] ||= []).push(widget) if widget.ids.empty?
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def each(&block)
|
|
24
|
+
@lookup.each_value do |widgets|
|
|
25
|
+
widgets.each { |widget| block.call(widget) }
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def [](key)
|
|
30
|
+
@lookup[key]&.size == 1 ? @lookup[key].first : @lookup[key]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def map_key?(key)
|
|
36
|
+
[String, Symbol].include?(key)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
class TkWrapper::Widgets::Base::WindowInfo
|
|
2
|
+
def initialize(widget)
|
|
3
|
+
@widget = widget
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
def method_missing(name, *args)
|
|
7
|
+
if @widget.tk_widget.respond_to?("winfo_#{name}")
|
|
8
|
+
@widget.tk_widget.send("winfo_#{name}", *args)
|
|
9
|
+
else
|
|
10
|
+
super
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
data/lib/widgets/grid.rb
CHANGED
|
@@ -10,8 +10,8 @@ class TkWrapper::Widgets::Grid < TkWrapper::Widgets::Base::Widget
|
|
|
10
10
|
TkWidgets::Frame
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
def initialize(
|
|
14
|
-
super(
|
|
13
|
+
def initialize(**arguments)
|
|
14
|
+
super(**arguments)
|
|
15
15
|
@childs.map! { |row| row.is_a?(Array) ? row : [row] }
|
|
16
16
|
configure_cells_for_grid
|
|
17
17
|
@childs.flatten! && @childs.select! { |cell| cell.is_a?(Widget) }
|
data/lib/widgets/menu.rb
CHANGED
|
@@ -5,8 +5,8 @@ class TkWrapper::Widgets::Menu < TkWrapper::Widgets::Base::Widget
|
|
|
5
5
|
TkWidgets::TkMenu
|
|
6
6
|
end
|
|
7
7
|
|
|
8
|
-
def build(parent)
|
|
9
|
-
super(parent)
|
|
8
|
+
def build(parent, **args)
|
|
9
|
+
super(parent, **args)
|
|
10
10
|
parent.tk_widget['menu'] = tk_widget
|
|
11
11
|
end
|
|
12
12
|
|
|
@@ -15,15 +15,18 @@ class TkWrapper::Widgets::Menu < TkWrapper::Widgets::Base::Widget
|
|
|
15
15
|
TkWidgets::TkMenu
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
def build(parent)
|
|
19
|
-
|
|
18
|
+
def build(parent, **args)
|
|
19
|
+
args[:configure] = false
|
|
20
|
+
super(parent, **args)
|
|
20
21
|
@config[:menu] = tk_widget
|
|
21
22
|
parent.tk_widget.add :cascade, **@config.config
|
|
22
23
|
end
|
|
23
24
|
end
|
|
24
25
|
|
|
25
26
|
class Command < TkWrapper::Widgets::Base::Widget
|
|
26
|
-
def build(parent)
|
|
27
|
+
def build(parent, **args)
|
|
28
|
+
args[:configure] = false
|
|
29
|
+
super(parent, **args)
|
|
27
30
|
parent.tk_widget.add :command, **@config.config
|
|
28
31
|
end
|
|
29
32
|
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class TkWrapper::Widgets::MountPoint < TkWrapper::Widgets::Base::Widget
|
|
4
|
+
def initialize(**args)
|
|
5
|
+
super(**args)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def build_childs(skip: true)
|
|
9
|
+
super() unless skip
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def mount=(childs)
|
|
13
|
+
mount(childs)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def mount(childs = nil)
|
|
17
|
+
if childs
|
|
18
|
+
@childs = childs.is_a?(Array) ? childs : [childs]
|
|
19
|
+
end
|
|
20
|
+
@childs.each do |child|
|
|
21
|
+
child.config.merge(@config)
|
|
22
|
+
end
|
|
23
|
+
build_childs(skip: false)
|
|
24
|
+
end
|
|
25
|
+
end
|
data/lib/widgets/widgets.rb
CHANGED
metadata
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: tkwrapper
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Benjamin Schnitzler
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2021-12-
|
|
12
|
-
dependencies:
|
|
11
|
+
date: 2021-12-20 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: tk
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0.4'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0.4'
|
|
13
27
|
description:
|
|
14
28
|
email: reception@e.mail.de
|
|
15
29
|
executables: []
|
|
@@ -19,18 +33,29 @@ files:
|
|
|
19
33
|
- lib/tk_extensions.rb
|
|
20
34
|
- lib/tkwrapper.rb
|
|
21
35
|
- lib/util/hash_recursive.rb
|
|
36
|
+
- lib/util/tk/cell.rb
|
|
37
|
+
- lib/util/tk/finder.rb
|
|
38
|
+
- lib/util/tk/font.rb
|
|
39
|
+
- lib/util/tk/tk.rb
|
|
22
40
|
- lib/util/virtual_methods.rb
|
|
23
41
|
- lib/widgets/auto_resize_entry.rb
|
|
24
42
|
- lib/widgets/base/base.rb
|
|
43
|
+
- lib/widgets/base/comparator_item_store.rb
|
|
25
44
|
- lib/widgets/base/configuration.rb
|
|
26
45
|
- lib/widgets/base/manager.rb
|
|
46
|
+
- lib/widgets/base/match.rb
|
|
47
|
+
- lib/widgets/base/matcher.rb
|
|
48
|
+
- lib/widgets/base/matches.rb
|
|
27
49
|
- lib/widgets/base/widget.rb
|
|
28
|
-
- lib/widgets/base/
|
|
50
|
+
- lib/widgets/base/widget_store.rb
|
|
51
|
+
- lib/widgets/base/window_info.rb
|
|
52
|
+
- lib/widgets/button.rb
|
|
29
53
|
- lib/widgets/entry.rb
|
|
30
54
|
- lib/widgets/frame.rb
|
|
31
55
|
- lib/widgets/grid.rb
|
|
32
56
|
- lib/widgets/label.rb
|
|
33
57
|
- lib/widgets/menu.rb
|
|
58
|
+
- lib/widgets/mount_point.rb
|
|
34
59
|
- lib/widgets/root.rb
|
|
35
60
|
- lib/widgets/text.rb
|
|
36
61
|
- lib/widgets/widgets.rb
|
data/lib/widgets/base/widgets.rb
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
class TkWrapper::Widgets::Base::Widgets
|
|
4
|
-
def initialize
|
|
5
|
-
@configurations = []
|
|
6
|
-
@modifications = []
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
def add_configuration(matcher, configuration)
|
|
10
|
-
@configurations.push(matcher, configuration)
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def add_modification(matcher, &callback)
|
|
14
|
-
@modifications.push(matcher, callback)
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def configurations(widget)
|
|
18
|
-
@configurations.filter_map do |(matcher, config)|
|
|
19
|
-
config if widget.check_match(matcher)
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def execute_modifications(widget)
|
|
24
|
-
@modifications.each do |(matcher, callback)|
|
|
25
|
-
next unless (match = widget.check_match(matcher))
|
|
26
|
-
|
|
27
|
-
arguments = match.is_a?(MatchData) ? [widget, match] : [widget]
|
|
28
|
-
callback.call(*arguments)
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|