sweet 0.0.2

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/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
+