| /* |
| * 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 |
| } |