glimmer-dsl-tk 0.0.37 → 0.0.41

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.
@@ -28,11 +28,15 @@ module Glimmer
28
28
  def can_interpret?(parent, keyword, *args, &block)
29
29
  block_given? and
30
30
  args.size == 0 and
31
- parent.respond_to?("#{keyword}_block=")
31
+ (parent.respond_to?("#{keyword}_block=") || (parent.respond_to?(:tk) && parent.tk.respond_to?(keyword)))
32
32
  end
33
33
 
34
34
  def interpret(parent, keyword, *args, &block)
35
- parent.send("#{keyword}_block=", block)
35
+ if parent.respond_to?("#{keyword}_block=")
36
+ parent.send("#{keyword}_block=", block)
37
+ else
38
+ parent.tk.send(keyword, block)
39
+ end
36
40
  nil
37
41
  end
38
42
  end
@@ -38,11 +38,11 @@ module Glimmer
38
38
  %w[
39
39
  list_selection_data_binding
40
40
  data_binding
41
- block_attribute
42
41
  attribute
43
42
  shine_data_binding
44
43
  widget
45
44
  built_in_dialog
45
+ block_attribute
46
46
  ]
47
47
  )
48
48
  end
@@ -20,7 +20,6 @@
20
20
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
21
 
22
22
  require 'glimmer/tk/widget_proxy'
23
- require 'glimmer/tk/commandable'
24
23
  require 'glimmer/tk/variable_owner'
25
24
 
26
25
  module Glimmer
@@ -29,7 +28,6 @@ module Glimmer
29
28
  #
30
29
  # Follows the Proxy Design Pattern
31
30
  class CheckbuttonProxy < WidgetProxy
32
- include Commandable
33
31
  include VariableOwner
34
32
  end
35
33
  end
