Skip to main content
replaced http://codereview.stackexchange.com/ with https://codereview.stackexchange.com/
Source Link

The previous and initial iteration at Simulating a GUI windowing system

The previous and initial iteration at Simulating a GUI windowing system

Added newlines between the file names and code.
Source Link
coderodde
  • 29.8k
  • 14
  • 77
  • 194

MyCanvas.java: package net.coderodde.gui.simulation;

package net.coderodde.gui.simulation;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.List;

class MyCanvas extends Canvas {

    private final int width;
    private final int height;
    private final List<MyWindow> windowList;

    MyCanvas(final int width, 
             final int height, 
             final List<MyWindow> wndList,
             final MyScreen screen) {
        this.width = width;
        this.height = height;
        setSize(width, height);
        this.windowList = wndList;
        final MyMouseListener mml = new MyMouseListener(screen);
        addMouseListener(mml);
        addMouseMotionListener(mml);
    }

    @Override
    public void paint(final Graphics g) {
        g.setColor(Color.BLACK);
        g.fillRect(0, 0, width, height);

        for (final MyWindow wnd : windowList) {
            wnd.draw(g);
        }
    }

    @Override
    public void update(final Graphics g) {
        paint(g);
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(width, height);
    }

    static class MyMouseListener implements MouseListener,
                                            MouseMotionListener {

        private final MyScreen screen;

        MyMouseListener(final MyScreen screen) {
            this.screen = screen;
        }

        @Override
        public void mouseClicked(MouseEvent e) {

        }

        @Override
        public void mousePressed(MouseEvent e) {
            screen.press(e.getX(), e.getY());
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            screen.releaseMouse();
        }

        @Override
        public void mouseEntered(MouseEvent e) {
            screen.allowDrag();
        }

        @Override
        public void mouseExited(MouseEvent e) {
            screen.disallowDrag();
        }    

        @Override
        public void mouseDragged(MouseEvent e) {
            screen.drag(e.getX(), e.getY());
        }

        @Override
        public void mouseMoved(MouseEvent e) {
           screen.move(e.getX(), e.getY());
        }
    }
}

MyScreen: package net.coderodde.gui.simulation;

package net.coderodde.gui.simulation;

import java.awt.Canvas;
import java.awt.Dimension;
import java.util.List;
import java.awt.Toolkit;
import java.util.ArrayList;
import javax.swing.JFrame;

public class MyScreen {

    private final static int FRAME_WIDTH = 1024;
    private final static int FRAME_HEIGHT = 768;

    private final JFrame frame;
    private final Canvas canvas;
    private final List<MyWindow> windowList;
    private MyWindow activeWindow;
    private MyWindow draggedWindow;
    private int draggedWindowOffsetX;
    private int draggedWindowOffsetY;
    private boolean topBorderLocked;
    private boolean rightBorderLocked;
    private boolean bottomBorderLocked;
    private boolean leftBorderLocked;
    private MyWindow highlightedWindow;
    private boolean disallowDrag;

    public MyScreen() {
        windowList = new ArrayList<>();
        frame = new JFrame("coderodde GUI simulation");
        frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
        frame.setResizable(false);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        final Dimension realScreenDimension = 
                Toolkit.getDefaultToolkit().getScreenSize();

        frame.setLocation((realScreenDimension.width - frame.getWidth()) / 2, 
                          (realScreenDimension.height - frame.getHeight()) / 2);

        canvas = new MyCanvas(frame.getWidth(), 
                              frame.getHeight(), 
                              windowList, 
                              this);

        frame.add(canvas);
        frame.setVisible(true);
    }

    public void addWindow(final MyWindow window) {
        windowList.add(window);

        if (activeWindow != null) {
            activeWindow.setActive(false);
        }

        window.setActive(true);
        activeWindow = window;
    }

    public void move(final int x, final int y) {
        MyWindow target = null;

        for (int i = windowList.size() - 1; i >= 0; --i) {
            if (windowList.get(i).windowContainsPoint(x, y)) {
                target = windowList.get(i);
                break;
            }
        }

        if (target == null) {
            if (highlightedWindow != null) {
                highlightedWindow.clearBorderHighlights();
                highlightedWindow = null;
                canvas.repaint();
            }

            return;
        }

        if (highlightedWindow != null) {
            highlightedWindow.clearBorderHighlights();
            highlightedWindow = null;
        }

        if (target.topBorderContainsPoint(x, y)) {
            target.highlightBorder(MyWindow.Border.TOP);
            highlightedWindow = target;
        } else if (target.bottomBorderContainsPoint(x, y)) {
            target.highlightBorder(MyWindow.Border.BOTTOM);
            highlightedWindow = target;
        }

        if (target.rightBorderContainsPoint(x, y)) {
            target.highlightBorder(MyWindow.Border.RIGHT);
            highlightedWindow = target;
        } else if (target.leftBorderContainsPoint(x, y)) {
            target.highlightBorder(MyWindow.Border.LEFT);
            highlightedWindow = target;
        }

        canvas.repaint();
    }

    public void press(final int x, final int y) {
        MyWindow newActiveWindow = null;

        for (int i = windowList.size() - 1; i >= 0; --i) {
            if (windowList.get(i).windowContainsPoint(x, y)) {
                newActiveWindow = windowList.remove(i);
                break;
            }
        }

        if (newActiveWindow == null) {
            return;
        }

        activeWindow.setActive(false);
        activeWindow = newActiveWindow;
        windowList.add(activeWindow);
        activeWindow.setActive(true);

        boolean doResize = false;

        if (activeWindow.topBorderContainsPoint(x, y)) {
            topBorderLocked = true;
            doResize = true;
        } else if (activeWindow.bottomBorderContainsPoint(x, y)) {
            bottomBorderLocked = true;
            doResize = true;
        }

        if (activeWindow.leftBorderContainsPoint(x, y)) {
            leftBorderLocked = true;
            doResize = true;
        } else if (activeWindow.rightBorderContainsPoint(x, y)) {
            rightBorderLocked = true;
            doResize = true;
        }

        if (!doResize && activeWindow.titleBarContainsPoint(x, y)) {
            draggedWindow = activeWindow;
            draggedWindowOffsetX = x - draggedWindow.getX();
            draggedWindowOffsetY = y - draggedWindow.getY();
        } 

        if (doResize) {
            draggedWindow = activeWindow;
        }

        canvas.repaint();
    }

    public void allowDrag() {
        disallowDrag = false;
    }

    public void disallowDrag() {
        disallowDrag = true;
    }

    public void drag(final int x, final int y) {
        if (draggedWindow == null) {
            return;
        }

        if (disallowDrag) {
            return;
        }

        if (!(topBorderLocked 
                || rightBorderLocked 
                || bottomBorderLocked
                || leftBorderLocked)) {
            // Move the entire window.
            draggedWindow.setX(x - draggedWindowOffsetX);
            draggedWindow.setY(y - draggedWindowOffsetY);
            canvas.repaint();
        }

        // Resize the window.
        final int originalX = draggedWindow.getX();
        final int originalY = draggedWindow.getY();

        if (topBorderLocked) {
            draggedWindow.resizeAtTop(y - originalY);
        } else if (bottomBorderLocked) {   
            draggedWindow.setHeight(y - originalY);
        }

        if (leftBorderLocked) {
            draggedWindow.resizeAtLeft(x - originalX);
        } else if (rightBorderLocked) {
            draggedWindow.setWidth(x - originalX);
        }

        canvas.repaint();
    }

