tkwrapper 1.4.0 → 1.7.1

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: 81c1971220f794d1e17288aac5b75221393727be934288a4993a621c35b3282d
4
- data.tar.gz: f2a841f6f3a6a7ee040c85fe0f2081234780bf1ff0bf4aeb70de9cf4c2a75055
3
+ metadata.gz: 90b29c12036c765281e148238dd8b97afc06091749eeb5c8a8595ce8a3ddd589
4
+ data.tar.gz: 8c7b8f7f3b5c9b54a239d9d7d034b14d8cde4e700d848e98d2b77329da03aa8b
5
5
  SHA512:
6
- metadata.gz: 22cf843324096411be2441088df910d723ecc353aa6b54dd64ba5703b2fcd47bec3c2424a38e910fefb187fdcf23ec60dd44640de3f0657a3cb2969e84da016c
7
- data.tar.gz: acb296b0e78c570e7d80ba96d91c4ebf62ecfd1ce7c97fac088546d599e0468a23dafac325ee14ae9fa0d4d08a9c8c70e5972df849346a05d8e2ac3ee9fb1154
6
+ metadata.gz: 402470958a23a858dbe40ab101e31b61fd7ccbf6c240dbaa83a9c6e6727c94ca1a5e81e9d73ab571fc3c85b0ba275fece636793eacb487a98a60ed85bffc1661
7
+ data.tar.gz: 3f74e9a050ec620ea4d77bc0e518dff8fe4037e79c37d5ca111d85f33d52288dc663449ba760a963fa26fb5dd2bcafb6d6edd9ecc6b84ac0831f67e1456bdbf1
data/lib/tkwrapper.rb CHANGED
@@ -2,7 +2,12 @@
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
 
data/lib/util/array.rb ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Util
4
+ def self.first_not_nil(array, default = nil)
5
+ index = indexes.find { |i| !array[i].nil? }
6
+ index.nil? ? default : array[index]
7
+ end
8
+ end
@@ -1,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Util
4
- def self.merge_recursive!(*hashes)
4
+ def self.merge_recursive!(*hashes, overwrite: true)
5
5
  hashes[0].merge!(*hashes[1..]) do |_key, old, new|
6
6
  if old.is_a?(Hash) && new.is_a?(Hash)
7
7
  merge_recursive!(old, new)
8
8
  else
9
- new
9
+ overwrite ? new : old
10
10
  end
11
11
  end
12
12
  end
data/lib/util/tk/cell.rb CHANGED
@@ -17,6 +17,7 @@ class TkWrapper::Util::Tk::Cell
17
17
  start_row = grid_info['row']
18
18
  end_row = start_row + grid_info['rowspan'] - 1
19
19
 
20
+ container.tk_widget.update
20
21
  TkGrid.bbox(container.tk_widget, start_col, start_row, end_col, end_row)
21
22
  end
22
23
 
@@ -2,48 +2,65 @@
2
2
 
3
3
  require "#{LIB_DIR}/widgets/base/manager"
4
4
  require "#{LIB_DIR}/widgets/base/matcher"
5
- require_relative 'tk'
5
+ require "#{LIB_DIR}/widgets/base/matches"
6
6
 
7
7
  class TkWrapper::Util::Tk::Finder
8
+ Match = TkWrapper::Widgets::Base::Match
8
9
  Matcher = TkWrapper::Widgets::Base::Matcher
10
+ Matches = TkWrapper::Widgets::Base::Matches
9
11
 
10
- def initialize(widgets: nil)
12
+ def initialize(widgets: nil, lookup: nil)
13
+ @lookup = lookup
11
14
  @widgets = widgets
12
15
  end
13
16
 
14
- def each_widget_match(widgets, matchers, &block)
15
- widgets.each do |widget|
16
- widget.ids.each do |id|
17
- matchers.each do |matcher|
18
- (match = matcher.match(id, widget)) && block.call(match)
19
- end
20
- end
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 }
21
22
  end
22
23
  end
23
24
 
24
- def find(comparators, widgets = @widgets)
25
- matchers = create_value_matchers(comparators)
26
-
27
- each_widget_match(widgets, matchers) do |match|
28
- return match.widget if match
29
- end
25
+ def find(comparators, widgets = @widgets, lookup = @lookup)
26
+ iter(comparators, widgets, lookup, &:itself).first
30
27
  end
31
28
 
