refactor: use property bindings in ThreadGroupGui, OpenModelThreadGroupGui
diff --git a/src/core/src/main/java/org/apache/jmeter/threads/gui/PostThreadGroupGui.java b/src/core/src/main/java/org/apache/jmeter/threads/gui/PostThreadGroupGui.java
index 8ef7bb4..3fffeb0 100644
--- a/src/core/src/main/java/org/apache/jmeter/threads/gui/PostThreadGroupGui.java
+++ b/src/core/src/main/java/org/apache/jmeter/threads/gui/PostThreadGroupGui.java
@@ -38,9 +38,7 @@
     }
 
     @Override
-    public TestElement createTestElement() {
-        PostThreadGroup tg = new PostThreadGroup();
-        modifyTestElement(tg);
-        return tg;
+    public TestElement makeTestElement() {
+        return new PostThreadGroup();
     }
 }
diff --git a/src/core/src/main/java/org/apache/jmeter/threads/gui/SetupThreadGroupGui.java b/src/core/src/main/java/org/apache/jmeter/threads/gui/SetupThreadGroupGui.java
index 7fde1c1..10a10d3 100644
--- a/src/core/src/main/java/org/apache/jmeter/threads/gui/SetupThreadGroupGui.java
+++ b/src/core/src/main/java/org/apache/jmeter/threads/gui/SetupThreadGroupGui.java
@@ -38,9 +38,7 @@
     }
 
     @Override
-    public TestElement createTestElement() {
-        SetupThreadGroup tg = new SetupThreadGroup();
-        modifyTestElement(tg);
-        return tg;
+    public TestElement makeTestElement() {
+        return new SetupThreadGroup();
     }
 }
diff --git a/src/core/src/main/java/org/apache/jmeter/threads/gui/ThreadGroupGui.java b/src/core/src/main/java/org/apache/jmeter/threads/gui/ThreadGroupGui.java
index 80fa3a8..4dd33c2 100644
--- a/src/core/src/main/java/org/apache/jmeter/threads/gui/ThreadGroupGui.java
+++ b/src/core/src/main/java/org/apache/jmeter/threads/gui/ThreadGroupGui.java
@@ -22,9 +22,9 @@
 import java.awt.BorderLayout;
 import java.awt.event.ItemEvent;
 import java.awt.event.ItemListener;
+import java.util.Arrays;
 
 import javax.swing.BorderFactory;
-import javax.swing.JCheckBox;
 import javax.swing.JLabel;
 import javax.swing.JPanel;
 import javax.swing.JTextField;
@@ -32,14 +32,15 @@
 import org.apache.jmeter.control.LoopController;
 import org.apache.jmeter.control.gui.LoopControlPanel;
 import org.apache.jmeter.gui.JBooleanPropertyEditor;
+import org.apache.jmeter.gui.JTextComponentBinding;
 import org.apache.jmeter.gui.TestElementMetadata;
 import org.apache.jmeter.testelement.TestElement;
-import org.apache.jmeter.testelement.property.BooleanProperty;
 import org.apache.jmeter.threads.AbstractThreadGroup;
 import org.apache.jmeter.threads.AbstractThreadGroupSchema;
 import org.apache.jmeter.threads.ThreadGroup;
 import org.apache.jmeter.threads.ThreadGroupSchema;
 import org.apache.jmeter.util.JMeterUtils;
+import org.apache.jorphan.gui.JEditableCheckBox;
 
 import net.miginfocom.swing.MigLayout;
 
@@ -61,7 +62,10 @@
 
     private JBooleanPropertyEditor delayedStart;
 
-    private final JCheckBox scheduler = new JCheckBox(JMeterUtils.getResString("scheduler"));
+    private final JBooleanPropertyEditor scheduler =
+            new JBooleanPropertyEditor(
+                    ThreadGroupSchema.INSTANCE.getUseScheduler(),
+                    JMeterUtils.getResString("scheduler"));
 
     private final JTextField duration = new JTextField();
     private final JLabel durationLabel = labelFor(duration, "duration");
@@ -83,15 +87,42 @@
         this.showDelayedStart = showDelayedStart;
         init();
         initGui();
