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/lib/sweet/fox.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'sweet/base'
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'fox16'
|
5
|
+
%w{object application}.each{|file| require "sweet/fox/#{file}"}
|
6
|
+
|
7
|
+
module Sweet
|
8
|
+
include Fox
|
9
|
+
|
10
|
+
# TODO integrate all standard widgets
|
11
|
+
WIDGET_HACKS = {
|
12
|
+
Wx::TextCtrl => {:custom_code => proc{ alias text get_value; alias text= set_value} }
|
13
|
+
}
|
14
|
+
|
15
|
+
default = {:init_args => :text}
|
16
|
+
WIDGET_DEFAULTS = {
|
17
|
+
:label => default,
|
18
|
+
:edit_line => default.merge(:class => TextCtrl),
|
19
|
+
:button => default.merge(:block_handler => :evt_button)
|
20
|
+
}
|
21
|
+
|
22
|
+
def self.create_app(name, opts, &block)
|
23
|
+
app = FXApp.new
|
24
|
+
class << app
|
25
|
+
include Sweet::Application
|
26
|
+
end
|
27
|
+
wnd = FXWindow.new(app, name)
|
28
|
+
wnd.sweeten(app, opts, &block)
|
29
|
+
app.create
|
30
|
+
wnd.show
|
31
|
+
app.run
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.create_widget_class(cls, init)
|
35
|
+
cls.is_a?(Class) ? cls : const_get('FX' + cls)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.initialize_widget(parent, cls, opts, init)
|
39
|
+
Sweet.debug "Creating #{cls}(#{parent})"
|
40
|
+
cls.new(parent, opts.delete(:text) || '')
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.delegate_events(cls)
|
44
|
+
# nothing to do
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Fox::Object
|
2
|
+
include Sweet::Component
|
3
|
+
|
4
|
+
def handle_event(evt, &block)
|
5
|
+
send evt, get_id do |event|
|
6
|
+
block.call
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def layout=(layout)
|
11
|
+
l = case layout
|
12
|
+
when :flow
|
13
|
+
Wx::BoxSizer.new(Wx::HORIZONTAL)
|
14
|
+
when :stack
|
15
|
+
Wx::BoxSizer.new(Wx::VERTICAL)
|
16
|
+
end
|
17
|
+
set_sizer l
|
18
|
+
end
|
19
|
+
end
|
data/lib/sweet/gtk2.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'sweet/base'
|
2
|
+
|
3
|
+
require 'gtk2'
|
4
|
+
%w{widget application}.each { |f| require "sweet/gtk2/#{f}" }
|
5
|
+
|
6
|
+
|
7
|
+
module Sweet
|
8
|
+
|
9
|
+
# TODO integrate all standard widgets
|
10
|
+
|
11
|
+
WIDGET_HACKS = {
|
12
|
+
}
|
13
|
+
|
14
|
+
default = {:init_args => :label}
|
15
|
+
WIDGET_DEFAULTS = {
|
16
|
+
:edit_line => default.merge(:class => Gtk::Entry),
|
17
|
+
:label => {:init_args => :text},
|
18
|
+
:button => default.merge(:block_handler => :clicked),
|
19
|
+
:flow => {:class => Gtk::HBox},
|
20
|
+
:stack => {:class => Gtk::VBox}
|
21
|
+
}
|
22
|
+
|
23
|
+
def self.create_app(name, opts, &block)
|
24
|
+
wnd = Gtk::Window.new
|
25
|
+
class << wnd; self; end.class_eval do
|
26
|
+
include Application
|
27
|
+
end
|
28
|
+
wnd.initialize_app wnd
|
29
|
+
wnd.sweeten(wnd, {:title => name}.merge(opts), &block)
|
30
|
+
wnd.show_all
|
31
|
+
wnd.signal_connect :destroy do
|
32
|
+
Gtk.main_quit
|
33
|
+
end
|
34
|
+
Gtk.main
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.create_widget_class(class_name, init)
|
38
|
+
class_name.is_a?(Class) ? class_name : Gtk.const_get(class_name)
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.initialize_widget(parent, cls, opts, init)
|
42
|
+
Sweet.debug "Creating #{cls}(#{parent})"
|
43
|
+
widget = cls.new
|
44
|
+
if appender = init[:appender]
|
45
|
+
parent.send appender, widget
|
46
|
+
else
|
47
|
+
parent.add(widget)
|
48
|
+
end
|
49
|
+
widget
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.delegate_events(cls)
|
53
|
+
# nothing to do
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
data/lib/sweet/hacks.rb
CHANGED
@@ -1,20 +1,3 @@
|
|
1
|
-
class Hash
|
2
|
-
def unify
|
3
|
-
each_pair do |k, v|
|
4
|
-
if k.is_a? Array
|
5
|
-
k.each do |key|
|
6
|
-
self[key.to_sym] = v
|
7
|
-
end
|
8
|
-
self.delete k
|
9
|
-
elsif ! k.is_a?(Symbol)
|
10
|
-
self[k.to_sym] = v
|
11
|
-
self.delete k
|
12
|
-
end
|
13
|
-
end
|
14
|
-
self
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
1
|
class Symbol
|
19
2
|
# For configuring layouts
|
20
3
|
def conf(opts = {})
|
data/lib/sweet/swing.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'sweet/base'
|
2
|
+
|
3
|
+
require 'java'
|
4
|
+
%w{application component frame}.each { |f| require "sweet/swing/#{f}" }
|
5
|
+
|
6
|
+
|
7
|
+
module Sweet
|
8
|
+
|
9
|
+
import 'javax.swing'
|
10
|
+
|
11
|
+
# TODO integrate all standard widgets
|
12
|
+
|
13
|
+
WIDGET_HACKS = {
|
14
|
+
}
|
15
|
+
|
16
|
+
default = {:init_args => :text}
|
17
|
+
WIDGET_DEFAULTS = {
|
18
|
+
:edit_line => default.merge(:class => JTextField),
|
19
|
+
:label => default,
|
20
|
+
:button => default.merge(:block_handler => :on_action),
|
21
|
+
:menu_bar => {:appender => :setJMenuBar}
|
22
|
+
}
|
23
|
+
|
24
|
+
def self.create_app(name, opts, &block)
|
25
|
+
frame = JFrame.new(name)
|
26
|
+
# TODO factor this out
|
27
|
+
class << frame; self; end.class_eval do
|
28
|
+
include Application
|
29
|
+
end
|
30
|
+
frame.initialize_app frame
|
31
|
+
frame.default_close_operation = JFrame::EXIT_ON_CLOSE
|
32
|
+
frame.sweeten(opts, &block)
|
33
|
+
frame.visible = true
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.create_widget_class(class_name, init)
|
37
|
+
class_name.is_a?(Class) ? class_name : const_get('J' + class_name)
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.initialize_widget(parent, cls, opts, init)
|
41
|
+
Sweet.debug "Creating #{cls.java_class.simple_name}(#{parent})"
|
42
|
+
widget = cls.new()
|
43
|
+
if appender = init[:appender]
|
44
|
+
parent.send appender, widget
|
45
|
+
else
|
46
|
+
if parent.respond_to?(:content_pane)
|
47
|
+
parent.content_pane
|
48
|
+
else
|
49
|
+
parent
|
50
|
+
end.add(widget)
|
51
|
+
end
|
52
|
+
widget
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.delegate_events(cls)
|
56
|
+
hacks = WIDGET_HACKS[cls]
|
57
|
+
default_listener = (hacks[:default_listener] || 'action').underscore if hacks
|
58
|
+
|
59
|
+
methods = cls.java_class.java_instance_methods
|
60
|
+
methods.each do |m|
|
61
|
+
next unless m.name =~ /(add(.*)Listener)/
|
62
|
+
j = $1
|
63
|
+
n = $2.underscore
|
64
|
+
p = m.parameter_types[0]
|
65
|
+
evts = p.declared_instance_methods
|
66
|
+
|
67
|
+
if evts.size == 1
|
68
|
+
s = "on_#{n}"
|
69
|
+
cls.send(:alias_method, s, j) unless cls.respond_to?(s)
|
70
|
+
else
|
71
|
+
evts.each do |evt|
|
72
|
+
s = evt.name.underscore
|
73
|
+
if s.split(/_/).size == 1 && n != default_listener
|
74
|
+
s = n + "_" + s
|
75
|
+
end
|
76
|
+
unused_events = evts.select{ |e| e.name != evt.name }.map{ |e| "def #{e.name.underscore}(e)\nend" }.join("\n")
|
77
|
+
cls.class_eval <<-EOF
|
78
|
+
def on_#{s}(&block)
|
79
|
+
l = Class.new do
|
80
|
+
include Java.#{p}
|
81
|
+
def initialize(&block)
|
82
|
+
@block = block
|
83
|
+
end
|
84
|
+
def #{evt.name}(e)
|
85
|
+
@block.call e
|
86
|
+
end
|
87
|
+
#{unused_events}
|
88
|
+
end
|
89
|
+
#{j} l.new(&block)
|
90
|
+
end
|
91
|
+
EOF
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# This is the only base class of ALL Swing components
|
2
|
+
class Java::JavaAwt::Container
|
3
|
+
|
4
|
+
include Sweet::Component
|
5
|
+
|
6
|
+
def layout=(value, opts = {})
|
7
|
+
Sweet.debug "layout = #{name}(#{opts.inspect})"
|
8
|
+
layout = "#{value.to_s.capitalize}Layout" if value.is_a?(Symbol)
|
9
|
+
layout = instance_eval("awt.#{layout}") unless value.is_a?(Class)
|
10
|
+
|
11
|
+
l = layout.new
|
12
|
+
opts.each_pair do |k,v|
|
13
|
+
l.send("#{k}=", v)
|
14
|
+
end
|
15
|
+
|
16
|
+
content_pane.setLayout l
|
17
|
+
Sweet.debug content_pane.getLayout
|
18
|
+
end
|
19
|
+
|
20
|
+
def awt
|
21
|
+
java.awt
|
22
|
+
end
|
23
|
+
end
|
data/lib/sweet/swt.rb
ADDED
@@ -0,0 +1,193 @@
|
|
1
|
+
require 'sweet/swt/downloader'
|
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 'sweet/base'
|
12
|
+
|
13
|
+
require 'java'
|
14
|
+
%w{widget composite shell dialog application}.each{|file| require "sweet/swt/#{file}"}
|
15
|
+
|
16
|
+
module Sweet
|
17
|
+
|
18
|
+
import 'org.eclipse.swt'
|
19
|
+
import 'org.eclipse.swt.widgets'
|
20
|
+
import 'org.eclipse.swt.custom'
|
21
|
+
import 'org.eclipse.swt.browser'
|
22
|
+
|
23
|
+
# TODO integrate all standard widgets
|
24
|
+
WIDGET_HACKS = {
|
25
|
+
ProgressBar => {:custom_code => proc {
|
26
|
+
def fraction=(value)
|
27
|
+
setSelection value.to_f * maximum
|
28
|
+
end
|
29
|
+
def fraction
|
30
|
+
selection / maximum
|
31
|
+
end
|
32
|
+
}},
|
33
|
+
Text => {:custom_code => proc{
|
34
|
+
def password=(value)
|
35
|
+
self.echo_char = ?* if value
|
36
|
+
end
|
37
|
+
def password
|
38
|
+
! echo_char.nil?
|
39
|
+
end
|
40
|
+
}},
|
41
|
+
CTabFolder => {:default_listener => 'CTabFolder2'}
|
42
|
+
}
|
43
|
+
|
44
|
+
default = {:init_args => :text, :block_handler => SWT::Selection}
|
45
|
+
button_default = default.merge(:class => Button)
|
46
|
+
WIDGET_DEFAULTS = {
|
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 => button_default.merge(:style => SWT::TOGGLE),
|
55
|
+
:radio_button => button_default.merge(:style => SWT::RADIO),
|
56
|
+
:check_button => button_default.merge(:style => SWT::CHECK),
|
57
|
+
:arrow_button => button_default.merge(: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, block|
|
81
|
+
c.control = block.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
|
+
:modal_dialog => {:style => SWT::APPLICATION_MODAL, :init_args => :text, :block_handler => proc { |c, opts, block|
|
92
|
+
meta.class_eval do
|
93
|
+
def open
|
94
|
+
shell = widgets::Shell.new(app, swt::DIALOG_TRIM | style)
|
95
|
+
shell.text = text
|
96
|
+
shell.app &block
|
97
|
+
shell.open
|
98
|
+
while !shell.isDisposed
|
99
|
+
app.display.sleep unless display.readAndDispatch()
|
100
|
+
return result
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
}
|
105
|
+
}
|
106
|
+
}
|
107
|
+
|
108
|
+
def self.create_app(name, opts, &block)
|
109
|
+
# build the UI
|
110
|
+
Display.setAppName(name)
|
111
|
+
display = Display.new
|
112
|
+
|
113
|
+
# TODO allow multiple shells
|
114
|
+
shell = Shell.new(display)
|
115
|
+
class << shell; self; end.class_eval do
|
116
|
+
include Application
|
117
|
+
end
|
118
|
+
shell.initialize_app shell
|
119
|
+
shell.sweeten(display, name, opts, &block)
|
120
|
+
shell.open
|
121
|
+
|
122
|
+
# dispatch loop
|
123
|
+
while (!shell.isDisposed) do
|
124
|
+
display.sleep unless display.readAndDispatch
|
125
|
+
end
|
126
|
+
|
127
|
+
display.dispose
|
128
|
+
end
|
129
|
+
|
130
|
+
def self.create_widget_class(cls, init)
|
131
|
+
cls.is_a?(Class) ? cls : const_get(cls)
|
132
|
+
end
|
133
|
+
|
134
|
+
def self.initialize_widget(parent, cls, opts, init)
|
135
|
+
style = to_style(opts.delete(:style) || init[:style] || SWT::NONE)
|
136
|
+
style |= to_style(opts.delete(:add_style) || 0)
|
137
|
+
|
138
|
+
Sweet.debug "Creating #{cls.java_class.simple_name}(#{parent}, #{style})"
|
139
|
+
cls.new(parent, style)
|
140
|
+
end
|
141
|
+
|
142
|
+
def self.delegate_events(cls)
|
143
|
+
hacks = WIDGET_HACKS[cls]
|
144
|
+
default_listener = (hacks[:default_listener] || '').underscore if hacks
|
145
|
+
|
146
|
+
methods = cls.java_class.java_instance_methods
|
147
|
+
methods.each do |m|
|
148
|
+
next unless m.name =~ /(add(.*)Listener)/
|
149
|
+
j = $1
|
150
|
+
n = $2.underscore
|
151
|
+
p = m.parameter_types[0]
|
152
|
+
evts = p.declared_instance_methods
|
153
|
+
|
154
|
+
if evts.size == 1
|
155
|
+
s = "on_#{n}"
|
156
|
+
cls.send(:alias_method, s, j) unless cls.respond_to?(s)
|
157
|
+
else
|
158
|
+
evts.each do |evt|
|
159
|
+
s = evt.name.underscore
|
160
|
+
if s.split(/_/).size == 1 && n != default_listener
|
161
|
+
s = n + "_" + s
|
162
|
+
end
|
163
|
+
# TODO provide addListener SWT::Xxx option
|
164
|
+
unused_events = evts.select{ |e| e.name != evt.name }.map{ |e| "def #{e.name.underscore}(e)\nend" }.join("\n")
|
165
|
+
cls.class_eval <<-EOF
|
166
|
+
def on_#{s}(&block)
|
167
|
+
l = Class.new do
|
168
|
+
include Java.#{p}
|
169
|
+
def initialize(&block)
|
170
|
+
@block = block
|
171
|
+
end
|
172
|
+
def #{evt.name}(e)
|
173
|
+
@block.call e
|
174
|
+
end
|
175
|
+
#{unused_events}
|
176
|
+
end
|
177
|
+
#{j} l.new(&block)
|
178
|
+
end
|
179
|
+
EOF
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
private
|
186
|
+
def self.to_style(style)
|
187
|
+
Array(style).inject(0) do |result, value|
|
188
|
+
value = value.swt_const if value.is_a? Symbol
|
189
|
+
result | value
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|