glimmer-dsl-tk 0.0.1 → 0.0.6

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.
@@ -0,0 +1,42 @@
1
+ # Copyright (c) 2020 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
+ # Proxy for Tk::Tile::Frame
27
+ #
28
+ # Follows the Proxy Design Pattern
29
+ class FrameProxy < WidgetProxy
30
+ attr_reader :tab_options
31
+
32
+ def initialize(underscored_widget_name, parent_proxy, args)
33
+ if parent_proxy.is_a?(NotebookProxy)
34
+ @tab_options, args[0] = args[0].to_h.partition {|key, value| NotebookProxy::TAB_OPTIONS.include?(key.to_s)}
35
+ @tab_options = Hash[@tab_options]
36
+ args.delete_at(0) if args[0].to_a.empty?
37
+ end
38
+ super
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,40 @@
1
+ # Copyright (c) 2020 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
+ # Proxy for Tk::Tile::TLabel
27
+ #
28
+ # Follows the Proxy Design Pattern
29
+ class LabelProxy < WidgetProxy
30
+ # TODO specify attribute setters
31
+ # def tk_widget_has_attribute_setter?(attribute)
32
+ # if ['anchor', 'justify'].include?(attribute.to_s)
33
+ # true
34
+ # else
35
+ # super
36
+ # end
37
+ # end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,60 @@
1
+ # Copyright (c) 2020 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
+ # Custom list widget implementation
27
+ class ListProxy < WidgetProxy
28
+ def initialize(underscored_widget_name, parent_proxy, args)
29
+ super('treeview', parent_proxy, args)
30
+ @tk.show = 'tree'
31
+ end
32
+
33
+ def widget_custom_attribute_mapping
34
+ @widget_custom_attribute_mapping ||= {
35
+ ::Tk::Tile::Treeview => {
36
+ 'items' => {
37
+ getter: {name: 'items', invoker: lambda { |widget, args| tk.children('').map(&:text) }},
38
+ setter: {name: 'items=', invoker: lambda { |widget, args|
39
+ @tk.delete @tk.children('')
40
+ args.first.each do |child|
41
+ @tk.insert('', 'end', :text => child)
42
+ end
43
+ }},
44
+ },
45
+ 'selection' => {
46
+ getter: {name: 'selection', invoker: lambda { |widget, args| @tk.selection.map(&:text) }},
47
+ setter: {name: 'selection=', invoker: lambda { |widget, args|
48
+ selection_args = args.first.is_a?(Array) ? args.first : [args.first]
49
+ selection_items = selection_args.map do |arg|
50
+ @tk.children('').detect {|item| item.text == arg}
51
+ end
52
+ @tk.selection_set(*selection_items)
53
+ }},
54
+ },
55
+ },
56
+ }
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,45 @@
1
+ # Copyright (c) 2020 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
+ # Proxy for Tk::Tile::Notebook
27
+ #
28
+ # Follows the Proxy Design Pattern
29
+ class NotebookProxy < WidgetProxy
30
+ TAB_OPTIONS = ['state', 'sticky', 'padding', 'text', 'image', 'compound', 'underline']
31
+
32
+ attr_reader :tab_proxies
33
+
34
+ def initialize(*args)
35
+ @tab_proxies = []
36
+ super
37
+ end
38
+
39
+ def post_initialize_child(child)
40
+ @tab_proxies << child
41
+ @tk.add child.tk, child.tab_options
42
+ end
43
+ end
44
+ end
45
+ end
@@ -29,7 +29,7 @@ module Glimmer
29
29
  class RootProxy < WidgetProxy
30
30
 
31
31
  def initialize(*args)
32
- @tk_widget = ::TkRoot.new
32
+ @tk = ::TkRoot.new
33
33
  end
34
34
 
35
35
  def open
@@ -37,7 +37,7 @@ module Glimmer
37
37
  end
38
38
 
39
39
  def content(&block)
40
- Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::SWT::RootExpression.new, &block)
40
+ Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::Tk::RootExpression.new, &block)
41
41
  end