32
- def find_all(comparators, widgets = @widgets)
33
- matchers = create_value_matchers(comparators)
34
- matches = TkWrapper::Widgets::Base::Matches.new
35
-
36
- each_widget_match(widgets, matchers) do |match|
37
- matches.push(match)
38
- end
39
-
40
- matches
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) }
41
32
  end
42
33
 
43
34
  private
44
35
 
45
36
  def create_value_matchers(comparators)
46
- comparators = [comparators] unless comparators.is_a?(Array)
47
37
  comparators.map { |comparator| Matcher.new(comparator: comparator) }
48
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
49
66
  end
data/lib/util/tk/font.rb CHANGED
@@ -48,4 +48,18 @@ class TkWrapper::Util::Tk::Font
48
48
  def update
49
49
  @tk_widget.font = TkFont.new(@config)
50
50
  end
51
+
52
+ def char_width
53
+ measure('0')
54
+ end
55
+
56
+ def linespace
57
+ metrics['linespace']
58
+ end
59
+
60
+ def metrics
61
+ @tk_widget.font.metrics.each_with_object({}) do |(key, value), metrics|
62
+ metrics[key] = value
63
+ end
64
+ end
51
65
  end
@@ -5,14 +5,14 @@ class TkWrapper::Widgets::AutoResizeEntry < TkWrapper::Widgets::Entry
5
5
  attr_accessor :min_width, :add_width
6
6
 
7
7
  def initialize(**args)
8
- @min_width = args[:config][:min_width] || 0
9
- @add_width = args[:config][:add_width] || 0
8
+ @min_width = args.dig(:config, :min_width) || 0
9
+ @add_width = args.dig(:config, :add_width) || 0
10
10
  super(**args)
11
11
  end
12
12
 
13
13
  def build(parent, **args)
14
14
  super(parent, **args)
15
- parent.tk_widget.bind('Configure') { resize }
15
+ parent.bind('Configure') { resize }
16
16
  tk_widget.textvariable = TkVariable.new unless tk_widget.textvariable
17
17
  tk_widget.textvariable.trace('write') { resize }
18
18
  resize
@@ -27,7 +27,7 @@ class TkWrapper::Widgets::AutoResizeEntry < TkWrapper::Widgets::Entry
27
27
  end
28
28
 
29
29
  def resize
30
- max_width = @cell.bbox[2]
30
+ max_width = [@cell.bbox[2], 0].max
31
31
  text_width = @font.measure(value)
32
32
  new_width = [[@min_width, text_width + @add_width].max, max_width].min
33
33
  tk_widget.width = 0
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'text'
4
+
5
+ class TkWrapper::Widgets::AutoResizeText < TkWrapper::Widgets::Frame
6
+ attr_reader :text
7
+
8
+ def initialize(**args)
9
+ @min_number_chars = 8
10
+ @min_number_lines = 2
11
+
12
+ super(**args)
13
+
14
+ @longest_line_width = 0
15
+ @config.merge({ grid: :onecell }, overwrite: false)
16
+ end
17
+
18
+ def create_childs
19
+ @text = Text.new(config: {
20
+ grid: { sticky: 'nsew' },
21
+ width: @min_number_chars,
22
+ height: @min_number_lines
23
+ })
24
+ end
25
+
26
+ def build(parent, **args)
27
+ super(parent, **args)
28
+ @text.bind('<Modified>', &method(:autoresize))
29
+ TkGrid.propagate(tk_widget, 0)
30
+ resize(lines: @min_number_lines, chars: @min_number_chars)
31
+ #@text.resize(lines: @min_number_lines, chars: @min_number_chars)
32
+ end
33
+
34
+ # width of cursor + borders (textfield + frame) + padding (textfield + frame)
35
+ def additional_width_needed_for_textfield
36
+ @text.opts.insertwidth +
37
+ @text.accumulated_border_and_padding_width +
38
+ accumulated_border_and_padding_width
39
+ end
40
+
41
+ def additional_height_needed_for_textfield
42
+ # TODO: cursor height?
43
+ @text.accumulated_border_and_padding_height +
44
+ accumulated_border_and_padding_height
45
+ end
46
+
47
+ def width_needed_for_chars(num_chars)
48
+ text.font.char_width * num_chars + additional_width_needed_for_textfield
49
+ end
50
+
51
+ def height_needed_for_lines(num_lines)
52
+ @text.height_of_lines(num_lines) + additional_height_needed_for_textfield
53
+ end
54
+
55
+ def resize_width
56
+ opts.width = width_needed_for_textfield
57
+ @parent.tk_widget.update
58
+ end
59
+
60
+ def resize(height: nil, width: nil, lines: nil, chars: nil)
61
+ width = width_needed_for_chars(chars) if chars
62
+ height = height_needed_for_lines(lines) if lines
63
+
64
+ opts.width = width if width
65
+ opts.height = height if height
66
+ end
67
+
68
+ def width_needed_for_textfield
69
+ @text.longest_line_width + additional_width_needed_for_textfield
70
+ end
71
+
72
+ def height_needed_for_textfield
73
+ @text.height_of_lines + additional_height_needed_for_textfield
74
+ end
75
+
76
+ def max_width
77
+ [@cell.bbox[2], 0].max
78
+ end
79
+
80
+ def min_width
81
+ [max_width, width_needed_for_chars(@min_number_chars)].min
82
+ end
83
+
84
+ def min_height
85
+ height_needed_for_lines(@min_number_lines)
86
+ end
87
+
88
+ def autoresize
89
+ return unless @text.tk_widget.modified?
90
+
91
+ width = [[min_width, width_needed_for_textfield].max, max_width].min
92
+ height = [min_height, height_needed_for_textfield].max
93
+
94
+ resize(height: height, width: width)
95
+
96
+ @text.tk_widget.modified(false)
97
+ end
98
+ end
@@ -10,8 +10,12 @@ class TkWrapper::Widgets::Base::ComparatorItemStore
10
10
  @comparator_map = {} # for lookup using comparisons by Matcher class