    public void releaseMouse() {
        draggedWindow = null;
        topBorderLocked = false;
        leftBorderLocked = false;
        bottomBorderLocked = false;
        rightBorderLocked = false;
    }

    List<MyWindow> getWindows() {
        return windowList;
    }
}

MyWindow.java: package net.coderodde.gui.simulation;

package net.coderodde.gui.simulation;

import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;

public class MyWindow {

    public static enum Border {
        TOP,
        RIGHT,
        BOTTOM,
        LEFT
    }

    public final static int MINIMUM_WIDTH = 50;
    public final static int MINIMUM_HEIGHT = 50;

    private final static int TITLE_BAR_HEIGHT = 30;
    private final static Color ACTIVE_TITLE_BAR_BACKGROUND = new Color(255, 150, 100);
    private final static Color TITLE_BAR_TEXT_COLOR = Color.WHITE;
    private final static Color PASSIVE_TITLE_BAR_BACKGROUND = new Color(200, 200, 200);
    private final static Color BORDER_HIGHLIGHT_COLOR = Color.GREEN;
    private final static Color BODY_COLOR = new Color(50, 50, 50);
    private final static Font TITLE_FONT = new Font("Monospaced", Font.BOLD, 12);
    private final static int TITLE_PADDING = 14;

    private String title;
    private int width;
    private int height;
    private int x;
    private int y;
    private boolean active;
    private boolean topBorderHightlighted;
    private boolean rightBorderHighlighted;
    private boolean bottomBorderHightlighted;
    private boolean leftBorderHighlighted;
    private boolean debug;

    public MyWindow(final String title, 
                    final int width, 
                    final int height,
                    final int x,
                    final int y) {
        this.title = title;
        this.width = Math.max(width, MINIMUM_WIDTH);
        this.height = Math.max(height, MINIMUM_HEIGHT) + TITLE_BAR_HEIGHT;
        this.x = x;
        this.y = y;
        this.active = false;
    }

    public void setDebug(final boolean debug) {
        this.debug = debug;
    }

    public boolean isActive() {
        return active;
    }

    public void setActive(final boolean active) {
        this.active = active;
    }

    public void clearBorderHighlights() {
        topBorderHightlighted = false;
        rightBorderHighlighted = false;
        bottomBorderHightlighted = false;
        leftBorderHighlighted = false;
    }

