sweet 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/COPYING ADDED
@@ -0,0 +1,18 @@
1
+ Copyright (c) 2010 Michael Klaus
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to
5
+ deal in the Software without restriction, including without limitation the
6
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7
+ sell copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16
+ THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,41 @@
1
+ == Sweet: The SWT wrapper for JRuby
2
+
3
+ Sweet is designed to provide the most readable and straight-forward DSL for the
4
+ SWT toolkit while maintaining all of its flexibility. If you find any limits to
5
+ this claim, please open a bug report or fix it on a fork if you feel
6
+ adventurous.
7
+
8
+ === Install
9
+
10
+ Sweet is available as a gem:
11
+
12
+ sudo gem install sweet
13
+
14
+ To install the necessary SWT for your platform libraries, run:
15
+
16
+ sudo sweet install
17
+
18
+
19
+ === Example
20
+
21
+ Sweet.app 'My first Application', :layout => :fill do
22
+ label 'Your name:'
23
+ @name = edit_line
24
+
25
+ button 'Push me' do
26
+ puts "Hello, #{@name.text}!"
27
+ end
28
+ end
29
+
30
+ You can then start your application via:
31
+
32
+ sweet my_app.rb
33
+
34
+ Or put the following lines into the head of your file:
35
+
36
+ require 'rubygems'
37
+ require 'sweet'
38
+
39
+ Also, take a look at the files in the example folder to get a head start.
40
+
41
+ Have fun!
data/Rakefile ADDED
@@ -0,0 +1,50 @@
1
+ #
2
+ # To change this template, choose Tools | Templates
3
+ # and open the template in the editor.
4
+
5
+
6
+ require 'rubygems'
7
+ require 'rake'
8
+ require 'rake/clean'
9
+ require 'rake/gempackagetask'
10
+ require 'rake/rdoctask'
11
+ require 'rake/testtask'
12
+ require 'spec/rake/spectask'
13
+
14
+ spec = Gem::Specification.new do |s|
15
+ s.name = 'Sweet'
16
+ s.version = '0.0.1'
17
+ s.has_rdoc = true
18
+ s.extra_rdoc_files = ['README', 'LICENSE']
19
+ s.summary = 'Your summary here'
20
+ s.description = s.summary
21
+ s.author = ''
22
+ s.email = ''
23
+ # s.executables = ['your_executable_here']
24
+ s.files = %w(LICENSE README Rakefile) + Dir.glob("{bin,lib,spec}/**/*")
25
+ s.require_path = "lib"
26
+ s.bindir = "bin"
27
+ end
28
+
29
+ Rake::GemPackageTask.new(spec) do |p|
30
+ p.gem_spec = spec
31
+ p.need_tar = true
32
+ p.need_zip = true
33
+ end
34
+
35
+ Rake::RDocTask.new do |rdoc|
36
+ files =['README', 'LICENSE', 'lib/**/*.rb']
37
+ rdoc.rdoc_files.add(files)
38
+ rdoc.main = "README" # page to start on
39
+ rdoc.title = "Sweet Docs"
40
+ rdoc.rdoc_dir = 'doc/rdoc' # rdoc output folder
41
+ rdoc.options << '--line-numbers'
42
+ end
43
+
44
+ Rake::TestTask.new do |t|
45
+ t.test_files = FileList['test/**/*.rb']
46
+ end
47
+
48
+ Spec::Rake::SpecTask.new do |t|
49
+ t.spec_files = FileList['spec/**/*.rb']
50
+ end
data/bin/sweet ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env jruby
2
+
3
+ require 'rubygems'
4
+ require 'sweet/downloader'
5
+
6
+ cmd = ARGV[0]
7
+
8
+ if cmd == 'install'
9
+ tk = ARGV[1]
10
+ opts = tk ? {:tk => tk} : {}
11
+ Sweet::Downloader.download_swt opts
12
+ else
13
+ require 'sweet'
14
+ load cmd
15
+ end
16
+
@@ -0,0 +1,11 @@
1
+ $: << File.join(__FILE__, '../lib')
2
+ require 'sweet'
3
+
4
+ Sweet.app 'My first Application', :layout => :fill do
5
+ label 'Your name:'
6
+ @name = edit_line
7
+
8
+ button 'Push me' do
9
+ puts "Hello, #{@name.text}!"
10
+ end
11
+ end
@@ -0,0 +1,46 @@
1
+ $: << File.join(__FILE__, '../lib')
2
+ require 'sweet'
3
+
4
+ # Display display = new Display ();
5
+ # Shell shell = new Shell (display);
6
+ # shell.setLayout (new RowLayout ());
7
+ Sweet.app :layout => :row do
8
+
9
+ # Label label = new Label (shell, SWT.NONE);
10
+ # label.setText ("Enter your name:");
11
+ label 'Enter your name:'
12
+
13
+ # Text text = new Text (shell, SWT.BORDER);
14
+ # text.setLayoutData (new RowData (100, SWT.DEFAULT));
15
+ edit_line :style => swt::BORDER, :row_data => 100
16
+
17
+ # Button ok = new Button (shell, SWT.PUSH);
18
+ # ok.setText ("OK");
19
+ # ok.addSelectionListener(new SelectionAdapter() {
20
+ # public void widgetSelected(SelectionEvent e) {
21
+ # System.out.println("OK");
22
+ # }
23
+ # });
24
+ button 'OK' do
25
+ puts 'OK'
26
+ end
27
+
28
+ # Button cancel = new Button (shell, SWT.PUSH);
29
+ # cancel.setText ("Cancel");
30
+ # cancel.addSelectionListener(new SelectionAdapter() {
31
+ # public void widgetSelected(SelectionEvent e) {
32
+ # System.out.println("Cancel");
33
+ # }
34
+ # });
35
+ # shell.setDefaultButton (cancel);
36
+ self.default_button = button 'Cancel' do
37
+ puts 'Cancel'
38
+ end
39
+
40
+ # shell.pack ();
41
+ # shell.open ();
42
+ # while (!shell.isDisposed ()) {
43
+ # if (!display.readAndDispatch ()) display.sleep ();
44
+ # }
45
+ # display.dispose ();
46
+ end
@@ -0,0 +1,159 @@
1
+ $: << File.join(__FILE__, '../lib')
2
+ require 'sweet'
3
+
4
+ # Display display = new Display();
5
+ # final Shell shell = new Shell(display);
6
+ # GridLayout gridLayout = new GridLayout();
7
+ # gridLayout.numColumns = 3;
8
+ # shell.setLayout(gridLayout);
9
+ Sweet.app :layout => :grid.conf(:numColumns => 3) do
10
+
11
+ # ToolBar toolbar = new ToolBar(shell, SWT.NONE);
12
+ # GridData data = new GridData();
13
+ # data.horizontalSpan = 3;
14
+ # toolbar.setLayoutData(data);
15
+ tool_bar :grid_data => {:span => 3} do
16
+
17
+ # ToolItem itemBack = new ToolItem(toolbar, SWT.PUSH);
18
+ # itemBack.setText("Back");
19
+ # ToolItem itemForward = new ToolItem(toolbar, SWT.PUSH);
20
+ # itemForward.setText("Forward");
21
+ # ToolItem itemStop = new ToolItem(toolbar, SWT.PUSH);
22
+ # itemStop.setText("Stop");
23
+ # ToolItem itemRefresh = new ToolItem(toolbar, SWT.PUSH);
24
+ # itemRefresh.setText("Refresh");
25
+ # ToolItem itemGo = new ToolItem(toolbar, SWT.PUSH);
26
+ # itemGo.setText("Go");
27
+ # /* event handling */
28
+ # Listener listener = new Listener() {
29
+ # public void handleEvent(Event event) {
30
+ # ToolItem item = (ToolItem)event.widget;
31
+ # String string = item.getText();
32
+ # if (string.equals("Back")) browser.back();
33
+ # else if (string.equals("Forward")) browser.forward();
34
+ # else if (string.equals("Stop")) browser.stop();
35
+ # else if (string.equals("Refresh")) browser.refresh();
36
+ # else if (string.equals("Go")) browser.setUrl(location.getText());
37
+ # }
38
+ # };
39
+ %w{Back Forward Stop Refresh}.each do |caption|
40
+ tool_item(caption) { @browser.send(caption.downcase) }
41
+ end
42
+ tool_item('Go') { @browser.setUrl @location.text }
43
+ end
44
+
45
+ # Label labelAddress = new Label(shell, SWT.NONE);
46
+ # labelAddress.setText("Address");
47
+ label 'Address'
48
+
49
+ # final Text location = new Text(shell, SWT.BORDER);
50
+ # data = new GridData();
51
+ # data.horizontalAlignment = GridData.FILL;
52
+ # data.horizontalSpan = 2;
53
+ # data.grabExcessHorizontalSpace = true;
54
+ # location.setLayoutData(data);
55
+ # location.addListener(SWT.DefaultSelection, new Listener() {
56
+ # public void handleEvent(Event e) {
57
+ # browser.setUrl(location.getText());
58
+ # }
59
+ # });
60
+ @location = edit_line :grid_data => {:align => :fill, :span => 2, :grab => true} do
61
+ @browser.setUrl @location.text
62
+ end
63
+
64
+
65
+ # final Browser browser;
66
+ # try {
67
+ # browser = new Browser(shell, SWT.NONE);
68
+ # } catch (SWTError e) {
69
+ # System.out.println("Could not instantiate Browser: " + e.getMessage());
70
+ # display.dispose();
71
+ # return;
72
+ # }
73
+ begin
74
+ # data = new GridData();
75
+ # data.horizontalAlignment = GridData.FILL;
76
+ # data.verticalAlignment = GridData.FILL;
77
+ # data.horizontalSpan = 3;
78
+ # data.grabExcessHorizontalSpace = true;
79
+ # data.grabExcessVerticalSpace = true;
80
+ # browser.setLayoutData(data);
81
+ @browser = browser :grid_data => {
82
+ :align => [:fill, :fill],
83
+ :span => 3,
84
+ :grab => [true, true]
85
+ }
86
+ rescue org.eclipse.swt.SWTError => e
87
+ SYSERR.puts "Could not instantiate Browser: #{e.message}"
88
+ exit
89
+ end
90
+
91
+ # final Label status = new Label(shell, SWT.NONE);
92
+ # data = new GridData(GridData.FILL_HORIZONTAL);
93
+ # data.horizontalSpan = 2;
94
+ # status.setLayoutData(data);
95
+ @status = label :grid_data => {:span => 2, :align => :fill}
96
+
97
+ # final ProgressBar progressBar = new ProgressBar(shell, SWT.NONE);
98
+ # data = new GridData();
99
+ # data.horizontalAlignment = GridData.END;
100
+ # progressBar.setLayoutData(data);
101
+ @progress_bar = progress :grid_data => {:align => :end}
102
+
103
+ #
104
+ # browser.addProgressListener(new ProgressListener() {
105
+ # public void changed(ProgressEvent event) {
106
+ # if (event.total == 0) return;
107
+ # int ratio = event.current * 100 / event.total;
108
+ # progressBar.setSelection(ratio);
109
+ # }
110
+ # public void completed(ProgressEvent event) {
111
+ # progressBar.setSelection(0);
112
+ # }
113
+ # });
114
+ @browser.on_progress_changed do |event|
115
+ if event.total != 0
116
+ @progress_bar.fraction = event.current.to_f / event.total
117
+ end
118
+ end
119
+ @browser.on_progress_completed do
120
+ @progress_bar.selection = 0
121
+ end
122
+
123
+ # browser.addStatusTextListener(new StatusTextListener() {
124
+ # public void changed(StatusTextEvent event) {
125
+ # status.setText(event.text);
126
+ # }
127
+ # });
128
+ @browser.on_status_text do |event|
129
+ @status.text = event.text
130
+ end
131
+
132
+ # browser.addLocationListener(new LocationListener() {
133
+ # public void changed(LocationEvent event) {
134
+ # if (event.top) location.setText(event.location);
135
+ # }
136
+ # public void changing(LocationEvent event) {
137
+ # }
138
+ # });
139
+ @browser.on_location_changed do |event|
140
+ @location.text = event.location if event.top
141
+ end
142
+
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
+ # shell.open();
150
+ # browser.setUrl("http://eclipse.org");
151
+ @browser.setUrl "http://eclipse.org"
152
+
153
+ #
154
+ # while (!shell.isDisposed()) {
155
+ # if (!display.readAndDispatch())
156
+ # display.sleep();
157
+ # }
158
+ # display.dispose();
159
+ end
@@ -0,0 +1,47 @@
1
+ $: << File.join(__FILE__, '../lib')
2
+ require 'sweet'
3
+
4
+ # Display display = new Display ();
5
+ # final Shell shell = new Shell (display);
6
+ # shell.setLayout (new FillLayout ());
7
+ Sweet.app :layout => :fill do
8
+
9
+ # Listener listener = new Listener () {
10
+ # public void handleEvent (Event e) {
11
+ # Control [] children = shell.getChildren ();
12
+ # for (int i=0; i<children.length; i++) {
13
+ # Control child = children [i];
14
+ # if (e.widget != child && child instanceof Button && (child.getStyle () & SWT.TOGGLE) != 0) {
15
+ # ((Button) child).setSelection (false);
16
+ # }
17
+ # }
18
+ # ((Button) e.widget).setSelection (true);
19
+ # }
20
+ # };
21
+ @listener = proc do |e|
22
+ shell.children.each do |child|
23
+ if e.widget != child && child.is_a?(widgets::Button) && (child.style & swt::TOGGLE)
24
+ child.selection = false
25
+ end
26
+ end
27
+ e.widget.selection = true
28
+ end
29
+
30
+ # for (int i=0; i<20; i++) {
31
+ # Button button = new Button (shell, SWT.TOGGLE);
32
+ # button.setText ("B" + i);
33
+ # button.addListener (SWT.Selection, listener);
34
+ # if (i == 0) button.setSelection (true);
35
+ # }
36
+
37
+ 20.times do |i|
38
+ button "B#{i}", :style => swt::TOGGLE, :selection => i == 0, &@listener
39
+ end
40
+
41
+ # shell.pack ();
42
+ # shell.open ();
43
+ # while (!shell.isDisposed ()) {
44
+ # if (!display.readAndDispatch ()) display.sleep ();
45
+ # }
46
+ # display.dispose ();
47
+ end
@@ -0,0 +1,57 @@
1
+ $: << File.join(__FILE__, '../lib')
2
+ require 'sweet'
3
+
4
+ #Display display = new Display();
5
+ # Shell shell = new Shell(display);
6
+ # shell.setLayout(new FillLayout());
7
+ Sweet.app :layout => :fill do
8
+
9
+ # CTabFolder folder = new CTabFolder(shell, SWT.BORDER);
10
+ @folder = tab_folder do
11
+ # for (int i = 0; i < 4; i++) {
12
+ # CTabItem item = new CTabItem(folder, SWT.CLOSE);
13
+ # item.setText("Item "+i);
14
+ # Text text = new Text(folder, SWT.MULTI);
15
+ # text.setText("Content for Item "+i);
16
+ # item.setControl(text);
17
+ # }
18
+ 4.times do |i|
19
+ tab_item "Item #{i}", :control => edit_area("Content for Item #{i}")
20
+ end
21
+
22
+ # final CTabItem specialItem = new CTabItem(folder, SWT.CLOSE);
23
+ # specialItem.setText("Don't Close Me");
24
+ # Text text = new Text(folder, SWT.MULTI);
25
+ # text.setText("This tab can never be closed");
26
+ # specialItem.setControl(text);
27
+ @special_item = tab_item("Don't Close Me") do
28
+ edit_area("This tab can never be closed")
29
+ end
30
+
31
+ # final CTabItem noCloseItem = new CTabItem(folder, SWT.NONE);
32
+ # noCloseItem.setText("No Close Button");
33
+ # Text text2 = new Text(folder, SWT.MULTI);
34
+ # text2.setText("This tab does not have a close button");
35
+ # noCloseItem.setControl(text2);
36
+ tab_item "No Close Button", edit_area("This tab does not have a close button"), :style => :none
37
+ end
38
+
39
+ # folder.addCTabFolder2Listener(new CTabFolder2Adapter() {
40
+ # public void close(CTabFolderEvent event) {
41
+ # if (event.item.equals(specialItem)) {
42
+ # event.doit = false;
43
+ # }
44
+ # }
45
+ # });
46
+ @folder.on_close do |event|
47
+ event.doit = event.item != @special_item
48
+ end
49
+
50
+ # shell.pack();
51
+ # shell.open();
52
+ # while (!shell.isDisposed()) {
53
+ # if (!display.readAndDispatch())
54
+ # display.sleep();
55
+ # }
56
+ # display.dispose();
57
+ end
data/lib/sweet/base.rb ADDED
@@ -0,0 +1,223 @@
1
+ require 'sweet/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 'java'
12
+ %w{hacks widget composite shell dialog}.each{|file| require "sweet/#{file}"}
13
+
14
+ # TODO create gemspec
15
+ # TODO evaluate integration of jface
16
+ # TODO enable additive and replacing :style on widget creation
17
+ module Sweet
18
+
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
+ WIDGETS = {}
94
+
95
+ def self.app(*args, &block)
96
+ # prepare the arguments
97
+ count = args.length
98
+ name = args.first.is_a?(String) ? args.shift : 'SWEET Application'
99
+ raise ArgumentError.new(count, 2) if args.size > 1
100
+ opts = args.first || {}
101
+
102
+ # build the UI
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
117
+ end
118
+
119
+ def self.create_widget(parent, name, *args, &block)
120
+ init = WIDGET_OPTIONS[name] || {}
121
+
122
+ unless cls = WIDGETS[name]
123
+ # puts "Loading widget #{name}"
124
+ # filename = File.join('sweet/widget', name.to_s)
125
+ # begin
126
+ # require filename
127
+ # rescue LoadError
128
+ cls = create_widget_class(name, init)
129
+ # end
130
+ end
131
+
132
+ 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
+ if init_args = init[:init_args]
145
+ Hash[Array(init_args).zip(args)].each do |k, v|
146
+ opts.merge! k => v if v
147
+ end
148
+ end
149
+ result.sweeten(parent.app, opts, &block)
150
+
151
+ result
152
+ end
153
+
154
+ def self.create_widget_class(name, init)
155
+ # build widget with default accessors
156
+ class_name = init[:class] || name
157
+ class_name = class_name.to_s.camelize(:upper) if class_name.is_a?(Symbol)
158
+ cls = class_name.is_a?(Class) ? class_name : const_get(class_name)
159
+
160
+ unless WIDGETS.values.member? cls
161
+ delegate_events(cls)
162
+ if hacks = WIDGET_HACKS[cls]
163
+ if custom_code = hacks[:custom_code]
164
+ cls.class_eval &custom_code
165
+ end
166
+ end
167
+ end
168
+
169
+ cls
170
+ end
171
+
172
+ def self.delegate_events(cls)
173
+ hacks = WIDGET_HACKS[cls]
174
+ default_listener = (hacks[:default_listener] || '').underscore if hacks
175
+
176
+ methods = cls.java_class.java_instance_methods
177
+ methods.each do |m|
178
+ next unless m.name =~ /(add(.*)Listener)/
179
+ j = $1
180
+ n = $2.underscore
181
+ p = m.parameter_types[0]
182
+ evts = p.declared_instance_methods
183
+
184
+ if evts.size == 1
185
+ s = "on_#{n}"
186
+ cls.send(:alias_method, s, j) unless cls.respond_to?(s)
187
+ else
188
+ evts.each do |evt|
189
+ s = evt.name.underscore
190
+ if s.split(/_/).size == 1 && n != default_listener
191
+ s = n + "_" + s
192
+ end
193
+ # TODO provide addListener SWT::Xxx option
194
+ unused_events = evts.select{ |e| e.name != evt.name }.map{ |e| "def #{e.name.underscore}(e)\nend" }.join("\n")
195
+ cls.class_eval <<-EOF
196
+ def on_#{s}(&block)
197
+ l = Class.new do
198
+ include Java.#{p}
199
+ def initialize(&block)
200
+ @block = block
201
+ end
202
+ def #{evt.name}(e)
203
+ @block.call e
204
+ end
205
+ #{unused_events}
206
+ end
207
+ #{j} l.new(&block)
208
+ end
209
+ EOF
210
+ end
211
+ end
212
+ end
213
+ end
214
+
215
+ def self.set_debug
216
+ @debug = true
217
+ end
218
+ def self.debug(text)
219
+ # TODO add a decent logging mechanism
220
+ puts text if @debug
221
+ end
222
+ end
223
+
@@ -0,0 +1,14 @@
1
+ class Java::OrgEclipseSwtWidgets::Composite
2
+
3
+ def layout=(name, options = {})
4
+ class_name = "#{name.to_s.capitalize}Layout"
5
+ l = instance_eval "org.eclipse.swt.layout.#{class_name}.new"
6
+
7
+ options.each_pair do |k,v|
8
+ l.send("#{k}=", v)
9
+ end
10
+
11
+ setLayout l
12
+ end
13
+
14
+ end
@@ -0,0 +1,9 @@
1
+ class Java::OrgEclipseSwtWidgets::Dialog
2
+
3
+ attr_reader :app
4
+
5
+ def sweeten(app, opts)
6
+ @app = app
7
+ end
8
+
9
+ end
@@ -0,0 +1,63 @@
1
+ module Sweet
2
+ SWT_JAR = File.join(File.dirname(__FILE__), '../../swt/swt.jar')
3
+
4
+ module Downloader
5
+ def self.download_swt(opts = {})
6
+ require 'rubygems'
7
+ require 'open-uri'
8
+ require 'zippy'
9
+ require 'fileutils'
10
+
11
+ platform = system_qualifier(opts)
12
+ puts "Downloading SWT #{platform}"
13
+
14
+ # TODO support other SWT versions
15
+ open("http://mirrors.ibiblio.org/pub/mirrors/eclipse/eclipse/downloads/drops/R-3.5.2-201002111343/swt-3.5.2-#{platform}.zip") do |remote|
16
+ Zippy.open(remote.path) do |zip|
17
+ FileUtils.mkdir_p File.dirname(Sweet::SWT_JAR)
18
+ File.open(Sweet::SWT_JAR, 'wb') do |f|
19
+ f.write zip['swt.jar']
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ # TODO integrate user input to all feasible platforms
26
+ def self.system_qualifier(opts = {})
27
+ props = Java.java.lang.System.properties
28
+ os = (opts[:os] || props['os.name']).downcase
29
+
30
+ arch = (opts[:arch] || props['os.arch']).downcase
31
+ arch = 'x86' if arch =~ /..86$/
32
+
33
+ wish_tk = opts[:tk]
34
+ tk = case os
35
+ when *%w{linux solaris sunos}
36
+ os = 'solaris' if os == 'sunos'
37
+ %w{gtk motif}.member?(wish_tk) ? wish_tk : 'gtk'
38
+ when 'mac os x'
39
+ os = 'macosx'
40
+ arch = nil if arch != 'x86_64'
41
+ %w{carbon cocoa}.member?(wish_tk) ? wish_tk : 'cocoa'
42
+ when 'qnx'
43
+ arch = 'x86'
44
+ 'photon'
45
+ when 'aix'
46
+ arch = 'ppc'
47
+ 'motif'
48
+ when 'hp-ux'
49
+ os = 'hpux'
50
+ arch = 'ia64_32'
51
+ 'motif'
52
+ when 'windows vista'
53
+ os = 'win32'
54
+ arch = 'x86' if wish_tk == 'wpf'
55
+ %w{wpf win32}.member?(wish_tk) ? wish_tk : 'win32'
56
+ when /windows/
57
+ os = 'win32' # returns win32 as tk also
58
+ end
59
+
60
+ [tk, os, arch].compact.join('-')
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,43 @@
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
+ class Symbol
19
+ # For configuring layouts
20
+ def conf(opts = {})
21
+ [self, opts]
22
+ end
23
+ def swt_const
24
+ org.eclipse.swt.SWT.const_get(self.to_s.upcase)
25
+ end
26
+ def swt_event
27
+ org.eclipse.swt.SWT.const_get(self.to_s.camelize(:upper))
28
+ end
29
+ end
30
+
31
+ # TODO find those things in JRuby
32
+ class String
33
+ def camelize(first_letter_in_uppercase = :upper)
34
+ s = gsub(/\/(.?)/){|x| "::#{x[-1..-1].upcase unless x == '/'}"}.gsub(/(^|_)(.)/){|x| x[-1..-1].upcase}
35
+ s[0...1] = s[0...1].downcase unless first_letter_in_uppercase == :upper
36
+ s
37
+ end
38
+ def underscore
39
+ gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
40
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').tr("-", "_").downcase
41
+ end
42
+ end
43
+
@@ -0,0 +1,53 @@
1
+ # TODO pack into WIDGETS
2
+ class Java::OrgEclipseSwtWidgets::Shell
3
+ attr_reader :display
4
+ alias_property :title => :text
5
+
6
+ def sweeten(display, name, opts = {}, &block)
7
+ @display = display
8
+
9
+ self.text = opts.delete(:title) || name
10
+ w, h = opts.delete(:width), opts.delete(:height)
11
+
12
+ super(self, opts, &block)
13
+
14
+ pack
15
+ size = (w || width), (h || height)
16
+ end
17
+
18
+ def sweet_containers
19
+ @sweet_containers ||= [self]
20
+ end
21
+
22
+ def perform(&block)
23
+ display.syncExec block
24
+ end
25
+
26
+ def busy(&block)
27
+ Thread.new do
28
+ perform do
29
+ custom::BusyIndicator.showWhile(display) do
30
+ block.call
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ def menubar(&block)
37
+ self.menu_bar = make_menu(:menubar, &block)
38
+ end
39
+
40
+ def popup(&block)
41
+ self.menu = make_menu(:popup, &block)
42
+ end
43
+
44
+ def method_missing(name, *args, &block)
45
+ Sweet.create_widget(sweet_containers.last, name, *args, &block) || super
46
+ end
47
+
48
+ private
49
+ def make_menu(type, &block)
50
+ Sweet.create_widget(sweet_containers.last, type, &block)
51
+ end
52
+
53
+ end
@@ -0,0 +1,151 @@
1
+ require 'forwardable'
2
+
3
+ class Java::OrgEclipseSwtWidgets::Widget
4
+ extend Forwardable
5
+ attr_reader :app
6
+
7
+ def sweeten(app, opts, &block)
8
+ @app = app
9
+ self.options = opts
10
+
11
+ if block
12
+ case handler = @block_handler
13
+ when nil
14
+ handle_container &block
15
+ when Symbol
16
+ send handler, &block
17
+ when Numeric
18
+ addListener handler, &block
19
+ when Proc
20
+ instance_eval do
21
+ handler.call self, opts, block
22
+ end
23
+ else
24
+ raise "Invalid :block_handler ",handler
25
+ end
26
+ end
27
+
28
+ puts "Unknown properties for class #{self.class}: #{opts.keys.inspect}" unless opts.empty?
29
+ end
30
+
31
+ def app(&block)
32
+ block ? @app.instance_eval(&block) : @app
33
+ end
34
+
35
+ def append(&block)
36
+ raise "Append called without block" unless block
37
+ handle_container &block
38
+ end
39
+
40
+ def meta(&block)
41
+ @meta ||= class << self
42
+ self
43
+ end
44
+ block ? @meta.class_eval(&block) : @meta
45
+ end
46
+
47
+ def self.alias_property(aliases = {})
48
+ aliases.each do |from, to|
49
+ alias_method from, to unless "to"
50
+ alias_method "#{from}=", "#{to}=" unless "#{to}="
51
+ end
52
+ end
53
+
54
+ def options=(opts)
55
+ # TODO reader method
56
+ opts.each_pair do |k, v|
57
+ case k
58
+ when :hidden
59
+ setVisible !v
60
+ else
61
+ name = k.to_s + "="
62
+ if respond_to?(name)
63
+ if v
64
+ Sweet.debug "#{self.java_class.simple_name}.#{k} = #{v.inspect}"
65
+ send name, *v
66
+ end
67
+ opts.delete(k)
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+
74
+ # TODO unify this layout mess somehow
75
+ # TODO hierarchical hash for nicer client code
76
+ def grid_data=(*opts)
77
+ opts = Hash[opts]
78
+ ax, ay = opts[:align]
79
+ gx, gy = opts[:grab]
80
+ sx, sy = opts[:span]
81
+ args = [ax || swt::BEGINNING, ay || swt::CENTER,
82
+ gx || false, gy || false, sx || 1, sy || 1]
83
+ args.map!{|v| v.is_a?(Symbol) ? v.swt_const : v}
84
+ Sweet.debug args.inspect
85
+ l = layouts::GridData.new(*args)
86
+ self.layout_data = l
87
+ end
88
+ def row_data=(x, y = nil)
89
+ self.layout_data = layouts::RowData.new(x || swt::DEFAULT, y || swt::DEFAULT)
90
+ end
91
+
92
+ def perform(&block)
93
+ app.perform &block
94
+ end
95
+
96
+ def hide
97
+ self.visible = false
98
+ end
99
+ def show
100
+ self.visible = true
101
+ end
102
+
103
+ def width
104
+ size.x
105
+ end
106
+ def width=(value)
107
+ self.size = value, nil
108
+ end
109
+
110
+ def height
111
+ size.y
112
+ end
113
+ def height=(value)
114
+ self.size = nil, value
115
+ end
116
+
117
+ def size=(*s)
118
+ nw, nh = s.flatten
119
+ nw ||= width
120
+ nh ||= height
121
+ setSize(nw, nh)
122
+ end
123
+
124
+ # TODO replace via import?
125
+ def swt
126
+ org.eclipse.swt.SWT
127
+ end
128
+ def widgets
129
+ org.eclipse.swt.widgets
130
+ end
131
+ def layouts
132
+ org.eclipse.swt.layout
133
+ end
134
+ def custom
135
+ org.eclipse.swt.custom
136
+ end
137
+
138
+ def method_missing(name, *args, &block)
139
+ handle_container(&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
+ end
151
+
data/lib/sweet.rb ADDED
@@ -0,0 +1 @@
1
+ require 'sweet/base'
@@ -0,0 +1,36 @@
1
+ require 'sweet/downloader'
2
+
3
+ describe Sweet::Downloader, '#system_qualifier' do
4
+ before :each do
5
+ @d = Sweet::Downloader
6
+ end
7
+
8
+ it 'returns gtk-linux-x86 for Linux i686' do
9
+ @d.system_qualifier(:os => 'Linux', :arch => 'i686').should == 'gtk-linux-x86'
10
+ end
11
+
12
+ it 'returns gtk-linux-x86 for Linux x86_64' do
13
+ @d.system_qualifier(:os => 'Linux', :arch => 'x86_64').should == 'gtk-linux-x86_64'
14
+ end
15
+
16
+ it 'returns motif-linux-x86 for Linux i686 and :tk => "motif"' do
17
+ @d.system_qualifier(:os => 'Linux', :arch => 'i686', :tk => 'motif').should == 'motif-linux-x86'
18
+ end
19
+
20
+ it 'returns cocoa-macos for Mac OS X i686' do
21
+ @d.system_qualifier(:os => 'Mac OS X', :arch => 'i686').should == 'cocoa-macosx'
22
+ end
23
+
24
+ it 'returns cocoa-macos-x86_64 for Mac OS X x86_64' do
25
+ @d.system_qualifier(:os => 'Mac OS X', :arch => 'x86_64').should == 'cocoa-macosx-x86_64'
26
+ end
27
+
28
+ it 'returns win32-win32-x86 for Windows XP' do
29
+ @d.system_qualifier(:os => 'Windows XP', :arch => 'i686').should == 'win32-win32-x86'
30
+ end
31
+
32
+ it 'returns win32-win32-x86_64 for Windows Vista on x86_64' do
33
+ @d.system_qualifier(:os => 'Windows Vista', :arch => 'x86_64').should == 'win32-win32-x86_64'
34
+ end
35
+
36
+ end
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sweet
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 2
9
+ version: 0.0.2
10
+ platform: ruby
11
+ authors:
12
+ - Michael Klaus
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-05-17 00:00:00 +02:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: zippy
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :runtime
32
+ version_requirements: *id001
33
+ description: The SWT wrapper for JRuby
34
+ email: Michael.Klaus@gmx.net
35
+ executables:
36
+ - sweet
37
+ extensions: []
38
+
39
+ extra_rdoc_files:
40
+ - README.rdoc
41
+ - COPYING
42
+ files:
43
+ - COPYING
44
+ - README.rdoc
45
+ - Rakefile
46
+ - bin/sweet
47
+ - spec/downloader_spec.rb
48
+ - lib/sweet.rb
49
+ - lib/sweet/shell.rb
50
+ - lib/sweet/widget.rb
51
+ - lib/sweet/hacks.rb
52
+ - lib/sweet/downloader.rb
53
+ - lib/sweet/composite.rb
54
+ - lib/sweet/base.rb
55
+ - lib/sweet/dialog.rb
56
+ - examples/snippet128.rb
57
+ - examples/snippet169.rb
58
+ - examples/snippet108.rb
59
+ - examples/snippet82.rb
60
+ - examples/hello_world.rb
61
+ has_rdoc: true
62
+ homepage: http://github.org/QaDeS/sweet
63
+ licenses: []
64
+
65
+ post_install_message:
66
+ rdoc_options:
67
+ - --quiet
68
+ - --line-numbers
69
+ - --inline-source
70
+ - --title
71
+ - "Sweet: The SWT wrapper for JRuby"
72
+ - --main
73
+ - README.rdoc
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ segments:
82
+ - 0
83
+ version: "0"
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ segments:
90
+ - 0
91
+ version: "0"
92
+ requirements: []
93
+
94
+ rubyforge_project:
95
+ rubygems_version: 1.3.7
96
+ signing_key:
97
+ specification_version: 3
98
+ summary: The SWT wrapper for JRuby
99
+ test_files: []
100
+