tkwrapper 1.2.0 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
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