Friday Mar 29, 2013

Keyboard Move Action for NetBeans Visual Library

By default, the MoveAction on Widgets in the NetBeans Visual Library lets the user move widgets via the mouse. But what about the keyboard? Using the code below, whenever the user presses Up/Down/Left/Right on a widget, it will move 20 pixels in that direction.

Note: Some little gotchas to be aware of are also solved below, e.g., include a SelectProvider so that when a widget is selected, it receives the focus and, more importantly, so that key event processing is correct.

public class AccountBuchWidget extends VMDNodeWidget {

    public AccountBuchWidget(final Scene scene, AccountBuch ab, Point point) {
        super(scene);
        setNodeName(ab.getDescription());
        setPreferredLocation(point);
        getActions().addAction(new KeyboardMoveAction());
        getActions().addAction(ActionFactory.createSelectAction(new SelectProvider() {
            @Override
            public boolean isAimingAllowed(Widget widget, Point localLocation, boolean invertSelection) {
                return true;
            }
            @Override
            public boolean isSelectionAllowed(Widget widget, Point localLocation, boolean invertSelection) {
                return true;
            }
            @Override
            public void select(Widget widget, Point localLocation, boolean invertSelection) {
                scene.setFocusedWidget(widget);
            }
        }));
    }

    private final class KeyboardMoveAction extends WidgetAction.Adapter {
        private MoveProvider provider;
        private KeyboardMoveAction() {
            this.provider = ActionFactory.createDefaultMoveProvider();
        }
        @Override
        public WidgetAction.State keyPressed(Widget widget, WidgetAction.WidgetKeyEvent event) {
            Point originalSceneLocation = provider.getOriginalLocation(widget);
            int newY = originalSceneLocation.y;
            int newX = originalSceneLocation.x;
            if (event.getKeyCode() == KeyEvent.VK_UP) {
                newY =
newY - 20;
            } else if (event.getKeyCode() == KeyEvent.VK_DOWN) {
                newY =
newY + 20;
            } else if (event.getKeyCode() == KeyEvent.VK_RIGHT) {
                newX =
newX + 20;
            } else if (event.getKeyCode() == KeyEvent.VK_LEFT) {
                newX =
newX - 20;
            }
            provider.movementStarted(widget);
            provider.setNewLocation(widget, new Point(newX, newY));
            return State.CONSUMED;
        }
        @Override
        public WidgetAction.State keyReleased(Widget widget, WidgetAction.WidgetKeyEvent event) {
            provider.movementFinished(widget);
            return State.REJECTED;
        }
    }

}

Here's the constructor of the related TopComponent, note especially the call to "setKeyEventProcessingType":

public EditorTopComponent() {

    initComponents();
    setName(Bundle.CTL_EditorTopComponent());
    setToolTipText(Bundle.HINT_EditorTopComponent());

    setLayout(new BorderLayout());

    JScrollPane pane = new JScrollPane();

    final LayerWidget layerWidget = new LayerWidget(scene);

    scene.getActions().addAction(ActionFactory.createAcceptAction(new AcceptProvider() {
        @Override
        public ConnectorState isAcceptable(Widget widget, Point point, Transferable t) {
            return ConnectorState.ACCEPT;
        }
        @Override
        public void accept(Widget widget, Point point, Transferable t) {
            Node node = NodeTransfer.node(t, NodeTransfer.DND_COPY_OR_MOVE);
            final AccountBuch ab = node.getLookup().lookup(AccountBuch.class);
            layerWidget.addChild(new AccountBuchWidget(scene, ab, point));
        }
    }));

    scene.setKeyEventProcessingType(EventProcessingType.FOCUSED_WIDGET_AND_ITS_CHILDREN);

    scene.addChild(layerWidget);

    pane.setViewportView(scene.createView());

    add(pane, BorderLayout.CENTER);

}

Also, make sure the TopComponent above includes this so that focus is on the Scene when the TopComponent is activated:

    @Override
    public void componentActivated() {
        scene.getView().requestFocusInWindow();
    }

For extra usability, you could include the above Action together with the default MoveAction:

    getActions().addAction(ActionFactory.createMoveAction());
    getActions().addAction(new KeyboardMoveAction());

Now your user can move widgets via the mouse as well as the keyboard. Maybe the keyboard would be used for finetuning the larger movements made by the mouse.

About

Geertjan Wielenga (@geertjanw) is a Principal Product Manager in the Oracle Developer Tools group living & working in Amsterdam. He is a Java technology enthusiast, evangelist, trainer, speaker, and writer. He blogs here daily.

The focus of this blog is mostly on NetBeans (a development tool primarily for Java programmers), with an occasional reference to NetBeans, and sometimes diverging to topics relating to NetBeans. And then there are days when NetBeans is mentioned, just for a change.

Search

Archives
« March 2013 »
SunMonTueWedThuFriSat
     
10
11
12
13
14
15
18
23
24
27
28
      
Today