42
42
 
43
43
  # Starts Tk mainloop
@@ -25,23 +25,67 @@ module Glimmer
25
25
  #
26
26
  # Follows the Proxy Design Pattern
27
27
  class WidgetProxy
28
- attr_reader :parent_proxy, :tk_widget, :drag_source_proxy, :drop_target_proxy, :drag_source_style, :drag_source_transfer, :drop_target_transfer
28
+ attr_reader :parent_proxy, :tk, :args
29
29
 
30
30
  DEFAULT_INITIALIZERS = {
31
- 'label' => lambda do |label|
32
- label.grid
31
+ 'combobox' => lambda do |tk|
32
+ tk.textvariable = ::TkVariable.new
33
+ end,
34
+ 'label' => lambda do |tk|
35
+ tk.textvariable = ::TkVariable.new
36
+ end,
37
+ 'entry' => lambda do |tk|
38
+ tk.textvariable = ::TkVariable.new
33
39
  end,
34
40
  }
35
41
 
42
+ class << self
43
+ def create(keyword, parent, args)
44
+ widget_proxy_class(keyword).new(keyword, parent, args)
45
+ end
46
+
47
+ def widget_proxy_class(keyword)
48
+ begin
49
+ class_name = "#{keyword.camelcase(:upper)}Proxy".to_sym
50
+ Glimmer::Tk.const_get(class_name)
51
+ rescue
52
+ Glimmer::Tk::WidgetProxy
53
+ end
54
+ end
55
+
56
+ # This supports widgets in and out of basic Tk
57
+ def tk_widget_class_for(underscored_widget_name)
58
+ tk_widget_class_basename = underscored_widget_name.camelcase(:upper)
59
+ potential_tk_widget_class_names = [
60
+ "::Tk::Tile::#{tk_widget_class_basename}",
61
+ "::Tk::#{tk_widget_class_basename}",
62
+ "::Tk#{tk_widget_class_basename}",
63
+ "::Glimmer::Tk::#{tk_widget_class_basename}Proxy",
64
+ ]
65
+ tk_widget_class = nil
66
+ potential_tk_widget_class_names.each do |tk_widget_name|
67
+ begin
68
+ tk_widget_class = eval(tk_widget_name)
69
+ break
70
+ rescue RuntimeError, SyntaxError, NameError => e
71
+ Glimmer::Config.logger.debug e.full_message
72
+ end
73
+ end
74
+ tk_widget_class
75
+ end
76
+ end
77
+
36
78
  # Initializes a new Tk Widget
37
79
  #
38
80
  # Styles is a comma separate list of symbols representing Tk styles in lower case
39
- def initialize(*init_args)
40
- underscored_widget_name, parent, extra_options = init_args
41
- @parent_proxy = parent
42
- tk_widget_class = self.class.tk_widget_class_for(underscored_widget_name)
43
- @tk_widget = tk_widget_class.new(@parent_proxy.tk_widget, *extra_options)
44
- DEFAULT_INITIALIZERS[underscored_widget_name]&.call(@tk_widget)
81
+ def initialize(underscored_widget_name, parent_proxy, args)
82
+ @parent_proxy = parent_proxy
83
+ @args = args
84
+ tk_widget_class = self.class.tk_widget_class_for(underscored_widget_name)
85
+ @tk = tk_widget_class.new(@parent_proxy.tk, *args)
86
+ # a common widget initializer
87
+ @tk.grid
88
+ DEFAULT_INITIALIZERS[underscored_widget_name]&.call(@tk)
45
89
  @parent_proxy.post_initialize_child(self)
46
90
  end
47
91
 
@@ -59,24 +103,23 @@ module Glimmer
59
103
  !!tk_widget_class_for(underscored_widget_name)
60
104
  end
61
105
 