@@ -0,0 +1,202 @@
1
+ # Copyright (c) 2020-2021 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require 'glimmer/tk/widget_proxy'
23
+
24
+ module Glimmer
25
+ module Tk
26
+ class MenuItemProxy < WidgetProxy
27
+ ACCELERATOR_MODIFIER_EVENT_MAP = {
28
+ 'Command' => 'Command',
29
+ 'Cmd' => 'Command',
30
+ 'Meta' => 'Command',
31
+ 'Option' => 'Option',
32
+ 'Opt' => 'Option',
33
+ 'Alt' => 'Option',
34
+ 'Shift' => 'Shift',
35
+ 'Ctrl' => 'Control',
36
+ 'Control' => 'Control',
37
+ }
38
+
39
+ attr_reader :options
40
+
41
+ def initialize(underscored_widget_name, parent_proxy, args, &block)
42
+ @options = args.last.is_a?(Hash) ? args.last : {}
43
+ super
44
+ end
45
+
46
+ def accelerator=(value)
47
+ @accelerator = value
48
+ configure_menu_item_attribute(accelerator: value)
49
+ root_parent_proxy.bind(accelerator_event) do |event|
50
+ @command_block&.call(event)
51
+ end
52
+ end
53
+
54
+ def accelerator
55
+ @accelerator
56
+ end
57
+
58
+ def accelerator_event
59
+ accelerator_parts = @accelerator.split('+')
60
+ accelerator_parts.map do |accelerator_part|
61
+ ACCELERATOR_MODIFIER_EVENT_MAP[accelerator_part.capitalize] || accelerator_part.downcase
62
+ end.join('-')
63
+ end
64
+
65
+ def state=(value)
66
+ @state = value
67
+ configure_menu_item_attribute(state: value)
68
+ end
69
+
70
+ def state
71
+ @state
72
+ end
73
+
74
+ def label
75
+ @options[:label]
76
+ end
77
+
78
+ def image=(*args)
79
+ @image = image_argument(args)
80
+ configure_menu_item_attribute(image: @image)
81
+ end
82
+
83
+ def image
84
+ @image
85
+ end
86
+
87
+ def compound=(value)
88
+ @compound = value
89
+ configure_menu_item_attribute(compound: @compound)
90
+ end
91
+
92
+ def compound
93
+ @compound
94
+ end
95
+
96
+ def command_block=(proc)
97
+ @command_block = proc
98
+ configure_menu_item_attribute(command: @command_block)
99
+ end
100
+
101
+ def handle_listener(listener_name, &listener)
102
+ case listener_name.to_s.downcase
103
+ when 'command'
104
+ self.command_block = listener
105
+ else
106
+ super
107
+ end
108
+ end
109
+
110
+ def command?
111
+ @args.first.nil? || @args.first == :command || @args.first.is_a?(Hash)
112
+ end
113
+
114
+ def radiobutton?
115
+ @args.first == :radiobutton
116
+ end
117
+
118
+ def checkbutton?
119
+ @args.first == :radiobutton
120
+ end
121
+
122
+ def separator?
123
+ @args.first == :separator
124
+ end
125
+
126
+ def about?
127
+ @args.first == :about
128
+ end
129
+
130
+ def preferences?
131
+ @args.first == :preferences
132
+ end
133
+
134
+ def help?
135
+ @args.first == :help
136
+ end
137
+
138
+ def quit?
139
+ @args.first == :quit
140
+ end
141
+
142
+ def variable(auto_create: true)
143
+ if @variable.nil? && auto_create
144
+ sibling_variable = sibling_radio_menu_items.map {|mi| mi.variable(auto_create: false)}.compact.first
145
+ @variable = sibling_variable.nil? ? ::TkVariable.new : sibling_variable
146
+ else
147
+ @variable
148
+ end
149
+ @variable
150
+ end
151
+
152
+ def selection=(value)
153
+ if value
154
+ variable.value = label
155
+ elsif checkbutton?
156
+ variable.value = ''
157
+ end
158
+ end
159
+
160
+ def selection
161
+ variable.value == label
162
+ end
163
+
164
+ # configures menu item attribute through parent menu
165
+ def configure_menu_item_attribute(attribute_value_hash)
166
+ if preferences? && attribute_value_hash[:command]
167
+ ::Tk.ip_eval("proc ::tk::mac::ShowPreferences {} {#{::Tk.install_cmd(attribute_value_hash[:command])}}") if OS.mac?
168
+ elsif help? && attribute_value_hash[:command]
169
+ ::Tk.ip_eval("proc ::tk::mac::ShowHelp {} {#{::Tk.install_cmd(attribute_value_hash[:command])}}") if OS.mac?
170
+ elsif quit? && attribute_value_hash[:command]
171
+ ::Tk.ip_eval("proc ::tk::mac::Quit {} {#{::Tk.install_cmd(attribute_value_hash[:command])}}") if OS.mac?
172
+ else
173
+ @parent_proxy.tk.entryconfigure label, attribute_value_hash
174
+ end
175
+ end
176
+
177
+ private
178
+
179
+ def sibling_radio_menu_items
180
+ @parent_proxy.children.select {|child| child.is_a?(MenuItemProxy) && radiobutton? && child != self}
181
+ end
182
+
183
+ def build_widget
184
+ @args.prepend(:command) if @args.first.is_a?(Hash)
185
+ @args.append({}) if !@args.last.is_a?(Hash)
186
+ @args.last.merge!(variable: variable, value: label) if radiobutton? || checkbutton?
187
+ case @parent_proxy
188
+ when MenuProxy
189
+ if @parent_proxy.application?
190
+ if OS.mac?
191
+ if about?
192
+ @parent_proxy.tk.add :command, :label => label
193
+ end
194
+ end
195
+ else
196
+ @parent_proxy.tk.add(*@args) unless help?
197
+ end
198
+ end
199
+ end
200
+ end
201
+ end
202
+ end
@@ -0,0 +1,88 @@
1
+ # Copyright (c) 2020-2021 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require 'glimmer/tk/widget_proxy'
23
+
24
+ module Glimmer
25
+ module Tk
26
+ class MenuProxy < WidgetProxy
27
+ attr_reader :options
28
+
29
+ def initialize(underscored_widget_name, parent_proxy, args, &block)
30
+ @options = args.last.is_a?(Hash) ? args.last : {}
31
+ super
32
+ end
33
+
34
+ def post_add_content
35
+ case @parent_proxy
36
+ when ToplevelProxy
37
+ @parent_proxy.tk['menu'] = @tk
38
+ end
39
+ end
40
+
41
+ def label
42
+ @options[:label]
43
+ end
44
+
45
+ def help?
46
+ label == 'Help'
47
+ end
48
+
49
+ def window?
50
+ label == 'Window'
51
+ end
52
+
53
+ def system?
54
+ label == 'System'
55
+ end
56
+
57
+ def application?
58
+ @args.first == :application
59
+ end
60
+
61
+ private
62
+
63
+ def build_widget
64
+ if application?
65
+ if OS.mac?
66
+ @tk = ::TkSysMenu_Apple.new(@parent_proxy.tk)
67
+ @parent_proxy.tk.add :cascade, :menu => @tk
68
+ end
69
+ else
70
+ if @parent_proxy.parent_proxy.is_a?(ToplevelProxy) && (OS.mac? || OS.linux?) && help?
71
+ @tk = ::TkSysMenu_Help.new(@parent_proxy.tk)
72
+ elsif @parent_proxy.parent_proxy.is_a?(ToplevelProxy) && OS.mac? && window?
73
+ @tk = ::Tk::TkSysMenu_Window.new(@parent_proxy.tk)
74
+ elsif @parent_proxy.parent_proxy.is_a?(ToplevelProxy) && OS.windows? && system?
75
+ @tk = ::TkSysMenu_System.new(@parent_proxy.tk)
76
+ else
77
+ tk_widget_class = self.class.tk_widget_class_for(@keyword)
78
+ @tk = tk_widget_class.new(@parent_proxy.tk)
79
+ end
80
+ case @parent_proxy
81
+ when MenuProxy
82
+ @parent_proxy.tk.add(:cascade, {menu: @tk}.merge(@options))
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -20,7 +20,6 @@
20
20
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
21
 
