PEAT Online Course -
How To Improve Your Personal Performance in Java work Technology

  • YOU + CONTROL
  • Focus
  • Autonomy
  • Proficiency
  • Purpose
  • Covid-19!!!
  • Deep Hidden Meaning
  • FREE updates
GET 75% OFF NOW
Your Promo code: TECHLAYOFF
Exclusively on
VIMEO ON-DEMAND

XenonDataGrid M2, Nelson Framework (Milestone 2)*WIP*

24 August 2009 Comments off

17 minutes

3768

XenonDataGrid M2, Nelson Framework (Milestone 2)*WIP*

Hi All

Yours truly I have just made Milestone 2.0 release of the Nelson Framework JavaFX Data Grid Component.

Changes

  • You can now scroll through a data grid.
  • There is a new reordering implementation of renderer cells in the grid layer
  • There is a new data structure Mapping and MappingGroup, which maintain position to index coordinate information.
  • Also the “cellLayer” has renamed to “bodyLayer”. The MasterLayer type is now composed of a corner, row header, column header and body cell layers
  • The javafx package structure has now been revised and refactored into separate comprehensible packages.
  • Incremental cell selection strategy has been disabled in the DefaultSelectionStrategy. (It is because the reordering of rows and columns breaks the design and U/X).

Starting with the root package:

Package
Description
com.xenonsoft.nelson.scene.layout This contains the XenonDataGrid AND AbstractXenonDataGrid component
com.xenonsoft.nelson.scene.layout.grid This package contains the table model and constraints class.
com.xenonsoft.nelson.scene.layout.grid.event This package contains event handling types
com.xenonsoft.nelson.scene.layout.grid.layer This package contains the layer classes and mixins
com.xenonsoft.nelson.scene.layout.grid.layout This package contains the data grid layout type, strategy and algorithms
com.xenonsoft.nelson.scene.layout.grid.renderer This package contains the renderer and drawable types.
com.xenonsoft.nelson.scene.layout.grid.selection This package contains the data grid selection context, strategy and algorithm.

Examples

Click on the Screenshots to run the file or the JNLP files to run the examples

Example 1

This is the same as the previous article. Non-scrollable, draggable row and columns. This test verifies the refactoring did not break the U/X of the initial example.

http://xenonsoft.com/jws/XDG-Example-01-1.0-M2.jnlp

Example 2

Here is a brand new example to demonstrate scrolling. Drag a body cell to scroll around the data set, draggable row and columns header cells.

http://xenonsoft.com/jws/XDG-Example-02-1.0-M2.jnlp

Example 3

Here is a brand new example to demonstrate scrolling. The user can use ScrollBars in order to scroll around the data set, draggable row and columns header cells.

http://xenonsoft.com/jws/XDG-Example-03-1.0-M2.jnlp

Example 4

Here is a brand new example to demonstrate scrolling. The user can use ScrollBars in order to scroll around the data set, draggable column header only. Double click a body cell in order to observe a perspective spin animation applied to the lead cell.

http://xenonsoft.com/jws/XDG-Example-04-1.0-M2.jnlp

Example 5

Here is a brand new example to demonstrate scrolling. The user can use ScrollBars in order to scroll around the data set, draggable row header only. Double click a body cell in order to observe animation effect, which happens in the pop up pane.

http://xenonsoft.com/jws/XDG-Example-05-1.0-M2.jnlp

DESIGN NOTE

There a five special scene graphs in the current design of the XDG, which are javafx.scene.Group nodes and are public-read access only. Actually there are currently declared in the AbstractXenonDataGrid. There are used to display cells, background and are very much like the layers in the old JLayerPane. The intention is allow animation and effect to be easily applied to a XDG component.

Name
Description
backPane a pane reserved for a background scenegraph node. (No Pun intended, this property name is shorter than “backgroundPane”)
centralPane a pane reserved for the layout of grid layer cells. The central pane sits above the backPane.
editorPane a pane reserved for the editor cell (TBD). The editor pane sits above the centralPane.
glassPane a pane reserved for the special FXs and animation. The glassPane sits above the  editorPane. (The current implementation of reordering of rows and columns, in the AbstractXenonDataGrid, uses the glassPane)
popupPane a pane reserved for the popup components. The popupPane sits above the glassPane.

See my other AudioBoo: Five Recommendations for JavaFX Table UI Component.

Understanding The Code

Here is the source code for the scrollable data grid, example 2 above.

// BigTableXDGTable.fx
package com.xenonsoft.nelson.scene.layout;

import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.text.Font;
import javafx.scene.paint.Color;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;
import javafx.scene.control.ScrollBar;

