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; | ||
+ | } | ||
+ | } | ||
+ | </ |