glimmer-dsl-tk 0.0.1 → 0.0.6

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