JavaFX – A Poor Man’s Form Designer


Person Form - Using Poor Man's Form Designer

Person Form

Introduction (Updated 2)

NOTE: If you’ve been here before and found this blog entry useful please note that I have updated it again (for good reason).  Let me explain why and what I’ve updated. Since I have found that I was becoming quite productive generating forms, I wanted a more decoupled approach, so I re-factored the source code to be more reusable and easily up-datable. To use the form designer for your applications you can skip down to the section called “Using the Form Designer” to help you create your own forms in no time at all! (I also had a crack at making the form a little more attractive too).

What is a Form Designer?

Software developers who build form based applications will often struggle with positioning controls dynamically while a window resizes. This is better known as “Layout Management”. Many developers will rely on GUI builders that provide a “WYSIWYG” ability allowing a designer to drag and drop components onto a canvas area. On the other hand some developers prefer to hand code GUI form screens. There are pros and cons to using both strategies; however in this article I will strike a balance by building a form designer to allow a developer to assist them in hand coding GUI form screens (sound strange, just read on). While the JavaFX community has anticipated for GUI designer tools, I couldn’t wait. So, I decided to create the “Poor Man’s Form Designer”. This simple designer tool uses the popular MigLayout by Mikael Grev and was ported over to the JavaFX platform by Dean Iverson (an author of the book “Pro JavaFX Platform”) into the JFXtras project. The PMFD‘s source code consists of one file Main.fx and just at around 370 lines of code including comments. After the Conclusion section of this article you will see the full source code to the demo and form designer. Simply click on the source code links to expand and your on your way to developing beautiful looking forms!

Poor Man's Form Designer Constraints Window

Poor Man's Form Designer Constraints Window

Note: You will notice I forgot to add column constraints. The designer now has the ability to adjust the column constraints.

Demo

Demo

Instructions:

  1. Click Launch button above
  2. Go to the MigLayout cheat sheet at http://migcalendar.com/miglayout/cheatsheet.html to keep handy when tweaking the “Form Design Constraint Editor” window.
  3. Go to the “Form Design Constraint Editor” and click the dump button. Here it will dump all the current constraints for the following areas: Layout, Column/Row and Components constraints.
  4. Go to the “Form Design Constraint Editor” window under the Component Constraint area is a combo box please select firstNameLabel, then tab to the constraint text field (just right of combo box) and type “align left“. Click Apply button to update changes.
  5. Be sure to observe the Form View window and you’ll notice the “First Name:” label is left justified beside the first name text field.
  6. Play around with other constraint areas. Cut and paste from the cheat sheet. The cheat sheet is divided in three sections: Layout Constraints, Column & Row Constraints and Component constraints. Don’t forget to hit the apply button.
  7. You’ll notice in the component constraint area when selecting a control in the combo box the previous constraint is preserved and displayed in the constraint text box.
  8. Go to the “Form Design Constraint Editor” and click the dump button. Here it will dump all the current constraints for the following areas: Layout, Column/Row and Components constraints.
  9. Copy and paste those dumped constraints to assist in your hand coded GUI Form. (See below “Using the Form Designer” section, Steps 3 & 4)

Disclaimer: While the coding details below seem strange,  you may want to visit Dean’s Pleasing Software blog entry “MigLayout for JavaFX Reloaded” regarding how to Get started with MigLayout and JavaFX. http://pleasingsoftware.blogspot.com/search?q=layout

Using the Form Designer

Step 1: Create a new JavaFX project / setup to include JFXtras libraries and MigLayout jar files

Step 2: Copy Source code poormans_form_designer.fx expand put into your project. (check package statement)

Step 3: Create a form (class) which extends MigLayout from the JFXtras project. Simply add two attributes:

  1. componentsConstraints – Sequence of String objects
  2. nodesToLayout – Sequence of Node objects

