blob: 6e2d5cb487545b500af0e6a24c6b94f7268d9f03 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package groovy.swing
import groovy.beans.Bindable
import groovy.beans.Vetoable
import javax.swing.DefaultBoundedRangeModel
import javax.swing.DefaultButtonModel
import javax.swing.DefaultListModel
import javax.swing.ListSelectionModel
import javax.swing.SpinnerNumberModel
import javax.swing.text.PlainDocument
import java.awt.event.ActionEvent
import java.beans.PropertyChangeEvent
import java.beans.PropertyVetoException
import java.text.SimpleDateFormat
class SwingBuilderBindingsTest extends GroovySwingTestCase {
void testSliderValueBinding() {
testInEDT {
SwingBuilder swing = new SwingBuilder()
swing.actions {
slider(id: 'sl')
textField(id: 'txt', text: bind(source: sl, sourceProperty: 'value', id: 'binding'))
slider(id: 'slReverse', value: bind(source: sl, sourceProperty: 'value', id: 'bindingReverse'))
// need to use a second slider for reverse test, because string->int autobox, not so happy
}
swing.sl.value = 10
assert swing.txt.text == '10'
swing.sl.value = 95
assert swing.txt.text == '95'
swing.binding.rebind()
swing.sl.value = 42
assert swing.txt.text == '42'
swing.binding.unbind()
swing.sl.value = 13
assert swing.txt.text == '42'
swing.binding.bind()
assert swing.txt.text == '42'
swing.binding.update()
assert swing.txt.text == '13'
swing.sl.model = new DefaultBoundedRangeModel(30, 1, 20, 40)
assert swing.txt.text == '30'
// first make sure we've been fireing
assert swing.slReverse.value == 30
swing.slReverse.value = 21
swing.bindingReverse.reverseUpdate()
assert swing.sl.value == 21
}
}
void testSpinnerValueBinding() {
testInEDT {
SwingBuilder swing = new SwingBuilder()
swing.actions {
spinner(id: 'sp', model: spinnerNumberModel(minimum: 0, maximum: 100, stepSize: 5))
textField(id: 'txt', text: bind(source: sp, sourceProperty: 'value', id: 'binding'))
spinner(id: 'spReverse', value: bind(source: sp, sourceProperty: 'value', id: 'bindingReverse'))
// need to use a second spinner for reverse test, because string->int autobox, not so happy
}
swing.sp.value = 10
assert swing.txt.text == '10'
swing.sp.value = 95
assert swing.txt.text == '95'
swing.binding.rebind()
swing.sp.value = 42
assert swing.txt.text == '42'
swing.binding.unbind()
swing.sp.value = 13
assert swing.txt.text == '42'
swing.binding.bind()
assert swing.txt.text == '42'
swing.binding.update()
assert swing.txt.text == '13'
swing.sp.model = new SpinnerNumberModel(30, 20, 40, 1)
assert swing.txt.text == '30'
// first make sure we've been fireing
assert swing.spReverse.value == 30
swing.spReverse.value = 21
swing.bindingReverse.reverseUpdate()
assert swing.sp.value == 21
}
}
void testScrollBarValueBinding() {
testInEDT {
SwingBuilder swing = new SwingBuilder()
swing.actions {
scrollBar(id: 'sb')
textField(id: 'txt', text: bind(source: sb, sourceProperty: 'value', id: 'binding'))
scrollBar(id: 'sbReverse', value: bind(source: sb, sourceProperty: 'value', id: 'bindingReverse'))
// need to use a second scrollBar for reverse test, because string->int autobox, not so happy
}
swing.sb.value = 10
assert swing.txt.text == '10'
swing.sb.value = 85
assert swing.txt.text == '85'
swing.binding.rebind()
swing.sb.value = 42
assert swing.txt.text == '42'
swing.binding.unbind()
swing.sb.value = 13
assert swing.txt.text == '42'
swing.binding.bind()
assert swing.txt.text == '42'
swing.binding.update()
assert swing.txt.text == '13'
swing.sb.model = new DefaultBoundedRangeModel(30, 1, 20, 40)
assert swing.txt.text == '30'
// first make sure we've been fireing
assert swing.sbReverse.value == 30
swing.sbReverse.value = 21
swing.bindingReverse.reverseUpdate()
assert swing.sb.value == 21
}
}
void testTextFieldTextBinding() {
testInEDT {
SwingBuilder swing = new SwingBuilder()
swing.actions {
textField('Bind', id: 'txts')
textField(id: 'txt', text: bind(source: txts, sourceProperty: 'text', id: 'binding'))
}
assert swing.txt.text == 'Bind'
swing.txts.text = 'Text'
assert swing.txt.text == 'Text'
swing.txts.text = 'Easily'
assert swing.txt.text == 'Easily'
swing.binding.rebind()
swing.txts.text = 'With'
assert swing.txt.text == 'With'
swing.binding.unbind()
swing.txts.text = 'bind()'
assert swing.txt.text == 'With'
swing.binding.bind()
assert swing.txt.text == 'With'
swing.binding.update()
assert swing.txt.text == 'bind()'
swing.txt.text = 'reversal'
swing.binding.reverseUpdate()
assert swing.txts.text == 'reversal'
PlainDocument doc = new PlainDocument()
doc.insertString(0, '{}', null)
swing.txts.document = doc
assert swing.txt.text == '{}'
}
}
void testCheckboxSelectedBinding() {
testInEDT {
SwingBuilder swing = new SwingBuilder()
swing.actions {
checkBox(id: 'cb')
textField(id: 'txt', enabled: bind(source: cb, sourceProperty: 'selected', id: 'binding'))
}
assert swing.txt.enabled == swing.cb.selected
swing.cb.selected = !swing.cb.selected
assert swing.txt.enabled == swing.cb.selected
swing.cb.selected = !swing.cb.selected
assert swing.txt.enabled == swing.cb.selected
swing.binding.rebind()
swing.cb.selected = !swing.cb.selected
assert swing.txt.enabled == swing.cb.selected
swing.binding.unbind()
swing.cb.selected = !swing.cb.selected
assert swing.txt.enabled != swing.cb.selected
swing.binding.bind()
assert swing.txt.enabled != swing.cb.selected
swing.binding.update()
assert swing.txt.enabled == swing.cb.selected
DefaultButtonModel md = new DefaultButtonModel()
md.enabled = !swing.txt.enabled
swing.cb.model = md
assert swing.txt.enabled == swing.cb.selected
swing.txt.enabled = !swing.txt.enabled
swing.binding.reverseUpdate()
assert swing.txt.enabled == swing.cb.selected
}
}
void testComboBoxBindSyntheticProperties() {
testInEDT {
SwingBuilder swing = new SwingBuilder()
def comboData = ['Alpha', 'Bravo', 'Charlie', 'Delta']
def vectorData = ['Adams', 'Boston', 'Chicago', 'Denver']
swing.frame {
comboBox(id: 'combo01', items: comboData)
comboBox(id: 'combo02', model: new javax.swing.DefaultComboBoxModel(new Vector(vectorData)))
t1e = label(text: bind {combo01.elements})
t1sx = label(text: bind {combo01.selectedIndex})
t1se = label(text: bind {combo01.selectedElement})
t1si = label(text: bind {combo01.selectedItem})
t2e = label(text: bind {combo02.elements})
t2sx = label(text: bind {combo02.selectedIndex})
t2se = label(text: bind {combo02.selectedElement})
t2si = label(text: bind {combo02.selectedItem})
}
assert swing.t1e.text == '[Alpha, Bravo, Charlie, Delta]'
assert swing.t1sx.text == '0'
assert swing.t1se.text == 'Alpha'
assert swing.t1si.text == 'Alpha'
assert swing.t2e.text == '[Adams, Boston, Chicago, Denver]'
assert swing.t2sx.text == '0'
assert swing.t2se.text == 'Adams'
assert swing.t2si.text == 'Adams'
swing.combo01.selectedIndex = 1
assert swing.t1e.text == '[Alpha, Bravo, Charlie, Delta]'
assert swing.t1sx.text == '1'
assert swing.t1se.text == 'Bravo'
assert swing.t1si.text == 'Bravo'
swing.combo02.selectedIndex = 1
assert swing.t2e.text == '[Adams, Boston, Chicago, Denver]'
assert swing.t2sx.text == '1'
assert swing.t2se.text == 'Boston'
assert swing.t2si.text == 'Boston'
swing.combo01.selectedIndex = -1
assert swing.t1e.text == '[Alpha, Bravo, Charlie, Delta]'
assert swing.t1sx.text == '-1'
assert swing.t1se.text == null
assert swing.t1si.text == null
swing.combo02.selectedIndex = -1
assert swing.t2e.text == '[Adams, Boston, Chicago, Denver]'
assert swing.t2sx.text == '-1'
assert swing.t2se.text == null
assert swing.t2si.text == null
swing.combo01.selectedItem = 'Charlie'
assert swing.t1e.text == '[Alpha, Bravo, Charlie, Delta]'
assert swing.t1sx.text == '2'
assert swing.t1se.text == 'Charlie'
assert swing.t1si.text == 'Charlie'
swing.combo02.selectedItem = 'Chicago'
assert swing.t2e.text == '[Adams, Boston, Chicago, Denver]'
assert swing.t2sx.text == '2'
assert swing.t2se.text == 'Chicago'
assert swing.t2si.text == 'Chicago'
swing.combo01.selectedItem = 'Fox Trot'
assert swing.t1e.text == '[Alpha, Bravo, Charlie, Delta]'
assert swing.t1sx.text == '2'
assert swing.t1se.text == 'Charlie'
assert swing.t1si.text == 'Charlie'
swing.combo02.selectedItem = 'Frank'
assert swing.t2e.text == '[Adams, Boston, Chicago, Denver]'
assert swing.t2sx.text == '2'
assert swing.t2se.text == 'Chicago'
assert swing.t2si.text == 'Chicago'
swing.combo01.selectedElement = 'Delta'
assert swing.t1e.text == '[Alpha, Bravo, Charlie, Delta]'
assert swing.t1sx.text == '3'
assert swing.t1se.text == 'Delta'
assert swing.t1si.text == 'Delta'
swing.combo02.selectedElement = 'Denver'
assert swing.t2e.text == '[Adams, Boston, Chicago, Denver]'
assert swing.t2sx.text == '3'
assert swing.t2se.text == 'Denver'
assert swing.t2si.text == 'Denver'
swing.combo01.selectedElement = 'Golf'
assert swing.t1e.text == '[Alpha, Bravo, Charlie, Delta]'
assert swing.t1sx.text == '3'
assert swing.t1se.text == 'Delta'
assert swing.t1si.text == 'Delta'
swing.combo02.selectedElement = 'George'
assert swing.t2e.text == '[Adams, Boston, Chicago, Denver]'
assert swing.t2sx.text == '3'
assert swing.t2se.text == 'Denver'
assert swing.t2si.text == 'Denver'
swing.combo01.model.addElement('Echo')
assert swing.t1e.text == '[Alpha, Bravo, Charlie, Delta, Echo]'
swing.combo02.model.addElement('Easy')
assert swing.t2e.text == '[Adams, Boston, Chicago, Denver, Easy]'
swing.combo01.model.removeElement('Bravo')
assert swing.t1e.text == '[Alpha, Charlie, Delta, Echo]'
swing.combo02.model.removeElement('Adams')
assert swing.t2e.text == '[Boston, Chicago, Denver, Easy]'
}
}
void testListBindSyntheticProperties() {
testInEDT {
SwingBuilder swing = new SwingBuilder()
def listModel = new DefaultListModel()
['Alpha', 'Bravo', 'Charlie', 'Delta'].each {listModel << it}
def map = [sis: null, ses: null, svs: null] as ObservableMap
swing.frame {
list(id: 'list01', model: listModel, selectionMode: ListSelectionModel.SINGLE_SELECTION)
t1e = label(text: bind {list01.elements})
t1sx = label(text: bind {list01.selectedIndex})
t1se = label(text: bind {list01.selectedElement})
t1si = label(text: bind {list01.selectedValue})
bean(map, sis: bind {list01.selectedIndices},
ses: bind {list01.selectedElements},
svs: bind {list01.selectedValues})
}
assert swing.t1e.text == '[Alpha, Bravo, Charlie, Delta]'
swing.list01.selectedIndex = -1
assert swing.t1sx.text == '-1'
assert swing.t1se.text == null
assert swing.t1si.text == null
assert map.sis == []
assert map.ses == []
assert map.svs == []
swing.list01.selectedIndex = 0
assert swing.t1e.text == '[Alpha, Bravo, Charlie, Delta]'
assert swing.t1sx.text == '0'
assert swing.t1se.text == 'Alpha'
assert swing.t1si.text == 'Alpha'
assert map.sis == [0]
assert map.ses == ['Alpha']
assert map.svs == ['Alpha']
swing.list01.selectedIndex = 1
assert swing.t1e.text == '[Alpha, Bravo, Charlie, Delta]'
assert swing.t1sx.text == '1'
assert swing.t1se.text == 'Bravo'
assert swing.t1si.text == 'Bravo'
assert map.sis == [1]
assert map.ses == ['Bravo']
assert map.svs == ['Bravo']
swing.list01.selectedValue = 'Charlie'
assert swing.t1e.text == '[Alpha, Bravo, Charlie, Delta]'
assert swing.t1sx.text == '2'
assert swing.t1se.text == 'Charlie'
assert swing.t1si.text == 'Charlie'
assert map.sis == [2]
assert map.ses == ['Charlie']
assert map.svs == ['Charlie']
swing.list01.selectedValue = 'Fox Trot'
assert swing.t1e.text == '[Alpha, Bravo, Charlie, Delta]'
assert swing.t1sx.text == '2'
assert swing.t1se.text == 'Charlie'
assert swing.t1si.text == 'Charlie'
assert map.sis == [2]
assert map.ses == ['Charlie']
assert map.svs == ['Charlie']
swing.list01.selectedElement = 'Delta'
assert swing.t1e.text == '[Alpha, Bravo, Charlie, Delta]'
assert swing.t1sx.text == '3'
assert swing.t1se.text == 'Delta'
assert swing.t1si.text == 'Delta'
assert map.sis == [3]
assert map.ses == ['Delta']
assert map.svs == ['Delta']
swing.list01.selectedElement = 'Golf'
assert swing.t1e.text == '[Alpha, Bravo, Charlie, Delta]'
assert swing.t1sx.text == '3'
assert swing.t1se.text == 'Delta'
assert swing.t1si.text == 'Delta'
assert map.sis == [3]
assert map.ses == ['Delta']
assert map.svs == ['Delta']
swing.list01.selectionMode = ListSelectionModel.MULTIPLE_INTERVAL_SELECTION
swing.list01.selectedIndices = ([1i, 3i] as int[])
assert swing.t1sx.text == '1'
assert swing.t1se.text == 'Bravo'
assert swing.t1si.text == 'Bravo'
assert map.sis == [1, 3]
assert map.ses == ['Bravo', 'Delta']
assert map.svs == ['Bravo', 'Delta']
swing.list01.model.addElement('Echo')
assert swing.t1e.text == '[Alpha, Bravo, Charlie, Delta, Echo]'
swing.list01.model.removeElement('Bravo')
assert swing.t1e.text == '[Alpha, Charlie, Delta, Echo]'
}
}
void testEventBinding() {
testInEDT {
SwingBuilder swing = new SwingBuilder()
def capture
swing.actions {
button('Button!', id: 'b')
textField(id: 'txt', text: bind(source: b, sourceEvent: 'actionPerformed', sourceValue: {b.text}))
textField(id: 'txt2', text: bind(source: b, sourceEvent: 'actionPerformed', sourceValue: {evt -> capture = evt; 'Captured!'}))
}
assert swing.txt.text == 'Button!'
swing.b.text = 'Pressed!'
// not pressed yet...
assert swing.txt.text == 'Button!'
swing.b.doClick()
//ok, now it's pressed
assert swing.txt.text == 'Pressed!'
//check that we get evet as closure arg
assert swing.txt2.text == 'Captured!'
assert capture instanceof ActionEvent
}
}
void testPropertyBinding() {
testInEDT {
SwingBuilder swing = new SwingBuilder()
swing.actions {
checkBox('Button!', id: 'cb')
textField(id: 'txt', enabled: bind(source: cb, sourceProperty: 'enabled', id: 'binding'))
}
assert swing.txt.enabled == swing.cb.enabled
swing.cb.enabled = !swing.cb.enabled
assert swing.txt.enabled == swing.cb.enabled
swing.cb.enabled = !swing.cb.enabled
assert swing.txt.enabled == swing.cb.enabled
swing.binding.rebind()
swing.cb.enabled = !swing.cb.enabled
assert swing.txt.enabled == swing.cb.enabled
swing.binding.unbind()
swing.cb.enabled = !swing.cb.enabled
assert swing.txt.enabled != swing.cb.enabled
swing.binding.bind()
assert swing.txt.enabled != swing.cb.enabled
swing.binding.update()
assert swing.txt.enabled == swing.cb.enabled
swing.txt.enabled = !swing.txt.enabled
swing.binding.reverseUpdate()
assert swing.txt.enabled == swing.cb.enabled
DefaultButtonModel md = new DefaultButtonModel()
md.enabled = !swing.txt.enabled
swing.cb.model = md
assert swing.txt.enabled == swing.cb.enabled
}
}
void testBindGroup() {
testInEDT {
SwingBuilder swing = new SwingBuilder()
int enabledChangeCount = 1
swing.actions {
bindGroup(id: 'testGroup')
checkBox('Button!', id: 'cb')
textField(id: 'txt1', text: bind(source: cb, sourceProperty: 'text', group: testGroup))
textField(id: 'txt2', text: bind(source: cb, sourceProperty: 'text', sourceValue: {enabledChangeCount++}, group: testGroup))
textField(id: 'txt3', text: bind(source: cb, sourceProperty: 'text', group: testGroup))
}
assert swing.txt1.text == 'Button!'
assert swing.txt2.text == '1'
assert swing.txt3.text == 'Button!'
swing.testGroup.unbind()
swing.cb.text = 'CheckBox!'
swing.cb.selected = true
assert swing.txt1.text == 'Button!'
assert swing.txt2.text == '1'
assert swing.txt3.text == 'Button!'
swing.testGroup.update()
assert swing.txt1.text == 'CheckBox!'
assert swing.txt2.text == '2'
assert swing.txt3.text == 'CheckBox!'
swing.testGroup.bind()
swing.cb.text = 'ComboBox!'
swing.cb.selected = true
assert swing.txt1.text == 'ComboBox!'
assert swing.txt2.text == '3'
assert swing.txt3.text == 'ComboBox!'
// test auto-bind
// test explicit true
swing.actions {
bindGroup(id: 'testGroup', bind: true)
checkBox('Button!', id: 'cb')
textField(id: 'txt1', text: bind(source: cb, sourceProperty: 'text', group: testGroup))
}
assert swing.txt1.text == 'Button!'
swing.cb.text = 'CheckBox!'
assert swing.txt1.text == 'CheckBox!'
// test explicit false
swing.actions {
bindGroup(id: 'testGroup', bind: false)
checkBox('Button!', id: 'cb')
textField(id: 'txt1', text: bind(source: cb, sourceProperty: 'text', group: testGroup))
}
assert swing.txt1.text == 'Button!'
swing.cb.text = 'CheckBox!'
assert swing.txt1.text == 'Button!'
// test implied = true
swing.actions {
bindGroup(id: 'testGroup')
checkBox('Button!', id: 'cb')
textField(id: 'txt1', text: bind(source: cb, sourceProperty: 'text', group: testGroup))
}
assert swing.txt1.text == 'Button!'
swing.cb.text = 'CheckBox!'
assert swing.txt1.text == 'CheckBox!'
}
}
void testPropertyEventBinding() {
testInEDT {
SwingBuilder swing = new SwingBuilder()
int enabledChangeCount = 1
swing.actions {
checkBox('Button!', id: 'cb')
textField(id: 'txtp', text: bind(source: cb, sourceProperty: 'text',))
textField(id: 'txtpv', text: bind(source: cb, sourceProperty: 'text', sourceValue: {enabledChangeCount++}))
textField(id: 'txtep', text: bind(source: cb, sourceEvent: 'stateChanged', sourceProperty: 'text'))
}
shouldFail {
swing.actions {
// all three are pointless
textField(id: 'txtepv', enabled: bind(source: cb, sourceEvent: 'stateChanged', sourceProperty: 'text', sourceValue: {enabledChangeCount++}))
}
}
shouldFail {
swing.actions {
// just value isn't enough info
textField(id: 'txtv', enabled: bind(source: cb, sourceValue: {enabledChangeCount++}))
}
}
shouldFail {
swing.actions {
// just event isn't enough
textField(id: 'txtepv', enabled: bind(source: cb, sourceEvent: 'stateChanged'))
}
}
assert swing.txtp.text == 'Button!'
assert swing.txtpv.text == '1'
assert swing.txtep.text == 'Button!'
swing.cb.text = 'CheckBox!'
assert swing.txtp.text == 'CheckBox!'
assert swing.txtpv.text == '2'
assert swing.txtep.text == 'Button!'
swing.cb.selected = true
assert swing.txtp.text == 'CheckBox!'
assert swing.txtpv.text == '2'
assert swing.txtep.text == 'CheckBox!'
}
}
void testBindNodeValue() {
testInEDT {
SwingBuilder swing = new SwingBuilder()
Map model = [
string: 'string',
bool : true
] as ObservableMap
shouldFail {
swing.actions {
textField(id: 'error', text: bind(model.bool))
}
}
shouldFail {
swing.actions {
textField(id: 'error', text: bind(model.string))
}
}
}
}
void testReversePropertyBinding() {
testInEDT {
SwingBuilder swing = new SwingBuilder()
swing.actions {
textField(id: 'txt')
checkBox('Button!', id: 'cb', enabled: bind(target: txt, targetProperty: 'enabled', id: 'binding'))
}
assert swing.txt.enabled == swing.cb.enabled
swing.cb.enabled = !swing.cb.enabled
assert swing.txt.enabled == swing.cb.enabled
swing.cb.enabled = !swing.cb.enabled
assert swing.txt.enabled == swing.cb.enabled
swing.binding.rebind()
swing.cb.enabled = !swing.cb.enabled
assert swing.txt.enabled == swing.cb.enabled
swing.binding.unbind()
swing.cb.enabled = !swing.cb.enabled
assert swing.txt.enabled != swing.cb.enabled
swing.binding.bind()
assert swing.txt.enabled != swing.cb.enabled
swing.binding.update()
assert swing.txt.enabled == swing.cb.enabled
swing.txt.enabled = !swing.txt.enabled
swing.binding.reverseUpdate()
assert swing.txt.enabled == swing.cb.enabled
DefaultButtonModel md = new DefaultButtonModel()
md.enabled = !swing.txt.enabled
swing.cb.model = md
assert swing.txt.enabled == swing.cb.enabled
}
}
void testValueNodeBinding() {
testInEDT {
SwingBuilder swing = new SwingBuilder()
swing.actions {
checkBox(id: 'cb1')
checkBox('Button!', id: 'cb2', selected: bind(source: cb1, 'selected', id: 'binding1'))
checkBox(id: 'cb3')
checkBox('Button!', id: 'cb4', selected: bind(target: cb3, 'selected', id: 'binding2'))
}
assert !swing.cb1.selected
assert !swing.cb2.selected
swing.cb1.selected = true
assert swing.cb1.selected
assert swing.cb2.selected
swing.cb2.selected = false
assert swing.cb1.selected
assert !swing.cb2.selected
swing.cb1.selected = false
swing.cb1.selected = true
assert swing.cb1.selected
assert swing.cb2.selected
assert !swing.cb3.selected
assert !swing.cb4.selected
swing.cb3.selected = true
assert swing.cb3.selected
assert !swing.cb4.selected
swing.cb4.selected = true
assert swing.cb3.selected
assert swing.cb4.selected
swing.cb4.selected = false
assert !swing.cb3.selected
assert !swing.cb4.selected
}
}
void testReversePropertyPropertites() {
testInEDT {
SwingBuilder swing = new SwingBuilder()
swing.actions {
textField(id: 'txt')
checkBox('Button!', id: 'cb1', enabled: bind(target: txt, targetProperty: 'enabled', converter: {it}, id: 'binding1'))
checkBox('Button!', id: 'cb2', enabled: bind(target: txt, targetProperty: 'enabled', id: 'binding2'))
}
assert swing.binding1.converter != null
assert swing.binding2.converter == null
}
}
void testConverters() {
testInEDT {
SwingBuilder swing = new SwingBuilder()
swing.actions {
checkBox(id: 'doner')
checkBox(id: 'cb1', enabled: bind(source: doner, sourceProperty: 'enabled', converter: {it}, id: 'binding1'))
checkBox(id: 'cb2', enabled: bind(source: doner, sourceProperty: 'enabled', id: 'binding2') {it})
checkBox(id: 'cb3', enabled: bind(source: doner, sourceProperty: 'enabled', id: 'binding3'))
}
assert swing.binding1.converter != null
assert swing.binding2.converter != null
assert swing.binding3.converter == null
// check double duty
shouldFail(RuntimeException) {
swing.actions {
checkBox(id: 'doner')
checkBox(id: 'cb1', enabled: bind(source: doner, sourceProperty: 'enabled', converter: {it}, id: 'binding1') {it})
}
}
// check reversed bindings
swing.actions {
checkBox(id: 'doner')
checkBox(id: 'cb1', enabled: bind(target: doner, targetProperty: 'enabled', converter: {it}, id: 'binding1'))
checkBox(id: 'cb2', enabled: bind(target: doner, targetProperty: 'enabled', id: 'binding2') {it})
checkBox(id: 'cb3', enabled: bind(target: doner, targetProperty: 'enabled', id: 'binding3'))
}
assert swing.binding1.converter != null
assert swing.binding2.converter != null
assert swing.binding3.converter == null
// check double duty
shouldFail(RuntimeException) {
swing.actions {
checkBox(id: 'doner')
checkBox(id: 'cb1', enabled: bind(target: doner, targetProperty: 'enabled', converter: {it}, id: 'binding1') {it})
}
}
}
}
void testDateConverters() {
testInEDT {
BindableBean model = new BindableBean()
model.date = new Date()
def dateConverter = { dateString ->
new SimpleDateFormat("dd.MM.yyyy", Locale.US).parse(dateString)
}
def dateValidator = { dateString ->
try {
def parser = new SimpleDateFormat("dd.MM.yyyy", Locale.US)
parser.lenient = false
parser.parse(dateString)
} catch (Exception e) {
return false
}
return true
}
SwingBuilder swing = new SwingBuilder()
swing.actions {
tf1 = textField('01.01.1970', id: 'birthdayText', columns: 20,
text: bind(
validator: dateValidator,
converter: dateConverter,
target: model, targetProperty: 'date'
))
tf2 = textField('01.01.1970', id: 'birthdayText', columns: 20)
binding = bind(
source: tf2,
sourceProperty: 'text',
validator: dateValidator,
converter: dateConverter,
target: model,
targetProperty: 'date'
)
}
}
}
void testPropertyValuePassthrough() {
testInEDT {
SwingBuilder swing = new SwingBuilder()
swing.actions {
spinner(id: 'spin1', value: 4)
spinner(id: 'spin2', value: 8)
slider(id: 's1', value: bind(target: spin1, targetProperty: 'value', id: 'binding1', value: 15))
slider(id: 's2', value: bind(source: spin2, sourceProperty: 'value', id: 'binding2', value: 16))
}
// s1 is the source, so its value should be reflected
assert swing.s1.value == 15
// s2 is target, not source, so its value setting should have no effect
assert swing.s2.value == 8
swing.actions {
slider(id: 's3', value: 4)
slider(id: 's4', value: 8)
textArea(id: 't1', text: bind(target: s3, targetProperty: 'value',
id: 'binding3', value: '15',
converter: {Integer.parseInt(String.valueOf(it))}))
textArea(id: 't2', text: bind(source: s4, sourceProperty: 'value',
id: 'binding4', value: '16',
converter: {Integer.parseInt(String.valueOf(it))}))
}
// s1 is the source, so its value should be reflected
assert swing.s3.value == 15
// s2 is target, not source, so its value setting should have no effect
assert swing.s4.value == 8
}
}
void testModel() {
testInEDT {
SwingBuilder swing = new SwingBuilder()
def bean = new DummyBean()
swing.bindProxy(bean, id: 'dummyBean')
// test initial binding
swing.textField(id: 'textField', text: swing.dummyBean.name)
assert swing.textField.text == bean.name
// test no live update by default
bean.name = 'Jochen'
assert swing.textField.text != bean.name
// test for no update on bean change
bean = new DummyBean()
bean.name = 'Alex'
swing.dummyBean.setModel(bean)
assert swing.textField.text != bean.name
//test for auto-update
swing.bindProxy(bean, id: 'boundDummyBean', bind: true)
swing.textField(id: 'boundTextField', text: swing.boundDummyBean.name)
assert swing.boundTextField.text == bean.name
bean.name = 'Danno'
assert swing.boundTextField.text == bean.name
// old model binding could be listening...
assert swing.textField.text != bean.name
}
}
void testModelUpdate() {
testInEDT {
SwingBuilder swing = new SwingBuilder()
def bean = new DummyBean()
swing.bindProxy(bean, id: 'dummyBean', bind: true)
// test initial binding
bean.name = 'Jochen'
swing.textField(id: 'textField', text: swing.dummyBean.name)
assert swing.textField.text == bean.name
// test for update on bean change
bean = new DummyBean()
bean.name = 'Alex'
swing.dummyBean.setModel(bean)
assert swing.textField.text == bean.name
// now simply one more live update: test fails here because
// setModel() has called rebind() instead of bind()!
bean.name = 'Danno'
assert swing.textField.text == bean.name
// test unbound, nothing should update no matter what we do
swing.dummyBean.unbind()
bean.name = 'Guillaume'
assert swing.textField.text != bean.name
bean = new DummyBean()
bean.name = 'Andres'
swing.dummyBean.setModel(bean)
assert swing.textField.text != bean.name
bean.name = 'James'
assert swing.textField.text != bean.name
// but a manual update should work
swing.dummyBean.update()
assert swing.textField.text == bean.name
}
}
def mutualPropertyShortWorkout = { source, sourceProperty, sourceMutators,
target, targetProperty, targetMutators, binding ->
sourceMutators[0]()
targetMutators[0]()
// test forward binding
assert source[sourceProperty] == target[targetProperty]
sourceMutators[1]()
assert source[sourceProperty] == target[targetProperty]
sourceMutators[0]()
assert source[sourceProperty] == target[targetProperty]
// test reverse binding
targetMutators[1]()
assert source[sourceProperty] == target[targetProperty]
targetMutators[0]()
assert source[sourceProperty] == target[targetProperty]
}
def mutualPropertyWorkout = { source, sourceProperty, sourceMutators,
target, targetProperty, targetMutators, binding ->
mutualPropertyShortWorkout(source, sourceProperty, sourceMutators,
target, targetProperty, targetMutators, binding)
// test rebound
binding.rebind()
targetMutators[1]()
assert source[sourceProperty] == target[targetProperty]
sourceMutators[0]()
assert source[sourceProperty] == target[targetProperty]
// test unbound not updating
binding.unbind()
sourceMutators[1]()
assert source[sourceProperty] != target[targetProperty]
targetMutators[1]()
assert source[sourceProperty] == target[targetProperty]
sourceMutators[0]()
assert source[sourceProperty] != target[targetProperty]
// test manual forward update
sourceMutators[0]()
assert source[sourceProperty] != target[targetProperty]
binding.update()
assert source[sourceProperty] == target[targetProperty]
// test manual reverse update
sourceMutators[1]()
assert source[sourceProperty] != target[targetProperty]
binding.reverseUpdate()
assert source[sourceProperty] == target[targetProperty]
}
void testMutualPropertyBinding() {
testInEDT {
['full', 'source', 'target'].each { mode -> // contextual bind mode
['prop', 'synth'].each { target -> // target binding
['prop', 'synth'].each { source -> // source binding
println "Trying $mode binding on $source source and $target target"
SwingBuilder swing = new SwingBuilder()
def sProp, tProp
swing.actions {
switch (source) {
case 'prop':
sProp = 'enabled'
st = new BindableBean(text: 'Baz')
break
case 'synth':
sProp = 'selected'
st = textField(text: 'Baz')
break
default: fail()
}
switch (target) {
case 'prop':
tProp = 'enabled'
tt = new BindableBean(text: 'Baz')
break
case 'synth':
tProp = 'selected'
tt = textField(text: 'Baz')
break
default: fail()
}
switch (mode) {
case 'full':
checkBox(id: 'cb1')
checkBox(id: 'cb2')
bind(source: cb1, sourceProperty: sProp,
target: cb2, targetProperty: tProp,
id: 'binding', mutual: 'true')
bind('text', source: st, target: tt,
id: 'textBinding', mutual: 'true')
break
case 'source':
checkBox(id: 'cb2')
checkBox(id: 'cb1', "$sProp": bind(
target: cb2, targetProperty: tProp,
id: 'binding', mutual: 'true'))
bean(st, text: bind(
target: tt, 'text',
id: 'textBinding', mutual: 'true'))
break
case 'target':
checkBox(id: 'cb1')
checkBox(id: 'cb2', "$tProp": bind(
source: cb1, sourceProperty: sProp,
id: 'binding', mutual: 'true'))
bean(tt, text: bind(
source: st, 'text',
id: 'textBinding', mutual: 'true'))
break
default: fail()
}
}
mutualPropertyWorkout(swing.cb1, sProp, [{swing.cb1[sProp] = true}, {swing.cb1[sProp] = false}],
swing.cb2, tProp, [{swing.cb2[tProp] = true}, {swing.cb2[tProp] = false}],
swing.binding)
mutualPropertyWorkout(swing.st, 'text', [{swing.st.text = "Foo"}, {swing.st.text = "Bar"}],
swing.tt, 'text', [{swing.tt.text = "Foo"}, {swing.tt.text = "Bar"}],
swing.textBinding)
}
if (mode != 'source') {
println "Trying $mode binding on event source and $target target"
SwingBuilder swing = new SwingBuilder()
def tProp
swing.actions {
st = button(actionCommand: 'Baz')
switch (target) {
case 'prop':
tProp = 'enabled'
tt = new BindableBean(text: 'Baz')
break
case 'synth':
tProp = 'selected'
tt = textField(text: 'Baz')
break
default: fail()
}
switch (mode) {
case 'full':
checkBox(id: 'cb1')
checkBox(id: 'cb2')
bind(source: cb1, sourceEvent: 'actionPerformed', sourceProperty: 'borderPaintedFlat',
target: cb2, targetProperty: tProp,
id: 'binding', mutual: 'true')
bind('text', source: st, sourceEvent: 'actionPerformed', sourceProperty: 'actionCommand', target: tt,
id: 'textBinding', mutual: 'true')
break
case 'target':
checkBox(id: 'cb1')
checkBox(id: 'cb2', "$tProp": bind(source: cb1,
sourceEvent: 'actionPerformed', sourceProperty: 'borderPaintedFlat',
id: 'binding', mutual: 'true'))
bean(tt, text: bind(
source: st, 'actionCommand', sourceEvent: 'actionPerformed',
id: 'textBinding', mutual: 'true'))
break
default: fail()
}
mutualPropertyShortWorkout(swing.cb1, 'borderPaintedFlat',
[
{swing.cb1.borderPaintedFlat = true; swing.cb1.doClick()},
{swing.cb1.borderPaintedFlat = false; swing.cb1.doClick()}
],
swing.cb2, tProp, [{swing.cb2[tProp] = true}, {swing.cb2[tProp] = false}],
swing.binding)
mutualPropertyShortWorkout(swing.st, 'actionCommand',
[
{swing.st.actionCommand = "Foo"; swing.st.doClick()},
{swing.st.actionCommand = "Bar"; swing.st.doClick()}
],
swing.tt, 'text', [{swing.tt.text = "Foo"}, {swing.tt.text = "Bar"}],
swing.textBinding)
}
}
}
}
println "finished all permutations successfully"
}
}
void testConverter() {
testInEDT {
SwingBuilder swing = new SwingBuilder()
def model = new BindableBean()
def toNumber = { v ->
if (v == null || v == "") return null
try { return new Integer(v) }
catch (x) { return null }
}
swing.actions {
frame(title: "Binding test", size: [100, 60]) {
textField(id: "t1", text: bind(target: model,
targetProperty: "value", converter: {toNumber(it)}))
textField(id: "t2", text: bind(target: model,
'floatValue', value: '1234', converter: { Float.parseFloat(it) * 2 }))
}
}
assert model.floatValue == 2468
swing.t1.text = '1234'
assert model.value == 1234
}
}
void testValidator() {
testInEDT {
SwingBuilder swing = new SwingBuilder()
def model = new BindableBean()
def isInteger = { v ->
try {
Float.parseFloat(v)
return true
} catch (NumberFormatException ignore) {
return false
}
}
swing.actions {
frame(title: "Binding test", size: [100, 60]) {
textField(id: "t1", text: bind(target: model, value: '123',
targetProperty: "value", validator: isInteger, converter: Integer.&parseInt))
}
}
assert model.value == 123
swing.t1.text = '1234'
assert model.value == 1234
swing.t1.text = 'Bogus'
assert model.value == 1234
swing.t1.text = '12345'
assert model.value == 12345
}
}
void testBindableVetoable() {
testInEDT {
def bbean = new BindableBean()
bbean.vetoableChange = { PropertyChangeEvent pce ->
if (pce.newValue ==~ /.*[ ]+.*/) {
throw new PropertyVetoException("No spaces allowed", pce)
}
}
def abean = null
SwingBuilder.build {
abean = bean(new BindableBean(), text: 'text', id: 'tf')
bean(bbean, vetoField: bind { tf.text })
}
abean.text = "test1"
assert abean.text == bbean.vetoField
abean.text = "this should fail"
assert abean.text != bbean.vetoField
}
}
void testGroovy4627_source_binding() {
testInEDT {
SwingBuilder swing = new SwingBuilder()
BindableBean model = new BindableBean(text: '0')
swing.actions {
bindGroup(id: 'formElements')
textField(id: 'txt1', text: bind(source: model, sourceProperty: 'text', group: formElements))
textField(id: 'txt2', text: bind(source: model, sourceProperty: 'text', group: formElements))
}
assert model.text == '0'
assert swing.txt1.text == '0'
assert swing.txt2.text == '0'
swing.formElements.unbind()
model.text = '1'
assert swing.txt1.text == '0'
assert swing.txt2.text == '0'
swing.formElements.rebind()
swing.formElements.update()
assert swing.txt1.text == '1'
assert swing.txt2.text == '1'
model.text = '2'
assert swing.txt1.text == '1'
assert swing.txt2.text == '1'
swing.formElements.update()
assert swing.txt1.text == '2'
assert swing.txt2.text == '2'
}
}
void testGroovy4627_target_binding() {
testInEDT {
SwingBuilder swing = new SwingBuilder()
BindableBean model = new BindableBean()
swing.actions {
bindGroup(id: 'formElements')
textField(id: 'txt1', text: bind(target: model, targetProperty: 'text', group: formElements, value: '0'))
}
assert model.text == '0'
assert swing.txt1.text == '0'
swing.formElements.unbind()
swing.txt1.text = '1'
assert model.text == '0'
swing.formElements.rebind()
swing.formElements.update()
assert model.text == '1'
swing.txt1.text = '2'
assert model.text == '1'
swing.formElements.update()
assert model.text == '2'
}
}
}
@Bindable class BindableBean {
boolean enabled
Integer value
float floatValue
int pvalue
String text
Date date
@Vetoable String vetoField
}