sweet 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/README.rdoc +9 -5
  2. data/bin/sweet +3 -1
  3. data/examples/fox/hello_world.rb +12 -0
  4. data/examples/gtk2/hello_world.rb +14 -0
  5. data/examples/swing/hello_world.rb +12 -0
  6. data/examples/swt/components.rb +112 -0
  7. data/examples/swt/components/buttons.rb +35 -0
  8. data/examples/{hello_world.rb → swt/hello_world.rb} +2 -2
  9. data/examples/{snippet108.rb → swt/snippet108.rb} +2 -2
  10. data/examples/{snippet128.rb → swt/snippet128.rb} +7 -8
  11. data/examples/{snippet169.rb → swt/snippet169.rb} +2 -2
  12. data/examples/{snippet82.rb → swt/snippet82.rb} +2 -2
  13. data/examples/wx/hello_world.rb +12 -0
  14. data/lib/sweet.rb +22 -1
  15. data/lib/sweet/base.rb +151 -177
  16. data/lib/sweet/fox.rb +47 -0
  17. data/lib/sweet/fox/application.rb +9 -0
  18. data/lib/sweet/fox/object.rb +19 -0
  19. data/lib/sweet/gtk2.rb +56 -0
  20. data/lib/sweet/gtk2/application.rb +11 -0
  21. data/lib/sweet/gtk2/widget.rb +5 -0
  22. data/lib/sweet/hacks.rb +0 -17
  23. data/lib/sweet/swing.rb +97 -0
  24. data/lib/sweet/swing/application.rb +9 -0
  25. data/lib/sweet/swing/component.rb +23 -0
  26. data/lib/sweet/swing/frame.rb +13 -0
  27. data/lib/sweet/swt.rb +193 -0
  28. data/lib/sweet/swt/application.rb +17 -0
  29. data/lib/sweet/{composite.rb → swt/composite.rb} +5 -2
  30. data/lib/sweet/{dialog.rb → swt/dialog.rb} +0 -0
  31. data/lib/sweet/{downloader.rb → swt/downloader.rb} +1 -1
  32. data/lib/sweet/swt/shell.rb +22 -0
  33. data/lib/sweet/swt/src.zip +0 -0
  34. data/lib/sweet/swt/swt.jar +0 -0
  35. data/lib/sweet/swt/widget.rb +89 -0
  36. data/lib/sweet/wx.rb +57 -0
  37. data/lib/sweet/wx/application.rb +9 -0
  38. data/lib/sweet/wx/object.rb +19 -0
  39. data/spec/api_spec.rb +15 -0
  40. data/spec/downloader_spec.rb +28 -26
  41. data/spec/fox_spec.rb +15 -0
  42. data/spec/gtk2_spec.rb +15 -0
  43. data/spec/sweet_spec.rb +15 -0
  44. data/spec/swing_spec.rb +15 -0
  45. data/spec/swt_spec.rb +15 -0
  46. data/spec/wx_spec.rb +15 -0
  47. metadata +44 -17
  48. data/lib/sweet/shell.rb +0 -53
  49. 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,9 @@
1
+ module Sweet::Application
2
+ def perform(&block)
3
+ instance_eval &block
4
+ end
5
+
6
+ def busy(&block)
7
+ instance_eval &block
8
+ end
9
+ 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
@@ -0,0 +1,11 @@
1
+ module Sweet::Application
2
+ def perform(&block)
3
+ # TODO implement
4
+ instance_eval &block
5
+ end
6
+
7
+ def busy(&block)
8
+ # TODO implement
9
+ instance_eval &block
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ class Gtk::Widget
2
+ include Sweet::Component
3
+ alias handle_event signal_connect
4
+ end
5
+
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 = {})
@@ -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,9 @@
1
+ module Sweet::Application
2
+ def perform(&block)
3
+ SwingUtilities.invoke_and_wait &block
4
+ end
5
+
6
+ def busy(&block)
7
+ SwingUtilities.invoke_later &block
8
+ end
9
+ 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
@@ -0,0 +1,13 @@
1
+ class Java::JavaxSwing::JFrame
2
+ alias_property :text => :title
3
+
4
+ def sweeten(opts = {}, &block)
5
+ w, h = opts.delete(:width), opts.delete(:height)
6
+
7
+ super(self, opts, &block)
8
+
9
+ pack
10
+ size = (w || width), (h || height)
11
+ end
12
+
13
+ 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