CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-codehaus-groovy--groovy-swing

A declarative Swing GUI builder for Groovy applications that provides a concise and maintainable way to create complex Swing user interfaces

Pending
Overview
Eval results
Files

data-binding.mddocs/

Data Binding System

Comprehensive data binding framework for connecting GUI components to data models with validation, conversion, and automatic synchronization.

Capabilities

Core Binding Interfaces

The foundation of the binding system providing contracts for data synchronization.

/**
 * Complete bidirectional binding interface with converter/validator support
 */
interface FullBinding extends BindingUpdatable {
    SourceBinding getSourceBinding()
    TargetBinding getTargetBinding()
    void setSourceBinding(SourceBinding source)
    void setTargetBinding(TargetBinding target)
    void setValidator(Closure validator)
    Closure getValidator()
    void setConverter(Closure converter)
    Closure getConverter()
    void setReverseConverter(Closure reverseConverter)
    Closure getReverseConverter()
}

/**
 * Base interface for all binding update operations
 */
interface BindingUpdatable {
    void bind()
    void unbind()
    void rebind()
    void update()
    void reverseUpdate()
}

/**
 * Source endpoint of a binding relationship
 */
interface SourceBinding extends BindingUpdatable {
    Object getSourceValue()
    void setSourceBinding(PropertyChangeListener listener)
}

/**
 * Target endpoint of a binding relationship  
 */
interface TargetBinding extends BindingUpdatable {
    void setTargetValue(Object value)
}

/**
 * Event-triggered binding interface
 */
interface TriggerBinding {
    FullBinding getFullBinding()
    void setFullBinding(FullBinding fullBinding)
    void bind()
    void unbind()
}

Binding Factory

The primary factory for creating and configuring data bindings through the builder DSL.

/**
 * Primary factory for creating data bindings
 */
class BindFactory {
    /** Create binding between source and target */
    Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes)
    
    /** Handle binding attributes in component definitions */
    boolean bindingAttributeDelegate(FactoryBuilderSupport builder, Object node, Map attributes)
}

// Binding DSL usage
bind(source: sourceObject, sourceProperty: 'propertyName',
     target: targetComponent, targetProperty: 'text',
     converter: { value -> value.toString() },
     validator: { value -> value != null })

Usage Examples:

import groovy.swing.SwingBuilder
import groovy.model.ValueHolder

def swing = new SwingBuilder()
def model = new ValueHolder('Initial Text')

swing.frame(title: 'Binding Example') {
    panel {
        // Bind text field to model
        textField(id: 'textInput', columns: 20)
        bind(source: model, sourceProperty: 'value',
             target: textInput, targetProperty: 'text')
        
        // Two-way binding with validation
        textField(id: 'numberInput', columns: 10)
        bind(source: model, sourceProperty: 'value',
             target: numberInput, targetProperty: 'text',
             converter: { it.toString() },
             reverseConverter: { 
                 try { Integer.parseInt(it) }
                 catch (NumberFormatException e) { 0 }
             },
             validator: { it instanceof Number && it >= 0 })
    }
}

Value Models

Observable data containers that support property change notifications.

/**
 * Interface for observable values with change notification
 */
interface ValueModel {
    Object getValue()
    void setValue(Object value)
    Class getType()
    boolean isEditable()
}

/**
 * Basic implementation of ValueModel
 */
class ValueHolder implements ValueModel {
    ValueHolder(Object value = null, Class type = Object, boolean editable = true)
    Object getValue()
    void setValue(Object value)
    Class getType()
    boolean isEditable()
    void addPropertyChangeListener(PropertyChangeListener listener)
    void removePropertyChangeListener(PropertyChangeListener listener)
}

/**
 * Property-based value model with reflection support
 */
class PropertyModel implements ValueModel {
    PropertyModel(Object bean, String propertyName)
    Object getValue()
    void setValue(Object value)
    Class getType()
    boolean isEditable()
    void addPropertyChangeListener(PropertyChangeListener listener)
    void removePropertyChangeListener(PropertyChangeListener listener)
}

/**
 * Nested property access model for complex object graphs
 */
class NestedValueModel implements ValueModel {
    NestedValueModel(Object root, String propertyPath)
    Object getValue()
    void setValue(Object value)
    Class getType()
    boolean isEditable()
    void addPropertyChangeListener(PropertyChangeListener listener)
    void removePropertyChangeListener(PropertyChangeListener listener)
}

