I am not in any way a fan of desktop GUI toolkits - HTML5 and jQuery totally spoiled me, so I resisted for a very long time making GUIs for my Total War tools - and happily enough, other people would sometimes make them for me.
But this time I decided to make a desktop GUI, in JRuby, and that means one of the awful non-HTML toolkits.
So my first idea was of course making a big window with a menu calling some functions, and big embedded HTML form with all stuff in HTML. I was even getting somewhere since Java Swing has HTML widget, but then it turned out it's HTML 3.2 only, no Javascript whatsoever, and serious pain to get data into and out of it.
I also tried SWT, and hoped danlucraft's cookbook would help me get somewhere with it, but I couldn't figure out most of the things I wanted to try, so I kept looking.
Finally I found this lovely cheri gem, which didn't seem to such too hard. I've heard mostly horrible things about Swing API, but it was only as bad as the rumor says, at least for my simple use case.
I'll put all that code for public view eventually, but it's pretty massive, so here are just some tips for working with Swing and cheri.
Basic window creation
Start a class and include Cheri::Swing module. What you probably want to match HTML-ish behaviour is actually not a single layout manager but GridBagLayout (for actual layout) within ScrollPane (so you get scrollbars when content).That's the code:
class ConcentratedVanillaBuilder
include Cheri::Swing
def initialize
@controls = {}
@frame = swing.frame('Concentrated Vanilla builder'){ |frm|
size 800, 800
default_close_operation :EXIT_ON_CLOSE
build_menu!
scroll_pane {
panel {
grid_bag_layout
grid_table {
background :WHITE
build_form!
}
}
}
}
load_settings! load_settings_file("settings/default.txt")
@frame.visible = true
end
end
This initialization is pretty generic (other than trivial matters of default window size and title), other than four italicized lines.
Separate form buildings from settings
That's advice for GUIs that simply configure some settings and then run some script. You want to keep your settings in a nice Hash, and don't mix GUI code with settings defaults.
So what you want are helper methods like these:
def checkbox(name, description)
grid_row{
@controls["checkbox-#{name}"] = swing.check_box description, :a => :w, :gridwidth => 3
}
end
And then use methods on @controls[something] to both get and set various fields. That's far easier than ton of on_change callbacks or whatever is their Swing equivalent.
Use text_area not label for labels
Label widgets are pretty dumb, and non-editable text areas can handle things like multiline text and formatting a lot better.
Just add some helper methods and pretend you're coding HTML:
def div_helpmsg(msg)
grid_row{
text_area(:a => :w, :gridwidth => 3){
editable false
text msg.gsub(/^\s+/, "")
}
}
end
def h1(msg)
font = java.awt.Font.new('Dialog', java.awt.Font::BOLD, 24)
grid_row{
text_area(:a => :w, :gridwidth => 3){
set_font font
editable false
text msg
}
}
end
If things get too complicated, you can always go for full HTML widgets.Result
Half-finished result looks something like this. Not amazing, but it will do the trick.It will get released sometime soon, and then you'll be able to play random scenarios everybody's waiting for.
By the way if any Java / JRuby experts has better ideas, go ahead. Googling was unusually unhelpful to me here, and IRC and StackOverflow were as useless as they always are.
Any update on this Tomasz? I was considering this approach but cheri seemed dead and I am a bit hesitant to start on it.
ReplyDeleteI just wish we had a good GUI & builder that was actually current and developed ;)
Kevin: The underlying Java APIs (swing/awt) aren't really evolving much, so I don't think it's a huge deal that cheri doesn't see much development these days.
ReplyDeleteYou can always drop down to low level APIs if cheri doesn't provide something, or fork it on github and make your own version.