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 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 => [:ragel, :test]
16
+ task :default => ["lib/profligacy/parser.jar", :test]
17
17
 
18
- version="0.4.1"
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
- task :ragel do
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, :ragel, :default, :test, :rdoc, :package, :site]
60
+ task :project => [:clean, :default, :test, :rdoc, :package, :site]
@@ -1,4 +1,3 @@
1
- require 'profligacy/swing'
2
1
  require 'profligacy/lel'
3
2
 
4
3
  module Test
@@ -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
- ChatInterface.new
112
+
113
+ SwingUtilities.invoke_later proc { ChatInterface.new }.to_runnable
@@ -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
Binary file
@@ -2,14 +2,43 @@ require 'java'
2
2
 
3
3
  import 'javax.swing.SwingUtilities'
4
4
 
5
- module Profligacy
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
- <<-END
40
- class #{listener}ListenerProc
41
- include Profligacy::Swing::#{listener}Listener
70
+ "class #{listener}ListenerProc
71
+ include Profligacy::Swing::#{listener}Listener
42
72
 
43
- def initialize(&block)
44
- @block = block
45
- end
73
+ def initialize(&block)
74
+ @block = block
75
+ end
46
76
 
47
- def method_missing(symb, *args)
48
- @block.call(symb, *args)
49
- end
50
- end
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
- def interactions
73
- yield @contents, @interactions
74
- end
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-09 04:00:00 +00:00
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.4.1
60
+ version: !str 1.0
61
61
  test_files: []
62
62
  bindir: bin
63
63
  dependencies: []