simple_gui_creator 0.1.4 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +3 -1
- data/README +73 -32
- data/Rakefile +1 -0
- data/TODO +25 -4
- data/VERSION +1 -1
- data/lib/simple_gui_creator/parse_template.rb +195 -156
- data/lib/simple_gui_creator/swing_helpers.rb +36 -4
- data/license.mit.txt +19 -0
- data/spec/parse_template.spec.rb +72 -27
- metadata +27 -3
data/ChangeLog
CHANGED
data/README
CHANGED
@@ -1,81 +1,122 @@
|
|
1
|
-
This gem is meant to make GUI development in Ruby
|
1
|
+
This gem is meant to make GUI development in Ruby apps trivial,
|
2
|
+
and easy to add to a project.
|
2
3
|
|
3
|
-
|
4
|
+
Basically it allows you to design your window GUI layout using
|
5
|
+
ASCII art-like text:
|
4
6
|
|
5
|
-
|
6
|
-
| [
|
7
|
-
|
|
8
|
-
|
7
|
+
---------------My Window Title--------------------------
|
8
|
+
| [Click this button :button1] |
|
9
|
+
| [Click this button too! :button2] |
|
10
|
+
--------------------------------------------------------
|
9
11
|
|
10
|
-
|
12
|
+
And then bind actions to ruby code, like this:
|
11
13
|
|
12
|
-
|
13
|
-
|
14
|
+
elements[:button1].on_clicked { puts 'you clicked button1' }
|
15
|
+
|
16
|
+
This makes the design part of the GUI much easier than trying to figure
|
17
|
+
it out by trial and error, or "guessing" what swing or shoes are going to actually
|
18
|
+
give for you in the end. It comes with its own experimental "GUI Editor" for
|
19
|
+
trying out designs on the fly.
|
20
|
+
|
21
|
+
How to get it:
|
22
|
+
|
23
|
+
# if you don't have jruby yet installed, for OS X or linux users (windows users use the jruby installer exe):
|
24
|
+
$ rvm install jruby
|
25
|
+
$ rvm use jruby
|
26
|
+
|
27
|
+
# now install the gem.
|
28
|
+
$ gem install simple_gui_creator
|
29
|
+
|
30
|
+
# now run the GUI design editor
|
31
|
+
$ jruby -S simple_gui_creator
|
32
|
+
|
33
|
+
Here's how to code it:
|
34
|
+
|
35
|
+
>> frame = SimpleGuiCreator::ParseTemplate.new # or optionally subclass this instead, and make sure to call super() in your constructor
|
14
36
|
>> frame.parse_setup_filename 'some_filename'
|
15
37
|
|
16
|
-
|
38
|
+
Set behaviors like this:
|
17
39
|
|
18
40
|
>> frame.elements['button1'].on_clicked {
|
19
41
|
SimpleGuiCreator.show_blocking_message_dialog "you clicked button1!"
|
20
42
|
}
|
21
43
|
|
22
|
-
This separates your views from your controllers, in this case, because you can store the layouts in
|
23
|
-
an entirely separate file, or embedded in the code. "Normal humans" can then edit the design layout files, for instance.
|
24
44
|
|
25
|
-
|
45
|
+
Having the layout as ASCII text separates views from controllers, in this case, because you can store the layouts in
|
46
|
+
an entirely separate file (or embedded in the code). "Normal humans" can then edit the design layout files, for instance.
|
47
|
+
It has proven quite nice at separating concerns.
|
48
|
+
|
49
|
+
More complicated example:
|
50
|
+
|
51
|
+
---------- Window Title ----------------------------------
|
52
|
+
| [a button:button1] [button text:button2] |
|
53
|
+
| "some text2:text1" |
|
54
|
+
| [An Editable Text Area with 4 rows:text_area] |
|
55
|
+
| [ ] |
|
56
|
+
| [ ] |
|
57
|
+
| [ ] |
|
58
|
+
| [Dropdown Default \/ :dropdown_name] |
|
59
|
+
| [✓:checkbox_name] "Some text" |
|
60
|
+
----------------------------------------------------------
|
61
|
+
|
62
|
+
See the file spec/parse_template.spec.rb for examples of how to use each element.
|
63
|
+
The window borders are also optional.
|
64
|
+
|
65
|
+
The library also has helper methods for common GUI tasks, like:
|
66
|
+
|
67
|
+
SimpleGuiCreator.show_blocking_message_dialog "A message box"
|
26
68
|
SimpleGuiCreator.new_nonexisting_filechooser_and_go # select file or filename for a "new file" (not yet existing)
|
27
69
|
SimpleGuiCreator.new_existing_dir_chooser_and_go # select pre-existing directory
|
28
70
|
SimpleGuiCreator.show_in_explorer(filename) # reveals file in Explorer for windows, Finder for OS X
|
29
71
|
text_from_user = SimpleGuiCreator.get_user_input "Input your name:" # these raise an exception if the user cancels the dialog,
|
30
72
|
|
31
|
-
|
32
|
-
|
73
|
+
Select-button prompt dialog:
|
33
74
|
if(SimpleGuiCreator.show_select_buttons_prompt("message title", :yes => 'text for the yes button', :no => 'text for the no button', :cancel => 'text for the cancel button') == :yes)
|
34
75
|
# they chose the "yes" equivalent button...
|
35
76
|
end
|
36
77
|
|
37
78
|
etc. ...
|
38
79
|
|
39
|
-
It provies a few helper methods to the ParseTemplate
|
80
|
+
It provies a few helper methods to the SimpleGuiCreator::ParseTemplate (and JFrame) classes, like:
|
40
81
|
|
41
82
|
#bring_to_front
|
42
83
|
#minimize
|
84
|
+
#maximize
|
43
85
|
#restore
|
44
86
|
#after_closed { ... }
|
45
87
|
#after_minimized { ... }
|
46
88
|
|
47
|
-
|
89
|
+
See the files lib/simple_gui_creator/swing_helpers.rb and lib/simple_gui_creator/jframe_helper_methods.rb.
|
90
|
+
|
91
|
+
It has helpers to playback/control audio files, like mp3's or wave's, starting/stopping asynchronously (see the files in the 'lib/simple_gui_creator/*' directory.
|
48
92
|
It has helpers to set/get system clipboard contents.
|
49
|
-
It has helpers to control/query the mouse
|
93
|
+
It has helpers to control/query the system mouse.
|
50
94
|
It has helpers to query the current system for its DVD drives, be notified when disks are inserted/changed, etc.
|
51
95
|
|
52
96
|
Feedback/feature requests welcome.
|
53
97
|
http://groups.google.com/group/roger-projects, roger-projects@googlegroups.com
|
54
|
-
I'd even be happy to wrap other gui frameworks (currently jruby/swing only) with the same
|
55
|
-
|
56
|
-
Enjoy!
|
57
|
-
|
58
|
-
To install/use:
|
59
|
-
|
60
|
-
$ gem install simple-ruby-gui-creator
|
61
|
-
|
62
|
-
>> require 'simple_gui_creator'
|
63
|
-
>> ... [see other examples]
|
98
|
+
I'd even be happy to wrap other gui frameworks (currently jruby/swing only) with the same functionality,
|
99
|
+
if anybody wanted it.
|
64
100
|
|
65
101
|
== GUI Editor ==
|
66
102
|
|
67
103
|
It also comes with its own "test as you go" design builder helper GUI, programmed, appropriately enough, using simple gui creator.
|
68
104
|
$ jruby -S simple_gui_creator
|
69
105
|
|
70
|
-
This pulls up a window with some demo code, and some links that let you save it.
|
106
|
+
This pulls up a window with some demo code, and some links that let you save it.
|
107
|
+
It's almost more of a "demo of how using it looks like" (the bin/simple_gui_creator file) than a
|
71
108
|
real editing tool, but feature requests/suggestions wanted!
|
72
109
|
|
73
110
|
== Documentation ==
|
74
111
|
|
75
|
-
Basically, see this file, the examples folder, the "bin/*"
|
112
|
+
Basically, see this README file, the examples folder, the "bin/*" file, and also the specs and lib files themselves.
|
76
113
|
https://github.com/rdp/ruby_simple_gui_creator
|
77
114
|
|
78
115
|
== Known problems ==
|
79
116
|
|
80
|
-
Only
|
81
|
-
|
117
|
+
Only Jruby today, which can make load times painful (hint: use splash screen).
|
118
|
+
|
119
|
+
= License ==
|
120
|
+
|
121
|
+
MIT license (which is quite open), see accompanying file license.mit.txt.
|
122
|
+
(C) 2012 Roger Pack
|
data/Rakefile
CHANGED
data/TODO
CHANGED
@@ -1,12 +1,33 @@
|
|
1
|
-
|
1
|
+
see spec/parse_template.spec.rb for another (the real) TODO list
|
2
2
|
|
3
|
-
|
3
|
+
= maybe =
|
4
|
+
|
5
|
+
# LODO allow internal sub-boxes LOL
|
6
|
+
# LODO should pass the button through to on_clicked [?] or button with frame, too?
|
7
|
+
# LODO should be able to clear everything a button does or used to do...
|
8
|
+
# LODO a 'title managing' object LOL
|
9
|
+
# LODO rel_width=+100 or some odd
|
10
|
+
# LODO real documentation LOL
|
11
|
+
# buttons should require a code name :P
|
12
|
+
|
13
|
+
if the window goes too low, they can't see it all, and it doesn't scroll?
|
14
|
+
|
15
|
+
|
16
|
+
use native file handlers, the swing ones shuck with default save filenames
|
4
17
|
|
5
18
|
= never =
|
6
19
|
|
7
|
-
|
20
|
+
HTML linked version. what the woot! LOL.
|
21
|
+
serialize the object...to RAM for now LOL.
|
22
|
+
pull the object from a db based on ID
|
23
|
+
instance_exec the [saved in RAM] proc
|
24
|
+
this would mean you can never "wildly redefine" action for pages though clicks...hmm...
|
25
|
+
or you could just pass "self" through...hmm...
|
26
|
+
whatever would be first pass ideal, just make that happen
|
27
|
+
|
28
|
+
fix: Parsing failed on line " _ _\t \n" number: 5!
|
8
29
|
|
9
|
-
use launchy for url's in linux
|
30
|
+
use launchy for at least url's in linux
|
10
31
|
|
11
32
|
ask_should_allow_on_minimize {
|
12
33
|
}
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
@@ -1,11 +1,13 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
require 'java'
|
3
|
+
require 'andand'
|
2
4
|
|
3
5
|
require File.dirname(__FILE__) + '/swing_helpers.rb' # for JButton#on_clicked, etc.,
|
4
6
|
|
5
7
|
# for documentation, see the README file
|
6
8
|
module SimpleGuiCreator
|
7
9
|
|
8
|
-
include_package 'javax.swing'; [JFrame, JPanel, JButton, JTextArea, JLabel, UIManager, JScrollPane]
|
10
|
+
include_package 'javax.swing'; [JFrame, JPanel, JButton, JTextArea, JLabel, UIManager, JScrollPane, JCheckBox, JComboBox]
|
9
11
|
java_import java.awt.Font
|
10
12
|
|
11
13
|
class ParseTemplate < JFrame
|
@@ -16,193 +18,230 @@ module SimpleGuiCreator
|
|
16
18
|
@elements = {}
|
17
19
|
@panel.set_layout nil
|
18
20
|
add @panel # why can't I just slap these down? panel? huh?
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
21
|
+
show # this always bites me...I new it up an it just doesn't appear...
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_reader :panel
|
25
|
+
attr_reader :elements
|
26
|
+
attr_accessor :original_title
|
27
|
+
attr_accessor :frame
|
28
|
+
|
27
29
|
def parse_setup_filename filename
|
28
30
|
parse_setup_string File.read(filename)
|
29
|
-
|
31
|
+
self
|
30
32
|
end
|
31
33
|
|
32
34
|
# "matches" whatever the template string looks like...
|
33
35
|
def parse_setup_string string
|
34
36
|
@frame = self # LODO refactor
|
35
|
-
|
36
|
-
|
37
|
-
|
37
|
+
@current_y = 10
|
38
|
+
@window_max_x = 100
|
39
|
+
all_lines = string.lines.to_a
|
38
40
|
all_lines.each_with_index{|line, idx|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
button = JButton.new
|
85
|
-
setup_element(button, name)
|
86
|
-
end
|
87
|
-
cur_x = end_spot # creep forward within this line...
|
88
|
-
end
|
89
|
-
@current_y += @current_line_height
|
90
|
-
elsif line =~ text_regex
|
91
|
-
for name in line.scan(text_regex)
|
92
|
-
label = JLabel.new
|
93
|
-
setup_element(label, name[0])
|
94
|
-
end
|
95
|
-
@current_y += @current_line_height
|
96
|
-
end
|
97
|
-
# build in realtime LOL
|
98
|
-
@frame.set_size @window_max_x + 25, @current_y + 40
|
99
|
-
rescue
|
100
|
-
puts "Parsing failed on line #{line.inspect} number: #{idx+1}!"
|
101
|
-
raise
|
102
|
-
end
|
103
|
-
}
|
41
|
+
begin
|
42
|
+
@current_x = 10
|
43
|
+
if line =~ /\t/
|
44
|
+
raise "sorry, tabs arent allowed, but you can request it"
|
45
|
+
end
|
46
|
+
button_line_regex = /\[(.*?)\]/
|
47
|
+
#>> "| [Setup Preferences:preferences] [Start:start] [Stop:stop] |" .scan button_line_regex
|
48
|
+
#=> [["Setup Preferences:preferences"], ["Start:start"], ["Stop:stop"]]
|
49
|
+
|
50
|
+
text_regex = /"([^"]+)"/ # "some text:name"
|
51
|
+
blank_line_regex = /^\s*(|\|)\s+(|\|)\s*$/ # matches " | | " or just empty...
|
52
|
+
title_regex = /\s*[-]+([\w ]+)[-]+\s*$/ # ----(a Title)---
|
53
|
+
@current_line_height = 25
|
54
|
+
|
55
|
+
if line =~ title_regex
|
56
|
+
@frame.set_title $1 # done :)
|
57
|
+
@frame.original_title = $1.dup.freeze # freeze...LOL
|
58
|
+
elsif line =~ blank_line_regex
|
59
|
+
@current_y += @current_line_height
|
60
|
+
else
|
61
|
+
# attempt an element line
|
62
|
+
cur_x = 0
|
63
|
+
while next_match = closest_next_regex_match([button_line_regex, text_regex], line[cur_x..-1])
|
64
|
+
cur_spot = line[cur_x..-1] =~ next_match
|
65
|
+
cur_spot += cur_x # we had only acted on a partial line, above, so add in the part we didn't do, to get the right offset number
|
66
|
+
captured = $1
|
67
|
+
end_spot = cur_spot + captured.length
|
68
|
+
if next_match == button_line_regex
|
69
|
+
handle_button_at_current captured, cur_spot, end_spot, all_lines, idx
|
70
|
+
elsif next_match == text_regex
|
71
|
+
label = JLabel.new
|
72
|
+
setup_element(label, captured)
|
73
|
+
end
|
74
|
+
cur_x = end_spot
|
75
|
+
end
|
76
|
+
@current_y += @current_line_height
|
77
|
+
end
|
78
|
+
|
79
|
+
# build in realtime LOL
|
80
|
+
@frame.set_size @window_max_x + 25, @current_y + 40
|
81
|
+
rescue
|
82
|
+
puts "Parsing failed on line #{line.inspect} number: #{idx+1}!"
|
83
|
+
raise
|
84
|
+
end
|
85
|
+
}
|
104
86
|
self
|
105
87
|
end
|
106
88
|
|
107
89
|
private
|
108
90
|
|
91
|
+
def closest_next_regex_match options, line
|
92
|
+
mapped = options.map{|option| line =~ option}
|
93
|
+
best = 10000
|
94
|
+
mapped.each{|number| best = [best, number].min if number}
|
95
|
+
if best == 10000
|
96
|
+
return nil
|
97
|
+
else
|
98
|
+
return options[mapped.index(best)] # yikes that was hard
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def handle_button_at_current captured, cur_spot, end_spot, all_lines, idx
|
103
|
+
count_lines_below = 0
|
104
|
+
matching_blank_text_area_string = '[' + ' '*(end_spot-cur_spot) + ']'
|
105
|
+
empty_it_out = matching_blank_text_area_string.gsub(/[\[]/, '_') # can't actually remove it...
|
106
|
+
for line2 in all_lines[idx+1..-1]
|
107
|
+
if line2[cur_spot..(end_spot+1)] == matching_blank_text_area_string
|
108
|
+
line2[cur_spot, end_spot-cur_spot+2] = empty_it_out # :)
|
109
|
+
count_lines_below += 1
|
110
|
+
else
|
111
|
+
break
|
112
|
+
end
|
113
|
+
end
|
114
|
+
text, code_name_with_attrs = split_int_text_name_and_code captured
|
115
|
+
text.strip! # hope we don't care for buttons...for now...
|
116
|
+
if count_lines_below > 0
|
117
|
+
rows = count_lines_below + 1 # at least 2...
|
118
|
+
text_area = JTextArea.new(rows, captured.split(':')[0].length)
|
119
|
+
text_area.text="\n"*rows
|
120
|
+
# width?
|
121
|
+
scrollPane = JScrollPane.new(text_area)
|
122
|
+
setup_element(scrollPane, captured, scrollPane.getPreferredSize.height, text_area)
|
123
|
+
elsif text == "✓"
|
124
|
+
check_box = JCheckBox.new
|
125
|
+
setup_element(check_box, ':' + code_name_with_attrs, nil, check_box, check_box.getPreferredSize.width)
|
126
|
+
elsif text.end_with?("\\/") || text.end_with?("▼") # dropdowns
|
127
|
+
drop_down = JComboBox.new
|
128
|
+
if text.end_with?("▼")
|
129
|
+
initial_value = text = text.gsub(/▼$/, '')
|
130
|
+
else
|
131
|
+
initial_value = text[0..-3].strip
|
132
|
+
end
|
133
|
+
if initial_value.present?
|
134
|
+
drop_down.add_item initial_value.strip # set the top line as default
|
135
|
+
end
|
136
|
+
setup_element drop_down, ':' + code_name_with_attrs, nil, drop_down, drop_down.getPreferredSize.width
|
137
|
+
else
|
138
|
+
# a "normal" button
|
139
|
+
button = JButton.new
|
140
|
+
setup_element(button, captured)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
109
144
|
def get_text_width text
|
110
145
|
get_text_dimentia(text).width
|
111
146
|
end
|
112
147
|
|
113
148
|
def get_text_dimentia text
|
114
|
-
|
149
|
+
font = UIManager.getFont("Label.font")
|
115
150
|
frc = java.awt.font.FontRenderContext.new(font.transform, true, true)
|
116
151
|
textLayout = java.awt.font.TextLayout.new(text, font, frc)
|
117
152
|
textLayout.bounds # has #height and #width
|
118
153
|
end
|
119
154
|
|
120
|
-
def
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
155
|
+
def split_int_text_name_and_code name_and_code
|
156
|
+
if name_and_code.include?(':') && !name_and_code.end_with?(':') # like "Start:start_button" or "start:button:code_name,attribs" but not "Hello:" let that through
|
157
|
+
text = name_and_code.split(':')[0..-2].join(':') # only accept last colon, so they can have text with colons in it
|
158
|
+
code_name_with_attrs = name_and_code.split(':')[-1]
|
159
|
+
[text, code_name_with_attrs]
|
160
|
+
else
|
161
|
+
[name_and_code, nil]
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def setup_element element, name_and_code, height=nil, set_text_on_this = element, width=nil
|
166
|
+
abs_x = nil
|
167
|
+
abs_y = nil
|
168
|
+
text, code_name_with_attrs = split_int_text_name_and_code name_and_code
|
169
|
+
if code_name_with_attrs
|
170
|
+
# extract attributes
|
171
|
+
if code_name_with_attrs.split(',')[0] !~ /=/
|
172
|
+
# then like code_name,width=250,x=y
|
173
|
+
code_name, *attributes = code_name_with_attrs.split(',')
|
174
|
+
else
|
175
|
+
code_name = nil
|
176
|
+
attributes = code_name_with_attrs.split(',')
|
177
|
+
end
|
178
|
+
attributes_hashed = {}
|
179
|
+
attributes.each{|attr|
|
180
|
+
key, value = attr.split('=')
|
181
|
+
attributes_hashed[key.strip] = value.strip
|
182
|
+
}
|
183
|
+
if type = attributes_hashed.delete('font')
|
184
|
+
if type == "fixed_width"
|
185
|
+
set_text_on_this.font = Font.new("Monospaced", Font::PLAIN, 14)
|
186
|
+
else
|
187
|
+
raise "all we support is fixed_width font as of yet #{type} #{name_and_code}"
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
for name2 in ['abs_x', 'abs_y', 'width', 'height']
|
192
|
+
var = attributes_hashed.delete(name2)
|
193
|
+
if var
|
194
|
+
if var =~ /chars$/
|
195
|
+
count = var[0..-5].to_i
|
196
|
+
var = get_text_width('m'*count) # TODO fails for height 30chars
|
197
|
+
elsif var =~ /px$/
|
198
|
+
var = var[0..-3].to_i
|
199
|
+
else
|
200
|
+
var = var.to_i # allow it to be clean :P
|
201
|
+
end
|
202
|
+
raise "#{var} has value of zero?" if var == 0
|
203
|
+
eval("#{name2} = #{var}") # ugh
|
204
|
+
end
|
205
|
+
end
|
206
|
+
raise "unknown attributes found: #{attributes_hashed.keys.inspect} #{attributes_hashed.inspect} #{code_name} #{name_and_code}" if attributes_hashed.length > 0
|
207
|
+
end
|
208
|
+
if !width
|
209
|
+
if text.blank?
|
210
|
+
raise 'cannot have blank original text without width specifier:' + name
|
211
|
+
end
|
212
|
+
if text.strip != text
|
213
|
+
# let blank space count as "space" for now, but don't actually set it LOL
|
214
|
+
# is this good for variable spaced fonts, though?
|
215
|
+
width = get_text_width("|" + text + "|") + 35
|
216
|
+
text.strip!
|
217
|
+
else
|
179
218
|
width = get_text_width(text) + 35
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
219
|
+
end
|
220
|
+
end
|
221
|
+
set_text_on_this.text=text if text.present?
|
222
|
+
abs_x ||= @current_x
|
223
|
+
abs_y ||= @current_y
|
224
|
+
height ||= 20
|
225
|
+
element.set_bounds(abs_x, abs_y, width, height)
|
187
226
|
@frame.panel.add element
|
188
|
-
|
189
|
-
|
190
|
-
|
227
|
+
if code_name
|
228
|
+
code_name.strip!
|
229
|
+
raise "double name not allowed #{name} #{code_name}" if @frame.elements[code_name.to_sym]
|
191
230
|
@frame.elements[code_name.to_sym] = set_text_on_this # just symbol access for now...
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
231
|
+
end
|
232
|
+
@current_x = [@current_x, abs_x + width + 5].max
|
233
|
+
@current_line_height = [@current_line_height, (abs_y + height + 5)-@current_y].max # LODO + 5 magic number? 25 - 20 hard coded? hmm...
|
234
|
+
|
235
|
+
@window_max_x = [@window_max_x, @current_x].max # have to track x, but not y
|
197
236
|
end
|
198
237
|
|
199
238
|
end
|
200
239
|
|
201
240
|
private
|
202
241
|
def _dgb
|
203
|
-
|
204
|
-
|
205
|
-
|
242
|
+
require 'rubygems'
|
243
|
+
require 'ruby-debug'
|
244
|
+
debugger
|
206
245
|
end
|
207
246
|
|
208
247
|
end
|
@@ -6,7 +6,7 @@ module SimpleGuiCreator
|
|
6
6
|
include_package 'javax.swing'
|
7
7
|
# and use these constants (bug: http://jira.codehaus.org/browse/JRUBY-5107)
|
8
8
|
[JProgressBar, JButton, JLabel, JPanel, JOptionPane,
|
9
|
-
JFileChooser, JComboBox, JDialog, SwingUtilities, JSlider, JPasswordField, UIManager]
|
9
|
+
JFileChooser, JComboBox, JDialog, SwingUtilities, JSlider, JPasswordField, JCheckBox, UIManager]
|
10
10
|
|
11
11
|
include_package 'java.awt'; [Font, FileDialog]
|
12
12
|
|
@@ -174,6 +174,19 @@ module SimpleGuiCreator
|
|
174
174
|
alias close dispose # yikes
|
175
175
|
end
|
176
176
|
|
177
|
+
class JComboBox
|
178
|
+
def on_select_new_element &block
|
179
|
+
add_item_listener { |e|
|
180
|
+
block.call(get_item_at(get_selected_index), get_selected_index)
|
181
|
+
}
|
182
|
+
end
|
183
|
+
|
184
|
+
def add_items items
|
185
|
+
for item in items
|
186
|
+
add_item item
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
177
190
|
|
178
191
|
class DropDownSelector < JDialog # JDialog is blocking...
|
179
192
|
|
@@ -193,8 +206,8 @@ module SimpleGuiCreator
|
|
193
206
|
end
|
194
207
|
|
195
208
|
box.add_item @prompt = prompt_for_top_entry # put something in index 0
|
196
|
-
options_array.each{|
|
197
|
-
box.add_item
|
209
|
+
options_array.each{|option|
|
210
|
+
box.add_item option
|
198
211
|
}
|
199
212
|
add box
|
200
213
|
pack # how do you get this arbitrary size? what the...
|
@@ -218,4 +231,23 @@ module SimpleGuiCreator
|
|
218
231
|
|
219
232
|
end
|
220
233
|
|
221
|
-
|
234
|
+
class JCheckBox
|
235
|
+
|
236
|
+
def after_checked &block
|
237
|
+
add_action_listener {
|
238
|
+
if isSelected # they just 'added' a check mark
|
239
|
+
block.call
|
240
|
+
end
|
241
|
+
}
|
242
|
+
end
|
243
|
+
|
244
|
+
def after_unchecked &block
|
245
|
+
add_action_listener {
|
246
|
+
if !isSelected # they just "unchecked" it
|
247
|
+
block.call
|
248
|
+
end
|
249
|
+
}
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
end
|
data/license.mit.txt
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) <year> <copyright holders>
|
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 deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
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 THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/spec/parse_template.spec.rb
CHANGED
@@ -1,25 +1,26 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
require File.dirname(__FILE__)+ '/common'
|
2
3
|
|
3
|
-
describe ParseTemplate do
|
4
|
+
describe SimpleGuiCreator::ParseTemplate do
|
4
5
|
|
5
6
|
def parse_string string
|
6
7
|
@frame = SimpleGuiCreator::ParseTemplate.new
|
7
|
-
|
8
|
-
|
8
|
+
@frame.parse_setup_string(string)
|
9
|
+
@frame
|
9
10
|
end
|
10
11
|
|
11
12
|
after do
|
12
13
|
@frame.close if @frame
|
13
14
|
end
|
14
15
|
|
15
|
-
it "should parse
|
16
|
+
it "should parse title lines" do
|
16
17
|
frame = parse_string " ------------A Title-------------------------------------------"
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
18
|
+
frame.title.should == "A Title"
|
19
|
+
frame.get_title.should == "A Title" # java method mapping :)
|
20
|
+
frame.original_title.should == "A Title"
|
21
|
+
frame.title= 'new title'
|
22
|
+
frame.get_title.should == "new title"
|
23
|
+
frame.original_title.should == "A Title"
|
23
24
|
end
|
24
25
|
|
25
26
|
it "should parse button only lines, with several buttons same line" do
|
@@ -39,14 +40,23 @@ describe ParseTemplate do
|
|
39
40
|
frame.get_size.width.should be > 0
|
40
41
|
end
|
41
42
|
|
42
|
-
|
43
|
-
|
44
|
-
|
43
|
+
it "should parse drop down lines" do
|
44
|
+
for string in ["[some dropdown lines \\/:dropdown_name]", "[some dropdown lines \\/ : dropdown_name]", "[some dropdown lines\\/: dropdown_name]", "[some dropdown lines ▼ : dropdown_name]"]
|
45
|
+
frame = parse_string string
|
46
|
+
frame.elements[:dropdown_name].class.should == Java::JavaxSwing::JComboBox
|
47
|
+
frame.elements[:dropdown_name].add_items ['a', 'b', 'c']
|
48
|
+
frame.elements[:dropdown_name].get_item_at(0).should == 'some dropdown lines'
|
49
|
+
frame.elements[:dropdown_name].on_select_new_element{|element, idx| p element, idx} # seems to work...
|
50
|
+
frame.close
|
51
|
+
end
|
52
|
+
frame = parse_string "[\\/:dropdown_name]"
|
53
|
+
frame.elements[:dropdown_name].get_item_at(0).should be_nil
|
54
|
+
end
|
45
55
|
|
46
56
|
it "should parse text strings" do
|
47
57
|
frame = parse_string '| "Temp Dir location:temp_dir" |'
|
48
|
-
|
49
|
-
|
58
|
+
assert frame.elements.length == 1
|
59
|
+
frame.elements[:temp_dir].should_not be nil
|
50
60
|
end
|
51
61
|
|
52
62
|
it "should handle a string below buttons" do
|
@@ -61,7 +71,7 @@ describe ParseTemplate do
|
|
61
71
|
|
62
72
|
it "should split escaped colons" do
|
63
73
|
frame = parse_string "| \"some stuff ::my_name\""
|
64
|
-
|
74
|
+
frame.elements[:my_name].text.should == 'some stuff :'
|
65
75
|
end
|
66
76
|
|
67
77
|
it "should not accept zero length strings without width spec" do
|
@@ -70,7 +80,7 @@ describe ParseTemplate do
|
|
70
80
|
|
71
81
|
it "should accept zero length strings if they have a width spec" do
|
72
82
|
frame = parse_string "| \":my_name,width=250\""
|
73
|
-
|
83
|
+
frame.elements[:my_name].text.should == ''
|
74
84
|
end
|
75
85
|
|
76
86
|
it "should not add codeless items to elements" do
|
@@ -82,16 +92,20 @@ describe ParseTemplate do
|
|
82
92
|
proc { parse_string " \" text:name,fake=fake \" "}.should raise_exception
|
83
93
|
end
|
84
94
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
95
|
+
it "should allow mixed on the same line" do
|
96
|
+
frame = parse_string %! "text:text", [button:button] !
|
97
|
+
frame.elements.size.should == 2
|
98
|
+
frame.elements[:button].should_not be_nil
|
99
|
+
frame.elements[:text].should_not be_nil
|
100
|
+
end
|
101
|
+
|
102
|
+
# LODO gets h,w of trivial text areas *wrong* oh so wrong
|
103
|
+
# TODO you can line up stuff to get start coords for everything
|
104
|
+
# end sizes
|
92
105
|
# allow prepropagation of textareas, for easier width detection...and/or separating out concerns...hmm...
|
106
|
+
# YAML-possible for the layout, in a separate file. Then it's still semi-separated LOL
|
93
107
|
# parse_setup_string string, :text_area_to_use_text => string
|
94
|
-
#
|
108
|
+
# Make a GUI editor for editing YAML
|
95
109
|
|
96
110
|
it "should accept height, width, abs_x, abs_y" do
|
97
111
|
frame = parse_string ' [a:my_name,abs_x=1,abs_y=2,width=100,height=101] '
|
@@ -152,7 +166,15 @@ describe ParseTemplate do
|
|
152
166
|
frame = parse_string string
|
153
167
|
frame.elements[:text_area].class.should == Java::JavaxSwing::JTextArea
|
154
168
|
frame.elements.length.should == 1 # not create fake empty buttons underneath :)
|
155
|
-
|
169
|
+
text_area_dimentia(frame.elements[:text_area]).should == [0, 0, 32, 319] # it's "sub-contained" in a jscrollpane so these numbers are relative to that <sigh>
|
170
|
+
end
|
171
|
+
|
172
|
+
def text_area_dimentia(element)
|
173
|
+
while(get_dimentia(element) == [0,0,0,0])
|
174
|
+
puts 'weird TextArea 0,0,0,0'
|
175
|
+
sleep 0.2
|
176
|
+
end
|
177
|
+
get_dimentia(element)
|
156
178
|
end
|
157
179
|
|
158
180
|
it "should let you use ending colons" do
|
@@ -166,7 +188,19 @@ describe ParseTemplate do
|
|
166
188
|
get_dimentia(frame.elements[:text])[3].should == 200
|
167
189
|
end
|
168
190
|
|
169
|
-
it "should
|
191
|
+
it "should allow for non symbol names" do
|
192
|
+
frame = parse_string "[button : button]"
|
193
|
+
frame.elements[:button].should_not be_nil
|
194
|
+
end
|
195
|
+
|
196
|
+
it "should parse text areas that aren't first, also one right next to it both sides" do
|
197
|
+
frame = parse_string <<-EOL
|
198
|
+
[button : button][textare : textarea]
|
199
|
+
[ ]
|
200
|
+
EOL
|
201
|
+
text_area_dimentia(frame.elements[:textarea]).should == [0, 0, 32, 88]
|
202
|
+
|
203
|
+
end
|
170
204
|
|
171
205
|
it "should allow for blank lines to mean spacing" do
|
172
206
|
frame = parse_string "| [ a button] |\n [ a button] \n[ a button]"
|
@@ -175,5 +209,16 @@ describe ParseTemplate do
|
|
175
209
|
frame = parse_string "| [ a button] |\n [ a button] \n[ a button]\n| |"
|
176
210
|
frame.size.height.should == 150
|
177
211
|
end
|
212
|
+
|
213
|
+
it "should allow for checkboxes" do
|
214
|
+
for string in ["[✓:checkbox_name]", "[✓ : checkbox_name]", "[_:checkbox_name"] # UTF-8 <sigh>
|
215
|
+
f = parse_string string
|
216
|
+
f.elements[:checkbox_name].get_text.should == ""
|
217
|
+
f.elements[:checkbox_name].class.should == Java::JavaxSwing::JCheckBox
|
218
|
+
f.elements[:checkbox_name].after_checked { puts 'checked!' }
|
219
|
+
f.elements[:checkbox_name].after_unchecked { puts 'unchecked!' }
|
220
|
+
f.close
|
221
|
+
end
|
222
|
+
end
|
178
223
|
|
179
224
|
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: simple_gui_creator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.
|
5
|
+
version: 0.2.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- rogerdpack
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2012-06-
|
13
|
+
date: 2012-06-29 00:00:00 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: sane
|
@@ -24,7 +24,7 @@ dependencies:
|
|
24
24
|
type: :runtime
|
25
25
|
version_requirements: *id001
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
|
-
name:
|
27
|
+
name: andand
|
28
28
|
prerelease: false
|
29
29
|
requirement: &id002 !ruby/object:Gem::Requirement
|
30
30
|
none: false
|
@@ -34,6 +34,28 @@ dependencies:
|
|
34
34
|
version: "0"
|
35
35
|
type: :runtime
|
36
36
|
version_requirements: *id002
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: sane
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: "0"
|
46
|
+
type: :runtime
|
47
|
+
version_requirements: *id003
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: andand
|
50
|
+
prerelease: false
|
51
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: "0"
|
57
|
+
type: :runtime
|
58
|
+
version_requirements: *id004
|
37
59
|
description: Framework to ease in creation of ruby GUI apps. Makes designing windows a snap.
|
38
60
|
email: rogerdpack@gmail.com
|
39
61
|
executables:
|
@@ -44,6 +66,7 @@ extra_rdoc_files:
|
|
44
66
|
- ChangeLog
|
45
67
|
- README
|
46
68
|
- TODO
|
69
|
+
- license.mit.txt
|
47
70
|
files:
|
48
71
|
- ChangeLog
|
49
72
|
- README
|
@@ -64,6 +87,7 @@ files:
|
|
64
87
|
- lib/simple_gui_creator/simple_gui_creator.rb
|
65
88
|
- lib/simple_gui_creator/storage.rb
|
66
89
|
- lib/simple_gui_creator/swing_helpers.rb
|
90
|
+
- license.mit.txt
|
67
91
|
- spec/common.rb
|
68
92
|
- spec/diesel.mp3
|
69
93
|
- spec/drive_info.spec.rb
|