/**
 * Closure-based computed model
 */
class ClosureModel implements ValueModel {
    ClosureModel(Closure closure)
    Object getValue()
    void setValue(Object value)
    Class getType()
    boolean isEditable()
    void addPropertyChangeListener(PropertyChangeListener listener)
    void removePropertyChangeListener(PropertyChangeListener listener)
}

Usage Examples:

import groovy.model.*

// Basic value holder
def nameModel = new ValueHolder('John Doe', String)
nameModel.addValueChangeListener { evt ->
    println "Name changed from ${evt.oldValue} to ${evt.newValue}"
}

// Property model for existing bean
class Person {
    String name
    int age
}
def person = new Person(name: 'Alice', age: 30)
def nameProperty = new PropertyModel(person, 'name')
def ageProperty = new PropertyModel(person, 'age')

// Nested property model
def addressProperty = new NestedValueModel(person, 'address.street')

// Computed model
def fullNameModel = new ClosureModel { 
    "${person.firstName} ${person.lastName}"
}

Component Property Bindings

Specialized binding classes for different Swing components.

/**
 * Text component binding with document listeners
 */
class JTextComponentProperties {
    static Object getText(JTextComponent self)
    static void setText(JTextComponent self, Object value)
}

/**
 * Button selection state binding
 */
class AbstractButtonProperties {
    static Object getSelected(AbstractButton self)
    static void setSelected(AbstractButton self, Object value)
}

/**
 * Slider value binding
 */
class JSliderProperties {
    static Object getValue(JSlider self)
    static void setValue(JSlider self, Object value)
}

/**
 * Combo box selection and item binding
 */
class JComboBoxProperties {
    static Object getSelectedItem(JComboBox self)
    static void setSelectedItem(JComboBox self, Object value)
    static Object getSelectedIndex(JComboBox self) 
    static void setSelectedIndex(JComboBox self, Object value)
}

/**
 * List selection binding
 */
class JListProperties {
    static Object getSelectedElement(JList self)
    static Object getSelectedElements(JList self)
    static Object getSelectedIndex(JList self)
    static Object getSelectedIndices(JList self)
    static Object getSelectedValue(JList self)
    static Object getSelectedValues(JList self)
}

/**
 * Table selection and data binding
 */
class JTableProperties {
    static Object getSelectedElement(JTable self)
    static Object getSelectedElements(JTable self)
    static Object getSelectedRow(JTable self)
    static Object getSelectedRows(JTable self)
    static Object getSelectedColumn(JTable self)
    static Object getSelectedColumns(JTable self)
}

/**
 * Spinner value binding
 */
class JSpinnerProperties {
    static Object getValue(JSpinner self)
    static void setValue(JSpinner self, Object value)
}

Binding Implementations

Concrete implementations of the binding interfaces for various scenarios.

/**
 * Base implementation of FullBinding
 */
abstract class AbstractFullBinding implements FullBinding {
    SourceBinding sourceBinding
    TargetBinding targetBinding
    Closure validator
    Closure converter
    Closure reverseConverter
    
    void bind()
    void unbind()
    void rebind()
    void update()
}

/**
 * Property-based binding implementation
 */
class PropertyBinding extends AbstractFullBinding {
    PropertyBinding(Object source, String sourceProperty, 
                   Object target, String targetProperty)
}

/**
 * Property path binding for nested properties
 */
class PropertyPathFullBinding extends AbstractFullBinding {
    PropertyPathFullBinding(Object source, String sourcePath,
                           Object target, String targetPath)
}

/**
 * Closure-based source binding
 */
class ClosureSourceBinding implements SourceBinding {
    ClosureSourceBinding(Closure closure)
    Object getSourceValue()
    void setSourceBinding(PropertyChangeListener listener)
    void bind()
    void unbind()
    void rebind()
    void update()
}

/**
 * Event-triggered binding
 */
class EventTriggerBinding implements TriggerBinding {
    EventTriggerBinding(Object source, String eventName, 
                       FullBinding fullBinding = null, Closure closure = null)
}

/**
 * Timer-based binding updates
 */
class SwingTimerTriggerBinding implements TriggerBinding {
    SwingTimerTriggerBinding(int delay, FullBinding fullBinding = null)
}

/**
 * Property-based binding with configurable update strategies
 */
class PropertyBinding extends AbstractFullBinding {
    PropertyBinding(Object sourceBean, String sourceProperty, 
                   Object targetBean, String targetProperty, 
                   UpdateStrategy updateStrategy = UpdateStrategy.MIXED)
    
