diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..2ffaf98 --- /dev/null +++ b/.clang-format @@ -0,0 +1,69 @@ +# Generated from CLion C/C++ Code Style settings +--- +Language: Cpp +BasedOnStyle: LLVM +AccessModifierOffset: -4 +AlignConsecutiveAssignments: true +AlignConsecutiveDeclarations: true +AlignOperands: true +AlignTrailingComments: true +AllowShortBlocksOnASingleLine: true +AllowShortCaseLabelsOnASingleLine: false # 禁止 case 标签与语句在同一行 +AllowShortIfStatementsOnASingleLine: false +AllowShortReturnStatementsOnASingleLine: WithoutReturnValue # **只允许无返回值的 return 语句在单行** +AlwaysBreakAfterDefinitionReturnType: false +AlwaysBreakAfterReturnType: None +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: false +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: true + BeforeElse: true + BeforeLambdaBody: false + BeforeWhile: true + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBraces: Custom +BreakBeforeTernaryOperators: false +BreakConstructorInitializers: AfterColon +BreakConstructorInitializersBeforeComma: false +ColumnLimit: 120 +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ContinuationIndentWidth: 8 +Cpp11BracedListStyle: false +IncludeCategories: + - Regex: '^<.*' + Priority: 1 + - Regex: '^".*' + Priority: 2 + - Regex: '.*' + Priority: 3 +IncludeIsMainRegex: '([-_](test|unittest))?$' +IndentCaseLabels: true +IndentWidth: 4 +InsertNewlineAtEOF: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 2 +NamespaceIndentation: All +PointerAlignment: Left +SpaceAfterCStyleCast: true +SpaceAfterTemplateKeyword: false +SpaceBeforeRangeBasedForLoopColon: false +SpaceInEmptyParentheses: false +SpacesInAngles: false +SpacesInConditionalStatement: false +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +TabWidth: 4 +UseTab: Always +... diff --git a/example/src/main.cpp b/example/src/main.cpp index 89f561c..7466be8 100644 --- a/example/src/main.cpp +++ b/example/src/main.cpp @@ -3,7 +3,7 @@ // #include "mirage.h" -#include "core/window/render_window.h" +#include "core/window/mwindow.h" #include "widget/compound_widget/mbutton.h" #include "widget/panel_widget/mbox.h" @@ -11,12 +11,13 @@ int main(int argc, char* argv[]) { mirage_app app; app.init(); - auto window = std::make_shared(); + auto window = std::make_shared(); window->create_window(800, 600, L"Hello, World!"); window->show(); mirage_app::get_render_context()->setup_surface(window.get()); + auto border = std::make_shared(); auto h_box = std::make_shared(); auto v_box = std::make_shared(); v_box->add_slot() @@ -62,6 +63,7 @@ int main(int argc, char* argv[]) { [ v_box ]; + h_box->add_slot() .stretch() .margin({ 5 }) diff --git a/src/core/render_context.h b/src/core/render_context.h index d28c142..a2741c5 100644 --- a/src/core/render_context.h +++ b/src/core/render_context.h @@ -11,7 +11,7 @@ #include "misc/mirage_type.h" #include "sokol/sokol_header.h" -class mirage_window; +class mwindow; /** * @class mirage_render_context @@ -81,7 +81,7 @@ public: * * 将渲染上下文与指定窗口的表面关联。 */ - virtual bool setup_surface(mirage_window* in_window) { return false; } + virtual bool setup_surface(mwindow* in_window) { return false; } }; /** diff --git a/src/core/render_elements.cpp b/src/core/render_elements.cpp index ee27f55..625d746 100644 --- a/src/core/render_elements.cpp +++ b/src/core/render_elements.cpp @@ -210,7 +210,7 @@ void render_elements::make_rect( float in_rotation_radians, const Eigen::Vector2f& in_pivot, const Eigen::Vector2f& in_scale) { - const auto& pos = in_geometry.local_to_parent(in_pos); + const auto& pos = in_geometry.local_to_window(in_pos); add_rect_to_batch(pos, in_size, in_color, diff --git a/src/core/window/mwindow.cpp b/src/core/window/mwindow.cpp new file mode 100644 index 0000000..7ab7058 --- /dev/null +++ b/src/core/window/mwindow.cpp @@ -0,0 +1,90 @@ +#include "mwindow.h" + +#include "widget/mwidget.h" + +void mwindow::on_paint() { + if (!content_widget_) + return; + + paint_context_.begin_frame(this); + layout_tree_.paint(paint_context_); + paint_context_.end_frame(); +} + +geometry_t mwindow::get_window_geometry_in_screen() const { + const auto& local_to_screen = get_local_to_screen_transform(); + return { get_window_frame_size().cast(), local_to_screen, {} }; +} + +geometry_t mwindow::get_window_geometry_in_window() const { + const auto& local_to_window = get_local_to_window_transform(); + return { get_window_frame_size().cast(), local_to_window, local_to_window }; +} + +void mwindow::arrange_children(const geometry_t& in_allotted_geometry, arranged_children& in_arranged_children) { + if (content_widget_) { + auto child_geo = in_allotted_geometry.make_child({0, 0}, get_window_frame_size().cast()); + in_arranged_children.add_widget(arranged_widget(child_geo, content_widget_)); + } +} + +void mwindow::set_content(const std::shared_ptr& in_widget) { + content_widget_ = in_widget; + layout_tree_.invalidate_layout(); +} + +std::vector> mwindow::get_children() const { + if (content_widget_) + return { content_widget_ }; + return mwidget::get_children(); +} + +void mwindow::on_resize(int width, int height) { + const Eigen::Vector2i size(width, height); + state_->swapchain.width = width; + state_->swapchain.height = height; + + paint_context_.update_projection_matrix(size); + + transform2d identity; + geometry_t new_geometry(size.cast(), identity, identity); + layout_tree_.set_root_geometry(new_geometry); + layout_tree_.invalidate_layout(); +} + +void mwindow::rebuild_swapchain() { + state_->rebuild_swapchain(get_window_frame_size()); +} + +void mwindow::on_move(int x, int y) { +} + +void mwindow::init_window() { + set_window(this); + layout_tree_.set_root(shared_from_this()); + layout_tree_.set_root_geometry(get_window_geometry_in_window()); +} + +void mwindow::on_paint(mirage_paint_context& in_context) { + if (content_widget_) + return content_widget_->on_paint(in_context); +} + +void mwindow::handle_mouse_move(const Eigen::Vector2f& in_window_pos) { + // 执行悬停测试 + layout_tree_.process_mouse_move(in_window_pos); +} + +void mwindow::handle_mouse_press(const Eigen::Vector2f& in_window_pos, mouse_button in_button) { + layout_tree_.process_mouse_press(in_window_pos, in_button); +} + +void mwindow::handle_mouse_release(const Eigen::Vector2f& in_window_pos, mouse_button in_button) { + layout_tree_.process_mouse_release(in_window_pos, in_button); +} + +void mwindow::handle_mouse_leave() { + layout_tree_.on_mouse_leave_window(); +} + + diff --git a/src/core/window/render_window.h b/src/core/window/mwindow.h similarity index 83% rename from src/core/window/render_window.h rename to src/core/window/mwindow.h index b0f569e..ae8072c 100644 --- a/src/core/window/render_window.h +++ b/src/core/window/mwindow.h @@ -1,6 +1,6 @@ #pragma once /** - * @file mirage_window.h + * @file render_window.h * @brief 定义UI系统的窗口类 * * 本文件定义了mirage_window类,作为UI系统的窗口容器,管理窗口的创建、 @@ -14,7 +14,9 @@ #include "geometry/geometry.h" #include "geometry/layout_transform.h" #include "geometry/widget_layout_tree.h" +#include "misc/key_type/key_type.h" #include "widget/compound_widget/mcompound_widget.h" +#include "widget/hit_test/hit_test_manager.h" /** * @struct mirage_window_state @@ -33,17 +35,22 @@ struct mirage_window_state { clear(); } + /** + * @brief 清理资源 + */ + void clear() { on_clear(); } + /** 顶点缓冲区 */ - sg_buffer buffer; + sg_buffer buffer{}; /** 窗口交换链 */ - sg_swapchain swapchain; + sg_swapchain swapchain{}; /** 渲染绑定 */ - sg_bindings bindings; + sg_bindings bindings{}; /** 渲染管线 */ - sg_pipeline pipeline; + sg_pipeline pipeline{}; /** 垂直同步标志 */ bool vsync = true; @@ -53,7 +60,7 @@ struct mirage_window_state { * * 虚方法,由派生类实现以释放平台特定资源。 */ - virtual void clear() {} + virtual void on_clear() {} /** * @brief 呈现渲染内容 @@ -72,13 +79,13 @@ struct mirage_window_state { }; /** - * @class mirage_window + * @class mwindow * @brief UI系统的窗口类 * * 表示一个可以包含UI组件的窗口。提供窗口创建、操作、样式设置和内容布局等功能。 * 作为一个组件容器,窗口本身也是一个UI组件。 */ -class mirage_window : public mwidget { +class mwindow : public mwidget { public: //-------------- 窗口创建和基本操作 -------------- @@ -183,56 +190,56 @@ public: * @param title 新标题 * @return 窗口对象引用,用于链式调用 */ - mirage_window& set_title(const wchar_t* title); + mwindow& set_title(const wchar_t* title); /** * @brief 设置是否有最小化按钮 * @param has_minimize_button 是否有最小化按钮 * @return 窗口对象引用,用于链式调用 */ - mirage_window& set_has_minimize_button(bool has_minimize_button); + mwindow& set_has_minimize_button(bool has_minimize_button); /** * @brief 设置是否有最大化按钮 * @param has_maximize_button 是否有最大化按钮 * @return 窗口对象引用,用于链式调用 */ - mirage_window& set_has_maximize_button(bool has_maximize_button); + mwindow& set_has_maximize_button(bool has_maximize_button); /** * @brief 设置是否有关闭按钮 * @param has_close_button 是否有关闭按钮 * @return 窗口对象引用,用于链式调用 */ - mirage_window& set_has_close_button(bool has_close_button); + mwindow& set_has_close_button(bool has_close_button); /** * @brief 设置是否有边框 * @param has_border 是否有边框 * @return 窗口对象引用,用于链式调用 */ - mirage_window& set_has_border(bool has_border); + mwindow& set_has_border(bool has_border); /** * @brief 设置是否有标题栏 * @param has_caption 是否有标题栏 * @return 窗口对象引用,用于链式调用 */ - mirage_window& set_has_caption(bool has_caption); + mwindow& set_has_caption(bool has_caption); /** * @brief 设置是否有可调整大小的边框 * @param has_resizable_border 是否有可调整大小的边框 * @return 窗口对象引用,用于链式调用 */ - mirage_window& set_has_resizable_border(bool has_resizable_border); + mwindow& set_has_resizable_border(bool has_resizable_border); /** * @brief 设置窗口是否总在最前 * @param is_topmost 是否总在最前 * @return 窗口对象引用,用于链式调用 */ - mirage_window& set_topmost(bool is_topmost); + mwindow& set_topmost(bool is_topmost); //-------------- 事件处理 -------------- @@ -242,6 +249,31 @@ public: */ [[nodiscard]] bool close_requested() const { return close_request_; } + /** + * @brief 处理鼠标移动事件 + * @param in_window_pos 窗口位置 + */ + void handle_mouse_move(const Eigen::Vector2f& in_window_pos); + + /** + * @brief 处理鼠标按下事件 + * @param in_window_pos 窗口位置 + * @param in_button 按下的按钮 + */ + void handle_mouse_press(const Eigen::Vector2f& in_window_pos, mouse_button in_button); + + /** + * @brief 处理鼠标释放事件 + * @param in_window_pos 窗口位置 + * @param in_button 释放的按钮 + */ + void handle_mouse_release(const Eigen::Vector2f& in_window_pos, mouse_button in_button); + + /** + * @brief 处理光标移出窗口事件 + */ + void handle_mouse_leave(); + /** * @brief 轮询所有窗口的事件 * @return 如果有活动窗口则返回true @@ -256,7 +288,7 @@ public: * * 静态方法,返回所有已创建窗口的列表。 */ - static const std::vector& get_windows(); + static const std::vector& get_windows(); /** * @brief 处理窗口大小改变事件 @@ -315,7 +347,7 @@ public: * @return 变换矩阵 */ [[nodiscard]] auto get_local_to_window_transform() const { - return transform2d({}, 0, { dpi_helper::get_global_scale() * get_dpi_scale(), dpi_helper::get_global_scale() * get_dpi_scale() }); + return transform2d({0, 0}, 0, { dpi_helper::get_global_scale() * get_dpi_scale(), dpi_helper::get_global_scale() * get_dpi_scale() }); } /** @@ -324,6 +356,11 @@ public: */ [[nodiscard]] geometry_t get_window_geometry_in_screen() const; + /** + * @brief 获取窗口在窗口中的几何信息 + * @return 窗口几何信息 + */ + [[nodiscard]] geometry_t get_window_geometry_in_window() const; //-------------- UI组件覆盖方法 -------------- /** @@ -393,5 +430,5 @@ private: std::shared_ptr content_widget_; /** 布局树 */ - widget_layout_tree layout_tree_; + widget_layout_tree layout_tree_{}; }; diff --git a/src/core/window/render_window.cpp b/src/core/window/render_window.cpp deleted file mode 100644 index ec9a1bf..0000000 --- a/src/core/window/render_window.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include "render_window.h" - -#include "widget/mwidget.h" - -void mirage_window::on_paint() { - if (!content_widget_) - return; - - paint_context_.begin_frame(this); - layout_tree_.paint(paint_context_); - paint_context_.end_frame(); -} - -geometry_t mirage_window::get_window_geometry_in_screen() const { - auto local_to_screen = get_local_to_window_transform(); - geometry_t root_geometry(get_window_frame_size().cast(), local_to_screen, transform2d()); - return root_geometry; -} - -void mirage_window::arrange_children(const geometry_t& in_allotted_geometry, arranged_children& in_arranged_children) { - if (content_widget_) { - auto child_geo = in_allotted_geometry.make_child({0, 0}, get_window_frame_size().cast()); - in_arranged_children.add_widget(arranged_widget(child_geo, content_widget_)); - } -} - -void mirage_window::set_content(const std::shared_ptr& in_widget) { - content_widget_ = in_widget; - layout_tree_.invalidate_layout(); -} - -std::vector> mirage_window::get_children() const { - if (content_widget_) - return { content_widget_ }; - return mwidget::get_children(); -} - -void mirage_window::on_resize(int width, int height) { - const Eigen::Vector2i size(width, height); - state_->swapchain.width = width; - state_->swapchain.height = height; - - paint_context_.update_projection_matrix(size); - - transform2d identity; - geometry_t new_geometry(size.cast(), identity, identity); - layout_tree_.set_root_geometry(new_geometry); - layout_tree_.invalidate_layout(); -} - -void mirage_window::rebuild_swapchain() { - state_->rebuild_swapchain(get_window_frame_size()); -} - -void mirage_window::on_move(int x, int y) { -} - -void mirage_window::init_window() { - set_window(this); - layout_tree_.set_root(shared_from_this()); - transform2d identity{}; - geometry_t root_geometry(get_window_frame_size().cast(), identity, identity); - layout_tree_.set_root_geometry(root_geometry); -} - -void mirage_window::on_paint(mirage_paint_context& in_context) { - if (content_widget_) - return content_widget_->on_paint(in_context); -} diff --git a/src/core/window/windows/pixel_format_convert.h b/src/core/window/windows/pixel_format_convert.h index 76d37dc..bbe7b42 100644 --- a/src/core/window/windows/pixel_format_convert.h +++ b/src/core/window/windows/pixel_format_convert.h @@ -4,63 +4,119 @@ inline DXGI_FORMAT sg_pixel_format_to_dxgi(sg_pixel_format fmt) { switch (fmt) { - case SG_PIXELFORMAT_R8: return DXGI_FORMAT_R8_UNORM; - case SG_PIXELFORMAT_R8SN: return DXGI_FORMAT_R8_SNORM; - case SG_PIXELFORMAT_R8UI: return DXGI_FORMAT_R8_UINT; - case SG_PIXELFORMAT_R8SI: return DXGI_FORMAT_R8_SINT; - case SG_PIXELFORMAT_R16: return DXGI_FORMAT_R16_UNORM; - case SG_PIXELFORMAT_R16SN: return DXGI_FORMAT_R16_SNORM; - case SG_PIXELFORMAT_R16UI: return DXGI_FORMAT_R16_UINT; - case SG_PIXELFORMAT_R16SI: return DXGI_FORMAT_R16_SINT; - case SG_PIXELFORMAT_R16F: return DXGI_FORMAT_R16_FLOAT; - case SG_PIXELFORMAT_RG8: return DXGI_FORMAT_R8G8_UNORM; - case SG_PIXELFORMAT_RG8SN: return DXGI_FORMAT_R8G8_SNORM; - case SG_PIXELFORMAT_RG8UI: return DXGI_FORMAT_R8G8_UINT; - case SG_PIXELFORMAT_RG8SI: return DXGI_FORMAT_R8G8_SINT; - case SG_PIXELFORMAT_R32UI: return DXGI_FORMAT_R32_UINT; - case SG_PIXELFORMAT_R32SI: return DXGI_FORMAT_R32_SINT; - case SG_PIXELFORMAT_R32F: return DXGI_FORMAT_R32_FLOAT; - case SG_PIXELFORMAT_RG16: return DXGI_FORMAT_R16G16_UNORM; - case SG_PIXELFORMAT_RG16SN: return DXGI_FORMAT_R16G16_SNORM; - case SG_PIXELFORMAT_RG16UI: return DXGI_FORMAT_R16G16_UINT; - case SG_PIXELFORMAT_RG16SI: return DXGI_FORMAT_R16G16_SINT; - case SG_PIXELFORMAT_RG16F: return DXGI_FORMAT_R16G16_FLOAT; - case SG_PIXELFORMAT_RGBA8: return DXGI_FORMAT_R8G8B8A8_UNORM; - case SG_PIXELFORMAT_SRGB8A8: return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; - case SG_PIXELFORMAT_RGBA8SN: return DXGI_FORMAT_R8G8B8A8_SNORM; - case SG_PIXELFORMAT_RGBA8UI: return DXGI_FORMAT_R8G8B8A8_UINT; - case SG_PIXELFORMAT_RGBA8SI: return DXGI_FORMAT_R8G8B8A8_SINT; - case SG_PIXELFORMAT_BGRA8: return DXGI_FORMAT_B8G8R8A8_UNORM; - case SG_PIXELFORMAT_RGB10A2: return DXGI_FORMAT_R10G10B10A2_UNORM; - case SG_PIXELFORMAT_RG11B10F: return DXGI_FORMAT_R11G11B10_FLOAT; - case SG_PIXELFORMAT_RGB9E5: return DXGI_FORMAT_R9G9B9E5_SHAREDEXP; - case SG_PIXELFORMAT_RG32UI: return DXGI_FORMAT_R32G32_UINT; - case SG_PIXELFORMAT_RG32SI: return DXGI_FORMAT_R32G32_SINT; - case SG_PIXELFORMAT_RG32F: return DXGI_FORMAT_R32G32_FLOAT; - case SG_PIXELFORMAT_RGBA16: return DXGI_FORMAT_R16G16B16A16_UNORM; - case SG_PIXELFORMAT_RGBA16SN: return DXGI_FORMAT_R16G16B16A16_SNORM; - case SG_PIXELFORMAT_RGBA16UI: return DXGI_FORMAT_R16G16B16A16_UINT; - case SG_PIXELFORMAT_RGBA16SI: return DXGI_FORMAT_R16G16B16A16_SINT; - case SG_PIXELFORMAT_RGBA16F: return DXGI_FORMAT_R16G16B16A16_FLOAT; - case SG_PIXELFORMAT_RGBA32UI: return DXGI_FORMAT_R32G32B32A32_UINT; - case SG_PIXELFORMAT_RGBA32SI: return DXGI_FORMAT_R32G32B32A32_SINT; - case SG_PIXELFORMAT_RGBA32F: return DXGI_FORMAT_R32G32B32A32_FLOAT; - case SG_PIXELFORMAT_DEPTH: return DXGI_FORMAT_R32_TYPELESS; - case SG_PIXELFORMAT_DEPTH_STENCIL: return DXGI_FORMAT_R24G8_TYPELESS; - case SG_PIXELFORMAT_BC1_RGBA: return DXGI_FORMAT_BC1_UNORM; - case SG_PIXELFORMAT_BC2_RGBA: return DXGI_FORMAT_BC2_UNORM; - case SG_PIXELFORMAT_BC3_RGBA: return DXGI_FORMAT_BC3_UNORM; - case SG_PIXELFORMAT_BC3_SRGBA: return DXGI_FORMAT_BC3_UNORM_SRGB; - case SG_PIXELFORMAT_BC4_R: return DXGI_FORMAT_BC4_UNORM; - case SG_PIXELFORMAT_BC4_RSN: return DXGI_FORMAT_BC4_SNORM; - case SG_PIXELFORMAT_BC5_RG: return DXGI_FORMAT_BC5_UNORM; - case SG_PIXELFORMAT_BC5_RGSN: return DXGI_FORMAT_BC5_SNORM; - case SG_PIXELFORMAT_BC6H_RGBF: return DXGI_FORMAT_BC6H_SF16; - case SG_PIXELFORMAT_BC6H_RGBUF: return DXGI_FORMAT_BC6H_UF16; - case SG_PIXELFORMAT_BC7_RGBA: return DXGI_FORMAT_BC7_UNORM; - case SG_PIXELFORMAT_BC7_SRGBA: return DXGI_FORMAT_BC7_UNORM_SRGB; - default: return DXGI_FORMAT_UNKNOWN; - }; + case SG_PIXELFORMAT_R8: + return DXGI_FORMAT_R8_UNORM; + case SG_PIXELFORMAT_R8SN: + return DXGI_FORMAT_R8_SNORM; + case SG_PIXELFORMAT_R8UI: + return DXGI_FORMAT_R8_UINT; + case SG_PIXELFORMAT_R8SI: + return DXGI_FORMAT_R8_SINT; + case SG_PIXELFORMAT_R16: + return DXGI_FORMAT_R16_UNORM; + case SG_PIXELFORMAT_R16SN: + return DXGI_FORMAT_R16_SNORM; + case SG_PIXELFORMAT_R16UI: + return DXGI_FORMAT_R16_UINT; + case SG_PIXELFORMAT_R16SI: + return DXGI_FORMAT_R16_SINT; + case SG_PIXELFORMAT_R16F: + return DXGI_FORMAT_R16_FLOAT; + case SG_PIXELFORMAT_RG8: + return DXGI_FORMAT_R8G8_UNORM; + case SG_PIXELFORMAT_RG8SN: + return DXGI_FORMAT_R8G8_SNORM; + case SG_PIXELFORMAT_RG8UI: + return DXGI_FORMAT_R8G8_UINT; + case SG_PIXELFORMAT_RG8SI: + return DXGI_FORMAT_R8G8_SINT; + case SG_PIXELFORMAT_R32UI: + return DXGI_FORMAT_R32_UINT; + case SG_PIXELFORMAT_R32SI: + return DXGI_FORMAT_R32_SINT; + case SG_PIXELFORMAT_R32F: + return DXGI_FORMAT_R32_FLOAT; + case SG_PIXELFORMAT_RG16: + return DXGI_FORMAT_R16G16_UNORM; + case SG_PIXELFORMAT_RG16SN: + return DXGI_FORMAT_R16G16_SNORM; + case SG_PIXELFORMAT_RG16UI: + return DXGI_FORMAT_R16G16_UINT; + case SG_PIXELFORMAT_RG16SI: + return DXGI_FORMAT_R16G16_SINT; + case SG_PIXELFORMAT_RG16F: + return DXGI_FORMAT_R16G16_FLOAT; + case SG_PIXELFORMAT_RGBA8: + return DXGI_FORMAT_R8G8B8A8_UNORM; + case SG_PIXELFORMAT_SRGB8A8: + return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; + case SG_PIXELFORMAT_RGBA8SN: + return DXGI_FORMAT_R8G8B8A8_SNORM; + case SG_PIXELFORMAT_RGBA8UI: + return DXGI_FORMAT_R8G8B8A8_UINT; + case SG_PIXELFORMAT_RGBA8SI: + return DXGI_FORMAT_R8G8B8A8_SINT; + case SG_PIXELFORMAT_BGRA8: + return DXGI_FORMAT_B8G8R8A8_UNORM; + case SG_PIXELFORMAT_RGB10A2: + return DXGI_FORMAT_R10G10B10A2_UNORM; + case SG_PIXELFORMAT_RG11B10F: + return DXGI_FORMAT_R11G11B10_FLOAT; + case SG_PIXELFORMAT_RGB9E5: + return DXGI_FORMAT_R9G9B9E5_SHAREDEXP; + case SG_PIXELFORMAT_RG32UI: + return DXGI_FORMAT_R32G32_UINT; + case SG_PIXELFORMAT_RG32SI: + return DXGI_FORMAT_R32G32_SINT; + case SG_PIXELFORMAT_RG32F: + return DXGI_FORMAT_R32G32_FLOAT; + case SG_PIXELFORMAT_RGBA16: + return DXGI_FORMAT_R16G16B16A16_UNORM; + case SG_PIXELFORMAT_RGBA16SN: + return DXGI_FORMAT_R16G16B16A16_SNORM; + case SG_PIXELFORMAT_RGBA16UI: + return DXGI_FORMAT_R16G16B16A16_UINT; + case SG_PIXELFORMAT_RGBA16SI: + return DXGI_FORMAT_R16G16B16A16_SINT; + case SG_PIXELFORMAT_RGBA16F: + return DXGI_FORMAT_R16G16B16A16_FLOAT; + case SG_PIXELFORMAT_RGBA32UI: + return DXGI_FORMAT_R32G32B32A32_UINT; + case SG_PIXELFORMAT_RGBA32SI: + return DXGI_FORMAT_R32G32B32A32_SINT; + case SG_PIXELFORMAT_RGBA32F: + return DXGI_FORMAT_R32G32B32A32_FLOAT; + case SG_PIXELFORMAT_DEPTH: + return DXGI_FORMAT_R32_TYPELESS; + case SG_PIXELFORMAT_DEPTH_STENCIL: + return DXGI_FORMAT_R24G8_TYPELESS; + case SG_PIXELFORMAT_BC1_RGBA: + return DXGI_FORMAT_BC1_UNORM; + case SG_PIXELFORMAT_BC2_RGBA: + return DXGI_FORMAT_BC2_UNORM; + case SG_PIXELFORMAT_BC3_RGBA: + return DXGI_FORMAT_BC3_UNORM; + case SG_PIXELFORMAT_BC3_SRGBA: + return DXGI_FORMAT_BC3_UNORM_SRGB; + case SG_PIXELFORMAT_BC4_R: + return DXGI_FORMAT_BC4_UNORM; + case SG_PIXELFORMAT_BC4_RSN: + return DXGI_FORMAT_BC4_SNORM; + case SG_PIXELFORMAT_BC5_RG: + return DXGI_FORMAT_BC5_UNORM; + case SG_PIXELFORMAT_BC5_RGSN: + return DXGI_FORMAT_BC5_SNORM; + case SG_PIXELFORMAT_BC6H_RGBF: + return DXGI_FORMAT_BC6H_SF16; + case SG_PIXELFORMAT_BC6H_RGBUF: + return DXGI_FORMAT_BC6H_UF16; + case SG_PIXELFORMAT_BC7_RGBA: + return DXGI_FORMAT_BC7_UNORM; + case SG_PIXELFORMAT_BC7_SRGBA: + return DXGI_FORMAT_BC7_UNORM_SRGB; + default: + return DXGI_FORMAT_UNKNOWN; + } } inline DXGI_FORMAT sg_pixel_format_to_dxgi_srv(sg_pixel_format fmt) { diff --git a/src/core/window/windows/windows_render_context.cpp b/src/core/window/windows/windows_render_context.cpp index 1ad8821..cb16168 100644 --- a/src/core/window/windows/windows_render_context.cpp +++ b/src/core/window/windows/windows_render_context.cpp @@ -8,7 +8,7 @@ #include #include "misc/angle_literals.h" -#include "core/window/render_window.h" +#include "core/window/mwindow.h" #include #include "windows_window_state.h" @@ -169,14 +169,11 @@ bool windows_mirage_render_context::init() { // 输出驱动类型信息 auto driver_type_str = "Unknown"; switch (used_driver_type) { - case D3D_DRIVER_TYPE_HARDWARE: - driver_type_str = "Hardware"; + case D3D_DRIVER_TYPE_HARDWARE: driver_type_str = "Hardware"; break; - case D3D_DRIVER_TYPE_WARP: - driver_type_str = "WARP"; + case D3D_DRIVER_TYPE_WARP: driver_type_str = "WARP"; break; - case D3D_DRIVER_TYPE_REFERENCE: - driver_type_str = "Reference"; + case D3D_DRIVER_TYPE_REFERENCE: driver_type_str = "Reference"; break; default: ; } @@ -185,17 +182,13 @@ bool windows_mirage_render_context::init() { // 输出特性级别信息 auto feature_level_str = "Unknown"; switch (feature_level) { - case D3D_FEATURE_LEVEL_11_1: - feature_level_str = "11.1"; + case D3D_FEATURE_LEVEL_11_1: feature_level_str = "11.1"; break; - case D3D_FEATURE_LEVEL_11_0: - feature_level_str = "11.0"; + case D3D_FEATURE_LEVEL_11_0: feature_level_str = "11.0"; break; - case D3D_FEATURE_LEVEL_10_1: - feature_level_str = "10.1"; + case D3D_FEATURE_LEVEL_10_1: feature_level_str = "10.1"; break; - case D3D_FEATURE_LEVEL_10_0: - feature_level_str = "10.0"; + case D3D_FEATURE_LEVEL_10_0: feature_level_str = "10.0"; break; default: ; } @@ -229,7 +222,7 @@ void windows_mirage_render_context::cleanup() { } void windows_mirage_render_context::tick(const duration_type& in_delta) { - const auto& windows = mirage_window::get_windows(); + const auto& windows = mwindow::get_windows(); for (const auto& window: windows) { auto& window_state = window->get_state(); sg_pass pass{}; @@ -262,7 +255,7 @@ sg_environment windows_mirage_render_context::get_environment() { }; } -bool windows_mirage_render_context::setup_surface(mirage_window* in_window) { +bool windows_mirage_render_context::setup_surface(mwindow* in_window) { auto state = std::make_unique(); if (!state->init(device, dxgi_factory, in_window)) { return false; } state->set_tearing_support(tearing_supported); diff --git a/src/core/window/windows/windows_render_context.h b/src/core/window/windows/windows_render_context.h index e1c2109..f9e4daf 100644 --- a/src/core/window/windows/windows_render_context.h +++ b/src/core/window/windows/windows_render_context.h @@ -75,7 +75,7 @@ public: * 为指定窗口创建交换链和渲染目标视图, * 建立Direct3D与窗口表面的连接。 */ - bool setup_surface(mirage_window* in_window) override; + bool setup_surface(mwindow* in_window) override; private: /** Direct3D 11设备 */ diff --git a/src/core/window/windows/windows_render_window.cpp b/src/core/window/windows/windows_render_window.cpp index 5575b97..7ff94ea 100644 --- a/src/core/window/windows/windows_render_window.cpp +++ b/src/core/window/windows/windows_render_window.cpp @@ -3,13 +3,13 @@ // #include -#include "core/window/render_window.h" +#include "core/window/mwindow.h" #include #define WINDOW_HANDLE static_cast(window_handle_) -std::vector windows; -mirage_window* get_window_from_hwnd(HWND hwnd) { +std::vector windows; +mwindow* get_window_from_hwnd(const HWND hwnd) { for (const auto& window: windows) { if (window->get_window_handle() == hwnd) { return window; @@ -19,36 +19,66 @@ mirage_window* get_window_from_hwnd(HWND hwnd) { } LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { - switch (uMsg) { - case WM_CLOSE: - if (auto window = get_window_from_hwnd(hwnd)) { - window->close(); - } - std::erase_if(windows, [hwnd](const mirage_window* window) { return window->get_window_handle() == hwnd; }); - return 0; - case WM_DESTROY: - PostQuitMessage(0); - return 0; - case WM_MOVE: - if (auto window = get_window_from_hwnd(hwnd)) { - window->on_move(LOWORD(lParam), HIWORD(lParam)); - } - return 0; - case WM_SIZE: - if (auto window = get_window_from_hwnd(hwnd)) { - // 只记录新的尺寸,不重建交换链 - window->on_resize(LOWORD(lParam), HIWORD(lParam)); - - // 特殊情况:最大化、最小化、恢复时立即处理 - if (wParam == SIZE_MAXIMIZED || wParam == SIZE_RESTORED) { window->rebuild_swapchain(); } - } - return 0; - default: - return DefWindowProc(hwnd, uMsg, wParam, lParam); + bool processed = false; + if (uMsg == WM_CLOSE) { + if (const auto window = get_window_from_hwnd(hwnd)) { window->close(); } + std::erase_if(windows, [hwnd](const mwindow* window) { return window->get_window_handle() == hwnd; }); + processed = true; } + if (uMsg == WM_DESTROY) { + PostQuitMessage(0); + processed = true; + } + if (uMsg == WM_MOVE) { + if (const auto window = get_window_from_hwnd(hwnd)) { window->on_move(LOWORD(lParam), HIWORD(lParam)); } + processed = true; + } + // if (uMsg == WM_PAINT) { + // if (const auto window = get_window_from_hwnd(hwnd)) { window->on_paint(); } + // return 0; + // } + if (uMsg == WM_SIZE) { + if (const auto window = get_window_from_hwnd(hwnd)) { + window->on_resize(LOWORD(lParam), HIWORD(lParam)); + if (wParam == SIZE_MAXIMIZED || wParam == SIZE_RESTORED) { window->rebuild_swapchain(); } + } + processed = true; + } + if (uMsg == WM_MOUSEMOVE) { + if (const auto window = get_window_from_hwnd(hwnd)) { + const int x = LOWORD(lParam); + const int y = HIWORD(lParam); + const Eigen::Vector2f pos(static_cast(x), static_cast(y)); + window->handle_mouse_move(pos); + } + processed = true; + } + if (uMsg >= WM_LBUTTONDOWN && uMsg <= WM_MBUTTONDBLCLK) { + if (const auto window = get_window_from_hwnd(hwnd)) { + const int x = LOWORD(lParam); + const int y = HIWORD(lParam); + const Eigen::Vector2f pos(static_cast(x), static_cast(y)); + const auto action = platform_event_to_mouse_action(uMsg, wParam); + const auto button = platform_event_to_mouse_button(uMsg, wParam); + if (action == mouse_action::press) + window->handle_mouse_press(pos, button); + if (action == mouse_action::release) + window->handle_mouse_release(pos, button); + } + processed = true; + } + if (uMsg == WM_MOUSELEAVE) { + if (const auto window = get_window_from_hwnd(hwnd)) { + window->handle_mouse_leave(); + } + processed = true; + } + + if (processed) { return 0; } + return DefWindowProc(hwnd, uMsg, wParam, lParam); } -bool mirage_window::create_window(int width, int height, const wchar_t* title) { +bool mwindow::create_window(int width, int height, const wchar_t* title) { WNDCLASS wc = {}; wc.lpfnWndProc = WindowProc; wc.hInstance = GetModuleHandle(nullptr); @@ -81,18 +111,18 @@ bool mirage_window::create_window(int width, int height, const wchar_t* title) { return true; } -void mirage_window::show() { ShowWindow(WINDOW_HANDLE, SW_SHOW); } -void mirage_window::hide() { ShowWindow(WINDOW_HANDLE, SW_HIDE); } +void mwindow::show() { ShowWindow(WINDOW_HANDLE, SW_SHOW); } +void mwindow::hide() { ShowWindow(WINDOW_HANDLE, SW_HIDE); } -void mirage_window::close() { +void mwindow::close() { close_request_ = true; DestroyWindow(WINDOW_HANDLE); } -void mirage_window::maximize() { ShowWindow(WINDOW_HANDLE, SW_MAXIMIZE); } -void mirage_window::minimize() { ShowWindow(WINDOW_HANDLE, SW_MINIMIZE); } +void mwindow::maximize() { ShowWindow(WINDOW_HANDLE, SW_MAXIMIZE); } +void mwindow::minimize() { ShowWindow(WINDOW_HANDLE, SW_MINIMIZE); } -void mirage_window::move(int x, int y) { +void mwindow::move(int x, int y) { RECT rect; GetWindowRect(WINDOW_HANDLE, &rect); MoveWindow(WINDOW_HANDLE, x, y, rect.right - rect.left, rect.bottom - rect.top, TRUE); @@ -100,13 +130,13 @@ void mirage_window::move(int x, int y) { on_move(x, y); } -void mirage_window::resize(int width, int height) { +void mwindow::resize(int width, int height) { RECT rect; GetWindowRect(WINDOW_HANDLE, &rect); MoveWindow(WINDOW_HANDLE, rect.left, rect.top, width, height, TRUE); } -Eigen::Vector2i mirage_window::get_window_size() const { +Eigen::Vector2i mwindow::get_window_size() const { // 获取窗口大小, 包括边框和标题栏 RECT rect; if (GetWindowRect(WINDOW_HANDLE, &rect)) { @@ -117,7 +147,7 @@ Eigen::Vector2i mirage_window::get_window_size() const { return Eigen::Vector2i(0, 0); } -Eigen::Vector2i mirage_window::get_window_frame_size() const { +Eigen::Vector2i mwindow::get_window_frame_size() const { // 获取窗口大小, 不包括边框和标题栏 (客户区大小) RECT rect; if (GetClientRect(WINDOW_HANDLE, &rect)) { @@ -128,73 +158,73 @@ Eigen::Vector2i mirage_window::get_window_frame_size() const { return Eigen::Vector2i(0, 0); } -float mirage_window::get_dpi_scale() const { +float mwindow::get_dpi_scale() const { return 1.f; } -Eigen::Vector2i mirage_window::get_window_position() const { +Eigen::Vector2i mwindow::get_window_position() const { RECT rect; if (!GetWindowRect(WINDOW_HANDLE, &rect)) { return {}; } return { rect.left, rect.top }; } -void* mirage_window::get_window_handle() const { return window_handle_; } +void* mwindow::get_window_handle() const { return window_handle_; } -mirage_window& mirage_window::set_title(const wchar_t* title) { +mwindow& mwindow::set_title(const wchar_t* title) { SetWindowText(WINDOW_HANDLE, title); return *this; } -mirage_window& mirage_window::set_has_minimize_button(bool has_minimize_button) { +mwindow& mwindow::set_has_minimize_button(bool has_minimize_button) { auto style = GetWindowLong(WINDOW_HANDLE, GWL_STYLE); if (has_minimize_button) { style |= WS_MINIMIZEBOX; } else { style &= ~WS_MINIMIZEBOX; } SetWindowLong(WINDOW_HANDLE, GWL_STYLE, style); return *this; } -mirage_window& mirage_window::set_has_maximize_button(bool has_maximize_button) { +mwindow& mwindow::set_has_maximize_button(bool has_maximize_button) { auto style = GetWindowLong(WINDOW_HANDLE, GWL_STYLE); if (has_maximize_button) { style |= WS_MAXIMIZEBOX; } else { style &= ~WS_MAXIMIZEBOX; } SetWindowLong(WINDOW_HANDLE, GWL_STYLE, style); return *this; } -mirage_window& mirage_window::set_has_close_button(bool has_close_button) { +mwindow& mwindow::set_has_close_button(bool has_close_button) { auto style = GetWindowLong(WINDOW_HANDLE, GWL_STYLE); if (has_close_button) { style |= WS_SYSMENU; } else { style &= ~WS_SYSMENU; } SetWindowLong(WINDOW_HANDLE, GWL_STYLE, style); return *this; } -mirage_window& mirage_window::set_has_border(bool has_border) { +mwindow& mwindow::set_has_border(bool has_border) { auto style = GetWindowLong(WINDOW_HANDLE, GWL_STYLE); if (has_border) { style |= WS_BORDER; } else { style &= ~WS_BORDER; } SetWindowLong(WINDOW_HANDLE, GWL_STYLE, style); return *this; } -mirage_window& mirage_window::set_has_caption(bool has_caption) { +mwindow& mwindow::set_has_caption(bool has_caption) { auto style = GetWindowLong(WINDOW_HANDLE, GWL_STYLE); if (has_caption) { style |= WS_CAPTION; } else { style &= ~WS_CAPTION; } SetWindowLong(WINDOW_HANDLE, GWL_STYLE, style); return *this; } -mirage_window& mirage_window::set_has_resizable_border(bool has_resizable_border) { +mwindow& mwindow::set_has_resizable_border(bool has_resizable_border) { auto style = GetWindowLong(WINDOW_HANDLE, GWL_STYLE); if (has_resizable_border) { style |= WS_THICKFRAME; } else { style &= ~WS_THICKFRAME; } SetWindowLong(WINDOW_HANDLE, GWL_STYLE, style); return *this; } -mirage_window& mirage_window::set_topmost(bool is_topmost) { +mwindow& mwindow::set_topmost(bool is_topmost) { auto style = GetWindowLong(WINDOW_HANDLE, GWL_EXSTYLE); if (is_topmost) { style |= WS_EX_TOPMOST; } else { style &= ~WS_EX_TOPMOST; } SetWindowLong(WINDOW_HANDLE, GWL_EXSTYLE, style); return *this; } -bool mirage_window::poll_events() { +bool mwindow::poll_events() { MSG msg; bool has_messages = false; while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { @@ -205,4 +235,4 @@ bool mirage_window::poll_events() { return has_messages; } -const std::vector& mirage_window::get_windows() { return windows; } +const std::vector& mwindow::get_windows() { return windows; } diff --git a/src/core/window/windows/windows_window_state.cpp b/src/core/window/windows/windows_window_state.cpp index 0d6bdfb..dfc9585 100644 --- a/src/core/window/windows/windows_window_state.cpp +++ b/src/core/window/windows/windows_window_state.cpp @@ -1,290 +1,327 @@ +/** + * @file windows_window_state.cpp + * @brief Windows平台特定的窗口渲染状态实现 + * + * 实现了Windows平台下窗口渲染状态的具体功能,包括Direct3D 11 + * 交换链的创建、管理、重建以及呈现操作。处理与窗口大小改变 + * 相关的资源管理和垂直同步设置。 + */ + #include "windows_window_state.h" #include #include "pixel_format_convert.h" #include "misc/scope_exit.h" -bool windows_window_state::init(ID3D11Device* in_device, IDXGIFactory* in_factory, mirage_window* in_window) { - dx_device = in_device; - dxgi_factory = in_factory; +/** + * @brief 初始化窗口渲染状态 + * @param in_device Direct3D 11设备 + * @param in_factory DXGI工厂 + * @param in_window 关联的窗口 + * @return 初始化是否成功 + * + * 创建交换链和渲染目标视图,并配置sokol图形库需要的交换链信息。 + * 使用窗口的当前大小和句柄设置交换链参数。 + */ +bool windows_window_state::init(ID3D11Device* in_device, IDXGIFactory* in_factory, mwindow* in_window) { + dx_device = in_device; + dxgi_factory = in_factory; - const auto size = in_window->get_window_frame_size(); - const auto window_handle = in_window->get_window_handle(); + const auto size = in_window->get_window_frame_size(); + const auto window_handle = in_window->get_window_handle(); #if MIRAGE_USE_HDR - constexpr auto sg_format = MIRAGE_HDR_FORMAT; + constexpr auto sg_format = MIRAGE_HDR_FORMAT; #else - constexpr auto sg_format = MIRAGE_PIXEL_FORMAT; + constexpr auto sg_format = MIRAGE_PIXEL_FORMAT; #endif - const auto format = sg_pixel_format_to_dxgi_srv(sg_format); + const auto format = sg_pixel_format_to_dxgi_srv(sg_format); - // 创建D3D11渲染目标视图 - DXGI_SWAP_CHAIN_DESC swap_chain_desc = { - .BufferDesc = { - .Width = static_cast(size.x()), - .Height = static_cast(size.y()), - .RefreshRate = { - .Numerator = 240, - .Denominator = 1 - }, - .Format = format, - }, - .SampleDesc = { - .Count = 1, - .Quality = 0 - }, - .BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT, - .BufferCount = 2, - .OutputWindow = (HWND) window_handle, - .Windowed = TRUE, - .SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD, - .Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING - }; + // 创建D3D11渲染目标视图 + DXGI_SWAP_CHAIN_DESC swap_chain_desc = { + .BufferDesc = { + .Width = static_cast(size.x()), + .Height = static_cast(size.y()), + .RefreshRate = { + .Numerator = 240, + .Denominator = 1 + }, + .Format = format, + }, + .SampleDesc = { + .Count = 1, + .Quality = 0 + }, + .BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT, + .BufferCount = 2, + .OutputWindow = (HWND) window_handle, + .Windowed = TRUE, + .SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD, + .Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING + }; - // 创建交换链 - HRESULT hr = dxgi_factory->CreateSwapChain(dx_device, &swap_chain_desc, &dx_swap_chain); - if (FAILED(hr)) { - std::cerr << "mirage: " << "Failed to create DXGI Swap Chain. HRESULT: 0x" << std::hex << hr << std::dec << - std::endl; - return false; - } + // 创建交换链 + HRESULT hr = dxgi_factory->CreateSwapChain(dx_device, &swap_chain_desc, &dx_swap_chain); + if (FAILED(hr)) { + std::cerr << "mirage: " << "Failed to create DXGI Swap Chain. HRESULT: 0x" << std::hex << hr << std::dec << + std::endl; + return false; + } - // 获取后台缓冲区 - ID3D11Texture2D* back_buffer = nullptr; - hr = dx_swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**) &back_buffer); - if (FAILED(hr)) { - std::cerr << "mirage: " << "Failed to get back buffer from DXGI Swap Chain. HRESULT: 0x" << std::hex << hr << - std::dec << std::endl; - return false; - } - ON_SCOPE_EXIT { back_buffer->Release(); }; + // 获取后台缓冲区 + ID3D11Texture2D* back_buffer = nullptr; + hr = dx_swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**) &back_buffer); + if (FAILED(hr)) { + std::cerr << "mirage: " << "Failed to get back buffer from DXGI Swap Chain. HRESULT: 0x" << std::hex << hr << + std::dec << std::endl; + return false; + } + ON_SCOPE_EXIT { back_buffer->Release(); }; - // 创建渲染目标视图 - ID3D11RenderTargetView* render_target_view = nullptr; - hr = dx_device->CreateRenderTargetView(back_buffer, nullptr, &render_target_view); - if (FAILED(hr)) { - std::cerr << "mirage: " << "Failed to create render target view. HRESULT: 0x" << std::hex << hr << std::dec << - std::endl; - return false; - } + // 创建渲染目标视图 + ID3D11RenderTargetView* render_target_view = nullptr; + hr = dx_device->CreateRenderTargetView(back_buffer, nullptr, &render_target_view); + if (FAILED(hr)) { + std::cerr << "mirage: " << "Failed to create render target view. HRESULT: 0x" << std::hex << hr << std::dec << + std::endl; + return false; + } // #ifdef MIRAGE_USE_HDR -// // 设置HDR元数据 -// DXGI_HDR_METADATA_HDR10 metadata = {}; -// metadata.MaxMasteringLuminance = 1000 * 10000; // 1000尼特 -// metadata.MinMasteringLuminance = 0.001 * 10000; // 0.001尼特 -// // ...设置其他元数据 -// dx_swap_chain->SetHDRMetaData(DXGI_HDR_METADATA_TYPE_HDR10, sizeof(metadata), &metadata); +// // 设置HDR元数据 +// DXGI_HDR_METADATA_HDR10 metadata = {}; +// metadata.MaxMasteringLuminance = 1000 * 10000; // 1000尼特 +// metadata.MinMasteringLuminance = 0.001 * 10000; // 0.001尼特 +// // ...设置其他元数据 +// dx_swap_chain->SetHDRMetaData(DXGI_HDR_METADATA_TYPE_HDR10, sizeof(metadata), &metadata); // #endif - swapchain.d3d11.render_view = render_target_view; - swapchain.width = static_cast(size.x()); - swapchain.height = static_cast(size.y()); - swapchain.color_format = sg_format; - swapchain.depth_format = SG_PIXELFORMAT_NONE; - swapchain.sample_count = 1; - swapchain.d3d11.resolve_view = nullptr; - swapchain.d3d11.depth_stencil_view = nullptr; - return true; + // 配置sokol交换链信息 + swapchain.d3d11.render_view = render_target_view; + swapchain.width = static_cast(size.x()); + swapchain.height = static_cast(size.y()); + swapchain.color_format = sg_format; + swapchain.depth_format = SG_PIXELFORMAT_NONE; + swapchain.sample_count = 1; + swapchain.d3d11.resolve_view = nullptr; + swapchain.d3d11.depth_stencil_view = nullptr; + return true; } -void windows_window_state::clear() { - dx_swap_chain->Release(); - get_dx_render_target_view()->Release(); +/** + * @brief 清理渲染资源 + * + * 释放交换链和渲染目标视图资源。 + * 覆盖基类的clear方法。 + */ +void windows_window_state::on_clear() { + dx_swap_chain->Release(); + get_dx_render_target_view()->Release(); } +/** + * @brief 呈现渲染内容到窗口 + * + * 根据垂直同步设置和撕裂支持选择合适的呈现标志, + * 调用交换链的Present方法显示渲染结果。 + * 处理可能的设备丢失和其他错误。 + */ void windows_window_state::present() { - UINT presentFlags = 0; + UINT presentFlags = 0; - // 只有当垂直同步关闭(vsync=false)时才考虑使用允许撕裂的标志 - // 并且需要系统支持撕裂特性 - if (!vsync && tearing_supported) { - presentFlags |= DXGI_PRESENT_ALLOW_TEARING; - } + // 只有当垂直同步关闭(vsync=false)时才考虑使用允许撕裂的标志 + // 并且需要系统支持撕裂特性 + if (!vsync && tearing_supported) { + presentFlags |= DXGI_PRESENT_ALLOW_TEARING; + } - // 调用Present并处理可能的错误 - HRESULT hr = dx_swap_chain->Present(vsync ? 1 : 0, presentFlags); + // 调用Present并处理可能的错误 + HRESULT hr = dx_swap_chain->Present(vsync ? 1 : 0, presentFlags); - // 检查呈现结果 - if (FAILED(hr)) { - if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) { - // 设备丢失,需要重建设备和所有资源 - HRESULT reason = dx_device->GetDeviceRemovedReason(); - std::cerr << "设备丢失,原因: 0x" << std::hex << reason << std::dec << std::endl; - // 在这里你可以触发设备重建流程 - // handle_device_lost(); - } - else { - std::cerr << "Present失败,错误码: 0x" << std::hex << hr << std::dec << std::endl; - } - } + // 检查呈现结果 + if (FAILED(hr)) { + if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) { + // 设备丢失,需要重建设备和所有资源 + HRESULT reason = dx_device->GetDeviceRemovedReason(); + std::cerr << "设备丢失,原因: 0x" << std::hex << reason << std::dec << std::endl; + // 在这里你可以触发设备重建流程 + // handle_device_lost(); + } + else { + std::cerr << "Present失败,错误码: 0x" << std::hex << hr << std::dec << std::endl; + } + } } - +/** + * @brief 重建交换链 + * @param size 新的窗口大小 + * + * 当窗口大小改变时重建交换链和渲染目标视图。 + * 根据可用的接口版本选择最佳的重建方法。 + * 处理不同版本的DXGI接口,提供向后兼容性。 + */ void windows_window_state::rebuild_swapchain(const Eigen::Vector2i& size) { - // 检查尺寸是否有效 - if (size.x() == 0 || size.y() == 0) { return; } + // 检查尺寸是否有效 + if (size.x() == 0 || size.y() == 0) { return; } - // 获取设备上下文 - ID3D11DeviceContext* context = nullptr; - dx_device->GetImmediateContext(&context); - if (!context) { - std::cerr << "获取设备上下文失败" << std::endl; - return; - } + // 获取设备上下文 + ID3D11DeviceContext* context = nullptr; + dx_device->GetImmediateContext(&context); + if (!context) { + std::cerr << "获取设备上下文失败" << std::endl; + return; + } - // 1. 清除所有渲染管线绑定 - ID3D11RenderTargetView* nullViews[] = { nullptr }; - context->OMSetRenderTargets(1, nullViews, nullptr); - context->ClearState(); - context->Flush(); + // 1. 清除所有渲染管线绑定 + ID3D11RenderTargetView* nullViews[] = { nullptr }; + context->OMSetRenderTargets(1, nullViews, nullptr); + context->ClearState(); + context->Flush(); - // 2. 释放当前渲染目标视图 - DXGI_FORMAT format = DXGI_FORMAT_B8G8R8A8_UNORM; // 默认格式 - auto dx_render_target_view = get_dx_render_target_view(); - if (dx_render_target_view) { - // 保存格式以便稍后使用 - D3D11_RENDER_TARGET_VIEW_DESC rtv_desc = {}; - dx_render_target_view->GetDesc(&rtv_desc); - format = rtv_desc.Format; + // 2. 释放当前渲染目标视图 + DXGI_FORMAT format = DXGI_FORMAT_B8G8R8A8_UNORM; // 默认格式 + auto dx_render_target_view = get_dx_render_target_view(); + if (dx_render_target_view) { + // 保存格式以便稍后使用 + D3D11_RENDER_TARGET_VIEW_DESC rtv_desc = {}; + dx_render_target_view->GetDesc(&rtv_desc); + format = rtv_desc.Format; - // 释放资源 - dx_render_target_view->Release(); - swapchain.d3d11.render_view = nullptr; - } + // 释放资源 + dx_render_target_view->Release(); + swapchain.d3d11.render_view = nullptr; + } - // 3. 检查是否有更高级别的交换链接口 - HRESULT hr = E_FAIL; - UINT swapChainFlags = 0; + // 3. 检查是否有更高级别的交换链接口 + HRESULT hr = E_FAIL; + UINT swapChainFlags = 0; - // 尝试获取IDXGISwapChain4 (Windows 10 1803+) - IDXGISwapChain4* swapChain4 = nullptr; - if (SUCCEEDED(dx_swap_chain->QueryInterface(__uuidof(IDXGISwapChain4), (void**)&swapChain4))) { + // 尝试获取IDXGISwapChain4 (Windows 10 1803+) + IDXGISwapChain4* swapChain4 = nullptr; + if (SUCCEEDED(dx_swap_chain->QueryInterface(__uuidof(IDXGISwapChain4), (void**)&swapChain4))) { #if DEBUG - std::cout << "使用IDXGISwapChain4重建交换链" << std::endl; + std::cout << "使用IDXGISwapChain4重建交换链" << std::endl; #endif - // 保留当前交换链标志 - DXGI_SWAP_CHAIN_DESC1 desc = {}; - swapChain4->GetDesc1(&desc); - swapChainFlags = desc.Flags; + // 保留当前交换链标志 + DXGI_SWAP_CHAIN_DESC1 desc = {}; + swapChain4->GetDesc1(&desc); + swapChainFlags = desc.Flags; - // 重建缓冲区 - hr = swapChain4->ResizeBuffers( - 0, - // 保持当前缓冲区数量 - size.x(), - size.y(), - // 新尺寸 - DXGI_FORMAT_UNKNOWN, - // 保持当前格式 - swapChainFlags // 使用当前标志 - ); + // 重建缓冲区 + hr = swapChain4->ResizeBuffers( + 0, // 保持当前缓冲区数量 + size.x(), + size.y(), // 新尺寸 + DXGI_FORMAT_UNKNOWN, // 保持当前格式 + swapChainFlags // 使用当前标志 + ); - swapChain4->Release(); - } - // 尝试获取IDXGISwapChain1 (Windows 8+) - else { - IDXGISwapChain1* swapChain1 = nullptr; - if (SUCCEEDED(dx_swap_chain->QueryInterface(__uuidof(IDXGISwapChain1), (void**)&swapChain1))) { - std::cout << "使用IDXGISwapChain1重建交换链" << std::endl; + swapChain4->Release(); + } + // 尝试获取IDXGISwapChain1 (Windows 8+) + else { + IDXGISwapChain1* swapChain1 = nullptr; + if (SUCCEEDED(dx_swap_chain->QueryInterface(__uuidof(IDXGISwapChain1), (void**)&swapChain1))) { + std::cout << "使用IDXGISwapChain1重建交换链" << std::endl; - // 保留当前交换链标志 - DXGI_SWAP_CHAIN_DESC1 desc = {}; - swapChain1->GetDesc1(&desc); - swapChainFlags = desc.Flags; + // 保留当前交换链标志 + DXGI_SWAP_CHAIN_DESC1 desc = {}; + swapChain1->GetDesc1(&desc); + swapChainFlags = desc.Flags; - // 重建缓冲区 - hr = swapChain1->ResizeBuffers( - 0, - // 保持当前缓冲区数量 - size.x(), - size.y(), - // 新尺寸 - DXGI_FORMAT_UNKNOWN, - // 保持当前格式 - swapChainFlags // 使用当前标志 - ); + // 重建缓冲区 + hr = swapChain1->ResizeBuffers( + 0, + // 保持当前缓冲区数量 + size.x(), + size.y(), + // 新尺寸 + DXGI_FORMAT_UNKNOWN, + // 保持当前格式 + swapChainFlags // 使用当前标志 + ); - swapChain1->Release(); - } - // 回退到基础IDXGISwapChain - else { - std::cout << "使用基础IDXGISwapChain重建交换链" << std::endl; + swapChain1->Release(); + } + // 回退到基础IDXGISwapChain + else { + std::cout << "使用基础IDXGISwapChain重建交换链" << std::endl; - // 获取基础交换链描述 - DXGI_SWAP_CHAIN_DESC desc = {}; - dx_swap_chain->GetDesc(&desc); - swapChainFlags = desc.Flags; + // 获取基础交换链描述 + DXGI_SWAP_CHAIN_DESC desc = {}; + dx_swap_chain->GetDesc(&desc); + swapChainFlags = desc.Flags; - // 重建缓冲区 - hr = dx_swap_chain->ResizeBuffers( - 0, - // 保持当前缓冲区数量 - size.x(), - size.y(), - // 新尺寸 - DXGI_FORMAT_UNKNOWN, - // 保持当前格式 - swapChainFlags // 使用当前标志 - ); - } - } + // 重建缓冲区 + hr = dx_swap_chain->ResizeBuffers( + 0, + // 保持当前缓冲区数量 + size.x(), + size.y(), + // 新尺寸 + DXGI_FORMAT_UNKNOWN, + // 保持当前格式 + swapChainFlags // 使用当前标志 + ); + } + } - // 检查交换链重建是否成功 - if (FAILED(hr)) { - std::cerr << "重建交换链失败,错误码: 0x" << std::hex << hr << std::dec << std::endl; + // 检查交换链重建是否成功 + if (FAILED(hr)) { + std::cerr << "重建交换链失败,错误码: 0x" << std::hex << hr << std::dec << std::endl; - // 检查设备是否被移除 - HRESULT deviceRemoveReason = dx_device->GetDeviceRemovedReason(); - if (deviceRemoveReason != S_OK) { - std::cerr << "设备被移除,原因: 0x" << std::hex << deviceRemoveReason << std::dec << std::endl; - } + // 检查设备是否被移除 + HRESULT deviceRemoveReason = dx_device->GetDeviceRemovedReason(); + if (deviceRemoveReason != S_OK) { + std::cerr << "设备被移除,原因: 0x" << std::hex << deviceRemoveReason << std::dec << std::endl; + } - context->Release(); - return; - } + context->Release(); + return; + } - // 4. 创建新的渲染目标视图 - ID3D11Texture2D* backBuffer = nullptr; - hr = dx_swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**) &backBuffer); - if (FAILED(hr)) { - std::cerr << "获取后台缓冲区失败,错误码: 0x" << std::hex << hr << std::dec << std::endl; - context->Release(); - return; - } + // 4. 创建新的渲染目标视图 + ID3D11Texture2D* backBuffer = nullptr; + hr = dx_swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**) &backBuffer); + if (FAILED(hr)) { + std::cerr << "获取后台缓冲区失败,错误码: 0x" << std::hex << hr << std::dec << std::endl; + context->Release(); + return; + } - // 创建渲染目标视图 - hr = dx_device->CreateRenderTargetView( - backBuffer, - nullptr, - // 使用默认视图描述 - (ID3D11RenderTargetView**) &swapchain.d3d11.render_view - ); + // 创建渲染目标视图 + hr = dx_device->CreateRenderTargetView( + backBuffer, + nullptr, + // 使用默认视图描述 + (ID3D11RenderTargetView**) &swapchain.d3d11.render_view + ); - // 释放后台缓冲区 - backBuffer->Release(); + // 释放后台缓冲区 + backBuffer->Release(); - if (FAILED(hr)) { - std::cerr << "创建渲染目标视图失败,错误码: 0x" << std::hex << hr << std::dec << std::endl; - context->Release(); - return; - } + if (FAILED(hr)) { + std::cerr << "创建渲染目标视图失败,错误码: 0x" << std::hex << hr << std::dec << std::endl; + context->Release(); + return; + } - // 5. 设置渲染目标和视口 - context->OMSetRenderTargets(1, (ID3D11RenderTargetView**) &swapchain.d3d11.render_view, nullptr); + // 5. 设置渲染目标和视口 + context->OMSetRenderTargets(1, (ID3D11RenderTargetView**) &swapchain.d3d11.render_view, nullptr); - D3D11_VIEWPORT viewport = {}; - viewport.Width = static_cast(size.x()); - viewport.Height = static_cast(size.y()); - viewport.MinDepth = 0.0f; - viewport.MaxDepth = 1.0f; - context->RSSetViewports(1, &viewport); + D3D11_VIEWPORT viewport = {}; + viewport.Width = static_cast(size.x()); + viewport.Height = static_cast(size.y()); + viewport.MinDepth = 0.0f; + viewport.MaxDepth = 1.0f; + context->RSSetViewports(1, &viewport); - // 释放上下文 - context->Release(); + // 释放上下文 + context->Release(); #if DEBUG - std::cout << "交换链重建成功,新尺寸: " << size.x() << "x" << size.y() << std::endl; + std::cout << "交换链重建成功,新尺寸: " << size.x() << "x" << size.y() << std::endl; #endif } diff --git a/src/core/window/windows/windows_window_state.h b/src/core/window/windows/windows_window_state.h index 00c8b8e..5ae4458 100644 --- a/src/core/window/windows/windows_window_state.h +++ b/src/core/window/windows/windows_window_state.h @@ -7,7 +7,7 @@ * 使用Direct3D 11和DXGI处理窗口的渲染表面、交换链和呈现操作。 */ -#include "core/window/render_window.h" +#include "core/window/mwindow.h" #include #include @@ -43,7 +43,7 @@ struct windows_window_state final : mirage_window_state { * 使用提供的Direct3D设备和DXGI工厂初始化窗口渲染状态, * 创建交换链和渲染目标视图。 */ - bool init(ID3D11Device* in_device, IDXGIFactory* in_factory, mirage_window* in_window); + bool init(ID3D11Device* in_device, IDXGIFactory* in_factory, mwindow* in_window); /** * @brief 设置可变刷新率支持 @@ -62,7 +62,7 @@ struct windows_window_state final : mirage_window_state { * 释放Direct3D和DXGI资源,包括渲染目标视图和交换链。 * 覆盖基类方法。 */ - virtual void clear() override; + virtual void on_clear() override; /** * @brief 呈现渲染内容 diff --git a/src/geometry/arranged_children.h b/src/geometry/arranged_children.h index df8be18..ea23166 100644 --- a/src/geometry/arranged_children.h +++ b/src/geometry/arranged_children.h @@ -90,7 +90,7 @@ public: * * 创建一个具有指定可见性过滤条件的arranged_children实例。 */ - explicit arranged_children(visibility in_visibility_filter) : visibility_filter_(in_visibility_filter) {} + explicit arranged_children(visibility in_visibility_filter = visibility::any_visible) : visibility_filter_(in_visibility_filter) {} //-------------- 可见性过滤 -------------- @@ -100,7 +100,7 @@ public: * @return 如果该可见性级别被接受则返回true */ [[nodiscard]] auto accepts(visibility in_visibility) const { - return in_visibility >= visibility_filter_; + return has_any_flag(in_visibility, visibility_filter_); } /** @@ -111,6 +111,22 @@ public: visibility_filter_ = in_visibility_filter; } + /** + * @brief 添加标志到筛选器 + * @param in_flag 要添加的可见性标志 + */ + void add_filter_flag(visibility in_flag) { + visibility_filter_ |= in_flag; + } + + /** + * @brief 移除筛选器中的标志 + * @param in_flag 要移除的可见性标志 + */ + void remove_filter_flag(visibility in_flag) { + visibility_filter_ &= ~in_flag; + } + //-------------- 子项操作 -------------- /** diff --git a/src/geometry/geometry.h b/src/geometry/geometry.h index 27375ec..4d8e700 100644 --- a/src/geometry/geometry.h +++ b/src/geometry/geometry.h @@ -29,18 +29,17 @@ public: * * 创建一个空的几何体,大小为零且无变换。 */ - geometry_t() : size_(), local_to_parent_(), local_to_absolute_() {} + geometry_t() : size_(), local_to_parent_(), local_to_window_() {} /** * @brief 构造具有特定大小和变换的几何体 * * @param in_size 组件的本地大小 * @param in_local_to_parent 本地到父级的变换 - * @param in_parent_to_absolute 父级到绝对坐标的变换(屏幕/窗口坐标) + * @param in_local_to_window 本地到窗口坐标系的变换 */ - geometry_t(Eigen::Vector2f in_size, transform2d in_local_to_parent, const transform2d& in_parent_to_absolute) - : size_(std::move(in_size)), local_to_parent_(std::move(in_local_to_parent)) { - local_to_absolute_ = in_parent_to_absolute.concatenate(local_to_parent_); + geometry_t(Eigen::Vector2f in_size, transform2d in_local_to_parent, transform2d in_local_to_window) + : size_(std::move(in_size)), local_to_parent_(std::move(in_local_to_parent)), local_to_window_(std::move(in_local_to_window)) { } //-------------- 子几何体创建 -------------- @@ -59,10 +58,11 @@ public: child_to_parent.set_transform(in_local_offset, 0.0f, Eigen::Vector2f(in_scale, in_scale)); child_to_parent = local_to_parent_.concatenate(child_to_parent); - // 级联变换以获得子级到绝对空间的变换 - const transform2d local_to_absolute = local_to_absolute_.concatenate(child_to_parent); + transform2d child_to_window; + child_to_window.set_transform(in_local_offset, 0.0f, Eigen::Vector2f(in_scale, in_scale)); + child_to_window = local_to_window_.concatenate(child_to_window); - return geometry_t(in_child_size, child_to_parent, local_to_absolute); + return geometry_t(in_child_size, child_to_parent, child_to_window); } //-------------- 变换和变换矩阵获取 -------------- @@ -80,9 +80,9 @@ public: * @brief 获取本地到绝对的变换 * @return 本地到绝对坐标的变换矩阵 */ - [[nodiscard]] const auto& get_local_to_absolute_transform() const + [[nodiscard]] const auto& get_local_to_window_transform() const { - return local_to_absolute_; + return local_to_window_; } //-------------- 坐标转换 -------------- @@ -92,19 +92,19 @@ public: * @param in_local_point 本地坐标中的点 * @return 绝对坐标中的点 */ - [[nodiscard]] auto local_to_absolute(const Eigen::Vector2f& in_local_point) const + [[nodiscard]] auto local_to_window(const Eigen::Vector2f& in_local_point) const { - return local_to_absolute_.transform_point(in_local_point); + return local_to_window_.transform_point(in_local_point); } /** * @brief 将点从绝对坐标转换到本地坐标 - * @param in_absolute_point 绝对坐标中的点 + * @param in_window_point 绝对坐标中的点 * @return 本地坐标中的点 */ - [[nodiscard]] auto absolute_to_local(const Eigen::Vector2f& in_absolute_point) const + [[nodiscard]] auto window_to_local(const Eigen::Vector2f& in_window_point) const { - return local_to_absolute_.inverse_transform_point(in_absolute_point); + return local_to_window_.inverse_transform_point(in_window_point); } /** @@ -145,15 +145,15 @@ public: * @brief 获取本地坐标系中的矩形 * @return 表示几何体大小的矩形,左上角位于原点 */ - [[nodiscard]] auto get_local_rect() const { return rect_t({}, size_); } + [[nodiscard]] auto get_local_rect() const { return rect_t({0, 0}, size_); } /** * @brief 获取此几何体在绝对坐标中的位置 * @return 绝对坐标中的位置(左上角) */ - [[nodiscard]] auto get_absolute_position() const + [[nodiscard]] auto get_window_position() const { - return local_to_absolute(Eigen::Vector2f(0, 0)); + return local_to_window(Eigen::Vector2f(0, 0)); } /** @@ -163,18 +163,18 @@ public: [[nodiscard]] auto get_scale() const { // 从本地到绝对变换中提取缩放分量 - return local_to_absolute_.get_scale(); + return local_to_window_.get_scale(); } /** * @brief 获取绝对坐标系中的矩形 * @return 表示几何体在绝对坐标系中的矩形 */ - [[nodiscard]] auto get_absolute_rect() const + [[nodiscard]] auto get_window_rect() const { // 变换本地矩形的四个角到绝对坐标 - const auto& top_left = local_to_absolute(Eigen::Vector2f(0, 0)); - const auto& bottom_right = local_to_absolute(size_); + const auto& top_left = local_to_window(Eigen::Vector2f(0, 0)); + const auto& bottom_right = local_to_window(size_); const Eigen::Vector2f& absolute_size = bottom_right - top_left; @@ -197,7 +197,7 @@ public: // 更新变换 - 先应用原变换,再应用偏移变换 auto local_to_parent = local_to_parent_.concatenate(offset_transform); - auto local_to_absolute = local_to_absolute_.concatenate(offset_transform); + auto local_to_absolute = local_to_window_.concatenate(offset_transform); return geometry_t(size_, local_to_parent, local_to_absolute); } @@ -222,9 +222,9 @@ public: ); // 计算新的本地到绝对变换 - const transform2d parent_to_absolute = local_to_absolute_.concatenate(local_to_parent_.inverse()); + const transform2d parent_to_window = local_to_window_.concatenate(local_to_parent_.inverse()); - return geometry_t(size_, new_local_to_parent, parent_to_absolute); + return geometry_t(size_, new_local_to_parent, parent_to_window); } /** @@ -245,7 +245,7 @@ public: // 更新变换 - 先应用原变换,再应用偏移变换 local_to_parent_ = local_to_parent_.concatenate(offset_transform); - local_to_absolute_ = local_to_absolute_.concatenate(offset_transform); + local_to_window_ = local_to_window_.concatenate(offset_transform); } //-------------- 测试和查询 -------------- @@ -253,17 +253,21 @@ public: /** * @brief 检查点是否在此几何体内 * - * @param in_absolute_point 以绝对坐标表示的点 + * @param in_window_point 以绝对坐标表示的点, 窗口内的点 * @return 如果点在几何体内,则为true */ - [[nodiscard]] auto is_under_location(const Eigen::Vector2f& in_absolute_point) const + [[nodiscard]] auto is_under_location(const Eigen::Vector2f& in_window_point) const { // 将点从绝对坐标转换到本地坐标 - auto local_point = absolute_to_local(in_absolute_point); + auto local_point = window_to_local(in_window_point); // 检查点是否在本地矩形范围内 - return local_point.x() >= 0 && local_point.x() <= size_.x() && - local_point.y() >= 0 && local_point.y() <= size_.y(); + if (local_point.x() >= 0 && + local_point.x() <= size_.x() && + local_point.y() >= 0 && + local_point.y() <= size_.y()) + return std::optional(local_point); + return std::optional(); } private: @@ -274,5 +278,5 @@ private: transform2d local_to_parent_; /** 从本地坐标系到绝对坐标系的变换 */ - transform2d local_to_absolute_; + transform2d local_to_window_; }; diff --git a/src/geometry/rect.h b/src/geometry/rect.h index d271d92..050ef55 100644 --- a/src/geometry/rect.h +++ b/src/geometry/rect.h @@ -634,7 +634,7 @@ public: */ template auto get_text_rect(horizontal_text_alignment in_h_align, vertical_text_alignment in_v_align, const Eigen::Vector2& in_text_size) const { - rect_t text_rect{{}, in_text_size}; + rect_t text_rect{{0, 0}, in_text_size}; switch (in_h_align) { case horizontal_text_alignment::left: diff --git a/src/geometry/widget_layout_tree.cpp b/src/geometry/widget_layout_tree.cpp index 35e8cd6..72b6bd2 100644 --- a/src/geometry/widget_layout_tree.cpp +++ b/src/geometry/widget_layout_tree.cpp @@ -2,7 +2,7 @@ #include -#include "core/window/render_window.h" +#include "core/window/mwindow.h" #include "misc/mirage_scoped_duration_timer.h" #include "widget/mwidget.h" @@ -93,7 +93,7 @@ void widget_layout_tree_node::update_layout(float in_layout_scale_multiplier) { // 为子节点分配几何区域 if (!children_.empty()) { // 创建arranged_children对象,使用可见性筛选器 - arranged_children arranged_widgets(visibility::visible); // 默认只接受可见的widgets + arranged_children arranged_widgets(visibility::any_visible); // 默认只接受可见的widgets // 调用widget的arrange_children方法填充arranged_widgets widget->arrange_children(geometry_, arranged_widgets); @@ -168,6 +168,37 @@ void widget_layout_tree_node::paint(mirage_paint_context& in_context, float in_l } } +hit_test_result widget_layout_tree_node::perform_hit_test( + const Eigen::Vector2f& in_window_pos, + const std::function, const Eigen::Vector2f&)>& in_hit_func) { + + // 首先检查点是否在几何范围内 + auto in_widget_pos = geometry_.is_under_location(in_window_pos); + if (!in_widget_pos) + return {}; + + // 获取widget的强引用,并立即检查是否有效 + auto widget = widget_.lock(); + if (!widget) + return {}; + + // 处理当前widget的命中测试 + if (widget->can_hit_test() && in_hit_func) { + const auto& widget_pos = in_widget_pos.value(); + const hit_test_handle handle = in_hit_func(widget, widget_pos); + if (handle.is_handled()) + return { widget, widget_pos }; // **提前返回命中的widget** + } + + // 递归检查子widget + for (const auto& child : children_) { + if (auto hit_widget = child->perform_hit_test(in_window_pos, in_hit_func)) + return hit_widget; // **立即返回第一个命中的子widget** + } + + return {}; +} + void widget_layout_tree_node::invalidate_layout() { is_layout_dirty_ = true; for (const auto& child : children_) { @@ -247,7 +278,7 @@ void widget_layout_tree::paint(mirage_paint_context& in_context) { } } -mirage_window* widget_layout_tree::get_window() const { +mwindow* widget_layout_tree::get_window() const { if (const auto widget = root_->get_widget()) return widget->get_window(); return nullptr; @@ -258,3 +289,120 @@ float widget_layout_tree::get_dpi_scale() const { return window->get_dpi_scale() * dpi_helper::get_global_scale(); return 1.0f; } + +hit_test_result widget_layout_tree::perform_hit_test(const Eigen::Vector2f& in_window_pos, const std::function, const Eigen::Vector2f&)>& in_hit_func) { + if (!root_) + return {}; + + return root_->perform_hit_test(in_window_pos, in_hit_func); +} + +void widget_layout_tree::on_mouse_leave_window() { + if (last_hover_widget_) + last_hover_widget_->on_mouse_leave(); + last_hover_widget_ = nullptr; +} + +void widget_layout_tree::process_mouse_move(const Eigen::Vector2f& in_window_pos) { + auto result = perform_hit_test(in_window_pos, [this](std::shared_ptr in_widget, const Eigen::Vector2f& in_local_pos) { + return in_widget->on_mouse_move(in_local_pos); + }); + + // 如果命中的widget与上次相同,则不需要处理 + if (last_hover_widget_ == result.widget) + return; + + // 如果上次有悬停widget,通知其离开 + if (last_hover_widget_) + last_hover_widget_->on_mouse_leave(); + + // 更新悬停widget + last_hover_widget_ = result; + if (last_hover_widget_) + last_hover_widget_->on_mouse_enter(); +} + +void widget_layout_tree::process_mouse_press(const Eigen::Vector2f& in_window_pos, mouse_button in_button) { + // 执行碰撞检测,找出鼠标位置下的widget + auto result = perform_hit_test(in_window_pos, [in_button](std::shared_ptr in_widget, const Eigen::Vector2f& in_local_pos) { + return in_widget->on_mouse_press(in_local_pos, in_button); + }); + + const auto& hit_widget = result.widget; + + // 更新最后一次点击的widget和时间 + last_hit_widget_ = hit_widget; + last_mouse_press_time_ = get_current_time(); + last_mouse_press_pos_ = in_window_pos; // 存储点击位置,用于空间判断 +} + +void widget_layout_tree::process_mouse_release(const Eigen::Vector2f& in_window_pos, mouse_button in_button) { + // 执行碰撞检测,找出鼠标位置下的widget + auto hit_result = perform_hit_test(in_window_pos, [in_button](std::shared_ptr widget, const Eigen::Vector2f& local_pos) { + return widget->on_mouse_release(local_pos, in_button); + }); + + const auto& hit_widget = hit_result.widget; + const auto& widget_local_pos = hit_result.widget_space_pos; + + // 如果没有点击到任何widget,重置状态并返回 + if (!hit_widget) { + click_count_ = 0; + last_hit_widget_ = nullptr; + return; + } + + // 定义时间和空间阈值 + constexpr auto CLICK_TIME_THRESHOLD = std::chrono::milliseconds(400); // 单次点击最大持续时间 + constexpr float CLICK_DISTANCE_THRESHOLD = 5.0f; // 点击位置容差 + + // 计算从按下到释放的持续时间 + const auto press_duration = get_current_time() - last_mouse_press_time_; + + // 计算与上次点击位置的距离 + const float distance = (in_window_pos - last_mouse_press_pos_).norm(); + + // 判断是否是有效点击(时间短且位置接近) + bool is_valid_click = press_duration < CLICK_TIME_THRESHOLD && distance < CLICK_DISTANCE_THRESHOLD; + + // 处理点击相关逻辑 + if (is_valid_click && last_hit_widget_ == hit_widget) { + // 计算上次点击事件到现在的时间 + const auto time_since_last_click = get_current_time() - last_click_time_; + + // 判断是否是连续点击(在双击时间窗口内) + if (click_count_ > 0 && time_since_last_click < CLICK_TIME_THRESHOLD) { + // 点击次数增加 + click_count_++; + + // 根据点击次数触发不同事件 + if (click_count_ == 2) { + // 双击事件 + hit_widget->on_double_click(widget_local_pos, in_button); + } else { + // 单击事件 + hit_widget->on_click(widget_local_pos, in_button); + } + } else { + // 超过双击时间窗口,视为新的点击序列 + click_count_ = 1; + hit_widget->on_click(widget_local_pos, in_button); + } + + // 更新最后点击时间 + last_click_time_ = get_current_time(); + } else { + // 无效点击或点击了新的widget + if (is_valid_click) { + click_count_ = 1; + hit_widget->on_click(widget_local_pos, in_button); + last_click_time_ = get_current_time(); + } else { + // 不是有效点击,重置点击计数 + click_count_ = 0; + } + } + + // 更新最后点击的widget + last_hit_widget_ = hit_widget; +} diff --git a/src/geometry/widget_layout_tree.h b/src/geometry/widget_layout_tree.h index 4ac39eb..6b77a26 100644 --- a/src/geometry/widget_layout_tree.h +++ b/src/geometry/widget_layout_tree.h @@ -14,8 +14,11 @@ #include "geometry.h" #include "core/render_elements.h" #include "misc/mirage_paint_context.h" +#include "misc/key_type/key_type.h" +#include "widget/hit_test/hit_test_parameters.h" +#include "widget/hit_test/hit_test_result.h" -class mirage_window; +class mwindow; class mwidget; /** @@ -96,6 +99,10 @@ public: */ [[nodiscard]] const auto& get_parent_geometry() const { return parent_->get_geometry(); } + /** + * @brief 进行命中测试 + * @return + //-------------- 布局处理 -------------- /** @@ -141,6 +148,14 @@ public: */ void paint(mirage_paint_context& in_context, float in_layout_scale_multiplier); + //----------------- 命中测试 ----------------- + + /** + * @brief 执行命中测试 + * @param in_window_pos 窗口内位置 + * @param in_hit_func 执行的控件相应事件 + */ + hit_test_result perform_hit_test(const Eigen::Vector2f& in_window_pos, const std::function, const Eigen::Vector2f&)>& in_hit_func); private: /** 节点对应的UI组件的弱引用 */ std::weak_ptr widget_; @@ -218,7 +233,7 @@ public: * @brief 获取关联的窗口 * @return 窗口指针 */ - [[nodiscard]] mirage_window* get_window() const; + [[nodiscard]] mwindow* get_window() const; /** * @brief 获取DPI缩放系数 @@ -226,7 +241,35 @@ public: */ [[nodiscard]] float get_dpi_scale() const; + //----------------- 命中测试 ----------------- + + /** + * @brief 执行命中测试 + * @param in_window_pos 窗口内位置 + * @param in_hit_func 执行的控件相应事件 + * @return 命中的控件 + */ + hit_test_result perform_hit_test(const Eigen::Vector2f& in_window_pos, const std::function, const Eigen::Vector2f&)>& in_hit_func); + + void on_mouse_leave_window(); + + void process_mouse_move(const Eigen::Vector2f& in_window_pos); + + void process_mouse_press(const Eigen::Vector2f& in_window_pos, mouse_button in_button); + + void process_mouse_release(const Eigen::Vector2f& in_window_pos, mouse_button in_button); private: /** 布局树的根节点 */ std::shared_ptr root_; + + //----------------- 命中测试 ----------------- + std::shared_ptr last_hit_widget_; + std::shared_ptr last_hover_widget_; + Eigen::Vector2f last_hit_position_; + Eigen::Vector2f last_mouse_position_; + int32_t click_count_{}; + + Eigen::Vector2f last_mouse_press_pos_; // 上次鼠标按下的位置 + time_type last_click_time_{}; // 上次点击完成的时间 + time_type last_mouse_press_time_{}; }; diff --git a/src/mirage.cpp b/src/mirage.cpp index dd6ded2..e92c7ac 100644 --- a/src/mirage.cpp +++ b/src/mirage.cpp @@ -5,7 +5,7 @@ #include #include -#include "core/window/render_window.h" +#include "core/window/mwindow.h" #include "misc/mirage_scoped_duration_timer.h" void mirage_log(const char* tag, uint32_t log_level, uint32_t log_item_id, const char* message_or_null, @@ -46,9 +46,9 @@ void mirage_app::init() { void mirage_app::run() { // std::thread render_thread(&mirage_app::render_thread, this); - while (!mirage_window::get_windows().empty()) { + while (!mwindow::get_windows().empty()) { delta_time = get_current_time() - last_time; - mirage_window::poll_events(); + mwindow::poll_events(); render_context->tick(delta_time); last_time = get_current_time(); diff --git a/src/misc/enum_flags.h b/src/misc/enum_flags.h new file mode 100644 index 0000000..c4fce29 --- /dev/null +++ b/src/misc/enum_flags.h @@ -0,0 +1,52 @@ +#pragma once +/** + * @file enum_flags.h + * @brief 提供将枚举转换为位标志的宏 + * + * 这个文件定义了一组宏,用于将枚举类型转换为支持位操作的标志类型。 + */ + +#include + +/** + * @def DEFINE_ENUM_FLAGS + * @brief 为枚举类型定义位运算操作符 + * @param EnumType 要启用位标志功能的枚举类型名称 + * + * 为指定的枚举类型定义位运算操作符(|, &, ^, ~, |=, &=, ^=)和辅助函数。 + * 使用此宏后,枚举类型可以作为位标志使用。 + */ +#define DEFINE_ENUM_FLAGS(EnumType) \ + inline EnumType operator|(EnumType a, EnumType b) { \ + return static_cast(static_cast>(a) | \ + static_cast>(b)); \ + } \ + inline EnumType operator&(EnumType a, EnumType b) { \ + return static_cast(static_cast>(a) & \ + static_cast>(b)); \ + } \ + inline EnumType operator^(EnumType a, EnumType b) { \ + return static_cast(static_cast>(a) ^ \ + static_cast>(b)); \ + } \ + inline EnumType operator~(EnumType a) { \ + return static_cast(~static_cast>(a)); \ + } \ + inline EnumType& operator|=(EnumType& a, EnumType b) { \ + return a = a | b; \ + } \ + inline EnumType& operator&=(EnumType& a, EnumType b) { \ + return a = a & b; \ + } \ + inline EnumType& operator^=(EnumType& a, EnumType b) { \ + return a = a ^ b; \ + } \ + inline bool has_flag(EnumType value, EnumType flag) { \ + return (static_cast>(value) & \ + static_cast>(flag)) == \ + static_cast>(flag); \ + } \ + inline bool has_any_flag(EnumType value, EnumType flags) { \ + return (static_cast>(value) & \ + static_cast>(flags)) != 0; \ + } diff --git a/src/misc/key_type/key_type.h b/src/misc/key_type/key_type.h new file mode 100644 index 0000000..a5d8fc1 --- /dev/null +++ b/src/misc/key_type/key_type.h @@ -0,0 +1,296 @@ +#pragma once +/** + * @file key_type.h + * @brief 定义键盘按键和鼠标按键的枚举 + * + * 本文件定义了键盘按键和鼠标按键的枚举值,用于输入事件处理。 + * 鼠标按键使用位标志,允许组合多个按键。 + */ + +#include +#include "misc/enum_flags.h" + +/** + * @enum key_code + * @brief 键盘按键码 + * + * 定义键盘按键的标准代码,基于常见的键盘布局。 + */ +enum class key_code : uint16_t { + // 特殊键 + none = 0, + unknown = 0, + + // 功能键 + escape = 0x01, + f1 = 0x02, + f2 = 0x03, + f3 = 0x04, + f4 = 0x05, + f5 = 0x06, + f6 = 0x07, + f7 = 0x08, + f8 = 0x09, + f9 = 0x0A, + f10 = 0x0B, + f11 = 0x0C, + f12 = 0x0D, + print_screen = 0x0E, + scroll_lock = 0x0F, + pause = 0x10, + + // 主键区 + back_quote = 0x11, // ` + num_1 = 0x12, + num_2 = 0x13, + num_3 = 0x14, + num_4 = 0x15, + num_5 = 0x16, + num_6 = 0x17, + num_7 = 0x18, + num_8 = 0x19, + num_9 = 0x1A, + num_0 = 0x1B, + minus = 0x1C, // - + equals = 0x1D, // = + backspace = 0x1E, + + tab = 0x1F, + q = 0x20, + w = 0x21, + e = 0x22, + r = 0x23, + t = 0x24, + y = 0x25, + u = 0x26, + i = 0x27, + o = 0x28, + p = 0x29, + left_bracket = 0x2A, // [ + right_bracket = 0x2B, // ] + backslash = 0x2C, // \ + + caps_lock = 0x2D, + a = 0x2E, + s = 0x2F, + d = 0x30, + f = 0x31, + g = 0x32, + h = 0x33, + j = 0x34, + k = 0x35, + l = 0x36, + semicolon = 0x37, // ; + apostrophe = 0x38, // ' + enter = 0x39, + + left_shift = 0x3A, + z = 0x3B, + x = 0x3C, + c = 0x3D, + v = 0x3E, + b = 0x3F, + n = 0x40, + m = 0x41, + comma = 0x42, // , + period = 0x43, // . + slash = 0x44, // / + right_shift = 0x45, + + left_control = 0x46, + left_meta = 0x47, // Windows/Command key + left_alt = 0x48, + space = 0x49, + right_alt = 0x4A, + right_meta = 0x4B, // Windows/Command key + context_menu = 0x4C, + right_control = 0x4D, + + // 编辑控制键区 + insert = 0x4E, + home = 0x4F, + page_up = 0x50, + delete_key = 0x51, + end = 0x52, + page_down = 0x53, + + // 方向键区 + up = 0x54, + left = 0x55, + down = 0x56, + right = 0x57, + + // 小键盘区 + num_lock = 0x58, + numpad_divide = 0x59, // / + numpad_multiply = 0x5A, // * + numpad_subtract = 0x5B, // - + numpad_7 = 0x5C, + numpad_8 = 0x5D, + numpad_9 = 0x5E, + numpad_add = 0x5F, // + + numpad_4 = 0x60, + numpad_5 = 0x61, + numpad_6 = 0x62, + numpad_1 = 0x63, + numpad_2 = 0x64, + numpad_3 = 0x65, + numpad_enter = 0x66, + numpad_0 = 0x67, + numpad_decimal = 0x68, // . + + // 多媒体键 + volume_mute = 0x69, + volume_down = 0x6A, + volume_up = 0x6B, + media_next = 0x6C, + media_prev = 0x6D, + media_stop = 0x6E, + media_play = 0x6F, + + // 浏览器键 + browser_back = 0x70, + browser_forward = 0x71, + browser_refresh = 0x72, + browser_stop = 0x73, + browser_search = 0x74, + browser_favorites = 0x75, + browser_home = 0x76, + + // 其他 + max_key_value = 0xFF +}; + +/** + * @enum mouse_button + * @brief 鼠标按钮的位标志 + * + * 定义鼠标按钮的位标志,支持组合多个按钮。 + */ +enum class mouse_button : uint8_t { + none = 0, ///< 无按钮 + left = 1 << 0, ///< 鼠标左键 + right = 1 << 1, ///< 鼠标右键 + middle = 1 << 2, ///< 鼠标中键 + x1 = 1 << 3, ///< 鼠标X1键(侧边后退键) + x2 = 1 << 4, ///< 鼠标X2键(侧边前进键) + + // 常用组合 + all = 0xFF, ///< 所有按钮 +}; + +/** + * @enum mouse_action + * @brief 鼠标按钮的动作 + */ +enum class mouse_action { + none, + press, + release, + dbl_click, + move, +}; + +// 为鼠标按钮启用位标志功能 +DEFINE_ENUM_FLAGS(mouse_button) + +/** + * @enum keyboard_modifier + * @brief 键盘修饰键状态的位标志 + * + * 定义键盘修饰键的位标志,支持组合多个修饰键。 + */ +enum class keyboard_modifier : uint8_t { + none = 0, ///< 无修饰键 + ctrl = 1 << 0, ///< Ctrl键 + shift = 1 << 1, ///< Shift键 + alt = 1 << 2, ///< Alt键 + meta = 1 << 3, ///< Meta/Win/Command键 + + // 常用组合 + all = 0xFF, ///< 所有修饰键 +}; + +// 为键盘修饰键启用位标志功能 +DEFINE_ENUM_FLAGS(keyboard_modifier) + +/** + * @brief 从键盘按键获取对应的修饰键标志 + * @param key 键盘按键 + * @return 对应的修饰键标志,如果不是修饰键则返回none + */ +inline keyboard_modifier key_to_modifier(key_code key) { + switch (key) { + case key_code::left_control: + case key_code::right_control: + return keyboard_modifier::ctrl; + + case key_code::left_shift: + case key_code::right_shift: + return keyboard_modifier::shift; + + case key_code::left_alt: + case key_code::right_alt: + return keyboard_modifier::alt; + + case key_code::left_meta: + case key_code::right_meta: + return keyboard_modifier::meta; + + default: + return keyboard_modifier::none; + } +} + +/** + * @enum wheel_type + * @brief 鼠标滚轮类型 + */ +enum class wheel_type : uint8_t { + none = 0, ///< 无滚轮事件 + vertical = 1, ///< 垂直滚轮 + horizontal = 2, ///< 水平滚轮 +}; + +/** + * @struct wheel_event + * @brief 鼠标滚轮事件数据 + */ +struct wheel_event { + wheel_type type = wheel_type::none; ///< 滚轮类型 + float delta_x = 0.0f; ///< 水平滚动量 + float delta_y = 0.0f; ///< 垂直滚动量 + keyboard_modifier modifiers = keyboard_modifier::none; ///< 同时按下的修饰键 +}; + +/** + * @brief 将平台特定的键码转换为统一的key_code + * @param native_key 平台原生键码 + * @return 统一的key_code枚举值 + */ +key_code platform_key_to_key_code(int32_t native_key); // 在平台特定代码中实现 + +/** + * @brief 将平台特定的鼠标事件转换为统一的mouse_button + * @param native_event 平台原生事件类型 + * @param native_param 平台原生事件参数 + * @return 统一的mouse_button枚举值 + */ +mouse_button platform_event_to_mouse_button(uint32_t native_event, uintptr_t native_param); // 在平台特定代码中实现 + +/** + * @brief 将平台特定的鼠标事件转换为统一的mouse_action + * @param native_event 平台原生事件类型 + * @param native_param 平台原生事件参数 + * @return 统一的mouse_action枚举值 +*/ +mouse_action platform_event_to_mouse_action(uint32_t native_event, uintptr_t native_param); + +/** + * @brief 将平台特定的滚轮事件转换为统一的wheel_event + * @param native_event 平台原生事件类型 + * @param native_param1 平台原生事件参数1 + * @param native_param2 平台原生事件参数2 + * @return 统一的wheel_event结构 + */ +wheel_event platform_event_to_wheel_event(uint32_t native_event, uintptr_t native_param1, intptr_t native_param2); diff --git a/src/misc/key_type/windows/key_type.cpp b/src/misc/key_type/windows/key_type.cpp new file mode 100644 index 0000000..6bde592 --- /dev/null +++ b/src/misc/key_type/windows/key_type.cpp @@ -0,0 +1,225 @@ +#include "misc/key_type/key_type.h" + +#include +#include + +key_code platform_key_to_key_code(int32_t native_key) { + switch (native_key) { + // 字母键 (A-Z) + case 'A': return key_code::a; + case 'B': return key_code::b; + case 'C': return key_code::c; + case 'D': return key_code::d; + case 'E': return key_code::e; + case 'F': return key_code::f; + case 'G': return key_code::g; + case 'H': return key_code::h; + case 'I': return key_code::i; + case 'J': return key_code::j; + case 'K': return key_code::k; + case 'L': return key_code::l; + case 'M': return key_code::m; + case 'N': return key_code::n; + case 'O': return key_code::o; + case 'P': return key_code::p; + case 'Q': return key_code::q; + case 'R': return key_code::r; + case 'S': return key_code::s; + case 'T': return key_code::t; + case 'U': return key_code::u; + case 'V': return key_code::v; + case 'W': return key_code::w; + case 'X': return key_code::x; + case 'Y': return key_code::y; + case 'Z': return key_code::z; + + // 数字键 (0-9) + case '0': return key_code::num_0; + case '1': return key_code::num_1; + case '2': return key_code::num_2; + case '3': return key_code::num_3; + case '4': return key_code::num_4; + case '5': return key_code::num_5; + case '6': return key_code::num_6; + case '7': return key_code::num_7; + case '8': return key_code::num_8; + case '9': return key_code::num_9; + + // 功能键 (F1-F12) + case VK_F1: return key_code::f1; + case VK_F2: return key_code::f2; + case VK_F3: return key_code::f3; + case VK_F4: return key_code::f4; + case VK_F5: return key_code::f5; + case VK_F6: return key_code::f6; + case VK_F7: return key_code::f7; + case VK_F8: return key_code::f8; + case VK_F9: return key_code::f9; + case VK_F10: return key_code::f10; + case VK_F11: return key_code::f11; + case VK_F12: return key_code::f12; + + // 特殊键 + case VK_ESCAPE: return key_code::escape; + case VK_TAB: return key_code::tab; + case VK_CAPITAL: return key_code::caps_lock; + case VK_SHIFT: return key_code::left_shift; + case VK_LSHIFT: return key_code::left_shift; + case VK_RSHIFT: return key_code::right_shift; + case VK_CONTROL: return key_code::left_control; + case VK_LCONTROL: return key_code::left_control; + case VK_RCONTROL: return key_code::right_control; + case VK_MENU: return key_code::left_alt; + case VK_LMENU: return key_code::left_alt; + case VK_RMENU: return key_code::right_alt; + case VK_LWIN: return key_code::left_meta; + case VK_RWIN: return key_code::right_meta; + case VK_APPS: return key_code::context_menu; + case VK_SPACE: return key_code::space; + case VK_RETURN: return key_code::enter; + case VK_BACK: return key_code::backspace; + case VK_DELETE: return key_code::delete_key; + + // 导航键 + case VK_INSERT: return key_code::insert; + case VK_HOME: return key_code::home; + case VK_END: return key_code::end; + case VK_PRIOR: return key_code::page_up; + case VK_NEXT: return key_code::page_down; + case VK_UP: return key_code::up; + case VK_DOWN: return key_code::down; + case VK_LEFT: return key_code::left; + case VK_RIGHT: return key_code::right; + + // 符号键 + case VK_OEM_3: return key_code::back_quote; // ` + case VK_OEM_MINUS: return key_code::minus; // - + case VK_OEM_PLUS: return key_code::equals; // = + case VK_OEM_4: return key_code::left_bracket; // [ + case VK_OEM_6: return key_code::right_bracket; // ] + case VK_OEM_5: return key_code::backslash; // 左斜杠 + case VK_OEM_1: return key_code::semicolon; // ; + case VK_OEM_7: return key_code::apostrophe; // ' + case VK_OEM_COMMA: return key_code::comma; // , + case VK_OEM_PERIOD: return key_code::period; // . + case VK_OEM_2: return key_code::slash; // / + + // 小键盘 + case VK_NUMLOCK: return key_code::num_lock; + case VK_NUMPAD0: return key_code::numpad_0; + case VK_NUMPAD1: return key_code::numpad_1; + case VK_NUMPAD2: return key_code::numpad_2; + case VK_NUMPAD3: return key_code::numpad_3; + case VK_NUMPAD4: return key_code::numpad_4; + case VK_NUMPAD5: return key_code::numpad_5; + case VK_NUMPAD6: return key_code::numpad_6; + case VK_NUMPAD7: return key_code::numpad_7; + case VK_NUMPAD8: return key_code::numpad_8; + case VK_NUMPAD9: return key_code::numpad_9; + case VK_MULTIPLY: return key_code::numpad_multiply; + case VK_ADD: return key_code::numpad_add; + case VK_SUBTRACT: return key_code::numpad_subtract; + case VK_DECIMAL: return key_code::numpad_decimal; + case VK_DIVIDE: return key_code::numpad_divide; + + // 系统/媒体键 + case VK_SNAPSHOT: return key_code::print_screen; + case VK_SCROLL: return key_code::scroll_lock; + case VK_PAUSE: return key_code::pause; + case VK_VOLUME_MUTE: return key_code::volume_mute; + case VK_VOLUME_DOWN: return key_code::volume_down; + case VK_VOLUME_UP: return key_code::volume_up; + case VK_MEDIA_PLAY_PAUSE: return key_code::media_play; + case VK_MEDIA_STOP: return key_code::media_stop; + case VK_MEDIA_PREV_TRACK: return key_code::media_prev; + case VK_MEDIA_NEXT_TRACK: return key_code::media_next; + + // 未识别的键 + default: return key_code::unknown; + } +} + +mouse_button platform_event_to_mouse_button(uint32_t native_event, uintptr_t native_param) { + switch (native_event) { + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_LBUTTONDBLCLK: return mouse_button::left; + + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_RBUTTONDBLCLK: return mouse_button::right; + + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + case WM_MBUTTONDBLCLK: return mouse_button::middle; + + case WM_XBUTTONDOWN: + case WM_XBUTTONUP: + case WM_XBUTTONDBLCLK: + // 高位字节指示X按钮 + return HIWORD(native_param) == XBUTTON1 ? mouse_button::x1 : mouse_button::x2; + default: ; + } + + return mouse_button::none; +} + +mouse_action platform_event_to_mouse_action(uint32_t native_event, uintptr_t native_param) { + switch (native_event) { + case WM_LBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_XBUTTONDOWN: return mouse_action::press; + + case WM_LBUTTONUP: + case WM_RBUTTONUP: + case WM_MBUTTONUP: + case WM_XBUTTONUP: return mouse_action::release; + + case WM_LBUTTONDBLCLK: + case WM_RBUTTONDBLCLK: + case WM_MBUTTONDBLCLK: + case WM_XBUTTONDBLCLK: return mouse_action::dbl_click; + + case WM_MOUSEMOVE: return mouse_action::move; + default: ; + } + + return mouse_action::none; +} + +wheel_event platform_event_to_wheel_event(uint32_t native_event, uintptr_t native_param1, intptr_t native_param2) { + wheel_event result; + + // 获取当前修饰键状态 + if (GetKeyState(VK_CONTROL) & 0x8000) { + result.modifiers |= keyboard_modifier::ctrl; + } + if (GetKeyState(VK_SHIFT) & 0x8000) { + result.modifiers |= keyboard_modifier::shift; + } + if (GetKeyState(VK_MENU) & 0x8000) { + result.modifiers |= keyboard_modifier::alt; + } + if ((GetKeyState(VK_LWIN) & 0x8000) || (GetKeyState(VK_RWIN) & 0x8000)) { + result.modifiers |= keyboard_modifier::meta; + } + + switch (native_event) { + case WM_MOUSEWHEEL: + result.type = wheel_type::vertical; + result.delta_y = static_cast(GET_WHEEL_DELTA_WPARAM(native_param1)) / WHEEL_DELTA; + break; + + case WM_MOUSEHWHEEL: + result.type = wheel_type::horizontal; + result.delta_x = static_cast(GET_WHEEL_DELTA_WPARAM(native_param1)) / WHEEL_DELTA; + break; + + default: + break; + } + + return result; +} + diff --git a/src/misc/mirage_type.h b/src/misc/mirage_type.h index 94210ce..c7a52cb 100644 --- a/src/misc/mirage_type.h +++ b/src/misc/mirage_type.h @@ -13,6 +13,7 @@ #include #include "color.h" +#include "enum_flags.h" //-------------- 时间处理 -------------- @@ -39,12 +40,9 @@ inline time_type get_current_time() { return std::chrono::high_resolution_clock: * @brief 水平对齐方式 */ enum class horizontal_alignment { - left, - ///< 左对齐 - center, - ///< 居中对齐 - right, - ///< 右对齐 + left, ///< 左对齐 + center, ///< 居中对齐 + right, ///< 右对齐 stretch ///< 拉伸填充 }; @@ -53,12 +51,9 @@ enum class horizontal_alignment { * @brief 垂直对齐方式 */ enum class vertical_alignment { - top, - ///< 顶部对齐 - center, - ///< 居中对齐 - bottom, - ///< 底部对齐 + top, ///< 顶部对齐 + center, ///< 居中对齐 + bottom, ///< 底部对齐 stretch ///< 拉伸填充 }; @@ -67,11 +62,9 @@ enum class vertical_alignment { * @brief 文本水平对齐方式 */ enum class horizontal_text_alignment { - left, - ///< 文本左对齐 - center, - ///< 文本居中对齐 - right ///< 文本右对齐 + left, ///< 文本左对齐 + center, ///< 文本居中对齐 + right ///< 文本右对齐 }; /** @@ -79,38 +72,39 @@ enum class horizontal_text_alignment { * @brief 文本垂直对齐方式 */ enum class vertical_text_alignment { - top, - ///< 文本顶部对齐 - center, - ///< 文本居中对齐 - bottom ///< 文本底部对齐 + top, ///< 文本顶部对齐 + center, ///< 文本居中对齐 + bottom ///< 文本底部对齐 }; /** * @enum visibility - * @brief 组件可见性和交互行为 + * @brief 组件可见性和交互行为的位标志 */ -enum class visibility { - collapsed, - ///< 不可见且不占用布局空间 - hidden, - ///< 不可见但占用布局空间 - visible, - ///< 可见且可交互 - self_hit_test_invisible, - ///< 自身不可点击, 但子控件可点击 - hit_test_invisible, - ///< 整个组件树不可点击 +enum class visibility : uint32_t { + none = 0, ///< 无特定可见性设置 + collapsed = 1 << 0, ///< 不可见且不占用布局空间 + hidden = 1 << 1, ///< 不可见但占用布局空间 + visible = 1 << 2, ///< 可见且可交互 + self_hit_test_invisible = 1 << 3, ///< 自身不可点击, 但子控件可点击 + hit_test_invisible = 1 << 4, ///< 整个组件树不可点击 + + // 常用组合 + all = 0xFFFFFFFF, ///< 所有可见性标志 + any_visible = visible | self_hit_test_invisible | hit_test_invisible, ///< 任何可见状态 + any_invisible = collapsed | hidden, ///< 任何不可见状态 + any_hit_testable = visible, ///< 任何可命中测试状态 }; +// 为visibility枚举启用位标志功能 +DEFINE_ENUM_FLAGS(visibility) /** * @enum orientation * @brief 组件方向 */ enum class orientation { - horizontal, - ///< 水平方向 - vertical ///< 垂直方向 + horizontal, ///< 水平方向 + vertical ///< 垂直方向 }; //-------------- 文本流方向 -------------- @@ -120,10 +114,8 @@ enum class orientation { * @brief 文本和布局流动方向 */ enum class flow_direction { - left_to_right, - ///< 从左到右 - right_to_left, - ///< 从右到左 + left_to_right, ///< 从左到右 + right_to_left, ///< 从右到左 }; /** @@ -131,14 +123,10 @@ enum class flow_direction { * @brief 流动方向首选项 */ enum class flow_direction_preference { - inherit, - ///< 继承父级设置 - culture, - ///< 根据当前文化设置 - left_to_right, - ///< 强制从左到右 - right_to_left, - ///< 强制从右到左 + inherit, ///< 继承父级设置 + culture, ///< 根据当前文化设置 + left_to_right, ///< 强制从左到右 + right_to_left, ///< 强制从右到左 }; /** diff --git a/src/widget/compound_widget/mbutton.cpp b/src/widget/compound_widget/mbutton.cpp index d47c908..b19a0a3 100644 --- a/src/widget/compound_widget/mbutton.cpp +++ b/src/widget/compound_widget/mbutton.cpp @@ -1,13 +1,20 @@ #include "mbutton.h" +#include + #include "geometry/margin.h" +mbutton::mbutton() { + set_visibility(visibility::visible); + color_ = {0.5, 0.5, 0.5, 1}; +} + void mbutton::on_paint(mirage_paint_context& in_context) { in_context.drawer().make_rounded_rect( { 0, 0 }, in_context.geo().get_local_size(), in_context.geo(), - {{0.5, 0.5, 0.5, 1}}, + { color_ }, { 10 } ); } @@ -22,3 +29,57 @@ Eigen::Vector2f mbutton::compute_desired_size(float in_layout_scale_multiplier) return desired_size; } + +hit_test_handle mbutton::on_click(const Eigen::Vector2f& in_position, mouse_button in_button) { + mborder::on_click(in_position, in_button); + std::cout << this << ": Button clicked!" << in_position.x() << ", " << in_position.y() << std::endl; + color_ = {0, 0, 1, 1}; + return hit_test_handle::handled(); +} + +hit_test_handle mbutton::on_double_click(const Eigen::Vector2f& in_position, mouse_button in_button) { + mborder::on_double_click(in_position, in_button); + std::cout << this << ": Button double clicked!" << in_position.x() << ", " << in_position.y() << std::endl; + color_ = {1, 1, 1, 1}; + return hit_test_handle::handled(); +} + +void mbutton::on_mouse_enter() { + mborder::on_mouse_enter(); + std::cout << this << ": Mouse entered!" << std::endl; + color_ = {0, 1, 0, 1}; +} + +void mbutton::on_mouse_leave() { + mborder::on_mouse_leave(); + std::cout << this << ": Mouse left!" << std::endl; + color_ = {0.5, 0.5, 0.5, 1}; +} + +hit_test_handle mbutton::on_mouse_move(const Eigen::Vector2f& in_position) { + mborder::on_mouse_move(in_position); + std::cout << this << ": Mouse moved!" << in_position.x() << ", " << in_position.y() << std::endl; + color_ = {1, 1, 0, 1}; + return hit_test_handle::handled(); +} + +hit_test_handle mbutton::on_mouse_press(const Eigen::Vector2f& in_position, mouse_button in_button) { + mborder::on_mouse_press(in_position, in_button); + std::cout << this << ": Mouse pressed!" << in_position.x() << ", " << in_position.y() << std::endl; + color_ = {0, 0, 1, 1}; + return hit_test_handle::handled(); +} + +hit_test_handle mbutton::on_mouse_release(const Eigen::Vector2f& in_position, mouse_button in_button) { + mborder::on_mouse_release(in_position, in_button); + std::cout << this << ": Mouse released!" << in_position.x() << ", " << in_position.y() << std::endl; + color_ = {1, 0, 0, 1}; + return hit_test_handle::handled(); +} + +hit_test_handle mbutton::on_mouse_wheel(const Eigen::Vector2f& in_position, const wheel_event& in_delta) { + mborder::on_mouse_wheel(in_position, in_delta); + std::cout << this << ": Mouse wheeled!" << in_position.x() << ", " << in_position.y() << std::endl; + color_ = {0, 1, 1, 1}; + return hit_test_handle::handled(); +} diff --git a/src/widget/compound_widget/mbutton.h b/src/widget/compound_widget/mbutton.h index 7e7bebf..a423c5b 100644 --- a/src/widget/compound_widget/mbutton.h +++ b/src/widget/compound_widget/mbutton.h @@ -5,8 +5,18 @@ class mbutton : public mborder { public: + mbutton(); virtual void on_paint(mirage_paint_context& in_context) override; virtual Eigen::Vector2f compute_desired_size(float in_layout_scale_multiplier) const override; -private: + virtual hit_test_handle on_click(const Eigen::Vector2f& in_position, mouse_button in_button) override; + virtual hit_test_handle on_double_click(const Eigen::Vector2f& in_position, mouse_button in_button) override; + virtual void on_mouse_enter() override; + virtual void on_mouse_leave() override; + virtual hit_test_handle on_mouse_move(const Eigen::Vector2f& in_position) override; + virtual hit_test_handle on_mouse_press(const Eigen::Vector2f& in_position, mouse_button in_button) override; + virtual hit_test_handle on_mouse_release(const Eigen::Vector2f& in_position, mouse_button in_button) override; + virtual hit_test_handle on_mouse_wheel(const Eigen::Vector2f& in_position, const wheel_event& in_delta) override; +private: + linear_color color_; }; diff --git a/src/widget/hit_test/hit_test_manager.cpp b/src/widget/hit_test/hit_test_manager.cpp new file mode 100644 index 0000000..f527df3 --- /dev/null +++ b/src/widget/hit_test/hit_test_manager.cpp @@ -0,0 +1,32 @@ +/** + * @file hit_test_manager.cpp + * @brief 命中测试管理器的实现 + */ + +#include "hit_test_manager.h" +#include "core/window/mwindow.h" +#include "widget/mwidget.h" +#include "geometry/widget_layout_tree.h" +#include "geometry/arranged_children.h" + +//-------------- 构造函数 -------------- + +hit_test_manager::hit_test_manager(mwindow* in_window) + : window_(in_window) +{ +} + +void hit_test_manager::cursor_leave() { +} + +void hit_test_manager::invalidate_cache() { + cache_valid_ = false; +} + +const std::shared_ptr& hit_test_manager::get_last_hit_widget() const { + return last_hit_widget_; +} + +const std::shared_ptr& hit_test_manager::get_last_hover_widget() const { + return last_hover_widget_; +} diff --git a/src/widget/hit_test/hit_test_manager.h b/src/widget/hit_test/hit_test_manager.h new file mode 100644 index 0000000..d908fc6 --- /dev/null +++ b/src/widget/hit_test/hit_test_manager.h @@ -0,0 +1,38 @@ +#pragma once +/** + * @file hit_test_manager.h + * @brief 定义UI命中测试管理器 + */ + +#include "hit_test_result.h" +#include "hit_test_parameters.h" + +class mwindow; +class mwidget; +class geometry_t; + +/** + * @class hit_test_manager + * @brief 管理UI命中测试流程 + */ +class hit_test_manager { +public: + // 构造函数 + explicit hit_test_manager(mwindow* in_window); + + void cursor_leave(); + + // 缓存控制 + void invalidate_cache(); + + // 访问方法 + [[nodiscard]] const std::shared_ptr& get_last_hit_widget() const; + [[nodiscard]] const std::shared_ptr& get_last_hover_widget() const; + +private: + // 成员变量 + mwindow* window_{nullptr}; ///< 关联的窗口 + std::shared_ptr last_hit_widget_{nullptr}; ///< 最近一次点击命中的组件 + std::shared_ptr last_hover_widget_{nullptr}; ///< 最近一次悬停的组件 + bool cache_valid_{false}; ///< 缓存是否有效 +}; diff --git a/src/widget/hit_test/hit_test_parameters.cpp b/src/widget/hit_test/hit_test_parameters.cpp new file mode 100644 index 0000000..c64ea49 --- /dev/null +++ b/src/widget/hit_test/hit_test_parameters.cpp @@ -0,0 +1,54 @@ +/** +* @file hit_test_parameters.cpp + * @brief 命中测试参数类的实现 + */ + +#include "hit_test_parameters.h" + +//-------------- 构造函数 -------------- + +hit_test_parameters::hit_test_parameters(const Eigen::Vector2f& in_position) + : position_(in_position) +{ +} + +hit_test_parameters::hit_test_parameters(const Eigen::Vector2f& in_position, input_device_type in_device_type) + : position_(in_position), device_type_(in_device_type) +{ +} + +//-------------- 访问方法 -------------- + +const Eigen::Vector2f& hit_test_parameters::get_position() const { + return position_; +} + +input_device_type hit_test_parameters::get_device_type() const { + return device_type_; +} + +const keyboard_modifiers& hit_test_parameters::get_modifiers() const { + return modifiers_; +} + +bool hit_test_parameters::is_hover_test() const { + return is_hover_test_; +} + +//-------------- 设置方法 -------------- + +void hit_test_parameters::set_position(const Eigen::Vector2f& in_position) { + position_ = in_position; +} + +void hit_test_parameters::set_device_type(input_device_type in_device_type) { + device_type_ = in_device_type; +} + +void hit_test_parameters::set_modifiers(const keyboard_modifiers& in_modifiers) { + modifiers_ = in_modifiers; +} + +void hit_test_parameters::set_hover_test(bool in_is_hover) { + is_hover_test_ = in_is_hover; +} diff --git a/src/widget/hit_test/hit_test_parameters.h b/src/widget/hit_test/hit_test_parameters.h new file mode 100644 index 0000000..6a9abbb --- /dev/null +++ b/src/widget/hit_test/hit_test_parameters.h @@ -0,0 +1,58 @@ +#pragma once +/** + * @file hit_test_parameters.h + * @brief 定义UI命中测试参数的数据结构 + */ + +#include + +/** + * @enum input_device_type + * @brief 输入设备类型枚举 + */ +enum class input_device_type { + mouse, ///< 鼠标输入 + touch, ///< 触摸输入 + pen, ///< 手写笔输入 + keyboard ///< 键盘输入(焦点导航) +}; + +/** + * @struct keyboard_modifiers + * @brief 键盘修饰键状态 + */ +struct keyboard_modifiers { + bool ctrl{false}; ///< Ctrl键是否按下 + bool shift{false}; ///< Shift键是否按下 + bool alt{false}; ///< Alt键是否按下 + bool meta{false}; ///< Meta/Win/Cmd键是否按下 +}; + +/** + * @class hit_test_parameters + * @brief 存储执行命中测试所需的参数 + */ +class hit_test_parameters { +public: + // 构造函数 + explicit hit_test_parameters(const Eigen::Vector2f& in_position); + hit_test_parameters(const Eigen::Vector2f& in_position, input_device_type in_device_type); + + // 访问方法 + [[nodiscard]] const Eigen::Vector2f& get_position() const; + [[nodiscard]] input_device_type get_device_type() const; + [[nodiscard]] const keyboard_modifiers& get_modifiers() const; + [[nodiscard]] bool is_hover_test() const; + + // 设置方法 + void set_position(const Eigen::Vector2f& in_position); + void set_device_type(input_device_type in_device_type); + void set_modifiers(const keyboard_modifiers& in_modifiers); + void set_hover_test(bool in_is_hover); + +private: + Eigen::Vector2f position_{0.0f, 0.0f}; ///< 测试点位置 + input_device_type device_type_{input_device_type::mouse}; ///< 输入设备类型 + keyboard_modifiers modifiers_{}; ///< 修饰键状态 + bool is_hover_test_{false}; ///< 是否为悬停测试 +}; diff --git a/src/widget/hit_test/hit_test_result.cpp b/src/widget/hit_test/hit_test_result.cpp new file mode 100644 index 0000000..1125132 --- /dev/null +++ b/src/widget/hit_test/hit_test_result.cpp @@ -0,0 +1,7 @@ +/** +* @file hit_test_result.cpp + * @brief 命中测试结果类的实现 + */ + +#include "hit_test_result.h" +#include "widget/mwidget.h" diff --git a/src/widget/hit_test/hit_test_result.h b/src/widget/hit_test/hit_test_result.h new file mode 100644 index 0000000..2aef261 --- /dev/null +++ b/src/widget/hit_test/hit_test_result.h @@ -0,0 +1,50 @@ +#pragma once +/** + * @file hit_test_result.h + * @brief 定义UI命中测试结果的数据结构 + */ + +#include +#include +#include + +class mwidget; + +/** + * @enum hit_test_result_behavior + * @brief 定义命中测试结果的行为 + */ +enum class hit_test_result_behavior { + continue_test, ///< 继续命中测试 + handle_event ///< 处理事件并停止测试 +}; + +struct hit_test_handle { +public: + static auto unhandled() { + hit_test_handle result{}; + result.behavior_ = hit_test_result_behavior::continue_test; + return result; + } + static auto handled() { + hit_test_handle result{}; + result.behavior_ = hit_test_result_behavior::handle_event; + return result; + } + + bool is_handled() const { + return behavior_ == hit_test_result_behavior::handle_event; + } +private: + hit_test_result_behavior behavior_{ hit_test_result_behavior::continue_test }; +}; + +struct hit_test_result { + std::shared_ptr widget; + Eigen::Vector2f widget_space_pos; + + operator bool() const { + return widget != nullptr; + } + operator std::shared_ptr() const { return widget; } +}; \ No newline at end of file diff --git a/src/widget/layout_utils.h b/src/widget/layout_utils.h index 700e4ab..d8405ed 100644 --- a/src/widget/layout_utils.h +++ b/src/widget/layout_utils.h @@ -167,7 +167,7 @@ void arrange_box_children( for (const auto& slot : child_slots) { auto widget = slot.get(); - if (widget && widget->get_visibility() != visibility::collapsed) { + if (widget && has_any_flag(widget->get_visibility(), visibility::any_visible)) { // 获取margin值 const auto& margin = slot.margin(); const float margin_left = margin.left; @@ -192,6 +192,7 @@ void arrange_box_children( // 累加主轴方向上的margin total_margins += margin_start + margin_end; + widget->cache_desired_size(1); slot_widget_info info{widget, &slot, 0.0f, margin_start, margin_end, margin_cross_start, margin_cross_end}; const auto& size_attr = slot.size(); @@ -248,9 +249,9 @@ void arrange_box_children( // 应用交叉轴margin减少size if (is_horizontal) { - size.y() -= (info.margin_cross_start + info.margin_cross_end); + size.y() -= info.margin_cross_start + info.margin_cross_end; } else { - size.x() -= (info.margin_cross_start + info.margin_cross_end); + size.x() -= info.margin_cross_start + info.margin_cross_end; } if (is_horizontal) { @@ -289,7 +290,7 @@ Eigen::Vector2f compute_box_desired_size( for (const auto& slot : child_slots) { auto widget = slot.get(); - if (widget && widget->get_visibility() != visibility::collapsed) { + if (widget && has_any_flag(widget->get_visibility(), visibility::any_visible)) { Eigen::Vector2f widget_desired_size = widget->get_desired_size(); if (LayoutOrientation == orientation::horizontal) { desired_size.x() += widget_desired_size.x(); diff --git a/src/widget/mwidget.cpp b/src/widget/mwidget.cpp index cb0f8d0..e01a24c 100644 --- a/src/widget/mwidget.cpp +++ b/src/widget/mwidget.cpp @@ -1,5 +1,38 @@ #include "mwidget.h" +//-------------- 尺寸计算 -------------- + void mwidget::cache_desired_size(float in_layout_scale_multiplier) { set_desired_size(compute_desired_size(in_layout_scale_multiplier)); } + +//-------------- 命中测试 -------------- + +bool mwidget::can_hit_test() const { + const auto vis = get_visibility(); + + // 不可见组件不参与命中测试 + if (has_any_flag(vis, visibility::any_invisible)) { + return false; + } + + // 如果整个组件树都不可点击 + if (has_flag(vis, visibility::hit_test_invisible)) { + return false; + } + + // 如果组件自身不可点击(但子组件可以) + if (has_flag(vis, visibility::self_hit_test_invisible)) { + return false; + } + + return is_hittable_; +} + +bool mwidget::is_hittable() const { + return is_hittable_; +} + +void mwidget::set_hittable(bool in_hittable) { + is_hittable_ = in_hittable; +} diff --git a/src/widget/mwidget.h b/src/widget/mwidget.h index c217170..4bcbf8c 100644 --- a/src/widget/mwidget.h +++ b/src/widget/mwidget.h @@ -9,9 +9,11 @@ #include "core/render_elements.h" #include "geometry/arranged_children.h" +#include "hit_test/hit_test_result.h" #include "misc/mirage_paint_context.h" +#include "misc/key_type/key_type.h" -class mirage_window; +class mwindow; /** * @class mwidget @@ -115,7 +117,7 @@ public: * @brief 设置关联的窗口 * @param in_window 窗口指针 */ - void set_window(mirage_window* in_window) { window_ = in_window; } + void set_window(mwindow* in_window) { window_ = in_window; } //-------------- 启用状态 -------------- @@ -140,6 +142,86 @@ public: */ auto should_be_enabled(bool in_parent_enabled) const { return in_parent_enabled && is_enabled(); } + //-------------- 命中测试 -------------- + + /** + * @brief 检查组件是否可以参与命中测试 + * @return 如果可以参与命中测试则为true + */ + [[nodiscard]] bool can_hit_test() const; + + /** + * @brief 设置组件是否可命中测试 + * @param in_hittable 是否可命中测试 + */ + void set_hittable(bool in_hittable); + + /** + * @brief 获取组件是否可命中测试 + * @return 是否可命中测试 + */ + [[nodiscard]] bool is_hittable() const; + + //-------------- 鼠标事件处理 -------------- + + /** + * @brief 处理鼠标进入事件 + * @result 命中测试结果 + */ + virtual void on_mouse_enter() {} + + /** + * @brief 处理鼠标离开事件 + * @result 命中测试结果 + */ + virtual void on_mouse_leave() {} + + /** + * @brief 处理鼠标移动事件 + * @param in_position 本地坐标系中的鼠标位置 + * @result 命中测试结果 + */ + virtual hit_test_handle on_mouse_move(const Eigen::Vector2f& in_position) { return hit_test_handle::unhandled(); } + + /** + * @brief 处理鼠标按下事件 + * @param in_position 本地坐标系中的鼠标位置 + * @param in_button 按下的鼠标按钮 + * @result 命中测试结果 + */ + virtual hit_test_handle on_mouse_press(const Eigen::Vector2f& in_position, mouse_button in_button) { return hit_test_handle::unhandled(); } + + /** + * @brief 处理鼠标释放事件 + * @param in_position 本地坐标系中的鼠标位置 + * @param in_button 释放的鼠标按钮 + * @result 命中测试结果 + */ + virtual hit_test_handle on_mouse_release(const Eigen::Vector2f& in_position, mouse_button in_button) { return hit_test_handle::unhandled(); } + + /** + * @brief 处理鼠标点击事件 + * @param in_position 本地坐标系中的鼠标位置 + * @param in_button 点击的鼠标按钮 + * @result 命中测试结果 + */ + virtual hit_test_handle on_click(const Eigen::Vector2f& in_position, mouse_button in_button) { return hit_test_handle::unhandled(); } + + /** + * @brief 处理鼠标双击事件 + * @param in_position 本地坐标系中的鼠标位置 + * @param in_button 双击的鼠标按钮 + * @result 命中测试结果 + */ + virtual hit_test_handle on_double_click(const Eigen::Vector2f& in_position, mouse_button in_button) { return hit_test_handle::unhandled(); } + + /** + * @brief 处理鼠标滚轮事件 + * @param in_position 本地坐标系中的鼠标位置 + * @param in_delta 滚轮增量 + * @result 命中测试结果 + */ + virtual hit_test_handle on_mouse_wheel(const Eigen::Vector2f& in_position, const wheel_event& in_delta) { return hit_test_handle::unhandled(); } private: /** * @brief 设置组件期望大小 @@ -153,7 +235,7 @@ private: visibility visibility_{ visibility::self_hit_test_invisible }; /** 关联的窗口指针 */ - mirage_window* window_{}; + mwindow* window_{}; /** 父组件的弱引用 */ std::weak_ptr parent_; @@ -163,6 +245,9 @@ private: /** 组件启用状态 */ bool enable_{ true }; + + /** 组件是否可命中测试 */ + bool is_hittable_{ true }; }; /**