62
- # This supports widgets in and out of basic Tk
63
- def self.tk_widget_class_for(underscored_widget_name)
64
- tk_widget_name = "::Tk::Tile::#{underscored_widget_name.camelcase(:upper)}"
65
- tk_widget_class = eval(tk_widget_name)
66
- tk_widget_class
67
- rescue SyntaxError, NameError => e
68
- puts e.full_message
69
- nil
70
- rescue => e
71
- puts e.full_message
72
- nil
106
+ def tk_widget_has_attribute_setter?(attribute)
107
+ result = nil
108
+ begin
109
+ # TK Widget currently doesn't support respond_to? properly, so I have to resort to this trick for now
110
+ @tk.send(attribute_setter(attribute), @tk.send(attribute))
111
+ result = true
112
+ rescue => e
113
+ result = false
114
+ end
115
+ result
73
116
  end
74
117
 
75
- def tk_widget_has_attribute?(attribute_name)
118
+ def tk_widget_has_attribute_getter_setter?(attribute)
76
119
  result = nil
77
120
  begin
78
121
  # TK Widget currently doesn't support respond_to? properly, so I have to resort to this trick for now
79
- @tk_widget.send(attribute_setter(attribute_name), @tk_widget.send(attribute_name))
122
+ @tk.send(attribute, @tk.send(attribute))
80
123
  result = true
81
124
  rescue => e
82
125
  result = false
@@ -84,40 +127,123 @@ module Glimmer
84
127
  result
85
128
  end
86
129
 
87
- def has_attribute?(attribute_name, *args)
88
- tk_widget_has_attribute?(attribute_name) || respond_to?(attribute_setter(attribute_name), args)
130
+ def has_attribute?(attribute, *args)
131
+ (widget_custom_attribute_mapping[tk.class] && widget_custom_attribute_mapping[tk.class][attribute.to_s]) ||
132
+ tk_widget_has_attribute_setter?(attribute) ||
133
+ tk_widget_has_attribute_getter_setter?(attribute) ||
134
+ respond_to?(attribute_setter(attribute), args)
89
135
  end
90
136
 
91
- def set_attribute(attribute_name, *args)
92
- if tk_widget_has_attribute?(attribute_name)
93
- @tk_widget.send(attribute_setter(attribute_name), *args) unless @tk_widget.send(attribute_name) == args.first
137
+ def set_attribute(attribute, *args)
138
+ widget_custom_attribute = widget_custom_attribute_mapping[tk.class] && widget_custom_attribute_mapping[tk.class][attribute.to_s]
139
+ if widget_custom_attribute
140
+ widget_custom_attribute[:setter][:invoker].call(@tk, args)
141
+ elsif tk_widget_has_attribute_setter?(attribute)
142
+ @tk.send(attribute_setter(attribute), *args) unless @tk.send(attribute) == args.first
143
+ elsif tk_widget_has_attribute_getter_setter?(attribute)
144
+ @tk.send(attribute, *args) unless @tk.send(attribute) == args.first
94
145
  else
95
- send(attribute_setter(attribute_name), args)
146
+ send(attribute_setter(attribute), args)
96
147
  end
97
148
  end
98
149
 
99
- def get_attribute(attribute_name)
100
- @tk_widget.send(attribute_name)
101
- end
150
+ def get_attribute(attribute)
151
+ widget_custom_attribute = widget_custom_attribute_mapping[tk.class] && widget_custom_attribute_mapping[tk.class][attribute.to_s]
152
+ if widget_custom_attribute
153
+ widget_custom_attribute[:getter][:invoker].call(@tk, args)
154
+ elsif tk_widget_has_attribute_getter_setter?(attribute)
155
+ @tk.send(attribute)
156
+ else
157
+ send(attribute)
158
+ end
159
+ end
102
160
 
