profligacy 0.4.1-jruby → 1.0-jruby
Sign up to get free protection for your applications and to get access to all the features.
- data/README +2 -5
- data/Rakefile +10 -4
- data/examples/layout_test.rb +0 -1
- data/examples/utu_main.rb +2 -2
- data/lib/profligacy/lel.rb +48 -2
- data/lib/profligacy/parser.jar +0 -0
- data/lib/profligacy/swing.rb +93 -18
- metadata +2 -2
data/README
CHANGED
@@ -8,10 +8,6 @@ The project is hosted at:
|
|
8
8
|
|
9
9
|
Where you can file bugs and other things, as well as download gems manually.
|
10
10
|
|
11
|
-
== Motivation
|
12
|
-
|
13
|
-
== Installing
|
14
|
-
|
15
11
|
== License
|
16
12
|
|
17
13
|
See the COPYING file included with the source. It's the same license as Ruby but
|
@@ -19,4 +15,5 @@ Copyright Zed A. Shaw 2007 All Right Reserved.
|
|
19
15
|
|
20
16
|
== Source Code
|
21
17
|
|
22
|
-
Source is available from the ihate project page.
|
18
|
+
Source is available from the ihate project page in the Files section as a .tgz
|
19
|
+
source tarball.
|
data/Rakefile
CHANGED
@@ -13,9 +13,9 @@ setup_clean ["pkg", "lib/*.bundle", "lib/profligacy/*.class", "*.gem", ".config"
|
|
13
13
|
setup_rdoc ['README', 'LICENSE', 'COPYING', 'lib/**/*.rb', 'doc/**/*.rdoc']
|
14
14
|
|
15
15
|
desc "Does a full compile, test run"
|
16
|
-
task :default => [
|
16
|
+
task :default => ["lib/profligacy/parser.jar", :test]
|
17
17
|
|
18
|
-
version="0
|
18
|
+
version="1.0"
|
19
19
|
name="profligacy"
|
20
20
|
|
21
21
|
setup_gem(name, version) do |spec|
|
@@ -30,9 +30,15 @@ setup_gem(name, version) do |spec|
|
|
30
30
|
spec.platform = "jruby"
|
31
31
|
end
|
32
32
|
|
33
|
-
|
33
|
+
file "lib/profligacy/LELParser.java" => ["lib/profligacy/LELParser.rl"] do
|
34
34
|
sh %{/usr/local/bin/ragel -J lib/profligacy/LELParser.rl | /usr/local/bin/rlgen-java -o lib/profligacy/LELParser.java}
|
35
|
+
end
|
36
|
+
|
37
|
+
file "lib/profligacy/LELParser.class" => ["lib/profligacy/LELParser.java"] do
|
35
38
|
sh %{javac -target 1.5 lib/profligacy/LELParser.java}
|
39
|
+
end
|
40
|
+
|
41
|
+
file "lib/profligacy/parser.jar" => ["lib/profligacy/LELParser.class"] do
|
36
42
|
Dir.chdir("lib") do
|
37
43
|
sh %{jar -cf profligacy/parser.jar profligacy/*.class}
|
38
44
|
end
|
@@ -51,4 +57,4 @@ task :site do
|
|
51
57
|
sh %{rsync -av doc/rdoc doc/site/output/* rubyforge.org:/var/www/gforge-projects/ihate/profligacy}
|
52
58
|
end
|
53
59
|
|
54
|
-
task :project => [:clean, :
|
60
|
+
task :project => [:clean, :default, :test, :rdoc, :package, :site]
|
data/examples/layout_test.rb
CHANGED
data/examples/utu_main.rb
CHANGED
@@ -9,7 +9,6 @@ require 'profligacy/lel'
|
|
9
9
|
class ChatInterface
|
10
10
|
include_package 'javax.swing'
|
11
11
|
include_package 'java.awt'
|
12
|
-
|
13
12
|
include Profligacy
|
14
13
|
|
15
14
|
def initialize
|
@@ -110,4 +109,5 @@ class ChatInterface
|
|
110
109
|
end
|
111
110
|
end
|
112
111
|
|
113
|
-
|
112
|
+
|
113
|
+
SwingUtilities.invoke_later proc { ChatInterface.new }.to_runnable
|
data/lib/profligacy/lel.rb
CHANGED
@@ -1,9 +1,14 @@
|
|
1
|
+
require 'profligacy/swing'
|
1
2
|
require 'profligacy/parser'
|
2
3
|
require 'profligacy/swing-layout'
|
3
4
|
|
4
5
|
module Profligacy
|
5
6
|
include_package 'profligacy'
|
6
7
|
|
8
|
+
# This is passed to the parser to scan over the LEL expression
|
9
|
+
# initially and pull out the list of cell names that are going
|
10
|
+
# to be used. The result of this scan is then used to create
|
11
|
+
# the contents and interactions structs.
|
7
12
|
class LELNameScanner
|
8
13
|
include Profligacy::LELParser::LELEventListener
|
9
14
|
attr_reader :children
|
@@ -21,12 +26,16 @@ module Profligacy
|
|
21
26
|
end
|
22
27
|
end
|
23
28
|
|
29
|
+
# Used by Swing::Build as the listener for LELParser which configures
|
30
|
+
# a GroupLayout from the LEL expression. See http://ihate.rubyforge.org/profligacy/lel.html
|
31
|
+
# for instructions on how to use LEL.
|
24
32
|
class LELGroupLayout
|
25
33
|
include Profligacy::LELParser::LELEventListener
|
26
34
|
|
27
35
|
include_package 'org.jdesktop.layout'
|
28
36
|
include_class 'java.lang.Short'
|
29
37
|
|
38
|
+
# Takes a GroupLayout and the list of components to organize inside it.
|
30
39
|
def initialize(layout, components)
|
31
40
|
@layout = layout
|
32
41
|
@hgroup = @layout.createSequentialGroup
|
@@ -41,15 +50,18 @@ module Profligacy
|
|
41
50
|
horizontals_reset
|
42
51
|
end
|
43
52
|
|
53
|
+
# Called when a '|' token is hit by the LELParser.
|
44
54
|
def col
|
45
55
|
horizontals_push; alignments_reset ; widths_reset
|
46
56
|
end
|
47
57
|
|
58
|
+
# Called when a '[' token is hit by the LELParser.
|
48
59
|
def ltab
|
49
60
|
horizontals_reset
|
50
61
|
@vertical = @layout.createBaselineGroup(true, false)
|
51
62
|
end
|
52
63
|
|
64
|
+
# Called when a '^' or '.' token is hit by the LELParser.
|
53
65
|
def valign(dir)
|
54
66
|
case dir
|
55
67
|
when "^" then @valign = GroupLayout::LEADING
|
@@ -59,6 +71,7 @@ module Profligacy
|
|
59
71
|
end
|
60
72
|
end
|
61
73
|
|
74
|
+
# Called when a name/id for a cell token is hit by the LELParser.
|
62
75
|
def id(name)
|
63
76
|
h = horizontals_cur
|
64
77
|
component = @components[name]
|
@@ -71,11 +84,13 @@ module Profligacy
|
|
71
84
|
end
|
72
85
|
end
|
73
86
|
|
87
|
+
# Called when a ']' token is hit by the LELParser.
|
74
88
|
def row
|
75
89
|
@vgroup.add(@vertical);
|
76
90
|
alignments_reset ; widths_reset ; heights_reset
|
77
91
|
end
|
78
92
|
|
93
|
+
# Called when a '>' or '<' token is hit by the LELParser.
|
79
94
|
def align(direction)
|
80
95
|
case direction
|
81
96
|
when "<" then @halign = GroupLayout::LEADING
|
@@ -85,18 +100,22 @@ module Profligacy
|
|
85
100
|
end
|
86
101
|
end
|
87
102
|
|
103
|
+
# Called when a '(#)' is hit (with # being some number) by the LELParser.
|
88
104
|
def setwidth(width)
|
89
105
|
@width = width
|
90
106
|
end
|
91
107
|
|
108
|
+
# Called when a height is added to '(#,#)' expressions in the LELParser.
|
92
109
|
def setheight(height)
|
93
110
|
@height = height
|
94
111
|
end
|
95
112
|
|
113
|
+
# Called when the cell should expand via a '*' token.
|
96
114
|
def expand
|
97
115
|
@max = Short::MAX_VALUE
|
98
116
|
end
|
99
117
|
|
118
|
+
# Called when it's done parsing, and whether there was an error.
|
100
119
|
def finished(error)
|
101
120
|
if !error
|
102
121
|
@layout.setHorizontalGroup(@hgroup);
|
@@ -106,28 +125,39 @@ module Profligacy
|
|
106
125
|
|
107
126
|
private
|
108
127
|
|
128
|
+
# Resets the vertical and horizontal alignments when the end of a or row is
|
129
|
+
# passed.
|
109
130
|
def alignments_reset
|
110
131
|
@valign = GroupLayout::CENTER
|
111
132
|
@halign = GroupLayout::CENTER
|
112
133
|
end
|
113
134
|
|
135
|
+
# Same as alignments_reset but for the widths.
|
114
136
|
def widths_reset
|
115
137
|
@width = GroupLayout::DEFAULT_SIZE
|
116
138
|
@max = GroupLayout::DEFAULT_SIZE
|
117
139
|
end
|
118
140
|
|
141
|
+
# Resets the heights when the current row ends.
|
119
142
|
def heights_reset
|
120
143
|
@height = GroupLayout::DEFAULT_SIZE
|
121
144
|
end
|
122
145
|
|
146
|
+
# The horizontal cells are organized in a dynamic array that's expanded as
|
147
|
+
# more are encountered. Just like an old typewriter, when the current row
|
148
|
+
# ends the next column to work on is "reset" by starting at the first one.
|
149
|
+
# This method just resets it to be the next one.
|
123
150
|
def horizontals_reset
|
124
151
|
@htop = 0
|
125
152
|
end
|
126
153
|
|
154
|
+
# Adds a new horizontal cell onto the list of cells being worked on.
|
127
155
|
def horizontals_push
|
128
156
|
@htop += 1
|
129
157
|
end
|
130
158
|
|
159
|
+
# Either makes a new horizontal cell for the curent operation to work on,
|
160
|
+
# or just returns the existing one.
|
131
161
|
def horizontals_cur
|
132
162
|
if !@horizontals[@htop]
|
133
163
|
@horizontals[@htop] = @layout.createParallelGroup()
|
@@ -141,12 +171,29 @@ module Profligacy
|
|
141
171
|
|
142
172
|
|
143
173
|
module Swing
|
174
|
+
|
175
|
+
# Layout Expression Language is a small regex like wiki language used to
|
176
|
+
# specify complex layouts in a tiny amount of space. The language is based on
|
177
|
+
# a Ragel based parser that configures a Swing GroupLayout with the right
|
178
|
+
# options to produce the desired effect. An example of LEL is:
|
179
|
+
#
|
180
|
+
#
|
181
|
+
# @layout = "
|
182
|
+
# [ label_1 | label3 ]
|
183
|
+
# [ (300,300)*text1 | (150)people ]
|
184
|
+
# [ <label2 | _ ]
|
185
|
+
# [ .message | ^buttons ]"
|
186
|
+
#
|
187
|
+
# Which will produce a panel where you can place components. Otherwise it works
|
188
|
+
# exactly like Swing::Build except you pass the LEL expression in to the constructor
|
189
|
+
# instead of an array of symbols.
|
190
|
+
#
|
191
|
+
# See http://ihate.rubyforge.org/profligacy/lel.html for more instructions.
|
144
192
|
class LEL < Build
|
145
193
|
include_package 'org.jdesktop.layout'
|
146
194
|
attr_reader :prefs
|
147
195
|
|
148
196
|
def initialize(type, expr)
|
149
|
-
|
150
197
|
@expr = expr
|
151
198
|
@container_class = type
|
152
199
|
@parser = LELParser.new
|
@@ -196,6 +243,5 @@ module Profligacy
|
|
196
243
|
end
|
197
244
|
end
|
198
245
|
end
|
199
|
-
|
200
246
|
end
|
201
247
|
end
|
data/lib/profligacy/parser.jar
CHANGED
Binary file
|
data/lib/profligacy/swing.rb
CHANGED
@@ -2,14 +2,43 @@ require 'java'
|
|
2
2
|
|
3
3
|
import 'javax.swing.SwingUtilities'
|
4
4
|
|
5
|
-
|
5
|
+
|
6
|
+
# Profligacy is a library that helps to build Swing GUIs without getting the
|
7
|
+
# way of the huge number of components and options available. The approach
|
8
|
+
# taken by Profligacy is to be a purposefully leaky abstraction. Rather than
|
9
|
+
# try to cover all the possible configurable options available to Swing, it
|
10
|
+
# simply attempts to solve three problems:
|
11
|
+
#
|
12
|
+
# First, building a swing interface involves mixing the layout construction with the
|
13
|
+
# widget construction. This is solved by a few simple builders named
|
14
|
+
# Swing::Build and Swing::LEL that help organize your components inside a
|
15
|
+
# given layout in a way that looks like Ruby and reduces tons of complexity.
|
16
|
+
#
|
17
|
+
# Second, using any of the more complex layouts like GridBagLayout or GroupLayout is
|
18
|
+
# nasty. This is solved by the Layout Expression Language that uses a wiki syntax
|
19
|
+
# that matches like a regex and builds any configurable GroupLayout you might need.
|
20
|
+
# It removes an *insane* amount of code you'd need to write by hand or the need for
|
21
|
+
# an external tool to configure the layout.
|
22
|
+
#
|
23
|
+
# Writing Java callback Listeners and Runnables is a pain in the ass. This is
|
24
|
+
# solved by an ugly hack where Profligacy generates a bunch of Listener -> Proc
|
25
|
+
# converters for each of the bazillion Listener implementations that SWing loves
|
26
|
+
# even though they all do the same damn thing anyway.
|
27
|
+
#
|
28
|
+
# See http://ihate.rubyforge.org/profligacy/ for more information.
|
29
|
+
#
|
30
|
+
module Profligacy
|
6
31
|
module Swing
|
7
32
|
include_class 'java.lang.Runnable'
|
8
33
|
include_package 'java.awt.event'
|
9
34
|
include_package 'javax.swing.event'
|
10
|
-
include_package 'javax.swing'
|
35
|
+
include_package 'javax.swing'
|
11
36
|
include_package 'java.awt'
|
12
37
|
|
38
|
+
# This is used by the added Proc.to_runnable to make a Runnable
|
39
|
+
# interface that just calls a proc anyway. With this you can
|
40
|
+
# do proc { puts "hi" }.to_runnable and pass the result to
|
41
|
+
# threads and such.
|
13
42
|
class RunnableProc
|
14
43
|
include Runnable
|
15
44
|
def initialize(&block)
|
@@ -22,6 +51,8 @@ module Profligacy
|
|
22
51
|
end
|
23
52
|
|
24
53
|
|
54
|
+
# NOTHING TO SEE HERE. GO AWAY. THIS CODE IS WRONG WRONG WRONG
|
55
|
+
# AND WILL GO AWAY IN THE NEAR FUTURE.
|
25
56
|
module Listeners
|
26
57
|
AWT_LISTENERS = ["Action","Adjustment","AWTEvent","Component","Container","Focus",
|
27
58
|
"HierarchyBounds","Hierarchy","InputMethod","Item","Key","Mouse",
|
@@ -36,24 +67,28 @@ module Profligacy
|
|
36
67
|
"UndoableEdit", ]
|
37
68
|
|
38
69
|
horrid_java_sucks_ass_hack = (SWING_LISTENERS + AWT_LISTENERS).collect do |listener|
|
39
|
-
|
40
|
-
|
41
|
-
include Profligacy::Swing::#{listener}Listener
|
70
|
+
"class #{listener}ListenerProc
|
71
|
+
include Profligacy::Swing::#{listener}Listener
|
42
72
|
|
43
|
-
|
44
|
-
|
45
|
-
|
73
|
+
def initialize(&block)
|
74
|
+
@block = block
|
75
|
+
end
|
46
76
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
END
|
77
|
+
def method_missing(symb, *args)
|
78
|
+
@block.call(symb, *args)
|
79
|
+
end
|
80
|
+
end"
|
52
81
|
end
|
53
82
|
|
54
83
|
module_eval horrid_java_sucks_ass_hack.join("\n")
|
55
84
|
end
|
56
85
|
|
86
|
+
# The Swing::Build class doesn't actually do any swing stuff, but instead
|
87
|
+
# it organizes the common pattern of constructing components, attaching
|
88
|
+
# interaction procs or methods.
|
89
|
+
#
|
90
|
+
# See the many examples and instructions at http://ihate.rubyforge.org/profligacy/
|
91
|
+
# for more information.
|
57
92
|
class Build
|
58
93
|
attr_accessor :children
|
59
94
|
attr_accessor :contents
|
@@ -63,16 +98,40 @@ module Profligacy
|
|
63
98
|
|
64
99
|
include_package 'javax.swing'
|
65
100
|
|
101
|
+
# When you construct a Swing::Build you pass in the container
|
102
|
+
# to use as the first argument, and then symbols for the names
|
103
|
+
# of the contents as the rest of the arguments.
|
104
|
+
#
|
105
|
+
# ui = Swing::Build.new JFrame, :left, :right, :top do |c,i|
|
106
|
+
# ...
|
107
|
+
# end
|
108
|
+
#
|
109
|
+
# The Build class doesn't actually do anything with this until
|
110
|
+
# you call the Swing::Build.build method. It just collects up
|
111
|
+
# the contents and interactions you attach to the c and i parameters
|
112
|
+
# in the block.
|
113
|
+
#
|
114
|
+
# The c and i parameters stand for contents and interactions and are
|
115
|
+
# a Ruby Struct objects that have the names you gave as elements.
|
116
|
+
# This means if you try to set one that doesn't exist you'll get an
|
117
|
+
# error (which is pretty handy).
|
66
118
|
def initialize(*children)
|
67
119
|
@container_class = children.shift
|
68
120
|
setup_children_and_interactions(children)
|
69
121
|
yield @contents, @interactions
|
70
122
|
end
|
71
123
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
124
|
+
# Build will finally build the container you configured, passing any
|
125
|
+
# arguments to that container. When it's done it returns the resulting
|
126
|
+
# container for you to modify.
|
127
|
+
#
|
128
|
+
# You can also attach a block to the method call that will be called
|
129
|
+
# with the container right before it's completed. This lets you modify
|
130
|
+
# it at the right moment. For example, if you need to set some options
|
131
|
+
# to a JFrame right before it's made visible.
|
132
|
+
#
|
133
|
+
# Finally, it's so common to make containers visible and pack them that
|
134
|
+
# this method will do that if the container has those methods.
|
76
135
|
def build(*args)
|
77
136
|
# create the container they ask for with these args
|
78
137
|
@container = @container_class.new *args
|
@@ -102,6 +161,11 @@ module Profligacy
|
|
102
161
|
@container
|
103
162
|
end
|
104
163
|
|
164
|
+
# It's kind of a pain to always access ui.contents.thename so
|
165
|
+
# the method_missing simply lets you do ui.thename. Won't work
|
166
|
+
# of course if your component is named "build", "children",
|
167
|
+
# "contents", "interactions", "layout", "container" since those
|
168
|
+
# exist in the Build object already.
|
105
169
|
def method_missing(symb, *args)
|
106
170
|
@contents[symb]
|
107
171
|
end
|
@@ -135,16 +199,27 @@ module Profligacy
|
|
135
199
|
} if actions
|
136
200
|
end
|
137
201
|
end
|
202
|
+
|
203
|
+
|
138
204
|
end
|
139
205
|
end
|
140
206
|
|
207
|
+
# Modifications to Proc to make the Runnable and Listener conversion easy.
|
141
208
|
class Proc
|
209
|
+
# Takes this proc and converts it to an ListenerProc based on the action name.
|
210
|
+
# The name should be one in Profligacy::Listeners and is based on the add_blah_listener
|
211
|
+
# method on that component. So, if you need a ChangeListener you do:
|
212
|
+
#
|
213
|
+
# proc {|t,e| puts t }.to_listener(:change)
|
214
|
+
#
|
215
|
+
# The two parameters are a symbol for the method that Java called on this, and then the
|
216
|
+
# event argument.
|
142
217
|
def to_listener(action)
|
143
218
|
Profligacy::Swing::Listeners.const_get("#{action.to_s.capitalize}ListenerProc").new &self
|
144
219
|
end
|
145
220
|
|
221
|
+
# Converts this Proc to a RunnableProc which implements the Runnable interface.
|
146
222
|
def to_runnable
|
147
223
|
Profligacy::Swing::RunnableProc.new &self
|
148
224
|
end
|
149
225
|
end
|
150
|
-
|
metadata
CHANGED
@@ -53,11 +53,11 @@ requirements: []
|
|
53
53
|
authors:
|
54
54
|
- Zed A. Shaw
|
55
55
|
platform: jruby
|
56
|
-
date: 2007-07-
|
56
|
+
date: 2007-07-11 04:00:00 +00:00
|
57
57
|
require_paths:
|
58
58
|
- lib
|
59
59
|
version: !ruby/object:Gem::Version
|
60
|
-
version: 0
|
60
|
+
version: !str 1.0
|
61
61
|
test_files: []
|
62
62
|
bindir: bin
|
63
63
|
dependencies: []
|