    public void highlightBorder(Border border) {
        switch (border) {
            case TOP:
                topBorderHightlighted = true;
                return;

            case RIGHT:
                rightBorderHighlighted = true;
                return;

            case BOTTOM:
                bottomBorderHightlighted = true;
                return;

            case LEFT:
                leftBorderHighlighted = true;
                return;
        }
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(final String title) {
        this.title = title;
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(final int width) {
        this.width = Math.max(width, MINIMUM_WIDTH);
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(final int height) {
        if (debug) {
            System.out.println("Height changed from " + getHeight() + " to " +
                               height);
        }

        this.height = Math.max(height, MINIMUM_HEIGHT);
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public void setX(final int x) {
        this.x = x;
    }

    public void setY(final int y) {
        this.y = y;
    }

    public boolean titleBarContainsPoint(final int x, final int y) {
        return getX() <= x 
                && x < getX() + width
                && getY() <= y
                && y < getY() + TITLE_BAR_HEIGHT;
    }

    public boolean windowContainsPoint(final int x, final int y) {
        return getX() <= x 
                && x < getX() + width 
                && getY() <= y
                && y < getY() + height;
    }

    public boolean topBorderContainsPoint(final int x, final int y) {
        return getX() <= x 
                && x < getX() + width 
                && (y == getY() || y == getY() + 1);
    }

    public boolean rightBorderContainsPoint(final int x, final int y) {
        return (x == getX() + width - 1 || x == getX() + width - 2) 
                && getY() <= y 
                && y < getY() + height;
    }

    public boolean bottomBorderContainsPoint(final int xx, final int yy) {
        return getX() <= xx 
                && xx < getX() + width 
                && (yy == getY() + height - 1 || yy == getY() + height - 2);
    }

    public boolean leftBorderContainsPoint(final int x, final int y) {
        return (x == getX() || x == getX() + 1) 
                && getY() <= y 
                && y < getY() + height;
    }

    public void draw(final Graphics g) {
        final Color titleBarColor = isActive() ?
                ACTIVE_TITLE_BAR_BACKGROUND :
                PASSIVE_TITLE_BAR_BACKGROUND;

        // Draw title bar.
        g.setColor(titleBarColor);
        g.fillRect(x, y, width, TITLE_BAR_HEIGHT);
        g.setColor(TITLE_BAR_TEXT_COLOR);
        g.setFont(TITLE_FONT);
        g.drawString(getContractedTitle(g.getFontMetrics(TITLE_FONT)), 
                                        x + TITLE_PADDING,
                                        y + 18);

        // Fill body rectangle.
        g.setColor(BODY_COLOR);
        g.fillRect(x, y + TITLE_BAR_HEIGHT, width, height - TITLE_BAR_HEIGHT);

        // Draw left, right and bottom colors.
        g.setColor(titleBarColor);
            // Draw left border.
        g.drawLine(x, y, x, y + height - 1);
            // Draw right border.
        g.drawLine(x + width - 1, y, x + width - 1, y + height - 1);
            // Draw bottom border
        g.drawLine(x, y + height - 1, x + width - 1, y + height - 1);

        // Try draw border highlights.
        g.setColor(BORDER_HIGHLIGHT_COLOR);

            // Top border.
        if (topBorderHightlighted) {
            g.fillRect(x, y, width, 2);
        }
            // Left border.
        if (leftBorderHighlighted) {
            g.fillRect(x, y, 2, height);
        }
            // Right border.
        if (rightBorderHighlighted) {
            g.fillRect(x + width - 2, y, 2, height);
        }
            // Bottom border.
        if (bottomBorderHightlighted) {
            g.fillRect(x, y + height - 2, width, 2);
        }
    }

    public void resizeAtTop(final int delta) {
        final int oldHeight = height;
        height = Math.max(MINIMUM_HEIGHT, height - delta);
        y += (oldHeight - height);
    }

    public void resizeAtLeft(final int delta) {
        final int oldWidth = width;
        width = Math.max(MINIMUM_WIDTH, width - delta);
        x += (oldWidth - width);
    }

    private String getContractedTitle(final FontMetrics fm) {
        final int allowedWidth = getWidth() - 2 * TITLE_PADDING;

        if (fm.stringWidth(title) <= allowedWidth) {
            // Once here, the window title fits entirely in the title bar.
            return title;
        }

        // Once here, we need to contract the string from right.
        for (int i = title.length() - 1; i > 0; --i) {
            final String contractedTitle = title.substring(0, i);
            final String titleWithDots = contractedTitle + "...";
            if (fm.stringWidth(titleWithDots) <= allowedWidth) {
                return titleWithDots;
            }
        }

        if (fm.stringWidth("...") <= allowedWidth) {
            return "...";
        }

        if (fm.stringWidth("..") <= allowedWidth) {
            return "..";
        }

        return "";
    }
}

MyCanvas.java: package net.coderodde.gui.simulation;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.List;

class MyCanvas extends Canvas {

    private final int width;
    private final int height;
    private final List<MyWindow> windowList;

    MyCanvas(final int width, 
             final int height, 
             final List<MyWindow> wndList,
             final MyScreen screen) {
        this.width = width;
        this.height = height;
        setSize(width, height);
        this.windowList = wndList;
        final MyMouseListener mml = new MyMouseListener(screen);
        addMouseListener(mml);
        addMouseMotionListener(mml);
    }

    @Override
    public void paint(final Graphics g) {
        g.setColor(Color.BLACK);
        g.fillRect(0, 0, width, height);

        for (final MyWindow wnd : windowList) {
            wnd.draw(g);
        }
    }

    @Override
    public void update(final Graphics g) {
        paint(g);
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(width, height);
    }

    static class MyMouseListener implements MouseListener,
                                            MouseMotionListener {

        private final MyScreen screen;

        MyMouseListener(final MyScreen screen) {
            this.screen = screen;
        }

        @Override
        public void mouseClicked(MouseEvent e) {

        }

        @Override
        public void mousePressed(MouseEvent e) {
            screen.press(e.getX(), e.getY());
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            screen.releaseMouse();
        }

        @Override
        public void mouseEntered(MouseEvent e) {
            screen.allowDrag();
        }

        @Override
        public void mouseExited(MouseEvent e) {
            screen.disallowDrag();
        }    

        @Override
        public void mouseDragged(MouseEvent e) {
            screen.drag(e.getX(), e.getY());
        }

        @Override
        public void mouseMoved(MouseEvent e) {
           screen.move(e.getX(), e.getY());
        }
    }
}

MyScreen: package net.coderodde.gui.simulation;

import java.awt.Canvas;
import java.awt.Dimension;
import java.util.List;
import java.awt.Toolkit;
import java.util.ArrayList;
import javax.swing.JFrame;

public class MyScreen {

    private final static int FRAME_WIDTH = 1024;
    private final static int FRAME_HEIGHT = 768;

    private final JFrame frame;
    private final Canvas canvas;
    private final List<MyWindow> windowList;
    private MyWindow activeWindow;
    private MyWindow draggedWindow;
    private int draggedWindowOffsetX;
    private int draggedWindowOffsetY;
    private boolean topBorderLocked;
    private boolean rightBorderLocked;
    private boolean bottomBorderLocked;
    private boolean leftBorderLocked;
    private MyWindow highlightedWindow;
    private boolean disallowDrag;

    public MyScreen() {
        windowList = new ArrayList<>();
        frame = new JFrame("coderodde GUI simulation");
        frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
        frame.setResizable(false);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        final Dimension realScreenDimension = 
                Toolkit.getDefaultToolkit().getScreenSize();

        frame.setLocation((realScreenDimension.width - frame.getWidth()) / 2, 
                          (realScreenDimension.height - frame.getHeight()) / 2);

        canvas = new MyCanvas(frame.getWidth(), 
                              frame.getHeight(), 
                              windowList, 
                              this);

        frame.add(canvas);
        frame.setVisible(true);
    }

    public void addWindow(final MyWindow window) {
        windowList.add(window);

        if (activeWindow != null) {
            activeWindow.setActive(false);
        }

        window.setActive(true);
        activeWindow = window;
    }

    public void move(final int x, final int y) {
        MyWindow target = null;

        for (int i = windowList.size() - 1; i >= 0; --i) {
            if (windowList.get(i).windowContainsPoint(x, y)) {
                target = windowList.get(i);
                break;
            }
        }

        if (target == null) {
            if (highlightedWindow != null) {
                highlightedWindow.clearBorderHighlights();
                highlightedWindow = null;
                canvas.repaint();
            }

            return;
        }

        if (highlightedWindow != null) {
            highlightedWindow.clearBorderHighlights();
            highlightedWindow = null;
        }

        if (target.topBorderContainsPoint(x, y)) {
            target.highlightBorder(MyWindow.Border.TOP);
            highlightedWindow = target;
        } else if (target.bottomBorderContainsPoint(x, y)) {
            target.highlightBorder(MyWindow.Border.BOTTOM);
            highlightedWindow = target;
        }

        if (target.rightBorderContainsPoint(x, y)) {
            target.highlightBorder(MyWindow.Border.RIGHT);
            highlightedWindow = target;
        } else if (target.leftBorderContainsPoint(x, y)) {
            target.highlightBorder(MyWindow.Border.LEFT);
            highlightedWindow = target;
        }

        canvas.repaint();
    }

    public void press(final int x, final int y) {
        MyWindow newActiveWindow = null;

        for (int i = windowList.size() - 1; i >= 0; --i) {
            if (windowList.get(i).windowContainsPoint(x, y)) {
                newActiveWindow = windowList.remove(i);
                break;
            }
        }

        if (newActiveWindow == null) {
            return;
        }

        activeWindow.setActive(false);
        activeWindow = newActiveWindow;
        windowList.add(activeWindow);
        activeWindow.setActive(true);

        boolean doResize = false;

        if (activeWindow.topBorderContainsPoint(x, y)) {
            topBorderLocked = true;
            doResize = true;
        } else if (activeWindow.bottomBorderContainsPoint(x, y)) {
            bottomBorderLocked = true;
            doResize = true;
        }

        if (activeWindow.leftBorderContainsPoint(x, y)) {
            leftBorderLocked = true;
            doResize = true;
        } else if (activeWindow.rightBorderContainsPoint(x, y)) {
            rightBorderLocked = true;
            doResize = true;
        }

        if (!doResize && activeWindow.titleBarContainsPoint(x, y)) {
            draggedWindow = activeWindow;
            draggedWindowOffsetX = x - draggedWindow.getX();
            draggedWindowOffsetY = y - draggedWindow.getY();
        } 

        if (doResize) {
            draggedWindow = activeWindow;
        }

        canvas.repaint();
    }

    public void allowDrag() {
        disallowDrag = false;
    }

    public void disallowDrag() {
        disallowDrag = true;
    }

    public void drag(final int x, final int y) {
        if (draggedWindow == null) {
            return;
        }

        if (disallowDrag) {
            return;
        }

        if (!(topBorderLocked 
                || rightBorderLocked 
                || bottomBorderLocked
                || leftBorderLocked)) {
            // Move the entire window.
            draggedWindow.setX(x - draggedWindowOffsetX);
            draggedWindow.setY(y - draggedWindowOffsetY);
            canvas.repaint();
        }

        // Resize the window.
        final int originalX = draggedWindow.getX();
        final int originalY = draggedWindow.getY();

        if (topBorderLocked) {
            draggedWindow.resizeAtTop(y - originalY);
        } else if (bottomBorderLocked) {   
            draggedWindow.setHeight(y - originalY);
        }

        if (leftBorderLocked) {
            draggedWindow.resizeAtLeft(x - originalX);
        } else if (rightBorderLocked) {
            draggedWindow.setWidth(x - originalX);
        }

        canvas.repaint();
    }

    public void releaseMouse() {
        draggedWindow = null;
        topBorderLocked = false;
        leftBorderLocked = false;
        bottomBorderLocked = false;
        rightBorderLocked = false;
    }

    List<MyWindow> getWindows() {
        return windowList;
    }
}

MyWindow.java: package net.coderodde.gui.simulation;

import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;

public class MyWindow {

    public static enum Border {
        TOP,
        RIGHT,
        BOTTOM,
        LEFT
    }

    public final static int MINIMUM_WIDTH = 50;
    public final static int MINIMUM_HEIGHT = 50;

    private final static int TITLE_BAR_HEIGHT = 30;
    private final static Color ACTIVE_TITLE_BAR_BACKGROUND = new Color(255, 150, 100);
    private final static Color TITLE_BAR_TEXT_COLOR = Color.WHITE;
    private final static Color PASSIVE_TITLE_BAR_BACKGROUND = new Color(200, 200, 200);
    private final static Color BORDER_HIGHLIGHT_COLOR = Color.GREEN;
    private final static Color BODY_COLOR = new Color(50, 50, 50);
    private final static Font TITLE_FONT = new Font("Monospaced", Font.BOLD, 12);
    private final static int TITLE_PADDING = 14;

    private String title;
    private int width;
    private int height;
    private int x;
    private int y;
    private boolean active;
    private boolean topBorderHightlighted;
    private boolean rightBorderHighlighted;
    private boolean bottomBorderHightlighted;
    private boolean leftBorderHighlighted;
    private boolean debug;

    public MyWindow(final String title, 
                    final int width, 
                    final int height,
                    final int x,
                    final int y) {
        this.title = title;
        this.width = Math.max(width, MINIMUM_WIDTH);
        this.height = Math.max(height, MINIMUM_HEIGHT) + TITLE_BAR_HEIGHT;
        this.x = x;
        this.y = y;
        this.active = false;
    }

    public void setDebug(final boolean debug) {
        this.debug = debug;
    }

    public boolean isActive() {
        return active;
    }

    public void setActive(final boolean active) {
        this.active = active;
    }

    public void clearBorderHighlights() {
        topBorderHightlighted = false;
        rightBorderHighlighted = false;
        bottomBorderHightlighted = false;
        leftBorderHighlighted = false;
    }

    public void highlightBorder(Border border) {
        switch (border) {
            case TOP:
                topBorderHightlighted = true;
                return;

            case RIGHT:
                rightBorderHighlighted = true;
                return;

            case BOTTOM:
                bottomBorderHightlighted = true;
                return;

            case LEFT:
                leftBorderHighlighted = true;
                return;
        }
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(final String title) {
        this.title = title;
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(final int width) {
        this.width = Math.max(width, MINIMUM_WIDTH);
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(final int height) {
        if (debug) {
            System.out.println("Height changed from " + getHeight() + " to " +
                               height);
        }

        this.height = Math.max(height, MINIMUM_HEIGHT);
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public void setX(final int x) {
        this.x = x;
    }

    public void setY(final int y) {
        this.y = y;
    }

    public boolean titleBarContainsPoint(final int x, final int y) {
        return getX() <= x 
                && x < getX() + width
                && getY() <= y
                && y < getY() + TITLE_BAR_HEIGHT;
    }

    public boolean windowContainsPoint(final int x, final int y) {
        return getX() <= x 
                && x < getX() + width 
                && getY() <= y
                && y < getY() + height;
    }

    public boolean topBorderContainsPoint(final int x, final int y) {
        return getX() <= x 
                && x < getX() + width 
                && (y == getY() || y == getY() + 1);
    }

    public boolean rightBorderContainsPoint(final int x, final int y) {
        return (x == getX() + width - 1 || x == getX() + width - 2) 
                && getY() <= y 
                && y < getY() + height;
    }

    public boolean bottomBorderContainsPoint(final int xx, final int yy) {
        return getX() <= xx 
                && xx < getX() + width 
                && (yy == getY() + height - 1 || yy == getY() + height - 2);
    }

    public boolean leftBorderContainsPoint(final int x, final int y) {
        return (x == getX() || x == getX() + 1) 
                && getY() <= y 
                && y < getY() + height;
    }

    public void draw(final Graphics g) {
        final Color titleBarColor = isActive() ?
                ACTIVE_TITLE_BAR_BACKGROUND :
                PASSIVE_TITLE_BAR_BACKGROUND;

        // Draw title bar.
        g.setColor(titleBarColor);
        g.fillRect(x, y, width, TITLE_BAR_HEIGHT);
        g.setColor(TITLE_BAR_TEXT_COLOR);
        g.setFont(TITLE_FONT);
        g.drawString(getContractedTitle(g.getFontMetrics(TITLE_FONT)), 
                                        x + TITLE_PADDING,
                                        y + 18);

        // Fill body rectangle.
        g.setColor(BODY_COLOR);
        g.fillRect(x, y + TITLE_BAR_HEIGHT, width, height - TITLE_BAR_HEIGHT);

        // Draw left, right and bottom colors.
        g.setColor(titleBarColor);
            // Draw left border.
        g.drawLine(x, y, x, y + height - 1);
            // Draw right border.
        g.drawLine(x + width - 1, y, x + width - 1, y + height - 1);
            // Draw bottom border
        g.drawLine(x, y + height - 1, x + width - 1, y + height - 1);

        // Try draw border highlights.
        g.setColor(BORDER_HIGHLIGHT_COLOR);

            // Top border.
        if (topBorderHightlighted) {
            g.fillRect(x, y, width, 2);
        }
            // Left border.
        if (leftBorderHighlighted) {
            g.fillRect(x, y, 2, height);
        }
            // Right border.
        if (rightBorderHighlighted) {
            g.fillRect(x + width - 2, y, 2, height);
        }
            // Bottom border.
        if (bottomBorderHightlighted) {
            g.fillRect(x, y + height - 2, width, 2);
        }
    }

    public void resizeAtTop(final int delta) {
        final int oldHeight = height;
        height = Math.max(MINIMUM_HEIGHT, height - delta);
        y += (oldHeight - height);
    }

    public void resizeAtLeft(final int delta) {
        final int oldWidth = width;
        width = Math.max(MINIMUM_WIDTH, width - delta);
        x += (oldWidth - width);
    }

    private String getContractedTitle(final FontMetrics fm) {
        final int allowedWidth = getWidth() - 2 * TITLE_PADDING;

        if (fm.stringWidth(title) <= allowedWidth) {
            // Once here, the window title fits entirely in the title bar.
            return title;
        }

        // Once here, we need to contract the string from right.
        for (int i = title.length() - 1; i > 0; --i) {
            final String contractedTitle = title.substring(0, i);
            final String titleWithDots = contractedTitle + "...";
            if (fm.stringWidth(titleWithDots) <= allowedWidth) {
                return titleWithDots;
            }
        }

        if (fm.stringWidth("...") <= allowedWidth) {
            return "...";
        }

        if (fm.stringWidth("..") <= allowedWidth) {
            return "..";
        }

        return "";
    }
}

MyCanvas.java:

package net.coderodde.gui.simulation;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.List;

class MyCanvas extends Canvas {

    private final int width;
    private final int height;
    private final List<MyWindow> windowList;

    MyCanvas(final int width, 
             final int height, 
             final List<MyWindow> wndList,
             final MyScreen screen) {
        this.width = width;
        this.height = height;
        setSize(width, height);
        this.windowList = wndList;
        final MyMouseListener mml = new MyMouseListener(screen);
        addMouseListener(mml);
        addMouseMotionListener(mml);
    }

    @Override
    public void paint(final Graphics g) {
        g.setColor(Color.BLACK);
        g.fillRect(0, 0, width, height);

        for (final MyWindow wnd : windowList) {
            wnd.draw(g);
        }
    }

    @Override
    public void update(final Graphics g) {
        paint(g);
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(width, height);
    }

    static class MyMouseListener implements MouseListener,
                                            MouseMotionListener {

        private final MyScreen screen;

        MyMouseListener(final MyScreen screen) {
            this.screen = screen;
        }

        @Override
        public void mouseClicked(MouseEvent e) {

        }

        @Override
        public void mousePressed(MouseEvent e) {
            screen.press(e.getX(), e.getY());
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            screen.releaseMouse();
        }

        @Override
        public void mouseEntered(MouseEvent e) {
            screen.allowDrag();
        }

        @Override
        public void mouseExited(MouseEvent e) {
            screen.disallowDrag();
        }    

        @Override
        public void mouseDragged(MouseEvent e) {
            screen.drag(e.getX(), e.getY());
        }

        @Override
        public void mouseMoved(MouseEvent e) {
           screen.move(e.getX(), e.getY());
        }
    }
}

MyScreen:

package net.coderodde.gui.simulation;

import java.awt.Canvas;
import java.awt.Dimension;
import java.util.List;
import java.awt.Toolkit;
import java.util.ArrayList;
import javax.swing.JFrame;

public class MyScreen {

    private final static int FRAME_WIDTH = 1024;
    private final static int FRAME_HEIGHT = 768;

    private final JFrame frame;
    private final Canvas canvas;
    private final List<MyWindow> windowList;
    private MyWindow activeWindow;
    private MyWindow draggedWindow;
    private int draggedWindowOffsetX;
    private int draggedWindowOffsetY;
    private boolean topBorderLocked;
    private boolean rightBorderLocked;
    private boolean bottomBorderLocked;
    private boolean leftBorderLocked;
    private MyWindow highlightedWindow;
    private boolean disallowDrag;

    public MyScreen() {
        windowList = new ArrayList<>();
        frame = new JFrame("coderodde GUI simulation");
        frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
        frame.setResizable(false);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        final Dimension realScreenDimension = 
                Toolkit.getDefaultToolkit().getScreenSize();

        frame.setLocation((realScreenDimension.width - frame.getWidth()) / 2, 
                          (realScreenDimension.height - frame.getHeight()) / 2);

        canvas = new MyCanvas(frame.getWidth(), 
                              frame.getHeight(), 
                              windowList, 
                              this);

        frame.add(canvas);
        frame.setVisible(true);
    }

    public void addWindow(final MyWindow window) {
        windowList.add(window);

        if (activeWindow != null) {
            activeWindow.setActive(false);
        }

        window.setActive(true);
        activeWindow = window;
    }

    public void move(final int x, final int y) {
        MyWindow target = null;

        for (int i = windowList.size() - 1; i >= 0; --i) {
            if (windowList.get(i).windowContainsPoint(x, y)) {
                target = windowList.get(i);
                break;
            }
        }

        if (target == null) {
            if (highlightedWindow != null) {
                highlightedWindow.clearBorderHighlights();
                highlightedWindow = null;
                canvas.repaint();
            }

            return;
        }

        if (highlightedWindow != null) {
            highlightedWindow.clearBorderHighlights();
            highlightedWindow = null;
        }

        if (target.topBorderContainsPoint(x, y)) {
            target.highlightBorder(MyWindow.Border.TOP);
            highlightedWindow = target;
        } else if (target.bottomBorderContainsPoint(x, y)) {
            target.highlightBorder(MyWindow.Border.BOTTOM);
            highlightedWindow = target;
        }

        if (target.rightBorderContainsPoint(x, y)) {
            target.highlightBorder(MyWindow.Border.RIGHT);
            highlightedWindow = target;
        } else if (target.leftBorderContainsPoint(x, y)) {
            target.highlightBorder(MyWindow.Border.LEFT);
            highlightedWindow = target;
        }

        canvas.repaint();
    }

    public void press(final int x, final int y) {
        MyWindow newActiveWindow = null;

        for (int i = windowList.size() - 1; i >= 0; --i) {
            if (windowList.get(i).windowContainsPoint(x, y)) {
                newActiveWindow = windowList.remove(i);
                break;
            }
        }

        if (newActiveWindow == null) {
            return;
        }

        activeWindow.setActive(false);
        activeWindow = newActiveWindow;
        windowList.add(activeWindow);
        activeWindow.setActive(true);

        boolean doResize = false;

        if (activeWindow.topBorderContainsPoint(x, y)) {
            topBorderLocked = true;
            doResize = true;
        } else if (activeWindow.bottomBorderContainsPoint(x, y)) {
            bottomBorderLocked = true;
            doResize = true;
        }

        if (activeWindow.leftBorderContainsPoint(x, y)) {
            leftBorderLocked = true;
            doResize = true;
        } else if (activeWindow.rightBorderContainsPoint(x, y)) {
            rightBorderLocked = true;
            doResize = true;
        }

        if (!doResize && activeWindow.titleBarContainsPoint(x, y)) {
            draggedWindow = activeWindow;
            draggedWindowOffsetX = x - draggedWindow.getX();
            draggedWindowOffsetY = y - draggedWindow.getY();
        } 

        if (doResize) {
            draggedWindow = activeWindow;
        }

        canvas.repaint();
    }

    public void allowDrag() {
        disallowDrag = false;
    }

    public void disallowDrag() {
        disallowDrag = true;
    }

    public void drag(final int x, final int y) {
        if (draggedWindow == null) {
            return;
        }

        if (disallowDrag) {
            return;
        }

        if (!(topBorderLocked 
                || rightBorderLocked 
                || bottomBorderLocked
                || leftBorderLocked)) {
            // Move the entire window.
            draggedWindow.setX(x - draggedWindowOffsetX);
            draggedWindow.setY(y - draggedWindowOffsetY);
            canvas.repaint();
        }

        // Resize the window.
        final int originalX = draggedWindow.getX();
        final int originalY = draggedWindow.getY();

        if (topBorderLocked) {
            draggedWindow.resizeAtTop(y - originalY);
        } else if (bottomBorderLocked) {   
            draggedWindow.setHeight(y - originalY);
        }

        if (leftBorderLocked) {
            draggedWindow.resizeAtLeft(x - originalX);
        } else if (rightBorderLocked) {
            draggedWindow.setWidth(x - originalX);
        }

        canvas.repaint();
    }

    public void releaseMouse() {
        draggedWindow = null;
        topBorderLocked = false;
        leftBorderLocked = false;
        bottomBorderLocked = false;
        rightBorderLocked = false;
    }

    List<MyWindow> getWindows() {
        return windowList;
    }
}

MyWindow.java:

package net.coderodde.gui.simulation;

import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;

public class MyWindow {

    public static enum Border {
        TOP,
        RIGHT,
        BOTTOM,
        LEFT
    }

    public final static int MINIMUM_WIDTH = 50;
    public final static int MINIMUM_HEIGHT = 50;

    private final static int TITLE_BAR_HEIGHT = 30;
    private final static Color ACTIVE_TITLE_BAR_BACKGROUND = new Color(255, 150, 100);
    private final static Color TITLE_BAR_TEXT_COLOR = Color.WHITE;
    private final static Color PASSIVE_TITLE_BAR_BACKGROUND = new Color(200, 200, 200);
    private final static Color BORDER_HIGHLIGHT_COLOR = Color.GREEN;
    private final static Color BODY_COLOR = new Color(50, 50, 50);
    private final static Font TITLE_FONT = new Font("Monospaced", Font.BOLD, 12);
    private final static int TITLE_PADDING = 14;

    private String title;
    private int width;
    private int height;
    private int x;
    private int y;
    private boolean active;
    private boolean topBorderHightlighted;
    private boolean rightBorderHighlighted;
    private boolean bottomBorderHightlighted;
    private boolean leftBorderHighlighted;
    private boolean debug;

    public MyWindow(final String title, 
                    final int width, 
                    final int height,
                    final int x,
                    final int y) {
        this.title = title;
        this.width = Math.max(width, MINIMUM_WIDTH);
        this.height = Math.max(height, MINIMUM_HEIGHT) + TITLE_BAR_HEIGHT;
        this.x = x;
        this.y = y;
        this.active = false;
    }

    public void setDebug(final boolean debug) {
        this.debug = debug;
    }

    public boolean isActive() {
        return active;
    }

    public void setActive(final boolean active) {
        this.active = active;
    }

    public void clearBorderHighlights() {
        topBorderHightlighted = false;
        rightBorderHighlighted = false;
        bottomBorderHightlighted = false;
        leftBorderHighlighted = false;
    }

    public void highlightBorder(Border border) {
        switch (border) {
            case TOP:
                topBorderHightlighted = true;
                return;

            case RIGHT:
                rightBorderHighlighted = true;
                return;

            case BOTTOM:
                bottomBorderHightlighted = true;
                return;

            case LEFT:
                leftBorderHighlighted = true;
                return;
        }
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(final String title) {
        this.title = title;
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(final int width) {
        this.width = Math.max(width, MINIMUM_WIDTH);
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(final int height) {
        if (debug) {
            System.out.println("Height changed from " + getHeight() + " to " +
                               height);
        }

        this.height = Math.max(height, MINIMUM_HEIGHT);
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public void setX(final int x) {
        this.x = x;
    }

    public void setY(final int y) {
        this.y = y;
    }

    public boolean titleBarContainsPoint(final int x, final int y) {
        return getX() <= x 
                && x < getX() + width
                && getY() <= y
                && y < getY() + TITLE_BAR_HEIGHT;
    }

    public boolean windowContainsPoint(final int x, final int y) {
        return getX() <= x 
                && x < getX() + width 
                && getY() <= y
                && y < getY() + height;
    }

    public boolean topBorderContainsPoint(final int x, final int y) {
        return getX() <= x 
                && x < getX() + width 
                && (y == getY() || y == getY() + 1);
    }

    public boolean rightBorderContainsPoint(final int x, final int y) {
        return (x == getX() + width - 1 || x == getX() + width - 2) 
                && getY() <= y 
                && y < getY() + height;
    }

    public boolean bottomBorderContainsPoint(final int xx, final int yy) {
        return getX() <= xx 
                && xx < getX() + width 
                && (yy == getY() + height - 1 || yy == getY() + height - 2);
    }

    public boolean leftBorderContainsPoint(final int x, final int y) {
        return (x == getX() || x == getX() + 1) 
                && getY() <= y 
                && y < getY() + height;
    }

    public void draw(final Graphics g) {
        final Color titleBarColor = isActive() ?
                ACTIVE_TITLE_BAR_BACKGROUND :
                PASSIVE_TITLE_BAR_BACKGROUND;

        // Draw title bar.
        g.setColor(titleBarColor);
        g.fillRect(x, y, width, TITLE_BAR_HEIGHT);
        g.setColor(TITLE_BAR_TEXT_COLOR);
        g.setFont(TITLE_FONT);
        g.drawString(getContractedTitle(g.getFontMetrics(TITLE_FONT)), 
                                        x + TITLE_PADDING,
                                        y + 18);

        // Fill body rectangle.
        g.setColor(BODY_COLOR);
        g.fillRect(x, y + TITLE_BAR_HEIGHT, width, height - TITLE_BAR_HEIGHT);

        // Draw left, right and bottom colors.
        g.setColor(titleBarColor);
            // Draw left border.
        g.drawLine(x, y, x, y + height - 1);
            // Draw right border.
        g.drawLine(x + width - 1, y, x + width - 1, y + height - 1);
            // Draw bottom border
        g.drawLine(x, y + height - 1, x + width - 1, y + height - 1);

        // Try draw border highlights.
        g.setColor(BORDER_HIGHLIGHT_COLOR);

            // Top border.
        if (topBorderHightlighted) {
            g.fillRect(x, y, width, 2);
        }
            // Left border.
        if (leftBorderHighlighted) {
            g.fillRect(x, y, 2, height);
        }
            // Right border.
        if (rightBorderHighlighted) {
            g.fillRect(x + width - 2, y, 2, height);
        }
            // Bottom border.
        if (bottomBorderHightlighted) {
            g.fillRect(x, y + height - 2, width, 2);
        }
    }

    public void resizeAtTop(final int delta) {
        final int oldHeight = height;
        height = Math.max(MINIMUM_HEIGHT, height - delta);
        y += (oldHeight - height);
    }

    public void resizeAtLeft(final int delta) {
        final int oldWidth = width;
        width = Math.max(MINIMUM_WIDTH, width - delta);
        x += (oldWidth - width);
    }

    private String getContractedTitle(final FontMetrics fm) {
        final int allowedWidth = getWidth() - 2 * TITLE_PADDING;

        if (fm.stringWidth(title) <= allowedWidth) {
            // Once here, the window title fits entirely in the title bar.
            return title;
        }

        // Once here, we need to contract the string from right.
        for (int i = title.length() - 1; i > 0; --i) {
            final String contractedTitle = title.substring(0, i);
            final String titleWithDots = contractedTitle + "...";
            if (fm.stringWidth(titleWithDots) <= allowedWidth) {
                return titleWithDots;
            }
        }

        if (fm.stringWidth("...") <= allowedWidth) {
            return "...";
        }

        if (fm.stringWidth("..") <= allowedWidth) {
            return "..";
        }

        return "";
    }
}
Source Link
coderodde
  • 29.8k
  • 14
  • 77
  • 194

Simulating a GUI windowing system - follow-up

The previous and initial iteration at Simulating a GUI windowing system

I have essentially cleaned the code based on the only answer.

Now I have:

MyCanvas.java: package net.coderodde.gui.simulation;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.List;

class MyCanvas extends Canvas {

    private final int width;
    private final int height;
    private final List<MyWindow> windowList;

    MyCanvas(final int width, 
             final int height, 
             final List<MyWindow> wndList,
             final MyScreen screen) {
        this.width = width;
        this.height = height;
        setSize(width, height);
        this.windowList = wndList;
        final MyMouseListener mml = new MyMouseListener(screen);
        addMouseListener(mml);
        addMouseMotionListener(mml);
    }

    @Override
    public void paint(final Graphics g) {
        g.setColor(Color.BLACK);
        g.fillRect(0, 0, width, height);

        for (final MyWindow wnd : windowList) {
            wnd.draw(g);
        }
    }

    @Override
    public void update(final Graphics g) {
        paint(g);
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(width, height);
    }

    static class MyMouseListener implements MouseListener,
                                            MouseMotionListener {

        private final MyScreen screen;

        MyMouseListener(final MyScreen screen) {
            this.screen = screen;
        }

        @Override
        public void mouseClicked(MouseEvent e) {

        }

        @Override
        public void mousePressed(MouseEvent e) {
            screen.press(e.getX(), e.getY());
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            screen.releaseMouse();
        }

        @Override
        public void mouseEntered(MouseEvent e) {
            screen.allowDrag();
        }

        @Override
        public void mouseExited(MouseEvent e) {
            screen.disallowDrag();
        }    

        @Override
        public void mouseDragged(MouseEvent e) {
            screen.drag(e.getX(), e.getY());
        }

        @Override
        public void mouseMoved(MouseEvent e) {
           screen.move(e.getX(), e.getY());
        }
    }
}

MyScreen: package net.coderodde.gui.simulation;

import java.awt.Canvas;
import java.awt.Dimension;
import java.util.List;
import java.awt.Toolkit;
import java.util.ArrayList;
import javax.swing.JFrame;

public class MyScreen {

    private final static int FRAME_WIDTH = 1024;
    private final static int FRAME_HEIGHT = 768;

    private final JFrame frame;
    private final Canvas canvas;
    private final List<MyWindow> windowList;
    private MyWindow activeWindow;
    private MyWindow draggedWindow;
    private int draggedWindowOffsetX;
    private int draggedWindowOffsetY;
    private boolean topBorderLocked;
    private boolean rightBorderLocked;
    private boolean bottomBorderLocked;
    private boolean leftBorderLocked;
    private MyWindow highlightedWindow;
    private boolean disallowDrag;

    public MyScreen() {
        windowList = new ArrayList<>();
        frame = new JFrame("coderodde GUI simulation");
        frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
        frame.setResizable(false);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        final Dimension realScreenDimension = 
                Toolkit.getDefaultToolkit().getScreenSize();

        frame.setLocation((realScreenDimension.width - frame.getWidth()) / 2, 
                          (realScreenDimension.height - frame.getHeight()) / 2);

        canvas = new MyCanvas(frame.getWidth(), 
                              frame.getHeight(), 
                              windowList, 
                              this);

        frame.add(canvas);
        frame.setVisible(true);
    }

    public void addWindow(final MyWindow window) {
        windowList.add(window);

        if (activeWindow != null) {
            activeWindow.setActive(false);
        }

        window.setActive(true);
        activeWindow = window;
    }

    public void move(final int x, final int y) {
        MyWindow target = null;

        for (int i = windowList.size() - 1; i >= 0; --i) {
            if (windowList.get(i).windowContainsPoint(x, y)) {
                target = windowList.get(i);
                break;
            }
        }

        if (target == null) {
            if (highlightedWindow != null) {
                highlightedWindow.clearBorderHighlights();
                highlightedWindow = null;
                canvas.repaint();
            }

            return;
        }

        if (highlightedWindow != null) {
            highlightedWindow.clearBorderHighlights();
            highlightedWindow = null;
        }

        if (target.topBorderContainsPoint(x, y)) {
            target.highlightBorder(MyWindow.Border.TOP);
            highlightedWindow = target;
        } else if (target.bottomBorderContainsPoint(x, y)) {
            target.highlightBorder(MyWindow.Border.BOTTOM);
            highlightedWindow = target;
        }

        if (target.rightBorderContainsPoint(x, y)) {
            target.highlightBorder(MyWindow.Border.RIGHT);
            highlightedWindow = target;
        } else if (target.leftBorderContainsPoint(x, y)) {
            target.highlightBorder(MyWindow.Border.LEFT);
            highlightedWindow = target;
        }

        canvas.repaint();
    }

    public void press(final int x, final int y) {
        MyWindow newActiveWindow = null;

        for (int i = windowList.size() - 1; i >= 0; --i) {
            if (windowList.get(i).windowContainsPoint(x, y)) {
                newActiveWindow = windowList.remove(i);
                break;
            }
        }

        if (newActiveWindow == null) {
            return;
        }

        activeWindow.setActive(false);
        activeWindow = newActiveWindow;
        windowList.add(activeWindow);
        activeWindow.setActive(true);

        boolean doResize = false;

        if (activeWindow.topBorderContainsPoint(x, y)) {
            topBorderLocked = true;
            doResize = true;
        } else if (activeWindow.bottomBorderContainsPoint(x, y)) {
            bottomBorderLocked = true;
            doResize = true;
        }

        if (activeWindow.leftBorderContainsPoint(x, y)) {
            leftBorderLocked = true;
            doResize = true;
        } else if (activeWindow.rightBorderContainsPoint(x, y)) {
            rightBorderLocked = true;
            doResize = true;
        }

        if (!doResize && activeWindow.titleBarContainsPoint(x, y)) {
            draggedWindow = activeWindow;
            draggedWindowOffsetX = x - draggedWindow.getX();
            draggedWindowOffsetY = y - draggedWindow.getY();
        } 

        if (doResize) {
            draggedWindow = activeWindow;
        }

        canvas.repaint();
    }

    public void allowDrag() {
        disallowDrag = false;
    }

    public void disallowDrag() {
        disallowDrag = true;
    }

    public void drag(final int x, final int y) {
        if (draggedWindow == null) {
            return;
        }

        if (disallowDrag) {
            return;
        }

        if (!(topBorderLocked 
                || rightBorderLocked 
                || bottomBorderLocked
                || leftBorderLocked)) {
            // Move the entire window.
            draggedWindow.setX(x - draggedWindowOffsetX);
            draggedWindow.setY(y - draggedWindowOffsetY);
            canvas.repaint();
        }

        // Resize the window.
        final int originalX = draggedWindow.getX();
        final int originalY = draggedWindow.getY();

        if (topBorderLocked) {
            draggedWindow.resizeAtTop(y - originalY);
        } else if (bottomBorderLocked) {   
            draggedWindow.setHeight(y - originalY);
        }

        if (leftBorderLocked) {
            draggedWindow.resizeAtLeft(x - originalX);
        } else if (rightBorderLocked) {
            draggedWindow.setWidth(x - originalX);
        }

        canvas.repaint();
    }

    public void releaseMouse() {
        draggedWindow = null;
        topBorderLocked = false;
        leftBorderLocked = false;
        bottomBorderLocked = false;
        rightBorderLocked = false;
    }

    List<MyWindow> getWindows() {
        return windowList;
    }
}

MyWindow.java: package net.coderodde.gui.simulation;

import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;

public class MyWindow {

    public static enum Border {
        TOP,
        RIGHT,
        BOTTOM,
        LEFT
    }

    public final static int MINIMUM_WIDTH = 50;
    public final static int MINIMUM_HEIGHT = 50;

    private final static int TITLE_BAR_HEIGHT = 30;
    private final static Color ACTIVE_TITLE_BAR_BACKGROUND = new Color(255, 150, 100);
    private final static Color TITLE_BAR_TEXT_COLOR = Color.WHITE;
    private final static Color PASSIVE_TITLE_BAR_BACKGROUND = new Color(200, 200, 200);
    private final static Color BORDER_HIGHLIGHT_COLOR = Color.GREEN;
    private final static Color BODY_COLOR = new Color(50, 50, 50);
    private final static Font TITLE_FONT = new Font("Monospaced", Font.BOLD, 12);
    private final static int TITLE_PADDING = 14;

    private String title;
    private int width;
    private int height;
    private int x;
    private int y;
    private boolean active;
    private boolean topBorderHightlighted;
    private boolean rightBorderHighlighted;
    private boolean bottomBorderHightlighted;
    private boolean leftBorderHighlighted;
    private boolean debug;

    public MyWindow(final String title, 
                    final int width, 
                    final int height,
                    final int x,
                    final int y) {
        this.title = title;
        this.width = Math.max(width, MINIMUM_WIDTH);
        this.height = Math.max(height, MINIMUM_HEIGHT) + TITLE_BAR_HEIGHT;
        this.x = x;
        this.y = y;
        this.active = false;
    }

    public void setDebug(final boolean debug) {
        this.debug = debug;
    }

    public boolean isActive() {
        return active;
    }

    public void setActive(final boolean active) {
        this.active = active;
    }

    public void clearBorderHighlights() {
        topBorderHightlighted = false;
        rightBorderHighlighted = false;
        bottomBorderHightlighted = false;
        leftBorderHighlighted = false;
    }

    public void highlightBorder(Border border) {
        switch (border) {
            case TOP:
                topBorderHightlighted = true;
                return;

            case RIGHT:
                rightBorderHighlighted = true;
                return;

            case BOTTOM:
                bottomBorderHightlighted = true;
                return;

            case LEFT:
                leftBorderHighlighted = true;
                return;
        }
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(final String title) {
        this.title = title;
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(final int width) {
        this.width = Math.max(width, MINIMUM_WIDTH);
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(final int height) {
        if (debug) {
            System.out.println("Height changed from " + getHeight() + " to " +
                               height);
        }

        this.height = Math.max(height, MINIMUM_HEIGHT);
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public void setX(final int x) {
        this.x = x;
    }

    public void setY(final int y) {
        this.y = y;
    }

    public boolean titleBarContainsPoint(final int x, final int y) {
        return getX() <= x 
                && x < getX() + width
                && getY() <= y
                && y < getY() + TITLE_BAR_HEIGHT;
    }

    public boolean windowContainsPoint(final int x, final int y) {
        return getX() <= x 
                && x < getX() + width 
                && getY() <= y
                && y < getY() + height;
    }

    public boolean topBorderContainsPoint(final int x, final int y) {
        return getX() <= x 
                && x < getX() + width 
                && (y == getY() || y == getY() + 1);
    }

    public boolean rightBorderContainsPoint(final int x, final int y) {
        return (x == getX() + width - 1 || x == getX() + width - 2) 
                && getY() <= y 
                && y < getY() + height;
    }

    public boolean bottomBorderContainsPoint(final int xx, final int yy) {
        return getX() <= xx 
                && xx < getX() + width 
                && (yy == getY() + height - 1 || yy == getY() + height - 2);
    }

    public boolean leftBorderContainsPoint(final int x, final int y) {
        return (x == getX() || x == getX() + 1) 
                && getY() <= y 
                && y < getY() + height;
    }

    public void draw(final Graphics g) {
        final Color titleBarColor = isActive() ?
                ACTIVE_TITLE_BAR_BACKGROUND :
                PASSIVE_TITLE_BAR_BACKGROUND;

        // Draw title bar.
        g.setColor(titleBarColor);
        g.fillRect(x, y, width, TITLE_BAR_HEIGHT);
        g.setColor(TITLE_BAR_TEXT_COLOR);
        g.setFont(TITLE_FONT);
        g.drawString(getContractedTitle(g.getFontMetrics(TITLE_FONT)), 
                                        x + TITLE_PADDING,
                                        y + 18);

        // Fill body rectangle.
        g.setColor(BODY_COLOR);
        g.fillRect(x, y + TITLE_BAR_HEIGHT, width, height - TITLE_BAR_HEIGHT);

        // Draw left, right and bottom colors.
        g.setColor(titleBarColor);
            // Draw left border.
        g.drawLine(x, y, x, y + height - 1);
            // Draw right border.
        g.drawLine(x + width - 1, y, x + width - 1, y + height - 1);
            // Draw bottom border
        g.drawLine(x, y + height - 1, x + width - 1, y + height - 1);

        // Try draw border highlights.
        g.setColor(BORDER_HIGHLIGHT_COLOR);

            // Top border.
        if (topBorderHightlighted) {
            g.fillRect(x, y, width, 2);
        }
            // Left border.
        if (leftBorderHighlighted) {
            g.fillRect(x, y, 2, height);
        }
            // Right border.
        if (rightBorderHighlighted) {
            g.fillRect(x + width - 2, y, 2, height);
        }
            // Bottom border.
        if (bottomBorderHightlighted) {
            g.fillRect(x, y + height - 2, width, 2);
        }
    }

    public void resizeAtTop(final int delta) {
        final int oldHeight = height;
        height = Math.max(MINIMUM_HEIGHT, height - delta);
        y += (oldHeight - height);
    }

    public void resizeAtLeft(final int delta) {
        final int oldWidth = width;
        width = Math.max(MINIMUM_WIDTH, width - delta);
        x += (oldWidth - width);
    }

    private String getContractedTitle(final FontMetrics fm) {
        final int allowedWidth = getWidth() - 2 * TITLE_PADDING;

        if (fm.stringWidth(title) <= allowedWidth) {
            // Once here, the window title fits entirely in the title bar.
            return title;
        }

        // Once here, we need to contract the string from right.
        for (int i = title.length() - 1; i > 0; --i) {
            final String contractedTitle = title.substring(0, i);
            final String titleWithDots = contractedTitle + "...";
            if (fm.stringWidth(titleWithDots) <= allowedWidth) {
                return titleWithDots;
            }
        }

        if (fm.stringWidth("...") <= allowedWidth) {
            return "...";
        }

        if (fm.stringWidth("..") <= allowedWidth) {
            return "..";
        }

        return "";
    }
}

The Demo.java is not changed.

Now, is the code now kewl and funky enough?