tkwrapper 1.0.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 +7 -0
- data/lib/tk_extensions.rb +49 -0
- data/lib/tkwrapper.rb +11 -0
- data/lib/util/hash_recursive.rb +30 -0
- data/lib/util/virtual_methods.rb +20 -0
- data/lib/widgets/auto_resize_entry.rb +63 -0
- data/lib/widgets/base/base.rb +7 -0
- data/lib/widgets/base/configuration.rb +119 -0
- data/lib/widgets/base/manager.rb +32 -0
- data/lib/widgets/base/widget.rb +130 -0
- data/lib/widgets/base/widgets.rb +31 -0
- data/lib/widgets/entry.rb +7 -0
- data/lib/widgets/frame.rb +7 -0
- data/lib/widgets/grid.rb +46 -0
- data/lib/widgets/label.rb +7 -0
- data/lib/widgets/menu.rb +53 -0
- data/lib/widgets/root.rb +12 -0
- data/lib/widgets/text.rb +7 -0
- data/lib/widgets/widgets.rb +14 -0
- metadata +60 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 6cf6fe96ebab8380076797dfe3f4ed9747d3f3709cc8d58e1bf1c012d28af572
|
4
|
+
data.tar.gz: b0956e96f04f7daf0b6d6f09317e7b2e73d010e866b57f678fa2fd7ba658fd16
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 848528e93f0dabed494f7e234789edc0108b4460b11964bc661c9457aeabcf2a022565461d4ae3d566de853ad2e96ec06b4112c2abf9f1520912c999675c9bd1
|
7
|
+
data.tar.gz: 613ec39818df4edb5714f0eac7f3aac73c7b403ed0a5d2371103a2e5419ec0c8bc763a2ec068b12538e2476cd29f1e0f49622af3cbcdfa7c7a734edc783c5c62
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'tk'
|
2
|
+
require 'tkextlib/tile'
|
3
|
+
|
4
|
+
module TkExtensions
|
5
|
+
# allow a Tk widget to have multiple bindings for the same event
|
6
|
+
module MultiBind
|
7
|
+
def bind(event, &callback)
|
8
|
+
@bindings ||= {}
|
9
|
+
|
10
|
+
unless @bindings[event]
|
11
|
+
@bindings[event] = []
|
12
|
+
super(event) { execute_all_bindings(event) }
|
13
|
+
end
|
14
|
+
|
15
|
+
@bindings[event].push(callback)
|
16
|
+
end
|
17
|
+
|
18
|
+
def execute_all_bindings(event)
|
19
|
+
@bindings[event].each(&:call)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module TkWidgets
|
24
|
+
# from several Tk widgets create subclasses, which include MultiBind
|
25
|
+
# the reason why we loop over strings and not the classes themselve is, that
|
26
|
+
# the class names may be aliases (e.g. Tk::Tile::Entry is an alias for
|
27
|
+
# Tk::Tile::TEntry)
|
28
|
+
tk_class_names = [
|
29
|
+
'TkRoot', # becomes TkExtensions::TkRoot
|
30
|
+
'TkText', # becomes TkExtensions::TkText
|
31
|
+
'TkMenu', # becomes TkExtensions::TkMenu
|
32
|
+
'Tk::Tile::Entry', # becomes TkExtensions::Entry
|
33
|
+
'Tk::Tile::Frame', # becomes TkExtensions::Frame
|
34
|
+
'Tk::Tile::Label' # becomes TkExtensions::Label
|
35
|
+
]
|
36
|
+
tk_class_names.each do |tk_class_name|
|
37
|
+
# extract last part of the name (e.g. Tk::Tile::Entry => Entry)
|
38
|
+
new_child_class_name = tk_class_name.match(/([^:]*)$/)[1]
|
39
|
+
# get the class from the class constant name
|
40
|
+
tk_class = const_get(tk_class_name)
|
41
|
+
# create a new child class of tk_class and have it include MultiBind, which
|
42
|
+
# overwrites the bind method of that class to allow for multiple bindings
|
43
|
+
new_child_class = Class.new(tk_class) { include(MultiBind) }
|
44
|
+
# make the new class known to the TkExtensions namespace to allow to use it
|
45
|
+
# by e.g. TkExtensions::Entry
|
46
|
+
const_set(new_child_class_name, new_child_class)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/tkwrapper.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Util
|
4
|
+
def self.merge_recursive!(*hashes)
|
5
|
+
hashes[0].merge!(*hashes[1..]) do |_key, old, new|
|
6
|
+
if old.is_a?(Hash) && new.is_a?(Hash)
|
7
|
+
merge_recursive!(old, new)
|
8
|
+
else
|
9
|
+
new
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.each_recursive(hash, &block)
|
15
|
+
hash.each do |key, value|
|
16
|
+
next block.call(hash, key, value) unless value.is_a?(Hash)
|
17
|
+
|
18
|
+
block.call(hash, key, each_recursive(value, &block))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.recursive_transform_key_value!(hash, &block)
|
23
|
+
hash.transform_keys! do |key|
|
24
|
+
value = hash[key]
|
25
|
+
next recursive_transform_key_value!(value, &block) if value.is_a?(Hash)
|
26
|
+
|
27
|
+
block.call(key, value)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# wraps some code of Tk to make it more object oriented, provide new
|
4
|
+
# functionality and to ease usage
|
5
|
+
module TkWrapper
|
6
|
+
# used to be abled to define virtual methods in a class which is extended by
|
7
|
+
# this module
|
8
|
+
module VirtualMethods
|
9
|
+
# error thrown, when a virtual method is called
|
10
|
+
class VirtualMethodCalledError < RuntimeError
|
11
|
+
def initialize(name)
|
12
|
+
super("Virtual function '#{name}' called.")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def virtual(method)
|
17
|
+
define_method(method) { raise VirtualMethodCalledError, method }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class TkWrapper::Widgets::AutoResizeEntry < TkWrapper::Widgets::Entry
|
4
|
+
# auto resizes on user input, only works if in the grid geometry manager of tk
|
5
|
+
attr_accessor :min_width, :add_width
|
6
|
+
|
7
|
+
def initialize(config: {}, childs: [])
|
8
|
+
@min_width = config.delete(:min_width) || 0
|
9
|
+
@add_width = config.delete(:add_width) || 0
|
10
|
+
super(config: config, childs: childs)
|
11
|
+
end
|
12
|
+
|
13
|
+
def build(parent, configure: true)
|
14
|
+
super(parent, configure: configure)
|
15
|
+
parent.tk_widget.bind('Configure') { resize }
|
16
|
+
tk_widget.textvariable = TkVariable.new unless tk_widget.textvariable
|
17
|
+
tk_widget.textvariable.trace('write') { resize }
|
18
|
+
resize
|
19
|
+
end
|
20
|
+
|
21
|
+
def config_for_dummy_label
|
22
|
+
grid_info = TkGrid.info(tk_widget)
|
23
|
+
{ config: { grid: {
|
24
|
+
row: grid_info['row'],
|
25
|
+
column: grid_info['column'],
|
26
|
+
columnspan: grid_info['columnspan'],
|
27
|
+
sticky: 'nw'
|
28
|
+
} } }
|
29
|
+
end
|
30
|
+
|
31
|
+
def create_dummy_label_with_same_size(&block)
|
32
|
+
label = Label.new(**config_for_dummy_label)
|
33
|
+
label.build(@parent)
|
34
|
+
label.tk_widget.text = tk_widget.textvariable.value
|
35
|
+
label.tk_widget.lower
|
36
|
+
result = block.call(label)
|
37
|
+
label.tk_widget.destroy
|
38
|
+
result
|
39
|
+
end
|
40
|
+
|
41
|
+
def text_width_in_pixel
|
42
|
+
create_dummy_label_with_same_size do |label|
|
43
|
+
@parent.tk_widget.update
|
44
|
+
label.tk_widget.winfo_width
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def textwidth_and_maxwidth_in_pixel
|
49
|
+
create_dummy_frame_with_same_size_label do |frame, label|
|
50
|
+
{ text_width: label.tk_widget.winfo_width,
|
51
|
+
max_width: frame.tk_widget.winfo_width }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def resize
|
56
|
+
max_width = cell_bbox[2]
|
57
|
+
text_width = text_width_in_pixel
|
58
|
+
new_width = [[@min_width, text_width + @add_width].max, max_width].min
|
59
|
+
tk_widget.width = 0
|
60
|
+
tk_widget.grid(ipadx: new_width / 2.0)
|
61
|
+
@parent.tk_widget.update
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "#{LIB_DIR}/util/hash_recursive.rb"
|
4
|
+
|
5
|
+
require_relative 'base'
|
6
|
+
|
7
|
+
class TkWrapper::Widgets::Base::Configuration
|
8
|
+
attr_accessor :config
|
9
|
+
|
10
|
+
GRID_SPECIAL_VALUES = {
|
11
|
+
onecell: {
|
12
|
+
weights: { rows: [1], cols: [1] }
|
13
|
+
},
|
14
|
+
fullcell: {
|
15
|
+
column: 0, row: 0, sticky: 'nsew'
|
16
|
+
},
|
17
|
+
default: {
|
18
|
+
weights: { rows: [1], cols: [1] },
|
19
|
+
column: 0, row: 0, sticky: 'nsew'
|
20
|
+
}
|
21
|
+
}.freeze
|
22
|
+
|
23
|
+
NON_TK_OPTIONS = %i[id tk_class tearoff weights menu].freeze
|
24
|
+
|
25
|
+
def initialize(config)
|
26
|
+
@config = parse!(config)
|
27
|
+
end
|
28
|
+
|
29
|
+
def merge!(*configurations)
|
30
|
+
configurations = configurations.map do |configuration|
|
31
|
+
next configuration.config if configuration.is_a?(self.class)
|
32
|
+
|
33
|
+
parse!(configuration)
|
34
|
+
end
|
35
|
+
|
36
|
+
Util.merge_recursive!(@config, *configurations)
|
37
|
+
end
|
38
|
+
|
39
|
+
def parse!(config)
|
40
|
+
Util.each_recursive(config) do |hash, key, value|
|
41
|
+
next if value.is_a?(Hash)
|
42
|
+
|
43
|
+
hash[key] = parse_value(key, value)
|
44
|
+
end
|
45
|
+
|
46
|
+
config
|
47
|
+
end
|
48
|
+
|
49
|
+
def [](key)
|
50
|
+
key = key.to_sym
|
51
|
+
return self.class.new(@config[key]) if @config[key].is_a?(Hash)
|
52
|
+
|
53
|
+
@config[key]
|
54
|
+
end
|
55
|
+
|
56
|
+
def []=(key, value)
|
57
|
+
key = key.to_sym
|
58
|
+
@config[key] = parse_value(key, value)
|
59
|
+
end
|
60
|
+
|
61
|
+
def parse_value(key, value)
|
62
|
+
case key
|
63
|
+
when :grid
|
64
|
+
parse_grid_value(value)
|
65
|
+
else
|
66
|
+
value
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def parse_grid_value(value)
|
71
|
+
return GRID_SPECIAL_VALUES[value] if value.is_a?(Symbol)
|
72
|
+
|
73
|
+
value
|
74
|
+
end
|
75
|
+
|
76
|
+
def grid(only_tk_options: false)
|
77
|
+
return @config[:grid] unless only_tk_options
|
78
|
+
|
79
|
+
@config[:grid].reject { |option, _| NON_TK_OPTIONS.include?(option) }
|
80
|
+
end
|
81
|
+
|
82
|
+
def configure_tk_widget(tk_widget)
|
83
|
+
@config.each do |option, value|
|
84
|
+
next if NON_TK_OPTIONS.include?(option)
|
85
|
+
|
86
|
+
if option == :grid
|
87
|
+
grid = grid(only_tk_options: true)
|
88
|
+
next if grid.empty?
|
89
|
+
next tk_widget.grid(grid) if option == :grid
|
90
|
+
end
|
91
|
+
|
92
|
+
tk_widget[option] = value
|
93
|
+
end
|
94
|
+
|
95
|
+
configure_weights(tk_widget)
|
96
|
+
end
|
97
|
+
|
98
|
+
def configure_weights(tk_widget)
|
99
|
+
return unless (weights = @config.dig(:grid, :weights))
|
100
|
+
|
101
|
+
(weights[:rows] || []).each_with_index do |weight, index|
|
102
|
+
TkGrid.rowconfigure(tk_widget, index, weight: weight)
|
103
|
+
end
|
104
|
+
|
105
|
+
(weights[:cols] || []).each_with_index do |weight, index|
|
106
|
+
TkGrid.columnconfigure(tk_widget, index, weight: weight)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def configure_tearoff
|
111
|
+
return if (tearoff = @config[:tearoff]).nil?
|
112
|
+
|
113
|
+
TkOption.add '*tearOff', (tearoff ? 1 : 0)
|
114
|
+
end
|
115
|
+
|
116
|
+
def merge_global_configurations(manager, widget)
|
117
|
+
merge!(*manager.configurations(widget))
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# manages widgets and their global configurations
|
4
|
+
class TkWrapper::Widgets::Base::Manager
|
5
|
+
def initialize
|
6
|
+
@configurations = []
|
7
|
+
@modifications = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_configuration(matcher, configuration)
|
11
|
+
@configurations.push([matcher, configuration])
|
12
|
+
end
|
13
|
+
|
14
|
+
def add_modification(matcher, &callback)
|
15
|
+
@modifications.push([matcher, callback])
|
16
|
+
end
|
17
|
+
|
18
|
+
def configurations(widget)
|
19
|
+
@configurations.filter_map do |(matcher, config)|
|
20
|
+
config if widget.check_match(matcher)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def execute_modifications(widget)
|
25
|
+
@modifications.each do |(matcher, callback)|
|
26
|
+
next unless (match = widget.check_match(matcher))
|
27
|
+
|
28
|
+
arguments = match.is_a?(MatchData) ? [widget, match] : [widget]
|
29
|
+
callback.call(*arguments)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'tk'
|
4
|
+
|
5
|
+
require "#{LIB_DIR}/tk_extensions"
|
6
|
+
|
7
|
+
require_relative 'base'
|
8
|
+
|
9
|
+
class TkWrapper::Widgets::Base::Widget
|
10
|
+
include TkExtensions
|
11
|
+
|
12
|
+
attr_accessor :config
|
13
|
+
attr_reader :parent, :childs
|
14
|
+
|
15
|
+
def tk_class() end
|
16
|
+
|
17
|
+
def self.manager
|
18
|
+
@manager ||= TkWrapper::Widgets::Base::Manager.new
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.config(matcher, configuration)
|
22
|
+
manager.add_configuration(matcher, configuration)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.modify(matcher, &callback)
|
26
|
+
manager.add_modification(matcher, &callback)
|
27
|
+
end
|
28
|
+
|
29
|
+
def manager
|
30
|
+
TkWrapper::Widgets::Base::Widget.manager
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(config: {}, childs: [])
|
34
|
+
@config = TkWrapper::Widgets::Base::Configuration.new(config)
|
35
|
+
@childs = childs.is_a?(Array) ? childs : [childs]
|
36
|
+
@id = config[:id]
|
37
|
+
end
|
38
|
+
|
39
|
+
def create_tk_widget(parent)
|
40
|
+
tk_class = @config[:tk_class] || self.tk_class
|
41
|
+
|
42
|
+
return unless tk_class
|
43
|
+
|
44
|
+
parent&.tk_widget ? tk_class.new(parent.tk_widget) : tk_class.new
|
45
|
+
end
|
46
|
+
|
47
|
+
# if parent is provided and self has no tk_class, the tk_widget of the
|
48
|
+
# parent is returned, if parent is not nil
|
49
|
+
def tk_widget(parent = @parent)
|
50
|
+
return @tk_widget if @tk_widget
|
51
|
+
|
52
|
+
(@tk_widget = create_tk_widget(parent)) || parent&.tk_widget
|
53
|
+
end
|
54
|
+
|
55
|
+
def build(parent, configure: true)
|
56
|
+
@parent = parent
|
57
|
+
tk_widget # creates the widget if possible and not yet created
|
58
|
+
self.configure if configure
|
59
|
+
manager.execute_modifications(self)
|
60
|
+
@childs.each { |child| child.build(self) }
|
61
|
+
end
|
62
|
+
|
63
|
+
def configure
|
64
|
+
@config.merge_global_configurations(manager, self)
|
65
|
+
@config.configure_tk_widget(tk_widget)
|
66
|
+
@config.configure_tearoff
|
67
|
+
end
|
68
|
+
|
69
|
+
def check_match(matcher)
|
70
|
+
case matcher
|
71
|
+
when Regexp
|
72
|
+
matcher.match(@id)
|
73
|
+
when String, Symbol
|
74
|
+
matcher == @id
|
75
|
+
when nil
|
76
|
+
true
|
77
|
+
else
|
78
|
+
is_a?(matcher)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def find(matcher)
|
83
|
+
nodes_to_scan = [self]
|
84
|
+
until nodes_to_scan.empty?
|
85
|
+
node = nodes_to_scan.pop
|
86
|
+
return node if node.check_match(matcher)
|
87
|
+
|
88
|
+
nodes_to_scan = node.childs + nodes_to_scan
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def find_all(matcher)
|
93
|
+
found_nodes = []
|
94
|
+
nodes_to_scan = [self]
|
95
|
+
|
96
|
+
until nodes_to_scan.empty?
|
97
|
+
node = nodes_to_scan.pop
|
98
|
+
found_nodes.push(node) if node.check_match(matcher)
|
99
|
+
|
100
|
+
nodes_to_scan = node.childs + nodes_to_scan
|
101
|
+
end
|
102
|
+
|
103
|
+
found_nodes
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# the first parent, which contains a tk_widget, which is really different
|
108
|
+
# from self.tk_widget
|
109
|
+
def get_container_parent
|
110
|
+
container = @parent
|
111
|
+
while container.tk_widget == tk_widget
|
112
|
+
return unless container.parent # not in a grid?
|
113
|
+
|
114
|
+
container = container.parent
|
115
|
+
end
|
116
|
+
container
|
117
|
+
end
|
118
|
+
|
119
|
+
# returns the bounding box of the tk_widget
|
120
|
+
def cell_bbox
|
121
|
+
return unless (container = get_container_parent)
|
122
|
+
|
123
|
+
grid_info = TkGrid.info(tk_widget)
|
124
|
+
start_col = grid_info['column']
|
125
|
+
end_col = start_col + grid_info['columnspan'] - 1
|
126
|
+
start_row = grid_info['row']
|
127
|
+
end_row = start_row + grid_info['rowspan'] - 1
|
128
|
+
|
129
|
+
TkGrid.bbox(container.tk_widget, start_col, start_row, end_col, end_row)
|
130
|
+
end
|
@@ -0,0 +1,31 @@
|
|
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
|
data/lib/widgets/grid.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# classification of TkGrid
|
4
|
+
class TkWrapper::Widgets::Grid < TkWrapper::Widgets::Base::Widget
|
5
|
+
attr_reader :parent, :matrix
|
6
|
+
|
7
|
+
def tk_class
|
8
|
+
TkWidgets::Frame
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(config: {}, childs: [])
|
12
|
+
super(config: config, childs: childs)
|
13
|
+
@childs.map! { |row| row.is_a?(Array) ? row : [row] }
|
14
|
+
configure_cells_for_grid
|
15
|
+
@childs.flatten! && @childs.select! { |cell| cell.is_a?(Widget) }
|
16
|
+
end
|
17
|
+
|
18
|
+
def configure_cells_for_grid
|
19
|
+
@childs.each_with_index do |row, row_i|
|
20
|
+
row.each_with_index do |cell, col_i|
|
21
|
+
next unless cell.is_a?(Widget)
|
22
|
+
|
23
|
+
(cell.config[:grid] ||= {}).merge!({ row: row_i, column: col_i })
|
24
|
+
|
25
|
+
configure_colspan(cell, row_i, col_i)
|
26
|
+
configure_rowspan(cell, row_i, col_i)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def configure_colspan(cell, row_i, col_i)
|
32
|
+
cols_after_cell = @childs[row_i][(col_i + 1)..]
|
33
|
+
colspan = cols_after_cell.reduce(1) do |span, content|
|
34
|
+
content == :right ? span + 1 : (break span)
|
35
|
+
end
|
36
|
+
(cell.config[:grid] ||= {}).merge!({ columnspan: colspan })
|
37
|
+
end
|
38
|
+
|
39
|
+
def configure_rowspan(cell, row_i, col_i)
|
40
|
+
rows_after_cell = @childs[(row_i + 1)..]
|
41
|
+
rowspan = rows_after_cell.reduce(1) do |span, row|
|
42
|
+
row[col_i] == :bottom ? span + 1 : (break span)
|
43
|
+
end
|
44
|
+
(cell.config[:grid] ||= {}).merge!({ rowspan: rowspan })
|
45
|
+
end
|
46
|
+
end
|
data/lib/widgets/menu.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class TkWrapper::Widgets::Menu < TkWrapper::Widgets::Base::Widget
|
4
|
+
def tk_class
|
5
|
+
TkWidgets::TkMenu
|
6
|
+
end
|
7
|
+
|
8
|
+
def build(parent)
|
9
|
+
super(parent)
|
10
|
+
parent.tk_widget['menu'] = tk_widget
|
11
|
+
end
|
12
|
+
|
13
|
+
class Cascade < TkWrapper::Widgets::Base::Widget
|
14
|
+
def tk_class
|
15
|
+
TkWidgets::TkMenu
|
16
|
+
end
|
17
|
+
|
18
|
+
def build(parent)
|
19
|
+
super(parent, configure: false)
|
20
|
+
@config[:menu] = tk_widget
|
21
|
+
parent.tk_widget.add :cascade, **@config.config
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Command < TkWrapper::Widgets::Base::Widget
|
26
|
+
def build(parent)
|
27
|
+
parent.tk_widget.add :command, **@config.config
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.create(structure: [], config: {})
|
32
|
+
Menu.new(
|
33
|
+
config: config,
|
34
|
+
childs: structure.map { |entry| create_subentry(entry) }
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.create_subentries(structure)
|
39
|
+
return [] unless structure && !structure.nil?
|
40
|
+
|
41
|
+
structure = [structure] unless structure.is_a?(Array)
|
42
|
+
structure.map { |entry| create_subentry(**entry) }
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.create_subentry(entry)
|
46
|
+
case entry
|
47
|
+
in { config: config, structure: structure }
|
48
|
+
return Cascade.new config: config, childs: create_subentries(structure)
|
49
|
+
else
|
50
|
+
return Command.new config: entry
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/widgets/root.rb
ADDED
data/lib/widgets/text.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TkWrapper::Widgets end
|
4
|
+
|
5
|
+
require_relative 'base/base'
|
6
|
+
|
7
|
+
require_relative 'root'
|
8
|
+
require_relative 'frame'
|
9
|
+
require_relative 'label'
|
10
|
+
require_relative 'menu'
|
11
|
+
require_relative 'text'
|
12
|
+
require_relative 'entry'
|
13
|
+
require_relative 'auto_resize_entry'
|
14
|
+
require_relative 'grid'
|
metadata
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tkwrapper
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Benjamin Schnitzler
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-12-12 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description:
|
14
|
+
email: reception@e.mail.de
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- lib/tk_extensions.rb
|
20
|
+
- lib/tkwrapper.rb
|
21
|
+
- lib/util/hash_recursive.rb
|
22
|
+
- lib/util/virtual_methods.rb
|
23
|
+
- lib/widgets/auto_resize_entry.rb
|
24
|
+
- lib/widgets/base/base.rb
|
25
|
+
- lib/widgets/base/configuration.rb
|
26
|
+
- lib/widgets/base/manager.rb
|
27
|
+
- lib/widgets/base/widget.rb
|
28
|
+
- lib/widgets/base/widgets.rb
|
29
|
+
- lib/widgets/entry.rb
|
30
|
+
- lib/widgets/frame.rb
|
31
|
+
- lib/widgets/grid.rb
|
32
|
+
- lib/widgets/label.rb
|
33
|
+
- lib/widgets/menu.rb
|
34
|
+
- lib/widgets/root.rb
|
35
|
+
- lib/widgets/text.rb
|
36
|
+
- lib/widgets/widgets.rb
|
37
|
+
homepage: https://github.com/bschnitz/tkwrapper
|
38
|
+
licenses:
|
39
|
+
- MIT
|
40
|
+
metadata: {}
|
41
|
+
post_install_message:
|
42
|
+
rdoc_options: []
|
43
|
+
require_paths:
|
44
|
+
- lib
|
45
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '3.0'
|
50
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
requirements: []
|
56
|
+
rubygems_version: 3.2.29
|
57
|
+
signing_key:
|
58
|
+
specification_version: 4
|
59
|
+
summary: Extensions/Wrapper ruby Tk
|
60
|
+
test_files: []
|