From 5149adb5f7f5b952d7b347bdfd27fc62bbbfb9f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A5=BF=E9=A3=8E=E9=80=8D=E9=81=A5=E6=B8=B8?= Date: Thu, 6 Aug 2026 00:14:27 +0800 Subject: [PATCH] feat: enhance window module with new functionalities and improvements - Updated the Window API to include methods for fullscreen management, window title handling, and position retrieval. - Refactored existing methods for better clarity and functionality, including the introduction of separate methods for fullscreen desktop and exclusive modes. - Improved DPI handling and pixel conversion methods to ensure accurate rendering across different display settings. - Enhanced unit tests for the window module, covering new functionalities and ensuring robust behavior in various scenarios. - Updated documentation to reflect the changes in the Window API and added details on new features. --- docs/模块设计.md | 6 +- docs/测试覆盖.md | 21 ++- src/modules/window/Window.h | 138 ++++++++--------- src/modules/window/sdl/Window.cpp | 248 +++++++++++++++++++++++++++++- src/modules/window/sdl/Window.h | 46 +++++- test/window_cpp.cpp | 155 ++++++++++++++++++- 6 files changed, 514 insertions(+), 100 deletions(-) diff --git a/docs/模块设计.md b/docs/模块设计.md index b05f4309..dc9d126e 100644 --- a/docs/模块设计.md +++ b/docs/模块设计.md @@ -49,8 +49,8 @@ - [x] 模块骨架与 SDL 后端 - [x] 窗口创建、关闭、基本属性 -- [ ] 位置 / 尺寸 / 样式完整控制 -- [ ] 全屏 / 无边框 / 高 DPI +- [x] 位置 / 尺寸 / 样式完整控制 +- [x] 全屏 / 无边框 / 高 DPI - [ ] 与 `graphics` 的呈现表面对接 ### 4. 图形系统 — `graphics` @@ -223,7 +223,7 @@ Love2D 风格即时模式仅作 C++/DevTools 内部逃生舱。 | 键盘 | `keyboard` | [ ] | | 触屏 | `touch` | [x] | | 手柄 | `joystick` | [ ] | -| 窗口 | `window` | [x] 基础可用 | +| 窗口 | `window` | [x] 日常控制 API 已实现(呈现对接待 graphics) | | 图形 | `graphics` | [ ] | | 数据 | `data` | [x] | | 图片 | `image` | [x] | diff --git a/docs/测试覆盖.md b/docs/测试覆盖.md index cb43760d..3026fe24 100644 --- a/docs/测试覆盖.md +++ b/docs/测试覆盖.md @@ -113,7 +113,7 @@ | keyboard | `src/modules/keyboard/Keyboard.h` | 部分 | N/A | **N/A** | 未 `create_module` / 无 `Module_REG`;见 kb-joy 报告 | | touch | `src/modules/touch/Touch.h` | 可用 | 已展开 | **2/2 已覆盖** | `test/touch.cpp`;`EVTouch` 已链入 unit_test | | joystick | `src/modules/joystick/Joystick.h` | 部分 | N/A | **N/A** | 未接入构建 + `love::` 残留 | -| window | `src/modules/window/Window.h` | 可用 | 已展开 | **见 §11** | `test/window_cpp.cpp`(7 用例) | +| window | `src/modules/window/Window.h` | 可用 | 已展开 | **见 §11** | `test/window_cpp.cpp`(14 用例) | | graphics | `src/modules/graphics/Graphics.h` | 部分 | 待展开 | - | 含 GPU/窗口依赖 | | data | `src/modules/data/DataModule.h` | 可用 | 已展开 | **33/35 已覆盖**(2 部分) | `test/data.cpp`;22 个 `data.*` 用例 | | image | `src/modules/image/Image.h` | 可用 | 已展开 | **见 §12** | `test/image.cpp`(10 用例) | @@ -274,14 +274,24 @@ ## 11. API 明细:`window` -用例:`test/window_cpp.cpp`(7)。脚本 `WindowTest.*` 不计入。 +用例:`test/window_cpp.cpp`(14)。脚本 `WindowTest.*` 不计入。回归 `--testcase=^window\..*$`:**14/14 PASS**(2026-08-06 Window API 轮)。 | API | 状态 | 备注 | |-----|------|------| -| `setWindowSettings` / `getWindowSettings` / `getHandle` | 已覆盖 | 320×240 往返;close 后重建 | +| `setWindowSettings` / `getWindowSettings` / `getHandle` | 已覆盖 | 320×240 往返;close 后重建;0×0 用桌面尺寸 | | `setSize` / `getWidth` / `getHeight` | 已覆盖 | | -| `setFullscreen`(双参 / 单参) | 已覆盖 | 仅 `desktop_mode`;独占全屏未测 | -| `close` | 已覆盖 | close 后 fullscreen→false | +| `setFullscreenDesktop` / `setFullscreenExclusive` | 部分 | desktop 往返 + close 后 false;独占全屏无正例 | +| `close` / `isOpen` | 已覆盖 | | +| `setWindowTitle` / `getWindowTitle` | 已覆盖 | 含创建前缓存 | +| `setPosition` / `getPosition` | 已覆盖 | 容差 ±32 px | +| `setVSync` / `getVSync` | 已覆盖 | | +| `isVisible` | 已覆盖 | 与 title/position 同用例 | +| `minimize` / `maximize` / `restore` / `isMaximized` / `isMinimized` | 未测 | 下轮 | +| `hasFocus` / `hasMouseFocus` | 未测 | | +| `getPixelWidth` / `getPixelHeight` / DPI 换算 | 部分 | round-trip;`high_dpi` 未单独 smoke | +| `getDisplayCount` / `getDisplayName` / `getDisplayOrientation` / `getFullscreenSizes` / `getDesktopDimensions` | 已覆盖 | 含无效 index 边界 | +| `showMessageBox` / `showMessageBoxData` | 部分 | 默认跳过;`EVENGINE_TEST_MSGBOX=1` 手动 | +| `requestAttention` | 已覆盖 | no-crash | | `setGraphics` | 部分 | 测试中调用,无独立断言 | | `setWindowSettings`→false | 未测 | 难以稳定触发 | @@ -344,4 +354,5 @@ | 2026-08-05 | 样板轮落地:`test/data.cpp` +13 用例;新建 `test/filesystem_cpp.cpp`(8 用例);更新 §5–§7 进度;回归 `data`+`filesystem`+`FilesystemTest` 33/33 PASS | | 2026-08-05 | Round 2:event/mouse/touch/window/image/sound/audio/network/timer C++ 补测;keyboard/joystick 标 N/A;`EVTouch` 链入 unit_test;回归过滤 69 PASS;放宽 `audio.source.seekTell`;更新 §5 与 §9–§16 | | 2026-08-05 | 修复:`File::read`/`open("r")` PhysFS 模式;Mouse WarpMouse 窗口句柄;Audio `stopAll` 野指针;HttpRequest verifySsl 继承(HTTPS 仍 stub `tls`);补强相关断言 | +| 2026-08-06 | Window API 轮收尾:`Window.h` 范围 B 纯虚齐全;`test/window_cpp.cpp` 14 用例;`^window\..*$` 14/14 PASS;更新 §5 / §11 | | diff --git a/src/modules/window/Window.h b/src/modules/window/Window.h index 0080a4f3..a4367333 100644 --- a/src/modules/window/Window.h +++ b/src/modules/window/Window.h @@ -51,14 +51,12 @@ public: }; struct MessageBoxData { - std::string type; + std::string type = "info"; std::string title; std::string message; - - int enterButtonIndex; - int escapeButtonIndex; - bool attachToWindow; - + int enterButtonIndex = 0; + int escapeButtonIndex = 0; + bool attachToWindow = false; std::vector buttons; }; @@ -75,86 +73,70 @@ public: virtual void close() = 0; - virtual bool setFullscreen(bool fullscreen, bool desktop_mode) = 0; - virtual bool setFullscreen(bool fullscreen) = 0; + virtual bool setFullscreenDesktop(bool fullscreen) = 0; + virtual bool setFullscreenExclusive(bool fullscreen) = 0; - // virtual bool onSizeChanged(int width, int height) = 0; + virtual bool isOpen() const = 0; - // virtual int getDisplayCount() const = 0; + virtual void setWindowTitle(const std::string& title) = 0; + virtual const std::string& getWindowTitle() const = 0; + virtual void setPosition(int x, int y, int display) = 0; + virtual void getPosition(int& x, int& y, int& display) = 0; + virtual void minimize() = 0; + virtual void maximize() = 0; + virtual void restore() = 0; + virtual bool isMaximized() const = 0; + virtual bool isMinimized() const = 0; + virtual bool hasFocus() const = 0; + virtual bool hasMouseFocus() const = 0; + virtual bool isVisible() const = 0; + virtual void setVSync(int vsync) = 0; + virtual int getVSync() const = 0; - // virtual const char *getDisplayName(int display) const = 0; - - // virtual DisplayOrientation getDisplayOrientation(int display) const = 0; - - // virtual std::vector getFullscreenSizes(int display) const = 0; - - // virtual void getDesktopDimensions(int display, int &width, int &height) const = 0; - - // virtual void setPosition(int x, int y, int display) = 0; - // virtual void getPosition(int &x, int &y, int &display) = 0; - - // virtual Rect getSafeArea() const = 0; - - // virtual bool isOpen() const = 0; - - // virtual void setWindowTitle(const std::string &title) = 0; - // virtual const std::string &getWindowTitle() const = 0; - - // // virtual bool setIcon(image::ImageData *image_data) = 0; - // // virtual image::ImageData *getIcon() = 0; - - // virtual void setVSync(int vsync) = 0; - // virtual int getVSync() const = 0; - - // virtual void setDisplaySleepEnabled(bool enable) = 0; - // virtual bool isDisplaySleepEnabled() const = 0; - - // virtual void minimize() = 0; - // virtual void maximize() = 0; - // virtual void restore() = 0; - - // virtual bool isMaximized() const = 0; - // virtual bool isMinimized() const = 0; - - // // default no-op implementation - // virtual void swapBuffers(); - - // virtual bool hasFocus() const = 0; - // virtual bool hasMouseFocus() const = 0; - - // virtual bool isVisible() const = 0; - - // virtual void setMouseGrab(bool grab) = 0; - // virtual bool isMouseGrabbed() const = 0; - - // virtual int getWidth() const = 0; - // virtual int getHeight() const = 0; - // virtual int getPixelWidth() const = 0; - // virtual int getPixelHeight() const = 0; - - // // Note: window-space coordinates are not necessarily the same as - // // density-independent units (which toPixels and fromPixels use.) - // virtual void windowToPixelCoords(double *x, double *y) const = 0; - // virtual void pixelToWindowCoords(double *x, double *y) const = 0; - - // virtual void windowToDPICoords(double *x, double *y) const = 0; - // virtual void DPIToWindowCoords(double *x, double *y) const = 0; - - // virtual double getDPIScale() const = 0; - // virtual double getNativeDPIScale() const = 0; - - // virtual double toPixels(double x) const = 0; - // virtual void toPixels(double wx, double wy, double &px, double &py) const = 0; - // virtual double fromPixels(double x) const = 0; - // virtual void fromPixels(double px, double py, double &wx, double &wy) const = 0; + virtual int getPixelWidth() const = 0; + virtual int getPixelHeight() const = 0; + virtual double getDPIScale() const = 0; + virtual double getNativeDPIScale() const = 0; + virtual void windowToPixelCoords(double* x, double* y) const = 0; + virtual void pixelToWindowCoords(double* x, double* y) const = 0; + virtual void windowToDPICoords(double* x, double* y) const = 0; + virtual void DPIToWindowCoords(double* x, double* y) const = 0; + virtual double toPixels(double x) const = 0; + virtual double fromPixels(double x) const = 0; + virtual void toPixelsXY(double wx, double wy, double& px, double& py) const = 0; + virtual void fromPixelsXY(double px, double py, double& wx, double& wy) const = 0; /** Native window handle (SDL_Window* on SDL backend). */ virtual void *getHandle() const = 0; - // virtual bool showMessageBox(const std::string &title, const std::string &message, MessageBoxType type, bool - // attachtowindow) = 0; virtual int showMessageBox(const MessageBoxData &data) = 0; + virtual int getDisplayCount() const = 0; + virtual std::string getDisplayName(int display) const = 0; + virtual std::string getDisplayOrientation(int display) const = 0; + virtual std::vector getFullscreenSizes(int display) const = 0; + virtual void getDesktopDimensions(int display, int& width, int& height) const = 0; - // virtual void requestAttention(bool continuous) = 0; + virtual bool showMessageBox(const std::string& title, const std::string& message, + const std::string& type, bool attachToWindow) = 0; + virtual int showMessageBoxData(const MessageBoxData& data) = 0; + virtual void requestAttention(bool continuous) = 0; + + // --- not yet implemented (kept as drafts) --- + + // virtual bool onSizeChanged(int width, int height) = 0; + + // virtual Rect getSafeArea() const = 0; + + // // virtual bool setIcon(image::ImageData *image_data) = 0; + // // virtual image::ImageData *getIcon() = 0; + + // virtual void setDisplaySleepEnabled(bool enable) = 0; + // virtual bool isDisplaySleepEnabled() const = 0; + + // // default no-op implementation + // virtual void swapBuffers(); + + // virtual void setMouseGrab(bool grab) = 0; + // virtual bool isMouseGrabbed() const = 0; }; // Window diff --git a/src/modules/window/sdl/Window.cpp b/src/modules/window/sdl/Window.cpp index 2b8d8aa8..7419af0e 100644 --- a/src/modules/window/sdl/Window.cpp +++ b/src/modules/window/sdl/Window.cpp @@ -23,6 +23,10 @@ #include #endif +#ifdef EVENGINE_MACOSX +#include "macosx/macosx.h" +#endif + namespace eve { namespace graphic{ @@ -32,7 +36,17 @@ namespace graphic{ namespace window { namespace sdl { -Window::Window() { +namespace { + +Uint32 messageBoxFlag(const std::string& type) { + if (type == "error") return SDL_MESSAGEBOX_ERROR; + if (type == "warning") return SDL_MESSAGEBOX_WARNING; + return SDL_MESSAGEBOX_INFORMATION; +} + +} // namespace + +Window::Window() : open(false) { if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) throw Exception("Could not initialize SDL video subsystem (%s)", SDL_GetError()); } @@ -57,7 +71,8 @@ bool Window::setWindowSettings(WindowSettings f) { f.minwidth = std::max(f.minwidth, (uint16_t)1); f.minheight = std::max(f.minheight, (uint16_t)1); - // f.display = std::min(std::max(f.display, 0), getDisplayCount() - 1); + if (getDisplayCount() > 0) + f.display = static_cast(std::min(std::max(int(f.display), 0), getDisplayCount() - 1)); if (f.width == 0 || f.height == 0) { SDL_DisplayMode mode = {}; SDL_GetDesktopDisplayMode(f.display, &mode); @@ -154,12 +169,21 @@ bool Window::setWindowSettings(WindowSettings f) { graphics->initWithWindow(window); graphics->setViewportSize(f.width, f.height, pw, ph); } + open = true; return true; } WindowSettings Window::getWindowSettings() { return settings; } -bool Window::setFullscreen(bool fullscreen, bool desktop_mode) { +bool Window::setFullscreenDesktop(bool fullscreen) { + return setFullscreenInternal(fullscreen, true); +} + +bool Window::setFullscreenExclusive(bool fullscreen) { + return setFullscreenInternal(fullscreen, false); +} + +bool Window::setFullscreenInternal(bool fullscreen, bool desktop_mode) { if (!window) return false; // if (graphics && graphics->isCanvasActive()) @@ -203,7 +227,221 @@ bool Window::setFullscreen(bool fullscreen, bool desktop_mode) { return false; } -bool Window::setFullscreen(bool fullscreen) { return setFullscreen(fullscreen, settings.desktop_mode); } +bool Window::isOpen() const { return open; } + +void Window::setWindowTitle(const std::string& t) { + title = t; + if (window) SDL_SetWindowTitle(window, title.c_str()); +} + +const std::string& Window::getWindowTitle() const { return title; } + +void Window::setPosition(int x, int y, int display) { + if (!window) return; + int count = SDL_GetNumVideoDisplays(); + if (display < 0 || display >= count) display = 0; + SDL_Rect bounds = {}; + SDL_GetDisplayBounds(display, &bounds); + SDL_SetWindowPosition(window, bounds.x + x, bounds.y + y); + settings.x = x; + settings.y = y; + settings.display = static_cast(display); + settings.use_position = true; +} + +void Window::getPosition(int& x, int& y, int& display) { + if (!window) { + x = settings.x; + y = settings.y; + display = settings.display; + return; + } + int wx = 0, wy = 0; + SDL_GetWindowPosition(window, &wx, &wy); + display = SDL_GetWindowDisplayIndex(window); + if (display < 0) display = 0; + SDL_Rect bounds = {}; + SDL_GetDisplayBounds(display, &bounds); + x = wx - bounds.x; + y = wy - bounds.y; +} + +void Window::minimize() { if (window) SDL_MinimizeWindow(window); } +void Window::maximize() { if (window) SDL_MaximizeWindow(window); } +void Window::restore() { if (window) SDL_RestoreWindow(window); } + +bool Window::isMaximized() const { + return window && (SDL_GetWindowFlags(window) & SDL_WINDOW_MAXIMIZED) != 0; +} +bool Window::isMinimized() const { + return window && (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) != 0; +} +bool Window::hasFocus() const { + return window && (SDL_GetWindowFlags(window) & SDL_WINDOW_INPUT_FOCUS) != 0; +} +bool Window::hasMouseFocus() const { + return window && (SDL_GetWindowFlags(window) & SDL_WINDOW_MOUSE_FOCUS) != 0; +} +bool Window::isVisible() const { + return window && (SDL_GetWindowFlags(window) & SDL_WINDOW_SHOWN) != 0 + && (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) == 0; +} + +void Window::setVSync(int vsync) { + settings.vsync = static_cast(vsync < 0 ? 0 : (vsync > 255 ? 255 : vsync)); +} + +int Window::getVSync() const { return settings.vsync; } + +int Window::getPixelWidth() const { return window ? pixelWidth : 0; } + +int Window::getPixelHeight() const { return window ? pixelHeight : 0; } + +double Window::getNativeDPIScale() const { + if (!window || windowWidth <= 0) return 1.0; + return double(pixelWidth) / double(windowWidth); +} + +double Window::getDPIScale() const { + if (!window) return 1.0; + if (!settings.use_dpi_scale) return settings.dpi_scale > 0.f ? double(settings.dpi_scale) : 1.0; + return getNativeDPIScale(); +} + +void Window::windowToPixelCoords(double* x, double* y) const { + if (!x || !y) return; + double s = getNativeDPIScale(); + *x *= s; + *y *= s; +} + +void Window::pixelToWindowCoords(double* x, double* y) const { + if (!x || !y) return; + double s = getNativeDPIScale(); + if (s == 0.0) return; + *x /= s; + *y /= s; +} + +void Window::windowToDPICoords(double* x, double* y) const { + if (!x || !y) return; + double s = getDPIScale(); + if (s == 0.0) return; + *x /= s; + *y /= s; +} + +void Window::DPIToWindowCoords(double* x, double* y) const { + if (!x || !y) return; + double s = getDPIScale(); + *x *= s; + *y *= s; +} + +double Window::toPixels(double x) const { return x * getNativeDPIScale(); } + +double Window::fromPixels(double x) const { + double s = getNativeDPIScale(); + return s == 0.0 ? x : x / s; +} + +void Window::toPixelsXY(double wx, double wy, double& px, double& py) const { + px = toPixels(wx); + py = toPixels(wy); +} + +void Window::fromPixelsXY(double px, double py, double& wx, double& wy) const { + wx = fromPixels(px); + wy = fromPixels(py); +} + +int Window::getDisplayCount() const { + int n = SDL_GetNumVideoDisplays(); + return n < 0 ? 0 : n; +} + +std::string Window::getDisplayName(int display) const { + if (display < 0 || display >= getDisplayCount()) return {}; + const char* n = SDL_GetDisplayName(display); + return n ? std::string(n) : std::string(); +} + +std::string Window::getDisplayOrientation(int display) const { + if (display < 0 || display >= getDisplayCount()) return "unknown"; + switch (SDL_GetDisplayOrientation(display)) { + case SDL_ORIENTATION_LANDSCAPE: return "landscape"; + case SDL_ORIENTATION_LANDSCAPE_FLIPPED: return "landscapeFlipped"; + case SDL_ORIENTATION_PORTRAIT: return "portrait"; + case SDL_ORIENTATION_PORTRAIT_FLIPPED: return "portraitFlipped"; + default: return "unknown"; + } +} + +std::vector Window::getFullscreenSizes(int display) const { + std::vector out; + if (display < 0 || display >= getDisplayCount()) return out; + int modes = SDL_GetNumDisplayModes(display); + for (int i = 0; i < modes; ++i) { + SDL_DisplayMode mode = {}; + if (SDL_GetDisplayMode(display, i, &mode) == 0) { + WindowSize s; + s.width = mode.w; + s.height = mode.h; + if (std::find(out.begin(), out.end(), s) == out.end()) out.push_back(s); + } + } + return out; +} + +void Window::getDesktopDimensions(int display, int& width, int& height) const { + width = height = 0; + if (display < 0 || display >= getDisplayCount()) return; + SDL_DisplayMode mode = {}; + if (SDL_GetDesktopDisplayMode(display, &mode) == 0) { + width = mode.w; + height = mode.h; + } +} + +bool Window::showMessageBox(const std::string& title, const std::string& message, + const std::string& type, bool attachToWindow) { + SDL_Window* parent = (attachToWindow && window) ? window : nullptr; + return SDL_ShowSimpleMessageBox(messageBoxFlag(type), title.c_str(), message.c_str(), parent) == 0; +} + +int Window::showMessageBoxData(const MessageBoxData& data) { + if (data.buttons.empty()) return -1; + std::vector buttons; + buttons.reserve(data.buttons.size()); + for (size_t i = 0; i < data.buttons.size(); ++i) { + Uint32 flags = 0; + if (int(i) == data.enterButtonIndex) flags |= SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT; + if (int(i) == data.escapeButtonIndex) flags |= SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT; + buttons.push_back({flags, int(i), data.buttons[i].c_str()}); + } + SDL_MessageBoxData sdl = {}; + sdl.flags = messageBoxFlag(data.type); + sdl.window = (data.attachToWindow && window) ? window : nullptr; + sdl.title = data.title.c_str(); + sdl.message = data.message.c_str(); + sdl.numbuttons = int(buttons.size()); + sdl.buttons = buttons.data(); + sdl.colorScheme = nullptr; + int buttonid = -1; + if (SDL_ShowMessageBox(&sdl, &buttonid) < 0) return -1; + return buttonid; +} + +void Window::requestAttention(bool continuous) { +#ifdef EVENGINE_MACOSX + eve::macosx::requestAttention(continuous); +#else + if (!window) return; +#if SDL_VERSION_ATLEAST(2, 0, 16) + SDL_FlashWindow(window, continuous ? SDL_FLASH_UNTIL_FOCUSED : SDL_FLASH_BRIEFLY); +#endif +#endif +} bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowflags, int msaa, bool stencil, int depth) { window = SDL_CreateWindow(title.c_str(), x, y, w, h, windowflags); @@ -235,6 +473,8 @@ void Window::close(bool allowExceptions) { SDL_FlushEvent(SDL_WINDOWEVENT); } + pixelWidth = pixelHeight = 0; + windowWidth = windowHeight = 0; open = false; } diff --git a/src/modules/window/sdl/Window.h b/src/modules/window/sdl/Window.h index 35cf3b23..3805ddcb 100644 --- a/src/modules/window/sdl/Window.h +++ b/src/modules/window/sdl/Window.h @@ -26,12 +26,54 @@ public: void close() override; - bool setFullscreen(bool fullscreen, bool desktop_mode) override; - bool setFullscreen(bool fullscreen) override; + bool setFullscreenDesktop(bool fullscreen) override; + bool setFullscreenExclusive(bool fullscreen) override; + + bool isOpen() const override; + + void setWindowTitle(const std::string& title) override; + const std::string& getWindowTitle() const override; + void setPosition(int x, int y, int display) override; + void getPosition(int& x, int& y, int& display) override; + void minimize() override; + void maximize() override; + void restore() override; + bool isMaximized() const override; + bool isMinimized() const override; + bool hasFocus() const override; + bool hasMouseFocus() const override; + bool isVisible() const override; + void setVSync(int vsync) override; + int getVSync() const override; + + int getPixelWidth() const override; + int getPixelHeight() const override; + double getDPIScale() const override; + double getNativeDPIScale() const override; + void windowToPixelCoords(double* x, double* y) const override; + void pixelToWindowCoords(double* x, double* y) const override; + void windowToDPICoords(double* x, double* y) const override; + void DPIToWindowCoords(double* x, double* y) const override; + double toPixels(double x) const override; + double fromPixels(double x) const override; + void toPixelsXY(double wx, double wy, double& px, double& py) const override; + void fromPixelsXY(double px, double py, double& wx, double& wy) const override; void *getHandle() const override { return window; } + int getDisplayCount() const override; + std::string getDisplayName(int display) const override; + std::string getDisplayOrientation(int display) const override; + std::vector getFullscreenSizes(int display) const override; + void getDesktopDimensions(int display, int& width, int& height) const override; + + bool showMessageBox(const std::string& title, const std::string& message, + const std::string& type, bool attachToWindow) override; + int showMessageBoxData(const MessageBoxData& data) override; + void requestAttention(bool continuous) override; + private: + bool setFullscreenInternal(bool fullscreen, bool desktop_mode); graphics::Graphics *graphics = nullptr; int width = 800, height = 600; diff --git a/test/window_cpp.cpp b/test/window_cpp.cpp index b1984e9c..96efa126 100644 --- a/test/window_cpp.cpp +++ b/test/window_cpp.cpp @@ -1,4 +1,6 @@ -#include "zeroerr/assert.h" +#include + +#include "zeroerr/assert.h" #include "zeroerr/unittest.h" #include "graphics/Graphics.h" @@ -91,12 +93,12 @@ TEST_CASE("window.setFullscreenDesktopRoundTrip") { CHECK(!win->getWindowSettings().fullscreen); - REQUIRE(win->setFullscreen(true, true)); + REQUIRE(win->setFullscreenDesktop(true)); CHECK(win->getWindowSettings().fullscreen); CHECK(win->getWindowSettings().desktop_mode); CHECK(win->getHandle() != nullptr); - REQUIRE(win->setFullscreen(false, true)); + REQUIRE(win->setFullscreenDesktop(false)); CHECK(!win->getWindowSettings().fullscreen); CHECK_EQ(win->getWidth(), 320); CHECK_EQ(win->getHeight(), 240); @@ -104,7 +106,7 @@ TEST_CASE("window.setFullscreenDesktopRoundTrip") { win->close(); } -TEST_CASE("window.setFullscreenSingleArgUsesDesktopMode") { +TEST_CASE("window.setFullscreenDesktopRoundTripUsesSettings") { eve::window::Window* win = nullptr; eve::graphics::Graphics* gfx = nullptr; openWindow(win, gfx, 320, 240); @@ -113,11 +115,11 @@ TEST_CASE("window.setFullscreenSingleArgUsesDesktopMode") { s.desktop_mode = true; REQUIRE(win->setWindowSettings(s)); - REQUIRE(win->setFullscreen(true)); + REQUIRE(win->setFullscreenDesktop(true)); CHECK(win->getWindowSettings().fullscreen); CHECK(win->getWindowSettings().desktop_mode); - REQUIRE(win->setFullscreen(false)); + REQUIRE(win->setFullscreenDesktop(false)); CHECK(!win->getWindowSettings().fullscreen); win->close(); @@ -131,8 +133,8 @@ TEST_CASE("window.closeClearsHandleAndBlocksFullscreen") { win->close(); CHECK(win->getHandle() == nullptr); - CHECK(!win->setFullscreen(true, true)); - CHECK(!win->setFullscreen(false)); + CHECK(!win->setFullscreenDesktop(true)); + CHECK(!win->setFullscreenExclusive(false)); } TEST_CASE("window.setWindowSettingsRecreatesAfterClose") { @@ -171,3 +173,140 @@ TEST_CASE("window.zeroSizeUsesDesktopDimensions") { win->close(); } + +TEST_CASE("window.isOpenAfterCreateAndClose") { + eve::window::Window* win = nullptr; + eve::graphics::Graphics* gfx = nullptr; + openWindow(win, gfx, 320, 240); + CHECK(win->isOpen()); + win->close(); + CHECK(!win->isOpen()); +} + +TEST_CASE("window.titleAndPositionAndVSync") { + eve::window::Window* win = nullptr; + eve::graphics::Graphics* gfx = nullptr; + openWindow(win, gfx, 320, 240); + + win->setWindowTitle("eve-window-test"); + CHECK_EQ(win->getWindowTitle(), std::string("eve-window-test")); + + win->setVSync(0); + CHECK_EQ(win->getVSync(), 0); + win->setVSync(1); + CHECK_EQ(win->getVSync(), 1); + + int x = 0, y = 0, display = -1; + win->getPosition(x, y, display); + CHECK_GE(display, 0); + + win->setPosition(40, 50, display); + int x2 = 0, y2 = 0, d2 = -1; + win->getPosition(x2, y2, d2); + CHECK_EQ(d2, display); + CHECK_LE(std::abs(x2 - 40), 32); + CHECK_LE(std::abs(y2 - 50), 32); + + CHECK(win->isVisible()); + win->close(); +} + +TEST_CASE("window.titleCachesBeforeOpen") { + auto* win = eve::window::Window::create(); + REQUIRE(win != nullptr); + win->setWindowTitle("cached-title"); + CHECK_EQ(win->getWindowTitle(), std::string("cached-title")); + CHECK(!win->isOpen()); +} + +TEST_CASE("window.displayQueries") { + auto* win = eve::window::Window::create(); + REQUIRE(win != nullptr); + + int n = win->getDisplayCount(); + CHECK_GT(n, 0); + + std::string name = win->getDisplayName(0); + CHECK(!name.empty()); + + std::string ori = win->getDisplayOrientation(0); + const bool validOrientation = ori == "unknown" || ori == "landscape" || ori == "landscapeFlipped" + || ori == "portrait" || ori == "portraitFlipped"; + CHECK(validOrientation); + + auto sizes = win->getFullscreenSizes(0); + CHECK_GT(sizes.size(), 0u); + + int dw = 0, dh = 0; + win->getDesktopDimensions(0, dw, dh); + CHECK_GT(dw, 0); + CHECK_GT(dh, 0); + + CHECK(win->getDisplayName(-1).empty()); + CHECK_EQ(win->getDisplayOrientation(999), std::string("unknown")); + CHECK(win->getFullscreenSizes(999).empty()); + int zw = 1, zh = 1; + win->getDesktopDimensions(999, zw, zh); + CHECK_EQ(zw, 0); + CHECK_EQ(zh, 0); +} + +TEST_CASE("window.pixelSizeAndDpiRoundTrip") { + eve::window::Window* win = nullptr; + eve::graphics::Graphics* gfx = nullptr; + openWindow(win, gfx, 320, 240); + + CHECK_GT(win->getPixelWidth(), 0); + CHECK_GT(win->getPixelHeight(), 0); + CHECK_GT(win->getNativeDPIScale(), 0.0); + + double px = 0, py = 0; + win->toPixelsXY(10.0, 20.0, px, py); + double wx = 0, wy = 0; + win->fromPixelsXY(px, py, wx, wy); + CHECK_EQ(wx, 10.0); + CHECK_EQ(wy, 20.0); + + double x = 5.0, y = 6.0; + win->windowToPixelCoords(&x, &y); + win->pixelToWindowCoords(&x, &y); + CHECK_EQ(x, 5.0); + CHECK_EQ(y, 6.0); + + win->close(); + CHECK_EQ(win->getPixelWidth(), 0); + CHECK_EQ(win->getPixelHeight(), 0); + CHECK_EQ(win->getDPIScale(), 1.0); +} + +TEST_CASE("window.requestAttentionNoCrash") { + eve::window::Window* win = nullptr; + eve::graphics::Graphics* gfx = nullptr; + openWindow(win, gfx, 320, 240); + win->requestAttention(false); + win->requestAttention(true); + win->close(); + win->requestAttention(false); +} + +TEST_CASE("window.showMessageBoxOptional") { + const char* run = std::getenv("EVENGINE_TEST_MSGBOX"); + if (!run || std::string(run) != "1") { + return; + } + eve::window::Window* win = nullptr; + eve::graphics::Graphics* gfx = nullptr; + openWindow(win, gfx, 320, 240); + CHECK(win->showMessageBox("t", "m", "info", true)); + eve::window::Window::MessageBoxData data; + data.type = "warning"; + data.title = "t"; + data.message = "m"; + data.buttons = {"OK", "Cancel"}; + data.enterButtonIndex = 0; + data.escapeButtonIndex = 1; + data.attachToWindow = true; + int idx = win->showMessageBoxData(data); + CHECK_GE(idx, 0); + win->close(); +}