103
- def attribute_setter(attribute_name)
104
- "#{attribute_name}="
161
+ def attribute_setter(attribute)
162
+ "#{attribute}="
163
+ end
164
+
165
+ def widget_custom_attribute_mapping
166
+ @widget_custom_attribute_mapping ||= {
167
+ ::Tk::Tile::TCombobox => {
168
+ 'text' => {
169
+ getter: {name: 'text', invoker: lambda { |widget, args| @tk.textvariable&.value }},
170
+ setter: {name: 'text=', invoker: lambda { |widget, args| @tk.textvariable&.value = args.first }},
171
+ },
172
+ },
173
+ ::Tk::Tile::TLabel => {
174
+ 'text' => {
175
+ getter: {name: 'text', invoker: lambda { |widget, args| @tk.textvariable&.value }},
176
+ setter: {name: 'text=', invoker: lambda { |widget, args| @tk.textvariable&.value = args.first }},
177
+ },
178
+ },
179
+ ::Tk::Tile::TEntry => {
180
+ 'text' => {
181
+ getter: {name: 'text', invoker: lambda { |widget, args| @tk.textvariable&.value }},
182
+ setter: {name: 'text=', invoker: lambda { |widget, args| @tk.textvariable&.value = args.first unless @text_variable_edit }},
183
+ },
184
+ },
185
+ }
186
+ end
187
+
188
+ def widget_attribute_listener_installers
189
+ @tk_widget_attribute_listener_installers ||= {
190
+ ::Tk::Tile::TCombobox => {
191
+ 'text' => lambda do |observer|
192
+ if observer.is_a?(Glimmer::DataBinding::ModelBinding)
193
+ model = observer.model
194
+ options_model_property = observer.property_name + '_options'
195
+ @tk.values = model.send(options_model_property) if model.respond_to?(options_model_property)
196
+ end
197
+ @tk.bind('<ComboboxSelected>') {
198
+ observer.call(@tk.textvariable.value)
199
+ }
200
+ end,
201
+ },
202
+ ::Tk::Tile::TEntry => {
203
+ 'text' => lambda do |observer|
204
+ tk.validate = 'key'
205
+ tk.validatecommand { |new_tk_variable|
206
+ @text_variable_edit = new_tk_variable.value != @tk.textvariable.value
207
+ if @text_variable_edit
208
+ observer.call(new_tk_variable.value)
209
+ @text_variable_edit = nil
210
+ true
211
+ else
212
+ false
213
+ end
214
+ }
215
+ end,
216
+ },
217
+ }
105
218
  end
219
+
220
+ def add_observer(observer, attribute)
221
+ attribute_listener_installers = @tk.class.ancestors.map {|ancestor| widget_attribute_listener_installers[ancestor]}.compact
222
+ widget_listener_installers = attribute_listener_installers.map{|installer| installer[attribute.to_s]}.compact if !attribute_listener_installers.empty?
223
+ widget_listener_installers.to_a.first&.call(observer)
224
+ end
106
225
 
107
226
  def content(&block)
108
227
  Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::Tk::WidgetExpression.new, &block)
109
228
  end
110
229
 
111
230
  def method_missing(method, *args, &block)
112
- tk_widget.send(method, *args, &block)
231
+ method = method.to_s
232
+ if args.empty? && block.nil? && widget_custom_attribute_mapping[tk.class] && widget_custom_attribute_mapping[tk.class][method]
233
+ get_attribute(method)
234
+ elsif widget_custom_attribute_mapping[tk.class] && widget_custom_attribute_mapping[tk.class][method.sub(/=$/, '')] && method.end_with?('=') && block.nil?
235
+ set_attribute(method.sub(/=$/, ''), *args)
236
+ else
237
+ tk.send(method, *args, &block)
238
+ end
113
239
  rescue => e
114
- Glimmer::Config.logger.debug {"Neither WidgetProxy nor #{tk_widget.class.name} can handle the method ##{method}"}
115
- super
240
+ Glimmer::Config.logger.debug {"Neither WidgetProxy nor #{tk.class.name} can handle the method ##{method}"}
241
+ super(method.to_sym, *args, &block)
116
242
  end
117
243
 
118
244
  def respond_to?(method, *args, &block)
119
245
  super ||
120
- tk_widget.respond_to?(method, *args, &block)
246
+ tk.respond_to?(method, *args, &block)
121
247
  end
122
248
  end
123
249
  end