22
22
  require 'glimmer/tk/widget_proxy'
23
- require 'glimmer/tk/commandable'
24
23
 
25
24
  module Glimmer
26
25
  module Tk
@@ -28,8 +27,6 @@ module Glimmer
28
27
  #
29
28
  # Follows the Proxy Design Pattern
30
29
  class RadiobuttonProxy < WidgetProxy
31
- include Commandable
32
-
33
30
  def sibling_radio_buttons
34
31
  @parent_proxy.children.select {|child| child.is_a?(RadiobuttonProxy) && child != self}
35
32
  end
@@ -47,22 +47,6 @@ module Glimmer
47
47
  Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::Tk::RootExpression.new, keyword, *args, &block)
48
48
  end
49
49
 
50
- def set_attribute(attribute, *args)
51
- case attribute.to_s
52
- when 'iconphoto'
53
- args[0..-1] = [image_argument(args)]
54
- super
55
- when 'resizable'
56
- if args.size == 1 && !args.first.is_a?(Array)
57
- self.resizable = [args.first]*2
58
- else
59
- super
60
- end
61
- else
62
- super
63
- end
64
- end
65
-
66
50
  def handle_listener(listener_name, &listener)
67
51
  case listener_name.to_s.upcase
68
52
  when 'WM_OPEN_WINDOW', 'OPEN_WINDOW'
