JRBuilder: Binding Demo 1
Home «  Prev    Contents    Next  » Download
JRBuilder: Binding Demo 1
More on Binding: Sliders and spinners are all bound together. Text fields are bound to each other.
The title of this example is a bit misleading, as binding was discussed in some detail in the previous example. In this example, I'll go into a little more detail about binding, while continuing to demonstrate JRBuilder's SwingBuilder capabilities.

While JRBuilder/SwingBuilder and Bindery define their own Domain-Specific Languages (DSLs) (based, in part, on class names and method names from the Java packages upon which they build), remember that the underlying language is still Ruby, and you still have full access to all of Ruby's features. So you are free to code something like this:

4.times {
  sliders << slider(:VERTICAL)
  x_glue
}
One convention used in JRBuilder is that normally you don't need to use the set_ prefix when setting properties, as in:
  size 300,300
This can, on rare occasions, result in a setter being called when a getter was intended, or vice versa. In those cases, you can always specify get_ (or set_) to get the intended behavior. You'll also need to do this when a property name is the same as a class name; for example, if you drill down on a JSpinner to get to the underlying JFormattedTextField:
spinner {
  editor {
    get_text_field {
      on_focus_gained { ... }
    }  
  }
}

The fixed_size pseudo-method is equivalent to setting (and internally, does set) each of minimum_size, preferred_size, and maximum_size to the same values. I've found this useful when dealing with intransigent Layout Managers. Note that you don't need to wrap the parameters for these methods in a Dimension object, though you still may if you prefer.

The event handlers shown so far haven't needed to access the Event object itself. The on_focus_gained handler for field2 shows how this is done. The (optional) obj parameter is the same as event.source.

So, here we are, binding ten objects together in eight lines of code. Not too hard, eh? Unfortunately, it isn't always that simple.

Sometimes, the object that fires an event is not the same as the object whose value you want to bind. That is the case in the Text Fields portion of this example, where I want the fields to remain in sync as the user types. The only single event that can be used is undoable_edit_happened, which is fired by the underlying PlainDocument.

The solution is to specify custom getters and setters for each correspondent. (Normally, the Bindery automatically creates getters and setters based on the property name specified.) This works fine, but it adds some extra work. It would be a real pain if there were a lot of these cases. Fortunately, you can use prototypes to reduce the work involved, as you'll see in the next example.

require 'jrbuilder'

ctx = JRBuilder.new_swing_context

ctx.enter {
  attr_reader :my_frame
  
  sliders = []
  spinners = []
  field1 = nil
  field2 = nil
  
  @my_frame = frame('Binding Demo 1') { size 400, 400; location 200, 100
    layout :grid,2,2; border :bevel, :RAISED
    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 }
        }
      }
    } 
    x_box { border(:titled, 'Vertical Sliders') { border :etched, :LOWERED}
      x_glue
      4.times {
        sliders << slider(:VERTICAL) { value 0 }
        x_glue
      }
    } 
    y_box { border(:titled, 'Horizontal Sliders') { border :etched, :LOWERED }
      y_glue
      4.times {
        sliders << slider(:HORIZONTAL) { value 0 }
        y_glue
      }
    }
    y_box { border(:titled, 'Spinners') { border :etched, :LOWERED}
      y_glue
      2.times {
        spinners << spinner(spinner_number_model(0,0,100,1)) {
          fixed_size 60,24; border :bevel, :LOWERED
        }
        y_glue
      }
    }
    y_box { border(:titled, 'Text Fields') { border :etched, :LOWERED }
      y_glue
      text_area('Type text in one box, and watch it appear in the other') {
        line_wrap true; wrap_style_word true; editable false;
        border :etched, :RAISED; fixed_size 175,40
      }
      y_glue
      field1 = text_field('Enter text here',15) {
        border :bevel, :LOWERED; fixed_size 175,24
        on_focus_gained { field1.select_all }
      }
      y_glue
      field2 = text_field('Enter text here',15) {
        border :bevel, :LOWERED; fixed_size 175,24
        on_focus_gained { |event,obj| obj.select_all }
      }
      y_glue
    }
  }
  bind {
    sliders.each do |slider|
      state_changed(slider,'value')
    end
    spinners.each do |spinner|
      state_changed(spinner,'value')
    end
  }
  bind {
    undoable_edit_happened(field1.document) {
      getter { |obj| field1.text }
      setter { |obj,value| field1.text = value }
    }
    undoable_edit_happened(field2.document) {
      getter { |obj| field2.text }
      setter { |obj,value| field2.text = value }
    }
  }
}

ctx.my_frame.show
Home «  Prev    Contents    Next  » Download