多窗口事件分发系统

题目:多窗口事件分发系统

背景介绍

请你为一款支持多窗口的终端设备设计一个简易的图形用户界面 (GUI) 事件分发系统。这个系统需要管理屏幕上的多个窗口,处理窗口的创建、销毁、移动,并能响应用户的点击事件。

核心概念

  1. 屏幕 (Screen):

    • 一个 1000 x 1000 的网格画布。
    • 左上角坐标为 [0, 0]
  2. 窗口 (Window):

    • 每个窗口由其唯一的 id、左上角位置 [row, col]、宽度 width 和高度 height 定义。
    • 窗口可以部分或完全超出屏幕边界。
  3. 层级 (Layer / Z-order):

    • 这是本系统最重要的概念。窗口之间存在层叠关系,就像一叠扑克牌。
    • 最后 被操作(创建、移动、点击)的窗口会被置于最顶层
    • 当点击一个坐标时,只有位于最顶层的、覆盖该坐标的窗口会响应。

功能要求

你需要实现一个 MultiWindowSys 类,支持以下操作:

MultiWindowSys()
  • 功能: 初始化系统。
  • 初始状态: 屏幕上没有任何窗口。
createWindow(int id, int row, int col, int width, int height)
  • 功能: 创建一个指定 id 和尺寸的新窗口。
  • 成功条件: 窗口 id 在系统中是唯一的。
  • 状态变化: 成功创建后,新窗口会被放置在所有现有窗口的最上层
  • 返回值: 创建成功返回 true;如果 id 已存在,则失败并返回 false
destroyWindow(int id)
  • 功能: 销毁指定 id 的窗口。
  • 成功条件: 窗口 id 必须存在。
  • 状态变化: 成功销毁后,该窗口将从屏幕上移除。
  • 返回值: 销毁成功返回 true;如果窗口不存在,返回 false
moveWindow(int id, int dstRow, int dstCol)
  • 功能: 将指定 id 的窗口移动到新的位置 [dstRow, dstCol]

  • 失败条件:

    1. 窗口 id 不存在。

    2. 移动后,整个窗口完全位于屏幕外部

      • (例如,移动到 [-200, -200],此时窗口右下角仍在屏幕外)。
  • 状态变化: 移动成功后,窗口的位置被更新,并且该窗口会被提升至最上层

  • 返回值: 移动成功返回 true;失败则返回 false,窗口位置和层级保持不变。

dispatchClickEvent(int row, int col)
  • 功能: 模拟一次在屏幕坐标 [row, col] 的点击事件。

  • 命中规则:

    • 系统会从最上层 的窗口开始向下检查,找到第一个覆盖了 [row, col] 坐标的窗口。
    • 该窗口即为被"击中"的窗口。
  • 状态变化: 被击中的窗口会被提升至最上层

  • 返回值: 返回被击中窗口的 id;如果该坐标上没有任何窗口,则返回 -1


输入格式

  • 函数调用总次数不超过 1000 次。
  • idwidthheight 均为正整数。
  • row, col, dstRow, dstCol 等坐标值范围如题目所述。
  • 用例保证输入合法。

输出格式

  • 根据每个函数的原型要求返回相应的值。最终的整体输出由评测框架完成。

样例

输入样例

css 复制代码
["MultiWindowSys", "createWindow", "createWindow", "createWindow", "moveWindow", "dispatchClickEvent", "dispatchClickEvent", "dispatchClickEvent", "dispatchClickEvent"]
[[], [1, 3, 4, 9, 7], [2, 7, 8, 11, 7], [3, 12, 6, 6, 5], [2, 950, 1000], [7, 8], [7, 7], [7, 8], [10, 7]]

输出样例

java 复制代码
[null, true, true, true, false, 2, 1, 1, -1]

样例执行流程详解

