profligacy 0.4.1-jruby → 1.0-jruby
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/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: []
|