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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a31665aae98cd470f88451d45cd6332030c8fe874e18faa1040bcd42103d9ace
4
- data.tar.gz: 93f428a63f6bf0832bf91accfdec1f14715cbfc6de2e9055f29b4cb146da0bf7
3
+ metadata.gz: 25ebb6c8831b18ae3b55f4d98896cc5cf824d628fd4bc245b6fc1ed9fd8c5ef0
4
+ data.tar.gz: b90ca62d87faa02e1a07f5f1145e175d55dfcfeacd71782c709a484038701dfc
5
5
  SHA512:
6
- metadata.gz: 461b4344678f1d7a374ad6d1d01ffbab5c4865a9c513457c7145b21e9582e9d0922d3666a93adf6ed8f57428c6485b8e693694438db1966a7967c01c0ae6e3fc
7
- data.tar.gz: f0a8d152e7aac88206454340c2fbce7e605117a85e2dc0cd550bffc4db9e49d09dd21b4610e52e19a87dc6313ce37b98c9a4bc4e8c44bf033b3f84fe41289833
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', # 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
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 end
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
@@ -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
@@ -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
@@ -0,0 +1,8 @@
1
+ module TkWrapper
2
+ module Util
3
+ module Tk
4
+ end
5
+ end
6
+ end
7
+
8
+ require_relative 'font'
@@ -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(config: {}, childs: [])
8
- @min_width = config[:min_width] || 0
9
- @add_width = config[:add_width] || 0
10
- super(config: config, childs: childs)
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, configure: true)
14
- super(parent, configure: configure)
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 = cell_bbox[2]
65
- text_width = text_width_in_pixel
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)
@@ -5,3 +5,4 @@ module TkWrapper::Widgets::Base end
5
5
  require_relative 'widget'
6
6
  require_relative 'manager'
7
7
  require_relative 'configuration'
8
+ require_relative 'matches'
@@ -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
- id tk_class tearoff weights menu min_width add_width
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
- if option == :grid
85
- grid = grid(only_tk_options: true)
86
- next if grid.empty?
87
- next tk_widget.grid(grid) if option == :grid
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
@@ -1,62 +1,56 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # manages widgets and their global configurations
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
- @configuration_matchers = { regex: {}, map: {} }
7
- @modification_matchers = { regex: {}, map: {} }
13
+ @configurations = ComparatorItemStore.new
14
+ @modifications = ComparatorItemStore.new
15
+ @widgets = WidgetStore.new
8
16
  end
9
17
 
10
- def add_configurations(matcher = nil, config = nil, **configs)
11
- add_matcher(matcher, config, @configuration_matchers) if config
18
+ def add_configurations(matcher = nil, configuration = nil, **configurations)
19
+ add_configuration(matcher, configuration) if configuration
12
20
 
13
- configs.each { |mat, cfg| add_matcher(mat, cfg, @configuration_matchers) }
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
- add_matcher(matcher, callback, @modification_matchers)
29
+ @modifications.push(matcher, callback)
18
30
  end
19
31
 
20
32
  def configurations(widget)
21
- configs = find_matching_items(widget.ids, @configuration_matchers)
22
- configs.map { |config| config[0] }
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
- callbacks = find_matching_items(widget.ids, @modification_matchers)
27
- callbacks.each do |callback|
28
- callback, match = callback
29
- match ? callback.call(widget, match) : callback.call(widget)
30
- end
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 items_from_map(key, container)
45
- (container[:map][key] || []).map { |item| [item, nil] } || []
46
+ def configure(widget)
47
+ widget.config.merge(*configurations(widget))
46
48
  end
47
49
 
48
- def items_by_regex(key, container)
49
- container[:regex].each_with_object([]) do |(matcher, items), merged_items|
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
- def add_matcher(matcher, item, container)
56
- if matcher.is_a?(Regexp)
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
@@ -1,47 +1,30 @@
1
- # frozen_string_literal: true
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 self.manager
18
- @manager ||= TkWrapper::Widgets::Base::Manager.new
19
- end
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
- @id = config[:id]
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
- parent&.tk_widget ? tk_class.new(parent.tk_widget) : tk_class.new
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 build(parent, configure: true)
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.execute_modifications(self)
68
- @childs.each { |child| child.build(self) }
69
- end
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 check_match(matcher)
83
- case matcher
84
- when Regexp
85
- matcher.match(@id)
86
- when String, Symbol
87
- matcher == @id
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 find_all(matcher)
106
- found_nodes = []
107
- nodes_to_scan = [self]
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
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TkWrapper::Widgets::Button < TkWrapper::Widgets::Base::Widget
4
+ def tk_class
5
+ TkWidgets::Button
6
+ end
7
+ 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(config: {}, childs: [])
14
- super(config: config, childs: childs)
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
- super(parent, configure: false)
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
@@ -12,3 +12,5 @@ require_relative 'text'
12
12
  require_relative 'entry'
13
13
  require_relative 'auto_resize_entry'
14
14
  require_relative 'grid'
15
+ require_relative 'mount_point'
16
+ require_relative 'button'
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.2.0
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-13 00:00:00.000000000 Z
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/widgets.rb
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
@@ -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