11
11
  end
12
12
 
13
+ def map_key?(key)
14
+ [String, Symbol].include?(key)
15
+ end
16
+
13
17
  def push(key, *items)
14
- if [String, Symbol].include?(key)
18
+ if map_key?(key)
15
19
  (@key_map[key] ||= []).concat(items)
16
20
  else
17
21
  (@comparator_map[key] ||= []).concat(items)
@@ -25,6 +29,11 @@ class TkWrapper::Widgets::Base::ComparatorItemStore
25
29
  end
26
30
  end
27
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
+
28
37
  private
29
38
 
30
39
  def items_from_key_map(id)
@@ -28,14 +28,14 @@ class TkWrapper::Widgets::Base::Configuration
28
28
  @config = parse_and_clone(config)
29
29
  end
30
30
 
31
- def merge(*configurations)
31
+ def merge(*configurations, overwrite: true)
32
32
  configurations = configurations.map do |configuration|
33
33
  configuration = configuration.config if configuration.is_a?(self.class)
34
34
 
35
35
  parse_and_clone(configuration)
36
36
  end
37
37
 
38
- Util.merge_recursive!(@config, *configurations)
38
+ Util.merge_recursive!(@config, *configurations, overwrite: overwrite)
39
39
  end
40
40
 
41
41
  def [](key)
@@ -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)
@@ -1,13 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'comparator_item_store'
4
+ require_relative 'widget_store'
4
5
 
5
6
  class TkWrapper::Widgets::Base::Manager
6
7
  ComparatorItemStore = TkWrapper::Widgets::Base::ComparatorItemStore
8
+ WidgetStore = TkWrapper::Widgets::Base::WidgetStore
9
+
10
+ attr_reader :widgets
7
11
 
8
12
  def initialize
9
13
  @configurations = ComparatorItemStore.new
10
14
  @modifications = ComparatorItemStore.new
15
+ @widgets = WidgetStore.new
11
16
  end
12
17
 
13
18
  def add_configurations(matcher = nil, configuration = nil, **configurations)
@@ -42,6 +47,10 @@ class TkWrapper::Widgets::Base::Manager
42
47
  widget.config.merge(*configurations(widget))
43
48
  end
44
49
 
50
+ def tk_widget(id)
51
+ @widgets[id].tk_widget
52
+ end
53
+
45
54
  alias modify add_modification
46
55
  alias config add_configurations
47
56
  end
@@ -39,7 +39,7 @@ class TkWrapper::Widgets::Base::Matcher
39
39
  when String, Symbol then match_string(value, comparator, widget)
40
40
  when Regexp then match_regex(value, comparator, widget)
41
41
  when Class then match_class(value, comparator, widget)
42
- when nil then Match(value, { widget: widget })
42
+ when nil then Match.new(value, widget: widget)
43
43
  else false
