NatTable 1.2.0 - New & Noteworthy
The NatTable 1.2.0 release was mainly made possible by UBS. There were a lot of contributions in form of ideas, bug reports, discussions and even new features like traversal handling, scaling for high resolutions and several more you can find in the following sections. We would like to thank UBS for their commitment and support on developing the 1.2.0 release.
Almost every change in code is tracked via ticket in Bugzilla, so if you are curious about the details and all the bugs that are fixed and enhancements that were added with the 1.2.0 release, have a look here.
Dependencies
- Removed dependency to Apache Commons Lang. It was used internally and caused issues for consumers of NatTable when setting up a target definition with newer versions of the Eclipse Platform.
- Replaced the dependency to org.eclipse.core.runtime bundle with org.eclipse.equinox.common to have a smaller set on dependent plugins. The dependency to the org.eclipse.equinox.common bundle is required by JFace, which is used for several NatTable dialogs.
API changes
- Added
ISortModel#getColumnComparator(int)
which is necessary to retrieve theComparator
for column values in order to be able to sort a tree according to the configured sorting. - Added
ICellEditor#activateOnTraversal(IConfigRegistry, List)
to be able to configure if an editor should be activated on traversal or not. Necessary because a checkbox shouldn’t change it’s value when automatically activated after committing a value in an adjacent editor. TheCheckBoxCellEditor
always returnsfalse
. The abstract implementations will check the newly introducedEditConfigAttributes#ACTIVATE_EDITOR_ON_TRAVERSAL
configuration attribute or returntrue
in case there is no such configuration attribute set. - Renamed
AggregrateConfigLabelAccumulator
toAggregateConfigLabelAccumulator
. - Renamed
ITreeRowModel#isCollapseable()
toITreeRowModel#isCollapsible()
. - Added
ITreeRowModel#expandToLevel(int)
,ITreeRowModel#expandToLevel(int, int)
andITreeRowModel#expandToLevel(T, int)
to be able to expand a tree to a certain level. - Deprecated
ITreeData#formatDataForDepth(int, int)
,ITreeData#formatDataForDepth(int, T)
andITreeRowModel#getObjectAtIndexAndDepth(int, int)
which were used byTreeExportFormatter
. Since it now correctly uses the configuredIDisplayConverter
to convert the values, these methods are not called from the framework anymore. - Deprecated all expand/collapse related methods in
GlazedListTreeData
and moved them toGlazedListTreeRowModel
where they should reside correctly. ISelectionModel
now extendsILayerEventHandler<IStructuralChangeEvent>
in order to be able to handle structural changes correctly dependent on the internal selection storage.- Deprecated
ColumnGroupMenuItemProviders
and moved the methods to the generalMenuItemProviders
. - Changed
MoveSelectionCommandHandler
moveLastSelectedXxx method parameters from int toITraversalStrategy
.
Enhancements and new features
-
Scaling support for high resolution displays
IntroducedIDpiConverter
andAbstractDpiConverter
that are used in conjunction with the newly introducedConfigureScalingCommand
to informILayer
andSizeConfig
instances about the scaling calculations that need to be performed. Extended theGUIHelper
for several methods related to scaling calculations and added the support for upscaled images. To add different images for different DPI settings, simply put images whose names are prefixed with the DPI values to the same place as the base image.The following shows how to provide a set of images that are used on scaling. The file checkbox.png will be used for 96 DPI, the others specify their DPI setting in the filename.
- checkbox.png
- checkbox_120_120.png
- checkbox_128_128.png
- checkbox_144_144.png
- checkbox_192_192.png
- checkbox_288_288.png
If no image is found for the current DPI value, the base image will be upscaled.
-
Fixed summary row
Introduced theFixedSummaryRowLayer
that renders a summary row at a fixed position if added to aCompositeLayer
. It can be configured for usage within a simple horizontal composition aswell as for usage within a grid.The following code shows how to add a fixed summary row above the viewport of a simple NatTable without column and row header.
// Plug in the FixedSummaryRowLayer FixedSummaryRowLayer summaryRowLayer = new FixedSummaryRowLayer(dataLayer, viewportLayer, configRegistry, false); // configure that the horizontal dimensional dependency // is not a CompositeLayer that adds an additional column summaryRowLayer.setHorizontalCompositeDependency(false); CompositeLayer composite = new CompositeLayer(1, 2); composite.setChildLayer("SUMMARY", summaryRowLayer, 0, 0); composite.setChildLayer(GridRegion.BODY, viewportLayer, 0, 1);
The following code shows how to add a fixed summary row at the bottom of a grid.
// create the FixedGridSummaryRowLayer FixedSummaryRowLayer summaryRowLayer = new FixedSummaryRowLayer( gridLayer.getBodyDataLayer(), gridLayer, configRegistry, false); // set the summary label that should be shown in the row header summaryRowLayer.setSummaryRowLabel("\\u2211"); // create a composition that has the grid on top and the summary // row at the bottom CompositeLayer composite = new CompositeLayer(1, 2); composite.setChildLayer("GRID", gridLayer, 0, 0); composite.setChildLayer(SUMMARY\_REGION, summaryRowLayer, 0, 1);
In case the
FixedSummaryRowLayer
should be shown in a grid on top of the body stack below the column header, the layer composition needs some additional modifications. TheCompositeLayer
needs to be top most layer in the body region (which is typically theViewportLayer
). The row header layer needs to be of typeFixedSummaryRowHeaderLayer
in order to render the row header cell for the additional summary row on top.You will find examples in the NatTable Examples Application in
Tutorial Examples -> Layers -> SummaryRow
that show in detail how to setup a layer composition with a fixed summary row. -
Traversal strategy
IntroducedITraversalStrategy
to be able to configure editing traversal and selection movement details. In short this means that you are able to specify if the selection anchor should cycle if moved over a table border and if the selection/focus should jump over cells.An
ITraversalStrategy
specifies aTraversalScope
, if the traversal should cycle, the step count and whether the target is valid. There are four default implementations that are only different forTraversalScope
and cycle configuration. All are configured for step count = 1 and accepting every target cell as valid.ITraversalStrategy#AXIS_TRAVERSAL_STRATEGY
TraversalScope = AXIS
cycle = false
On traversal we only see the current row/column and will stop at a table border. This is the known default behavior in NatTable.ITraversalStrategy#AXIS_CYCLE_TRAVERSAL_STRATEGY
TraversalScope#AXIS
cycle = true
On traversal we only see the current row/column. On moving over a table border, the selection will move to the opposite side. E.g. moving over the last column in row 2 will set the selection to the first column in the same row.ITraversalStrategy#TABLE_TRAVERSAL_STRATEGY
TraversalScope#TABLE
cycle = false
On traversal we see the whole table. On moving over a table border, the selection will move to the opposite side by an additional row/column. E.g. moving to the right over the last column in row 2 will set the selection to the first column in the next row.
Since cycle is set tofalse
, the traversal will stop at table borders (last column/last row or first column/first row).ITraversalStrategy#TABLE_CYCLE_TRAVERSAL_STRATEGY
TraversalScope#TABLE
cycle = true
On traversal we see the whole table. On moving over a table border, the selection will move to the opposite side by an additional row/column. E.g. moving to the right over the last column in row 2 will set the selection to the first column in the next row.
Since cycle is set to true, the traversal will jump over the table borders, e.g. on moving over the last column/last row cell in a table, the selection will jump to the first column/first row cell in the table.
To specify a custom
ITraversalStrategy
a customMoveSelectionCommandHandler
needs to be registered. By default aMoveSelectionCommandHandler
withITraversalStrategy#AXIS_TRAVERSAL_STRATEGY
is registered for theSelectionLayer
. To override that behavior you can either exchange theDefaultSelectionLayerConfiguration
or register the command handler on a layer above theSelectionLayer
, e.g. theViewportLayer
. This will exchange the traversal settings globally.// register a MoveCellSelectionCommandHandler with // TABLE_CYCLE_TRAVERSAL_STRATEGY viewportLayer.registerCommandHandler( new MoveCellSelectionCommandHandler( selectionLayer, ITraversalStrategy.TABLE_CYCLE_TRAVERSAL_STRATEGY));
Note that it is even possible to register different
ITraversalStrategy
for horizontal and vertical movements.The global
ITraversalStrategy
settings in theMoveSelectionCommandHandler
can be overridden for a single command by executing aMoveSelectionCommand
with customized settings.Via
EditTraversalStrategy
you are able to wrap aITraversalStrategy
and add the ability to check for editable state. Using theEditTraversalStrategy
in conjunction with edit configurations for opening the adjacent editor on commit or editing traversal, the focus will jump to the next editable cell. -
SelectionModel updates
As already stated in the API changes section,ISelectionModel
now extendsILayerEventHandler<IStructuralChangeEvent>
. Doing this we introduced a tight connection between theISelectionModel
and the handling of structural changes to update the selection. These two were loosely coupled before, which lead to several issues when exchanging theISelectionModel
.Note:
If you used thePreserveSelectionStructuralChangeEventHandler
workaround in previous versions for not clearing the selection on structural changes, you will notice that this workaround will not work anymore. If you still need that behavior, you are now able to achieve the same by configuring and setting aSelectionModel
instance like this:SelectionModel model = new SelectionModel(selectionLayer); // configure to not clear the selection on structural changes model.setClearSelectionOnChange(false); selectionLayer.setSelectionModel(model);
If you expect that the selection should update and move with structural changes (e.g. sorting), try to use the
PreserveSelectionModel
. -
PopupMenuBuilder - DisposeListener
ThePopupMenuBuilder
now adds the necessaryDisposeListener
to the NatTable on callingPopupMenuBuilder#build()
. It is not necessary anymore to register such a listener in a configuration yourself. -
PopupMenuBuilder - MenuManager
ThePopupMenuBuilder
now internally uses aMenuManager
for creating and handling MenuItems. This way it is possible to combine Eclipse popup menus with NatTable configurations in conjunction with using core expressions for visibility. Additionally thePopupMenuAction
now sets theNatEventData
to theMenu
data map for the keyMenuItemProviders#NAT_EVENT_DATA_KEY
. This change was necessary because the Eclipse 4MenuManagerRenderer
is setting theMenuManager
reference to theMenu
data map without a key. -
PopupMenuBuilder - IMenuItemState
With the internal usage ofMenuManager
it is now also possible to configure visibility and enablement of NatTable menu items. To configure these states theIMenuItemState
was introduced. It can be registered for an ID that points to a menu item. All default NatTable menu items are now identifiable via ID, which are available as constants inPopupMenuBuilder
. The following code creates a menu containing the debug menu item that is only visible for columns at column position > 1.Menu debugMenu = new PopupMenuBuilder(natTable) .withInspectLabelsMenuItem() .withEnabledState( PopupMenuBuilder.INSPECT_LABEL_MENU_ITEM_ID, new IMenuItemState() { @Override public boolean isActive(NatEventData natEventData) { return natEventData.getColumnPosition() > 1; } }) .build();
-
DisplayModeMouseEventMatcher
Introduced theDisplayModeMouseEventMatcher
that allows to register a UI binding for aDisplayMode
. Using theDisplayModeMouseEventMatcher
for example allows to register different actions on performing a right click on a selected cell or a non selected cell. -
Tree - identification of tree column (index/position)
TheTreeLayer
always specifies the first column to be the tree column. It is now possible to specify whether the column index or the column position should be used to determine the tree column. The default is to use the column position, which means that reordering another column to become the first column in a grid, will make that column the tree column. Changing the behavior to use the column index for tree column determination, column reordering will lead to also reordering the tree column.
The following code will change the identification of the tree column from column position to column index:treeLayer.setUseTreeColumnIndex(true);
-
Tree - expand to level
It is now possible to expand nodes in a tree to a certain level. For this theITreeRowModel
.// expand all nodes to level 2 natTable.doCommand(new TreeExpandToLevelCommand(2)); // expand node at index 5 to level 2 natTable.doCommand(new TreeExpandToLevelCommand(5, 2));
-
Tree - expand/collapse by key
It is now possible to expand and collapse tree nodes via key interaction. For this theTreeExpandCollapseKeyAction
was introduced. On registering the defaultTreeLayerExpandCollapseKeyBindings
this action will be bound to the space key. Note that the binding requires theSelectionLayer
and theTreeLayer
to work correctly.// adds the key bindings that allows pressing space bar to // expand/collapse tree nodes natTable.addConfiguration( new TreeLayerExpandCollapseKeyBindings( bodyLayerStack.getTreeLayer(), bodyLayerStack.getSelectionLayer()));
-
GroupBy feature enhancements/corrections
There were numerous bug reports regarding the groupBy feature. They were mostly related to the usage of the wrongIDisplayConverter
or the wrongComparator
. To solve the issues regarding theIDisplayConverter
issues, theGroupByDisplayConverter
was introduced. It is registered by default viaGroupByDataLayerConfiguration
. If you are not using the default configuration, you need to ensure to register it in your custom configuration in order to solve the converter issues.To solve the issues related to sorting, it is necessary to enrich the
GroupByDataLayer
with additional information. TheISortModel
is needed to retrieve the correctComparator
, theTreeLayer
is needed to be able to determine the tree column in order to be able to sort the tree nodes, and theGroupByDataLayer
reference is needed to be able to sort by summary values. The necessary references can be set using the newly introduced initialize method.// connect ISortModel, TreeLayer and GroupByDataLayer to the // Comparator to support sorting by groupBy summary values bodyLayerStack.getBodyDataLayer().initializeTreeComparator( sortHeaderLayer.getSortModel(), bodyLayerStack.getTreeLayer(), true);
This is also the reason for the API change in
ISortModel
. -
Excel Export - Exporter configuration
It is now possible to configure the charset and the sheet name to use when exporting a NatTable using the coreExcelExporter
. The following code will for example register anExcelExporter
that exports a NatTable in UTF-8, setting the sheet name of the Excel sheet to My data.ExcelExporter exporter = new ExcelExporter(); exporter.setCharset("UTF-8"); exporter.setSheetname("My data"); configRegistry.registerConfigAttribute( ExportConfigAttributes.EXPORTER, exporter);
The
PoiExcelExporter
in the Apache POI extension now also allows to set the sheet name. -
The
CalculatedValueCache
, that is internally used for the calculation and caching of summary values, is now using theConcurrentHashMap
to avoid concurrency issues related to the usage ofHashMap
. See here for a more detailed description on that issue.