@@ -0,0 +1,133 @@
1
+ # Copyright (c) 2020-2021 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require 'glimmer/tk/widget_proxy'
23
+
24
+ module Glimmer
25
+ module Tk
26
+ # Scrolledframe (#scrolledframe_tk attribute)
27
+ # Nested widgets go into the child site (#tk attribute or @scrolledframe_tk.child_site)
28
+ class ScrollbarFrameProxy < WidgetProxy
29
+ attr_reader :original_parent_proxy, :container_frame_proxy, :canvas_proxy, :yscrollbar_proxy, :xscrollbar_proxy
30
+
31
+ def post_add_content
32
+ @tk.bind('Configure') {
33
+ @canvas_proxy.tk.scrollregion(@canvas_proxy.tk.bbox("all"))
34
+ }
35
+ @canvas_proxy.tk.yscrollcommand {|*args| @yscrollbar_proxy&.set(*args)}
36
+ @canvas_proxy.tk.xscrollcommand {|*args| @xscrollbar_proxy&.set(*args)}
37
+ end
38
+
39
+ def has_attribute?(attribute, *args)
40
+ case attribute.to_s
41
+ when 'yscrollbar', 'xscrollbar'
42
+ true
43
+ else
44
+ container_frame_proxy.has_attribute?(attribute, *args)
45
+ end
46
+ end
47
+
48
+ def set_attribute(attribute, *args)
49
+ case attribute.to_s
50
+ when 'yscrollbar'
51
+ @yscrollbar = args.first
52
+ if @yscrollbar == true
53
+ build_yscrollbar unless @yscrollbar_proxy
54
+ elsif @yscrollbar.is_a?(Glimmer::Tk::WidgetProxy)
55
+ @yscrollbar_proxy.destroy
56
+ build_yscrollbar(@yscrollbar)
57
+ else
58
+ @yscrollbar_proxy.destroy
59
+ @yscrollbar_proxy = nil
60
+ end
61
+ when 'xscrollbar'
62
+ @xscrollbar = args.first
63
+ if @xscrollbar == true
64
+ build_xscrollbar unless @xscrollbar_proxy
65
+ elsif @xscrollbar.is_a?(Glimmer::Tk::WidgetProxy)
66
+ @xscrollbar_proxy.destroy
67
+ build_xscrollbar(@xscrollbar)
68
+ else
69
+ @xscrollbar_proxy.destroy
70
+ @xscrollbar_proxy = nil
71
+ end
72
+ else
73
+ container_frame_proxy.set_attribute(attribute, *args)
74
+ end
75
+ end
76
+
77
+ def get_attribute(attribute, *args)
78
+ case attribute.to_s
79
+ when 'show_yscrollbar'
80
+ @yscrollbar
81
+ when 'show_xscrollbar'
82
+ @xscrollbar
83
+ else
84
+ container_frame_proxy.get_attribute(attribute, *args)
85
+ end
86
+ end
87
+
88
+ private
89
+
90
+ def griddable_parent_proxy
91
+ @original_parent_proxy
92
+ end
93
+
94
+ def griddable_proxy
95
+ @container_frame_proxy
96
+ end
97
+
98
+ def build_widget
99
+ @original_parent_proxy = @parent_proxy
100
+ @container_frame_proxy = WidgetProxy.new('frame', @parent_proxy, @args)
101
+ @parent_proxy = @canvas_proxy = WidgetProxy.new('canvas', @container_frame_proxy, [{width: 300, height: 300}])
102
+ @canvas_proxy.grid(row: 0, column: 0, row_weight: 1, column_weight: 1)
103
+ build_yscrollbar
104
+ build_xscrollbar
105
+ tk_widget_class = self.class.tk_widget_class_for('frame')
106
+ @tk = tk_widget_class.new(@canvas_proxy.tk, *args)
107
+ TkcWindow.new(@canvas_proxy.tk, 0, 0, :anchor => "nw", :window => @tk)
108
+ end
109
+
110
+ def build_yscrollbar(scrollbar_widget_proxy = nil)
111
+ @yscrollbar_proxy = scrollbar_widget_proxy || WidgetProxy.new('scrollbar', @container_frame_proxy, [])
112
+ @yscrollbar_proxy.orient = 'vertical'
113
+ @yscrollbar_proxy.grid(row: 0, column: 1) unless scrollbar_widget_proxy
114
+ @yscrollbar_proxy.command {|*args| @canvas_proxy.tk.yview(*args)}
115
+ end
116
+
117
+ def build_xscrollbar(scrollbar_widget_proxy = nil)
118
+ @xscrollbar_proxy = scrollbar_widget_proxy || WidgetProxy.new('scrollbar', @container_frame_proxy, [])
119
+ @xscrollbar_proxy.orient = 'horizontal'
120
+ @xscrollbar_proxy.grid(row: 1, column: 0, column_span: 2, row_weight: 0) unless scrollbar_widget_proxy
121
+ @xscrollbar_proxy.command {|*args| @canvas_proxy.tk.xview(*args)}
122
+ end
123
+
124
+ def initialize_defaults
125
+ options = {}
126
+ options[:sticky] = 'nsew'
127
+ options[:column_weight] = 1 if @original_parent_proxy.children.count == 1
128
+ options[:row_weight] = 1 if @original_parent_proxy.children.count == 1
129
+ grid(options)
130
+ end
131
+ end
132
+ end
133
+ end
@@ -50,6 +50,22 @@ module Glimmer
50
50
  %w[width height x y].include?(attribute.to_s) || super
