The following properties can be used with either the long or contextual binding syntax

- mutual:

Bindings are usually setup in one direction. If this property is specified with a value of `true` then a bidirectional binding will be created instead.

    import groovy.beans.Bindable
    import groovy.swing.SwingBuilderclass MyModel {
      @Bindable String value
    }def model = new MyModel()
    def swing  = new SwingBuilder()
    swing.edt {
      frame(title: 'Binding', pack: true, visible: true) {
        gridLayout(cols: 2, rows: 3)
        label 'Normal'
        textField(columns: 20, text: bind('value', target: model))
        label 'Bidirectional'
        textField(columns: 20, text: bind('value', target: model, mutual: true))
        label 'Model'
        textField(columns: 20, text: bind('value', source: model))
      }
    }

Typing text on textfield #2 pushes the value to model, which in turns updates textfield #2 and #3, demonstrating that textfield #2 listens top model updates. Typing text on textfield #2 pushes the value to textfield #3 but not #1, demonstrating that textfield #1 is not a bidirectional binding.

- converter:

Transforms the value before it is sent to event listeners.

    import groovy.beans.Bindable
    import groovy.swing.SwingBuilderclass MyModel {
      @Bindable String value
    }def convertValue = { val ->
      '*' * val?.size()
    }def model = new MyModel()
    def swing  = new SwingBuilder()
    swing.edt {
      frame(title: 'Binding', pack: true, visible: true) {
        gridLayout(cols: 2, rows: 3)
        label 'Normal'
        textField(columns: 20, text: bind('value', target: model))
        label 'Converter'
        textField(columns: 20, text: bind('value', target: model, converter: convertValue))
        label 'Model'
        textField(columns: 20, text:  bind('value', source: model))
      }
    }

Typing text on textfield #1 pushes the value to the model as expected, which you can inspect by looking at textfield #3. Typing text on textfield #2 however transform's every keystroke into an '*' character.

- validator:

Guards the trigger. Prevents the event from being sent if the return value is `false` or `null`.

    import groovy.beans.Bindable
    import groovy.swing.SwingBuilderclass MyModel {
      @Bindable String value
    }def isNumber = { val ->
      if(!val) return true
      try {
        Double.parseDouble(val)
      } catch(NumberFormatException e) {
        false
      }
    }def model = new MyModel()
    def swing  = new SwingBuilder()
    swing.edt {
      frame(title: 'Binding', pack: true, visible: true) {
        gridLayout(cols: 2, rows: 3)
        label 'Normal'
        textField(columns: 20, text: bind('value', target: model))
        label 'Converter'
        textField(columns: 20, text: bind('value', target: model, validator: isNumber))
        label 'Model'
        textField(columns: 20, text:  bind('value', source: model))
      }
    }

You can type any characters on textfield #1 and see the result in textfield #3. You can only type numbers on textfield #2 and see the result in textfield #3

> This type of validation is not suitable for semantic validation (a.k.a. constraints in domain classes). You would want to have a look at the Validation plugin.

- sourceEvent:

Maps a different event type, instead of `PropertyChangeEvent`.

- sourceValue:

Specify a value that may come from a different source. Usually found in partnership with `sourceevent`.

    import groovy.beans.Bindable
    import groovy.swing.SwingBuilderclass MyModel {
      @Bindable String value
    }def model = new MyModel()
    def swing  = new SwingBuilder()
    swing.edt {
      frame(title: 'Binding', pack: true, visible: true) {
        gridLayout(cols: 2, rows: 3)
        label 'Text'
        textField(columns: 20, id: 'tf1')
        label 'Trigger'
        button('Copy Text', id: 'bt1')
        bind(source: bt1,
             sourceEvent: 'actionPerformed',
             sourceValue: {tf1.text},
             target: model,
             targetProperty: 'value')
        label 'Model'
        textField(columns: 20, text:  bind('value', source: model))
      }
    }

A contrived way to copy text from one textfield to another. The copy is performed by listening to `ActionEvent`s pumped by the button.