import com.xenonsoft.nelson.scene.layout.XenonDataGrid;
import com.xenonsoft.nelson.scene.layout.grid.TableModel;
import com.xenonsoft.nelson.scene.layout.grid.layer.MultiGridLayer;
import com.xenonsoft.nelson.scene.layout.grid.layer.GridLayer;
import com.xenonsoft.nelson.scene.layout.grid.renderer.XenonGridCellRenderer;
import com.xenonsoft.nelson.scene.layout.grid.renderer.XenonGridCellHeaderRenderer;
import com.xenonsoft.nelson.scene.layout.grid.selection.SelectionContext;

/**
 * @author Peter Pilgrim
 */
def MARGIN_WIDTH  = 5.0;
def MARGIN_HEIGHT = 5.0;
def OFFSET = 25.0;
def GAP_SIZE = 3.0;

// XDG size
def GRID_ROWS=8;
def GRID_COLUMNS=8;

// Model matrix size
def MATRIX_ROWS=50;
def MATRIX_COLUMNS=50;
def mt = new Matrix( MATRIX_ROWS, MATRIX_COLUMNS );
for ( r in  [0..mt.getRows()-1] ) {
    for ( c in  [0..mt.getColumns()-1] ) {
        mt.setData( r, c, “Cell_{r}_{c}”);
    }
}

def font: Font = Font { size: 12 };
def headerFont: Font = Font { name: “Arial Bold” size: 12 };

def backFill = LinearGradient {
    startX: 0.0, startY: 0.0, endX: 0.0, endY: 100
    proportional: false
    stops: [
        Stop { offset: 0 color: Color.GRAY },
        Stop { offset: 0.2 color: Color.BLACK }
    ]
}

var scene: Scene;
var dataGrid: XenonDataGrid;
var vertSB: ScrollBar;
var horzSB: ScrollBar;

var myScrollRow : Float = 0 on replace {
    dataGrid.scrollRow = myScrollRow as Integer;
};

var myScrollColumn : Float = 0 on replace {
    dataGrid.scrollColumn = myScrollColumn as Integer;
};

