JRBuilder: Binding Demo 5
Home «  Prev    Contents    Next  » Download
JRBuilder: Binding Demo 5
Even more on Binding: Translators and Converters. Unit and Type.
This example is a continuation of the previous example. You probably want to look at that one first, if you haven't yet.

The problem with the last example was that there were two translators exactly alike except that their input and output units were reversed. This example shows the solution I came up with.

I had been thinking for a while (a week or so, at this writing it's a little over two weeks since I started writing the Bindery) that units should be classes. My first take was to make Slider and Shade subclasses of Scale. That worked, but it wasn't quite right; a slider and a color shade aren't really the same sort of thing, the scale is just a facet of their behavior. Then I had an aha! moment, and realized that mixins made much more sense. (I remembered then something that Charles Oliver Nutter had said about mapping Java interfaces as Modules, and though it doesn't exactly apply to this case, it resonated in my Java-tinged brain.)

I'm still not sure if this is the best solution. Though I can't think of a compelling argument against it, there might well be a better way. As with everything else in this JRBuilder/Bindery project, I welcome your comments and suggestions.

require 'jrbuilder'

bindery = Bindery::Bindery.new
bindery.binding_context('my_swing_lib') {

module Scale; end
class Shade; include Scale; end
class Slider; include Scale; end

  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(Scale,Scale,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
  }

  # unneeded translator deleted

}

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 5") { 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