51
51
  end
52
52
 
53
+ def set_attribute(attribute, *args)
54
+ case attribute.to_s
55
+ when 'iconphoto'
56
+ args[0..-1] = [image_argument(args)]
57
+ super
58
+ when 'resizable'
59
+ if args.size == 1 && !args.first.is_a?(Array)
60
+ self.resizable = [args.first]*2
61
+ else
62
+ super
63
+ end
64
+ else
65
+ super
66
+ end
67
+ end
68
+
53
69
  def width
54
70
  geometry.split(REGEX_GEOMETRY)[0].to_i
55
71
  end
@@ -46,10 +46,13 @@ module Glimmer
46
46
  # This supports widgets in and out of basic Tk
47
47
  def tk_widget_class_for(underscored_widget_name)
48
48
  tk_widget_class_basename = underscored_widget_name.camelcase(:upper)
49
+ # TODO consider exposing this via Glimmer::Config
49
50
  potential_tk_widget_class_names = [
50
51
  "::Tk::Tile::#{tk_widget_class_basename}",
51
- "::Tk::#{tk_widget_class_basename}",
52
+ "::Tk::BWidget::#{tk_widget_class_basename}",
53
+ "::Tk::Iwidgets::#{tk_widget_class_basename}",
52
54
  "::Tk#{tk_widget_class_basename}",
55
+ "::Tk::#{tk_widget_class_basename}",
53
56
  "::Glimmer::Tk::#{tk_widget_class_basename}Proxy",
54
57
  ]
55
58
  tk_widget_class = nil
@@ -77,10 +80,9 @@ module Glimmer
77
80
  @args = args
78
81
  @keyword = underscored_widget_name
79
82
  @block = block
80
- tk_widget_class = self.class.tk_widget_class_for(underscored_widget_name)
81
- @tk = tk_widget_class.new(@parent_proxy.tk, *args)
83
+ build_widget
82
84
  # a common widget initializer
83
- @parent_proxy.post_initialize_child(self)
85
+ @parent_proxy&.post_initialize_child(self)
84
86
  initialize_defaults
85
87
  post_add_content if @block.nil?
86
88
  end
@@ -121,10 +123,11 @@ module Glimmer
121
123
  end
122
124
 
123
125
  def self.widget_exists?(underscored_widget_name)
124
- !!tk_widget_class_for(underscored_widget_name)
126
+ !!tk_widget_class_for(underscored_widget_name) || (Glimmer::Tk.constants.include?("#{underscored_widget_name.camelcase(:upper)}Proxy".to_sym) && Glimmer::Tk.const_get("#{underscored_widget_name.camelcase(:upper)}Proxy".to_sym).respond_to?(:new))
125
127
  end