    static enum UpdateStrategy {
        /** EDT if on EDT, otherwise invokeLater */
        MIXED,
        /** Always use invokeLater */
        ASYNC,
        /** invokeAndWait if not on EDT, direct if on EDT */
        SYNC,
        /** Execute in same thread */
        SAME,
        /** Execute outside EDT */
        OUTSIDE,
        /** Always execute in background thread */
        DEFER
    }
}

/**
 * Bidirectional property binding between two properties  
 */
class MutualPropertyBinding extends PropertyBinding {
    MutualPropertyBinding(Object sourceBean, String sourceProperty,
                         Object targetBean, String targetProperty,
                         UpdateStrategy updateStrategy = UpdateStrategy.MIXED)
}

/**
 * Aggregate multiple bindings for group operations
 */
class AggregateBinding implements FullBinding {
    void addBinding(FullBinding binding)
    void removeBinding(FullBinding binding)
    List<FullBinding> getBindings()
}

Binding Groups and Proxies

Advanced binding constructs for complex scenarios.

/**
 * Grouped bindings for batch operations
 */
class BindGroupFactory {
    Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes)
}

/**
 * Binding proxy for complex binding scenarios
 */
class BindingProxy {
    BindingProxy(Object target, String property, Map args = [:])
    void bind(Object source, String sourceProperty)
    void unbind()
}

/**
 * Multiple binding aggregation
 */
class AggregateBinding extends AbstractFullBinding {
    AggregateBinding(FullBinding... bindings)
    void addBinding(FullBinding binding)
    void removeBinding(FullBinding binding)
}

Usage Examples:

// Binding group for form validation
bindGroup {
    bind(source: model.name, target: nameField, targetProperty: 'text')
    bind(source: model.email, target: emailField, targetProperty: 'text')
    bind(source: model.age, target: ageSpinner, targetProperty: 'value')
}

// Binding proxy for complex property chains
def proxy = new BindingProxy(someComplexObject, 'deeply.nested.property')
proxy.bind(textField, 'text')

// Timer-based binding for real-time updates
def timerBinding = new SwingTimerTriggerBinding(1000) // Update every second
timerBinding.fullBinding = bind(source: statusModel, target: statusLabel)

Validation and Conversion

Built-in support for data validation and type conversion in bindings.

// Binding with validation
bind(source: model, sourceProperty: 'value',
     target: textField, targetProperty: 'text',
     validator: { value ->
         if (value == null || value.toString().trim().isEmpty()) {
             throw new IllegalArgumentException('Value cannot be empty')
         }
         return true
     })

// Binding with bi-directional conversion
bind(source: model, sourceProperty: 'temperature',
     target: textField, targetProperty: 'text',
     converter: { celsius -> 
         String.format('%.1f°C', celsius)
     },
     reverseConverter: { text ->
         def match = text =~ /(\d+\.?\d*)°?C?/
         match ? Double.parseDouble(match[0][1]) : 0.0
     })

Table Model Integration

Enhanced table models with binding support for complex data display.

/**
 * Enhanced table model with PropertyModel columns
 */
class DefaultTableModel extends AbstractTableModel {
    DefaultTableModel(List rowData = [], List columnNames = [])
    void addColumn(String name, Closure valueGetter)
    void addPropertyColumn(String name, String property)
    void fireTableDataChanged()
}

/**
 * Table column with value model support
 */
class DefaultTableColumn extends TableColumn {
    DefaultTableColumn(ValueModel valueModel, String header)
    ValueModel getValueModel()
    void setValueModel(ValueModel model)
}

Usage Examples:

// Table with bound data model
table {
    tableModel(list: peopleList) {
        propertyColumn(header: 'Name', propertyName: 'name', editable: true)
        propertyColumn(header: 'Age', propertyName: 'age', type: Integer)
        closureColumn(header: 'Status') { person ->
            person.age >= 18 ? 'Adult' : 'Minor'
        }
    }
}

// Bind table selection to detail view
bind(source: table, sourceProperty: 'selectedElement',
     target: detailPanel, targetProperty: 'person')

Install with Tessl CLI

npx tessl i tessl/maven-org-codehaus-groovy--groovy-swing

docs

data-binding.md

extensions.md

gui-building.md

index.md

layout-management.md

look-and-feel.md

models.md

tile.json