Search code examples
javajpanelkeylistenerkey-bindingskeyevent

MainPanel not listening to KeyBinding inside nested JPanel


I have a JFrame and on it a MainPanel that extends a JPanel. The MainPanel contains 2 other JPanels using the BorderLayout to arrange the view.

One of the Panel contains a button where the click event is triggered. But for the other Panel, the keybinding does not get triggered at all.

Here's the code for the MainClass that hold the 2 Panels

public class MainClass implements ... {

    public JFrame frame;
    JPanel highscore;
    Board board;
    Board.MoveAction moveAction;

    @Override
    public void init(Platform platform, int id) {
        this.platform = platform;
        this.selfId = id;

        platform.setAbout("Hello ");
        //platform.setSleepAfterSync(sleepPeriod);

        frame = platform.createWindow(false);

        // Create mainPanel to add all nested panels and buttons inside
        JPanel mainPanel = new MainPanel();
        mainPanel.add(board, BorderLayout.CENTER);
        mainPanel.add(highscore, BorderLayout.PAGE_END);
        frame.add(mainPanel);

        frame.setTitle("Test");
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setSize(380, 440);
        //frame.setBackground(Color.YELLOW);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }


public class MainPanel extends JPanel {

    public MainPanel() {
        setLayout(new BorderLayout());
        //setBackground(Color.RED);

        <-- the board here is the issue in the mainpanel-->
        board = new Board(platform);
        //board.setBackground(Color.BLUE);


        highscore = new JPanel();
        JButton button = new JButton("highscore");
        button.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.print("highscore button");
            }
        });
        highscore.add(button);
        //highscore.setBackground(Color.GREEN);
    }
}

And here is the code for the Board class that extends JPanel

public class Board extends JPanel implements ActionListener {

    public Platform platform;
    private int req_dx, req_dy, view_dx, view_dy;
    private int IFW = JComponent.WHEN_IN_FOCUSED_WINDOW;
    private final String MOVE_UP = "UP";
    private final String MOVE_DOWN = "DOWN";
    private final String MOVE_RIGHT = "RIGHT";
    private final String MOVE_LEFT = "LEFT";
    private final String START_GAME = "START";

    public Board(Platform platform) {

        this.platform = platform;
        loadImages();
        initVariables();
        initBoard();
        initKeyBinding();
    }

    private void initBoard() {
        setBackground(Color.black);
        setDoubleBuffered(true);
    }

    private void initVariables() {
    ...
    ...

    }

    ..
    ..
    ..
    ..

     public void initKeyBinding() {

        System.out.println("HERE?  init key binding -> yep ");

        addKeyBinding(MOVE_UP, KeyEvent.VK_UP, new MoveAction(MOVE_UP, 0,-1));
        addKeyBinding(MOVE_DOWN, KeyEvent.VK_DOWN, new MoveAction(MOVE_DOWN, 0,1));
        addKeyBinding(MOVE_LEFT, KeyEvent.VK_LEFT, new MoveAction(MOVE_LEFT, -1 ,0));
        addKeyBinding(MOVE_RIGHT, KeyEvent.VK_RIGHT, new MoveAction(MOVE_RIGHT, 1,0));

        //this 's' to start is definitely wrong
        //figure out how to change from if (key == 's' || key == 'S') to keybinding
        //addKeyBinding(START_GAME, KeyEvent.VK_S, new MoveAction(START_GAME, KeyEvent.VK_S, KeyEvent.VK_S));
}


    private void addKeyBinding(String name, int keyCode, AbstractAction action) {
        //on keyRelease - true if the KeyStroke should represent a key release; false otherwise
        // keyCode - an int specifying the numeric code for a keyboard key
        // ie getInputMap(IFW).put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true/false), MOVE_UP);

        getInputMap(IFW).put(KeyStroke.getKeyStroke(keyCode, 0), name);
        getActionMap().put(keyCode, action);
}


