(These examples require a Shoes that’s no older than a week or two.)
On Sun, Jun 08, 2008 at 08:29:55AM +0900, Martin DeMello wrote:
- A component consisting of a series of existing components hooked
together to act as a single widget
In Shoes, you inherit from the Widget class and you can paint
your own custom widgets or combine pre-existing ones.
Like, for instance, Shoes doesn’t come with a file input field like
HTML does. But you can combine an edit_line
and a button
.
class Browse < Widget
def initialize
@name = edit_line
@find =
button(“Browse…”).click do
@name.text = ask_open_file
end
end
def filename
@name.text
end
end
Shoes.app do
browse
end
When you inherit from Widget, you get a free lowercased method added
to Shoes for inserting that widget into any “stack” or “flow” (like
HTML divs, basically.)
And since the widget itself is a “flow”, you can move the whole widget
as a single component. So you can call move(x, y)
or show
or
hide
on the object returned by the browse
method.
- A component built ‘from scratch’ atop a canvas, that is, handling
its own drawing and event management
Too easy, Shoes has painting nailed. I’ve worked hard to get things
on par with Processing. See the samples
directory for a clock
widget and a calculator and a dictionary and you’ll find a lot more
out on the web.
- A component combining a canvas and existing widgets
So a combo of #1 and #2? Well, okay, so taking the Browse
example
from earlier, we could add a background and a border. This just
illustrates that all widgets are drawn on a canvas anyway.
class Browse < Widget
def initialize
flow do
background “#09F”
border “#FFF”
@name = edit_line
@find =
button(“Browse…”).click do
@name.text = ask_open_file
end
end
end
end
- A container that takes a collection of widgets and lays them out
according to some userdefined algorithm
I’ll have to think about this one. Shoes already has a ‘wrapbox’
called a flow
, but I can see why you’d want to lay things out
yourself.
Okay, let’s see. So let’s try a cascade
layout that positions
everything diagonally from the element previous to it.
class Cascade < Widget
def initialize &blk
instance_eval &blk
end
def draw(a,b)
x, y = 0, 0
contents.each do |e|
if x != e.left && y != e.top
e.move x, y
end
x += e.height
y += e.width
end
super(a,b)
end
end
Shoes.app do
cascade do
button “1”
button “2”
button “3”
end
end
The initialize
part works because every widget is just a canvas
anyway.
And the draw
method is called every time there’s a repaint.
That’s a bit clunky. But proves that it can be done.
Examples (more welcomed):
- An icon widget, that combines a picture and a textfield
underneath, with config options to turn either off or size the image,
make the text editable, etc
- A speedometer-type dial with a configurable range and tick interval
- A box that holds a component and paints a customised border around it
- A pure-ruby implementation of a wrapbox
(http://zem.novylen.net/ruby/wrapboxdemo.png)
I’ll work on some really nice answers to these. This is a fantastic
exercise.
_why