sweet 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +9 -5
- data/bin/sweet +3 -1
- data/examples/fox/hello_world.rb +12 -0
- data/examples/gtk2/hello_world.rb +14 -0
- data/examples/swing/hello_world.rb +12 -0
- data/examples/swt/components.rb +112 -0
- data/examples/swt/components/buttons.rb +35 -0
- data/examples/{hello_world.rb → swt/hello_world.rb} +2 -2
- data/examples/{snippet108.rb → swt/snippet108.rb} +2 -2
- data/examples/{snippet128.rb → swt/snippet128.rb} +7 -8
- data/examples/{snippet169.rb → swt/snippet169.rb} +2 -2
- data/examples/{snippet82.rb → swt/snippet82.rb} +2 -2
- data/examples/wx/hello_world.rb +12 -0
- data/lib/sweet.rb +22 -1
- data/lib/sweet/base.rb +151 -177
- data/lib/sweet/fox.rb +47 -0
- data/lib/sweet/fox/application.rb +9 -0
- data/lib/sweet/fox/object.rb +19 -0
- data/lib/sweet/gtk2.rb +56 -0
- data/lib/sweet/gtk2/application.rb +11 -0
- data/lib/sweet/gtk2/widget.rb +5 -0
- data/lib/sweet/hacks.rb +0 -17
- data/lib/sweet/swing.rb +97 -0
- data/lib/sweet/swing/application.rb +9 -0
- data/lib/sweet/swing/component.rb +23 -0
- data/lib/sweet/swing/frame.rb +13 -0
- data/lib/sweet/swt.rb +193 -0
- data/lib/sweet/swt/application.rb +17 -0
- data/lib/sweet/{composite.rb → swt/composite.rb} +5 -2
- data/lib/sweet/{dialog.rb → swt/dialog.rb} +0 -0
- data/lib/sweet/{downloader.rb → swt/downloader.rb} +1 -1
- data/lib/sweet/swt/shell.rb +22 -0
- data/lib/sweet/swt/src.zip +0 -0
- data/lib/sweet/swt/swt.jar +0 -0
- data/lib/sweet/swt/widget.rb +89 -0
- data/lib/sweet/wx.rb +57 -0
- data/lib/sweet/wx/application.rb +9 -0
- data/lib/sweet/wx/object.rb +19 -0
- data/spec/api_spec.rb +15 -0
- data/spec/downloader_spec.rb +28 -26
- data/spec/fox_spec.rb +15 -0
- data/spec/gtk2_spec.rb +15 -0
- data/spec/sweet_spec.rb +15 -0
- data/spec/swing_spec.rb +15 -0
- data/spec/swt_spec.rb +15 -0
- data/spec/wx_spec.rb +15 -0
- metadata +44 -17
- data/lib/sweet/shell.rb +0 -53
- data/lib/sweet/widget.rb +0 -151
data/README.rdoc
CHANGED
@@ -1,9 +1,13 @@
|
|
1
|
-
== Sweet: The
|
1
|
+
== Sweet: The GUI wrapper for Ruby/JRuby
|
2
2
|
|
3
3
|
Sweet is designed to provide the most readable and straight-forward DSL for the
|
4
|
-
|
5
|
-
this claim, please open a bug report or fix it on a fork if you
|
6
|
-
adventurous.
|
4
|
+
most common GUI toolkits while maintaining all of their flexibility. If you find
|
5
|
+
any limits to this claim, please open a bug report or fix it on a fork if you
|
6
|
+
feel adventurous.
|
7
|
+
|
8
|
+
The primary and most complete toolkit right now is SWT, which requires JRuby.
|
9
|
+
Swing, wxRuby and Gnome2 exist with rudimentary support so far. Cocoa is in
|
10
|
+
the making. Pull requests for new widgets and toolkits are always welcome.
|
7
11
|
|
8
12
|
=== Install
|
9
13
|
|
@@ -11,7 +15,7 @@ Sweet is available as a gem:
|
|
11
15
|
|
12
16
|
sudo gem install sweet
|
13
17
|
|
14
|
-
To install the necessary SWT for your platform
|
18
|
+
To install the necessary SWT libraries for your platform, run:
|
15
19
|
|
16
20
|
sudo sweet install
|
17
21
|
|
data/bin/sweet
CHANGED
@@ -0,0 +1,12 @@
|
|
1
|
+
$: << File.join(File.dirname(__FILE__), '../../lib')
|
2
|
+
require 'sweet/fox'
|
3
|
+
Sweet.set_debug
|
4
|
+
|
5
|
+
Sweet.app 'My first Application', :layout => :flow do
|
6
|
+
label 'Your name:'
|
7
|
+
@name = edit_line :columns => 10
|
8
|
+
|
9
|
+
button 'Push me' do
|
10
|
+
puts "Hello, #{@name.text}!"
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
$: << File.join(File.dirname(__FILE__), '../../lib')
|
2
|
+
require 'sweet/gtk2'
|
3
|
+
Sweet.set_debug
|
4
|
+
|
5
|
+
Sweet.app 'My first Application', :border_width => 10 do
|
6
|
+
flow do
|
7
|
+
label 'Your name:'
|
8
|
+
@name = edit_line :columns => 10
|
9
|
+
|
10
|
+
button 'Push me' do
|
11
|
+
puts "Hello, #{@name.text}!"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
$: << File.join(File.dirname(__FILE__), '../../lib')
|
2
|
+
require 'sweet/swing'
|
3
|
+
Sweet.set_debug
|
4
|
+
|
5
|
+
Sweet.app 'My first Application', :layout => :flow do
|
6
|
+
label 'Your name:'
|
7
|
+
@name = edit_line :columns => 10
|
8
|
+
|
9
|
+
button 'Push me' do
|
10
|
+
puts "Hello, #{@name.text}!"
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
$: << File.join(File.dirname(__FILE__), '../../lib')
|
2
|
+
require 'sweet/swt'
|
3
|
+
|
4
|
+
class Tab < Sweet::VarContainer
|
5
|
+
def self.title(title = nil)
|
6
|
+
title ? instances[@title = title] = self : @title
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.instances
|
10
|
+
@instances ||= {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def create
|
14
|
+
@style = 0
|
15
|
+
group :layout => :grid, :grid_data => {:align => [:fill, :fill], :grab => [true, true]} do
|
16
|
+
create_example_group
|
17
|
+
end
|
18
|
+
group 'Parameters', :layout => :grid, :grid_data => {:align => [:fill, :fill]} do
|
19
|
+
create_parameter_group
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def create_example_group
|
24
|
+
example_layout =
|
25
|
+
@example_group = group('Examples', example_layout) do
|
26
|
+
example_groups if respond_to? :example_group
|
27
|
+
end
|
28
|
+
@parameters_group =
|
29
|
+
create_example_widgets
|
30
|
+
end
|
31
|
+
|
32
|
+
def create_example_widgets
|
33
|
+
if widgets = @example_widgets
|
34
|
+
widgets.each { |widget| widget.dispose }
|
35
|
+
end
|
36
|
+
@example_widgets = example_widgets.flatten
|
37
|
+
# TODO hook listener and set state
|
38
|
+
@example_widgets.each do |widget|
|
39
|
+
widget.alignment = widget_alignment
|
40
|
+
widget.visible = true
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def create_style_group
|
45
|
+
listener = proc { |event|
|
46
|
+
if event.widget.selection
|
47
|
+
self.widget_style = event.widget
|
48
|
+
create_example_widgets
|
49
|
+
end
|
50
|
+
}
|
51
|
+
|
52
|
+
@style_group = group('Styles', :grid_data => {:align => [:fill, :fill]}) do
|
53
|
+
%w{PUSH CHECK RADIO TOGGLE}.each{ |type| radio_button type, &listener}
|
54
|
+
%w{FLAT BORDER}.each{ |type| check_button type, &listener}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def widget_style
|
59
|
+
@style ||= 0
|
60
|
+
end
|
61
|
+
def widget_style=(value)
|
62
|
+
@style = value
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class AlignableTab < Tab
|
67
|
+
def create_alignment_group
|
68
|
+
listener = proc { |event|
|
69
|
+
if event.widget.selection
|
70
|
+
self.widget_alignment = event.widget
|
71
|
+
create_example_widgets
|
72
|
+
end
|
73
|
+
}
|
74
|
+
|
75
|
+
group = @parent.append{ group('Alignment', :layout => :grid, :grid_data => {:align => [:fill, :fill]}) }
|
76
|
+
group.append do
|
77
|
+
radio_button 'Left', :selection => true, &listener
|
78
|
+
radio_button 'Center', &listener
|
79
|
+
radio_button 'Right', &listener
|
80
|
+
end
|
81
|
+
|
82
|
+
alignment_group(group, listener) if respond_to? :alignment_group
|
83
|
+
end
|
84
|
+
|
85
|
+
def widget_alignment
|
86
|
+
@widget_alignment ||= 0
|
87
|
+
end
|
88
|
+
def widget_alignment=(value)
|
89
|
+
@widget_alignment |= value.text.to_sym.swt_const
|
90
|
+
end
|
91
|
+
def widget_style
|
92
|
+
super | widget_alignment
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
Dir.glob(File.join(File.dirname(__FILE__), 'components/*.rb')).each{|file| load file}
|
97
|
+
|
98
|
+
|
99
|
+
Sweet.app 'Component DEMO', :layout => :fill do
|
100
|
+
|
101
|
+
tab_folder do
|
102
|
+
tab_item do
|
103
|
+
buttons = Buttons.new(:layout => :grid.conf(:numColumns => 3), :style => swt::CLOSE)
|
104
|
+
|
105
|
+
buttons.create_alignment_group
|
106
|
+
buttons.create_example_group
|
107
|
+
buttons.create_style_group
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class Buttons < AlignableTab
|
2
|
+
title "Buttons"
|
3
|
+
|
4
|
+
def alignment_group(listener)
|
5
|
+
radio_button 'Up', &listener
|
6
|
+
radio_button 'Down', &listener
|
7
|
+
end
|
8
|
+
|
9
|
+
def example_group
|
10
|
+
# TODO densify with :grid_data => [:fill_both, :grab_both] or alike
|
11
|
+
group_layout = {:layout => :grid.conf(:numColumns => 3), :grid_data => {:align => [:fill, :fill], :grab => [true, true]}}
|
12
|
+
|
13
|
+
@text_buttons = group('Text Buttons', group_layout)
|
14
|
+
@image_buttons = group('Image Buttons', group_layout)
|
15
|
+
@image_text_buttons = group('Image Text Buttons', group_layout)
|
16
|
+
end
|
17
|
+
|
18
|
+
def example_widgets(style)
|
19
|
+
[ #returns all created widgets
|
20
|
+
@text_buttons.append do
|
21
|
+
%w{One Two Three}.map { |caption| button caption, :style => style }
|
22
|
+
end,
|
23
|
+
@image_buttons.append do
|
24
|
+
# TODO images
|
25
|
+
%w{One Two Three}.map { |type| button type, :style => style }
|
26
|
+
end,
|
27
|
+
@image_text_buttons.append do
|
28
|
+
# TODO images
|
29
|
+
%w{One Two Three}.map { |type| button type, :style => style }
|
30
|
+
end
|
31
|
+
].map(&:children)
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
@@ -1,5 +1,5 @@
|
|
1
|
-
$: << File.join(__FILE__, '
|
2
|
-
require 'sweet'
|
1
|
+
$: << File.join(File.dirname(__FILE__), '../../lib')
|
2
|
+
require 'sweet/swt'
|
3
3
|
|
4
4
|
# Display display = new Display();
|
5
5
|
# final Shell shell = new Shell(display);
|
@@ -36,6 +36,11 @@ Sweet.app :layout => :grid.conf(:numColumns => 3) do
|
|
36
36
|
# else if (string.equals("Go")) browser.setUrl(location.getText());
|
37
37
|
# }
|
38
38
|
# };
|
39
|
+
# itemBack.addListener(SWT.Selection, listener);
|
40
|
+
# itemForward.addListener(SWT.Selection, listener);
|
41
|
+
# itemStop.addListener(SWT.Selection, listener);
|
42
|
+
# itemRefresh.addListener(SWT.Selection, listener);
|
43
|
+
# itemGo.addListener(SWT.Selection, listener);
|
39
44
|
%w{Back Forward Stop Refresh}.each do |caption|
|
40
45
|
tool_item(caption) { @browser.send(caption.downcase) }
|
41
46
|
end
|
@@ -140,12 +145,6 @@ Sweet.app :layout => :grid.conf(:numColumns => 3) do
|
|
140
145
|
@location.text = event.location if event.top
|
141
146
|
end
|
142
147
|
|
143
|
-
# itemBack.addListener(SWT.Selection, listener);
|
144
|
-
# itemForward.addListener(SWT.Selection, listener);
|
145
|
-
# itemStop.addListener(SWT.Selection, listener);
|
146
|
-
# itemRefresh.addListener(SWT.Selection, listener);
|
147
|
-
# itemGo.addListener(SWT.Selection, listener);
|
148
|
-
#
|
149
148
|
# shell.open();
|
150
149
|
# browser.setUrl("http://eclipse.org");
|
151
150
|
@browser.setUrl "http://eclipse.org"
|
@@ -0,0 +1,12 @@
|
|
1
|
+
$: << File.join(File.dirname(__FILE__), '../../lib')
|
2
|
+
require 'sweet/wxruby'
|
3
|
+
Sweet.set_debug
|
4
|
+
|
5
|
+
Sweet.app 'My first Application', :layout => :flow do
|
6
|
+
label 'Your name:'
|
7
|
+
@name = edit_line :columns => 10
|
8
|
+
|
9
|
+
button 'Push me' do
|
10
|
+
puts "Hello, #{@name.text}!"
|
11
|
+
end
|
12
|
+
end
|
data/lib/sweet.rb
CHANGED
@@ -1 +1,22 @@
|
|
1
|
-
|
1
|
+
case RUBY_PLATFORM
|
2
|
+
when /java/
|
3
|
+
begin
|
4
|
+
require 'sweet/swt'
|
5
|
+
rescue
|
6
|
+
require 'sweet/swing'
|
7
|
+
end
|
8
|
+
when /linux/
|
9
|
+
begin
|
10
|
+
require 'sweet/gtk2'
|
11
|
+
rescue
|
12
|
+
begin
|
13
|
+
require 'sweet/fox'
|
14
|
+
rescue
|
15
|
+
require 'sweet/wx'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
when /win/i
|
19
|
+
require 'sweet/fox'
|
20
|
+
else # MacOS
|
21
|
+
require 'sweet/fox'
|
22
|
+
end
|
data/lib/sweet/base.rb
CHANGED
@@ -1,95 +1,7 @@
|
|
1
|
-
require 'sweet/
|
2
|
-
|
3
|
-
begin
|
4
|
-
load Sweet::SWT_JAR
|
5
|
-
rescue LoadError => e
|
6
|
-
STDERR.puts "No swt.jar found. Run:"
|
7
|
-
STDERR.puts " sweet install [toolkit]"
|
8
|
-
exit
|
9
|
-
end
|
10
|
-
|
11
|
-
require 'java'
|
12
|
-
%w{hacks widget composite shell dialog}.each{|file| require "sweet/#{file}"}
|
1
|
+
require 'sweet/hacks'
|
13
2
|
|
14
|
-
# TODO create gemspec
|
15
|
-
# TODO evaluate integration of jface
|
16
|
-
# TODO enable additive and replacing :style on widget creation
|
17
3
|
module Sweet
|
18
4
|
|
19
|
-
import 'org.eclipse.swt'
|
20
|
-
import 'org.eclipse.swt.widgets'
|
21
|
-
import 'org.eclipse.swt.custom'
|
22
|
-
import 'org.eclipse.swt.browser'
|
23
|
-
|
24
|
-
# TODO integrate all standard widgets
|
25
|
-
WIDGET_HACKS = {
|
26
|
-
ProgressBar => {:custom_code => proc {
|
27
|
-
def fraction=(value)
|
28
|
-
setSelection value.to_f * maximum
|
29
|
-
end
|
30
|
-
def fraction
|
31
|
-
selection / maximum
|
32
|
-
end
|
33
|
-
}},
|
34
|
-
Text => {:custom_code => proc{
|
35
|
-
def password=(value)
|
36
|
-
self.echo_char = ?* if value
|
37
|
-
end
|
38
|
-
def password
|
39
|
-
! echo_char.nil?
|
40
|
-
end
|
41
|
-
}},
|
42
|
-
CTabFolder => {:default_listener => 'CTabFolder2'}
|
43
|
-
}
|
44
|
-
|
45
|
-
DEFAULT = {:init_args => :text, :block_handler => SWT::Selection}
|
46
|
-
WIDGET_OPTIONS = {
|
47
|
-
# Text components
|
48
|
-
:label => {:style => SWT::WRAP, :init_args => :text},
|
49
|
-
:edit_line => {:class => Text, :style => SWT::SINGLE | SWT::BORDER, :init_args => :text, :block_handler => :on_widget_default_selected},
|
50
|
-
:edit_area => {:class => Text, :style => SWT::MULTI | SWT::BORDER, :init_args => :text, :block_handler => :on_widget_default_selected},
|
51
|
-
|
52
|
-
# Buttons
|
53
|
-
:button => DEFAULT.merge(:style => SWT::PUSH),
|
54
|
-
:toggle_button => DEFAULT.merge(:class => Button, :style => SWT::TOGGLE),
|
55
|
-
:radio_button => DEFAULT.merge(:class => Button, :style => SWT::RADIO),
|
56
|
-
:check_button => DEFAULT.merge(:class => Button, :style => SWT::CHECK),
|
57
|
-
:arrow_button => DEFAULT.merge(:class => Button, :style => SWT::ARROW),
|
58
|
-
|
59
|
-
# Menus
|
60
|
-
:menubar => {:class => Menu, :style => SWT::BAR},
|
61
|
-
:popup => {:class => Menu, :style => SWT::POP_UP},
|
62
|
-
:item => {:class => MenuItem, :style => SWT::PUSH, :init_args => :text, :block_handler => :on_widget_selected},
|
63
|
-
:separator => {:class => MenuItem, :style => SWT::SEPARATOR},
|
64
|
-
:submenu => {:class => MenuItem, :style => SWT::CASCADE, :init_args => :text, :block_handler => proc{|c, opts, block|
|
65
|
-
sub = Menu.new(c.app, SWT::DROP_DOWN)
|
66
|
-
sub.sweeten(c.app, opts, &block)
|
67
|
-
c.menu = sub
|
68
|
-
}
|
69
|
-
},
|
70
|
-
|
71
|
-
# Toolbars
|
72
|
-
:tool_item => DEFAULT.merge(:style => SWT::PUSH),
|
73
|
-
# TODO make CoolBar work
|
74
|
-
# :cool_item => {:init_args => :control, :block_handler => proc{ |c, opts, proc|
|
75
|
-
# c.control = proc.call
|
76
|
-
# }},
|
77
|
-
|
78
|
-
# Tabs
|
79
|
-
:tab_folder => {:class => CTabFolder, :style => SWT::BORDER},
|
80
|
-
:tab_item => {:class => CTabItem, :style => SWT::CLOSE, :init_args => [:text, :control], :block_handler => proc{|c, opts, proc|
|
81
|
-
c.control = proc.call
|
82
|
-
}},
|
83
|
-
|
84
|
-
# Other components
|
85
|
-
:tree => {:style => SWT::VIRTUAL, :block_handler => SWT::Selection},
|
86
|
-
:progress => {:class => ProgressBar, :style => SWT::SMOOTH},
|
87
|
-
:group => {:init_args => :text},
|
88
|
-
|
89
|
-
# Dialogs
|
90
|
-
:color_dialog => {:style => SWT::APPLICATION_MODAL, :init_args => :text}
|
91
|
-
}.unify
|
92
|
-
|
93
5
|
WIDGETS = {}
|
94
6
|
|
95
7
|
def self.app(*args, &block)
|
@@ -99,25 +11,11 @@ module Sweet
|
|
99
11
|
raise ArgumentError.new(count, 2) if args.size > 1
|
100
12
|
opts = args.first || {}
|
101
13
|
|
102
|
-
|
103
|
-
Display.setAppName(name)
|
104
|
-
display = Display.new
|
105
|
-
|
106
|
-
# TODO allow multiple shells
|
107
|
-
shell = Shell.new(display)
|
108
|
-
shell.sweeten(display, name, opts, &block)
|
109
|
-
shell.open
|
110
|
-
|
111
|
-
# dispatch loop
|
112
|
-
while (!shell.isDisposed) do
|
113
|
-
display.sleep unless display.readAndDispatch
|
114
|
-
end
|
115
|
-
|
116
|
-
display.dispose
|
14
|
+
create_app(name, opts, &block)
|
117
15
|
end
|
118
16
|
|
119
17
|
def self.create_widget(parent, name, *args, &block)
|
120
|
-
init =
|
18
|
+
init = WIDGET_DEFAULTS[name] || {}
|
121
19
|
|
122
20
|
unless cls = WIDGETS[name]
|
123
21
|
# puts "Loading widget #{name}"
|
@@ -125,99 +23,175 @@ module Sweet
|
|
125
23
|
# begin
|
126
24
|
# require filename
|
127
25
|
# rescue LoadError
|
128
|
-
|
26
|
+
class_name = init[:class] || name
|
27
|
+
class_name = class_name.to_s.camelize(:upper) if class_name.is_a?(Symbol)
|
28
|
+
cls = create_widget_class(class_name, init)
|
29
|
+
|
30
|
+
unless WIDGETS.values.member? cls
|
31
|
+
delegate_events(cls)
|
32
|
+
if hacks = WIDGET_HACKS[cls]
|
33
|
+
if custom_code = hacks[:custom_code]
|
34
|
+
cls.class_eval &custom_code
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
129
39
|
# end
|
130
40
|
end
|
131
41
|
|
132
42
|
opts = args.last.is_a?(Hash) ? args.pop.dup : {}
|
133
|
-
|
134
|
-
style = opts.delete(:style) || init[:style] || SWT::NONE
|
135
|
-
style = Array(style).inject(0) do |result, value|
|
136
|
-
value = value.swt_const if value.is_a? Symbol
|
137
|
-
result | value
|
138
|
-
end
|
139
|
-
|
140
|
-
Sweet.debug "Creating #{cls.java_class.simple_name}(#{parent}, #{style})"
|
141
|
-
result = cls.new(parent, style)
|
142
|
-
result.instance_variable_set(:@block_handler, init[:block_handler])
|
143
|
-
|
144
43
|
if init_args = init[:init_args]
|
145
|
-
|
146
|
-
opts.merge!
|
44
|
+
Array(init_args).zip(args).each do |(k, v)|
|
45
|
+
opts.merge!(k => v) if v
|
147
46
|
end
|
148
47
|
end
|
149
|
-
result.sweeten(parent.app, opts, &block)
|
150
48
|
|
151
|
-
|
49
|
+
widget = initialize_widget(parent, cls, opts, init)
|
50
|
+
widget.instance_variable_set(:@block_handler, init[:block_handler])
|
51
|
+
|
52
|
+
widget.sweeten(parent.app, opts, &block)
|
53
|
+
|
54
|
+
widget
|
55
|
+
end
|
56
|
+
|
57
|
+
@debug = ARGV.delete('--debug')
|
58
|
+
def self.set_debug(value = true)
|
59
|
+
@debug = value
|
60
|
+
end
|
61
|
+
def self.debug(text)
|
62
|
+
# TODO add a decent logging mechanism
|
63
|
+
puts text if @debug
|
152
64
|
end
|
153
65
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
66
|
+
module Component
|
67
|
+
|
68
|
+
def self.included(cls)
|
69
|
+
puts "extending #{cls}"
|
70
|
+
cls.extend(ClassMethods)
|
71
|
+
end
|
72
|
+
|
73
|
+
attr_reader :app
|
74
|
+
|
75
|
+
def sweeten(app, opts, &block)
|
76
|
+
@app = app
|
77
|
+
self.options = opts
|
78
|
+
|
79
|
+
if block
|
80
|
+
case handler = @block_handler
|
81
|
+
when nil
|
82
|
+
handle_container &block
|
83
|
+
when Symbol
|
84
|
+
handle_event handler, &block
|
85
|
+
when Proc
|
86
|
+
instance_eval do
|
87
|
+
handler.call self, opts, block
|
88
|
+
end
|
89
|
+
else
|
90
|
+
raise("Invalid :block_handler ",handler) unless sweet_block_handler(handler, &block)
|
165
91
|
end
|
166
92
|
end
|
93
|
+
|
94
|
+
puts "Unknown properties for class #{self.class}: #{opts.keys.inspect}" unless opts.empty?
|
167
95
|
end
|
96
|
+
alias handle_event send
|
168
97
|
|
169
|
-
|
170
|
-
|
98
|
+
def app(&block)
|
99
|
+
return @app unless block
|
100
|
+
@app.var_containers.last.instance_eval(&block)
|
101
|
+
end
|
171
102
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
103
|
+
def append(&block)
|
104
|
+
raise "Append called without block" unless block
|
105
|
+
handle_container &block
|
106
|
+
end
|
107
|
+
|
108
|
+
def meta(&block)
|
109
|
+
@meta ||= class << self
|
110
|
+
self
|
111
|
+
end
|
112
|
+
block ? @meta.class_eval(&block) : @meta
|
113
|
+
end
|
114
|
+
|
115
|
+
def options=(opts)
|
116
|
+
# TODO reader method
|
117
|
+
opts.each_pair do |k, v|
|
118
|
+
case k
|
119
|
+
when :hidden
|
120
|
+
setVisible !v
|
121
|
+
else
|
122
|
+
name = k.to_s + "="
|
123
|
+
next unless respond_to? name
|
124
|
+
if v
|
125
|
+
classname = respond_to?(:java_class) ? self.java_class.simple_name : self.class.name
|
126
|
+
Sweet.debug "#{classname}.#{k} = #{v.inspect}"
|
127
|
+
send name, *v
|
192
128
|
end
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
129
|
+
opts.delete(k)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def perform(&block)
|
135
|
+
app.perform &block
|
136
|
+
end
|
137
|
+
|
138
|
+
def method_missing(name, *args, &block)
|
139
|
+
app.send(name, *args, &block) rescue super
|
140
|
+
end
|
141
|
+
|
142
|
+
private
|
143
|
+
def handle_container(&block)
|
144
|
+
app.sweet_containers.push self
|
145
|
+
app &block
|
146
|
+
ensure
|
147
|
+
app.sweet_containers.pop
|
148
|
+
end
|
149
|
+
|
150
|
+
module ClassMethods
|
151
|
+
def alias_property(aliases = {})
|
152
|
+
aliases.each do |from, to|
|
153
|
+
alias_method from, to unless respond_to? to
|
154
|
+
alias_method "#{from}=", "#{to}=" unless respond_to? "#{to}="
|
210
155
|
end
|
211
156
|
end
|
212
157
|
end
|
158
|
+
|
213
159
|
end
|
214
160
|
|
215
|
-
|
216
|
-
|
161
|
+
module Application
|
162
|
+
def initialize_app(root)
|
163
|
+
sweet_containers << root
|
164
|
+
var_containers << root
|
165
|
+
end
|
166
|
+
def sweet_containers
|
167
|
+
@sweet_containers ||= []
|
168
|
+
end
|
169
|
+
def var_containers
|
170
|
+
@var_containers ||= []
|
171
|
+
end
|
172
|
+
# TODO find suitable exception
|
173
|
+
def perform(&block)
|
174
|
+
raise 'perform is not implemented'
|
175
|
+
end
|
176
|
+
|
177
|
+
def busy(&block)
|
178
|
+
raise 'busy is not implemented'
|
179
|
+
end
|
180
|
+
def method_missing(name, *args, &block)
|
181
|
+
Sweet.create_widget(sweet_containers.last, name, *args, &block) || super
|
182
|
+
end
|
217
183
|
end
|
218
|
-
|
219
|
-
|
220
|
-
|
184
|
+
|
185
|
+
class VarContainer
|
186
|
+
include Component
|
187
|
+
|
188
|
+
private
|
189
|
+
def handle_container(&block)
|
190
|
+
app.var_containers.push self
|
191
|
+
app &block
|
192
|
+
ensure
|
193
|
+
app.var_containers.pop
|
194
|
+
end
|
221
195
|
end
|
222
|
-
end
|
223
196
|
|
197
|
+
end
|