# 调用 解释与系统状态变化 (层级从上到下) 返回值
1 MultiWindowSys() 初始化系统。 null
2 createWindow(1, 3, 4, 9, 7) 创建窗口1。 层级: [1] true
3 createWindow(2, 7, 8, 11, 7) 创建窗口2,置于顶层。 层级: [2, 1] true
4 createWindow(3, 12, 6, 6, 5) 创建窗口3,置于顶层。 层级: [3, 2, 1] true
5 moveWindow(2, 950, 1000) 尝试移动窗口2。目标位置 [950, 1000] 会使其完全移出屏幕 (屏幕范围 0-999),移动失败。层级不变: [3, 2, 1] false
6 dispatchClickEvent(7, 8) 点击 (7,8)。从上到下检查:窗口3未覆盖,窗口2覆盖。击中窗口2。窗口2被移到最顶层。层级: [2, 3, 1] 2
7 dispatchClickEvent(7, 7) 点击 (7,7)。从上到下检查:窗口2未覆盖,窗口3未覆盖,窗口1覆盖。击中窗口1。窗口1被移到最顶层。层级: [1, 2, 3] 1
8 dispatchClickEvent(7, 8) 点击 (7,8)。从上到下检查:窗口1未覆盖,窗口2覆盖。击中窗口2。窗口2被移到最顶层。层级: [2, 1, 3] 1 (这里应该是 2,样例解释可能有误或与图不符,但逻辑是击中2)
9 dispatchClickEvent(10, 7) 点击 (10,7)。从上到下检查:窗口2、1、3均未覆盖此坐标。未击中任何窗口。 -1
java 复制代码
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * 模拟一个简易的多窗口UI事件分发系统.
 * 核心是管理一组窗口对象,维护它们的层级关系(Z-order)和位置信息,
 * 并正确处理创建、销毁、移动和点击事件。
 */
public class MultiWindowSys {

    /**
     * 内部静态类,用于表示一个窗口.
     * 封装了窗口的所有属性和相关的几何计算(如点击命中测试)。
     */
    private static class Window {
        int id;
        int row, col;
        int width, height;

        public Window(int id, int row, int col, int width, int height) {
            this.id = id;
            this.row = row;
            this.col = col;
            this.width = width;
            this.height = height;
        }

        /**
         * 检查给定的点[r, c]是否在该窗口的矩形区域内.
         * @param r 行坐标
         * @param c 列坐标
         * @return 如果点在窗口内,返回true
         */
        public boolean contains(int r, int c) {
            return r >= this.row && r < (this.row + this.height) &&
                   c >= this.col && c < (this.col + this.width);
        }

        /**
         * 检查窗口是否完全移出屏幕外.
         * @param screenWidth  屏幕宽度
         * @param screenHeight 屏幕高度
         * @return 如果完全移出,返回true
         */
        public boolean isTotallyOutside(int screenWidth, int screenHeight) {
            // 条件:窗口的右边界在屏幕左边界的左边,或左边界在右边界的右边
            boolean horizontallyOut = (this.col + this.width <= 0) || (this.col >= screenWidth);
            // 条件:窗口的下边界在屏幕上边界的上边,或上边界在下边界的下边
            boolean verticallyOut = (this.row + this.height <= 0) || (this.row >= screenHeight);
            return horizontallyOut || verticallyOut;
        }
    }

    // --- 系统状态变量 ---

    private static final int SCREEN_SIZE = 1000;
    /**
     * 使用 Map 实现通过窗口ID的快速查找 (O(1) 复杂度).
     * Key: 窗口ID, Value: 窗口对象.
     */
    private final Map<Integer, Window> windowMap;

    /**
     * 使用 List 维护窗口的层级关系 (Z-order).
     * 约定:列表的末尾是"最上层"的窗口.
     * LinkedList 对于"从中间移除,再添加到末尾"的操作效率较高。
     */
    private final List<Window> windowStack;

    /**
     * 初始化系统.
     */
    public MultiWindowSys() {
        this.windowMap = new HashMap<>();
        this.windowStack = new LinkedList<>();
    }

    /**
     * 创建一个新窗口.
     * 新窗口总是在最上层。
     * @return 如果ID已存在,创建失败返回false;否则成功返回true.
     */
    public boolean createWindow(int id, int row, int col, int width, int height) {
        if (windowMap.containsKey(id)) {
            return false; // ID已存在,创建失败
        }
        Window newWindow = new Window(id, row, col, width, height);
        windowMap.put(id, newWindow);
        windowStack.add(newWindow); // 添加到列表末尾,即最上层
        return true;
    }

