Cell: how to activate a contextMenu by keyboard?

前提是你 提交于 2019-12-01 19:26:24

Digging up some facts:

  • the contextMenuEvent is created and fired off in scene.processMenuEvent(...)
  • for keyboard triggered events, the method calculates scene/screen coordinates relative to somewhere in the middle of the target node (which is the current focus owner)
  • these (scene/screen) absolute coordinates can't be changed: event.copyFor(...) only maps them to the new target local coordinates

So any hope for some automagic didn't work out, we have to re-calculate the location. A (tentative) place to do this is a custom EventDispatcher. The raw (read: missing all sanity checks, not formally tested, might have unwanted side-effects!) example below simply replaces a keyboard-triggered contextMenuEvent by a new one before delegating to an injected EventDispatcher. Client code (like f.i. the ListViewSkin) must pass-in the targetCell before prepending to the EventDispatchChain.

/**
 * EventDispatcher that replaces a keyboard-triggered ContextMenuEvent by a 
 * newly created event that has screen coordinates relativ to the target cell.
 * 
 */
private static class ContextMenuEventDispatcher implements EventDispatcher {

    private EventDispatcher delegate;
    private Cell<?> targetCell;

    public ContextMenuEventDispatcher(EventDispatcher delegate) {
        this.delegate = delegate;
    }

    /**
     * Sets the target cell for the context menu.
     * @param cell
     */
    public void setTargetCell(Cell<?> cell) {
        this.targetCell = cell;
    }

    /**
     * Implemented to replace a keyboard-triggered contextMenuEvent before
     * letting the delegate dispatch it.
     * 
     */
    @Override
    public Event dispatchEvent(Event event, EventDispatchChain tail) {
        event = handleContextMenuEvent(event);
        return delegate.dispatchEvent(event, tail);
    }

    private Event handleContextMenuEvent(Event event) {
        if (!(event instanceof ContextMenuEvent) || targetCell == null) return event;
        ContextMenuEvent cme = (ContextMenuEvent) event;
        if (!cme.isKeyboardTrigger()) return event;
        final Bounds bounds = targetCell.localToScreen(
                targetCell.getBoundsInLocal());
        // calculate screen coordinates of contextMenu
        double x2 = bounds.getMinX() + bounds.getWidth() / 4;
        double y2 = bounds.getMinY() + bounds.getHeight() / 2;
        // instantiate a contextMenuEvent with the cell-related coordinates
        ContextMenuEvent toCell = new ContextMenuEvent(ContextMenuEvent.CONTEXT_MENU_REQUESTED, 
                0, 0, x2, y2, true, null);
        return toCell;
    }

}

// usage (f.i. in ListViewSkin)
/**
 * ListViewSkin that implements EventTarget and hooks the focused cell into
 * the event dispatch chain
 */
private static class ListViewCSkin<T> extends ListViewSkin<T> implements
        EventTarget {

    private ContextMenuEventDispatcher contextHandler = 
            new ContextMenuEventDispatcher(new EventHandlerManager(this));

    @Override
    public EventDispatchChain buildEventDispatchChain(
            EventDispatchChain tail) {
        int focused = getSkinnable().getFocusModel().getFocusedIndex();
        Cell cell = null;
        if (focused > -1) {
            cell = flow.getCell(focused);
            tail = cell.buildEventDispatchChain(tail);
        }
        contextHandler.setTargetCell(cell);
        // the handlerManager doesn't make a difference
        return tail.prepend(contextHandler);
    }

    // boiler-plate constructor
    public ListViewCSkin(ListView<T> listView) {
        super(listView);
    }

}

Edit

Just noticed a slight (?) glitch in that a keyboard-activated contextMenu on the listView is shown at the cell location if the cell doesn't have a contextMenu on its own. Couldn't find a way to not replace the event if unused by the cell, probably still missing something obvious (?) in the event dispatch.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!