44
44
  end
45
45
  end
@@ -25,10 +25,14 @@ class TkWrapper::Widgets::Base::Matches
25
25
  end
26
26
 
27
27
  def [](key)
28
- case @matches[key].size
29
- when 0 then nil
30
- when 1 then @matches[key][0]
31
- else @matches[key]
28
+ case @matches[key]&.size
29
+ when 0, nil then nil
30
+ when 1 then @matches[key][0]
31
+ else @matches[key]
32
32
  end
33
33
  end
34
+
35
+ def first
36
+ @matches.first&.[](1)
37
+ end
34
38
  end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TkWrapper::Widgets::Base::Padding
4
+ def initialize(widget)
5
+ @widget = widget
6
+ end
7
+
8
+ def set(left: nil, top: nil, right: nil, bottom: nil)
9
+ values = get
10
+ @widget.tk_widget.padding = [
11
+ left || values.left,
12
+ top || values.top,
13
+ right || values.right,
14
+ bottom || values.bottom
15
+ ]
16
+ end
17
+
18
+ def get
19
+ (left, top, right, bottom) = @widget.tk_widget.padding.split
20
+
21
+ {
22
+ left: left || 0,
23
+ top: top || left || 0,
24
+ right: right || left || 0,
25
+ bottom: bottom || top || left || 0
26
+ }
27
+ end
28
+
29
+ def left
30
+ get[:left]
31
+ end
32
+
33
+ def top
34
+ get[:top]
35
+ end
36
+
37
+ def right
38
+ get[:right]
39
+ end
40
+
41
+ def bottom
42
+ get[:bottom]
43
+ end
44
+
45
+ def left=(left)
46
+ set(left: left)
47
+ end
48
+
49
+ def top=(top)
50
+ set(top: top)
51
+ end
52
+
53
+ def right=(right)
54
+ set(right: right)
55
+ end
56
+
57
+ def bottom=(bottom)
58
+ set(bottom: bottom)
59
+ end
60
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'padding'
4
+
5
+ class TkWrapper::Widgets::Base::TkOptions
6
+ attr_reader :padding
7
+
8
+ def self.default_to_zero(*properties)
9
+ properties.each do |property|
10
+ define_method(property) do
11
+ value = @widget.tk_widget[property]
12
+ value.is_a?(Numeric) ? value : 0
13
+ end
14
+ end
15
+ end
16
+
17
+ default_to_zero :padx, :insertwidth, :borderwidth,
18
+ :spacing1, :spacing2, :spacing3
19
+
20
+ def initialize(widget)
21
+ @widget = widget
22
+ @padding = TkWrapper::Widgets::Base::Padding.new(@widget)
23
+ end
24
+
25
+ def padding=(hash)
26
+ @padding.set(**hash)
27
+ end
28
+
29
+ def method_missing(key, value = nil)
30
+ if key[-1] == '='
31
+ @widget.tk_widget[key[0..-2].to_sym] = value
32
+ else
33
+ @widget.tk_widget[key]
34
+ end
35
+ end
36
+
37
+ def respond_to_missing?(*)
38
+ # no known way to check if tk_widget[key] exists
39
+ false
40
+ end
41
+ end
@@ -1,28 +1,49 @@
1
1
  require "#{LIB_DIR}/util/tk/cell"
2
2
  require "#{LIB_DIR}/util/tk/finder"
3
- require "#{LIB_DIR}/tk_extensions"
4
3
 
5
4
  require_relative 'base'
5
+ require_relative 'window_info'
6
+ require_relative 'tk_options'
6
7
 
7
8
  class TkWrapper::Widgets::Base::Widget
8
9
  extend Forwardable
9
- include TkExtensions
10
10
  include Enumerable
11
11
 
12
12
  def_delegators :@finder, :find, :find_all
13
+ def_delegators :tk_widget, :update
14
+ def_delegator :tk_widget, :bind_append, :bind
15
+ def_delegator :@manager, :widgets, :managed
13
16
 
14
17
  attr_accessor :config
15
- attr_reader :parent, :ids, :cell, :childs, :manager
18
+ attr_reader :parent, :ids, :cell, :childs, :manager, :winfo, :opts, :font
16
19
 
17
20
  def tk_class() end
18
21
 
