Search code examples
javaawt

Java AWT: JFrame SetLocation should not move outside the screen


Using the four buttons, the window should be moved in four directions (north, south, east, west) and the window should not leave the screen. This works smoothly to the north and west, but unfortunately not in the other directions. I guess that the error is in the getEffectiveScreenArea method, but I am not sure.

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;

public class MoveWindowGUI extends JFrame {
    private JButton moveUp, moveDown, moveLeft, moveRight;
    private int x, y;
    
    public MoveWindowGUI(String title) {
        super(title);
        this.initializeButtons();
        this.prepareWindow();
        this.addActionListener();
        pack();
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);
    }
    
    private void initializeButtons() {
        this.moveUp = new JButton("U");
        this.moveDown = new JButton("D");
        this.moveLeft = new JButton("L");
        this.moveRight = new JButton("R");
    }
    
    private void prepareWindow() {
        setLayout(new BorderLayout(25, 25));
        this.addComponents();
        this.centerWindow();
        setSize(300, 300);
    }
    
    private void centerWindow() {
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        Dimension windowSize = getSize();
        int centralWidth = (screenSize.width - windowSize.width) / 2;
        int centralHeight = (screenSize.height - windowSize.height) / 2;
        setLocation(centralWidth, centralHeight);
    }
    
    private void addComponents() {
        add(this.moveUp, BorderLayout.NORTH);
        add(this.moveDown, BorderLayout.SOUTH);
        add(this.moveLeft, BorderLayout.WEST);
        add(this.moveRight, BorderLayout.EAST);
    }
    
    private void addActionListener() {
        ActionListener listener = new WindowActionListener();
        moveUp.addActionListener(listener);
        moveDown.addActionListener(listener);
        moveLeft.addActionListener(listener);
        moveRight.addActionListener(listener);
    }
    
    class WindowActionListener implements ActionListener {  
        @Override
        public void actionPerformed(ActionEvent e) {
            String command = e.getActionCommand();
            if (command.equals("U")) {
                y -= 10;
            } else if (command.equals("D")) {
                y += 10;
            } else if (command.equals("L")) {
                x -= 10;
            } else if (command.equals("R")) {
                x += 10;
            }
            this.refreshWindow();
        }
        
        private void refreshWindow() {
            Point lastLocation = getLocation();
            Rectangle effectiveScreenArea = getEffectiveScreenArea();
            int newX = lastLocation.x + x;
            int newY = lastLocation.y + y;
            if (newX < effectiveScreenArea.x) {
                newX = effectiveScreenArea.x;
            } else if (newY < effectiveScreenArea.y) {
                newY = effectiveScreenArea.y;
            } else if (newX >= effectiveScreenArea.width) {
                newX = effectiveScreenArea.width;
            } else if (newY >= effectiveScreenArea.height) {
                newY = effectiveScreenArea.height;
            }
            setLocation(newX, newY);
            this.resetHeightAndWitdhLocally();
            pack();
        }
        
        public Rectangle getEffectiveScreenArea() {
            int minX = 0, minY = 0, maxX = 0, maxY = 0;
            GraphicsEnvironment environment = GraphicsEnvironment.getLocalGraphicsEnvironment();
            int screenDevices = environment.getScreenDevices().length;
            for(GraphicsDevice device : environment.getScreenDevices()) {
                Rectangle bounds = device.getDefaultConfiguration().getBounds();
                minX = Math.min(minX, bounds.x);
                minY = Math.min(minY, bounds.y);
                maxX = Math.max(maxX, bounds.x + bounds.width);
                maxY = Math.max(maxY, bounds.y + bounds.height);
            }
            return new Rectangle(minX, minY, (maxX - minX) / screenDevices, (maxY - minY) / screenDevices);
        }
        
        /*
        private Rectangle getEffectiveScreenArea() {
            GraphicsConfiguration graphicsConfiguration = getGraphicsConfiguration();
            Rectangle bounds = graphicsConfiguration.getBounds();
            Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets(graphicsConfiguration);
            Rectangle effectiveScreenArea = new Rectangle();
            effectiveScreenArea.x = bounds.x + screenInsets.left;
            effectiveScreenArea.y = bounds.y + screenInsets.top;
            effectiveScreenArea.height = bounds.height + screenInsets.top - screenInsets.bottom;
            effectiveScreenArea.width = bounds.width - screenInsets.left - screenInsets.right;
            return effectiveScreenArea;
        }
        */
        
        private void resetHeightAndWitdhLocally() {
            x = 0;
            y = 0;
        }
    }
}

