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.

Comments:

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?

Posted by Mike on March 30, 2013 at 10:21 AM PDT #

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.

Posted by Geertjan on March 31, 2013 at 01:36 AM PDT #

Post a Comment:
  • HTML Syntax: NOT allowed
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
« April 2014
SunMonTueWedThuFriSat
  
12
13
14
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today