+        if (showDelayedStart) {
+            bindingGroup.add(delayedStart);
+        }
+        bindingGroup.addAll(
+                Arrays.asList(
+                        new JTextComponentBinding(threadInput, AbstractThreadGroupSchema.INSTANCE.getNumThreads()),
+                        new JTextComponentBinding(rampInput, ThreadGroupSchema.INSTANCE.getRampTime()),
+                        new JTextComponentBinding(duration, ThreadGroupSchema.INSTANCE.getDuration()),
+                        new JTextComponentBinding(delay, ThreadGroupSchema.INSTANCE.getDelay()),
+                        sameUserBox,
+                        scheduler
+                )
+        );
+    }
+
+    @Override
+    public TestElement makeTestElement() {
+        return new ThreadGroup();
     }
 
     @Override
     public TestElement createTestElement() {
-        ThreadGroup tg = new ThreadGroup();
+        TestElement tg = makeTestElement();
+        // modifyTestElement is here for backward compatibility
         modifyTestElement(tg);
+        assignDefaultValues(tg);
         return tg;
     }
 
+    @Override
+    public void assignDefaultValues(TestElement element) {
+        super.assignDefaultValues(element);
+        element.set(ThreadGroupSchema.INSTANCE.getNumThreads(), 1);
+        element.set(ThreadGroupSchema.INSTANCE.getRampTime(), 1);
+    }
+
     /**
      * Modifies a given TestElement to mirror the data in the gui components.
      *
@@ -103,46 +134,22 @@
         if (tg instanceof AbstractThreadGroup) {
             ((AbstractThreadGroup) tg).setSamplerController((LoopController) loopPanel.createTestElement());
         }
-        tg.set(AbstractThreadGroupSchema.INSTANCE.getNumThreads(), threadInput.getText());
-        tg.setProperty(ThreadGroup.RAMP_TIME, rampInput.getText());
-        if (showDelayedStart) {
-            delayedStart.updateElement(tg);
-        }
-        tg.setProperty(new BooleanProperty(ThreadGroup.SCHEDULER, scheduler.isSelected()));
-        tg.setProperty(ThreadGroup.DURATION, duration.getText());
-        tg.setProperty(ThreadGroup.DELAY, delay.getText());
-        sameUserBox.updateElement(tg);
+        toggleSchedulerFields();
     }
 
     @Override
     public void configure(TestElement tg) {
         super.configure(tg);
-        threadInput.setText(tg.getString(AbstractThreadGroupSchema.INSTANCE.getNumThreads()));
-        rampInput.setText(tg.getPropertyAsString(ThreadGroup.RAMP_TIME));
         loopPanel.configure((TestElement) tg.getProperty(AbstractThreadGroup.MAIN_CONTROLLER).getObjectValue());
-        if (showDelayedStart) {
-            delayedStart.updateUi(tg);
-        }
-        scheduler.setSelected(tg.getPropertyAsBoolean(ThreadGroup.SCHEDULER));
-
-        toggleSchedulerFields(scheduler.isSelected());
-
-        duration.setText(tg.getPropertyAsString(ThreadGroup.DURATION));
-        delay.setText(tg.getPropertyAsString(ThreadGroup.DELAY));
-        sameUserBox.updateUi(tg);
     }
 
     @Override
     public void itemStateChanged(ItemEvent ie) {
-        if (ie.getItem().equals(scheduler)) {
-            toggleSchedulerFields(scheduler.isSelected());
-        }
+        // Method kept for backward compatibility
     }
 
-    /**
-     * @param enable boolean used to enable/disable fields related to scheduler
-     */
-    private void toggleSchedulerFields(boolean enable) {
+    private void toggleSchedulerFields() {
+        boolean enable = !scheduler.getValue().equals(JEditableCheckBox.Value.of(false));
         duration.setEnabled(enable);
         durationLabel.setEnabled(enable);
         delay.setEnabled(enable);
@@ -171,16 +178,7 @@
 
     // Initialise the gui field values
     private void initGui(){
-        threadInput.setText("1"); // $NON-NLS-1$
-        rampInput.setText("1"); // $NON-NLS-1$
         loopPanel.clearGui();
-        if (showDelayedStart) {
-            delayedStart.reset();
-        }
-        scheduler.setSelected(false);
-        delay.setText(""); // $NON-NLS-1$
-        duration.setText(""); // $NON-NLS-1$
-        sameUserBox.reset();
     }
 
     private void init() { // WARNING: called from ctor so must not be overridden (i.e. must be private or final)
@@ -211,7 +209,8 @@
                     JMeterUtils.getResString("delayed_start")); // $NON-NLS-1$
             threadPropsPanel.add(delayedStart, "span 2");
         }
-        scheduler.addItemListener(this);
+        scheduler.addPropertyChangeListener(
+                JBooleanPropertyEditor.VALUE_PROPERTY, (ev) -> toggleSchedulerFields());
 
         threadPropsPanel.add(scheduler, "span 2");
 
diff --git a/src/core/src/main/kotlin/org/apache/jmeter/threads/openmodel/gui/OpenModelThreadGroupGui.kt b/src/core/src/main/kotlin/org/apache/jmeter/threads/openmodel/gui/OpenModelThreadGroupGui.kt
index 055ed32..a83f4ff 100644
--- a/src/core/src/main/kotlin/org/apache/jmeter/threads/openmodel/gui/OpenModelThreadGroupGui.kt
+++ b/src/core/src/main/kotlin/org/apache/jmeter/threads/openmodel/gui/OpenModelThreadGroupGui.kt
@@ -19,11 +19,12 @@
 
 import net.miginfocom.swing.MigLayout
 import org.apache.jmeter.engine.util.CompoundVariable
+import org.apache.jmeter.gui.JTextComponentBinding
 import org.apache.jmeter.gui.TestElementMetadata
 import org.apache.jmeter.testelement.TestElement
 import org.apache.jmeter.threads.gui.AbstractThreadGroupGui
-import org.apache.jmeter.threads.openmodel.DefaultThreadSchedule
 import org.apache.jmeter.threads.openmodel.OpenModelThreadGroup
+import org.apache.jmeter.threads.openmodel.OpenModelThreadGroupSchema
 import org.apache.jmeter.threads.openmodel.ThreadSchedule
 import org.apache.jmeter.threads.openmodel.ThreadScheduleStep
 import org.apache.jmeter.threads.openmodel.asSeconds
@@ -69,6 +70,12 @@
                 updateExplanation()
             }
         })