19
- def initialize(config: {}, childs: [], manager: nil, ids: [])
20
- @cell = TkWrapper::Util::Tk::Cell.new(self)
21
- @finder = TkWrapper::Util::Tk::Finder.new(widgets: self)
22
+ def initialize(parent: nil, config: {}, childs: [], manager: nil, ids: [], id: [])
23
+ initialize_utilities
22
24
  @config = TkWrapper::Widgets::Base::Configuration.new(config)
23
- @childs = childs.is_a?(Array) ? childs : [childs]
24
25
  @manager = manager
25
- @ids = ids.is_a?(Array) ? ids : [ids]
26
+ @ids = init_id(id) + init_id(ids)
27
+ @parent = parent
28
+ modify_configuration(@config)
29
+ @childs = normalize_childs(childs)
30
+ parent&.push(self)
31
+ end
32
+
33
+ def initialize_utilities
34
+ @cell = TkWrapper::Util::Tk::Cell.new(self)
35
+ @winfo = TkWrapper::Widgets::Base::WindowInfo.new(self)
36
+ @opts = TkWrapper::Widgets::Base::TkOptions.new(self)
37
+ @finder = TkWrapper::Util::Tk::Finder.new(widgets: self)
38
+ end
39
+
40
+ def init_id(id)
41
+ id.is_a?(Array) ? id : [id]
42
+ end
43
+
44
+ def normalize_childs(childs)
45
+ childs = create_childs || childs
46
+ childs.is_a?(Array) ? childs : [childs]
26
47
  end
27
48
 
28
49
  def create_tk_widget(parent)
@@ -30,7 +51,7 @@ class TkWrapper::Widgets::Base::Widget
30
51
 
31
52
  return unless tk_class
32
53
 
33
- parent&.tk_widget ? tk_class.new(parent.tk_widget) : tk_class.new
54
+ tk_class.new(parent&.tk_widget)
34
55
  end
35
56
 
36
57
  # if parent is provided and self has no tk_class, the tk_widget of the
@@ -41,22 +62,6 @@ class TkWrapper::Widgets::Base::Widget
41
62
  (@tk_widget = create_tk_widget(parent)) || parent&.tk_widget
42
63
  end
43
64
 
44
- def build(parent, configure: true, manager: nil)
45
- @parent = parent
46
- tk_widget # creates the widget if possible and not yet created
47
- @font = TkWrapper::Util::Tk::Font.new(tk_widget)
48
- @manager ||= manager
49
- @config.merge(*@manager.configurations(self)) if @manager
50
- self.configure if configure
51
- @manager&.execute_modifications(self)
52
- @childs.each { |child| child.build(self, manager: @manager) }
53
- end
54
-
55
- def configure
56
- @config.configure_tk_widget(tk_widget)
57
- @config.configure_tearoff
58
- end
59
-
60
65
  def each(&block)
61
66
  nodes_to_walk = [self]
62
67
  until nodes_to_walk.empty?
@@ -68,6 +73,43 @@ class TkWrapper::Widgets::Base::Widget
68
73
 
69
74
  def push(child)
70
75
  @childs.push(child)
71
- child.build(self)
76
+ child.build(self, manager: @manager)
77
+ end
78
+
79
+ protected
80
+
81
+ def build_childs
82
+ @childs.each { |child| child.build(self, manager: @manager) }
83
+ end
84
+
85
+ def modify_configuration(config) end
86
+
87
+ def create_childs() end
88
+
89
+ def before_build(**args) end
90
+
91
+ def after_build() end
92
+
93
+ def inner_build(configure: true, manager: nil)
94
+ tk_widget # creates the widget if possible and not yet created
95
+ @font = TkWrapper::Util::Tk::Font.new(tk_widget)
96
+ @manager ||= manager
97
+ @config.merge(*@manager.configurations(self), overwrite: false) if @manager
98
+ self.configure if configure
99
+ @manager&.execute_modifications(self)
100
+ @manager&.widgets&.push(self)
101
+ build_childs
102
+ end
103
+
104
+ def build(parent, configure: true, manager: nil)
105
+ @parent = parent
106
+ before_build(configure: configure, manager: manager)
107
+ inner_build(configure: configure, manager: manager)
108
+ after_build
109
+ end
110
+
111
+ def configure
112
+ @config.configure_tk_widget(tk_widget)
113
+ @config.configure_tearoff
72
114
  end
