| Home | « Prev Contents Next » | Download |
| JRBuilder: Binding Demo 4 | ||
| Even more on Binding: Translators and Converters. Unit and Type. |
|
|
This example introduces translators, along
with the special attributes
unit and
type. It also
discusses converters, but does not demonstrate
them.
In the binding examples up to this point, properties that have been bound in a given binding have had the same data type. In the case of numeric values, they've also had the same unit of measure: the value 17 on one slider corresponded to the value 17 on any other; no translation (or mapping) was required. But that's not always the case.
The Bindery lets you define converters and translators to
connect properties that differ in their data types and in
their nature. In Bindery terms, translators operate on
what I've called unit (I'm open to a better term),
translating, or mapping, between values that are dissimilar
in some way. On the other hand, (again in Bindery terms),
converters operate on values that are the same except for
data type; for example, converting from
This (admittedly contrived) example shows the use of
translators to translate between
You can see from the prototypes that each defines unit and type. Unit and type can be either Symbols or Classes. Translators match on unit; they must define from and to units, and they should define from and to types. Converters match on type; they must define from and to types. One problem you may have noticed is that the two translators are exactly the same, except that their from and to units are swapped. You could give the sliders and the color panel the same unit; but they really are different things, and in an application with more things going on, that could cause problems (recall that unit and type are part of the matching criteria for prototypes). See the next example for my current solution. |
require 'jrbuilder'
bindery = Bindery::Bindery.new
bindery.binding_context('my_swing_lib') {
property_change(java.awt.Component) {
unit :shade
type Fixnum
property_name 'background'
min_value 0
max_value 255
predicate { |event,from_attrs|
event.property_name == from_attrs[:property_name].to_s
}
getter { |obj,attribs|
color = obj.send(attribs[:property_name].to_sym)
color.send(attribs[:component])
}
setter { |obj,value,attribs|
prop = attribs[:property_name]
old_color = obj.send(prop.to_sym)
red = old_color.red
green = old_color.green
blue = old_color.blue
case attribs[:component]
when :red : red = value
when :green : green = value
when :blue : blue = value
end
obj.send((prop.to_s+'=').to_sym,java.awt.Color.new(red,green,blue))
}
}
state_changed(javax.swing.JSlider,'value') {
unit :slider
type Fixnum
min_value 0
max_value 100
}
translator(:shade,:slider,Fixnum,Fixnum) { |value,from_attrs,to_attrs|
from_min = from_attrs[:min_value]
from_range = (from_attrs[:max_value] - from_min + 1).to_f
to_min = to_attrs[:min_value]
to_range = (to_attrs[:max_value] - to_min + 1).to_f
(((value - from_min) * (to_range/from_range)) + to_min).round
}
translator(:slider,:shade,Fixnum,Fixnum) { |value,from_attrs,to_attrs|
from_min = from_attrs[:min_value]
from_range = (from_attrs[:max_value] - from_min + 1).to_f
to_min = to_attrs[:min_value]
to_range = (to_attrs[:max_value] - to_min + 1).to_f
(((value - from_min) * (to_range/from_range)) + to_min).round
}
}
ctx = JRBuilder.new_swing_context(bindery)
ctx.enter {
attr_reader :my_frame
red_slider_a = nil
green_slider_a = nil
blue_slider_a = nil
red_slider_b = nil
green_slider_b = nil
blue_slider_b = nil
color_box = nil
@my_frame = frame("Binding Demo 4") { size 400,300
border :empty, 10,10,10,10
on_window_closing { my_frame.dispose }
menu_bar {
menu("File") { mnemonic :VK_F; background :WHITE
menu_item("Exit") { mnemonic :VK_X; background :WHITE
on_click { my_frame.dispose }
}
} }
y_box {
x_box {
text_area('The sliders in groups A and B are not connected directly; '+
'all events flow through the color panel',2,10) {
editable false; line_wrap true; wrap_style_word true; border :etched,:LOWERED
}
}
y_strut 10
x_box { border :compound, border(:bevel,:RAISED), border(:empty, 10,10,10,10)
y_box {
border(:titled, 'Slider Group A') { border :etched, :LOWERED }
y_box { border :empty, 10,10,10,10
red_slider_a = slider { background :RED; value 0 }
y_strut 10
green_slider_a = slider { background :GREEN; value 0 }
y_strut 10
blue_slider_a = slider { background :BLUE; value 0 }
}
}
color_box = panel {
border :bevel, :LOWERED
fixed_size 100,100
background :BLACK
}
y_box {
border(:titled, 'Slider Group B') { border :etched, :LOWERED }
y_box { border :empty, 10,10,10,10
red_slider_b = slider { background :RED; value 0 }
y_strut 10
green_slider_b = slider { background :GREEN; value 0 }
y_strut 10
blue_slider_b = slider { background :BLUE; value 0 }
}
}
}
y_strut 35
}
}
# new binding context, inherits from our swing lib
binding_context('my_swing_lib','my_app_ctx') {
# each slider is bound to the color box. notice that none
# of the sliders are bound together -- all the events
# flow through the color box.
bind('red_a') {
proto(red_slider_a)
proto(color_box) { unit :shade; component :red }
}
bind('green_a') {
proto(green_slider_a)
proto(color_box) { unit :shade; component :green }
}
bind('blue_a') {
proto(blue_slider_a)
proto(color_box) { unit :shade; component :blue }
}
bind('red_b') {
proto(red_slider_b)
proto(color_box) { unit :shade; component :red }
}
bind('green_b') {
proto(green_slider_b)
proto(color_box) { unit :shade; component :green }
}
bind('blue_b') {
proto(blue_slider_b)
proto(color_box) { unit :shade; component :blue }
}
}
}
ctx.my_frame.show
|
|
| Home | « Prev Contents Next » | Download |