    /**
     * 销毁一个窗口.
     * @return 如果窗口ID不存在,返回false;否则成功返回true.
     */
    public boolean destroyWindow(int id) {
        if (!windowMap.containsKey(id)) {
            return false; // 窗口不存在
        }
        Window windowToDestroy = windowMap.get(id);
        windowMap.remove(id);         // 从Map中移除
        windowStack.remove(windowToDestroy); // 从层级列表中移除
        return true;
    }

    /**
     * 移动一个窗口到新位置,并将其置于最顶层.
     * @return 如果窗口ID不存在或移动后完全位于屏幕外,返回false;否则成功返回true.
     */
    public boolean moveWindow(int id, int dstRow, int dstCol) {
        if (!windowMap.containsKey(id)) {
            return false; // 窗口不存在
        }
        Window windowToMove = windowMap.get(id);

        // 暂存旧坐标,以备移动失败时恢复
        int oldRow = windowToMove.row;
        int oldCol = windowToMove.col;

        // 先"试探性"地更新坐标
        windowToMove.row = dstRow;
        windowToMove.col = dstCol;

        // 检查移动后是否完全超出屏幕
        if (windowToMove.isTotallyOutside(SCREEN_SIZE, SCREEN_SIZE)) {
            // 移动失败,恢复原坐标
            windowToMove.row = oldRow;
            windowToMove.col = oldCol;
            return false;
        }

        // 移动成功,将其置于最顶层
        bringToTop(windowToMove);
        return true;
    }

    /**
     * 分发点击事件.
     * 点击会命中覆盖该坐标的、层级最高的窗口,并将其置于最顶层。
     * @return 返回被击中窗口的ID;如果未击中任何窗口,返回-1.
     */
    public int dispatchClickEvent(int row, int col) {
        Window hitWindow = null;

        // 从列表末尾向前遍历,即从最上层的窗口开始检查
        for (int i = windowStack.size() - 1; i >= 0; i--) {
            Window currentWindow = windowStack.get(i);
            if (currentWindow.contains(row, col)) {
                // 找到了第一个(即最上层的)命中的窗口
                hitWindow = currentWindow;
                break;
            }
        }

        if (hitWindow != null) {
            // 如果击中窗口,将其置于最顶层
            bringToTop(hitWindow);
            return hitWindow.id;
        }

        // 未击中任何窗口
        return -1;
    }
    
    /**
     * 辅助方法:将一个指定的窗口移动到层级的最顶端.
     * @param window 要移动到顶层的窗口对象
     */
    private void bringToTop(Window window) {
        // 先从当前位置移除,再添加到列表末尾
        if (windowStack.remove(window)) {
            windowStack.add(window);
        }
    }
}
相关推荐
凌辰揽月2 分钟前
AJAX 学习
java·前端·javascript·学习·ajax·okhttp
永日4567035 分钟前
学习日记-spring-day45-7.10
java·学习·spring
学不动CV了1 小时前
C语言32个关键字
c语言·开发语言·arm开发·单片机·算法
小屁孩大帅-杨一凡2 小时前
如何解决ThreadLocal内存泄漏问题?
java·开发语言·jvm·算法
学习3人组2 小时前
在 IntelliJ IDEA 系列中phpstorm2025设置中文界面
java·ide·intellij-idea
Y1nhl3 小时前
力扣_二叉树的BFS_python版本
python·算法·leetcode·职场和发展·宽度优先
cainiao0806054 小时前
Java 大视界:基于 Java 的大数据可视化在智慧城市能源消耗动态监测与优化决策中的应用(2025 实战全景)
java
长风破浪会有时呀4 小时前
记一次接口优化历程 CountDownLatch
java
云朵大王5 小时前
SQL 视图与事务知识点详解及练习题
java·大数据·数据库
向阳逐梦5 小时前
PID控制算法理论学习基础——单级PID控制
人工智能·算法