XenonDataGrid M2, Nelson Framework (Milestone 2)*WIP*
24 August 2009 Comments off
Reading time:
17 minutes
Word count:
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