X

Geertjan's Blog

  • March 29, 2013

Keyboard Move Action for NetBeans Visual Library

Geertjan Wielenga
Product Manager

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.

Join the discussion

Comments ( 2 )
  • Mike Saturday, March 30, 2013

    Awesome!! It totally worked!!

    I just added another delete function for the key event. Also worked!

    --

    else if (event.getKeyCode() == KeyEvent.VK_DELETE){

    widget.removeFromParent();

    }

    --

    By the way, did you figure out what was causing the problem with the input-action map?


  • Geertjan Sunday, March 31, 2013

    Great to hear it works for you Mike! The InputAction map problem was the same as this one, i.e., the key event processing had not been correctly set. Also, you were interested in adding connections between widgets, see the next blog entry, i.e., https://blogs.oracle.com/geertjan/entry/adding_removing_connection_widgets for code and details on that topic.


Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.