73
115
  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,17 @@
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
+
14
+ def respond_to_missing?(name, *)
15
+ @widget.tk_widget.respond_to?("winfo_#{name}") || super
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tkextlib/tile'
4
+
5
+ class TkWrapper::Widgets::Button < TkWrapper::Widgets::Base::Widget
6
+ def tk_class
7
+ Tk::Tile::Button
8
+ end
9
+ end
data/lib/widgets/entry.rb CHANGED
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'tkextlib/tile'
4
+
3
5
  class TkWrapper::Widgets::Entry < TkWrapper::Widgets::Base::Widget
4
6
  def tk_class
5
- TkWidgets::Entry
7
+ Tk::Tile::Entry
6
8
  end
7
9
  end
data/lib/widgets/frame.rb CHANGED
@@ -1,7 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'tkextlib/tile'
4
+
3
5
  class TkWrapper::Widgets::Frame < TkWrapper::Widgets::Base::Widget
4
6
  def tk_class
5
- TkWidgets::Frame
7
+ Tk::Tile::Frame
8
+ end
9
+
10
+ def accumulated_border_and_padding_width
11
+ opts.padding.left + opts.padding.right + 2 * opts.borderwidth
12
+ end
13
+
14
+ def accumulated_border_and_padding_height
15
+ opts.padding.top + opts.padding.bottom + 2 * opts.borderwidth
6
16
  end
7
17
  end
data/lib/widgets/grid.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'tkextlib/tile'
4
+
3
5
  # classification of TkGrid
4
6
  class TkWrapper::Widgets::Grid < TkWrapper::Widgets::Base::Widget
5
7
  attr_reader :parent, :matrix
@@ -7,14 +9,16 @@ class TkWrapper::Widgets::Grid < TkWrapper::Widgets::Base::Widget
7
9
  Widget = TkWrapper::Widgets::Base::Widget
8
10
 
9
11
  def tk_class
10
- TkWidgets::Frame
12
+ Tk::Tile::Frame
11
13
  end
12
14
 
13
15
  def initialize(**arguments)
16
+ parent = arguments.delete(:parent)
14
17
  super(**arguments)
15
18
  @childs.map! { |row| row.is_a?(Array) ? row : [row] }
16
19
  configure_cells_for_grid
17
20
  @childs.flatten! && @childs.select! { |cell| cell.is_a?(Widget) }
21
+ parent&.push(self)
18
22
  end
19
23
 
20
24
  def configure_cells_for_grid
data/lib/widgets/label.rb CHANGED
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'tkextlib/tile'
4
+
3
5
  class TkWrapper::Widgets::Label < TkWrapper::Widgets::Base::Widget
4
6
  def tk_class
5
- TkWidgets::Label
7
+ Tk::Tile::Label
6
8
  end
7
9
  end
data/lib/widgets/menu.rb CHANGED
@@ -1,8 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'tk'
4
+
3
5
  class TkWrapper::Widgets::Menu < TkWrapper::Widgets::Base::Widget
4
6
  def tk_class
5
- TkWidgets::TkMenu
7
+ TkMenu
6
8
  end
7
9
 
8
10
  def build(parent, **args)
@@ -12,7 +14,7 @@ class TkWrapper::Widgets::Menu < TkWrapper::Widgets::Base::Widget
12
14
 
13
15
  class Cascade < TkWrapper::Widgets::Base::Widget
14
16
  def tk_class
15
- TkWidgets::TkMenu
17
+ TkMenu
16
18
  end
17
19
 
18
20
  def build(parent, **args)
@@ -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/root.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'tk'
4
+
3
5
  class TkWrapper::Widgets::Root < TkWrapper::Widgets::Base::Widget
4
6
  def initialize(**arguments)
5
7
  super(**arguments)
@@ -7,6 +9,6 @@ class TkWrapper::Widgets::Root < TkWrapper::Widgets::Base::Widget
7
9
  end
8
10
 
9
11
  def tk_class
10
- TkWidgets::TkRoot
12
+ TkRoot
11
13
  end
12
14
  end
data/lib/widgets/text.rb CHANGED
@@ -1,7 +1,66 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'tk'
4
+
3
5
  class TkWrapper::Widgets::Text < TkWrapper::Widgets::Base::Widget
4
6
  def tk_class