    public class MoveAction extends AbstractAction {

        private int deltaX;
        private int deltaY;
        private String direction;

        public MoveAction(String direction, int deltaX, int deltaY) {
            this.direction = direction;
            this.deltaX = deltaX;
            this.deltaY = deltaY;
        }

        @Override
        public void actionPerformed(ActionEvent e) {

            System.out.print(direction);


            switch (direction) {
                case MOVE_UP:
                    System.out.println("up? ");
                    req_dx = 0;
                    req_dy = -1;
                    // do stuff that will move up
                case MOVE_DOWN:
                    System.out.println("down? ");
                    req_dx = 0;
                    req_dy = 1;
                    // do stuff that will move down
               // case START_GAME:
               //     System.out.println("sss");
               //     inGame = true;
               //     initGame();

            }
        }
    }
}

I understand there are 4 components to note for KeyBinding

an Action ie actionPerformed {.. }

a KeyStroke ie (KeyStroke.getKeyStroke..)

an InputMap ie getInputMap(...)

an ActionMap ie getActionMap(...)

What am I missing for my keyboard event to be triggered in the MainPanel? thanks!


Solution

  • using this as reference KeyEvent keyPressed key combination is blocked managed to change keylisteners to keybinding instead

      public void initKeyBinding() {
    
        mapKeys = new HashMap<>();
        mapKeys.put(MOVE_UP, false);
        mapKeys.put(MOVE_DOWN, false);
        mapKeys.put(MOVE_LEFT, false);
        mapKeys.put(MOVE_RIGHT, false);
        mapKeys.put(START_GAME, false);
        mapKeys.put(PAUSE_GAME, false);
        mapKeys.put(ESCAPE, false);
    
        addKeyBinding(KeyEvent.VK_UP, MOVE_UP);
        addKeyBinding(KeyEvent.VK_DOWN, MOVE_DOWN);
        addKeyBinding(KeyEvent.VK_LEFT, MOVE_LEFT);
        addKeyBinding(KeyEvent.VK_RIGHT, MOVE_RIGHT);
        addKeyBinding(KeyEvent.VK_S, START_GAME);
        addKeyBinding(KeyEvent.VK_ESCAPE, ESCAPE);
    
    }
    
    private void addKeyBinding(int keyCode, String name) {
        // keyRelease - true if the KeyStroke should represent a key release; false otherwise
        // keyCode - an int specifying the numeric code for a keyboard key
    
        InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW);
        ActionMap actionMap = getActionMap();
    
        inputMap.put(KeyStroke.getKeyStroke(keyCode, 0, true), "pressed" + name);
        inputMap.put(KeyStroke.getKeyStroke(keyCode, 0, false), "released" + name);
    
        actionMap.put("pressed" + name, new KeyAction(name, true));
        actionMap.put("released" + name, new KeyAction(name, false));
    }
    
    
    public class KeyAction extends AbstractAction {
        private String name;
        private boolean state;
    
        public KeyAction(String name, boolean state) {
            this.name = name;
            this.state = state;
        }
    
        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println(name);
            System.out.println(state);
            mapKeys.put(name, state);
    
            if (inGame) {
                switch (name) {
                    case MOVE_UP:
                        req_dx = 0;
                        req_dy = -1;
                        break;
                    case MOVE_DOWN:
                        req_dx = 0;
                        req_dy = 1;
                        break;
                    case MOVE_RIGHT:
                        req_dx = 1;
                        req_dy = 0;
                        break;
                    case MOVE_LEFT:
                        req_dx = -1;
                        req_dy = 0;
                        break;
                    case ESCAPE:
                        if (timer.isRunning()) {
                            inGame = false;
                        }
                        break;
                }
            } else {
                if (mapKeys.put(START_GAME, false)) {
                    inGame = true;
                    initGame();
                }
            }
        }
    }
    
    @Override
    public void actionPerformed(ActionEvent e) {
    
        repaint();
    }