sweet 0.0.2 → 0.0.3
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.
- 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
|