126
128
 
127
129
  def tk_widget_has_attribute_setter?(attribute)
130
+ return true if @tk.respond_to?(attribute)
128
131
  result = nil
129
132
  begin
130
133
  # TK Widget currently doesn't support respond_to? properly, so I have to resort to this trick for now
@@ -213,8 +216,8 @@ module Glimmer
213
216
  raise "#{self} cannot handle attribute #{attribute} with args #{args.inspect}"
214
217
  end
215
218
  rescue => e
216
- Glimmer::Config.logger.debug {"Failed to set attribute #{attribute} with args #{args.inspect}. Attempting to set through style instead..."}
217
- Glimmer::Config.logger.debug {e.full_message}
219
+ Glimmer::Config.logger.error {"Failed to set attribute #{attribute} with args #{args.inspect}. Attempting to set through style instead..."}
220
+ Glimmer::Config.logger.error {e.full_message}
218
221
  apply_style(attribute => args.first)
219
222
  end
220
223
  end
@@ -250,7 +253,6 @@ module Glimmer
250
253
 
251
254
  def grid(options = {})
252
255
  options = options.stringify_keys
253
- index_in_parent = @parent_proxy&.children&.index(self)
254
256
  options['rowspan'] = options.delete('row_span') if options.keys.include?('row_span')
255
257
  options['columnspan'] = options.delete('column_span') if options.keys.include?('column_span')
256
258
  options['rowweight'] = options.delete('row_weight') if options.keys.include?('row_weight')
@@ -263,13 +265,14 @@ module Glimmer
263
265
  options['columnminsize'] = options.delete('minwidth') if options.keys.include?('minwidth')
264
266
  options['columnminsize'] = options.delete('min_width') if options.keys.include?('min_width')
265
267
  options['columnminsize'] = options['rowminsize'] = options.delete('minsize') if options.keys.include?('minsize')
268
+ index_in_parent = griddable_parent_proxy&.children&.index(griddable_proxy)
266
269
  if index_in_parent
267
- TkGrid.rowconfigure(@parent_proxy.tk, index_in_parent, 'weight'=> options.delete('rowweight')) if options.keys.include?('rowweight')
268
- TkGrid.rowconfigure(@parent_proxy.tk, index_in_parent, 'minsize'=> options.delete('rowminsize')) if options.keys.include?('rowminsize')
269
- TkGrid.columnconfigure(@parent_proxy.tk, index_in_parent, 'weight'=> options.delete('columnweight')) if options.keys.include?('columnweight')
270
- TkGrid.columnconfigure(@parent_proxy.tk, index_in_parent, 'minsize'=> options.delete('columnminsize')) if options.keys.include?('columnminsize')
270
+ TkGrid.rowconfigure(griddable_parent_proxy.tk, index_in_parent, 'weight'=> options.delete('rowweight')) if options.keys.include?('rowweight')
271
+ TkGrid.rowconfigure(griddable_parent_proxy.tk, index_in_parent, 'minsize'=> options.delete('rowminsize')) if options.keys.include?('rowminsize')
272
+ TkGrid.columnconfigure(griddable_parent_proxy.tk, index_in_parent, 'weight'=> options.delete('columnweight')) if options.keys.include?('columnweight')
273
+ TkGrid.columnconfigure(griddable_parent_proxy.tk, index_in_parent, 'minsize'=> options.delete('columnminsize')) if options.keys.include?('columnminsize')
271
274
  end
272
- @tk.grid(options)
275
+ griddable_proxy&.tk&.grid(options)
273
276
  end
274
277
 
275
278
  def font=(value)
@@ -451,6 +454,7 @@ module Glimmer
451
454
 
452
455
  def handle_listener(listener_name, &listener)