Solution

  • I took your code and modified it somewhat. Here's the GUI I created.

    Move Window GUI

    I placed the JButtons on a JPanel. Then I placed the JPanel on the JFrame. Generally, it's a good idea to put Swing components on a JPanel, rather than directly on the JFrame.

    I cleaned up the WindowActionListener class.

    Here's the complete runnable code. I made the WindowActionListener class an inner class so I could post this code as one block.

    import java.awt.BorderLayout;
    import java.awt.Dimension;
    import java.awt.GraphicsDevice;
    import java.awt.GraphicsEnvironment;
    import java.awt.Rectangle;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    
    import javax.swing.BorderFactory;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.SwingUtilities;
    
    public class MoveWindowGUI implements Runnable {
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new MoveWindowGUI());
        }
        
        private JFrame frame;
    
        @Override
        public void run() {
            frame = new JFrame("Move Window GUI");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            
            frame.add(createMainPanel(), BorderLayout.CENTER);
            
            frame.pack();
            frame.setLocationByPlatform(true);
            frame.setVisible(true);
        }
        
        private JPanel createMainPanel() {
            JPanel panel = new JPanel(new BorderLayout());
            panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
    
            WindowActionListener listener = new WindowActionListener(this);
    
            JButton upButton = new JButton("Up");
            upButton.addActionListener(listener);
            panel.add(upButton, BorderLayout.NORTH);
            
            JButton downButton = new JButton("Down");
            downButton.addActionListener(listener);
            panel.add(downButton, BorderLayout.SOUTH);
            
            JButton leftButton = new JButton("Left");
            leftButton.addActionListener(listener);
            panel.add(leftButton, BorderLayout.WEST);
            
            JButton rightButton = new JButton("Right");
            rightButton.addActionListener(listener);
            panel.add(rightButton, BorderLayout.EAST);
            
            Dimension d = panel.getPreferredSize();
            d.width = 300;
            panel.setPreferredSize(d);
    
            return panel;
        }
        
        public JFrame getFrame() {
            return frame;
        }
        
        public class WindowActionListener implements ActionListener {
            
            private final MoveWindowGUI view;
    
            public WindowActionListener(MoveWindowGUI view) {
                this.view = view;
            }
    
            @Override
            public void actionPerformed(ActionEvent event) {
                String command = event.getActionCommand();
                int increment = 100;
                switch (command) {
                case "Up":
                    refreshWindow(0, -increment);
                    break;
                case "Down":
                    refreshWindow(0, increment);
                    break;
                case "Left":
                    refreshWindow(-increment, 0);
                    break;
                case "Right":
                    refreshWindow(increment, 0);
                    break;
                }
            }
            
            private void refreshWindow(int deltaX, int deltaY) {
                Rectangle frameArea = view.getFrame().getBounds();
                Rectangle screenArea = getEffectiveScreenArea();
                
                int x = frameArea.x;
                int y = frameArea.y;
                int minX = screenArea.x;
                int minY = screenArea.y;
                int maxX = screenArea.width - frameArea.width;
                int maxY = screenArea.height - frameArea.height;
                
                x += deltaX;
                y += deltaY;
                
                x = Math.max(minX, x);
                x = Math.min(maxX, x);
                
                y = Math.max(minY, y);
                y = Math.min(maxY, y);
                
                view.getFrame().setBounds(new Rectangle(x, y, frameArea.width, frameArea.height));
            }
            
            private Rectangle getEffectiveScreenArea() {
                int minX = 0, minY = 0, maxX = 0, maxY = 0;
                GraphicsEnvironment environment = GraphicsEnvironment.getLocalGraphicsEnvironment();
                int screenDevices = environment.getScreenDevices().length;
                for (GraphicsDevice device : environment.getScreenDevices()) {
                    Rectangle bounds = device.getDefaultConfiguration().getBounds();
                    minX = Math.min(minX, bounds.x);
                    minY = Math.min(minY, bounds.y);
                    maxX = Math.max(maxX, bounds.x + bounds.width);
                    maxY = Math.max(maxY, bounds.y + bounds.height);
                }
                return new Rectangle(minX, minY, (maxX - minX) / screenDevices, 
                        (maxY - minY) / screenDevices);
            }
            
        }
    
    }