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!
Note: You will notice I forgot to add column constraints. The designer now has the ability to adjust the column constraints.
Demo
Instructions:
- Click Launch button above
- Go to the MigLayout cheat sheet at http://migcalendar.com/miglayout/cheatsheet.html to keep handy when tweaking the “Form Design Constraint Editor” window.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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:
- componentsConstraints – Sequence of String objects
- 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 Software – http://pleasingsoftware.blogspot.com/search?q=layout
JavaFX and Layouts by Amy Fowler – http://weblogs.java.net/blog/aim/archive/2009/09/10/javafx12-layout
JFXtras & MigLayout – http://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 wiki – http://jfxtras.org/portal/core/-/wiki/JFXtras/MigLayout
Swing GUI Builder (formerly Project Matisse) – http://netbeans.org/features/java/swing.html
Abeille Form Designer – https://abeille.dev.java.net/
JForm Designer – http://www.formdev.com/
WindowBuilder – http://www.instantiations.com/windowbuilder/
Swing tutorial – http://java.sun.com/docs/books/tutorial/uiswing/
SWT – http://www.eclipse.org/swt/
Swing layout – http://java.sun.com/docs/books/tutorial/uiswing/layout/using.html