453
456
  listener_name = listener_name.to_s
457
+ # TODO return a listener registration object that has a deregister method
454
458
  if listener_name == 'destroy'
455
459
  # 'destroy' is a more reliable alternative listener binding to '<Destroy>'
456
460
  @on_destroy_procs ||= []
@@ -458,22 +462,26 @@ module Glimmer
458
462
  @on_destroy_procs << listener
459
463
  @tk.bind('<Destroy>', listener)
460
464
  parent_proxy.handle_listener(listener_name, &listener) if parent_proxy
461
- # TODO return a listener registration object that has a deregister method
462
465
  else
463
466
  @listeners ||= {}
464
467
  begin
465
468
  @listeners[listener_name] ||= []
466
- @tk.bind(listener_name) { |event| @listeners[listener_name].each {|l| l.call(event)} } if @listeners[listener_name].empty?
469
+ if @tk.respond_to?(listener_name)
470
+ @tk.send(listener_name) { |*args| @listeners[listener_name].each {|l| l.call(*args)} } if @listeners[listener_name].empty?
471
+ else
472
+ @tk.bind(listener_name) { |*args| @listeners[listener_name].each {|l| l.call(*args)} } if @listeners[listener_name].empty?
473
+ end
467
474
  @listeners[listener_name] << listener
468
475
  rescue => e
469
476
  @listeners.delete(listener_name)
470
- Glimmer::Config.logger.debug {"Unable to bind to #{listener_name} .. attempting to surround with <>"}
477
+ Glimmer::Config.logger.debug {"Unable to bind to #{listener_name} .. attempting to surround with <> ..."}
471
478
  Glimmer::Config.logger.debug {e.full_message}
472
- listener_name = "<#{listener_name}" if !listener_name.start_with?('<')
473
- listener_name = "#{listener_name}>" if !listener_name.end_with?('>')
474
- @listeners[listener_name] ||= []
475
- @tk.bind(listener_name) { |event| @listeners[listener_name].each {|l| l.call(event)} } if @listeners[listener_name].empty?
476
- @listeners[listener_name] << listener
479
+ new_listener_name = listener_name
480
+ new_listener_name = "<#{new_listener_name}" if !new_listener_name.start_with?('<')
481
+ new_listener_name = "#{new_listener_name}>" if !new_listener_name.end_with?('>')
482
+ @listeners[new_listener_name] ||= []
483
+ @tk.bind(new_listener_name) { |*args| @listeners[new_listener_name].each {|l| l.call(*args)} } if @listeners[new_listener_name].empty?
484
+ @listeners[new_listener_name] << listener
477
485
  end
478
486
  end
479
487
  end
@@ -507,11 +515,26 @@ module Glimmer
507
515
 
508
516
  private
509
517
 
518
+ # The griddable parent widget proxy to apply grid to (is different from @tk in composite widgets like notebook or scrolledframe)
519
+ def griddable_parent_proxy
520
+ @parent_proxy
521
+ end
522
+
523
+ # The griddable widget proxy to apply grid to (is different from @tk in composite widgets like notebook or scrolledframe)
524
+ def griddable_proxy
525
+ self
526
+ end
527
+
528
+ def build_widget
529
+ tk_widget_class = self.class.tk_widget_class_for(@keyword)
530
+ @tk = tk_widget_class.new(@parent_proxy.tk, *args)
531
+ end
532
+
510
533
  def initialize_defaults
511
534
  options = {}
512
535
  options[:sticky] = 'nsew'
513
536
  options[:column_weight] = 1 if @parent_proxy.children.count == 1
514
- grid(options) unless @tk.is_a?(::Tk::Toplevel)
537
+ grid(options) unless @tk.is_a?(::Tk::Toplevel) || @tk.is_a?(::Tk::Menu) || @tk.nil? # TODO refactor by adding a griddable? method that could be overriden by subclasses to consult for this call
515
538
  end
516
539
  end
517
540
  end