I am experimenting with JRuby/SWT/Mozilla/JavaXPComm. It is a lot of
fun, but one of the things that has bothered me a bit is how to create
listeners for UI events. I want to specify listeners for events using
blocks and not have to create ruby classes for every listener
interface or listener adapter. Are there any helper libraries out
there for constructing GUI’s from jruby and/or are there any documents
on integration patterns for jruby?
The rest of this email describes 2 approaches I used for implementing
callbacks/listeners.
I am using an org.eclipse.swt.browser.Browser object, which has an
addProgressListener method. There is a ProgressListener interface and
a ProgressAdapter which has empty methods for the ProgressListener
methods. This is similar to various swing listeners. When the
browser has completed loading some document, I want to run some code.
(It might do things like find html elements and attach ruby event
handlers to them…)
I can create a ruby subclass of ProgressAdapter like so:
class OnCompleted < Java.org.eclipse.swt.browser.ProgressAdapter
def initialize(&block)
@block = block
end
def completed(event)
@block.call(event)
end
end
In my application class, I can then use the OnCompleted ruby class to
register a block of code like this:
@browser.add_progress_listener(OnCompleted.new do |event|
on_completed
end)
This works, but feels so clunky. There are many different types of
listeners in SWT and I’d like a dynamic way to create these inline,
similar to java’s anonymous classes. I’d like to supply a hash of
blocks to implement an interface or subclass an abstract class.
Here is an example of subclassing ProgressAdapter with method
overrides given as a hash of blocks.
@browser.add_progress_listener(ProgressAdapter.java_callback(
:completed => lambda { |obj,event| on_completed }
))
I implemented this by adding a method, java_callback, to Module.
class Module
def java_callback(methods={})
if self.respond_to? :java_class and self.java_class.interface?
a_class = Class.new
a_class.send :include, self
else
a_class = Class.new self
end
methods.each do |mname, block|
a_class.send(:define_method, mname) do |*args|
block.call(self,*args)
end
end
a_class.new
end
end
This is working for me right now, but I am not creating many listener
instances yet. I imagine that creating dynamic classes would
eventually become a memory problem, since I doubt that classes are
garbage collected in Ruby.
I then tried to get around the memory issue, by caching a single
dynamically created subclass per listener interface/adapter. I was
then hoping to override methods on the object instance of the subclass
after it was constructed. This can be accomplished through the method
instance_eval. However, these object instance methods did not seem
visible to java.
Anyway, I’m hoping somebody out there had some clever, yet clean ways
of implementing listener interfaces.
Thanks.
To unsubscribe from this list, please visit:
http://xircles.codehaus.org/manage_email