glimmer-dsl-tk 0.0.34 → 0.0.38
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +30 -0
- data/README.md +464 -21
- data/VERSION +1 -1
- data/bin/girb_runner.rb +1 -1
- data/glimmer-dsl-tk.gemspec +0 -0
- data/lib/glimmer/dsl/tk/widget_expression.rb +1 -1
- data/lib/glimmer/tk/menu_item_proxy.rb +144 -0
- data/lib/glimmer/tk/menu_proxy.rb +48 -0
- data/lib/glimmer/tk/root_proxy.rb +1 -16
- data/lib/glimmer/tk/text_proxy.rb +25 -37
- data/lib/glimmer/tk/toplevel_proxy.rb +40 -0
- data/lib/glimmer/tk/widget_proxy.rb +18 -8
- data/lib/glimmer-dsl-tk.rb +4 -0
- data/samples/elaborate/meta_sample.rb +1 -1
- data/samples/hello/hello_checkbutton.rb +1 -1
- data/samples/hello/hello_menu_bar.rb +183 -0
- data/samples/hello/hello_text.rb +9 -9
- data/samples/hello/hello_toplevel.rb +178 -0
- metadata +6 -2
@@ -0,0 +1,144 @@
|
|
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
|
+
@parent_proxy.tk.entryconfigure @options[:label], 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
|
+
@parent_proxy.tk.entryconfigure @options[:label], :state => value
|
68
|
+
end
|
69
|
+
|
70
|
+
def state
|
71
|
+
@state
|
72
|
+
end
|
73
|
+
|
74
|
+
def command_block=(proc)
|
75
|
+
@command_block = proc
|
76
|
+
@parent_proxy.tk.entryconfigure @options[:label], command: @command_block
|
77
|
+
end
|
78
|
+
|
79
|
+
def handle_listener(listener_name, &listener)
|
80
|
+
case listener_name.to_s.downcase
|
81
|
+
when 'command'
|
82
|
+
self.command_block = listener
|
83
|
+
else
|
84
|
+
super
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def command?
|
89
|
+
@args.first.nil? || @args.first == :command || @args.first.is_a?(Hash)
|
90
|
+
end
|
91
|
+
|
92
|
+
def radiobutton?
|
93
|
+
@args.first == :radiobutton
|
94
|
+
end
|
95
|
+
|
96
|
+
def checkbutton?
|
97
|
+
@args.first == :radiobutton
|
98
|
+
end
|
99
|
+
|
100
|
+
def separator?
|
101
|
+
@args.first == :separator
|
102
|
+
end
|
103
|
+
|
104
|
+
def variable(auto_create: true)
|
105
|
+
if @variable.nil? && auto_create
|
106
|
+
sibling_variable = sibling_radio_menu_items.map {|mi| mi.variable(auto_create: false)}.compact.first
|
107
|
+
@variable = sibling_variable.nil? ? ::TkVariable.new : sibling_variable
|
108
|
+
else
|
109
|
+
@variable
|
110
|
+
end
|
111
|
+
@variable
|
112
|
+
end
|
113
|
+
|
114
|
+
def selection=(value)
|
115
|
+
if value
|
116
|
+
variable.value = @options[:label]
|
117
|
+
# TODO handle image case where there is no label
|
118
|
+
elsif checkbutton?
|
119
|
+
variable.value = '__unchecked__'
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def selection
|
124
|
+
variable.value == @options[:label]
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
def sibling_radio_menu_items
|
130
|
+
@parent_proxy.children.select {|child| child.is_a?(MenuItemProxy) && radiobutton? && child != self}
|
131
|
+
end
|
132
|
+
|
133
|
+
def build_widget
|
134
|
+
@args.prepend(:command) if @args.first.is_a?(Hash)
|
135
|
+
@args.append({}) if !@args.last.is_a?(Hash)
|
136
|
+
@args.last.merge!(variable: variable, value: @options[:label]) if radiobutton? || checkbutton?
|
137
|
+
case @parent_proxy
|
138
|
+
when MenuProxy
|
139
|
+
@parent_proxy.tk.add(*@args)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,48 @@
|
|
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
|
+
private
|
35
|
+
|
36
|
+
def build_widget
|
37
|
+
tk_widget_class = self.class.tk_widget_class_for(@keyword)
|
38
|
+
@tk = tk_widget_class.new(@parent_proxy.tk)
|
39
|
+
case @parent_proxy
|
40
|
+
when ToplevelProxy
|
41
|
+
@parent_proxy.tk['menu'] = @tk
|
42
|
+
when MenuProxy
|
43
|
+
@parent_proxy.tk.add(:cascade, {menu: @tk}.merge(@options))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -36,6 +36,7 @@ module Glimmer
|
|
36
36
|
|
37
37
|
def post_add_content
|
38
38
|
set_attribute('iconphoto', File.expand_path('../../../icons/glimmer.png', __dir__)) if @tk.iconphoto.nil?
|
39
|
+
super
|
39
40
|
end
|
40
41
|
|
41
42
|
def open
|
@@ -46,22 +47,6 @@ module Glimmer
|
|
46
47
|
Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::Tk::RootExpression.new, keyword, *args, &block)
|
47
48
|
end
|
48
49
|
|
49
|
-
def set_attribute(attribute, *args)
|
50
|
-
case attribute.to_s
|
51
|
-
when 'iconphoto'
|
52
|
-
args[0..-1] = [image_argument(args)]
|
53
|
-
super
|
54
|
-
when 'resizable'
|
55
|
-
if args.size == 1 && !args.first.is_a?(Array)
|
56
|
-
self.resizable = [args.first]*2
|
57
|
-
else
|
58
|
-
super
|
59
|
-
end
|
60
|
-
else
|
61
|
-
super
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
50
|
def handle_listener(listener_name, &listener)
|
66
51
|
case listener_name.to_s.upcase
|
67
52
|
when 'WM_OPEN_WINDOW', 'OPEN_WINDOW'
|
@@ -39,7 +39,6 @@ module Glimmer
|
|
39
39
|
@modified_count ||= 0
|
40
40
|
@modified_count += 1
|
41
41
|
listener.call(*args)
|
42
|
-
apply_all_tag
|
43
42
|
@insert_mark_moved_proc&.call
|
44
43
|
@tk.modified = false
|
45
44
|
end
|
@@ -72,28 +71,17 @@ module Glimmer
|
|
72
71
|
end
|
73
72
|
end
|
74
73
|
else
|
75
|
-
|
76
|
-
# TODO make listener pass an event that has a modifiers attribute for easy representation of :shift, :meta, :control, etc... while a letter button is pressed
|
77
|
-
@listeners ||= {}
|
78
|
-
begin
|
79
|
-
@listeners[listener_name] ||= []
|
80
|
-
@tk.tag_bind(ALL_TAG, listener_name) { |event| @listeners[listener_name].each {|l| l.call(event)} } if @listeners[listener_name].empty?
|
81
|
-
@listeners[listener_name] << listener
|
82
|
-
rescue => e
|
83
|
-
@listeners.delete(listener_name)
|
84
|
-
Glimmer::Config.logger.debug {"Unable to bind to #{listener_name} .. attempting to surround with <>"}
|
85
|
-
Glimmer::Config.logger.debug {e.full_message}
|
86
|
-
listener_name = "<#{listener_name}" if !listener_name.start_with?('<')
|
87
|
-
listener_name = "#{listener_name}>" if !listener_name.end_with?('>')
|
88
|
-
@listeners[listener_name] ||= []
|
89
|
-
@tk.tag_bind(ALL_TAG, listener_name) { |event| @listeners[listener_name].each {|l| l.call(event)} } if @listeners[listener_name].empty?
|
90
|
-
@listeners[listener_name] << listener
|
91
|
-
end
|
74
|
+
super
|
92
75
|
end
|
93
76
|
end
|
94
77
|
|
95
78
|
def edit_undo
|
96
|
-
|
79
|
+
# <Modified> fires twice the first time, which is equivalent to one change.
|
80
|
+
if @modified_count.to_i > 2
|
81
|
+
# must count the extra 2 modified count that will occur upon undo too
|
82
|
+
@modified_count -= 4
|
83
|
+
@tk.edit_undo
|
84
|
+
end
|
97
85
|
end
|
98
86
|
|
99
87
|
def edit_redo
|
@@ -104,31 +92,31 @@ module Glimmer
|
|
104
92
|
end
|
105
93
|
end
|
106
94
|
|
107
|
-
def add_selection_format(option, value, no_selection_default: :insert_word)
|
108
|
-
process_selection_ranges(no_selection_default: no_selection_default) { |range_start, range_end| add_format(range_start, range_end, option, value) }
|
95
|
+
def add_selection_format(option, value, no_selection_default: :insert_word, focus: true)
|
96
|
+
process_selection_ranges(no_selection_default: no_selection_default, focus: focus) { |range_start, range_end| add_format(range_start, range_end, option, value) }
|
109
97
|
end
|
110
98
|
|
111
|
-
def remove_selection_format(option, value, no_selection_default: :insert_word)
|
112
|
-
process_selection_ranges(no_selection_default: no_selection_default) { |range_start, range_end| remove_format(range_start, range_end, option, value) }
|
99
|
+
def remove_selection_format(option, value, no_selection_default: :insert_word, focus: true)
|
100
|
+
process_selection_ranges(no_selection_default: no_selection_default, focus: focus) { |range_start, range_end| remove_format(range_start, range_end, option, value) }
|
113
101
|
end
|
114
102
|
|
115
|
-
def toggle_selection_format(option, value, no_selection_default: :insert_word)
|
116
|
-
process_selection_ranges(no_selection_default: no_selection_default) { |range_start, range_end| toggle_format(range_start, range_end, option, value) }
|
103
|
+
def toggle_selection_format(option, value, no_selection_default: :insert_word, focus: true)
|
104
|
+
process_selection_ranges(no_selection_default: no_selection_default, focus: focus) { |range_start, range_end| toggle_format(range_start, range_end, option, value) }
|
117
105
|
end
|
118
106
|
|
119
|
-
def add_selection_font_format(option, value, no_selection_default: :insert_word)
|
120
|
-
process_selection_ranges(no_selection_default: no_selection_default) { |range_start, range_end| add_font_format(range_start, range_end, option, value) }
|
107
|
+
def add_selection_font_format(option, value, no_selection_default: :insert_word, focus: true)
|
108
|
+
process_selection_ranges(no_selection_default: no_selection_default, focus: focus) { |range_start, range_end| add_font_format(range_start, range_end, option, value) }
|
121
109
|
end
|
122
110
|
|
123
|
-
def remove_selection_font_format(option, value, no_selection_default: :insert_word)
|
124
|
-
process_selection_ranges(no_selection_default: no_selection_default) { |range_start, range_end| remove_font_format(range_start, range_end, option, value) }
|
111
|
+
def remove_selection_font_format(option, value, no_selection_default: :insert_word, focus: true)
|
112
|
+
process_selection_ranges(no_selection_default: no_selection_default, focus: focus) { |range_start, range_end| remove_font_format(range_start, range_end, option, value) }
|
125
113
|
end
|
126
114
|
|
127
|
-
def toggle_selection_font_format(option, value, no_selection_default: :insert_word)
|
128
|
-
process_selection_ranges(no_selection_default: no_selection_default) { |range_start, range_end| toggle_font_format(range_start, range_end, option, value) }
|
115
|
+
def toggle_selection_font_format(option, value, no_selection_default: :insert_word, focus: true)
|
116
|
+
process_selection_ranges(no_selection_default: no_selection_default, focus: focus) { |range_start, range_end| toggle_font_format(range_start, range_end, option, value) }
|
129
117
|
end
|
130
118
|
|
131
|
-
def process_selection_ranges(no_selection_default: :insert_word, &processor)
|
119
|
+
def process_selection_ranges(no_selection_default: :insert_word, focus: true, &processor)
|
132
120
|
regions = @tk.tag_ranges('sel')
|
133
121
|
if regions.empty?
|
134
122
|
case no_selection_default
|
@@ -143,6 +131,11 @@ module Glimmer
|
|
143
131
|
range_end = region.last
|
144
132
|
processor.call(range_start, range_end)
|
145
133
|
end
|
134
|
+
if focus == true
|
135
|
+
@tk.focus
|
136
|
+
elsif focus.is_a?(Integer)
|
137
|
+
::Tk.after(focus) { @tk.focus }
|
138
|
+
end
|
146
139
|
end
|
147
140
|
|
148
141
|
def applied_format?(region_start, region_end, option, value)
|
@@ -444,16 +437,11 @@ module Glimmer
|
|
444
437
|
self.wrap = 'none'
|
445
438
|
self.padx = 5
|
446
439
|
self.pady = 5
|
447
|
-
on('Modified') { apply_all_tag }
|
448
440
|
end
|
449
441
|
|
450
442
|
def clone_font(font)
|
451
443
|
::TkFont.new(Hash[font.actual])
|
452
444
|
end
|
453
|
-
|
454
|
-
def apply_all_tag
|
455
|
-
@tk.tag_add(ALL_TAG, '1.0', 'end')
|
456
|
-
end
|
457
445
|
end
|
458
446
|
end
|
459
447
|
end
|
@@ -32,11 +32,40 @@ module Glimmer
|
|
32
32
|
DEFAULT_HEIGHT = 95
|
33
33
|
|
34
34
|
attr_reader :tk
|
35
|
+
attr_accessor :escapable
|
36
|
+
alias escapable? escapable
|
37
|
+
|
38
|
+
def post_add_content
|
39
|
+
if escapable?
|
40
|
+
on('KeyPress') do |event|
|
41
|
+
if event.keysym == 'Escape'
|
42
|
+
grab_release
|
43
|
+
destroy
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
35
48
|
|
36
49
|
def has_attribute?(attribute, *args)
|
37
50
|
%w[width height x y].include?(attribute.to_s) || super
|
38
51
|
end
|
39
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
|
+
|
40
69
|
def width
|
41
70
|
geometry.split(REGEX_GEOMETRY)[0].to_i
|
42
71
|
end
|
@@ -114,6 +143,17 @@ module Glimmer
|
|
114
143
|
destroy
|
115
144
|
end
|
116
145
|
end
|
146
|
+
|
147
|
+
def mac_style=(mac_class, mac_attribute_list = nil)
|
148
|
+
if OS.mac?
|
149
|
+
@mac_style = [mac_class, mac_attribute_list]
|
150
|
+
::Tk.tk_call("::tk::unsupported::MacWindowStyle", "style", @tk, *@mac_style.compact)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def mac_style
|
155
|
+
@mac_style
|
156
|
+
end
|
117
157
|
end
|
118
158
|
end
|
119
159
|
end
|
@@ -77,8 +77,7 @@ module Glimmer
|
|
77
77
|
@args = args
|
78
78
|
@keyword = underscored_widget_name
|
79
79
|
@block = block
|
80
|
-
|
81
|
-
@tk = tk_widget_class.new(@parent_proxy.tk, *args)
|
80
|
+
build_widget
|
82
81
|
# a common widget initializer
|
83
82
|
@parent_proxy.post_initialize_child(self)
|
84
83
|
initialize_defaults
|
@@ -121,7 +120,7 @@ module Glimmer
|
|
121
120
|
end
|
122
121
|
|
123
122
|
def self.widget_exists?(underscored_widget_name)
|
124
|
-
!!tk_widget_class_for(underscored_widget_name)
|
123
|
+
!!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
124
|
end
|
126
125
|
|
127
126
|
def tk_widget_has_attribute_setter?(attribute)
|
@@ -213,8 +212,8 @@ module Glimmer
|
|
213
212
|
raise "#{self} cannot handle attribute #{attribute} with args #{args.inspect}"
|
214
213
|
end
|
215
214
|
rescue => e
|
216
|
-
Glimmer::Config.logger.
|
217
|
-
Glimmer::Config.logger.
|
215
|
+
Glimmer::Config.logger.error {"Failed to set attribute #{attribute} with args #{args.inspect}. Attempting to set through style instead..."}
|
216
|
+
Glimmer::Config.logger.error {e.full_message}
|
218
217
|
apply_style(attribute => args.first)
|
219
218
|
end
|
220
219
|
end
|
@@ -460,14 +459,20 @@ module Glimmer
|
|
460
459
|
parent_proxy.handle_listener(listener_name, &listener) if parent_proxy
|
461
460
|
# TODO return a listener registration object that has a deregister method
|
462
461
|
else
|
462
|
+
@listeners ||= {}
|
463
463
|
begin
|
464
|
-
@
|
464
|
+
@listeners[listener_name] ||= []
|
465
|
+
@tk.bind(listener_name) { |event| @listeners[listener_name].each {|l| l.call(event)} } if @listeners[listener_name].empty?
|
466
|
+
@listeners[listener_name] << listener
|
465
467
|
rescue => e
|
468
|
+
@listeners.delete(listener_name)
|
466
469
|
Glimmer::Config.logger.debug {"Unable to bind to #{listener_name} .. attempting to surround with <>"}
|
467
470
|
Glimmer::Config.logger.debug {e.full_message}
|
468
471
|
listener_name = "<#{listener_name}" if !listener_name.start_with?('<')
|
469
472
|
listener_name = "#{listener_name}>" if !listener_name.end_with?('>')
|
470
|
-
@
|
473
|
+
@listeners[listener_name] ||= []
|
474
|
+
@tk.bind(listener_name) { |event| @listeners[listener_name].each {|l| l.call(event)} } if @listeners[listener_name].empty?
|
475
|
+
@listeners[listener_name] << listener
|
471
476
|
end
|
472
477
|
end
|
473
478
|
end
|
@@ -501,11 +506,16 @@ module Glimmer
|
|
501
506
|
|
502
507
|
private
|
503
508
|
|
509
|
+
def build_widget
|
510
|
+
tk_widget_class = self.class.tk_widget_class_for(@keyword)
|
511
|
+
@tk = tk_widget_class.new(@parent_proxy.tk, *args)
|
512
|
+
end
|
513
|
+
|
504
514
|
def initialize_defaults
|
505
515
|
options = {}
|
506
516
|
options[:sticky] = 'nsew'
|
507
517
|
options[:column_weight] = 1 if @parent_proxy.children.count == 1
|
508
|
-
grid(options) unless @tk.is_a?(::Tk::Toplevel)
|
518
|
+
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
|
509
519
|
end
|
510
520
|
end
|
511
521
|
end
|
data/lib/glimmer-dsl-tk.rb
CHANGED
@@ -29,6 +29,9 @@ require 'puts_debuggerer' if ENV['pd'].to_s.downcase == 'true'
|
|
29
29
|
require 'tk'
|
30
30
|
require 'os'
|
31
31
|
require 'facets/hash/symbolize_keys'
|
32
|
+
require 'facets/string/underscore'
|
33
|
+
require 'facets/string/camelcase'
|
34
|
+
require 'delegate'
|
32
35
|
|
33
36
|
# Internal requires
|
34
37
|
# require 'ext/glimmer/config'
|
@@ -40,3 +43,4 @@ Glimmer::Config.excluded_keyword_checkers << lambda do |method_symbol, *args|
|
|
40
43
|
result = false
|
41
44
|
result ||= method == 'load_iseq'
|
42
45
|
end
|
46
|
+
::TkOption.add '*tearOff', 0
|
@@ -56,7 +56,7 @@ class MetaSample
|
|
56
56
|
|
57
57
|
def run_sample(sample)
|
58
58
|
Thread.new do
|
59
|
-
command = "ruby -r #{glimmer_dsl_tk_file} #{sample} 2>&1"
|
59
|
+
command = "#{RbConfig.ruby} -r #{glimmer_dsl_tk_file} #{sample} 2>&1"
|
60
60
|
result = ''
|
61
61
|
IO.popen(command) do |f|
|
62
62
|
f.each_line do |line|
|
@@ -0,0 +1,183 @@
|
|
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-dsl-tk'
|
23
|
+
|
24
|
+
include Glimmer
|
25
|
+
|
26
|
+
COLORS = [:white, :red, :yellow, :green, :blue, :magenta, :gray, :black]
|
27
|
+
|
28
|
+
Tk::Tile::Style.theme_use "classic" # this enables setting background on label just for demo purposes
|
29
|
+
|
30
|
+
root { |r|
|
31
|
+
title 'Hello, Menu Bar!'
|
32
|
+
|
33
|
+
@label = label {
|
34
|
+
grid row_weight: 1, column_weight: 1
|
35
|
+
text 'Check Out The Menu Bar Above!'
|
36
|
+
font size: 50
|
37
|
+
anchor 'center'
|
38
|
+
}
|
39
|
+
|
40
|
+
menu {
|
41
|
+
menu(label: 'File', underline: 0) {
|
42
|
+
menu_item(label: 'New', underline: 0) {
|
43
|
+
accelerator 'Command+N'
|
44
|
+
|
45
|
+
on('command') do
|
46
|
+
message_box(parent: r, title: 'New', message: 'New file created.')
|
47
|
+
end
|
48
|
+
}
|
49
|
+
menu_item(label: 'Open...', underline: 0) {
|
50
|
+
accelerator 'Command+O'
|
51
|
+
|
52
|
+
on('command') do
|
53
|
+
message_box(parent: r, title: 'Open', message: 'Opening File...')
|
54
|
+
end
|
55
|
+
}
|
56
|
+
menu(label: 'Open Recent', underline: 5) {
|
57
|
+
menu_item(label: 'File 1') {
|
58
|
+
on('command') do
|
59
|
+
message_box(parent: r, title: 'File 1', message: 'File 1 Contents')
|
60
|
+
end
|
61
|
+
}
|
62
|
+
menu_item(label: 'File 2') {
|
63
|
+
on('command') do
|
64
|
+
message_box(parent: r, title: 'File 2', message: 'File 2 Contents')
|
65
|
+
end
|
66
|
+
}
|
67
|
+
}
|
68
|
+
menu_item(:separator)
|
69
|
+
menu_item(label: 'Exit', underline: 1) {
|
70
|
+
on('command') do
|
71
|
+
exit(0)
|
72
|
+
end
|
73
|
+
}
|
74
|
+
}
|
75
|
+
menu(label: 'Edit', underline: 0) {
|
76
|
+
menu_item(label: 'Cut', underline: 2) {
|
77
|
+
accelerator 'Command+X'
|
78
|
+
}
|
79
|
+
menu_item(label: 'Copy', underline: 0) {
|
80
|
+
accelerator 'Command+C'
|
81
|
+
}
|
82
|
+
menu_item(label: 'Paste', underline: 0) {
|
83
|
+
accelerator 'Command+V'
|
84
|
+
}
|
85
|
+
}
|
86
|
+
menu(label: 'Options', underline: 0) {
|
87
|
+
menu_item(:checkbutton, label: 'Enabled', underline: 0) {
|
88
|
+
on('command') do
|
89
|
+
@select_one_menu.children.each { |menu_item| menu_item.state = menu_item.state == 'disabled' ? 'normal' : 'disabled' }
|
90
|
+
@select_multiple_menu.children.each { |menu_item| menu_item.state = menu_item.state == 'disabled' ? 'normal' : 'disabled' }
|
91
|
+
end
|
92
|
+
}
|
93
|
+
@select_one_menu = menu(label: 'Select One', underline: 0) {
|
94
|
+
menu_item(:radiobutton, label: 'Option 1') {
|
95
|
+
state 'disabled'
|
96
|
+
}
|
97
|
+
menu_item(:radiobutton, label: 'Option 2') {
|
98
|
+
state 'disabled'
|
99
|
+
}
|
100
|
+
menu_item(:radiobutton, label: 'Option 3') {
|
101
|
+
state 'disabled'
|
102
|
+
}
|
103
|
+
}
|
104
|
+
@select_multiple_menu = menu(label: 'Select Multiple', underline: 0) {
|
105
|
+
menu_item(:checkbutton, label: 'Option 4') {
|
106
|
+
state 'disabled'
|
107
|
+
}
|
108
|
+
menu_item(:checkbutton, label: 'Option 5') {
|
109
|
+
state 'disabled'
|
110
|
+
}
|
111
|
+
menu_item(:checkbutton, label: 'Option 6') {
|
112
|
+
state 'disabled'
|
113
|
+
}
|
114
|
+
}
|
115
|
+
}
|
116
|
+
menu(label: 'Format', underline: 0) {
|
117
|
+
menu(label: 'Background Color', underline: 0) {
|
118
|
+
COLORS.each { |color_style|
|
119
|
+
menu_item(:radiobutton, label: color_style.to_s.split('_').map(&:capitalize).join(' ')) {
|
120
|
+
on('command') do
|
121
|
+
@label.background = color_style
|
122
|
+
end
|
123
|
+
}
|
124
|
+
}
|
125
|
+
}
|
126
|
+
menu(label: 'Foreground Color', underline: 11) {
|
127
|
+
COLORS.each { |color_style|
|
128
|
+
menu_item(:radiobutton, label: color_style.to_s.split('_').map(&:capitalize).join(' ')) {
|
129
|
+
on('command') do
|
130
|
+
@label.foreground = color_style
|
131
|
+
end
|
132
|
+
}
|
133
|
+
}
|
134
|
+
}
|
135
|
+
}
|
136
|
+
menu(label: 'View', underline: 0) {
|
137
|
+
menu_item(:radiobutton, label: 'Small', underline: 0) {
|
138
|
+
accelerator 'Command+S'
|
139
|
+
|
140
|
+
on('command') do
|
141
|
+
@label.font = {size: 25}
|
142
|
+
end
|
143
|
+
}
|
144
|
+
menu_item(:radiobutton, label: 'Medium', underline: 0) {
|
145
|
+
accelerator 'Command+M'
|
146
|
+
selection true
|
147
|
+
|
148
|
+
on('command') do
|
149
|
+
@label.font = {size: 50}
|
150
|
+
end
|
151
|
+
}
|
152
|
+
menu_item(:radiobutton, label: 'Large', underline: 0) {
|
153
|
+
accelerator 'Command+L'
|
154
|
+
|
155
|
+
on('command') do
|
156
|
+
@label.font = {size: 75}
|
157
|
+
end
|
158
|
+
}
|
159
|
+
}
|
160
|
+
menu(label: 'Help', underline: 0) {
|
161
|
+
menu_item(label: 'Manual', underline: 0) {
|
162
|
+
accelerator 'Command+Shift+M'
|
163
|
+
|
164
|
+
on('command') do
|
165
|
+
message_box(parent: r, title: 'Manual', message: 'Manual Contents')
|
166
|
+
end
|
167
|
+
}
|
168
|
+
menu_item(label: 'Tutorial', underline: 0) {
|
169
|
+
accelerator 'Command+Shift+T'
|
170
|
+
|
171
|
+
on('command') do
|
172
|
+
message_box(parent: r, title: 'Tutorial', message: 'Tutorial Contents')
|
173
|
+
end
|
174
|
+
}
|
175
|
+
menu_item(:separator)
|
176
|
+
menu_item(label: 'Report an Issue...', underline: 0) {
|
177
|
+
on('command') do
|
178
|
+
message_box(parent: r, title: 'Report an Issue', message: 'Reporting an issue...')
|
179
|
+
end
|
180
|
+
}
|
181
|
+
}
|
182
|
+
}
|
183
|
+
}.open
|