Stage {
    title: “Xenon Data Grid Demo #2 (Scrollable)”
    width: 750
    height: 500
    scene: scene = Scene {
        fill: Color.web(“#F0F0F0”)
        content: [
            vertSB = ScrollBar {
                layoutX: bind scene.width – OFFSET/2
                layoutY: MARGIN_HEIGHT
                width: OFFSET
                height: bind scene.height – ( 2 * MARGIN_HEIGHT + OFFSET + GAP_SIZE)
                min: 0
                max: mt.getRows() – GRID_ROWS
                vertical: true
                value: bind myScrollRow with inverse
            },

            horzSB = ScrollBar {
                layoutX: MARGIN_WIDTH
                layoutY: bind scene.height – OFFSET
                width: bind scene.width – ( 2 * MARGIN_WIDTH + OFFSET + GAP_SIZE)
                height: OFFSET
                min: 1
                max: mt.getColumns() – GRID_COLUMNS
                vertical: false
                value: bind myScrollColumn with inverse
            },

            dataGrid = XenonDataGrid {
                debug: true;

                layoutX: MARGIN_WIDTH
                layoutY: MARGIN_HEIGHT
                width: bind scene.width – (2 * MARGIN_WIDTH + OFFSET + GAP_SIZE )
                height: bind scene.height – ( 2 * MARGIN_HEIGHT + OFFSET + GAP_SIZE )
            
                headerCellFont: headerFont
                cellFont: font;

                masterLayer: MultiGridLayer {
                    cornerLayer:  GridLayer {
                        debug: true
                        rowSize: 1
                        columnSize: 1
                        renderers: [
                            XenonGridCellHeaderRenderer {
                                backgroundFill: Color.DARKGRAY
                                foregroundFill: Color.LIGHTGRAY
                            }
                        ]

                        tableModel: TableModel {
                            rowSize: 1 columnSize: 1 data: “Corner”
                        }
                    }

                    rowHeaderLayer:  GridLayer {
                        debug: true
                        rowSize: GRID_ROWS
                        columnSize: 1
                        renderers: [
                            for ( r in  [0..<GRID_ROWS] ) {
                                XenonGridCellHeaderRenderer {
                                    backgroundFill: backFill
                                    foregroundFill: Color.WHITE
                                }
                            }
                        ]

                        tableModel: TableModel {
                            rowSize: mt.getRows()
                            columnSize: 1
                            data: for ( r in  [0..mt.getRows()-1] ) {
                                “Row {r}” } }
                    }
                        

                    columnHeaderLayer:  GridLayer {
                        debug: true
                        rowSize: 1
                        columnSize: GRID_COLUMNS
                        renderers: [
                            for ( c in  [0..<GRID_COLUMNS] ) {
                                XenonGridCellHeaderRenderer {
                                    backgroundFill: backFill
                                    foregroundFill: Color.WHITE
                                }
                            }
                        ]

                        tableModel: TableModel {
                            rowSize: 1
                            columnSize: mt.getColumns()
                            data:
                            for ( c in  [0..mt.getColumns()-1] ) {
                                “Column {c}”
                            }
                        }
                    }

                    bodyLayer:  GridLayer {
                        debug: true
                        rowSize: GRID_ROWS
                        columnSize: GRID_COLUMNS
                        renderers: [
                            for ( r in  [0..<GRID_ROWS] ) {
                                for ( c in  [0..<GRID_COLUMNS] ) {
                                    XenonGridCellRenderer {
                                        backgroundFill: if ( r mod 2 == 1 ) Color.LIGHTGREEN else Color.LIGHTGRAY
                                    }
                                }
                            }
                        ]

                        tableModel: TableModel {
                            rowSize: mt.getRows()
                            columnSize: mt.getColumns()
                            data:
                            for ( r in  [0..mt.getRows()-1] ) {
                                for ( c in  [0..mt.getColumns()-1] ) {
                                if ( c == 2)
                                    “LONGTEXT {mt.getData( r, c).toString()}”
                                else
                                    mt.getData( r, c).toString()
                                }
                            }
                        }
                    }
                }
            }
        ]
    }
}

dataGrid.selectionStrategy.onUnselection = function ( ctx: SelectionContext ) {
    FX.println(“old selection ( {ctx.selectedRowMin},  {ctx.selectedColumnMin} ) – ( {ctx.selectedRowMax},  {ctx.selectedColumnMax} )”);
}

dataGrid.selectionStrategy.onSelection = function ( ctx: SelectionContext, oldCtx: SelectionContext ) {
    FX.println(“new selection ( {ctx.selectedRowMin},  {ctx.selectedColumnMin} ) – ( {ctx.selectedRowMax},  {ctx.selectedColumnMax} )”);
}

// END

In this example, we create a Java object type, Matrix, which represents our large table (50 rows down and 50 rows across, a grand total of 2500). XDG does not care where the data comes from, because it is contained in a TableModel type. Our scene contains three UI components, two scroll bars, horizontal and vertically respectively and the XeNoNDataGrid.

We have two script variables, myScrollRow and myScrollColumn, to listen to the changes in the scrollbar. There are two triggers on them, which update the XDG scroll position. Two indirect script variable are used indirectly instead of binding, because it allows for future updates in the reverse direction – XDG implementation can update the scroll position.

Pay particularly attention to the code, which declares each ScrollBar and set the maximum value. We avoid out of bound runtime exceptions if we can limit the coordinates to the confines of the table model

So we declare the Scene and scrollbars. We declare the XDG with a MultiGridLayer. We can optionally define the corner, column header and/or row header layers, but we must declare the body layer. The body layer is a GridLayer type, which accepts a sequence of renderer cells and a table model. Notice we set up the rowSize and columnSize of the grid layer’s renderers, which different to the user defined table model bounderies. It is this difference that provides the scrolling ability.

Conclusion

The article introduced the XenonDataGrid and its capabilities in scrolling data sets. The Milestone M2 code is downloadable from Project Kenai in binary and source release.

TODO: Milestone 3 will feature the ability to edit the data and it will show off editor renderer cell(s). There will be a fix to the ProportionalResizeLayoutStrategy (buggy). There will be ability to set the minimum and maximum width and heights of header cells. Finally there will possibly be a better selection model based on ranges.

This is Peter Pilgrim. Out.

STOP PRESS

There is a bug in the reordering of the rows and column, which I won’t fix it in the M2 release. The solution is to change this line 183 in the TableUtils.fx

var value Integer = (mapping.getByB( j ) as IntegerMapping ).getB();

to

var value Integer = (mapping.getByA( j ) as IntegerMapping ).getB();

Download the source code, build the distribution, then recompile the code and build the JAR. This reminds to ask Stephen Chin’s permission to put the JFXtras 0.5 JARs into a Maven Repository in the future.

See you for the M3 release

Hey all! Thanks for visiting. I provide fringe benefits to interested readers: checkout consultancy, training or mentorship Please make enquiries by email or
call +44 (0)7397 067 658.

Due to the Off-Payroll Working plan for the UK government, I am enforcing stricter measures on contracts. All potential public sector GOV.UK contracts engagements must be approved by QDOS and/or SJD Accounting. Please enquire for further information.