NatTable cell click not heard by listener

Dave Carpeneto :

The following SSCCE:

  • creates a 1x1 NatTable with the label "BUTTON" applied to the single contained cell
  • adds a ButtonCellPainter which works (the cell looks like a button)
  • tries to add a click listener which doesn't work (else I'd see "YOU ARE HERE" in stderr)

The bit I'm confused about is between the START OF CONFUSION & END OF CONFUSION comment lines - the rest is stuff to get the example set up. I've tried to follow along with the Rendering_cells_as_a_link_and_button example provided, but obviously there's something I'm messing up here.

The performance & aesthetics of NatTable really are amazing; it'd be nice to have someone point out my error here so that I can add this to my project :-)

import org.eclipse.nebula.widgets.nattable.*;
import org.eclipse.nebula.widgets.nattable.config.*;
import org.eclipse.nebula.widgets.nattable.data.*;
import org.eclipse.nebula.widgets.nattable.grid.layer.*;
import org.eclipse.nebula.widgets.nattable.hideshow.*;
import org.eclipse.nebula.widgets.nattable.layer.*;
import org.eclipse.nebula.widgets.nattable.layer.cell.*;
import org.eclipse.nebula.widgets.nattable.painter.cell.*;
import org.eclipse.nebula.widgets.nattable.selection.*;
import org.eclipse.nebula.widgets.nattable.ui.action.*;
import org.eclipse.nebula.widgets.nattable.ui.binding.*;
import org.eclipse.nebula.widgets.nattable.ui.matcher.*;
import org.eclipse.nebula.widgets.nattable.viewport.*;
import org.eclipse.swt.*;
import org.eclipse.swt.events.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.widgets.*;

public class NatTableTest extends Dialog implements IDataProvider, IConfigLabelAccumulator {

    @Override protected Control createDialogArea(Composite parent) {
        Composite toReturn = (Composite) super.createDialogArea(parent);
        toReturn.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
        IDataProvider dataProvider = this; 
        DataLayer bodyDataLayer = new DataLayer(dataProvider);
        bodyDataLayer.setConfigLabelAccumulator(this);
        RowHideShowLayer rowHideShowLayer = new RowHideShowLayer(bodyDataLayer);
        SelectionLayer selectionLayer = new SelectionLayer(rowHideShowLayer);
        ViewportLayer viewportLayer = new ViewportLayer(selectionLayer);
        DataLayer rowDataLayer = new DefaultRowHeaderDataLayer(dataProvider);
        ILayer rowLayer = new RowHeaderLayer(rowDataLayer, viewportLayer, selectionLayer);
        DataLayer headerDataLayer = new DefaultColumnHeaderDataLayer(dataProvider);
        ILayer headerLayer = new ColumnHeaderLayer(headerDataLayer, viewportLayer, selectionLayer);
        DataLayer cornerDataLayer = new DataLayer(dataProvider);
        ILayer cornerLayer = new CornerLayer(cornerDataLayer, rowLayer, headerLayer);
        GridLayer gridLayer = new GridLayer(viewportLayer, headerLayer, rowLayer, cornerLayer);
        NatTable nattable = new NatTable(toReturn, gridLayer, false);
        nattable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
        nattable.addConfiguration(new DefaultNatTableStyleConfiguration());

        // ================================================== START OF CONFUSION

        ButtonCellPainter buttonPainter = new ButtonCellPainter(new TextPainter());

        buttonPainter.addClickListener(new IMouseAction(){
            @Override public void run(NatTable table, MouseEvent event) {
                System.err.println("YOU ARE HERE");
            }
        });     

        nattable.addConfiguration(new AbstractUiBindingConfiguration(){
            @Override public void configureUiBindings(UiBindingRegistry registry) {
                CellLabelMouseEventMatcher mouseEventMatcher = new CellLabelMouseEventMatcher("BODY",1,"BUTTON");
                registry.registerMouseDownBinding(mouseEventMatcher, buttonPainter);
            }
        });

        IConfigRegistry configRegistry = new ConfigRegistry();

        configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_PAINTER, buttonPainter, "NORMAL","BUTTON");

        nattable.setConfigRegistry(configRegistry);

        // ================================================== END OF CONFUSION

        nattable.configure();
        return toReturn;
    }

    public NatTableTest(Shell parentShell) {super(parentShell);}

    // IDataProvider methods
    @Override public int getRowCount() {return 1;}
    @Override public int getColumnCount() {return 1;}
    @Override public Object getDataValue(int var1, int var2) {return "X";}
    @Override public void setDataValue(int var1, int var2, Object var3) {}

    // IConfigLabelAccumulator methods
    @Override public void accumulateConfigLabels(LabelStack labels, int col, int row) {labels.addLabel("BUTTON");}

}

edit: in debugging it seems that in the below (from NatTable's UiBindingRegistry) regionLabels only contains BODY :

    private IMouseAction getMouseEventAction(MouseEventTypeEnum mouseEventType, MouseEvent event) {
        try {
            LinkedList<MouseBinding> mouseEventBindings = (LinkedList) this.mouseBindingsMap.get(mouseEventType);
            if (mouseEventBindings != null) {
                LabelStack regionLabels = this.natTable.getRegionLabelsByXY(event.x, event.y);
                Iterator var6 = mouseEventBindings.iterator();

                while (var6.hasNext()) {
                    MouseBinding mouseBinding = (MouseBinding) var6.next();
                    if (mouseBinding.getMouseEventMatcher().matches(this.natTable, event, regionLabels)) {
                        return mouseBinding.getAction();
                    }
                }
            }
        } catch (Exception var7) {
            log.error("Exception on retrieving a mouse event action", var7);
        }

        return null;
    }

... should BUTTON not also be returned here, since the label is added to that cell (I can tell the region is added correctly since the cell does get rendered as a button)?

NatTable is 2.0.0.201910310701

Dirk Fauth :

The issue is that single click actions are exclusive. So the first match wins. In your case the SelectCellAction gets triggered, which is registered by the DefaultSelectionBindings. And since the selection is triggered, the button click is skipped.

In your code you can simply fix this by using registerFirstMouseDownBinding()

nattable.addConfiguration(new AbstractUiBindingConfiguration(){
        @Override public void configureUiBindings(UiBindingRegistry registry) {
            CellLabelMouseEventMatcher mouseEventMatcher = new CellLabelMouseEventMatcher("BODY",1,"BUTTON");
            registry.registerFirstMouseDownBinding(mouseEventMatcher, buttonPainter);
        }
    });

This way your binding is registered on top of the registry and therefore checked first. As our check has more conditions (region label, state mask, mouse button AND cell label) it is only triggered on the cells with the BUTTON label in the BODY region. The button cell can then not be selected, which is correct IMHO. But you could still select other cells that do not have the BUTTON cell label.

Regarding your second question: No, the BUTTON label should NOT be part of the region labels. It is a cell label, not a region label.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=23276&siteId=1