eclipse:nattable
Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| eclipse:nattable [2015/11/21 15:16] – [Styling] skip | eclipse:nattable [2023/11/01 07:15] (current) – ↷ Page moved from camunda:eclipse:nattable to eclipse:nattable skipidar | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| + | ====== NatTable ====== | ||
| + | A table is created as following: | ||
| + | - create a BodyLayerStack. Each layer is based on previous. | ||
| + | - create a RowHeaderLayer - it will provide rownames | ||
| + | - create a ColumnNameLayer - it will provide columnnames | ||
| + | - create a CornerLayer - to add some functionality to the left, upper table corner | ||
| + | - assemble all layers in one GridLayer | ||
| + | |||
| + | The functionality is contributed in BodyLayerStack. | ||
| + | |||
| + | {{http:// | ||
| + | |||
| + | {{http:// | ||
| + | |||
| + | {{http:// | ||
| + | |||
| + | |||
| + | =====FallPits===== | ||
| + | * Layers are created like SWT Widgets - one is part of another. When creating the Gridlayer - use the bottom layer. | ||
| + | * When creating the Column / row layer - use the bottom Layer either asf | ||
| + | <sxh java> | ||
| + | // 1. Use the dataProvider, | ||
| + | dataLayer = new DataLayer(bodyDataProvider); | ||
| + | // 2. | ||
| + | ColumnReorderLayer columnReorderLayer = new ColumnReorderLayer(dataLayer); | ||
| + | // 3. disables some columns | ||
| + | ColumnHideShowLayer columnHideShowLayer = new ColumnHideShowLayer(dataLayer); | ||
| + | // 4. can handle selection | ||
| + | SelectionLayer selectionLayer = new SelectionLayer(dataLayer); | ||
| + | // 5. draw Scrollbars | ||
| + | ViewportLayer viewportLayer = new ViewportLayer(selectionLayer); | ||
| + | |||
| + | viewportLayer.setRegionName(GridRegion.BODY); | ||
| + | |||
| + | // column names | ||
| + | // DataLayer dataLayerColHeader = new DataLayer(this.columnDataProvider); | ||
| + | // REFLECTIVE column provider | ||
| + | DataLayer dataLayerColHeader = new DataLayer(columnDataProviderExt); | ||
| + | ColumnHeaderLayer columnHeaderLayer = new ColumnHeaderLayer(dataLayerColHeader, | ||
| + | |||
| + | // row names. | ||
| + | // DEMO of encapsulating layers and configs in an " | ||
| + | RowHeaderLayerStack rowHeaderLayer = new RowHeaderLayerStack(this.rowDatadataProvider, | ||
| + | </ | ||
| + | * a table can oly have ONE ColumnOverrideLabelAccumulator - setConfigLabelAccumulator() allways overrides the old one! | ||
| + | * Columns and rows in a layer are referenced either by position or index:< | ||
| + | |||
| + | The **position** of a column/row in a layer corresponds to the **physical location of the column/ | ||
| + | **Positions reorder Indexes.** \\ | ||
| + | The **index of a column/ | ||
| + | They correspond to the numbers within data providers. | ||
| + | |||
| + | |||
| + | These concepts are illustrated by the following example: | ||
| + | |||
| + | < | ||
| + | ColumnHideShowLayer C | ||
| + | 0 1 2 3 4 <- column positions | ||
| + | 1 0 3 4 5 <- column indexes | ||
| + | |||
| + | |||
| + | ColumnReorderLayer B | ||
| + | 0 1 2 3 4 5 <- column positions | ||
| + | 2 1 0 3 4 5 <- column indexes | ||
| + | |||
| + | |||
| + | DataLayer A | ||
| + | 0 1 2 3 4 5 <- column positions | ||
| + | 0 1 2 3 4 5 <- column indexes | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | Switching between Position and INdex is possible via the nattable | ||
| + | |||
| + | < | ||
| + | // position -> index | ||
| + | natTable.getColumnIndexByPosition(columnPosition) | ||
| + | natTable.getRowIndexByPosition(rowPosition) | ||
| + | </ | ||
| + | ===== Inserting own Data ===== | ||
| + | To insert own data into the table - implement IDataProvider. | ||
| + | IDataProvider asks for Data by providing the | ||
| + | - **row** | ||
| + | - **column** | ||
| + | |||
| + | Usualy own Data are stored in a Collection of own Objects. | ||
| + | So the **row**s are mapped to the collection cells, and the right object is retrieved, which should be mapped to cells. | ||
| + | |||
| + | Now somehow the own-object' | ||
| + | |||
| + | ==Access own DataObject by the Reflection.== | ||
| + | There is a reflective way of accessing the objects, by using ReflectiveColumnPropertyAccessor. | ||
| + | It finds the getter methods on its own, if it gets a list of attributes, to display. | ||
| + | |||
| + | <sxh java> | ||
| + | //map the labels to attributes names | ||
| + | IDataProvider dataProvider = new ListDataProvider< | ||
| + | </ | ||
| + | |||
| + | ==== Concepts ==== | ||
| + | |||
| + | ^Subject ^ Describtion^ | ||
| + | |IConfigRegistry configRegistry| An object, that is registered to **NatTables**. | ||
| + | configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_PAINTER, | ||
| + | configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITOR, | ||
| + | </ | ||
| + | |IConfiguration | An object, that is registered to **ILayers**. is is asked to add Configurations like Events, Style etc. to the IConfigRegistry | ||
| + | |AggregateConfiguration | An IConfiguration that stores many IConfiguration objects. Used to put many Configurations E.g. for Edit, head, print, styling etc. in 1 object. Use addConfiguration() in constructor, | ||
| + | |Labels| Labels are registered to **Cells, Columns, Rows**. They allow to map the Configurations from ConfigRegistry to cells, columns, rows. < | ||
| + | //associate cellStyle with Label GridRegion.ROW_HEADER | ||
| + | |||
| + | |||
| + | Style cellStyle = new Style(); | ||
| + | cellStyle.setAttributeValue(CellStyleAttributes.BACKGROUND_COLOR, | ||
| + | cellStyle.setAttributeValue(CellStyleAttributes.FOREGROUND_COLOR, | ||
| + | cellStyle.setAttributeValue(CellStyleAttributes.GRADIENT_BACKGROUND_COLOR, | ||
| + | cellStyle.setAttributeValue(CellStyleAttributes.GRADIENT_FOREGROUND_COLOR, | ||
| + | cellStyle.setAttributeValue(CellStyleAttributes.HORIZONTAL_ALIGNMENT, | ||
| + | cellStyle.setAttributeValue(CellStyleAttributes.VERTICAL_ALIGNMENT, | ||
| + | cellStyle.setAttributeValue(CellStyleAttributes.BORDER_STYLE, | ||
| + | cellStyle.setAttributeValue(CellStyleAttributes.FONT, | ||
| + | |||
| + | configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_STYLE, | ||
| + | </ | ||
| + | | UiBindingRegistry | An object, that is registered to **NatTables**. | ||
| + | uiBindingRegistry.registerFirstSingleClickBinding( | ||
| + | new CellPainterMouseEventMatcher(GridRegion.COLUMN_HEADER, | ||
| + | new ToggleCheckBoxColumnAction(columnHeaderCheckBoxPainter, | ||
| + | ); | ||
| + | |||
| + | |||
| + | //enables row resizing | ||
| + | public void configureUiBindings(UiBindingRegistry uiBindingRegistry) { | ||
| + | // Mouse move - Show resize cursor | ||
| + | uiBindingRegistry.registerFirstMouseMoveBinding(new RowResizeEventMatcher(SWT.NONE, | ||
| + | uiBindingRegistry.registerMouseMoveBinding(new MouseEventMatcher(), | ||
| + | |||
| + | // Row resize | ||
| + | uiBindingRegistry.registerFirstMouseDragMode(new RowResizeEventMatcher(SWT.NONE, | ||
| + | |||
| + | uiBindingRegistry.registerDoubleClickBinding(new RowResizeEventMatcher(SWT.NONE, | ||
| + | uiBindingRegistry.registerSingleClickBinding(new RowResizeEventMatcher(SWT.NONE, | ||
| + | } | ||
| + | |||
| + | </ | ||
| + | |||
| + | |||
| + | |||
| + | CellStyleAttributes - Objects encapsulate the Labels, which are used to register functionality via labels, or otherwise. That objects have a generic parameter and contribute a generic type, so that only objects of the right type may be regitered with the given CellStyleAttribute. | ||
| + | |||
| + | ^ CellAttributes collection^ What maay be registered? | ||
| + | |CellConfigAttributes |< | ||
| + | * CELL_PAINTER | ||
| + | * CELL_STYLE | ||
| + | * DISPLAY_CONVERTER | ||
| + | * EXPORT_FORMATTER | ||
| + | <sxh java> | ||
| + | configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_PAINTER, | ||
| + | </ | ||
| + | </ | ||
| + | |EditConfigAttributes|< | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | <sxh java> | ||
| + | configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITOR, | ||
| + | </ | ||
| + | |CellConfigAttributes|< | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | <sxh java> | ||
| + | IStyle cellStyle = new Style(); | ||
| + | cellStyle.setAttributeValue(CellStyleAttributes.BACKGROUND_COLOR, | ||
| + | configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_STYLE, | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | |||
| + | ===== useful API ===== | ||
| + | ^API ^ Describtion^ | ||
| + | |ColumnResizeCommand | Can resize a column. < | ||
| + | ColumnResizeCommand columnCommand = new ColumnResizeCommand(gridLayer, | ||
| + | natTable.doCommand(columnCommand); | ||
| + | </ | ||
| + | |[[http:// | ||
| + | | ||
| + | configRegistry.registerConfigAttribute(CellConfigAttributes.DISPLAY_CONVERTER, | ||
| + | new DefaultDateDisplayConverter(), | ||
| + | </ | ||
| + | |[[http:// | ||
| + | |||
| + | | ||
| + | IUniqueIndexLayer baseLayer = getDependedLayer(); | ||
| + | |||
| + | CompositeLayer compositeLayer = new CompositeLayer(2, | ||
| + | |||
| + | DimensionallyDependentLayer testLayer = new DimensionallyDependentLayer(baseLayer, | ||
| + | |||
| + | compositeLayer.setChildLayer(" | ||
| + | compositeLayer.setChildLayer(" | ||
| + | |||
| + | return compositeLayer; | ||
| + | } | ||
| + | | ||
| + | IUniqueIndexLayer getDependedLayer(){ | ||
| + | return new DataLayer(new IDataProvider() { | ||
| + | @Override | ||
| + | public void setDataValue(int columnIndex, | ||
| + | } | ||
| + | @Override | ||
| + | public int getRowCount() { | ||
| + | // Is never used, because this layer is used to create a vertically depending layer. | ||
| + | // The rowCount is set by the layer, from which the current layer vertically depends. | ||
| + | return 1000000; | ||
| + | } | ||
| + | @Override | ||
| + | public Object getDataValue(int columnIndex, | ||
| + | return " | ||
| + | } | ||
| + | @Override | ||
| + | public int getColumnCount() { | ||
| + | return 1; | ||
| + | } | ||
| + | }); | ||
| + | } | ||
| + | |||
| + | </ | ||
| + | |[[http:// | ||
| + | IUniqueIndexLayer baseLayer = getDependedLayer(); | ||
| + | DimensionallyDependentLayer testLayer = new DimensionallyDependentLayer(baseLayer, | ||
| + | |||
| + | IUniqueIndexLayer getDependedLayer(){ | ||
| + | return new DataLayer(new IDataProvider() { | ||
| + | @Override | ||
| + | public void setDataValue(int columnIndex, | ||
| + | } | ||
| + | @Override | ||
| + | public int getRowCount() { | ||
| + | // Is never used, because this layer is used to create a vertically depending layer. | ||
| + | // The rowCount is set by the layer, from which the current layer vertically depends. | ||
| + | return 1000000; | ||
| + | } | ||
| + | @Override | ||
| + | public Object getDataValue(int columnIndex, | ||
| + | return " | ||
| + | } | ||
| + | @Override | ||
| + | public int getColumnCount() { | ||
| + | return 1; | ||
| + | } | ||
| + | }); | ||
| + | } | ||
| + | </ | ||
| + | |[[http:// | ||
| + | |||
| + | private class RowHeaderLayerStack extends AbstractLayerTransform { | ||
| + | |||
| + | public RowHeaderLayerStack(IDataProvider dataProvider, | ||
| + | DataLayer dataLayer = new DataLayer(dataProvider, | ||
| + | RowHeaderLayer rowHeaderLayer = new RowHeaderLayer(dataLayer, | ||
| + | setUnderlyingLayer(rowHeaderLayer); | ||
| + | } | ||
| + | } | ||
| + | |||
| + | </ | ||
| + | |[[http:// | ||
| + | |||
| + | Commands are passed through all layers top down. | ||
| + | Default commands are handled by handlers, many of them are owned by the GridLayer. | ||
| + | <sxh java> | ||
| + | //Layers can execute commands and pass them to sublayers | ||
| + | Layer.doCommand(ILayerCommand command) | ||
| + | |||
| + | /* | ||
| + | / EDITING HAPPENS AS FOLLOWING: | ||
| + | */ | ||
| + | |||
| + | //register an Editor to the cell's label | ||
| + | configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITOR, | ||
| + | DisplayMode.EDIT, | ||
| + | |||
| + | // make the cell, which we registered an editor for - editable | ||
| + | configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITABLE_RULE, | ||
| + | |||
| + | //register a mouse event, which will trigger editing (here it is a the first doubleclick) | ||
| + | uiBindingRegistry.registerFirstDoubleClickBinding(new BodyCellEditorMouseEventMatcher(TextCellEditor.class), | ||
| + | |||
| + | //FIRST DOUBLE CLICK occurs | ||
| + | |||
| + | //ask, whether the event with found editor matches requirements for editing: BodyCellEditorMouseEventMatcher.matches(...) | ||
| + | //if yes - trigger an EditCellCommand | ||
| + | |||
| + | //ask, EditCellCommandHandler in GridLayer to Handle the EditCellCommand | ||
| + | |||
| + | //pass editing to the registered editor in cell | ||
| + | |||
| + | </ | ||
| + | |< | ||
| + | natTable.getConfigLabelsByPosition(columnPosition, | ||
| + | natTable.getDataValueByPosition(columnPosition, | ||
| + | natTable.getColumnIndexByPosition(columnPosition) | ||
| + | natTable.getRowIndexByPosition(rowPosition) </ | ||
| + | |[[http:// | ||
| + | |||
| + | |||
| + | </ | ||
| + | ===== Header Size ===== | ||
| + | |||
| + | Header-Height depends on the TextPainter height. \\ | ||
| + | Header-Width depends on body width. \\ | ||
| + | See the diagram below. \\ | ||
| + | {{http:// | ||
| + | |||
| + | |||
| + | <sxh java> | ||
| + | |||
| + | /** | ||
| + | * The Column Resizing has to happen AFTER the Column was rendered. For that | ||
| + | * a Paintlistener is used, which will unregister itselfe. | ||
| + | * | ||
| + | * Details: http:// | ||
| + | */ | ||
| + | private void delayedColumnResize() { | ||
| + | natTable.addListener(SWT.Paint, | ||
| + | |||
| + | @Override | ||
| + | public void handleEvent(Event arg0) { | ||
| + | for (int i = 0; i < natTable.getPreferredColumnCount(); | ||
| + | InitializeAutoResizeColumnsCommand columnCommand = new InitializeAutoResizeColumnsCommand( | ||
| + | natTable, | ||
| + | new GCFactory(natTable)); | ||
| + | natTable.doCommand(columnCommand); | ||
| + | } | ||
| + | |||
| + | natTable.removeListener(SWT.Paint, | ||
| + | } | ||
| + | }); | ||
| + | } | ||
| + | |||
| + | private void addNormalModeStyling(IConfigRegistry configRegistry) { | ||
| + | Image bgImage = CommonResourceUtil.getImage(COLUMN_HEADER_BG_PATH); | ||
| + | |||
| + | // Painter of headers | ||
| + | // + Padding | ||
| + | // + make cell as high/wide as content | ||
| + | TextPainter txtPainter = new TextPainter(false, | ||
| + | Constants.TABLE_PADDING_COLUMNHEADER, | ||
| + | |||
| + | // Header BG | ||
| + | ICellPainter bgImagePainter = new BackgroundImagePainter(txtPainter, | ||
| + | bgImage, | ||
| + | |||
| + | SortableHeaderTextPainter sortableHeaderTextPainter = new SortableHeaderTextPainter( | ||
| + | bgImagePainter, | ||
| + | |||
| + | configRegistry.registerConfigAttribute( | ||
| + | CellConfigAttributes.CELL_PAINTER, | ||
| + | DisplayMode.NORMAL, | ||
| + | |||
| + | configRegistry.registerConfigAttribute( | ||
| + | CellConfigAttributes.CELL_PAINTER, | ||
| + | DisplayMode.NORMAL, | ||
| + | |||
| + | // the top filter row in the corner should have the same BG color | ||
| + | configRegistry.registerConfigAttribute( | ||
| + | CellConfigAttributes.CELL_PAINTER, | ||
| + | DisplayMode.NORMAL, | ||
| + | } | ||
| + | |||
| + | |||
| + | |||
| + | </ | ||
| + | |||
| + | |||
| + | |||
| + | |||
| + | ===== Styling ===== | ||
| + | The styling may happen via different mechanisms: | ||
| + | === Style === | ||
| + | The cellStyle may style cells or headers, since headers are cells too. | ||
| + | < | ||
| + | Style cellStyle = new Style(); | ||
| + | BACKGROUND_COLOR | ||
| + | FOREGROUND_COLOR | ||
| + | GRADIENT_BACKGROUND_COLOR | ||
| + | GRADIENT_FOREGROUND_COLOR | ||
| + | HORIZONTAL_ALIGNMENT | ||
| + | VERTICAL_ALIGNMENT | ||
| + | FONT | ||
| + | IMAGE | ||
| + | BORDER_STYLE | ||
| + | PASSWORD_ECHO_CHAR | ||
| + | TEXT_DECORATION | ||
| + | </ | ||
| + | |||
| + | The cellStyle is registered to different DisplayModes, | ||
| + | may become active when the cell is Selected, Edited or just viewed. | ||
| + | |||
| + | <sxh java> | ||
| + | configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_STYLE, | ||
| + | cellStyle, | ||
| + | configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_STYLE, | ||
| + | cellStyle, | ||
| + | configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_STYLE, | ||
| + | cellStyle, | ||
| + | |||
| + | </ | ||
| + | |||
| + | === Painter === | ||
| + | The Painter may draw BG or forbid to draw bg. \\ | ||
| + | When renderer forbids t odraw BG - no registered style is used: | ||
| + | |||
| + | <sxh java> | ||
| + | // no wrap | ||
| + | // + DONT draw BG | ||
| + | // + Padding | ||
| + | // + make cell as high/wide as content & spacing | ||
| + | ICellPainter txtPainter = new TextPainter(false, | ||
| + | TABLE_PADDING_COLUMNHEADER, | ||
| + | |||
| + | // no wrap | ||
| + | // + DRAW BG | ||
| + | // + Padding | ||
| + | // + make cell as high/wide as content & spacing | ||
| + | ICellPainter txtPainter = new TextPainter(false, | ||
| + | TABLE_PADDING_COLUMNHEADER, | ||
| + | </ | ||
| + | |||
| + | |||
| + | === Style resolution mechanism === | ||
| + | Which Style wins, if you register 2 different Styles to different Labels, which are assigned to the same cell? | ||
| + | |||
| + | == DisplayMode Priority == | ||
| + | The order (and priority in which Labels are evaluated). Definde in **DefaultDisplayModeOrdering**: | ||
| + | |SELECT_HOVER, | ||
| + | |||
| + | All styles from the current mode up to NORMAL-DisplayMode-Labels registered styles are considered. | ||
| + | |||
| + | E.g. | ||
| + | * If current mode is SELECT - then the NatTable will consider all Styles for modes: SELECT, HOVER, NORMAL. | ||
| + | * It will omit the Styles, registerd for the mode SELECT_HOVER | ||
| + | |||
| + | == Label Priority == | ||
| + | The mechanism is encapsulated inside: | ||
| + | |||
| + | <sxh java> | ||
| + | package org.eclipse.nebula.widgets.nattable.style; | ||
| + | |||
| + | class CellStyleUtil{ | ||
| + | ... | ||
| + | |||
| + | public static IStyle getCellStyle(ILayerCell cell, | ||
| + | IConfigRegistry configRegistry) { | ||
| + | return new CellStyleProxy(configRegistry, | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | The important part is the | ||
| + | < | ||
| + | cell.getConfigLabels().getLabels() | ||
| + | </ | ||
| + | which retrieves the labels in a special **order X**. | ||
| + | This order decides, which Style is chosen. | ||
| + | |||
| + | - The Class StyleProxy iterates all labels, in the given **order X**, when it is asked for a special **Style-Attribute A**. First label, for which there is an instance of **Style-Attribute A** in configRegistry - wins the race. | ||
| + | - if no **Style-Attribute A** for no cell labels, then there is a fallback to a none label request. | ||
| + | |||
| + | <sxh java> | ||
| + | package org.eclipse.nebula.widgets.nattable.style; | ||
| + | |||
| + | class StyleProxy{ | ||
| + | ... | ||
| + | public <T> T getAttributeValue(ConfigAttribute< | ||
| + | ... | ||
| + | for (String displayMode : displayModeOrdering | ||
| + | .getDisplayModeOrdering(this.targetDisplayMode)) { | ||
| + | for (String configLabel : this.configLabels) { | ||
| + | IStyle cellStyle = this.configRegistry.getSpecificConfigAttribute( | ||
| + | this.styleConfigAttribute, | ||
| + | if (cellStyle != null) { | ||
| + | styleAttributeValue = cellStyle | ||
| + | .getAttributeValue(styleAttribute); | ||
| + | if (styleAttributeValue != null) { | ||
| + | return styleAttributeValue; | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | |||
| + | // default | ||
| + | IStyle cellStyle = this.configRegistry.getSpecificConfigAttribute( | ||
| + | this.styleConfigAttribute, | ||
| + | if (cellStyle != null) { | ||
| + | styleAttributeValue = cellStyle | ||
| + | .getAttributeValue(styleAttribute); | ||
| + | if (styleAttributeValue != null) { | ||
| + | return styleAttributeValue; | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | |||
| + | return null; | ||
| + | } | ||
| + | </ | ||
| + | ===== Sorting ===== | ||
| + | Sorting is implemented via **ISortModel**. It is passed to the **SortHeaderLayer** | ||
| + | < | ||
| + | import org.eclipse.nebula.widgets.nattable.sort.ISortModel; | ||
| + | ISortModel tableSorter = new ISortModel{ | ||
| + | ... | ||
| + | } | ||
| + | |||
| + | public TableHeaderStack() { | ||
| + | |||
| + | // 0. datalayer. stores the headers of columns | ||
| + | dataLayerColHeader = new DataLayer(columnDataProvider); | ||
| + | |||
| + | // 1. column header layer. stores the headers | ||
| + | columnHeaderLayer = new ColumnHeaderLayer(dataLayerColHeader, | ||
| + | bodyStack.selectionLayer); | ||
| + | |||
| + | // 2. sorting layer handles SortCommands. it is placed below the column header layer. | ||
| + | // it handles the SortCommands by manipulating the ISortModel | ||
| + | // it handles the SortCommands by adding sort labels to sorted columns | ||
| + | sortHeaderLayer = new SortHeaderLayer(columnHeaderLayer, | ||
| + | |||
| + | // create filterRow composite - the widget where filter text is entered | ||
| + | filterRowHeaderComposite = createFilterRow(sortHeaderLayer, | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | |||
| + | Via Configurations you can add some contextmenus to add sort functionality | ||
| + | |||
| + | < | ||
| + | /** | ||
| + | * Creates the {@link PopupMenuBuilder} for the column header menu with the menu | ||
| + | * items that should be added to the menu. | ||
| + | * | ||
| + | * @param natTable | ||
| + | | ||
| + | * @return The {@link PopupMenuBuilder} that is used to build the column | ||
| + | | ||
| + | */ | ||
| + | protected PopupMenuBuilder createColumnHeaderMenu(NatTable natTable) { | ||
| + | return new PopupMenuBuilder(natTable).withHideColumnMenuItem(Messages.MenuItemProviders_hideColumn) | ||
| + | .withShowAllColumnsMenuItem(Messages.MenuItemProviders_showAllColumns) | ||
| + | .withAutoResizeSelectedColumnsMenuItem(Messages.MenuItemProviders_autoResizeAllSelectedColumns) | ||
| + | .withClearAllFilters(Messages.MenuItemProviders_clearAllFilters) | ||
| + | .withMenuItemProvider(getMenuItemSort()); | ||
| + | } | ||
| + | |||
| + | private IMenuItemProvider getMenuItemSort() { | ||
| + | return new IMenuItemProvider() { | ||
| + | |||
| + | @Override | ||
| + | public void addMenuItem(final NatTable natTable, Menu popupMenu) { | ||
| + | |||
| + | /* | ||
| + | * at this place we do not receive the concrete column - so we only can introduce a | ||
| + | * general menu for all columns. | ||
| + | * | ||
| + | * That means we can not display sorting state of the current column, | ||
| + | * we can not disable the menu, if sorting is not allowed on this column. | ||
| + | * | ||
| + | * Introduces a mechanism which would allow to react on the state current sorting | ||
| + | * state. | ||
| + | */ | ||
| + | * | ||
| + | Image iconUp = GUIHelper.getImage(" | ||
| + | Image iconDown = GUIHelper.getImage(" | ||
| + | |||
| + | // group | ||
| + | MenuItem sortMenuGroupItem = new MenuItem(popupMenu, | ||
| + | /** the hotkey is defined in ConfigurationHeaderActions */ | ||
| + | sortMenuGroupItem.setText(Messages.ConfigurationHeaderContextMenu_sorting + MENU_TAB_CHAR + HOTKEY_CTRL | ||
| + | + PLUS_CHAR + HOTKEY_SEARCH); | ||
| + | sortMenuGroupItem.setImage(iconDown); | ||
| + | |||
| + | sortMenuGroupItem.setEnabled(true); | ||
| + | Menu sortMenuGroup = new Menu(sortMenuGroupItem); | ||
| + | sortMenuGroupItem.setMenu(sortMenuGroup); | ||
| + | |||
| + | | ||
| + | // Item sort none | ||
| + | MenuItem sortMenuItemNo = new MenuItem(sortMenuGroup, | ||
| + | sortMenuItemNo.setText(Messages.ConfigurationHeaderContextMenu_noSort); | ||
| + | sortMenuItemNo | ||
| + | .addSelectionListener(getSelectionAdapterSorting(SortDirectionEnum.NONE, | ||
| + | |||
| + | // item sort asc | ||
| + | MenuItem sortMenuItemAsc = new MenuItem(sortMenuGroup, | ||
| + | sortMenuItemAsc.setText(Messages.ConfigurationHeaderContextMenu_ascending); | ||
| + | sortMenuItemAsc.setImage(iconUp); | ||
| + | sortMenuItemAsc | ||
| + | .addSelectionListener(getSelectionAdapterSorting(SortDirectionEnum.ASC, | ||
| + | |||
| + | // Item sort desc | ||
| + | MenuItem sortMenuItemDesc = new MenuItem(sortMenuGroup, | ||
| + | sortMenuItemDesc.setText(Messages.ConfigurationHeaderContextMenu_descending); | ||
| + | sortMenuItemDesc.setImage(iconDown); | ||
| + | sortMenuItemDesc | ||
| + | .addSelectionListener(getSelectionAdapterSorting(SortDirectionEnum.DESC, | ||
| + | |||
| + | // HELPER | ||
| + | |||
| + | private SelectionAdapter getSelectionAdapterSorting(final SortDirectionEnum sortDirectionEnum, | ||
| + | final ISortModel sortModel, final NatTable natTable) { | ||
| + | return new SelectionAdapter() { | ||
| + | |||
| + | @SuppressWarnings(" | ||
| + | @Override | ||
| + | public void widgetSelected(SelectionEvent e) { | ||
| + | NatEventData natEventData = MenuItemProviders.getNatEventData(e); | ||
| + | int columnPosition = natEventData.getColumnPosition(); | ||
| + | int columnIndex = natTable.getColumnIndexByPosition(columnPosition); | ||
| + | |||
| + | // switch model into the predescessor state. SortColumnCommand will siwtch it into | ||
| + | // the next state | ||
| + | sortModel.sort(columnIndex, | ||
| + | |||
| + | // send a command | ||
| + | // the command will switch the model into the next sorting-state | ||
| + | natTable.doCommand(new SortColumnCommand(natTable, | ||
| + | } | ||
| + | |||
| + | }; | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | |||
| + | =====Example Snippets Repository ===== | ||
| + | |||
| + | Are checked in on GitHub: https:// | ||
| + | |||
| + | |||
| + | |||
| + | |||
| + | ===== Labels ===== | ||
| + | |||
| + | Jede Konfiguration, | ||
| + | |||
| + | == Where is the place, where Labels are ? == | ||
| + | |||
| + | In **ILayer.class** | ||
| + | <sxh java> | ||
| + | @Override | ||
| + | public LabelStack getConfigLabelsByPosition(int columnPosition, | ||
| + | LabelStack configLabels = new LabelStack(); | ||
| + | if (this.configLabelAccumulator != null) { | ||
| + | this.configLabelAccumulator.accumulateConfigLabels(configLabels, | ||
| + | } | ||
| + | if (this.regionName != null) { | ||
| + | configLabels.addLabel(this.regionName); | ||
| + | } | ||
| + | return configLabels; | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | |||
| + | == How to contribute a label by a custom layer == | ||
| + | |||
| + | |||
| + | |||
| + | Use method getConfigLabelsByPosition in **ILayer.class** | ||
| + | |||
| + | <sxh java> | ||
| + | // need to contribute a label for the whole layer | ||
| + | | ||
| + | // WRONG - layers have no chance to contribute Labels, but only via Regions. Noone asks for registered IConfigLabelAccumulator | ||
| + | // @Override | ||
| + | // public LabelStack getRegionLabelsByXY(int x, int y) { | ||
| + | // LabelStack stack = super.getRegionLabelsByXY(x, | ||
| + | // stack.addLabel(GridRegion.ROW_HEADER); | ||
| + | // return stack; | ||
| + | // } | ||
| + | |||
| + | // RIGHT | ||
| + | @Override | ||
| + | public LabelStack getConfigLabelsByPosition(int columnPosition, | ||
| + | LabelStack stack = super.getConfigLabelsByPosition(columnPosition, | ||
| + | stack.addLabel(GridRegion.ROW_HEADER); | ||
| + | return stack; | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Attach Labels via Regions. Each region becomes a label too. | ||
| + | <sxh java> | ||
| + | combinedCheckboxRowheaderLayer = new CompositeLayer(2, | ||
| + | combinedCheckboxRowheaderLayer.setChildLayer(REGIONS.CHECKBOX_LAYER, | ||
| + | combinedCheckboxRowheaderLayer.setChildLayer(REGIONS.ROW_HEADER_LAYER, | ||
| + | </ | ||
| + | |||
| + | =====Example Code ===== | ||
| + | |||
| + | Example of using [[http:// | ||
| + | |||
| + | <sxh java> | ||
| + | |||
| + | import org.eclipse.nebula.widgets.nattable.NatTable; | ||
| + | import org.eclipse.nebula.widgets.nattable.config.AbstractUiBindingConfiguration; | ||
| + | import org.eclipse.nebula.widgets.nattable.config.CellConfigAttributes; | ||
| + | import org.eclipse.nebula.widgets.nattable.config.ConfigRegistry; | ||
| + | import org.eclipse.nebula.widgets.nattable.config.DefaultNatTableStyleConfiguration; | ||
| + | import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry; | ||
| + | import org.eclipse.nebula.widgets.nattable.data.IDataProvider; | ||
| + | import org.eclipse.nebula.widgets.nattable.grid.GridRegion; | ||
| + | import org.eclipse.nebula.widgets.nattable.grid.data.DefaultCornerDataProvider; | ||
| + | import org.eclipse.nebula.widgets.nattable.grid.data.DummyBodyDataProvider; | ||
| + | import org.eclipse.nebula.widgets.nattable.grid.layer.ColumnHeaderLayer; | ||
| + | import org.eclipse.nebula.widgets.nattable.grid.layer.CornerLayer; | ||
| + | import org.eclipse.nebula.widgets.nattable.grid.layer.GridLayer; | ||
| + | import org.eclipse.nebula.widgets.nattable.grid.layer.RowHeaderLayer; | ||
| + | import org.eclipse.nebula.widgets.nattable.hideshow.ColumnHideShowLayer; | ||
| + | import org.eclipse.nebula.widgets.nattable.layer.AbstractLayerTransform; | ||
| + | import org.eclipse.nebula.widgets.nattable.layer.DataLayer; | ||
| + | import org.eclipse.nebula.widgets.nattable.layer.ILayer; | ||
| + | import org.eclipse.nebula.widgets.nattable.layer.cell.ColumnOverrideLabelAccumulator; | ||
| + | import org.eclipse.nebula.widgets.nattable.painter.cell.ButtonCellPainter; | ||
| + | import org.eclipse.nebula.widgets.nattable.painter.cell.ImagePainter; | ||
| + | import org.eclipse.nebula.widgets.nattable.painter.cell.TextPainter; | ||
| + | import org.eclipse.nebula.widgets.nattable.painter.cell.decorator.CellPainterDecorator; | ||
| + | import org.eclipse.nebula.widgets.nattable.reorder.ColumnReorderLayer; | ||
| + | import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer; | ||
| + | import org.eclipse.nebula.widgets.nattable.style.CellStyleAttributes; | ||
| + | import org.eclipse.nebula.widgets.nattable.style.DisplayMode; | ||
| + | import org.eclipse.nebula.widgets.nattable.style.Style; | ||
| + | import org.eclipse.nebula.widgets.nattable.ui.action.IMouseAction; | ||
| + | import org.eclipse.nebula.widgets.nattable.ui.binding.UiBindingRegistry; | ||
| + | import org.eclipse.nebula.widgets.nattable.ui.matcher.CellLabelMouseEventMatcher; | ||
| + | import org.eclipse.nebula.widgets.nattable.ui.matcher.MouseEventMatcher; | ||
| + | import org.eclipse.nebula.widgets.nattable.ui.util.CellEdgeEnum; | ||
| + | import org.eclipse.nebula.widgets.nattable.util.GUIHelper; | ||
| + | import org.eclipse.nebula.widgets.nattable.viewport.ViewportLayer; | ||
| + | import org.eclipse.swt.events.MouseEvent; | ||
| + | import org.eclipse.swt.layout.FillLayout; | ||
| + | import org.eclipse.swt.layout.GridLayout; | ||
| + | import org.eclipse.swt.widgets.Display; | ||
| + | import org.eclipse.swt.widgets.Shell; | ||
| + | |||
| + | public class Table { | ||
| + | public static final String CUSTOM_CELL_LABEL = " | ||
| + | |||
| + | public static void main(String[] args) { | ||
| + | Display display = new Display (); | ||
| + | Shell shell = new Shell (display); | ||
| + | shell.setLayout(new GridLayout()); | ||
| + | |||
| + | shell.setSize(1000, | ||
| + | |||
| + | new Table(shell); | ||
| + | |||
| + | shell.open (); | ||
| + | while (!shell.isDisposed ()) { | ||
| + | if (!display.readAndDispatch ()) display.sleep (); | ||
| + | } | ||
| + | display.dispose (); | ||
| + | |||
| + | } | ||
| + | |||
| + | public Table(Shell parent) { | ||
| + | |||
| + | parent.setLayout(new FillLayout()); | ||
| + | |||
| + | IDataProvider bodyDataProvider = setupBigDataProvider(); | ||
| + | |||
| + | //All of the layers, needed for the table | ||
| + | BodyStack bodyStack = new BodyStack(bodyDataProvider); | ||
| + | |||
| + | //column names | ||
| + | IDataProvider dataProviderColHeader = getColumnLabelProvider(bodyDataProvider); | ||
| + | DataLayer dataLayerColHeader = new DataLayer(dataProviderColHeader); | ||
| + | ColumnHeaderLayer columnHeaderLayer = new ColumnHeaderLayer(dataLayerColHeader, | ||
| + | |||
| + | |||
| + | //row names. | ||
| + | //DEMO of encapsulating layers and configs in an " | ||
| + | IDataProvider dataProviderRowHeader = getRowLabelProvider(bodyDataProvider); | ||
| + | RowHeaderLayerStack rowHeaderLayer = new RowHeaderLayerStack(dataProviderRowHeader, | ||
| + | // | ||
| + | //CUSTOM AbstractLayerTransform, | ||
| + | // | ||
| + | //Can change provide changes to layers beneath. | ||
| + | |||
| + | //(left upper)corner | ||
| + | DefaultCornerDataProvider cornerDataProvider = new DefaultCornerDataProvider(dataProviderColHeader, | ||
| + | CornerLayer cornerLayer = new CornerLayer(new DataLayer(cornerDataProvider), | ||
| + | |||
| + | //top layer | ||
| + | GridLayer gridLayer = new GridLayer(bodyStack, | ||
| + | NatTable natTable = new NatTable(parent, | ||
| + | |||
| + | |||
| + | |||
| + | IConfigRegistry configRegistry = new ConfigRegistry(); | ||
| + | |||
| + | |||
| + | // Step 1: Create a label accumulator - adds custom labels to all cells which we | ||
| + | // wish to render differently. In this case render as a button. | ||
| + | ColumnOverrideLabelAccumulator cellLabelAccumulator = new ColumnOverrideLabelAccumulator(bodyStack.bodyDataLayer); | ||
| + | // | ||
| + | cellLabelAccumulator.registerColumnOverrides(2, | ||
| + | |||
| + | // Step 2: Apply the Accumulator to the column | ||
| + | bodyStack.bodyDataLayer.setConfigLabelAccumulator(cellLabelAccumulator); | ||
| + | |||
| + | |||
| + | //Step 3: Apply the Custom style Painter to the cells, annotated by the Label " | ||
| + | |||
| + | //3.1 new painter | ||
| + | final ButtonCellPainter buttonPainter = new ButtonCellPainter( | ||
| + | new CellPainterDecorator( | ||
| + | new TextPainter(), | ||
| + | ) | ||
| + | ); | ||
| + | |||
| + | //3.2 make painter responsible for drawing CUSTOM_CELL_LABEL annotated cells | ||
| + | configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_PAINTER, | ||
| + | buttonPainter, | ||
| + | DisplayMode.NORMAL, | ||
| + | CUSTOM_CELL_LABEL); | ||
| + | |||
| + | |||
| + | //3.3 Add the listener to the button | ||
| + | buttonPainter.addClickListener(new IMouseAction() { | ||
| + | |||
| + | @Override | ||
| + | public void run(NatTable natTable, MouseEvent event) { | ||
| + | System.out.println(" | ||
| + | } | ||
| + | }); | ||
| + | |||
| + | |||
| + | //3.4 set the style for the CUSTOM_CELL_LABEL annotated cells | ||
| + | Style style = new Style(); | ||
| + | style.setAttributeValue(CellStyleAttributes.BACKGROUND_COLOR, | ||
| + | |||
| + | configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_STYLE, | ||
| + | configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_STYLE, | ||
| + | |||
| + | |||
| + | //3.5 add Mouse listener to use the Painter on Mouse event | ||
| + | natTable.addConfiguration(new AbstractUiBindingConfiguration() { | ||
| + | |||
| + | @Override | ||
| + | public void configureUiBindings(UiBindingRegistry uiBindingRegistry) { | ||
| + | // Match a mouse event on the body, when the left button is clicked | ||
| + | // and the custom cell label is present | ||
| + | CellLabelMouseEventMatcher mouseEventMatcher = new CellLabelMouseEventMatcher( | ||
| + | GridRegion.BODY, | ||
| + | MouseEventMatcher.LEFT_BUTTON, | ||
| + | CUSTOM_CELL_LABEL); | ||
| + | // Inform the button painter of the click. | ||
| + | uiBindingRegistry.registerMouseDownBinding(mouseEventMatcher, | ||
| + | } | ||
| + | }); | ||
| + | |||
| + | //Step 4 now apply the configuration to the NatTable | ||
| + | //apply the style, which will draw the data into the Table | ||
| + | natTable.setConfigRegistry(configRegistry); | ||
| + | natTable.addConfiguration(new DefaultNatTableStyleConfiguration()); | ||
| + | natTable.configure(); | ||
| + | |||
| + | } | ||
| + | |||
| + | |||
| + | |||
| + | |||
| + | //HELPER | ||
| + | private IDataProvider setupBigDataProvider() { | ||
| + | DummyBodyDataProvider data = new DummyBodyDataProvider(500, | ||
| + | return data; | ||
| + | } | ||
| + | | ||
| + | | ||
| + | public class BodyStack extends AbstractLayerTransform { | ||
| + | |||
| + | private DataLayer bodyDataLayer; | ||
| + | private ColumnReorderLayer columnReorderLayer; | ||
| + | private ColumnHideShowLayer columnHideShowLayer; | ||
| + | private SelectionLayer selectionLayer; | ||
| + | private ViewportLayer viewportLayer; | ||
| + | |||
| + | public BodyStack(IDataProvider bodyDataProvider){ | ||
| + | bodyDataLayer = new DataLayer(bodyDataProvider); | ||
| + | columnReorderLayer = new ColumnReorderLayer(bodyDataLayer); | ||
| + | columnHideShowLayer = new ColumnHideShowLayer(columnReorderLayer); | ||
| + | selectionLayer = new SelectionLayer(columnHideShowLayer); | ||
| + | viewportLayer = new ViewportLayer(selectionLayer); | ||
| + | | ||
| + | // make the bottom layer to the layer, which will be used to extend the stack | ||
| + | setUnderlyingLayer(viewportLayer); | ||
| + | } | ||
| + | |||
| + | } | ||
| + | |||
| + | private IDataProvider getRowLabelProvider(final IDataProvider bodyDataProvider){ | ||
| + | return new IDataProvider() { | ||
| + | |||
| + | @Override | ||
| + | public void setDataValue(int columnIndex, | ||
| + | //nothing | ||
| + | } | ||
| + | |||
| + | @Override | ||
| + | public int getRowCount() { | ||
| + | return bodyDataProvider.getRowCount(); | ||
| + | } | ||
| + | |||
| + | @Override | ||
| + | public Object getDataValue(int columnIndex, | ||
| + | //here the name of the row is set | ||
| + | return rowIndex; | ||
| + | } | ||
| + | |||
| + | @Override | ||
| + | public int getColumnCount() { | ||
| + | return 1; | ||
| + | } | ||
| + | }; | ||
| + | } | ||
| + | private IDataProvider getColumnLabelProvider(final IDataProvider bodyDataProvider){ | ||
| + | return new IDataProvider() { | ||
| + | |||
| + | @Override | ||
| + | public void setDataValue(int columnIndex, | ||
| + | //nothing | ||
| + | } | ||
| + | |||
| + | @Override | ||
| + | public int getRowCount() { | ||
| + | return 1; | ||
| + | } | ||
| + | |||
| + | @Override | ||
| + | public Object getDataValue(int columnIndex, | ||
| + | //here the name of the row is set | ||
| + | return columnIndex; | ||
| + | } | ||
| + | |||
| + | @Override | ||
| + | public int getColumnCount() { | ||
| + | return bodyDataProvider.getColumnCount(); | ||
| + | } | ||
| + | }; | ||
| + | } | ||
| + | |||
| + | private class RowHeaderLayerStack extends AbstractLayerTransform { | ||
| + | |||
| + | public RowHeaderLayerStack(IDataProvider dataProvider, | ||
| + | DataLayer dataLayer = new DataLayer(dataProvider, | ||
| + | RowHeaderLayer rowHeaderLayer = new RowHeaderLayer(dataLayer, | ||
| + | setUnderlyingLayer(rowHeaderLayer); | ||
| + | } | ||
| + | } | ||
| + | |||
| + | } | ||
| + | |||
| + | </ | ||
| + | |||
| + | |||
| + | |||
| + | == Rendering a Cell as a Checkbox == | ||
| + | <sxh java> | ||
| + | | ||
| + | protected void registerDomainSpecificLabels( | ||
| + | ColumnOverrideLabelAccumulator columnOverrideLabelAccumulator) { | ||
| + | |||
| + | //1. add labels to specific columns | ||
| + | columnOverrideLabelAccumulator.registerColumnOverrides(0, | ||
| + | columnOverrideLabelAccumulator.registerColumnOverrides(1, | ||
| + | } | ||
| + | | ||
| + | @Override | ||
| + | protected void modifyConfigRegistry(IConfigRegistry configRegistry) { | ||
| + | |||
| + | //2. register painter using labels | ||
| + | configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_PAINTER, | ||
| + | |||
| + | //3. register editor for BOTH modes NORMAL and EDIT using labels | ||
| + | CheckBoxCellEditor checkBoxCellEditor = new CheckBoxCellEditor(); | ||
| + | configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITOR, | ||
| + | configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITOR, | ||
| + | |||
| + | //4. when are the cells editable? - always | ||
| + | configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITABLE_RULE, | ||
| + | configRegistry.registerConfigAttribute(CellConfigAttributes.DISPLAY_CONVERTER, | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | |||
| + | == Catching Single Clicks on Column == | ||
| + | <sxh java> | ||
| + | @Override | ||
| + | protected void modifyUiBindingRegistry(UiBindingRegistry uiBindingRegistry) { | ||
| + | |||
| + | IMouseEventMatcher mouseEventMatcher = new IMouseEventMatcher() { | ||
| + | @Override | ||
| + | public boolean matches(NatTable natTable, MouseEvent event, | ||
| + | LabelStack regionLabels) { | ||
| + | return true; | ||
| + | } | ||
| + | }; | ||
| + | IMouseAction mouseAction = new IMouseAction() { | ||
| + | @Override | ||
| + | public void run(NatTable natTable, MouseEvent event) { | ||
| + | int columnPosition = natTable.getColumnPositionByX(event.x); | ||
| + | int rowPosition = natTable.getRowPositionByY(event.y); | ||
| + | | ||
| + | int columnIndex = natTable.getColumnIndexByPosition(columnPosition); | ||
| + | int rowIndex = natTable.getRowIndexByPosition(rowPosition); | ||
| + | | ||
| + | IDataProvider dataProvider = getDataProvider(); | ||
| + | boolean val = (boolean) dataProvider.getDataValue(columnIndex, | ||
| + | dataProvider.setDataValue(columnIndex, | ||
| + | } | ||
| + | }; | ||
| + | |||
| + | uiBindingRegistry.registerFirstSingleClickBinding( | ||
| + | mouseEventMatcher, | ||
| + | mouseAction | ||
| + | ); | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | |||
| + | == Registering Comboboxes on a Cell == | ||
| + | <sxh java> | ||
| + | ... | ||
| + | registerColumnLabels(columnOverrideLabelAccumulator, | ||
| + | makeColumnsEditable(); | ||
| + | registerComboBox(configRegistry); | ||
| + | registerValidators(configRegistry); | ||
| + | } | ||
| + | |||
| + | |||
| + | //RENDERING | ||
| + | // 1. add label to the columns | ||
| + | private void registerColumnLabels(ColumnOverrideLabelAccumulator columnOverrideLabelAccumulator, | ||
| + | columnOverrideLabelAccumulator.registerColumnOverrides(0, | ||
| + | columnOverrideLabelAccumulator.registerColumnOverrides(1, | ||
| + | columnOverrideLabelAccumulator.registerColumnOverrides(2, | ||
| + | columnOverrideLabelAccumulator.registerColumnOverrides(3, | ||
| + | columnOverrideLabelAccumulator.registerColumnOverrides(4, | ||
| + | } | ||
| + | |||
| + | //2. make cells editable | ||
| + | private void makeColumnsEditable(){ | ||
| + | //2. make the cell editable | ||
| + | // | ||
| + | configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITABLE_RULE, | ||
| + | configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITABLE_RULE, | ||
| + | configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITABLE_RULE, | ||
| + | configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITABLE_RULE, | ||
| + | } | ||
| + | |||
| + | //3. register painter | ||
| + | //4. register editor | ||
| + | private static void registerComboBox(IConfigRegistry configRegistry ) { | ||
| + | String COMBO_BOX_CONFIG_LABEL = " | ||
| + | String FORMAT_PRICING_TYPE_CONFIG_LABEL = " | ||
| + | |||
| + | ICellPainter comboBoxCellPainter = new ComboBoxPainter(); | ||
| + | // | ||
| + | // | ||
| + | ICellEditor comboBoxCellEditor = new ComboBoxCellEditor(Arrays.asList(1, | ||
| + | |||
| + | configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_PAINTER, | ||
| + | configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITOR, | ||
| + | configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITOR, | ||
| + | |||
| + | // displayed values can be displayed differently | ||
| + | // ACHTUNG: DISPLAY_CONVERTER can't return Integer when using ComboBoxCellEditor | ||
| + | configRegistry.registerConfigAttribute(CellConfigAttributes.DISPLAY_CONVERTER, | ||
| + | } | ||
| + | |||
| + | |||
| + | //5. data validator | ||
| + | private void registerValidators(IConfigRegistry configRegistry){ | ||
| + | // | ||
| + | } | ||
| + | |||
| + | </ | ||
| + | |||
| + | |||
| + | == Retrieving an ICellEditor from the cell== | ||
| + | <sxh java> | ||
| + | ILayerCell cell = natTable.getCellByPosition(natTable.getColumnPositionByX(event.x), | ||
| + | ICellEditor cellEditor = natTable.getConfigRegistry().getConfigAttribute(EditConfigAttributes.CELL_EDITOR, | ||
| + | </ | ||
| + | |||
| + | == Retrieving an ICellPainter from the cell== | ||
| + | <sxh java> | ||
| + | ILayerCell cell = natTable.getCellByPosition(natTable.getColumnPositionByX(event.x), | ||
| + | ICellPainter painter = natTableNew.getCellPainter(colPosition, | ||
| + | </ | ||
| + | |||
| + | == Rendering an own SWT Widget inside of the table == | ||
| + | <sxh java> | ||
| + | Control myControl = new Label(natTable, | ||
| + | |||
| + | ICellPainter cellPainter = new AbstractCellPainter() { | ||
| + | public void paintCell(ILayerCell cell, GC gc, Rectangle bounds, IConfigRegistry configRegistry) { | ||
| + | myControl.setBounds(bounds); | ||
| + | } | ||
| + | |||
| + | public int getPreferredWidth(ILayerCell cell, GC gc, IConfigRegistry configRegistry) { | ||
| + | return myControl.getBounds().y; | ||
| + | } | ||
| + | |||
| + | public int getPreferredHeight(ILayerCell cell, GC gc, IConfigRegistry configRegistry) { | ||
| + | return myControl.getBounds().x; | ||
| + | } | ||
| + | }; | ||
| + | </ | ||
| + | ===== NatTable Entry-Point ===== | ||
| + | Here the painting of the table is started: | ||
| + | |||
| + | **GridLineCellLayerPainter.class** | ||
| + | <sxh java> | ||
| + | @Override | ||
| + | public void paintLayer(ILayer natLayer, GC gc, int xOffset, int yOffset, | ||
| + | Rectangle rectangle, IConfigRegistry configRegistry) { | ||
| + | Boolean renderConfig = null; | ||
| + | LabelStack stack = natLayer.getRegionLabelsByXY(xOffset, | ||
| + | if (stack != null) { | ||
| + | // check if there is a configuration telling to not rendering grid | ||
| + | // lines | ||
| + | renderConfig = configRegistry.getConfigAttribute( | ||
| + | CellConfigAttributes.RENDER_GRID_LINES, | ||
| + | stack.getLabels()); | ||
| + | } | ||
| + | |||
| + | this.renderGridLines = (renderConfig != null) ? renderConfig : true; | ||
| + | |||
| + | // Draw GridLines | ||
| + | if (this.renderGridLines) | ||
| + | drawGridLines(natLayer, | ||
| + | |||
| + | super.paintLayer(natLayer, | ||
| + | configRegistry); | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | |||
| + | |||
| + | |||
| + | =====Useful Code ===== | ||
| + | |||
| + | ====Debugging Table==== | ||
| + | For debugging it is really important to know, which settings are saved for each particular cell in table. | ||
| + | This code will echo the configuration for each cell, which the mouse hovers over. | ||
| + | |||
| + | <sxh java> | ||
| + | import java.util.Arrays; | ||
| + | import java.util.List; | ||
| + | |||
| + | import org.eclipse.core.runtime.Platform; | ||
| + | import org.eclipse.nebula.widgets.nattable.NatTable; | ||
| + | import org.eclipse.nebula.widgets.nattable.config.CellConfigAttributes; | ||
| + | import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry; | ||
| + | import org.eclipse.nebula.widgets.nattable.data.convert.IDisplayConverter; | ||
| + | import org.eclipse.nebula.widgets.nattable.edit.EditConfigAttributes; | ||
| + | import org.eclipse.nebula.widgets.nattable.edit.editor.ICellEditor; | ||
| + | import org.eclipse.nebula.widgets.nattable.layer.LabelStack; | ||
| + | import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell; | ||
| + | import org.eclipse.nebula.widgets.nattable.painter.cell.ICellPainter; | ||
| + | import org.eclipse.nebula.widgets.nattable.style.DisplayMode; | ||
| + | import org.eclipse.swt.events.MouseEvent; | ||
| + | import org.eclipse.swt.events.MouseMoveListener; | ||
| + | |||
| + | public class TableObjectsDebugTrace { | ||
| + | |||
| + | private static final String PARAMETER_FOR_DEBUG_TRACE_IN_NATTABLE = " | ||
| + | |||
| + | // init | ||
| + | boolean showCoordinates = true; | ||
| + | boolean showLabels = true; | ||
| + | boolean showCellPainter = true; | ||
| + | boolean showCellEditor = true; | ||
| + | boolean showDisplayConverter = true; | ||
| + | |||
| + | private NatTable natTable; | ||
| + | private IConfigRegistry configRegistry; | ||
| + | |||
| + | public TableObjectsDebugTrace(NatTable nattable) { | ||
| + | this.natTable = nattable; | ||
| + | this.configRegistry = nattable.getConfigRegistry(); | ||
| + | |||
| + | // check for my custom runtime configuration parameter | ||
| + | List< | ||
| + | |||
| + | if (parameters.contains(PARAMETER_FOR_DEBUG_TRACE_IN_NATTABLE)) { | ||
| + | nattable.addMouseMoveListener(getDebugTraceMouseListener()); | ||
| + | } | ||
| + | } | ||
| + | |||
| + | private MouseMoveListener getDebugTraceMouseListener() { | ||
| + | |||
| + | MouseMoveListener listener = new MouseMoveListener() { | ||
| + | |||
| + | @Override | ||
| + | public void mouseMove(MouseEvent event) { | ||
| + | |||
| + | int row = natTable.getRowPositionByY(event.y); | ||
| + | int col = natTable.getColumnPositionByX(event.x); | ||
| + | ILayerCell cellTemp = natTable.getCellByPosition(col, | ||
| + | |||
| + | if (cellTemp == null) { | ||
| + | // we are pointing to an empty space, ther is no cell | ||
| + | return; | ||
| + | } | ||
| + | |||
| + | LabelStack labelStack = cellTemp.getConfigLabels(); | ||
| + | |||
| + | if (labelStack == null) { | ||
| + | return; | ||
| + | } | ||
| + | List< | ||
| + | |||
| + | ICellPainter cellPainter = configRegistry.getConfigAttribute(CellConfigAttributes.CELL_PAINTER, | ||
| + | DisplayMode.NORMAL, | ||
| + | ICellEditor cellEditor = configRegistry.getConfigAttribute(EditConfigAttributes.CELL_EDITOR, | ||
| + | DisplayMode.EDIT, | ||
| + | |||
| + | IDisplayConverter displayConverterNorm = configRegistry.getConfigAttribute( | ||
| + | CellConfigAttributes.DISPLAY_CONVERTER, | ||
| + | IDisplayConverter displayConverterEdit = configRegistry.getConfigAttribute( | ||
| + | CellConfigAttributes.DISPLAY_CONVERTER, | ||
| + | |||
| + | StringBuilder stringBuilder = new StringBuilder(); | ||
| + | |||
| + | if (showCoordinates) { | ||
| + | stringBuilder.append(" | ||
| + | stringBuilder.append(" | ||
| + | } | ||
| + | |||
| + | if (showLabels) { | ||
| + | stringBuilder.append(" | ||
| + | stringBuilder.append(" | ||
| + | } | ||
| + | |||
| + | if (showCellPainter) { | ||
| + | stringBuilder.append(" | ||
| + | stringBuilder.append(" | ||
| + | } | ||
| + | |||
| + | if (showCellEditor) { | ||
| + | stringBuilder.append(" | ||
| + | stringBuilder.append(" | ||
| + | } | ||
| + | |||
| + | if (showDisplayConverter) { | ||
| + | stringBuilder.append(" | ||
| + | stringBuilder.append(" | ||
| + | stringBuilder.append(" | ||
| + | stringBuilder.append(" | ||
| + | stringBuilder.append(" | ||
| + | } | ||
| + | |||
| + | // echo result | ||
| + | System.out.println(stringBuilder.toString()); | ||
| + | } | ||
| + | |||
| + | }; | ||
| + | |||
| + | return listener; | ||
| + | } | ||
| + | } | ||
| + | </ | ||