5
- TkWidgets::TkText
7
+ TkText
8
+ end
9
+
10
+ def pos(key)
11
+ match = tk_widget.index(key).match(/(.*)\.(.*)/)
12
+ [match[1].to_i, match[2].to_i]
13
+ end
14
+
15
+ def count_lines
16
+ pos('end')[0] - 1
17
+ end
18
+
19
+ def current_line_nr
20
+ pos('insert')[0]
21
+ end
22
+
23
+ def value=(value)
24
+ tk_widget.delete('1.0', 'end')
25
+ tk_widget.insert('1.0', value)
26
+ end
27
+
28
+ def lines(start_pos = '1.0', end_pos = 'end')
29
+ start_pos = start_pos.is_a?(String) ? start_pos : "#{start_pos}.0"
30
+ end_pos = end_pos.is_a?(String) ? end_pos : "#{end_pos}.0"
31
+ tk_widget.get(start_pos, end_pos).split("\n")
32
+ end
33
+
34
+ def longest_line_width
35
+ lines.map { |line| @font.measure(line) }.max || 0
36
+ end
37
+
38
+ # includes spacing1 and spacing2 (add. space above and below line)
39
+ # does not include spacing3 (additional space between lines)
40
+ def line_height_including_padding
41
+ # linespace: max height of characters of the font
42
+ # spacing1: additional space above line
43
+ # spacing3: additional space below line
44
+ @font.linespace + opts.spacing1 + opts.spacing3
45
+ end
46
+
47
+ def height_of_lines(num_lines = lines.size)
48
+ h = num_lines * line_height_including_padding
49
+
50
+ # spacing 3: additional space between lines
51
+ h + [num_lines - 1, 0].max * opts.spacing3
52
+ end
53
+
54
+ def resize(lines: nil, chars: nil)
55
+ tk_widget['height'] = lines if lines
56
+ tk_widget['width'] = chars if chars
57
+ end
58
+
59
+ def accumulated_border_and_padding_width
60
+ 2 * (opts.padx + opts.borderwidth)
61
+ end
62
+
63
+ def accumulated_border_and_padding_height
64
+ 2 * (opts.pady + opts.borderwidth)
6
65
  end
7
66
  end
@@ -9,6 +9,9 @@ require_relative 'frame'
9
9
  require_relative 'label'
10
10
  require_relative 'menu'
11
11
  require_relative 'text'
12
+ require_relative 'auto_resize_text'
12
13
  require_relative 'entry'
13
14
  require_relative 'auto_resize_entry'
14
15
  require_relative 'grid'
16
+ require_relative 'mount_point'
17
+ require_relative 'button'
metadata CHANGED
@@ -1,23 +1,37 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tkwrapper
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 1.7.1
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-16 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2021-12-26 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: []
16
30
  extensions: []
17
31
  extra_rdoc_files: []
18
32
  files:
19
- - lib/tk_extensions.rb
20
33
  - lib/tkwrapper.rb
34
+ - lib/util/array.rb
21
35
  - lib/util/hash_recursive.rb
22
36
  - lib/util/tk/cell.rb
23
37
  - lib/util/tk/finder.rb
@@ -25,6 +39,7 @@ files:
25
39
  - lib/util/tk/tk.rb
26
40
  - lib/util/virtual_methods.rb
27
41
  - lib/widgets/auto_resize_entry.rb
42
+ - lib/widgets/auto_resize_text.rb
28
43
  - lib/widgets/base/base.rb
29
44
  - lib/widgets/base/comparator_item_store.rb
30
45
  - lib/widgets/base/configuration.rb
@@ -32,12 +47,18 @@ files:
32
47
  - lib/widgets/base/match.rb
33
48
  - lib/widgets/base/matcher.rb
34
49
  - lib/widgets/base/matches.rb
50
+ - lib/widgets/base/padding.rb
51
+ - lib/widgets/base/tk_options.rb
35
52
  - lib/widgets/base/widget.rb
53
+ - lib/widgets/base/widget_store.rb
54
+ - lib/widgets/base/window_info.rb
55
+ - lib/widgets/button.rb
36
56
  - lib/widgets/entry.rb
37
57
  - lib/widgets/frame.rb
38
58
  - lib/widgets/grid.rb
39
59
  - lib/widgets/label.rb
40
60
  - lib/widgets/menu.rb
61
+ - lib/widgets/mount_point.rb
41
62
  - lib/widgets/root.rb
42
63
  - lib/widgets/text.rb
43
64
  - lib/widgets/widgets.rb
data/lib/tk_extensions.rb DELETED
@@ -1,49 +0,0 @@
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