+        bindingGroup.addAll(
+            listOf(
+                JTextComponentBinding(scheduleStringEditor, OpenModelThreadGroupSchema.schedule),
+                JTextComponentBinding(randomSeedEditor, OpenModelThreadGroupSchema.randomSeed)
+            )
+        )
     }
 
     private fun createPanel() =
@@ -87,6 +94,7 @@
 
             add(explanation)
             add(targetRateChart, "height 200")
+            updateExplanation()
         }
 
     private fun templateButton(title: String) = JButton(title).apply {
@@ -131,36 +139,5 @@
 
     private fun evaluate(input: String): String = CompoundVariable(input).execute()
 
-    override fun createTestElement(): TestElement =
-        OpenModelThreadGroup().also {
-            modifyTestElement(it)
-        }
-
-    override fun modifyTestElement(tg: TestElement) {
-        configureTestElement(tg)
-        tg as OpenModelThreadGroup
-        tg.scheduleString = scheduleStringEditor.text
-        tg.randomSeedString = randomSeedEditor.text
-    }
-
-    override fun configure(tg: TestElement) {
-        super.configure(tg)
-        tg as OpenModelThreadGroup
-        scheduleStringEditor.text = tg.scheduleString
-        randomSeedEditor.text = tg.randomSeedString
-    }
-
-    override fun clearGui() {
-        super.clearGui()
-        scheduleStringEditor.text = ""
-        randomSeedEditor.text = ""
-        targetRateChart.updateSchedule(
-            DefaultThreadSchedule(
-                listOf(
-                    ThreadScheduleStep.RateStep(0.0),
-                    ThreadScheduleStep.ArrivalsStep(ThreadScheduleStep.ArrivalType.RANDOM, 1.0)
-                )
-            )
-        )
-    }
+    override fun makeTestElement(): TestElement = OpenModelThreadGroup()
 }