Although this seems odd, I plan to possibly create a Mixin class or a derived MigLayout class to carry these attributes. Internally each Node contains a layoutInfo which contains a MigNodeLayoutInfo object (see http://jfxtras.org/portal/core/-/wiki/JFXtras/MigLayout for details). You should notice that the nameform.fx does not have dependencies to the designer library code, thus allowing the form developer to use CustomNode if they choose.

Create a Form (see nameform.fx for full source code)

public class NameForm extends MigLayout {
  public var componentsConstraints:String[] = [];
  public var nodesToLayout:Node[] = [];

  init {

    // ... other controls
    var firstNameLabel:Label = Label {
      id: "firstNameLabel"
      text: "First Name"

      font : Font {
          size: 18
      }
    };

    var firstNameField:TextBox = TextBox {
      id:"firstNameField"
    };
    // ... other controls

   nodesToLayout = [
      sectionFullName,  // 0
      instructionsText, // 1
      firstNameLabel,   // 2
      firstNameField,   // 3
      // ... all controls
   ];
    // +---------------------------------------------------
    // ! Poor Man's designer constraints info goes below
    // +---------------------------------------------------
    // [BEGIN]

    constraints = "";
    columns = "[pref]10[fill]";
    rows = "[pref]10[pref]";
    componentsConstraints = [
      "span, growx, wrap",                // fullNameTitle 0
      "span, wrap 15px",               // instructionsText 1
      "align right",                     // firstNameLabel 2
      "span, w min:100:300, wrap",       // firstNameField 3
      "align right",                    // middleNameLabel 4
      "growx, w min:100:300, wrap",     // middleNameField 5
      "align right",                      // lastNameLabel 6
      "growx,  w min:100:300, wrap",      // lastNameField 7
      "align right",                    // suffixNameLabel 8
      " w min:100:300, wrap 15",        // suffixNameField 9
      "span, wrap",                           // dobTitle 10
      "align right",                          // dobLabel 11
      " w min:100:pref, wrap 15",             // dobField 12
      "span, wrap",                           // pobTitle 13
      "align right",                      // pobCityLabel 14
      "span, growx,  w min:100:300, wrap",  // pobCityField 15
      "align right",                    // pobCountyLabel 16
      "span, growx,  w min:100:300, wrap",  // pobCountyField 17
      "align right",                     // pobStateLabel 18
      "span, w pref:100:pref + 10, wrap",  // pobStateField 19
    ];

    // [END]
    // +---------------------------------------------------
    // ! Poor Man's designer constraints info goes above
    // +---------------------------------------------------
  }

  // IMPORTANT - This will generate mig nodes to be put
  // into your content to be displayed in the scene.
  postinit {
    content = for (i in [0.. sizeof nodesToLayout -1]) {
      migNode(nodesToLayout[i], componentsConstraints[i]);
    }
  }

}

Note: Next, will be Step 3, Launching your newly created MigLayout form into the designer. Once you are happy with the constraints you may press the “Dump” button to output the code that you will cut and paste into your form above. Place code between your [BEGIN] and [END] comment tags (Between lines 32 & 60)

Step 3: Launching your newly created MigLayout form into the designer (see Main.fx for full source code below conclusion)

Create a Main.fx to launch form into the Poor Man’s Form Designer tool. (see poormans_form_designer.fx for full source code below conclusion )

  // http://pleasingsoftware.blogspot.com/search?q=layout
  // Create name form
  def nameForm:nameform.NameForm = NameForm{
  };

  // Remove content, due to nodes placed in the migNameForm later
  delete nameForm.content;

  // Create a miglayout designer object
  def designer:MigLayoutDesigner = MigLayoutDesigner{
      layoutConstraint: nameForm.constraints
      columnsConstraint: nameForm.columns
      rowsConstraint: nameForm.rows
      componentsConstraints: nameForm.componentsConstraints
      nodesToLayout: nameForm.nodesToLayout
  };

  // launch designer
  poormans_form_designer.launch(designer, null, null);

Troubleshooting

  • Missing imports or resolving – NetBeans: Control-Shift-I . Eclipse: Control-Shift-O . In NetBeans when using script level functions or classes inside a script file just use the ‘*’ wild card. ie: import poormans_form_designer.*;
  • Script file does not contain correct package name. ie: package xyz;
  • Form does not have two attributes called nodesToLayout and componentsConstraints.
  • If Form is extending from MigLayout it should set its content using the two known attributes. ie: content = for (i in [0.. sizeof nodesToLayout -1]) {
    migNode(nodesToLayout[i], componentsConstraints[i]);
    }

Enhancements

Above you will see hand coded controls that eventually get displayed onto the Form View window (Person Form). Since the the GUI control code elements are simply sequential, I believe it would be quite easy to add, insert and remove controls dynamically onto the Form View window area.  Also, possibly a property sheet window for controls, skins and behavior swapping.

Conclusion

The main goal is to properly lay out components in order to create a nice looking forms and also to learn the popular layout framework library MigLayout with it’s constraints language syntax. Using JavaFX‘s binding allows the form designer tool to enable the user to easily tweak constraints on the fly without having to rerun an application GUI for every adjustment made during the layout process in a form view. Another interesting thing to note is that since we are in the JavaFX world we can layout shapes, custom nodes and graphics (not just regular form controls). As a reminder regarding the use of JFXtras MigLayout, users should read about the known issues, please go to JFXtras MigLayout wiki at: http://jfxtras.org/portal/core/-/wiki/JFXtras/MigLayout . Well, by the time you read this blog entry I’m sure a nice GUI form designer/builder tool will be available.

The source code is listed below (click source code link to expand):

Note: I am in the process of putting the zipped up project onto the JFXtras.org for easy downloading.

Requirements:

  • Java 6 JDK or greater
  • JavaFX 1.2.1 SDK
  • JFXtras version 0.5 jars
  • latest Miglayout jar

Main.fx –

/*
 * Main.fx
 * @author Carl Dea
 * Created on Jan 9, 2010, 9:06:36 PM
 */
package migtest3;

import javafx.stage.Stage;
import migtest3.nameform.*;
import javafx.scene.Scene;
import org.jfxtras.scene.ResizableScene;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;
import javafx.scene.paint.Color;
import migtest3.poormans_form_designer.*;

function run(__ARGS__ : String[]) {

  // Create name form
  def nameForm:nameform.NameForm = NameForm{
  };

  // Remove content, due to nodes placed in the migNameForm later
  delete nameForm.content;

  // Create a miglayout designer object
  def designer:MigLayoutDesigner = MigLayoutDesigner{
      layoutConstraint: nameForm.constraints
      columnsConstraint: nameForm.columns
      rowsConstraint: nameForm.rows
      componentsConstraints: nameForm.componentsConstraints
      nodesToLayout: nameForm.nodesToLayout
  };

  // Create a custom scene. Note: this overrides default
  // scene inside launch PMFD script function.
  var newDesignerViewScene:Scene = ResizableScene {
    fill: LinearGradient {
      startX : 0.0
      startY : 0.0
      endX : 1.0
      endY : 0.0
      stops: [
        Stop {
          color : Color.rgb(251, 251, 251)
          offset: 0.0
        },
        Stop {
          color : Color.rgb(240, 240, 240)
          offset: 1.0
        },
     ]
    }
    content: [designer.dynamicMigLayout] // use the dynamicMigLayout
  }; // scene

  // create a custom Stage. Note: this overrides default
  // Stage inside launch PMFD script function.
  var newDesignerStageView = Stage {
      y:150
      x:100
      width: 490
      height: 600
      scene:newDesignerViewScene
      visible:true
  };

  // launch designer
  poormans_form_designer.launch(designer, newDesignerViewScene, newDesignerStageView);
}

nameform.fx –

/*
 * nameform.fx
 *
 * @author Carl Dea
 * Created on Jan 9, 2010, 11:03:57 AM
 */

package migtest3;

import javafx.ext.swing.SwingComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.TextBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.scene.Node;
import javafx.scene.CustomNode;
import javafx.ext.swing.SwingComboBoxItem;
import javafx.scene.Group;
import javafx.scene.shape.Rectangle;
import org.jfxtras.scene.layout.MigLayout;

public var US_STATES = [
  "AL","AK","AS","AZ","AR","CA","CO", "CT","DE","DC","FM","FL","GA","GU","HI","ID","IL","IN","IA","KS",
  "KY","LA","ME","MH","MD","MA","MI","MN","MS","MO","MT","NE","NV","NH","NJ","NM","NY","NC","ND","MP",
  "OH","OK","OR","PW","PA","PR","RI","SC","SD","TN","TX","UT","VT","VI","VA","WA","WV","WI","WY"
];

public function comboItemSeqCreator(names:String[]):SwingComboBoxItem[]{
  for( n in names) SwingComboBoxItem{ text:n };
}

public class SectionTitleHeader extends CustomNode {
  public-init var text:String;
  public var height;
  public var width;
  public var rectangleColor:Color on replace {
    if (not FX.isInitialized(rectangleColor)){
      rectangleColor = Color.rgb(0,130,171);
    }
  };

  public override function create():Node{
    var title = Group {
      content: [
        Rectangle {
          x: 10,
          y: height - 85,
          arcHeight: 5,
          arcWidth: 5,
          width: bind width - 30,
          height: 30,
          stroke:Color.BLACK
          fill: rectangleColor
          opacity: .5
        },
        Text {
          x: 15,
          y: height - 65,
          content: text
          fill: Color.WHITE
          font: Font {
            name: "Arial Bold"
            letterSpacing: .20
            size: 20
          }
        }
      ]
    }; // title
    return title;
  }

}

public class NameForm extends MigLayout {
  public var componentsConstraints:String[] = [];
  public var nodesToLayout:Node[] = [];

  init {

    var sectionFullName:SectionTitleHeader = SectionTitleHeader {
      id:"fullNameTitle"
      text:"1  Full Name"
      width:bind sectionFullName.scene.stage.width
      height:100
      rectangleColor:Color.BLUE
    }

    def instructions:String =
      "- If you have only initials in your name, use them "
      "and enter (I/O) after the initial(s). \n"
      "- If you have no middle name, enter \"NMN\".\n"
      "- If you are \"Jr.,\" \"Sr.,\" etc. enter this in the box after your middle name.\n";

    var instructionsText:Text = Text {
      id: "instructionsText"
      content: instructions
      fill:Color.BLACK
      font : Font {
        embolden:true
        size: 14
      }
    };

    var firstNameLabel:Label = Label {
      id: "firstNameLabel"
      text: "First Name"

      font : Font {
          size: 18
      }
    };

    var firstNameField:TextBox = TextBox {
      id:"firstNameField"
    };

    var middleNameLabel:Label = Label {
      id: "middleNameLabel"
      text: "Middle Name"
      font : Font {
        size: 18
      }
    };

    var middleNameField:TextBox = TextBox {
      id:"middleNameField"
    };

    var lastNameLabel:Label = Label {
      id: "lastNameLabel"
      text: "Last Name"
      font : Font {
        size: 18
      }
    };

    var lastNameField:TextBox = TextBox {
      id:"lastNameField"
    };

    var suffixNameLabel:Label = Label {
      id: "suffixNameLabel"
      text: "Suffix"
      font : Font {
        size: 18
      }
    };

    var suffixNameField:TextBox = TextBox {
      id:"suffixNameField"
    };

    var sectionDob:SectionTitleHeader = SectionTitleHeader {
      id:"dobTitle"
      text:"2  Date of Birth"
      width: bind sectionDob.scene.stage.width
      height:100
      rectangleColor:Color.BLUE
    }

    var dobLabel:Label = Label {
      id: "dobLabel"
      text: "DOB"
      font : Font {
        size: 18
      }
    };

    var dobField:TextBox = TextBox {
      id:"dobField"
    };

    var sectionPlaceOfBirth:SectionTitleHeader = SectionTitleHeader {
      id: "pobTitle"
      text: "3  Place of Birth"
      width: bind sectionPlaceOfBirth.scene.stage.width
      height:100
      rectangleColor:Color.BLUE
    }
    var pobCityLabel:Label = Label {
      id: "pobCityLabel"
      text: "City"
      font : Font {
        size: 18
      }
    };

    var pobCityField:TextBox = TextBox {
      id:"pobCityField"
    };
    var pobCountyLabel:Label = Label {
      id: "pobCountyLabel"
      text: "County"
      font : Font {
        size: 18
      }
    };

    var pobCountyField:TextBox = TextBox {
      id:"pobCountyField"
    };

    var pobStateLabel:Label = Label {
      id: "pobStateLabel"
      text: "State"
      font : Font {
        size: 18
      }
    };
    var statesComboItems = comboItemSeqCreator(US_STATES);
    var pobStateField:SwingComboBox = SwingComboBox {
       id: "pobStateField"
       items: [statesComboItems]
       visible: true
    }

    var changeFieldsOpacity:Node[] = [
      firstNameField,
      middleNameField,
      lastNameField,
      suffixNameField,
      dobField,
      pobCityField,
      pobCountyField,
      pobStateField,
    ];
    changeOpacity(changeFieldsOpacity, .80);

   nodesToLayout = [
      sectionFullName,  // 0
      instructionsText, // 1
      firstNameLabel,   // 2
      firstNameField,   // 3
      middleNameLabel,  // 4
      middleNameField,  // 5
      lastNameLabel,    // 6
      lastNameField,    // 7
      suffixNameLabel,  // 8
      suffixNameField,  // 9
      sectionDob,       // 10
      dobLabel,         // 11
      dobField,         // 12
      sectionPlaceOfBirth,// 13
      pobCityLabel,     // 14
      pobCityField,     // 15
      pobCountyLabel,   // 16
      pobCountyField,   // 17
      pobStateLabel,    // 18
      pobStateField,    // 19
    ];

    // +---------------------------------------------------
    // ! Poor Man's designer constraints info goes below
    // +---------------------------------------------------
    // [BEGIN]

    constraints = "";
    columns = "[pref]10[fill]";
    rows = "[pref]10[pref]";
    componentsConstraints = [
      "span, growx, wrap",                // fullNameTitle 0
      "span, wrap 15px",               // instructionsText 1
      "align right",                     // firstNameLabel 2
      "span, w min:100:300, wrap",       // firstNameField 3
      "align right",                    // middleNameLabel 4
      "growx, w min:100:300, wrap",     // middleNameField 5
      "align right",                      // lastNameLabel 6
      "growx,  w min:100:300, wrap",      // lastNameField 7
      "align right",                    // suffixNameLabel 8
      " w min:100:300, wrap 15",        // suffixNameField 9
      "span, wrap",                           // dobTitle 10
      "align right",                          // dobLabel 11
      " w min:100:pref, wrap 15",             // dobField 12
      "span, wrap",                           // pobTitle 13
      "align right",                      // pobCityLabel 14
      "span, growx,  w min:100:300, wrap",  // pobCityField 15
      "align right",                    // pobCountyLabel 16
      "span, growx,  w min:100:300, wrap",  // pobCountyField 17
      "align right",                     // pobStateLabel 18
      "span, w pref:100:pref + 10, wrap",  // pobStateField 19
    ];

    // [END]
    // +---------------------------------------------------
    // ! Poor Man's designer constraints info goes above
    // +---------------------------------------------------
  }

  // IMPORTANT - This will generate mig nodes to be put
  // into your content to be displayed in the scene.
  postinit {
    content = for (i in [0.. sizeof nodesToLayout -1]) {
      migNode(nodesToLayout[i], componentsConstraints[i]);
    }
  }

}
public function changeOpacity(nodes:Node[], opacityLevel:Float):Void {
  for (n in nodes) {
    n.opacity = opacityLevel;
  }

}

poormans_form_designer.fx –

/*
 * poormans_form_designer.fx
 *
 * @author Carl Dea
 * Created on Jan 2, 2010, 8:05:06 PM
 */
package migtest3;

import javafx.ext.swing.SwingComboBox;
import javafx.ext.swing.SwingComboBoxItem;
import javafx.ext.swing.SwingComponent;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextBox;
import javafx.scene.paint.Color;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javafx.util.Sequences;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import org.jfxtras.scene.ResizableScene;
import org.jfxtras.scene.layout.MigLayout;
import org.jfxtras.scene.layout.MigLayout.*;
import org.jfxtras.stage.JFXDialog;

/** Title of the designer */
var designViewTitle:String = "Poor Man's Form Designer ver 0.2";

/**
 * This makes a node MigLayout capable.
 *
 */
public class MigLayoutDesigner {
  public var layoutConstraint:String = "";
  public var columnsConstraint:String = "";
  public var rowsConstraint:String = "";
  public var componentsConstraints:String[] = [];
  public var nodesToLayout:Node[];
  public var migNodesToLayout:Node[] = bind generateMigNodes(nodesToLayout, componentsConstraints);
  protected var dynamicMigLayout:MigLayout;

  postinit {
    dynamicMigLayout = createBindableMigLayout();
  }

//  public abstract function createMigLayoutNode():Void;
  /**
   * Creates a bound MigLayout.
   */
  bound public function createBindableMigLayout() {
    MigLayout {
        constraints: bind layoutConstraint
        columns: bind columnsConstraint
        rows: bind rowsConstraint
        content: bind migNodesToLayout
    }
  }
}

/**
 * Generates a sequence of Nodes wrapped with migNode function.
 */
bound public function generateMigNodes(nodes:Node[], componentConstraintStrings:String[]){
    for (i in [0.. sizeof nodes -1]) {
      migNode(nodes[i], componentConstraintStrings[i]);
    }
}

/**
 * @param migForm - a MigLayoutCapable form. Inherits from the mixin MigLayoutCapable.
 * @param designerViewScene - The designer View of the Scene. If null one will be created.
 * @param designerViewStage - The designer View of the Stage (Window). If null one will be created.
 */
public function launch(migForm:MigLayoutDesigner, designerViewScene:Scene, designerViewStage:Stage) {

  // List Comprehensions - Strings representing nodes' id
  var listIds = for( n in migForm.nodesToLayout) n.id;

  // List comprehensions - SwingComboBoxItems
  var comboboxItems = for( n in listIds) SwingComboBoxItem{ text:n };

  // Combo Box control with ids of each widget on form.
  var idListBox:SwingComboBox = SwingComboBox {
    text : bind componentIdSelection with inverse;
    items: [comboboxItems]
  }

  // select first one as default
  idListBox.selectedIndex = 0;

  ////////////////////////////////////////////////////
  // Layout constraints section
  ////////////////////////////////////////////////////
  var layoutConstraintLabel:Label = Label {
    text: "Layout Constraint"
     font: Font {
        embolden:true
        size: 20
     }
  }
  var layoutConstraintTextBox:TextBox = TextBox {
    text: migForm.layoutConstraint
    selectOnFocus: true
  }

  // Updates the layout constraint when pressed
  var layoutConstraintApplyButton:Button = Button {
    text: "Apply"
    action: function() {
        migForm.layoutConstraint = layoutConstraintTextBox.text;
    }
  }

  ////////////////////////////////////////////////////
  // Column constraints section
  ////////////////////////////////////////////////////
  var columnsConstraintLabel:Label = Label {
    text: "Columns Constraint"
     font: Font {
        embolden:true
        size: 20
     }
  }

  // Constraint textbox
  var columnsConstraintTextBox:TextBox = TextBox {
    text: migForm.columnsConstraint
    columns: 30
  };

  // Updates the columns constraint when pressed
  var columnsConstraintApplyButton:Button = Button {
     text: "Apply"
     action: function() {
        migForm.columnsConstraint = columnsConstraintTextBox.text;
     }
  };

  ////////////////////////////////////////////////////
  // Row constraints section
  ////////////////////////////////////////////////////
  var rowsConstraintLabel:Label = Label {
    text: "Rows Constraint"
     font: Font {
        embolden:true
        size: 20
     }
  }

  // Constraint textbox
  var rowsConstraintTextBox:TextBox = TextBox {
    text: migForm.rowsConstraint
    columns: 30
  };

  // Updates the rows constraint when pressed
  var rowsConstrainApplyButton:Button = Button {
     text: "Apply"
     action: function() {
        migForm.rowsConstraint = rowsConstraintTextBox.text;
     }
  };

  ////////////////////////////////////////////////////
  // Component constraints section
  ////////////////////////////////////////////////////
  var componentsConstraintLabel:Label = Label {
     text: "Nodes Constraints"
     font: Font {
        embolden:true
        size: 20
     }
  };

  // Constraint textbox
  var componentConstraintTextBox:TextBox = TextBox {
      columns: 30
  };

  var componentIdSelection: String on replace {
      componentConstraintTextBox.text = migForm.componentsConstraints [
        Sequences.indexByIdentity(listIds, componentIdSelection)
      ];
  };

  var componentConstraintApplyButton:Button = Button {
     text: "Apply"
     action: function() {
        for (i in [0.. sizeof migForm.nodesToLayout -1]) {
           if (migForm.nodesToLayout[i].id.equalsIgnoreCase(componentIdSelection)) {
              migForm.componentsConstraints[i] = componentConstraintTextBox.text;
              break;
           }
        }
     } // action
  };

  ////////////////////////////////////////////////////
  // MigLayout Dump constraints section
  ////////////////////////////////////////////////////
  var miglayoutDumpLabel:Label = Label {
     text: "MigLayout Constraints Dump"
     font: Font {
        embolden:true
        size: 20
     }
  };

  var constraintsDumpButton:Button = Button {
     text: "Dump"
     action: function() {
        //var miglayout:MigLayout = personFormScene.content[0] as MigLayout;
        var buffer:String;
        var migStructure:String;
        buffer = "constraints = \"{migForm.layoutConstraint}\";\n"
           "columns = \"{migForm.columnsConstraint}\"; \n"
           "rows = \"{migForm.rowsConstraint}\";\n"
           "componentsConstraints = [\n";

        for (i in [0.. sizeof listIds -1]) {
          //buffer2 = "{buffer2}\"  id: {listIds[i]} - {migForm.componentsConstraints[i]}\n";
          var strElement = "\"{migForm.componentsConstraints[i]}\", // {listIds[i]} {i} \n";
          var spaces = generateSpaces(55 - strElement.length());
          buffer = "{buffer}  \"{migForm.componentsConstraints[i]}\", {spaces} // {listIds[i]} {i} \n";
        }
        buffer = "{buffer}];\n";
        constraintsDumpTextArea.setText("{buffer}");
     }
  };

  // set up a swing wrapped text area box to hold dumped constraints.
  var constraintsDumpTextArea:JTextArea = new JTextArea();
  constraintsDumpTextArea.setColumns(40);
  constraintsDumpTextArea.setRows(20);
  constraintsDumpTextArea.setAutoscrolls(true);
  var sp = new JScrollPane(constraintsDumpTextArea);
  var constraintsDumpTextAreaJfx = SwingComponent.wrap(sp);

  // constrains for the constraint configure window.
  var editorComponentsConstraints = [
     "wrap",
     "growx",
     "wrap",
     "span, newline 15px, wrap",
     "growx",
     "wrap",
     "span, newline 15px, wrap",
     "growx",
     "wrap",
     "span, newline 15px, wrap",
     "growx",
     "grow",
     "",
     "newline 20px",
     "span, align right, wrap",
     "span, growx"
  ];

  var inputPanelToLayout = [
      layoutConstraintLabel,    // 0
      layoutConstraintTextBox,  // 1
      layoutConstraintApplyButton,// 2
      columnsConstraintLabel,   // 3
      columnsConstraintTextBox, // 4
      columnsConstraintApplyButton,// 5
      rowsConstraintLabel,      // 6
      rowsConstraintTextBox,    // 7
      rowsConstrainApplyButton, // 8
      componentsConstraintLabel,// 9
      idListBox,                // 10
      componentConstraintTextBox,// 11
      componentConstraintApplyButton, // 12
      miglayoutDumpLabel,       // 13
      constraintsDumpButton,    // 14
      constraintsDumpTextAreaJfx,// 15
    ];

// convert to miglayout type nodes wrapped.
var editorMigNodesToLayout:Node[] = bind generateMigNodes(inputPanelToLayout, editorComponentsConstraints);

// check if user of the api sent in a Scene or not.
var newDesignerViewScene:Scene = designerViewScene;
  if (designerViewScene == null) {
    newDesignerViewScene = ResizableScene {
      fill:  LinearGradient {
        startX : 0.0
        startY : 0.0
        endX : 1.0
        endY : 0.0
        stops: [
          Stop {
            color : Color.DARKTURQUOISE
            offset: 0.0
          },
          Stop {
            color : Color.WHITE
            offset: 1.0
          },
        ] // stops
      } // fill
      content: [migForm.dynamicMigLayout] // content
    }; // scene
  }

  // check if user of the api sent in a Stage or not.
  var newDesignerStageView:Stage = designerViewStage;

  if (designerViewStage == null) {
    // Form View (Person Form)
    newDesignerStageView = Stage {
      y:150
      x:100
      width: 500
      height: 500
      title: designViewTitle
      scene: newDesignerViewScene
      visible:true
      opacity: .94
    }
  } else {
    newDesignerStageView.scene = newDesignerViewScene;
    if ("".equals(newDesignerStageView.title.trim())) {
      newDesignerStageView.title = designViewTitle;
    }
  }

  // Generate Scene for design view if one doesn't exist
  var constraintInputWindowScene:Scene = ResizableScene {
     fill: Color.GRAY

     content: [
        MigLayout {
           content:bind inputPanelToLayout
        },
     ] // content
  };

  // Form Design Constraint Editor.
  var constraintInputWindow:JFXDialog = JFXDialog{
     title: "Form Design Constraints Editor"
     owner:newDesignerStageView
     x: newDesignerStageView.x + newDesignerStageView.width
     y: 150
     modal:false
     visible:true
     scene:constraintInputWindowScene
     height:500
     width: 600
  }
}

/** Assisting dump button to display constraints nicely
 * to ease cut and paste in to Form for the user.
 * @param numSpaces
 */
function generateSpaces(numSpaces:Integer):String {
  var spaces:String = "";
  for (i in [1..numSpaces]) {
    spaces = "{spaces} ";
  }
  return spaces;
}

References

MigLayout by Dean Iverson – Pleasing Softwarehttp://pleasingsoftware.blogspot.com/search?q=layout

JavaFX and Layouts by Amy Fowlerhttp://weblogs.java.net/blog/aim/archive/2009/09/10/javafx12-layout

JFXtras & MigLayouthttp://jfxtras.googlecode.com/svn/site/javadoc/release-0.5/org.jfxtras.scene.layout/org.jfxtras.scene.layout.MigLayout.html

MigLayout Cheatsheet http://migcalendar.com/miglayout/cheatsheet.html

JFXtras – Miglayout wikihttp://jfxtras.org/portal/core/-/wiki/JFXtras/MigLayout

Swing GUI Builder (formerly Project Matisse)http://netbeans.org/features/java/swing.html

Abeille Form Designerhttps://abeille.dev.java.net/

JForm Designerhttp://www.formdev.com/

WindowBuilderhttp://www.instantiations.com/windowbuilder/

Swing tutorialhttp://java.sun.com/docs/books/tutorial/uiswing/

SWThttp://www.eclipse.org/swt/

Swing layouthttp://java.sun.com/docs/books/tutorial/uiswing/layout/using.html

Swing GUI Builder (formerly Project Matisse)

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s