simple_gui_creator 0.1.4 → 0.2.0
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/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
|