From e44a9bda57784ea429dfa9accac8e88eb086617a Mon Sep 17 00:00:00 2001 From: Nanako <469449812@qq.com> Date: Wed, 26 Mar 2025 17:25:55 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=A0=E9=99=A4ECS=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitmodules | 3 - CMakeLists.txt | 5 +- example/CMakeLists.txt | 2 +- example/src/main.cpp | 34 +- src/mirage_app/CMakeLists.txt | 8 + src/mirage_app/mirage.cpp | 90 ++++ src/{mirage_core => mirage_app}/mirage.h | 15 +- src/mirage_core/CMakeLists.txt | 12 +- src/mirage_core/mirage.cpp | 59 --- src/mirage_core/misc/hit_test_result.cpp | 6 - src/mirage_core/misc/mirage_type.h | 2 +- src/mirage_core/widget/mwidget.cpp | 85 ---- .../widget_tree/widget_component.cpp | 33 -- .../widget_tree/widget_component.h | 93 ---- .../widget_tree/widget_manager.cpp | 38 -- src/mirage_core/widget_tree/widget_manager.h | 85 ---- src/mirage_core/widget_tree/widget_system.cpp | 385 ---------------- src/mirage_core/widget_tree/widget_system.h | 204 --------- .../window/android/android_render_window.cpp | 4 - .../window/ios/ios_render_window.cpp | 8 - .../window/linux/linux_render_window.cpp | 8 - .../window/mac/mac_render_window.mm | 8 - src/mirage_core/window/mwindow.cpp | 97 ---- src/mirage_core/window/mwindow.h | 431 ------------------ src/mirage_render/CMakeLists.txt | 18 + .../platform_window/platform_window.cpp | 36 ++ .../platform_window/platform_window.h | 242 ++++++++++ .../windows/windows_window.cpp} | 75 ++- .../render/mirage_paint_context.cpp | 19 +- .../render/mirage_paint_context.h | 21 +- .../render/render_context.h | 83 +++- .../render/render_elements.cpp | 0 .../render/render_elements.h | 0 .../render/render_image.h | 0 .../render/windows/pixel_format_convert.h | 0 .../render/windows/render_image.cpp | 3 +- .../render/windows/windows_render_context.cpp | 15 +- .../render/windows/windows_render_context.h | 12 +- .../render/windows/windows_render_state.cpp | 11 +- .../render/windows/windows_render_state.h | 8 +- .../shaders/mirage_rounded_rect.slang | 0 .../shaders/mirage_util.slang | 0 .../shaders/test.slang | 0 src/mirage_widget/CMakeLists.txt | 8 + .../widget/compound_widget/mborder.cpp | 0 .../widget/compound_widget/mborder.h | 2 +- .../widget/compound_widget/mbutton.cpp | 28 +- .../widget/compound_widget/mbutton.h | 2 +- .../widget/leaf_widget/mimage.cpp | 0 .../widget/leaf_widget/mimage.h | 0 .../widget/mcompound_widget.h | 25 +- .../widget/mleaf_widget.h | 0 .../widget/mpanel_widget.h | 55 +-- src/mirage_widget/widget/mwidget.cpp | 109 +++++ .../widget/mwidget.h | 77 ++-- .../widget/panel_widget/mbox.cpp | 2 +- .../widget/panel_widget/mbox.h | 0 .../widget/slot_util.h | 5 +- .../widget/widget_size.h | 0 .../widget_tree}/hit_test_result.h | 14 +- .../widget_utils}/layout_utils.cpp | 0 .../widget_utils}/layout_utils.h | 17 +- src/mirage_widget/window/mwindow.cpp | 315 +++++++++++++ src/mirage_widget/window/mwindow.h | 173 +++++++ third_party/mustache | 1 - 65 files changed, 1250 insertions(+), 1841 deletions(-) create mode 100644 src/mirage_app/CMakeLists.txt create mode 100644 src/mirage_app/mirage.cpp rename src/{mirage_core => mirage_app}/mirage.h (61%) delete mode 100644 src/mirage_core/mirage.cpp delete mode 100644 src/mirage_core/misc/hit_test_result.cpp delete mode 100644 src/mirage_core/widget/mwidget.cpp delete mode 100644 src/mirage_core/widget_tree/widget_component.cpp delete mode 100644 src/mirage_core/widget_tree/widget_component.h delete mode 100644 src/mirage_core/widget_tree/widget_manager.cpp delete mode 100644 src/mirage_core/widget_tree/widget_manager.h delete mode 100644 src/mirage_core/widget_tree/widget_system.cpp delete mode 100644 src/mirage_core/widget_tree/widget_system.h delete mode 100644 src/mirage_core/window/android/android_render_window.cpp delete mode 100644 src/mirage_core/window/ios/ios_render_window.cpp delete mode 100644 src/mirage_core/window/linux/linux_render_window.cpp delete mode 100644 src/mirage_core/window/mac/mac_render_window.mm delete mode 100644 src/mirage_core/window/mwindow.cpp delete mode 100644 src/mirage_core/window/mwindow.h create mode 100644 src/mirage_render/CMakeLists.txt create mode 100644 src/mirage_render/platform_window/platform_window.cpp create mode 100644 src/mirage_render/platform_window/platform_window.h rename src/{mirage_core/window/windows/windows_render_window.cpp => mirage_render/platform_window/windows/windows_window.cpp} (76%) rename src/{mirage_core => mirage_render}/render/mirage_paint_context.cpp (68%) rename src/{mirage_core => mirage_render}/render/mirage_paint_context.h (85%) rename src/{mirage_core => mirage_render}/render/render_context.h (51%) rename src/{mirage_core => mirage_render}/render/render_elements.cpp (100%) rename src/{mirage_core => mirage_render}/render/render_elements.h (100%) rename src/{mirage_core => mirage_render}/render/render_image.h (100%) rename src/{mirage_core => mirage_render}/render/windows/pixel_format_convert.h (100%) rename src/{mirage_core => mirage_render}/render/windows/render_image.cpp (92%) rename src/{mirage_core => mirage_render}/render/windows/windows_render_context.cpp (96%) rename src/{mirage_core => mirage_render}/render/windows/windows_render_context.h (90%) rename src/{mirage_core => mirage_render}/render/windows/windows_render_state.cpp (97%) rename src/{mirage_core => mirage_render}/render/windows/windows_render_state.h (93%) rename src/{mirage_core => mirage_render}/shaders/mirage_rounded_rect.slang (100%) rename src/{mirage_core => mirage_render}/shaders/mirage_util.slang (100%) rename src/{mirage_core => mirage_render}/shaders/test.slang (100%) create mode 100644 src/mirage_widget/CMakeLists.txt rename src/{mirage_core => mirage_widget}/widget/compound_widget/mborder.cpp (100%) rename src/{mirage_core => mirage_widget}/widget/compound_widget/mborder.h (97%) rename src/{mirage_core => mirage_widget}/widget/compound_widget/mbutton.cpp (85%) rename src/{mirage_core => mirage_widget}/widget/compound_widget/mbutton.h (92%) rename src/{mirage_core => mirage_widget}/widget/leaf_widget/mimage.cpp (100%) rename src/{mirage_core => mirage_widget}/widget/leaf_widget/mimage.h (100%) rename src/{mirage_core => mirage_widget}/widget/mcompound_widget.h (88%) rename src/{mirage_core => mirage_widget}/widget/mleaf_widget.h (100%) rename src/{mirage_core => mirage_widget}/widget/mpanel_widget.h (63%) create mode 100644 src/mirage_widget/widget/mwidget.cpp rename src/{mirage_core => mirage_widget}/widget/mwidget.h (54%) rename src/{mirage_core => mirage_widget}/widget/panel_widget/mbox.cpp (96%) rename src/{mirage_core => mirage_widget}/widget/panel_widget/mbox.h (100%) rename src/{mirage_core => mirage_widget}/widget/slot_util.h (92%) rename src/{mirage_core => mirage_widget}/widget/widget_size.h (100%) rename src/{mirage_core/misc => mirage_widget/widget_tree}/hit_test_result.h (82%) rename src/{mirage_core/geometry => mirage_widget/widget_utils}/layout_utils.cpp (100%) rename src/{mirage_core/geometry => mirage_widget/widget_utils}/layout_utils.h (94%) create mode 100644 src/mirage_widget/window/mwindow.cpp create mode 100644 src/mirage_widget/window/mwindow.h delete mode 160000 third_party/mustache diff --git a/.gitmodules b/.gitmodules index 7281f11..764bea8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ [submodule "third_party/msdfgen"] path = third_party/msdfgen url = https://github.com/Chlumsky/msdfgen.git -[submodule "third_party/mustache"] - path = third_party/mustache - url = https://nanako.site/gitea/Nanako/mustache.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 3e55117..968c82d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,7 +35,6 @@ set(MSDFGEN_USE_VCPKG OFF CACHE BOOL "Use VCPKG for MSDFGen" FORCE) set(MSDFGEN_USE_OPENMP ON CACHE BOOL "Use OpenMP for MSDFGen" FORCE) set(MSDFGEN_BUILD_STANDALONE ON CACHE BOOL "Build MSDFGen standalone" FORCE) set(MSDFGEN_BUILD_STANDALONE ON CACHE BOOL "Build MSDFGen standalone" FORCE) -set(MUSTACHE_BUILD_SHARED OFF CACHE BOOL "Build shared libraries?" FORCE) # 配置输出目录 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) @@ -56,11 +55,13 @@ else () endif () add_subdirectory(third_party/msdfgen) -add_subdirectory(third_party/mustache) add_subdirectory(src/sokol) +add_subdirectory(src/mirage_render) add_subdirectory(src/mirage_image) add_subdirectory(src/mirage_stb_image) add_subdirectory(src/mirage_core) +add_subdirectory(src/mirage_widget) +add_subdirectory(src/mirage_app) set(BUILD_EXAMPLE FALSE CACHE BOOL "Build example") if (BUILD_EXAMPLE) diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 7be86f4..31b791a 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -4,7 +4,7 @@ set(CMAKE_CXX_STANDARD 23) set(SRC_FILES "") retrieve_files(${CMAKE_CURRENT_SOURCE_DIR}/src SRC_FILES) add_executable(${PROJECT_NAME} ${SRC_FILES}) -target_link_libraries(${PROJECT_NAME} PRIVATE mirage_core) +target_link_libraries(${PROJECT_NAME} PRIVATE mirage_app) if (MSVC) target_link_options(${PROJECT_NAME} PRIVATE "/SUBSYSTEM:WINDOWS" "/ENTRY:mainCRTStartup") endif() diff --git a/example/src/main.cpp b/example/src/main.cpp index 7d3a4be..ce0b2f3 100644 --- a/example/src/main.cpp +++ b/example/src/main.cpp @@ -11,8 +11,7 @@ #include "mirage_stb_image.h" int main(int argc, char* argv[]) { - mirage_app app; - app.init(); + mirage_app::get().init(); auto file = mapped_file::create(); file->map_file(L"Z:/Root/Local/NanakoDisk/涩图/可爱偷猴计划/东风谷早苗/57092520_p1.jpg"); @@ -26,32 +25,11 @@ int main(int argc, char* argv[]) { auto accessor = pixel_factory::create_image_accessor(pixel_format, heap->data, heap->info.width, heap->info.height); pixel_factory::destroy_image_accessor(accessor); - auto window = std::make_shared(); - window->create_window(800, 600, L"Hello, World!"); - window->show(); + auto window = std::make_shared(800, 600, L"Hello, World!"); + window->set_content( + std::make_shared() + ); - mirage_app::get_render_context()->setup_surface(window.get()); - widget_manager::get().init_window(window); - - auto weak_vbox = widget_manager::get().new_widget(window.get()); - auto vbox = weak_vbox.lock(); - - vbox->add_child().margin({ 5 }); - vbox->add_child().margin({ 5 }).stretch(1); - - auto weak_hbox = vbox->add_child().stretch(2).get(); - auto hbox = std::static_pointer_cast(weak_hbox.lock()); - hbox->add_child().margin({ 5 }); - hbox->add_child().margin({ 5 }).stretch(); - hbox->add_child().margin({ 5 }); - - - vbox->add_child().margin({ 5 }); - vbox->add_child().margin({ 5 }); - - - window->set_content(vbox); - - app.run(); + mirage_app::get().run(); return 0; } diff --git a/src/mirage_app/CMakeLists.txt b/src/mirage_app/CMakeLists.txt new file mode 100644 index 0000000..2fe3872 --- /dev/null +++ b/src/mirage_app/CMakeLists.txt @@ -0,0 +1,8 @@ +project(mirage_app) + +set(SRC_FILES) +retrieve_files(${CMAKE_CURRENT_SOURCE_DIR} SRC_FILES) +add_library(${PROJECT_NAME} STATIC ${SRC_FILES}) + +target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(${PROJECT_NAME} PUBLIC mirage_core mirage_widget mirage_render) diff --git a/src/mirage_app/mirage.cpp b/src/mirage_app/mirage.cpp new file mode 100644 index 0000000..9121f71 --- /dev/null +++ b/src/mirage_app/mirage.cpp @@ -0,0 +1,90 @@ + +#include "mirage.h" + +#include +#include + +#include "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, + uint32_t line_nr, const char* filename_or_null, void* user_data) { + /* 处理NULL指针情况 */ + const char* tag_str = tag ? tag : "(null)"; + const char* msg_str = message_or_null ? message_or_null : "(no message)"; + const char* file_str = filename_or_null ? filename_or_null : "(unknown)"; + + /* 根据log_level确定日志级别字符串和输出流 */ + const char* level_str; + FILE* output_stream; + + switch (log_level) { + case 0: + level_str = "PANIC"; + output_stream = stderr; + break; + case 1: + level_str = "ERROR"; + output_stream = stderr; + break; + case 2: + level_str = "WARNING"; + output_stream = stderr; + break; + case 3: + level_str = "INFO"; + output_stream = stdout; + break; + default: + level_str = "UNKNOWN"; + output_stream = stderr; // 默认使用stderr + break; + } + + /* 打印格式化的日志消息到相应的流 */ + fprintf(output_stream, "[%s][%s][ID:%u][%s:%u] %s\n", tag_str, level_str, log_item_id, file_str, line_nr, msg_str); + + /* 在此实现中未使用user_data */ + (void) user_data; +} + + +void mirage_app::init() { + duration_type duration; + { + mirage_scoped_duration_timer timer(duration); + + last_time = get_current_time(); + render_context = mirage_create_render_context(); + render_context->init(); + const sg_desc desc = { + .logger = { + .func = mirage_log, + .user_data = nullptr + }, + .environment = render_context->get_environment(), + }; + sg_setup(desc); + render_context->end_init(); + } + // 初始化用时 + std::cout << "mirage: " << "Initialization took " << std::chrono::duration_cast(duration).count() << "ms" << std::endl; +} + + +void mirage_app::run() { + while (!platform_window::get_windows().empty()) { + delta_time = get_current_time() - last_time; + platform_window::poll_events(); + + for (const auto& window: mwindow::get_all()) { + window->update(); + } + + last_time = get_current_time(); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + running = false; + render_context->cleanup(); + delete render_context; +} diff --git a/src/mirage_core/mirage.h b/src/mirage_app/mirage.h similarity index 61% rename from src/mirage_core/mirage.h rename to src/mirage_app/mirage.h index 6dd89a9..f59863e 100644 --- a/src/mirage_core/mirage.h +++ b/src/mirage_app/mirage.h @@ -1,6 +1,7 @@ #pragma once #include +#include "misc/mirage_type.h" #include "render/render_context.h" class mirage_render_context; @@ -10,6 +11,19 @@ public: void init(); void run(); + static mirage_app& get() { + static mirage_app instance; + return instance; + } + + [[nodiscard]] bool is_running() const { + return running; + } + + [[nodiscard]] duration_type get_delta_time() const { + return delta_time; + } + [[nodiscard]] static mirage_render_context* get_render_context() { return render_context; } @@ -18,5 +32,4 @@ private: time_type last_time = {}; duration_type delta_time = {}; std::atomic_bool running = true; - // std::mutex render_mutex; }; diff --git a/src/mirage_core/CMakeLists.txt b/src/mirage_core/CMakeLists.txt index 5c5dcd3..9097115 100644 --- a/src/mirage_core/CMakeLists.txt +++ b/src/mirage_core/CMakeLists.txt @@ -5,21 +5,11 @@ project(mirage_core LANGUAGES C CXX) find_package(Freetype REQUIRED) find_package(Eigen3 REQUIRED) -option(MIRAGE_STB_IMAGE "Use stb_image" ON) - set(SRC_FILES) retrieve_files(${CMAKE_CURRENT_SOURCE_DIR} SRC_FILES) add_library(${PROJECT_NAME} STATIC ${SRC_FILES}) -target_link_libraries(${PROJECT_NAME} PUBLIC Freetype::Freetype Eigen3::Eigen mustache sokol) +target_link_libraries(${PROJECT_NAME} PUBLIC Freetype::Freetype Eigen3::Eigen) target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) add_os_definitions(${PROJECT_NAME}) target_compile_definitions(${PROJECT_NAME} PUBLIC -DNOMINMAX) - -if (MIRAGE_STB_IMAGE) - target_link_libraries(${PROJECT_NAME} PUBLIC mirage_stb_image) -endif () - -# 添加编译shader的自定义命令 -add_mirage_shader_directory(${CMAKE_CURRENT_SOURCE_DIR}/shaders) -add_shader_dependencies(${PROJECT_NAME}) diff --git a/src/mirage_core/mirage.cpp b/src/mirage_core/mirage.cpp deleted file mode 100644 index a6b46cb..0000000 --- a/src/mirage_core/mirage.cpp +++ /dev/null @@ -1,59 +0,0 @@ - -#include "mirage.h" - -#include -#include - -#include "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, - uint32_t line_nr, const char* filename_or_null, void* user_data) { - if (log_level == 0) // painc - std::cerr << "sg: " << message_or_null << std::endl; - else if (log_level == 1) // error - std::cerr << "sg: " << message_or_null << std::endl; - else if (log_level == 2) // warning - std::cout << "sg: " << message_or_null << std::endl; - else if (log_level == 3) // info - std::clog << "sg: " << message_or_null << std::endl; -} - -void mirage_app::init() { - duration_type duration; - { - mirage_scoped_duration_timer timer(duration); - - last_time = get_current_time(); - render_context = mirage_create_render_context(); - render_context->init(); - const sg_desc desc = { - .logger = { - .func = mirage_log, - .user_data = nullptr - }, - .environment = render_context->get_environment(), - }; - sg_setup(desc); - render_context->end_init(); - } - // 初始化用时 - std::cout << "mirage: " << "Initialization took " << std::chrono::duration_cast(duration).count() << "ms" << std::endl; -} - - -void mirage_app::run() { - while (!mwindow::get_windows().empty()) { - delta_time = get_current_time() - last_time; - mwindow::poll_events(); - - widget_manager::get().update(); - - last_time = get_current_time(); - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - // std::this_thread::yield(); - } - running = false; - render_context->cleanup(); - delete render_context; -} diff --git a/src/mirage_core/misc/hit_test_result.cpp b/src/mirage_core/misc/hit_test_result.cpp deleted file mode 100644 index a49822d..0000000 --- a/src/mirage_core/misc/hit_test_result.cpp +++ /dev/null @@ -1,6 +0,0 @@ -/** -* @file hit_test_result.cpp - * @brief 命中测试结果类的实现 - */ - -#include "hit_test_result.h" diff --git a/src/mirage_core/misc/mirage_type.h b/src/mirage_core/misc/mirage_type.h index eb03a75..36559ec 100644 --- a/src/mirage_core/misc/mirage_type.h +++ b/src/mirage_core/misc/mirage_type.h @@ -1,6 +1,6 @@ #pragma once /** - * @file render_elements.h + * @file mirage_type.h * @brief 定义渲染元素和UI相关的基础类型 * * 本文件定义了UI系统中用于渲染和布局的基础类型,包括: diff --git a/src/mirage_core/widget/mwidget.cpp b/src/mirage_core/widget/mwidget.cpp deleted file mode 100644 index 1e7321a..0000000 --- a/src/mirage_core/widget/mwidget.cpp +++ /dev/null @@ -1,85 +0,0 @@ -// -// Created by 46944 on 25-3-23. -// - -#include "mwidget.h" - -#include "window/mwindow.h" -#include "geometry/dpi_helper.h" -#include "widget_tree/widget_manager.h" - -mwidget::~mwidget() { - widget_manager::get().delete_widget(key_); -} - -void mwidget::init_component(mustache::EntityManager& in_entity_manager) { - in_entity_manager.assign(key_, widget_ptr{ shared_from_this() }); - in_entity_manager.assign(key_); - in_entity_manager.assign(key_); - in_entity_manager.assign(key_); - in_entity_manager.assign(key_); -} - -void mwidget::set_geometry(const geometry_t& in_geometry) { - get_component_ref().geometry = in_geometry; -} - -void mwidget::cache_desired_size(float in_layout_scale_multiplier, bool in_force) { - if (auto* layout = get_component()) { - if (!in_force && layout->desired_size.has_value()) - return; - layout->desired_size = compute_desired_size(in_layout_scale_multiplier); - } -} - -auto mwidget::get_dpi_scale() const -> float { - if (const auto window = get_window()) - return window->get_window_dpi_scale() * dpi_helper::get_global_scale(); - return 1.f; -} - -std::weak_ptr mwidget::get_parent_widget() const { - if (const auto parent = get_parent()) - return widget_manager::get().get_component(parent)->widget; - return {}; -} - -void mwidget::set_parent(const widget_key& in_parent) { - auto& manager = widget_manager::get(); - auto* parent_hierarchy = manager.get_component(in_parent); - auto* hierarchy = manager.get_component(key_); - - // 1. 如果当前有父节点,先从父节点中移除 - if (hierarchy->parent) { - auto* old_parent_hierarchy = manager.get_component(hierarchy->parent); - old_parent_hierarchy->remove_child(key_); - } - - // 2. 设置新的父节点 - hierarchy->parent = in_parent; - - // 3. 更新父子关系 - parent_hierarchy->add_child(key_); - - // 4. 通知父节点需要重新计算布局 - invalidate(invalidate_reason::layout); -} - -void mwidget::invalidate(invalidate_reason in_reason) { - // 设置失效标记 - if (auto* invalidate = get_component()) { - invalidate->set(in_reason); - } - - // 通知父组件 - if (const auto parent = get_parent_widget().lock()) { - parent->invalidate(in_reason); - } - - // 如果是布局失效, 重置期望大小 - if (has_any_flag(in_reason, invalidate_reason::layout)) { - if (auto* layout = get_component()) { - layout->desired_size.reset(); - } - } -} diff --git a/src/mirage_core/widget_tree/widget_component.cpp b/src/mirage_core/widget_tree/widget_component.cpp deleted file mode 100644 index 7e746ce..0000000 --- a/src/mirage_core/widget_tree/widget_component.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "widget_component.h" - -#include "widget_manager.h" - -void widget_hierarchy::add_child(const widget_key& in_child) { - children.push_back(in_child); -} - -void widget_hierarchy::remove_child(const widget_key& in_child) { - const auto find = std::ranges::find(children, in_child); - if (find != children.end()) { - children.erase(find); - } -} - -bool widget_visibility::can_hit_test() const { - // 不可见组件不参与命中测试 - if (has_any_flag(visibility, visibility_t::any_invisible)) { - return false; - } - - // 如果整个组件树都不可点击 - if (has_flag(visibility, visibility_t::hit_test_invisible)) { - return false; - } - - // 如果组件自身不可点击(但子组件可以) - if (has_flag(visibility, visibility_t::self_hit_test_invisible)) { - return false; - } - - return true; -} diff --git a/src/mirage_core/widget_tree/widget_component.h b/src/mirage_core/widget_tree/widget_component.h deleted file mode 100644 index c773226..0000000 --- a/src/mirage_core/widget_tree/widget_component.h +++ /dev/null @@ -1,93 +0,0 @@ -#pragma once -#include "geometry/geometry.h" -#include "misc/invalidate_reason.h" -#include "misc/key_type/key_type.h" -#include "mustache/ecs/entity.hpp" - -class mwindow; -class mwidget; - -struct widget_key { - widget_key() = default; - - widget_key(mwindow* in_window, mustache::Entity const& in_entity) : - window_(in_window), - entity_(in_entity) {} - - widget_key(const widget_key& in_other) : - window_(in_other.window_), - entity_(in_other.entity_) {} - auto& operator=(const widget_key& in_other) { - if (this != &in_other) { - window_ = in_other.window_; - entity_ = in_other.entity_; - } - return *this; - } - - operator bool() const { return window_ && !entity_.isNull(); } - operator const mustache::Entity&() const { return entity_; } - operator mwindow*() const { return window_; } - - bool operator==(const widget_key& in_other) const { return window_ == in_other.window_ && entity_ == in_other.entity_; } - bool operator!=(const widget_key& in_other) const { return !(*this == in_other); } - - static auto invalid() { return widget_key(nullptr, mustache::Entity()); } - - [[nodiscard]] const auto& get_window() const { return window_; } - [[nodiscard]] const auto& get_entity() const { return entity_; } -private: - mwindow* window_; - mustache::Entity entity_; -}; - -struct widget_ptr { - std::shared_ptr widget; - - operator std::shared_ptr() const { return widget; } - auto operator->() const { return widget.get(); } - operator bool() const { return widget != nullptr; } -}; - -struct widget_layout { - geometry_t geometry; // 几何信息 - std::optional desired_size; // 期望大小 - - auto is_under_location(const Eigen::Vector2f& in_window_location) const { - return geometry.is_under_location(in_window_location); - } - auto is_under_local_location(const Eigen::Vector2f& in_local_location) const { - return geometry.is_under_local_location(in_local_location); - } -}; - -struct widget_hierarchy { - widget_key parent; - std::vector children; - uint32_t depth; - - void add_child(const widget_key& in_child); - void remove_child(const widget_key& in_child); -}; - -struct widget_visibility { - bool enabled = true; - visibility_t visibility = visibility_t::self_hit_test_invisible; - - [[nodiscard]] auto is_enabled() const { return enabled; } - [[nodiscard]] auto get_visibility() const { return visibility; } - void set_enabled(const bool in_enabled) { enabled = in_enabled; } - void set_visibility(const visibility_t in_visibility) { visibility = in_visibility; } - - [[nodiscard]] bool can_hit_test() const; -}; - -struct widget_invalidate { - invalidate_reason invalidate = invalidate_reason::none; - - void set(const invalidate_reason in_reason) { set_flag(invalidate, in_reason); } - void unset(const invalidate_reason in_reason) { clear_flag(invalidate, in_reason); } - [[nodiscard]] auto has(const invalidate_reason in_reason) const { return has_any_flag(invalidate, in_reason); } - - void clear() { invalidate = invalidate_reason::none; } -}; diff --git a/src/mirage_core/widget_tree/widget_manager.cpp b/src/mirage_core/widget_tree/widget_manager.cpp deleted file mode 100644 index aaa1c9c..0000000 --- a/src/mirage_core/widget_tree/widget_manager.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "widget_manager.h" - -#include "widget_system.h" -#include "window/mwindow.h" - -void widget_manager::init_window(const std::shared_ptr& in_window) { - auto& registry = registries[in_window.get()]; - auto& system_manager = registry.systems(); - auto& entity_manager = registry.entities(); - - const auto layout_system = std::make_shared(); - const auto hit_test_system = std::make_shared(); - const auto render_system = std::make_shared(); - - layout_system->set_window(in_window); - hit_test_system->set_window(in_window); - render_system->set_window(in_window); - - system_manager.addSystem(layout_system); - system_manager.addSystem(hit_test_system); - system_manager.addSystem(render_system); - - registry.init(); - - in_window->on_close_delegate.add_raw(this, &widget_manager::on_window_close); - - const auto& entity = entity_manager.create<>(); - widget_key key { in_window.get(), entity }; - in_window->set_key(key); - in_window->init_component(entity_manager); -} - -void widget_manager::update() { - for (auto& pair : registries) { - auto& registry = pair.second; - registry.systems().update(); - } -} diff --git a/src/mirage_core/widget_tree/widget_manager.h b/src/mirage_core/widget_tree/widget_manager.h deleted file mode 100644 index 9e0cfe5..0000000 --- a/src/mirage_core/widget_tree/widget_manager.h +++ /dev/null @@ -1,85 +0,0 @@ -#pragma once -#include - -#include - -#include "widget_component.h" - -class mwidget; -class mwindow; - -class widget_manager { -public: - static widget_manager& get() { - static widget_manager instance; - return instance; - } - void init_window(const std::shared_ptr& in_window); - - auto& get_registry(mwindow* in_window) { - return registries[in_window]; - } - - template - auto new_widget(mwindow* in_window) { - auto& entity_manager = registries[in_window].entities(); - const auto entity = entity_manager.create<>(); - widget_key key{ in_window, entity }; - - auto widget = std::make_shared(); - widget->set_key(key); - widget->init_component(entity_manager); - return std::weak_ptr{ widget }; - } - - template - auto new_widget(mwindow* in_window, Args&&... args) { - auto& entity_manager = registries[in_window].entities(); - const auto entity = entity_manager.create<>(); - widget_key key{ in_window, entity }; - - auto widget = std::make_shared(std::forward(args)...); - widget->set_key(key); - widget->init_component(entity_manager); - return widget; - } - - auto delete_widget(const widget_key& in_key) { - return registries[in_key].entities().destroy(in_key); - } - - template - auto& add_component(const widget_key& in_key) { - return registries[in_key].entities().assign(in_key); - } - template - auto& add_component(const widget_key& in_key, Args&&... args) { - return registries[in_key].entities().assign(in_key, std::forward(args)...); - } - - template - auto* get_component(const widget_key& in_key) { - return registries[in_key].entities().getComponent(in_key); - } - template - auto& get_component_ref(const widget_key& in_key) { - return *registries[in_key].entities().getComponent(in_key); - } - - template - auto get_system(mwindow* in_window) { - auto shared = registries[in_window].systems().findSystem(); - return std::static_pointer_cast(shared); - } - - void update(); -private: - widget_manager() = default; - widget_manager(const widget_manager&) = delete; - - void on_window_close(mwindow* in_window) { - registries.erase(in_window); - } - - std::map registries; -}; diff --git a/src/mirage_core/widget_tree/widget_system.cpp b/src/mirage_core/widget_tree/widget_system.cpp deleted file mode 100644 index 2a1b7c0..0000000 --- a/src/mirage_core/widget_tree/widget_system.cpp +++ /dev/null @@ -1,385 +0,0 @@ -#include "widget_system.h" - -#include "mustache/ecs/world.hpp" -#include "widget/mwidget.h" -#include "window/mwindow.h" - -// ===== widget_layout_system 实现 ===== - -/** - * @brief 获取当前DPI缩放比例 - * @return 计算后的DPI缩放因子 - */ -auto widget_layout_system::get_dpi_scale() const -> float { - if (const auto window = window_.lock()) - return window->get_window_dpi_scale() * dpi_helper::get_global_scale(); - return 1.f; -} - -/** - * @brief 更新所有需要重新布局的控件 - * - * 查找所有标记为需要更新布局的控件,并重新计算其布局 - * - * @param in_world ECS世界引用 - */ -void widget_layout_system::onUpdate(mustache::World& in_world) { - bool is_layout_changed = false; - // 为所有需要更新布局的控件重新排列子控件 - in_world.entities().forEach([&](const widget_layout& in_layout, widget_invalidate& in_invalidate, const widget_ptr& in_ptr) { - // 如果布局有效则跳过 - if (!in_invalidate.has(invalidate_reason::layout)) - return; - - // 根据当前几何信息重新排列子控件 - in_ptr->arrange_children(in_layout.geometry); - - // 清除布局无效标记并发布布局变更事件 - in_invalidate.unset(invalidate_reason::layout); - is_layout_changed = true; - }); - if (is_layout_changed) - in_world.events().post(layout_changed{}); -} - -// ===== widget_hit_test_system 实现 ===== - -/** - * @brief 析构函数,移除所有事件监听 - */ -widget_hit_test_system::~widget_hit_test_system() { - if (const auto window = window_.lock()) { - window->on_mouse_move_delegate.remove_object(this); - window->on_mouse_button_up_delegate.remove_object(this); - window->on_mouse_button_down_delegate.remove_object(this); - window->on_mouse_button_dbl_delegate.remove_object(this); - window->on_mouse_wheel_delegate.remove_object(this); - window->on_mouse_leave_delegate.remove_object(this); - } -} - -/** - * @brief 设置窗口并注册所有鼠标事件处理函数 - * @param in_window 窗口共享指针 - */ -void widget_hit_test_system::set_window(const std::shared_ptr& in_window) { - window_ = in_window; - - // 注册所有鼠标事件处理函数 - in_window->on_mouse_move_delegate.add_raw(this, &widget_hit_test_system::process_mouse_move); - in_window->on_mouse_button_up_delegate.add_raw(this, &widget_hit_test_system::process_mouse_button_up); - in_window->on_mouse_button_down_delegate.add_raw(this, &widget_hit_test_system::process_mouse_button_down); - in_window->on_mouse_button_dbl_delegate.add_raw(this, &widget_hit_test_system::process_mouse_button_dbl); - in_window->on_mouse_wheel_delegate.add_raw(this, &widget_hit_test_system::process_mouse_wheel); - in_window->on_mouse_leave_delegate.add_raw(this, &widget_hit_test_system::process_mouse_leave); -} - -/** - * @brief 配置系统,订阅布局变化事件 - * @param in_world ECS世界引用 - * @param in_config 系统配置 - */ -void widget_hit_test_system::onConfigure(mustache::World& in_world, mustache::SystemConfig& in_config) { - in_world.events().subscribe_(this); -} - -/** - * @brief 系统更新函数,处理命中测试状态变化 - * @param in_world ECS世界引用 - */ -void widget_hit_test_system::onUpdate(mustache::World& in_world) { - // 如果命中测试状态已变脏,重新执行鼠标移动处理 - if (hit_test_dirty_) { - process_mouse_move(last_mouse_pos_); - hit_test_dirty_ = false; - } -} - -/** - * @brief 处理鼠标移动事件,更新悬停控件状态 - * @param in_window_pos 鼠标在窗口中的位置 - */ -void widget_hit_test_system::process_mouse_move(const Eigen::Vector2f& in_window_pos) { - // 执行命中测试,查找鼠标位置下的控件 - const auto& result = perform_hit_test(in_window_pos, [this](const std::shared_ptr& in_widget, const Eigen::Vector2f& in_local_pos) { - last_hover_local_pos_ = in_local_pos; - return in_widget->on_mouse_move(in_local_pos); - }); - - // 更新最后鼠标位置 - last_mouse_pos_ = in_window_pos; - - // 如果悬停控件没有变化,直接返回 - if (last_hover_widget_ == result.widget) - return; - - // 处理鼠标离开上一个悬停控件 - if (auto ptr = widget_manager::get().get_component(last_hover_widget_)) - ptr->widget->on_mouse_leave(); - - // 更新悬停控件并触发鼠标进入事件 - last_hover_widget_ = result; - if (auto ptr = widget_manager::get().get_component(last_hover_widget_)) - ptr->widget->on_mouse_enter(); -} - -/** - * @brief 处理鼠标按钮释放事件,处理点击逻辑 - * @param in_window_pos 鼠标在窗口中的位置 - * @param in_button 释放的鼠标按钮 - */ -void widget_hit_test_system::process_mouse_button_up(const Eigen::Vector2f& in_window_pos, mouse_button in_button) { - // 执行命中测试,找出鼠标位置下的控件 - const auto& hit_result = perform_hit_test(in_window_pos, [in_button](const std::shared_ptr& widget, const Eigen::Vector2f& local_pos) { - return widget->on_mouse_button_up(local_pos, in_button); - }); - - const auto& hit_widget = hit_result.widget; - const auto& widget_local_pos = hit_result.widget_space_pos; - - // 如果没有点击到任何控件,重置状态并返回 - if (!hit_widget) { - last_hit_widget_ = widget_key::invalid(); - return; - } - - const auto& ptr = widget_manager::get().get_component_ref(last_hit_widget_); - - // 定义点击判定阈值 - constexpr auto CLICK_TIME_THRESHOLD = std::chrono::milliseconds(200); // 单次点击最大持续时间 - 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(); - - // 判断是否是有效点击(时间短且位置接近) - - // 只处理有效点击 - if (press_duration >= CLICK_TIME_THRESHOLD || distance >= CLICK_DISTANCE_THRESHOLD) { - return; - } - - // 触发点击事件并更新最后点击的控件 - ptr->on_click(widget_local_pos, in_button); - last_hit_widget_ = hit_widget; -} - -/** - * @brief 处理鼠标按钮按下事件,记录按下状态 - * @param in_window_pos 鼠标在窗口中的位置 - * @param in_button 按下的鼠标按钮 - */ -void widget_hit_test_system::process_mouse_button_down(const Eigen::Vector2f& in_window_pos, mouse_button in_button) { - // 执行命中测试,找出鼠标位置下的控件 - const auto& result = perform_hit_test(in_window_pos, [in_button](const std::shared_ptr& in_widget, const Eigen::Vector2f& in_local_pos) { - return in_widget->on_mouse_button_down(in_local_pos, in_button); - }); - - // 记录点击状态信息 - last_hit_widget_ = result; - last_mouse_press_time_ = get_current_time(); - last_mouse_press_pos_ = in_window_pos; -} - -/** - * @brief 处理鼠标双击事件,调用控件的双击处理函数 - * @param in_window_pos 鼠标在窗口中的位置 - * @param in_button 双击的鼠标按钮 - */ -void widget_hit_test_system::process_mouse_button_dbl(const Eigen::Vector2f& in_window_pos, mouse_button in_button) const { - if (const auto last_hover_widget = widget_manager::get().get_component(last_hover_widget_)) { - last_hover_widget->widget->on_double_click(last_hover_local_pos_, in_button); - } -} - -/** - * @brief 处理鼠标滚轮事件,调用控件的滚轮处理函数 - * @param in_window_pos 鼠标在窗口中的位置 - * @param in_wheel_event 滚轮事件 - */ -void widget_hit_test_system::process_mouse_wheel(const Eigen::Vector2f& in_window_pos, wheel_event in_wheel_event) const { - if (const auto last_hover_widget = widget_manager::get().get_component(last_hover_widget_)) { - last_hover_widget->widget->on_mouse_wheel(last_hover_local_pos_, in_wheel_event); - } -} - -/** - * @brief 处理鼠标离开窗口事件,清除悬停状态 - */ -void widget_hit_test_system::process_mouse_leave() { - // 触发上一个悬停控件的鼠标离开事件 - if (auto last_hover_widget = widget_manager::get().get_component(last_hover_widget_)) { - last_hover_widget->widget->on_mouse_leave(); - } - // 重置悬停控件 - last_hover_widget_ = widget_key::invalid(); -} - -/** - * @brief 执行命中测试,查找鼠标位置下的控件 - * @param in_window_pos 鼠标在窗口中的位置 - * @param in_hit_func 命中测试回调函数 - * @return 命中测试结果 - */ -hit_test_result widget_hit_test_system::perform_hit_test( - const Eigen::Vector2f& in_window_pos, - const std::function&, const Eigen::Vector2f&)>& in_hit_func) const { - // 获取窗口对象 - auto window = window_.lock(); - if (!window) - return {}; - - // 获取窗口内容控件 - auto window_content = window->get_content(); - if (!window_content) - return {}; - - // 从内容控件开始执行命中测试 - return perform_hit_test(window_content->get_key(), in_window_pos, in_hit_func); -} - -/** - * @brief 对特定控件及其子控件执行命中测试 - * @param in_key 控件键值 - * @param in_window_pos 鼠标在窗口中的位置 - * @param in_hit_func 命中测试回调函数 - * @return 命中测试结果 - */ -hit_test_result widget_hit_test_system::perform_hit_test( - const widget_key& in_key, - const Eigen::Vector2f& in_window_pos, - const std::function&, const Eigen::Vector2f&)>& in_hit_func) { - // 获取控件布局 - const auto& layout = widget_manager::get().get_component_ref(in_key); - - // 检查鼠标位置是否在控件区域内 - auto widget_local_pos = layout.is_under_location(in_window_pos); - if (!widget_local_pos) - return {}; - - // 获取控件指针 - auto& widget = widget_manager::get().get_component_ref(in_key); - - // 如果控件可以执行命中测试,则调用回调函数 - if (widget->can_hit_test() && in_hit_func) { - const auto& widget_pos = widget_local_pos.value(); - const auto& handle = in_hit_func(widget, widget_pos); - if (handle.is_handled()) - return { in_key, widget_pos }; - } - - // 递归检查所有子控件 - const auto& children = widget_manager::get().get_component_ref(in_key).children; - for (const auto& child : children) { - if (const auto& result = perform_hit_test(child, in_window_pos, in_hit_func)) - return result; - } - - return {}; -} - -// ===== widget_render_system 实现 ===== - -/** - * @brief 析构函数,移除窗口大小变化监听 - */ -widget_render_system::~widget_render_system() { - if (const auto window = window_.lock()) { - window->on_resize_delegate.remove_object(this); - } -} - -/** - * @brief 设置窗口并初始化渲染上下文 - * @param in_window 窗口共享指针 - */ -void widget_render_system::set_window(const std::shared_ptr& in_window) { - window_ = in_window; - // 初始化渲染上下文 - context.init(in_window->get_window_frame_size()); - // 注册窗口大小变化事件 - in_window->on_resize_delegate.add_raw(this, &widget_render_system::on_resize); -} - -/** - * @brief 窗口大小改变回调,更新投影矩阵 - * @param in_size 新的窗口尺寸 - */ -void widget_render_system::on_resize(const Eigen::Vector2i& in_size) { - context.update_projection_matrix(in_size); -} - -/** - * @brief 系统更新函数,执行渲染流程 - * @param in_world ECS世界引用 - */ -void widget_render_system::onUpdate(mustache::World& in_world) { - // 获取窗口对象 - const auto window = window_.lock(); - if (!window) - return; - - // 如果窗口不可见或不需要重绘,则跳过 - if (!window->is_visible()) - return; - if (!window->has_invalidate_reason(invalidate_reason::paint)) - return; - - // 获取窗口状态 - auto& window_state = window->get_state(); - - // 设置渲染通道属性 - sg_pass pass{}; - pass.action.colors[0].load_action = SG_LOADACTION_CLEAR; - pass.action.colors[0].store_action = SG_STOREACTION_STORE; - pass.action.colors[0].clear_value = { 0.f, 0.f, 0.f, 1.0f }; // 黑色背景 - - pass.action.depth.load_action = SG_LOADACTION_CLEAR; - pass.action.depth.store_action = SG_STOREACTION_DONTCARE; - pass.action.depth.clear_value = 1.0f; - pass.swapchain = window_state.swapchain; - - // 开始渲染流程 - sg_begin_pass(pass); - sg_apply_viewport(0, 0, window_state.swapchain.width, window_state.swapchain.height, true); - - // 执行控件绘制 - context.begin_frame(window.get()); - paint(window->get_key()); - context.end_frame(); - - // 结束渲染并提交 - sg_end_pass(); - sg_commit(); - - // 显示渲染结果并清除重绘标记 - window_state.present(); - window->clear_invalidate_reason(invalidate_reason::paint); -} - -/** - * @brief 绘制指定控件及其子控件 - * @param in_key 控件键值 - */ -void widget_render_system::paint(const widget_key& in_key) { - // 获取控件指针 - const auto& ptr = widget_manager::get().get_component_ref(in_key); - if (!ptr) - return; - - // 获取控件几何信息 - const auto& geo = ptr->get_geometry(); - - // 设置渲染上下文并绘制当前控件 - context.set_geometry(geo); - ptr->on_paint(context); - - // 递归绘制所有子控件 - const auto& children = widget_manager::get().get_component_ref(in_key).children; - for (const auto& child : children) { - paint(child); - } -} diff --git a/src/mirage_core/widget_tree/widget_system.h b/src/mirage_core/widget_tree/widget_system.h deleted file mode 100644 index be6257d..0000000 --- a/src/mirage_core/widget_tree/widget_system.h +++ /dev/null @@ -1,204 +0,0 @@ -#pragma once -#include "widget_component.h" -#include "render/mirage_paint_context.h" -#include "mustache/ecs/event_manager.hpp" -#include "mustache/ecs/system.hpp" -#include "misc/hit_test_result.h" - -/** - * @struct layout_changed - * @brief 布局变更事件结构体,用于通知系统布局已经发生变化 - */ -struct layout_changed {}; - -/** - * @class widget_layout_system - * @brief 控件布局系统,负责计算和应用控件的布局信息 - * - * 该系统处理所有控件的布局计算、排列和大小调整 - */ -class widget_layout_system : public mustache::System { -public: - /** - * @brief 设置窗口引用 - * @param in_window 窗口共享指针 - */ - void set_window(std::shared_ptr in_window) { window_ = in_window; } - - /** - * @brief 获取当前DPI缩放比例 - * @return 当前DPI缩放因子 - */ - auto get_dpi_scale() const -> float; - -protected: - /** - * @brief 系统更新函数,处理所有需要更新布局的控件 - * @param in_world ECS世界引用 - */ - virtual void onUpdate(mustache::World& in_world) override; - -private: - std::weak_ptr window_{}; ///< 持有窗口的弱引用 -}; - -/** - * @class widget_hit_test_system - * @brief 控件命中测试系统,负责处理输入事件并将其分发给相应的控件 - * - * 该系统管理鼠标事件、点击检测和悬停状态,监听布局变化以更新命中测试结果 - */ -class widget_hit_test_system : public mustache::System, - public mustache::Receiver { -public: - virtual ~widget_hit_test_system() override; - - /** - * @brief 设置窗口并注册事件处理函数 - * @param in_window 窗口共享指针 - */ - void set_window(const std::shared_ptr& in_window); - -protected: - // ===== 系统回调函数 ===== - - /** - * @brief 系统配置回调,订阅布局变化事件 - * @param in_world ECS世界引用 - * @param in_config 系统配置 - */ - void onConfigure(mustache::World& in_world, mustache::SystemConfig& in_config) override; - - /** - * @brief 系统更新回调,处理命中测试状态 - * @param in_world ECS世界引用 - */ - void onUpdate(mustache::World& in_world) override; - - /** - * @brief 布局变化事件处理函数 - * @param collision 布局变化事件 - */ - void onEvent(const layout_changed& collision) override { hit_test_dirty_ = true; } - - // ===== 鼠标事件处理函数 ===== - - /** - * @brief 处理鼠标移动事件 - * @param in_window_pos 鼠标在窗口中的位置 - */ - void process_mouse_move(const Eigen::Vector2f& in_window_pos); - - /** - * @brief 处理鼠标按钮释放事件 - * @param in_window_pos 鼠标在窗口中的位置 - * @param in_button 释放的鼠标按钮 - */ - void process_mouse_button_up(const Eigen::Vector2f& in_window_pos, mouse_button in_button); - - /** - * @brief 处理鼠标按钮按下事件 - * @param in_window_pos 鼠标在窗口中的位置 - * @param in_button 按下的鼠标按钮 - */ - void process_mouse_button_down(const Eigen::Vector2f& in_window_pos, mouse_button in_button); - - /** - * @brief 处理鼠标双击事件 - * @param in_window_pos 鼠标在窗口中的位置 - * @param in_button 双击的鼠标按钮 - */ - void process_mouse_button_dbl(const Eigen::Vector2f& in_window_pos, mouse_button in_button) const; - - /** - * @brief 处理鼠标滚轮事件 - * @param in_window_pos 鼠标在窗口中的位置 - * @param in_wheel_event 滚轮事件 - */ - void process_mouse_wheel(const Eigen::Vector2f& in_window_pos, wheel_event in_wheel_event) const; - - /** - * @brief 处理鼠标离开窗口事件 - */ - void process_mouse_leave(); - - // ===== 命中测试函数 ===== - - /** - * @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) const; - - /** - * @brief 对特定控件及其子控件执行命中测试 - * @param in_key 控件键值 - * @param in_window_pos 鼠标在窗口中的位置 - * @param in_hit_func 命中测试回调函数 - * @return 命中测试结果 - */ - static hit_test_result perform_hit_test( - const widget_key& in_key, - const Eigen::Vector2f& in_window_pos, - const std::function&, const Eigen::Vector2f&)>& in_hit_func); - -private: - // 窗口引用 - std::weak_ptr window_{}; ///< 持有窗口的弱引用 - - // 状态追踪 - Eigen::Vector2f last_mouse_pos_ = Eigen::Vector2f::Zero(); ///< 上次鼠标位置 - Eigen::Vector2f last_hover_local_pos_ = Eigen::Vector2f::Zero(); ///< 上次悬停控件的本地位置 - widget_key last_hit_widget_{}; ///< 上次点击的控件 - widget_key last_hover_widget_{}; ///< 当前悬停的控件 - bool hit_test_dirty_{}; ///< 命中测试是否需要刷新 - - // 点击追踪相关 - Eigen::Vector2f last_mouse_press_pos_ = Eigen::Vector2f::Zero(); ///< 上次鼠标按下的位置 - time_type last_click_time_{}; ///< 上次点击完成的时间 - time_type last_mouse_press_time_{}; ///< 上次鼠标按下的时间 -}; - -/** - * @class widget_render_system - * @brief 控件渲染系统,负责绘制所有可见控件 - * - * 该系统管理渲染上下文并处理控件的绘制操作 - */ -class widget_render_system : public mustache::System { -public: - virtual ~widget_render_system() override; - - /** - * @brief 设置窗口并初始化渲染上下文 - * @param in_window 窗口共享指针 - */ - void set_window(const std::shared_ptr& in_window); - -protected: - /** - * @brief 系统更新回调,执行渲染流程 - * @param in_world ECS世界引用 - */ - virtual void onUpdate(mustache::World& in_world) override; - - /** - * @brief 绘制指定控件及其子控件 - * @param in_key 控件键值 - */ - void paint(const widget_key& in_key); - - /** - * @brief 窗口大小改变回调,更新投影矩阵 - * @param in_size 新的窗口尺寸 - */ - void on_resize(const Eigen::Vector2i& in_size); - -private: - mirage_paint_context context; ///< 渲染上下文 - std::weak_ptr window_{}; ///< 持有窗口的弱引用 -}; diff --git a/src/mirage_core/window/android/android_render_window.cpp b/src/mirage_core/window/android/android_render_window.cpp deleted file mode 100644 index c1d53e9..0000000 --- a/src/mirage_core/window/android/android_render_window.cpp +++ /dev/null @@ -1,4 +0,0 @@ -// -// Created by Administrator on 25-3-1. -// -#include "core/window/render_window.h" diff --git a/src/mirage_core/window/ios/ios_render_window.cpp b/src/mirage_core/window/ios/ios_render_window.cpp deleted file mode 100644 index 4938f41..0000000 --- a/src/mirage_core/window/ios/ios_render_window.cpp +++ /dev/null @@ -1,8 +0,0 @@ -// -// Created by Administrator on 25-3-1. -// -#include "core/window/render_window.h" - -void* mirage_window::get_window_handle() const { - return glfwGetIOSurface(window); -} diff --git a/src/mirage_core/window/linux/linux_render_window.cpp b/src/mirage_core/window/linux/linux_render_window.cpp deleted file mode 100644 index bfd253e..0000000 --- a/src/mirage_core/window/linux/linux_render_window.cpp +++ /dev/null @@ -1,8 +0,0 @@ -// -// Created by Administrator on 25-3-1. -// -#include "core/window/render_window.h" - -void* mirage_window::get_window_handle() const { - return glfwGetX11Window(window); -} diff --git a/src/mirage_core/window/mac/mac_render_window.mm b/src/mirage_core/window/mac/mac_render_window.mm deleted file mode 100644 index 04ce732..0000000 --- a/src/mirage_core/window/mac/mac_render_window.mm +++ /dev/null @@ -1,8 +0,0 @@ -// -// Created by Administrator on 25-3-1. -// -#include "window/render_window.h" - -void* mirage_window::get_window_handle() const { - return glfwGetCocoaWindow(window); -} diff --git a/src/mirage_core/window/mwindow.cpp b/src/mirage_core/window/mwindow.cpp deleted file mode 100644 index b3d7b63..0000000 --- a/src/mirage_core/window/mwindow.cpp +++ /dev/null @@ -1,97 +0,0 @@ -#include "mwindow.h" - -#include "widget_tree/widget_system.h" - -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) { - if (content_widget_) { - auto child_geo = in_allotted_geometry.make_child({0, 0}, get_window_frame_size().cast()); - content_widget_->set_geometry(child_geo); - } -} - -void mwindow::set_content(const std::shared_ptr& in_widget) { - content_widget_ = in_widget; - in_widget->set_parent(get_key()); - invalidate(invalidate_reason::all); -} - -void mwindow::on_resize(int width, int height) { - const Eigen::Vector2i size(width, height); - state_->swapchain.width = width; - state_->swapchain.height = height; - - on_resize_delegate.broadcast(size); - - transform2d identity; - geometry_t new_geometry(size.cast(), identity, identity); - if (content_widget_) { - content_widget_->set_geometry(new_geometry); - } - invalidate_all(); -} - -void mwindow::rebuild_swapchain() { - state_->rebuild_swapchain(get_window_frame_size()); -} - -void mwindow::on_move(int x, int y) { -} - -void mwindow::on_paint(mirage_paint_context& in_context) { - if (content_widget_) - content_widget_->on_paint(in_context); -} - -void invalidate_recursive(const std::shared_ptr& in_widget, invalidate_reason in_reason) { - in_widget->invalidate(in_reason); - for (const auto& child : in_widget->get_children()) { - const auto& ptr = widget_manager::get().get_component_ref(child); - invalidate_recursive(ptr, in_reason); - } - - // 如果是布局失效, 重置期望大小 - if (has_any_flag(in_reason, invalidate_reason::layout)) { - if (auto* layout = in_widget->get_component()) { - layout->desired_size.reset(); - } - } -} - -void mwindow::invalidate_all() { - // 设置失效标记 - invalidate_recursive(shared_from_this(), invalidate_reason::all); -} - -void mwindow::handle_mouse_move(const Eigen::Vector2f& in_window_pos) { - on_mouse_move_delegate.broadcast(in_window_pos); -} - -void mwindow::handle_mouse_button_down(const Eigen::Vector2f& in_window_pos, mouse_button in_button) { - on_mouse_button_down_delegate.broadcast(in_window_pos, in_button); -} - -void mwindow::handle_mouse_button_up(const Eigen::Vector2f& in_window_pos, mouse_button in_button) { - on_mouse_button_up_delegate.broadcast(in_window_pos, in_button); -} - -void mwindow::handle_mouse_dbl_click(const Eigen::Vector2f& pos, mouse_button button) { - on_mouse_button_dbl_delegate.broadcast(pos, button); -} - -void mwindow::handle_mouse_wheel(const Eigen::Vector2f& pos, wheel_event delta) { - on_mouse_wheel_delegate.broadcast(pos, delta); -} - -void mwindow::handle_mouse_leave() { - on_mouse_leave_delegate.broadcast(); -} diff --git a/src/mirage_core/window/mwindow.h b/src/mirage_core/window/mwindow.h deleted file mode 100644 index 009bf34..0000000 --- a/src/mirage_core/window/mwindow.h +++ /dev/null @@ -1,431 +0,0 @@ -#pragma once -/** - * @file mwindow.h - * @brief 定义UI系统的窗口类 - * - * 本文件定义了mirage_window类,作为UI系统的窗口容器,管理窗口的创建、 - * 操作、渲染和内容布局。窗口本身也是一个UI组件,可以包含其他组件。 - */ - -#include "render/windows/windows_render_context.h" -#include - -#include "geometry/dpi_helper.h" -#include "geometry/geometry.h" -#include "geometry/layout_transform.h" -#include "misc/delegates.h" -#include "misc/key_type/key_type.h" -#include "widget/mwidget.h" - -/** - * @struct mirage_window_state - * @brief 存储窗口渲染状态的结构 - * - * 维护与特定窗口关联的渲染资源和状态,包括GPU缓冲区、交换链和渲染设置。 - * 这是一个虚拟基类,不同平台可以提供特定实现。 - */ -struct mirage_window_state { - /** - * @brief 虚析构函数 - * - * 确保派生类可以正确清理资源。 - */ - virtual ~mirage_window_state() { - clear(); - } - - /** - * @brief 清理资源 - */ - void clear() { on_clear(); } - - /** 顶点缓冲区 */ - sg_buffer buffer{}; - - /** 窗口交换链 */ - sg_swapchain swapchain{}; - - /** 渲染绑定 */ - sg_bindings bindings{}; - - /** 渲染管线 */ - sg_pipeline pipeline{}; - - /** 垂直同步标志 */ - bool vsync = true; - - /** - * @brief 清理资源 - * - * 虚方法,由派生类实现以释放平台特定资源。 - */ - virtual void on_clear() {} - - /** - * @brief 呈现渲染内容 - * - * 虚方法,由派生类实现以将渲染内容呈现到窗口。 - */ - virtual void present() {} - - /** - * @brief 重建交换链 - * @param size 新的窗口大小 - * - * 当窗口大小改变时重建交换链。虚方法,由派生类实现。 - */ - virtual void rebuild_swapchain(const Eigen::Vector2i& size) {} -}; - -/** - * @class mwindow - * @brief UI系统的窗口类 - * - * 表示一个可以包含UI组件的窗口。提供窗口创建、操作、样式设置和内容布局等功能。 - * 作为一个组件容器,窗口本身也是一个UI组件。 - */ -class mwindow : public mwidget { -public: - virtual ~mwindow() override { close(); } - //-------------- 窗口创建和基本操作 -------------- - - /** - * @brief 创建窗口 - * @param width 窗口宽度 - * @param height 窗口高度 - * @param title 窗口标题 - * @return 是否成功创建窗口 - */ - bool create_window(int width, int height, const wchar_t* title); - - /** - * @brief 显示窗口 - */ - void show(); - - /** - * @brief 隐藏窗口 - */ - void hide(); - - /** - * @brief 关闭窗口 - */ - void close(); - - /** - * @brief 最大化窗口 - */ - void maximize(); - - /** - * @brief 最小化窗口 - */ - void minimize(); - - /** - * @brief 是否可见 - */ - bool is_visible() const; - - //-------------- 窗口位置和大小 -------------- - - /** - * @brief 调整窗口大小 - * @param width 新宽度 - * @param height 新高度 - */ - void resize(int width, int height); - - /** - * @brief 调整窗口大小 - * @param size 新大小向量 - */ - void resize(const Eigen::Vector2i& size) { resize(size.x(), size.y()); } - - /** - * @brief 移动窗口 - * @param x 新的X坐标 - * @param y 新的Y坐标 - */ - void move(int x, int y); - - /** - * @brief 移动窗口 - * @param pos 新位置向量 - */ - void move(const Eigen::Vector2i& pos) { move(pos.x(), pos.y()); } - - //-------------- 窗口属性获取 -------------- - - /** - * @brief 获取窗口大小 - * @return 窗口大小向量 - */ - [[nodiscard]] Eigen::Vector2i get_window_size() const; - - /** - * @brief 获取窗口位置 - * @return 窗口位置向量 - */ - [[nodiscard]] Eigen::Vector2i get_window_position() const; - - /** - * @brief 获取窗口框架大小 - * @return 窗口框架大小向量 - */ - [[nodiscard]] Eigen::Vector2i get_window_frame_size() const; - - /** - * @brief 获取DPI缩放系数 - * @return DPI缩放系数 - */ - [[nodiscard]] float get_window_dpi_scale() const; - - /** - * @brief 获取窗口句柄 - * @return 原生窗口句柄 - */ - [[nodiscard]] void* get_window_handle() const; - - //-------------- 窗口样式设置 -------------- - - /** - * @brief 设置窗口标题 - * @param title 新标题 - * @return 窗口对象引用,用于链式调用 - */ - mwindow& set_title(const wchar_t* title); - - /** - * @brief 设置是否有最小化按钮 - * @param has_minimize_button 是否有最小化按钮 - * @return 窗口对象引用,用于链式调用 - */ - mwindow& set_has_minimize_button(bool has_minimize_button); - - /** - * @brief 设置是否有最大化按钮 - * @param has_maximize_button 是否有最大化按钮 - * @return 窗口对象引用,用于链式调用 - */ - mwindow& set_has_maximize_button(bool has_maximize_button); - - /** - * @brief 设置是否有关闭按钮 - * @param has_close_button 是否有关闭按钮 - * @return 窗口对象引用,用于链式调用 - */ - mwindow& set_has_close_button(bool has_close_button); - - /** - * @brief 设置是否有边框 - * @param has_border 是否有边框 - * @return 窗口对象引用,用于链式调用 - */ - mwindow& set_has_border(bool has_border); - - /** - * @brief 设置是否有标题栏 - * @param has_caption 是否有标题栏 - * @return 窗口对象引用,用于链式调用 - */ - mwindow& set_has_caption(bool has_caption); - - /** - * @brief 设置是否有可调整大小的边框 - * @param has_resizable_border 是否有可调整大小的边框 - * @return 窗口对象引用,用于链式调用 - */ - mwindow& set_has_resizable_border(bool has_resizable_border); - - /** - * @brief 设置窗口是否总在最前 - * @param is_topmost 是否总在最前 - * @return 窗口对象引用,用于链式调用 - */ - mwindow& set_topmost(bool is_topmost); - - //-------------- 事件处理 -------------- - - /** - * @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_button_down(const Eigen::Vector2f& in_window_pos, mouse_button in_button); - - /** - * @brief 处理鼠标释放事件 - * @param in_window_pos 窗口位置 - * @param in_button 释放的按钮 - */ - void handle_mouse_button_up(const Eigen::Vector2f& in_window_pos, mouse_button in_button); - - /** - * @brief 处理鼠标双击事件 - * @param pos 窗口位置 - * @param button 按下的按钮 - */ - void handle_mouse_dbl_click(const Eigen::Vector2f& pos, mouse_button button); - - /** - * @brief 处理鼠标滚轮事件 - * @param pos 窗口位置 - * @param delta 滚轮事件 - */ - void handle_mouse_wheel(const Eigen::Vector2f& pos, wheel_event delta); - - /** - * @brief 处理光标移出窗口事件 - */ - void handle_mouse_leave(); - - /** - * @brief 轮询所有窗口的事件 - * @return 如果有活动窗口则返回true - * - * 静态方法,处理所有窗口的事件队列。 - */ - static bool poll_events(); - - /** - * @brief 获取所有窗口的列表 - * @return 窗口指针列表 - * - * 静态方法,返回所有已创建窗口的列表。 - */ - static const std::vector& get_windows(); - - /** - * @brief 处理窗口大小改变事件 - * @param width 新宽度 - * @param height 新高度 - */ - void on_resize(int width, int height); - - /** - * @brief 重建交换链 - * - * 当窗口大小改变时重建渲染交换链。 - */ - void rebuild_swapchain(); - - /** - * @brief 处理窗口移动事件 - * @param x 新的X坐标 - * @param y 新的Y坐标 - */ - void on_move(int x, int y); - - //-------------- 渲染和状态 -------------- - - /** - * @brief 设置窗口渲染状态 - * @param in_state 新的渲染状态 - */ - void set_state(std::unique_ptr&& in_state) { state_ = std::move(in_state); } - - /** - * @brief 获取窗口渲染状态 - * @return 渲染状态常量引用 - */ - [[nodiscard]] auto& get_state() const { return *state_; } - - //-------------- 坐标变换 -------------- - - /** - * @brief 获取从本地坐标到屏幕坐标的变换 - * @return 变换矩阵 - */ - [[nodiscard]] auto get_local_to_screen_transform() const { - return transform2d(get_window_position().cast(), 0, { dpi_helper::get_global_scale() * get_window_dpi_scale(), dpi_helper::get_global_scale() * get_window_dpi_scale() }); - } - - /** - * @brief 获取从本地坐标到窗口坐标的变换 - * @return 变换矩阵 - */ - [[nodiscard]] auto get_local_to_window_transform() const { - return transform2d({0, 0}, 0, { dpi_helper::get_global_scale() * get_window_dpi_scale(), dpi_helper::get_global_scale() * get_window_dpi_scale() }); - } - - /** - * @brief 获取窗口在屏幕中的几何信息 - * @return 窗口几何信息 - */ - [[nodiscard]] geometry_t get_window_geometry_in_screen() const; - - /** - * @brief 获取窗口在窗口中的几何信息 - * @return 窗口几何信息 - */ - [[nodiscard]] geometry_t get_window_geometry_in_window() const; - //-------------- UI组件覆盖方法 -------------- - - /** - * @brief 计算窗口的期望大小 - * @param in_layout_scale_multiplier 布局缩放系数 - * @return 窗口的期望大小 - * - * 覆盖基类方法,窗口的期望大小就是其框架大小。 - */ - virtual Eigen::Vector2f compute_desired_size(float in_layout_scale_multiplier) const override { return get_window_frame_size().cast(); } - - /** - * @brief 排列子组件 - * @param in_allotted_geometry 分配的几何区域 - * - * 覆盖基类方法,安排窗口内容的布局。 - */ - virtual void arrange_children(const geometry_t& in_allotted_geometry) override; - - /** - * @brief 设置窗口内容 - * @param in_widget 要设置为内容的组件 - * - * 设置窗口的主要内容组件。 - */ - void set_content(const std::shared_ptr& in_widget); - - /** - * @brief 获取窗口内容 - * @return 窗口内容组件 - */ - [[nodiscard]] auto get_content() const { return content_widget_; } - - /** - * @brief 绘制窗口及其内容 - * @param in_context 绘制上下文 - * - * 覆盖基类方法,处理窗口及其内容的绘制。 - */ - virtual void on_paint(mirage_paint_context& in_context) override; - - void invalidate_all(); - - multicast_delegate on_resize_delegate; - multicast_delegate on_close_delegate; - - multicast_delegate on_mouse_move_delegate; - multicast_delegate on_mouse_button_down_delegate; - multicast_delegate on_mouse_button_up_delegate; - multicast_delegate on_mouse_button_dbl_delegate; - multicast_delegate on_mouse_wheel_delegate; - multicast_delegate<> on_mouse_leave_delegate; -private: - /** 原生窗口句柄 */ - void* window_handle_{}; - - /** 窗口渲染状态 */ - std::unique_ptr state_; - - /** 窗口内容组件 */ - std::shared_ptr content_widget_; -}; diff --git a/src/mirage_render/CMakeLists.txt b/src/mirage_render/CMakeLists.txt new file mode 100644 index 0000000..ae00892 --- /dev/null +++ b/src/mirage_render/CMakeLists.txt @@ -0,0 +1,18 @@ +project(mirage_render) + +set(SOURCE_FILES) +retrieve_files(${CMAKE_CURRENT_SOURCE_DIR} SOURCE_FILES) + +add_library(${PROJECT_NAME} STATIC ${SOURCE_FILES}) +target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(${PROJECT_NAME} PUBLIC mirage_core sokol) + +option(MIRAGE_STB_IMAGE "Use stb_image" ON) + +if (MIRAGE_STB_IMAGE) + target_link_libraries(${PROJECT_NAME} PUBLIC mirage_stb_image) +endif () + +# 添加编译shader的自定义命令 +add_mirage_shader_directory(${CMAKE_CURRENT_SOURCE_DIR}/shaders) +add_shader_dependencies(${PROJECT_NAME}) diff --git a/src/mirage_render/platform_window/platform_window.cpp b/src/mirage_render/platform_window/platform_window.cpp new file mode 100644 index 0000000..a4b673f --- /dev/null +++ b/src/mirage_render/platform_window/platform_window.cpp @@ -0,0 +1,36 @@ +#include "platform_window.h" + +#include "geometry/geometry.h" + +void platform_window::on_resize(int width, int height) { + const Eigen::Vector2i size(width, height); + on_resize_delegate.broadcast(size); +} + +void platform_window::on_move(const int x, const int y) { + on_move_delegate.broadcast(Eigen::Vector2i(x, y)); +} + +void platform_window::handle_mouse_move(const Eigen::Vector2f& in_window_pos) { + on_mouse_move_delegate.broadcast(in_window_pos); +} + +void platform_window::handle_mouse_button_down(const Eigen::Vector2f& in_window_pos, mouse_button in_button) { + on_mouse_button_down_delegate.broadcast(in_window_pos, in_button); +} + +void platform_window::handle_mouse_button_up(const Eigen::Vector2f& in_window_pos, mouse_button in_button) { + on_mouse_button_up_delegate.broadcast(in_window_pos, in_button); +} + +void platform_window::handle_mouse_dbl_click(const Eigen::Vector2f& pos, mouse_button button) { + on_mouse_button_dbl_delegate.broadcast(pos, button); +} + +void platform_window::handle_mouse_wheel(const Eigen::Vector2f& pos, wheel_event delta) { + on_mouse_wheel_delegate.broadcast(pos, delta); +} + +void platform_window::handle_mouse_leave() { + on_mouse_leave_delegate.broadcast(); +} diff --git a/src/mirage_render/platform_window/platform_window.h b/src/mirage_render/platform_window/platform_window.h new file mode 100644 index 0000000..f8d2093 --- /dev/null +++ b/src/mirage_render/platform_window/platform_window.h @@ -0,0 +1,242 @@ +#pragma once +#include "misc/delegates.h" +#include + +#include "misc/key_type/key_type.h" + +class platform_window { +public: + platform_window(int32_t in_width, int32_t in_height, const wchar_t* in_title); + /** + * @brief 显示窗口 + */ + void show(); + + /** + * @brief 隐藏窗口 + */ + void hide(); + + /** + * @brief 关闭窗口 + */ + void close(); + + /** + * @brief 最大化窗口 + */ + void maximize(); + + /** + * @brief 最小化窗口 + */ + void minimize(); + + /** + * @brief 是否可见 + */ + bool is_visible() const; + + //-------------- 窗口位置和大小 -------------- + + /** + * @brief 调整窗口大小 + * @param width 新宽度 + * @param height 新高度 + */ + void resize(int width, int height); + + /** + * @brief 调整窗口大小 + * @param size 新大小向量 + */ + void resize(const Eigen::Vector2i& size) { resize(size.x(), size.y()); } + + /** + * @brief 移动窗口 + * @param x 新的X坐标 + * @param y 新的Y坐标 + */ + void move(int x, int y); + + /** + * @brief 移动窗口 + * @param pos 新位置向量 + */ + void move(const Eigen::Vector2i& pos) { move(pos.x(), pos.y()); } + + //-------------- 窗口属性获取 -------------- + + /** + * @brief 获取窗口大小 + * @return 窗口大小向量 + */ + [[nodiscard]] Eigen::Vector2i get_window_size() const; + + /** + * @brief 获取窗口位置 + * @return 窗口位置向量 + */ + [[nodiscard]] Eigen::Vector2i get_window_position() const; + + /** + * @brief 获取窗口框架大小 + * @return 窗口框架大小向量 + */ + [[nodiscard]] Eigen::Vector2i get_window_frame_size() const; + + /** + * @brief 获取DPI缩放系数 + * @return DPI缩放系数 + */ + [[nodiscard]] float get_window_dpi_scale() const; + + /** + * @brief 获取窗口句柄 + * @return 原生窗口句柄 + */ + [[nodiscard]] void* get_window_handle() const; + + //-------------- 窗口样式设置 -------------- + + /** + * @brief 设置窗口标题 + * @param title 新标题 + * @return 窗口对象引用,用于链式调用 + */ + platform_window& set_title(const wchar_t* title); + + /** + * @brief 设置是否有最小化按钮 + * @param has_minimize_button 是否有最小化按钮 + * @return 窗口对象引用,用于链式调用 + */ + platform_window& set_has_minimize_button(bool has_minimize_button); + + /** + * @brief 设置是否有最大化按钮 + * @param has_maximize_button 是否有最大化按钮 + * @return 窗口对象引用,用于链式调用 + */ + platform_window& set_has_maximize_button(bool has_maximize_button); + + /** + * @brief 设置是否有关闭按钮 + * @param has_close_button 是否有关闭按钮 + * @return 窗口对象引用,用于链式调用 + */ + platform_window& set_has_close_button(bool has_close_button); + + /** + * @brief 设置是否有边框 + * @param has_border 是否有边框 + * @return 窗口对象引用,用于链式调用 + */ + platform_window& set_has_border(bool has_border); + + /** + * @brief 设置是否有标题栏 + * @param has_caption 是否有标题栏 + * @return 窗口对象引用,用于链式调用 + */ + platform_window& set_has_caption(bool has_caption); + + /** + * @brief 设置是否有可调整大小的边框 + * @param has_resizable_border 是否有可调整大小的边框 + * @return 窗口对象引用,用于链式调用 + */ + platform_window& set_has_resizable_border(bool has_resizable_border); + + /** + * @brief 设置窗口是否总在最前 + * @param is_topmost 是否总在最前 + * @return 窗口对象引用,用于链式调用 + */ + platform_window& set_topmost(bool is_topmost); + + //-------------- 事件处理 -------------- + + /** + * @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_button_down(const Eigen::Vector2f& in_window_pos, mouse_button in_button); + + /** + * @brief 处理鼠标释放事件 + * @param in_window_pos 窗口位置 + * @param in_button 释放的按钮 + */ + void handle_mouse_button_up(const Eigen::Vector2f& in_window_pos, mouse_button in_button); + + /** + * @brief 处理鼠标双击事件 + * @param pos 窗口位置 + * @param button 按下的按钮 + */ + void handle_mouse_dbl_click(const Eigen::Vector2f& pos, mouse_button button); + + /** + * @brief 处理鼠标滚轮事件 + * @param pos 窗口位置 + * @param delta 滚轮事件 + */ + void handle_mouse_wheel(const Eigen::Vector2f& pos, wheel_event delta); + + /** + * @brief 处理光标移出窗口事件 + */ + void handle_mouse_leave(); + + /** + * @brief 轮询所有窗口的事件 + * @return 如果有活动窗口则返回true + * + * 静态方法,处理所有窗口的事件队列。 + */ + static bool poll_events(); + + /** + * @brief 获取所有窗口的列表 + * @return 窗口指针列表 + * + * 静态方法,返回所有已创建窗口的列表。 + */ + static const std::vector& get_windows(); + + /** + * @brief 处理窗口大小改变事件 + * @param width 新宽度 + * @param height 新高度 + */ + void on_resize(int width, int height); + + /** + * @brief 处理窗口移动事件 + * @param x 新的X坐标 + * @param y 新的Y坐标 + */ + void on_move(int x, int y); + + multicast_delegate on_move_delegate; + multicast_delegate on_resize_delegate; + multicast_delegate on_close_delegate; + + multicast_delegate on_mouse_move_delegate; + multicast_delegate on_mouse_button_down_delegate; + multicast_delegate on_mouse_button_up_delegate; + multicast_delegate on_mouse_button_dbl_delegate; + multicast_delegate on_mouse_wheel_delegate; + multicast_delegate<> on_mouse_leave_delegate; +private: + /** 原生窗口句柄 */ + void* window_handle_{}; +}; diff --git a/src/mirage_core/window/windows/windows_render_window.cpp b/src/mirage_render/platform_window/windows/windows_window.cpp similarity index 76% rename from src/mirage_core/window/windows/windows_render_window.cpp rename to src/mirage_render/platform_window/windows/windows_window.cpp index c72f805..8a3cfb0 100644 --- a/src/mirage_core/window/windows/windows_render_window.cpp +++ b/src/mirage_render/platform_window/windows/windows_window.cpp @@ -1,16 +1,15 @@ -// -// Created by Administrator on 25-3-1. -// #include -#include "window/mwindow.h" -#include +#include "platform_window/platform_window.h" + +#include #define WINDOW_HANDLE static_cast(window_handle_) -std::vector windows; -bool mouse_tracking_ = FALSE; -mwindow* get_window_from_hwnd(const HWND hwnd) { +bool mouse_tracking_ = FALSE; +std::vector windows; + +platform_window* get_window_from_hwnd(const HWND hwnd) { for (const auto& window: windows) { if (window->get_window_handle() == hwnd) { return window; @@ -23,7 +22,7 @@ LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) bool processed = false; // 窗口关闭事件 if (uMsg == WM_CLOSE) { - std::erase_if(windows, [hwnd](mwindow* window) { + std::erase_if(windows, [hwnd](platform_window* window) { if (window->get_window_handle() == hwnd) { window->close(); return true; @@ -49,7 +48,6 @@ LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 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; } @@ -118,7 +116,7 @@ LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) return DefWindowProc(hwnd, uMsg, wParam, lParam); } -bool mwindow::create_window(int width, int height, const wchar_t* title) { +platform_window::platform_window(int32_t in_width, int32_t in_height, const wchar_t* in_title) { WNDCLASS wc = {}; wc.lpfnWndProc = WindowProc; wc.hInstance = GetModuleHandle(nullptr); @@ -128,11 +126,11 @@ bool mwindow::create_window(int width, int height, const wchar_t* title) { wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); RegisterClass(&wc); - RECT rect = { 0, 0, width, height }; + RECT rect = { 0, 0, in_width, in_height }; AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE); window_handle_ = (void*)CreateWindowW( - L"mirage_window_class", title, + L"mirage_window_class", in_title, WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, rect.right - rect.left, rect.bottom - rect.top, @@ -141,32 +139,31 @@ bool mwindow::create_window(int width, int height, const wchar_t* title) { if (!window_handle_) { std::cerr << "Failed to create window" << std::endl; - return false; + return; } windows.push_back(this); - return true; } -void mwindow::show() { ShowWindow(WINDOW_HANDLE, SW_SHOW); } -void mwindow::hide() { ShowWindow(WINDOW_HANDLE, SW_HIDE); } +void platform_window::show() { ShowWindow(WINDOW_HANDLE, SW_SHOW); } +void platform_window::hide() { ShowWindow(WINDOW_HANDLE, SW_HIDE); } -void mwindow::close() { +void platform_window::close() { if (!window_handle_) { return; } on_close_delegate.broadcast(this); DestroyWindow(WINDOW_HANDLE); window_handle_ = nullptr; } -void mwindow::maximize() { ShowWindow(WINDOW_HANDLE, SW_MAXIMIZE); } -void mwindow::minimize() { ShowWindow(WINDOW_HANDLE, SW_MINIMIZE); } +void platform_window::maximize() { ShowWindow(WINDOW_HANDLE, SW_MAXIMIZE); } +void platform_window::minimize() { ShowWindow(WINDOW_HANDLE, SW_MINIMIZE); } -bool mwindow::is_visible() const { +bool platform_window::is_visible() const { // 判断最小化和隐藏状态 return IsWindowVisible(WINDOW_HANDLE) && !IsIconic(WINDOW_HANDLE); } -void mwindow::move(int x, int y) { +void platform_window::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); @@ -174,13 +171,13 @@ void mwindow::move(int x, int y) { on_move(x, y); } -void mwindow::resize(int width, int height) { +void platform_window::resize(int width, int height) { RECT rect; GetWindowRect(WINDOW_HANDLE, &rect); MoveWindow(WINDOW_HANDLE, rect.left, rect.top, width, height, TRUE); } -Eigen::Vector2i mwindow::get_window_size() const { +Eigen::Vector2i platform_window::get_window_size() const { // 获取窗口大小, 包括边框和标题栏 RECT rect; if (GetWindowRect(WINDOW_HANDLE, &rect)) { @@ -191,7 +188,7 @@ Eigen::Vector2i mwindow::get_window_size() const { return Eigen::Vector2i(0, 0); } -Eigen::Vector2i mwindow::get_window_frame_size() const { +Eigen::Vector2i platform_window::get_window_frame_size() const { // 获取窗口大小, 不包括边框和标题栏 (客户区大小) RECT rect; if (GetClientRect(WINDOW_HANDLE, &rect)) { @@ -202,76 +199,76 @@ Eigen::Vector2i mwindow::get_window_frame_size() const { return Eigen::Vector2i(0, 0); } -float mwindow::get_window_dpi_scale() const { +float platform_window::get_window_dpi_scale() const { return 1.f; } -Eigen::Vector2i mwindow::get_window_position() const { +Eigen::Vector2i platform_window::get_window_position() const { RECT rect; if (!GetWindowRect(WINDOW_HANDLE, &rect)) { return {}; } return { rect.left, rect.top }; } -void* mwindow::get_window_handle() const { return window_handle_; } +void* platform_window::get_window_handle() const { return window_handle_; } -mwindow& mwindow::set_title(const wchar_t* title) { +platform_window& platform_window::set_title(const wchar_t* title) { SetWindowText(WINDOW_HANDLE, title); return *this; } -mwindow& mwindow::set_has_minimize_button(bool has_minimize_button) { +platform_window& platform_window::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; } -mwindow& mwindow::set_has_maximize_button(bool has_maximize_button) { +platform_window& platform_window::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; } -mwindow& mwindow::set_has_close_button(bool has_close_button) { +platform_window& platform_window::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; } -mwindow& mwindow::set_has_border(bool has_border) { +platform_window& platform_window::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; } -mwindow& mwindow::set_has_caption(bool has_caption) { +platform_window& platform_window::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; } -mwindow& mwindow::set_has_resizable_border(bool has_resizable_border) { +platform_window& platform_window::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; } -mwindow& mwindow::set_topmost(bool is_topmost) { +platform_window& platform_window::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 mwindow::poll_events() { +bool platform_window::poll_events() { MSG msg; bool has_messages = false; - while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { has_messages = true; TranslateMessage(&msg); DispatchMessage(&msg); @@ -279,4 +276,4 @@ bool mwindow::poll_events() { return has_messages; } -const std::vector& mwindow::get_windows() { return windows; } +const std::vector& platform_window::get_windows() { return windows; } diff --git a/src/mirage_core/render/mirage_paint_context.cpp b/src/mirage_render/render/mirage_paint_context.cpp similarity index 68% rename from src/mirage_core/render/mirage_paint_context.cpp rename to src/mirage_render/render/mirage_paint_context.cpp index d4fa6c2..28754bc 100644 --- a/src/mirage_core/render/mirage_paint_context.cpp +++ b/src/mirage_render/render/mirage_paint_context.cpp @@ -8,8 +8,6 @@ #include "mirage_paint_context.h" -#include "widget/mwidget.h" - //-------------- 初始化 -------------- /** @@ -44,21 +42,6 @@ void mirage_paint_context::reset() { * 设置父组件状态,并通知渲染元素开始新的帧。 * 每个渲染帧的开始都应调用此函数。 */ -void mirage_paint_context::begin_frame(mwidget* in_parent_widget) { - set_parent_widget(in_parent_widget); +void mirage_paint_context::begin_frame() { elements_.begin_frame(); } - -//-------------- 父组件管理 -------------- - -/** - * @brief 设置当前绘制的父组件 - * @param in_widget 要设置的父组件 - * - * 更新当前绘制上下文的父组件引用,并根据父组件的状态 - * 更新父组件启用状态。这影响子组件的启用状态计算。 - */ -void mirage_paint_context::set_parent_widget(mwidget* in_widget) { - parent_enabled_ = in_widget->is_enabled(); - parent_widget_ = in_widget; -} diff --git a/src/mirage_core/render/mirage_paint_context.h b/src/mirage_render/render/mirage_paint_context.h similarity index 85% rename from src/mirage_core/render/mirage_paint_context.h rename to src/mirage_render/render/mirage_paint_context.h index 6db594f..63f62ec 100644 --- a/src/mirage_core/render/mirage_paint_context.h +++ b/src/mirage_render/render/mirage_paint_context.h @@ -10,8 +10,6 @@ #include "render_elements.h" #include "geometry/geometry.h" -class mwidget; - /** * @struct mirage_paint_context * @brief 绘制上下文结构,提供组件绘制所需的环境和功能 @@ -45,7 +43,7 @@ struct mirage_paint_context { * * 初始化新的绘制帧,设置父组件和相关状态。 */ - void begin_frame(mwidget* in_parent_widget); + void begin_frame(); /** * @brief 结束当前绘制帧 @@ -54,20 +52,6 @@ struct mirage_paint_context { */ void end_frame() { elements_.end_frame(); reset(); } - //-------------- 父组件管理 -------------- - - /** - * @brief 获取当前绘制的父组件 - * @return 当前父组件指针 - */ - [[nodiscard]] auto get_parent_widget() const { return parent_widget_; } - - /** - * @brief 设置当前绘制的父组件 - * @param in_widget 父组件指针 - */ - void set_parent_widget(mwidget* in_widget); - //-------------- 图层管理 -------------- /** @@ -112,9 +96,6 @@ struct mirage_paint_context { void set_geometry(const geometry_t& in_geometry) { geometry_ = &in_geometry; } private: - /** 当前绘制的父组件 */ - mwidget* parent_widget_{}; - /** 当前几何区域指针 */ const geometry_t* geometry_{}; diff --git a/src/mirage_core/render/render_context.h b/src/mirage_render/render/render_context.h similarity index 51% rename from src/mirage_core/render/render_context.h rename to src/mirage_render/render/render_context.h index a1e4ae7..738db16 100644 --- a/src/mirage_core/render/render_context.h +++ b/src/mirage_render/render/render_context.h @@ -1,6 +1,6 @@ #pragma once /** - * @file mirage_render_context.h + * @file render_context.h * @brief 定义渲染上下文的抽象接口 * * 本文件定义了mirage_render_context抽象类,提供了渲染上下文的基本操作接口, @@ -11,7 +11,65 @@ #include "misc/mirage_type.h" #include "sokol_gfx.h" -class mwindow; +/** + * @struct mirage_window_state + * @brief 存储窗口渲染状态的结构 + * + * 维护与特定窗口关联的渲染资源和状态,包括GPU缓冲区、交换链和渲染设置。 + * 这是一个虚拟基类,不同平台可以提供特定实现。 + */ +struct mirage_window_state { + /** + * @brief 虚析构函数 + * + * 确保派生类可以正确清理资源。 + */ + virtual ~mirage_window_state() { + clear(); + } + + /** + * @brief 清理资源 + */ + void clear() { on_clear(); } + + /** 顶点缓冲区 */ + sg_buffer buffer{}; + + /** 窗口交换链 */ + sg_swapchain swapchain{}; + + /** 渲染绑定 */ + sg_bindings bindings{}; + + /** 渲染管线 */ + sg_pipeline pipeline{}; + + /** 垂直同步标志 */ + bool vsync = true; + + /** + * @brief 清理资源 + * + * 虚方法,由派生类实现以释放平台特定资源。 + */ + virtual void on_clear() {} + + /** + * @brief 呈现渲染内容 + * + * 虚方法,由派生类实现以将渲染内容呈现到窗口。 + */ + virtual void present() {} + + /** + * @brief 重建交换链 + * @param size 新的窗口大小 + * + * 当窗口大小改变时重建交换链。虚方法,由派生类实现。 + */ + virtual void rebuild_swapchain(const Eigen::Vector2i& size) {} +}; /** * @class mirage_render_context @@ -23,12 +81,18 @@ class mwindow; */ class mirage_render_context { public: + mirage_render_context() { + instance_ = this; + } + /** * @brief 虚析构函数 * * 确保派生类可以正确析构。 */ - virtual ~mirage_render_context() = default; + virtual ~mirage_render_context() { + instance_ = nullptr; + } //-------------- 生命周期管理 -------------- @@ -64,14 +128,11 @@ public: */ virtual sg_environment get_environment() = 0; - /** - * @brief 设置渲染表面 - * @param in_window 要渲染到的窗口 - * @return 设置是否成功 - * - * 将渲染上下文与指定窗口的表面关联。 - */ - virtual bool setup_surface(mwindow* in_window) { return false; } + virtual std::unique_ptr create_window_state(const Eigen::Vector2i& in_window_frame_size, void* in_window_handle) = 0; + + static auto get() { return instance_; } +private: + inline static mirage_render_context* instance_{}; }; /** diff --git a/src/mirage_core/render/render_elements.cpp b/src/mirage_render/render/render_elements.cpp similarity index 100% rename from src/mirage_core/render/render_elements.cpp rename to src/mirage_render/render/render_elements.cpp diff --git a/src/mirage_core/render/render_elements.h b/src/mirage_render/render/render_elements.h similarity index 100% rename from src/mirage_core/render/render_elements.h rename to src/mirage_render/render/render_elements.h diff --git a/src/mirage_core/render/render_image.h b/src/mirage_render/render/render_image.h similarity index 100% rename from src/mirage_core/render/render_image.h rename to src/mirage_render/render/render_image.h diff --git a/src/mirage_core/render/windows/pixel_format_convert.h b/src/mirage_render/render/windows/pixel_format_convert.h similarity index 100% rename from src/mirage_core/render/windows/pixel_format_convert.h rename to src/mirage_render/render/windows/pixel_format_convert.h diff --git a/src/mirage_core/render/windows/render_image.cpp b/src/mirage_render/render/windows/render_image.cpp similarity index 92% rename from src/mirage_core/render/windows/render_image.cpp rename to src/mirage_render/render/windows/render_image.cpp index 86f7ab9..a8011b7 100644 --- a/src/mirage_core/render/windows/render_image.cpp +++ b/src/mirage_render/render/windows/render_image.cpp @@ -1,12 +1,11 @@ #include "render/render_image.h" #include -#include "mirage.h" #include "pixel_format_convert.h" #include "windows_render_context.h" void render_image::create(void* in_data, const Eigen::Vector2i& in_size, sg_pixel_format in_pixel_format) { - auto render_context = mirage_app::get_render_context(); + auto render_context = mirage_render_context::get(); if (!render_context) return; auto windows_render_context = (windows_mirage_render_context*)render_context; diff --git a/src/mirage_core/render/windows/windows_render_context.cpp b/src/mirage_render/render/windows/windows_render_context.cpp similarity index 96% rename from src/mirage_core/render/windows/windows_render_context.cpp rename to src/mirage_render/render/windows/windows_render_context.cpp index 2362da4..0e12142 100644 --- a/src/mirage_core/render/windows/windows_render_context.cpp +++ b/src/mirage_render/render/windows/windows_render_context.cpp @@ -1,14 +1,9 @@ -// -// Created by Administrator on 25-3-1. -// - #include "windows_render_context.h" #include #include #include "misc/angle_literals.h" -#include "window/mwindow.h" #include #include "windows_render_state.h" @@ -230,12 +225,12 @@ sg_environment windows_mirage_render_context::get_environment() { }; } -bool windows_mirage_render_context::setup_surface(mwindow* in_window) { +std::unique_ptr windows_mirage_render_context::create_window_state(const Eigen::Vector2i& in_window_frame_size, void* in_window_handle) { auto state = std::make_unique(); - if (!state->init(device, dxgi_factory, in_window)) { return false; } - state->set_tearing_support(tearing_supported); - in_window->set_state(std::move(state)); - return true; + if (!state->init(device, dxgi_factory, in_window_frame_size, in_window_handle)) { + return {}; + } + return state; } mirage_render_context* mirage_create_render_context() { return new windows_mirage_render_context(); } diff --git a/src/mirage_core/render/windows/windows_render_context.h b/src/mirage_render/render/windows/windows_render_context.h similarity index 90% rename from src/mirage_core/render/windows/windows_render_context.h rename to src/mirage_render/render/windows/windows_render_context.h index 1af9cf2..e42045b 100644 --- a/src/mirage_core/render/windows/windows_render_context.h +++ b/src/mirage_render/render/windows/windows_render_context.h @@ -57,16 +57,6 @@ public: */ sg_environment get_environment() override; - /** - * @brief 设置渲染表面 - * @param in_window 要渲染到的窗口 - * @return 设置是否成功 - * - * 为指定窗口创建交换链和渲染目标视图, - * 建立Direct3D与窗口表面的连接。 - */ - bool setup_surface(mwindow* in_window) override; - /** * @brief 获取设备 * @@ -96,6 +86,8 @@ public: * */ [[nodiscard]] auto is_tearing_supported() const { return tearing_supported; } + + virtual std::unique_ptr create_window_state(const Eigen::Vector2i& in_window_frame_size, void* in_window_handle) override; private: /** Direct3D 11设备 */ ID3D11Device* device{}; diff --git a/src/mirage_core/render/windows/windows_render_state.cpp b/src/mirage_render/render/windows/windows_render_state.cpp similarity index 97% rename from src/mirage_core/render/windows/windows_render_state.cpp rename to src/mirage_render/render/windows/windows_render_state.cpp index 6becd5b..3539378 100644 --- a/src/mirage_core/render/windows/windows_render_state.cpp +++ b/src/mirage_render/render/windows/windows_render_state.cpp @@ -17,18 +17,19 @@ * @brief 初始化窗口渲染状态 * @param in_device Direct3D 11设备 * @param in_factory DXGI工厂 - * @param in_window 关联的窗口 + * @param in_window_frame_size 窗口框架大小 + * @param in_window_handle Windows HWND句柄 * @return 初始化是否成功 * * 创建交换链和渲染目标视图,并配置sokol图形库需要的交换链信息。 * 使用窗口的当前大小和句柄设置交换链参数。 */ -bool windows_render_state::init(ID3D11Device* in_device, IDXGIFactory* in_factory, mwindow* in_window) { +bool windows_render_state::init(ID3D11Device* in_device, IDXGIFactory* in_factory, const Eigen::Vector2i& in_window_frame_size, void* in_window_handle) { 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_frame_size; + const auto window_handle = in_window_handle; #if MIRAGE_USE_HDR constexpr auto sg_format = MIRAGE_HDR_FORMAT; @@ -54,7 +55,7 @@ bool windows_render_state::init(ID3D11Device* in_device, IDXGIFactory* in_factor }, .BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT, .BufferCount = 2, - .OutputWindow = (HWND) window_handle, + .OutputWindow = (HWND)window_handle, .Windowed = TRUE, .SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD, .Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING diff --git a/src/mirage_core/render/windows/windows_render_state.h b/src/mirage_render/render/windows/windows_render_state.h similarity index 93% rename from src/mirage_core/render/windows/windows_render_state.h rename to src/mirage_render/render/windows/windows_render_state.h index f0e69eb..2c35e86 100644 --- a/src/mirage_core/render/windows/windows_render_state.h +++ b/src/mirage_render/render/windows/windows_render_state.h @@ -7,10 +7,11 @@ * 使用Direct3D 11和DXGI处理窗口的渲染表面、交换链和呈现操作。 */ -#include "window/mwindow.h" #include #include +#include "render/render_context.h" + /** * @struct windows_render_state * @brief Windows平台的窗口渲染状态实现 @@ -37,13 +38,14 @@ struct windows_render_state final : mirage_window_state { * @brief 初始化窗口渲染状态 * @param in_device Direct3D 11设备 * @param in_factory DXGI工厂 - * @param in_window 关联的窗口 + * @param in_window_frame_size 窗口框架大小 + * @param in_window_handle Windows HWND句柄 * @return 初始化是否成功 * * 使用提供的Direct3D设备和DXGI工厂初始化窗口渲染状态, * 创建交换链和渲染目标视图。 */ - bool init(ID3D11Device* in_device, IDXGIFactory* in_factory, mwindow* in_window); + bool init(ID3D11Device* in_device, IDXGIFactory* in_factory, const Eigen::Vector2i& in_window_frame_size, void* in_window_handle); /** * @brief 设置可变刷新率支持 diff --git a/src/mirage_core/shaders/mirage_rounded_rect.slang b/src/mirage_render/shaders/mirage_rounded_rect.slang similarity index 100% rename from src/mirage_core/shaders/mirage_rounded_rect.slang rename to src/mirage_render/shaders/mirage_rounded_rect.slang diff --git a/src/mirage_core/shaders/mirage_util.slang b/src/mirage_render/shaders/mirage_util.slang similarity index 100% rename from src/mirage_core/shaders/mirage_util.slang rename to src/mirage_render/shaders/mirage_util.slang diff --git a/src/mirage_core/shaders/test.slang b/src/mirage_render/shaders/test.slang similarity index 100% rename from src/mirage_core/shaders/test.slang rename to src/mirage_render/shaders/test.slang diff --git a/src/mirage_widget/CMakeLists.txt b/src/mirage_widget/CMakeLists.txt new file mode 100644 index 0000000..f6d2e88 --- /dev/null +++ b/src/mirage_widget/CMakeLists.txt @@ -0,0 +1,8 @@ +project(mirage_widget) + +set(SRC_FILES) +retrieve_files(${CMAKE_CURRENT_SOURCE_DIR} SRC_FILES) + +add_library(${PROJECT_NAME} STATIC ${SRC_FILES}) +target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(${PROJECT_NAME} PUBLIC mirage_core mirage_render) diff --git a/src/mirage_core/widget/compound_widget/mborder.cpp b/src/mirage_widget/widget/compound_widget/mborder.cpp similarity index 100% rename from src/mirage_core/widget/compound_widget/mborder.cpp rename to src/mirage_widget/widget/compound_widget/mborder.cpp diff --git a/src/mirage_core/widget/compound_widget/mborder.h b/src/mirage_widget/widget/compound_widget/mborder.h similarity index 97% rename from src/mirage_core/widget/compound_widget/mborder.h rename to src/mirage_widget/widget/compound_widget/mborder.h index b8b59ad..3ce4973 100644 --- a/src/mirage_core/widget/compound_widget/mborder.h +++ b/src/mirage_widget/widget/compound_widget/mborder.h @@ -52,7 +52,7 @@ public: */ virtual void arrange_children(const geometry_t& in_allotted_geometry) override { const auto& slot = get_child_slot(); - if (const auto child_widget = slot.get().lock()) + if (const auto child_widget = slot.get()) arrange_single_child(in_allotted_geometry, child_widget, slot.h_alignment(), diff --git a/src/mirage_core/widget/compound_widget/mbutton.cpp b/src/mirage_widget/widget/compound_widget/mbutton.cpp similarity index 85% rename from src/mirage_core/widget/compound_widget/mbutton.cpp rename to src/mirage_widget/widget/compound_widget/mbutton.cpp index b6f892a..17d6839 100644 --- a/src/mirage_core/widget/compound_widget/mbutton.cpp +++ b/src/mirage_widget/widget/compound_widget/mbutton.cpp @@ -9,8 +9,8 @@ mbutton::mbutton() { color_ = {0.5, 0.5, 0.5, 1}; } -void mbutton::init_component(mustache::EntityManager& in_entity_manager) { - mborder::init_component(in_entity_manager); +void mbutton::init() { + mborder::init(); set_visibility(visibility_t::visible); } @@ -28,7 +28,7 @@ Eigen::Vector2f mbutton::compute_desired_size(float in_layout_scale_multiplier) const margin_t button_padding = { 10, 10, 10, 10 }; // TODO 临时值, 以后会从主题中获取 Eigen::Vector2f desired_size = button_padding.get_total_spacing(); - if (const auto& child = get_child_slot().get().lock()) { + if (const auto& child = get_child_slot().get()) { desired_size += child->compute_desired_size(in_layout_scale_multiplier); } @@ -39,17 +39,17 @@ void mbutton::on_click(const Eigen::Vector2f& in_position, mouse_button in_butto 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}; - auto parent = get_parent_widget(); - if (std::shared_ptr hbox = std::dynamic_pointer_cast(parent.lock())) { - hbox->add_child() - .stretch() - .margin({ 5 }); - } - if (std::shared_ptr vbox = std::dynamic_pointer_cast(parent.lock())) { - vbox->add_child() - .stretch() - .margin({ 5 }); - } + // const auto& parent = get_parent(); + // if (std::shared_ptr hbox = std::dynamic_pointer_cast(parent)) { + // hbox->add_child() + // .stretch() + // .margin({ 5 }); + // } + // if (std::shared_ptr vbox = std::dynamic_pointer_cast(parent)) { + // vbox->add_child() + // .stretch() + // .margin({ 5 }); + // } invalidate(invalidate_reason::paint); } diff --git a/src/mirage_core/widget/compound_widget/mbutton.h b/src/mirage_widget/widget/compound_widget/mbutton.h similarity index 92% rename from src/mirage_core/widget/compound_widget/mbutton.h rename to src/mirage_widget/widget/compound_widget/mbutton.h index 3ce8016..342ce06 100644 --- a/src/mirage_core/widget/compound_widget/mbutton.h +++ b/src/mirage_widget/widget/compound_widget/mbutton.h @@ -4,7 +4,7 @@ class mbutton : public mborder { public: mbutton(); - virtual void init_component(mustache::EntityManager& in_entity_manager) override; + virtual void init() override; virtual void on_paint(mirage_paint_context& in_context) override; virtual Eigen::Vector2f compute_desired_size(float in_layout_scale_multiplier) const override; diff --git a/src/mirage_core/widget/leaf_widget/mimage.cpp b/src/mirage_widget/widget/leaf_widget/mimage.cpp similarity index 100% rename from src/mirage_core/widget/leaf_widget/mimage.cpp rename to src/mirage_widget/widget/leaf_widget/mimage.cpp diff --git a/src/mirage_core/widget/leaf_widget/mimage.h b/src/mirage_widget/widget/leaf_widget/mimage.h similarity index 100% rename from src/mirage_core/widget/leaf_widget/mimage.h rename to src/mirage_widget/widget/leaf_widget/mimage.h diff --git a/src/mirage_core/widget/mcompound_widget.h b/src/mirage_widget/widget/mcompound_widget.h similarity index 88% rename from src/mirage_core/widget/mcompound_widget.h rename to src/mirage_widget/widget/mcompound_widget.h index 53d7ec6..42d2136 100644 --- a/src/mirage_core/widget/mcompound_widget.h +++ b/src/mirage_widget/widget/mcompound_widget.h @@ -7,7 +7,8 @@ * 通过模板参数SlotType可以自定义子组件插槽的具体行为。 */ -#include "geometry/layout_utils.h" +#include "mwidget.h" +#include "widget_utils/layout_utils.h" #include "slot_util.h" /** @@ -28,7 +29,7 @@ struct mcompound_widget_slot { auto& me() { return static_cast(*this); } - widget_key slot_owner; + std::weak_ptr slot_owner; // 插槽功能宏 - 定义内容管理和对齐属性 SLOT_CONTENT() @@ -48,7 +49,7 @@ struct mcompound_widget_slot { template class mcompound_widget : public mwidget { public: - virtual void init_component(mustache::EntityManager& in_entity_manager) override; + virtual void init() override; //-------------- 内容设置 -------------- /** @@ -97,7 +98,8 @@ public: */ virtual void arrange_children(const geometry_t& in_allotted_geometry) override; - SlotType& get_child_slot() const { return get_component_ref(); } + SlotType& get_child_slot() { return slot_; } + const SlotType& get_child_slot() const { return slot_; } protected: /** * @brief 在设置内容时的回调函数 @@ -107,13 +109,14 @@ protected: * 基类实现为空。 */ virtual void on_set_content(const std::shared_ptr& in_widget) {} + + SlotType slot_; }; template -void mcompound_widget::init_component(mustache::EntityManager& in_entity_manager) { - mwidget::init_component(in_entity_manager); - auto& slot = add_component(); - slot.slot_owner = get_key(); +void mcompound_widget::init() { + mwidget::init(); + slot_.slot_owner = shared_from_this(); } /** @@ -123,7 +126,7 @@ void mcompound_widget::init_component(mustache::EntityManager& in_enti */ template Eigen::Vector2f mcompound_widget::compute_desired_size(float in_layout_scale_multiplier) const { - if (auto child_widget = get_child_slot().get().lock()) { + if (const auto& child_widget = get_child_slot().get()) { if (has_any_flag(child_widget->get_visibility(), visibility_t::any_visible)) { return child_widget->compute_desired_size(in_layout_scale_multiplier); } @@ -138,8 +141,8 @@ Eigen::Vector2f mcompound_widget::compute_desired_size(float in_layout */ template void mcompound_widget::arrange_children(const geometry_t& in_allotted_geometry) { - auto& slot = get_child_slot(); - if (const auto child_widget = slot.get().lock()) { + const auto& slot = get_child_slot(); + if (const auto& child_widget = slot.get()) { arrange_single_child(in_allotted_geometry, child_widget, slot.h_alignment(), slot.v_alignment(), {}); } } diff --git a/src/mirage_core/widget/mleaf_widget.h b/src/mirage_widget/widget/mleaf_widget.h similarity index 100% rename from src/mirage_core/widget/mleaf_widget.h rename to src/mirage_widget/widget/mleaf_widget.h diff --git a/src/mirage_core/widget/mpanel_widget.h b/src/mirage_widget/widget/mpanel_widget.h similarity index 63% rename from src/mirage_core/widget/mpanel_widget.h rename to src/mirage_widget/widget/mpanel_widget.h index 8092826..dc91bf4 100644 --- a/src/mirage_core/widget/mpanel_widget.h +++ b/src/mirage_widget/widget/mpanel_widget.h @@ -9,7 +9,6 @@ #include "mwidget.h" #include "slot_util.h" -#include "widget_tree/widget_manager.h" /** * @struct mpanel_widget_slot @@ -29,7 +28,7 @@ struct mpanel_widget_slot { auto& me() { return static_cast(*this); } - widget_key slot_owner; + std::weak_ptr slot_owner; // 插槽功能宏 - 定义内容管理 SLOT_CONTENT() @@ -74,61 +73,31 @@ public: * 创建并添加一个新的子组件插槽到面板中。 */ template - auto add_child() -> SlotType& { - auto& manager = widget_manager::get(); - auto widget_entity = manager.new_widget(get_window()); - auto w = widget_entity.lock(); - auto& slot = w->template add_component(); - slot.slot_owner = key_; - slot.set(w); + auto add_slot() -> SlotType& { + auto& slot = slots_.emplace_back(); + slot.slot_owner = shared_from_this(); invalidate(invalidate_reason::all); return slot; } template auto add_child(Args&&... in_args) -> SlotType& { - auto& manager = widget_manager::get(); - auto widget_entity = manager.new_widget(get_window(), std::forward(in_args)...); - auto w = widget_entity.lock(); - auto& slot = w->template add_component(); - slot.slot_owner = key_; - slot.set(w); + auto& slot = slots_.emplace_back(std::forward(in_args)...); + slot.slot_owner = shared_from_this(); invalidate(invalidate_reason::all); return slot; } - auto get_slots_ref() { - std::vector children; - if (auto hierarchy = get_component()) { - auto& manager = widget_manager::get(); - for (const auto& child : hierarchy->children) { - if (auto slot = manager.get_component(child)) { - children.push_back(*slot); - } - } - } - return children; - } - auto get_slots() const { - std::vector children; - if (auto hierarchy = get_component()) { - auto& manager = widget_manager::get(); - for (const auto& child : hierarchy->children) { - if (auto slot = manager.get_component(child)) { - children.push_back(slot); - } - } - } - return children; + const auto& get_slots() const { + return slots_; } +private: + std::vector slots_; }; template void mpanel_widget::cache_all_children_desired_size(float in_layout_scale_multiplier, bool in_force) const { - auto& manager = widget_manager::get(); - const auto& hierarchy = get_component_ref(); - - for (const auto& child: hierarchy.children) { - const auto& ptr = manager.get_component_ref(child); + for (const auto& slot: slots_) { + const auto& ptr = slot.get(); ptr->cache_desired_size(in_layout_scale_multiplier, in_force); } } diff --git a/src/mirage_widget/widget/mwidget.cpp b/src/mirage_widget/widget/mwidget.cpp new file mode 100644 index 0000000..8b77742 --- /dev/null +++ b/src/mirage_widget/widget/mwidget.cpp @@ -0,0 +1,109 @@ +#include "mwidget.h" + +#include "window/mwindow.h" +#include "geometry/dpi_helper.h" + +mwidget::~mwidget() { + remove_from_parent(); +} + +const mwindow* mwidget::get_window() const { + if (const auto parent = get_parent()) { + return parent->get_window(); + } + return nullptr; +} + +mwindow* mwidget::get_window() { + if (const auto parent = get_parent()) { + return parent->get_window(); + } + return nullptr; +} + +void mwidget::set_geometry(const geometry_t& in_geometry) { + geometry_ = in_geometry; +} + +void mwidget::cache_desired_size(float in_layout_scale_multiplier, bool in_force) { + if (!in_force && desired_size_.has_value()) + return; + desired_size_ = compute_desired_size(in_layout_scale_multiplier); +} + +auto mwidget::get_dpi_scale() const -> float { + if (const auto window = get_window()) + return window->get_dpi_scale(); + return 1.f; +} + +void mwidget::set_parent(const std::shared_ptr& in_parent) { + // 先从原父节点中移除 + remove_from_parent(); + + // 设置新的父节点 + parent_ = in_parent; + in_parent->add_child(shared_from_this()); +} + +void mwidget::remove_from_parent() { + const auto parent = get_parent(); + if (!parent) + return; + + parent->remove_child(shared_from_this()); +} + +void mwidget::add_child(const std::shared_ptr& in_child) { +#if DEBUG + // 避免重复添加 + const auto& it = std::ranges::find_if(children_, [&](const auto& child) { return child == in_child; }); + assert(it == children_.end()); +#endif + children_.push_back(in_child); + invalidate(invalidate_reason::layout); +} + +void mwidget::remove_child(const std::shared_ptr& in_child) { + const auto& it = std::ranges::find_if(children_, [&](const auto& child) { + return child == in_child; + }); + if (it != children_.end()) { + children_.erase(it); + } + invalidate(invalidate_reason::layout); +} + +void mwidget::invalidate(invalidate_reason in_reason) { + // 设置失效标记 + set_flag(invalidate_, in_reason); + + // 通知父组件 + if (const auto parent = get_parent()) { + parent->invalidate(in_reason); + } + + // 如果是布局失效, 重置期望大小 + if (has_any_flag(in_reason, invalidate_reason::layout)) { + desired_size_.reset(); + } +} + +bool mwidget::can_hit_test() const { + // 不可见组件不参与命中测试 + if (has_any_flag(visibility_, visibility_t::any_invisible)) { + return false; + } + + // 如果整个组件树都不可点击 + if (has_flag(visibility_, visibility_t::hit_test_invisible)) { + return false; + } + + // 如果组件自身不可点击(但子组件可以) + if (has_flag(visibility_, visibility_t::self_hit_test_invisible)) { + return false; + } + + return true; +} diff --git a/src/mirage_core/widget/mwidget.h b/src/mirage_widget/widget/mwidget.h similarity index 54% rename from src/mirage_core/widget/mwidget.h rename to src/mirage_widget/widget/mwidget.h index eccb444..892226e 100644 --- a/src/mirage_core/widget/mwidget.h +++ b/src/mirage_widget/widget/mwidget.h @@ -1,8 +1,8 @@ #pragma once -#include "misc/hit_test_result.h" +#include "misc/invalidate_reason.h" +#include "widget_tree/hit_test_result.h" #include "render/mirage_paint_context.h" -#include "mustache/ecs/entity_manager.hpp" -#include "widget_tree/widget_manager.h" +#include "misc/key_type/key_type.h" class mwindow; @@ -15,59 +15,43 @@ class mwindow; class mwidget : public std::enable_shared_from_this { public: virtual ~mwidget(); + virtual const mwindow* get_window() const; + virtual mwindow* get_window(); - template - auto& add_component() { - return widget_manager::get().add_component(key_); - } - template - auto& add_component(Args&&... args) { - return widget_manager::get().add_component(key_, std::forward(args)...); - } - template - auto* get_component() const { - return widget_manager::get().get_component(key_); - } - template - auto& get_component_ref() const { - return *widget_manager::get().get_component(key_); - } + virtual void init() {} - void set_key(const widget_key& in_key) { key_ = in_key; } - - virtual void init_component(mustache::EntityManager& in_entity_manager); virtual void on_paint(mirage_paint_context& in_context) = 0; - void set_geometry(const geometry_t& in_geometry); + void set_geometry(const geometry_t& in_geometry); + const auto& get_geometry() const { return geometry_; } - const auto& get_geometry() const { return get_component_ref().geometry; } virtual auto compute_desired_size(float in_layout_scale_multiplier) const -> Eigen::Vector2f = 0; void cache_desired_size(float in_layout_scale_multiplier, bool in_force = false); - const auto& get_desired_size() const { return get_component_ref().desired_size; } + const auto& get_desired_size() const { return desired_size_; } virtual void arrange_children(const geometry_t& in_allotted_geometry) = 0; - const auto& get_key() const { return key_; } - auto get_window() const { return key_.get_window(); } - auto get_entity() const { return key_.get_entity(); } - auto get_dpi_scale() const -> float; + virtual auto get_dpi_scale() const -> float; - auto get_parent() const { return get_component_ref().parent; } - auto get_children() const { return get_component_ref().children; } - auto get_parent_widget() const -> std::weak_ptr; - void set_parent(const widget_key& in_parent); + auto get_parent() const { return parent_.lock(); } + const auto& get_children() const { return children_; } + void set_parent(const std::shared_ptr& in_parent); + void remove_from_parent(); + + void add_child(const std::shared_ptr& in_child); + void remove_child(const std::shared_ptr& in_child); virtual void invalidate(invalidate_reason in_reason); - auto has_invalidate_reason(invalidate_reason in_reason) const { return get_component_ref().has(in_reason); } - void clear_invalidate_reason(invalidate_reason in_reason) { get_component_ref().unset(in_reason); } - auto get_invalidation_reason() const { return get_component_ref().invalidate; } + auto has_invalidate_reason(invalidate_reason in_reason) const { return has_any_flag(invalidate_, in_reason); } + void set_invalidate_reason(invalidate_reason in_reason) { set_flag(invalidate_, in_reason); } + void unset_invalid_reason(invalidate_reason in_reason) { clear_flag(invalidate_, in_reason); } + auto get_invalidation_reason() const { return invalidate_; } + auto is_enabled() const { return enabled_; } + void set_enabled(bool in_enabled) { enabled_ = in_enabled; } + auto get_visibility() const { return visibility_; } + void set_visibility(visibility_t in_visibility) { visibility_ = in_visibility; } - auto is_enabled() const { return get_component_ref().is_enabled(); } - void set_enabled(bool in_enabled) { get_component_ref().set_enabled(in_enabled); } - auto get_visibility() const { return get_component_ref().get_visibility(); } - void set_visibility(visibility_t in_visibility) { get_component_ref().set_visibility(in_visibility); } - - bool can_hit_test() const { return get_component_ref().can_hit_test(); } + bool can_hit_test() const; //-------------- 鼠标事件处理 -------------- @@ -129,6 +113,13 @@ public: * @result 命中测试结果 */ virtual hit_test_handle on_mouse_wheel(const Eigen::Vector2f& in_position, const wheel_event& in_delta) { return hit_test_handle::unhandled(); } + protected: - widget_key key_ = widget_key::invalid(); + bool enabled_ = true; + invalidate_reason invalidate_{}; + visibility_t visibility_ = visibility_t::self_hit_test_invisible; + std::optional desired_size_{}; + std::weak_ptr parent_; + std::vector> children_; + geometry_t geometry_{}; }; diff --git a/src/mirage_core/widget/panel_widget/mbox.cpp b/src/mirage_widget/widget/panel_widget/mbox.cpp similarity index 96% rename from src/mirage_core/widget/panel_widget/mbox.cpp rename to src/mirage_widget/widget/panel_widget/mbox.cpp index 1461234..abdc583 100644 --- a/src/mirage_core/widget/panel_widget/mbox.cpp +++ b/src/mirage_widget/widget/panel_widget/mbox.cpp @@ -1,6 +1,6 @@ #include "mbox.h" -#include "geometry/layout_utils.h" +#include "widget_utils/layout_utils.h" void mh_box::arrange_children(const geometry_t& in_allotted_geometry) { arrange_box_children(in_allotted_geometry, diff --git a/src/mirage_core/widget/panel_widget/mbox.h b/src/mirage_widget/widget/panel_widget/mbox.h similarity index 100% rename from src/mirage_core/widget/panel_widget/mbox.h rename to src/mirage_widget/widget/panel_widget/mbox.h diff --git a/src/mirage_core/widget/slot_util.h b/src/mirage_widget/widget/slot_util.h similarity index 92% rename from src/mirage_core/widget/slot_util.h rename to src/mirage_widget/widget/slot_util.h index 895e83f..eb3161c 100644 --- a/src/mirage_core/widget/slot_util.h +++ b/src/mirage_widget/widget/slot_util.h @@ -16,7 +16,8 @@ public: \ auto& set(const std::shared_ptr& in_widget) { \ widget_ = in_widget; \ - in_widget->set_parent(slot_owner); \ + in_widget->init(); \ + in_widget->set_parent(slot_owner.lock()); \ return me(); \ } \ const auto& get() const { return widget_; } \ @@ -25,7 +26,7 @@ return me(); \ } \ protected: \ - std::weak_ptr widget_{}; + std::shared_ptr widget_{}; #define SLOT_OPTIONAL_ATTRIBUTE(type, name) \ public: \ diff --git a/src/mirage_core/widget/widget_size.h b/src/mirage_widget/widget/widget_size.h similarity index 100% rename from src/mirage_core/widget/widget_size.h rename to src/mirage_widget/widget/widget_size.h diff --git a/src/mirage_core/misc/hit_test_result.h b/src/mirage_widget/widget_tree/hit_test_result.h similarity index 82% rename from src/mirage_core/misc/hit_test_result.h rename to src/mirage_widget/widget_tree/hit_test_result.h index 34b8f8d..936f2e1 100644 --- a/src/mirage_core/misc/hit_test_result.h +++ b/src/mirage_widget/widget_tree/hit_test_result.h @@ -5,11 +5,9 @@ */ #include -#include #include -#include "widget_tree/widget_component.h" - +class mwidget; /** * @enum hit_test_result_behavior * @brief 定义命中测试结果的行为 @@ -35,16 +33,20 @@ public: bool is_handled() const { return behavior_ == hit_test_result_behavior::handle_event; } + + operator bool() const { + return is_handled(); + } private: hit_test_result_behavior behavior_{ hit_test_result_behavior::continue_test }; }; struct hit_test_result { - widget_key widget; + std::shared_ptr widget; Eigen::Vector2f widget_space_pos; operator bool() const { - return widget; + return static_cast(widget); } - operator widget_key() const { return widget; } + operator std::shared_ptr() const { return widget; } }; \ No newline at end of file diff --git a/src/mirage_core/geometry/layout_utils.cpp b/src/mirage_widget/widget_utils/layout_utils.cpp similarity index 100% rename from src/mirage_core/geometry/layout_utils.cpp rename to src/mirage_widget/widget_utils/layout_utils.cpp diff --git a/src/mirage_core/geometry/layout_utils.h b/src/mirage_widget/widget_utils/layout_utils.h similarity index 94% rename from src/mirage_core/geometry/layout_utils.h rename to src/mirage_widget/widget_utils/layout_utils.h index 590c0ee..2988816 100644 --- a/src/mirage_core/geometry/layout_utils.h +++ b/src/mirage_widget/widget_utils/layout_utils.h @@ -1,6 +1,6 @@ #pragma once #include "widget/mwidget.h" -#include "geometry.h" +#include "geometry/geometry.h" #include "misc/mirage_type.h" /** @@ -25,7 +25,7 @@ void arrange_box_children( const geometry_t& in_allotted_geometry, float in_layout_scale_multiplier, visibility_t in_visibility_filter, - const std::vector& child_slots, + const std::vector& child_slots, flow_direction_t flow_dir = flow_direction_t::left_to_right ){ const auto& container_size = in_allotted_geometry.get_local_size(); @@ -54,8 +54,7 @@ void arrange_box_children( float total_margins = 0.0f; // 主轴方向上所有边距的总和 for (const auto& child_slot : child_slots) { - std::weak_ptr weak_widget = child_slot->get(); - const auto widget = weak_widget.lock(); + const auto& widget = child_slot.get(); // 无效部件或不可见部件跳过 if (!widget) continue; @@ -63,7 +62,7 @@ void arrange_box_children( if (!has_any_flag(widget->get_visibility(), in_visibility_filter)) continue; - const auto& margin = child_slot->margin(); + const auto& margin = child_slot.margin(); const float margin_left = margin.left; const float margin_right = margin.right; const float margin_top = margin.top; @@ -84,7 +83,7 @@ void arrange_box_children( total_margins += margin_start + margin_end; slot_widget_info info{ widget, - child_slot, + &child_slot, 0.0f, margin_start, margin_end, @@ -92,7 +91,7 @@ void arrange_box_children( margin_cross_end }; - const auto& size_attr = child_slot->size(); + const auto& size_attr = child_slot.size(); if (size_attr.is_auto_size()) { // 缓存部件的期望大小 widget->cache_desired_size(in_layout_scale_multiplier); @@ -169,11 +168,11 @@ void arrange_box_children( // 计算水平或垂直框的期望大小 template -Eigen::Vector2f compute_box_desired_size(const std::vector& child_slots) { +Eigen::Vector2f compute_box_desired_size(const std::vector& child_slots) { Eigen::Vector2f desired_size = Eigen::Vector2f::Zero(); for (const auto& slot : child_slots) { - auto widget = slot->get().lock(); + const auto& widget = slot.get(); if (widget && has_any_flag(widget->get_visibility(), visibility_t::any_visible)) { widget->cache_desired_size(widget->get_dpi_scale()); Eigen::Vector2f widget_desired_size = widget->get_desired_size().value(); diff --git a/src/mirage_widget/window/mwindow.cpp b/src/mirage_widget/window/mwindow.cpp new file mode 100644 index 0000000..8322b46 --- /dev/null +++ b/src/mirage_widget/window/mwindow.cpp @@ -0,0 +1,315 @@ +#include "mwindow.h" + +#include "geometry/dpi_helper.h" +#include "render/render_context.h" + +mwindow::mwindow(int32_t in_width, int32_t in_height, const wchar_t* in_title) { + windows_.push_back(this); + + platform_window_ = std::make_unique(in_width, in_height, in_title); + platform_window_->on_resize_delegate.add_raw(this, &mwindow::on_resize); + + const auto& window_frame_size = platform_window_->get_window_frame_size(); + state_ = mirage_render_context::get()->create_window_state(window_frame_size, + platform_window_->get_window_handle()); + platform_window_->show(); + context.init(window_frame_size); + invalidate_all(); + + platform_window_->on_mouse_move_delegate.add_raw(this, &mwindow::process_mouse_move); + platform_window_->on_mouse_button_down_delegate.add_raw(this, &mwindow::process_mouse_button_down); + platform_window_->on_mouse_button_up_delegate.add_raw(this, &mwindow::process_mouse_button_up); + platform_window_->on_mouse_wheel_delegate.add_raw(this, &mwindow::process_mouse_wheel); + platform_window_->on_mouse_leave_delegate.add_raw(this, &mwindow::process_mouse_leave); + platform_window_->on_mouse_button_dbl_delegate.add_raw(this, &mwindow::process_mouse_button_dbl); + + const transform2d identity; + geometry_ = { window_frame_size.cast(), identity, identity }; +} + +mwindow::~mwindow() { + platform_window_->close(); + std::erase(windows_, this); +} + +void mwindow::update() { + update_layout(); + update_hit_test(); + update_paint(); +} + +void mwindow::update_layout() { + layout_all(this); +} + +void mwindow::update_paint() { + if (!get_platform_window()->is_visible()) + return; + if (!has_invalidate_reason(invalidate_reason::paint)) + return; + + // 设置渲染通道属性 + sg_pass pass{}; + pass.action.colors[0].load_action = SG_LOADACTION_CLEAR; + pass.action.colors[0].store_action = SG_STOREACTION_STORE; + pass.action.colors[0].clear_value = { 0.f, 0.f, 0.f, 1.0f }; // 黑色背景 + + pass.action.depth.load_action = SG_LOADACTION_CLEAR; + pass.action.depth.store_action = SG_STOREACTION_DONTCARE; + pass.action.depth.clear_value = 1.0f; + pass.swapchain = state_->swapchain; + + // 开始渲染流程 + sg_begin_pass(pass); + sg_apply_viewport(0, 0, state_->swapchain.width, state_->swapchain.height, true); + + // 执行控件绘制 + context.begin_frame(); + paint_all(this); + context.end_frame(); + + // 结束渲染并提交 + sg_end_pass(); + sg_commit(); + + // 显示渲染结果并清除重绘标记 + state_->present(); + set_invalidate_reason(invalidate_reason::paint); +} + +void mwindow::update_hit_test() { + if (!is_layout_changed_) + return; + is_layout_changed_ = false; + + process_mouse_move(last_mouse_pos_); +} + +float mwindow::get_dpi_scale() const { + return dpi_helper::get_global_scale() * platform_window_->get_window_dpi_scale(); +} + +transform2d mwindow::get_local_to_screen_transform() const { + return transform2d(platform_window_->get_window_position().cast(), 0, { get_dpi_scale(), get_dpi_scale() }); +} + +transform2d mwindow::get_local_to_window_transform() const { + return transform2d({0, 0}, 0, { get_dpi_scale(), get_dpi_scale() }); +} + +geometry_t mwindow::get_window_geometry_in_screen() const { + const auto& local_to_screen = get_local_to_screen_transform(); + return { platform_window_->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 { platform_window_->get_window_frame_size().cast(), local_to_window, local_to_window }; +} + +Eigen::Vector2f mwindow::compute_desired_size(float in_layout_scale_multiplier) const { + return platform_window_->get_window_frame_size().cast(); +} + +void mwindow::arrange_children(const geometry_t& in_allotted_geometry) { + if (content_widget_) { + auto child_geo = in_allotted_geometry.make_child({0, 0}, platform_window_->get_window_frame_size().cast()); + content_widget_->set_geometry(child_geo); + } +} + +void mwindow::set_content(const std::shared_ptr& in_widget) { + content_widget_ = in_widget; + in_widget->init(); + in_widget->set_parent(shared_from_this()); + invalidate(invalidate_reason::all); +} + +void mwindow::on_paint(mirage_paint_context& in_context) { + if (content_widget_) + content_widget_->on_paint(in_context); +} + +void invalidate_recursive(mwidget* in_widget, invalidate_reason in_reason) { + in_widget->invalidate(in_reason); + for (const auto& child : in_widget->get_children()) { + invalidate_recursive(child.get(), in_reason); + } +} + +void mwindow::invalidate_all() { + // 设置失效标记 + invalidate_recursive(this, invalidate_reason::all); +} + +void mwindow::rebuild_swapchain() { + state_->rebuild_swapchain(platform_window_->get_window_frame_size()); +} + +void mwindow::paint_all(mwidget* in_widget) { + // 设置渲染上下文并绘制当前控件 + context.set_geometry(in_widget->get_geometry()); + in_widget->on_paint(context); + + // 递归绘制所有子控件 + for (const auto& child : in_widget->get_children()) { + paint_all(child.get()); + } +} + +void mwindow::layout_all(mwidget* in_widget) { + if (in_widget->has_invalidate_reason(invalidate_reason::layout)) { + in_widget->arrange_children(in_widget->get_geometry()); + in_widget->unset_invalid_reason(invalidate_reason::layout); + is_layout_changed_ = true; + } + + for (const auto& child : in_widget->get_children()) { + layout_all(child.get()); + } +} + +void mwindow::on_resize(const Eigen::Vector2i& in_size) { + state_->swapchain.width = in_size.x(); + state_->swapchain.height = in_size.y(); + context.update_projection_matrix(in_size); + + const transform2d identity; + geometry_ = { in_size.cast(), identity, identity }; + const geometry_t new_geometry(in_size.cast(), identity, identity); + if (content_widget_) { + content_widget_->set_geometry(new_geometry); + } + invalidate_all(); + rebuild_swapchain(); +} + +void mwindow::process_mouse_move(const Eigen::Vector2f& in_window_pos) { + // 执行命中测试,查找鼠标位置下的控件 + const auto& result = perform_hit_test(in_window_pos, [this](mwidget* in_widget, const Eigen::Vector2f& in_local_pos) { + return in_widget->on_mouse_move(in_local_pos); + }); + + // 更新最后鼠标位置 + last_mouse_pos_ = in_window_pos; + + // 如果悬停控件没有变化,直接返回 + if (last_hover_widget_.lock() == result.widget) + return; + + // 处理鼠标离开上一个悬停控件 + if (const auto ptr = last_hover_widget_.lock()) + ptr->on_mouse_leave(); + + // 更新悬停控件并触发鼠标进入事件 + last_hover_widget_ = result.widget; + if (const auto ptr = last_hover_widget_.lock()) + ptr->on_mouse_enter(); +} + +void mwindow::process_mouse_button_up(const Eigen::Vector2f& in_window_pos, mouse_button in_button) { + // 执行命中测试,找出鼠标位置下的控件 + const auto& hit_result = perform_hit_test(in_window_pos, [in_button](mwidget* widget, const Eigen::Vector2f& local_pos) { + return widget->on_mouse_button_up(local_pos, in_button); + }); + + const auto& hit_widget = hit_result.widget; + const auto& widget_local_pos = hit_result.widget_space_pos; + + // 如果没有点击到任何控件,重置状态并返回 + if (!hit_widget) { + last_hit_widget_.reset(); + return; + } + + const auto& ptr = last_hit_widget_.lock(); + if (!ptr) + return; + + // 定义点击判定阈值 + constexpr auto CLICK_TIME_THRESHOLD = std::chrono::milliseconds(200); // 单次点击最大持续时间 + 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(); + + // 判断是否是有效点击(时间短且位置接近) + + // 只处理有效点击 + if (press_duration >= CLICK_TIME_THRESHOLD || distance >= CLICK_DISTANCE_THRESHOLD) { + return; + } + + // 触发点击事件并更新最后点击的控件 + ptr->on_click(widget_local_pos, in_button); + last_hit_widget_ = hit_widget; +} + +void mwindow::process_mouse_button_down(const Eigen::Vector2f& in_window_pos, mouse_button in_button) { + // 执行命中测试,找出鼠标位置下的控件 + const auto& result = perform_hit_test(in_window_pos, [in_button](mwidget* in_widget, const Eigen::Vector2f& in_local_pos) { + return in_widget->on_mouse_button_down(in_local_pos, in_button); + }); + + // 记录点击状态信息 + last_hit_widget_ = result.widget; + last_mouse_press_time_ = get_current_time(); + last_mouse_press_pos_ = in_window_pos; +} + +void mwindow::process_mouse_button_dbl(const Eigen::Vector2f& in_window_pos, mouse_button in_button) { + if (auto last_hover_widget = last_hover_widget_.lock()) { + last_hover_widget->on_double_click(last_mouse_pos_, in_button); + } +} + +void mwindow::process_mouse_wheel(const Eigen::Vector2f& in_window_pos, wheel_event in_wheel_event) { + if (auto last_hover_widget = last_hover_widget_.lock()) { + last_hover_widget->on_mouse_wheel(last_mouse_pos_, in_wheel_event); + } +} + +void mwindow::process_mouse_leave() { + if (auto last_hover_widget = last_hover_widget_.lock()) { + last_hover_widget->on_mouse_leave(); + } + last_hover_widget_.reset(); +} + +hit_test_result mwindow::perform_hit_test(const Eigen::Vector2f& in_window_pos, + const std::function& in_hit_func) +const { + if (!content_widget_) + return {}; + return perform_hit_test(content_widget_.get(), in_window_pos, in_hit_func); +} + +hit_test_result mwindow::perform_hit_test(mwidget* in_widget, const Eigen::Vector2f& in_window_pos, + const std::function& in_hit_func) +const { + if (!in_widget) + return {}; + const auto& widget_geo = in_widget->get_geometry(); + + auto widget_local_pos = widget_geo.is_under_location(in_window_pos); + if (!widget_local_pos) + return {}; + + // 如果控件可以命中测试,执行命中测试回调 + if (in_widget->can_hit_test()) { + const auto& widget_pos = widget_local_pos.value(); + if (in_hit_func(in_widget, widget_pos)) + return { in_widget->shared_from_this(), widget_pos }; + } + + // 递归查找子控件 + for (const auto& child : in_widget->get_children()) { + if (const auto& result = perform_hit_test(child.get(), in_window_pos, in_hit_func)) + return result; + } + + return {}; +} diff --git a/src/mirage_widget/window/mwindow.h b/src/mirage_widget/window/mwindow.h new file mode 100644 index 0000000..f4e03e1 --- /dev/null +++ b/src/mirage_widget/window/mwindow.h @@ -0,0 +1,173 @@ +#pragma once +/** + * @file mwindow.h + * @brief 定义UI系统的窗口类 + * + * 本文件定义了mirage_window类,作为UI系统的窗口容器,管理窗口的创建、 + * 操作、渲染和内容布局。窗口本身也是一个UI组件,可以包含其他组件。 + */ + +#include "platform_window/platform_window.h" +#include "widget/mwidget.h" + +struct mirage_window_state; + +/** + * @class mwindow + * @brief UI系统的窗口类 + * + * 表示一个可以包含UI组件的窗口。提供窗口创建、操作、样式设置和内容布局等功能。 + * 作为一个组件容器,窗口本身也是一个UI组件。 + */ +class mwindow : public mwidget { +public: + mwindow(int32_t in_width, int32_t in_height, const wchar_t* in_title); + virtual ~mwindow() override; + + auto get_platform_window() const { return platform_window_.get(); } + virtual const mwindow* get_window() const override { return this; } + virtual mwindow* get_window() override { return this; } + + void update(); + void update_layout(); + void update_paint(); + void update_hit_test(); + + //-------------- 坐标变换 -------------- + + [[nodiscard]] virtual auto get_dpi_scale() const -> float override; + + /** + * @brief 获取从本地坐标到屏幕坐标的变换 + * @return 变换矩阵 + */ + [[nodiscard]] transform2d get_local_to_screen_transform() const; + + /** + * @brief 获取从本地坐标到窗口坐标的变换 + * @return 变换矩阵 + */ + [[nodiscard]] transform2d get_local_to_window_transform() const; + + /** + * @brief 获取窗口在屏幕中的几何信息 + * @return 窗口几何信息 + */ + [[nodiscard]] geometry_t get_window_geometry_in_screen() const; + + /** + * @brief 获取窗口在窗口中的几何信息 + * @return 窗口几何信息 + */ + [[nodiscard]] geometry_t get_window_geometry_in_window() const; + + //-------------- UI组件覆盖方法 -------------- + + /** + * @brief 计算窗口的期望大小 + * @param in_layout_scale_multiplier 布局缩放系数 + * @return 窗口的期望大小 + * + * 覆盖基类方法,窗口的期望大小就是其框架大小。 + */ + virtual Eigen::Vector2f compute_desired_size(float in_layout_scale_multiplier) const override; + + /** + * @brief 排列子组件 + * @param in_allotted_geometry 分配的几何区域 + * + * 覆盖基类方法,安排窗口内容的布局。 + */ + virtual void arrange_children(const geometry_t& in_allotted_geometry) override; + + /** + * @brief 设置窗口内容 + * @param in_widget 要设置为内容的组件 + * + * 设置窗口的主要内容组件。 + */ + void set_content(const std::shared_ptr& in_widget); + + /** + * @brief 获取窗口内容 + * @return 窗口内容组件 + */ + [[nodiscard]] auto get_content() const { return content_widget_; } + + /** + * @brief 绘制窗口及其内容 + * @param in_context 绘制上下文 + * + * 覆盖基类方法,处理窗口及其内容的绘制。 + */ + virtual void on_paint(mirage_paint_context& in_context) override; + + //-------------- 失效处理 -------------- + + /** + * @brief 设置所有组件的失效标记 + * + * 设置窗口及其所有子组件的失效标记。 + */ + void invalidate_all(); + + //-------------- 渲染和状态 -------------- + + /** + * @brief 获取窗口渲染状态 + * @return 渲染状态常量引用 + */ + [[nodiscard]] auto& get_state() const { return *state_; } + + /** + * @brief 重建交换链 + * + * 当窗口大小改变时重建渲染交换链。 + */ + void rebuild_swapchain(); + + static const auto& get_all() { return windows_; } + + multicast_delegate on_close_delegate; +protected: + void paint_all(mwidget* in_widget); + void layout_all(mwidget* in_widget); + + bool is_layout_changed_{ false }; + Eigen::Vector2f last_mouse_pos_ = Eigen::Vector2f::Zero(); ///< 上次鼠标位置 + Eigen::Vector2f last_mouse_press_pos_ = Eigen::Vector2f::Zero(); ///< 上次鼠标按下的位置 + std::weak_ptr last_hover_widget_; + std::weak_ptr last_hit_widget_; + time_type last_click_time_{}; ///< 上次点击完成的时间 + time_type last_mouse_press_time_{}; ///< 上次鼠标按下的时间 + + //-------------- 事件处理 -------------- + void on_resize(const Eigen::Vector2i& in_size); + void process_mouse_move(const Eigen::Vector2f& in_window_pos); + void process_mouse_button_up(const Eigen::Vector2f& in_window_pos, mouse_button in_button); + void process_mouse_button_down(const Eigen::Vector2f& in_window_pos, mouse_button in_button); + void process_mouse_button_dbl(const Eigen::Vector2f& in_window_pos, mouse_button in_button); + void process_mouse_wheel(const Eigen::Vector2f& in_window_pos, wheel_event in_wheel_event); + void process_mouse_leave(); + + hit_test_result perform_hit_test( + const Eigen::Vector2f& in_window_pos, + const std::function& in_hit_func) const; + + hit_test_result perform_hit_test( + mwidget* in_widget, + const Eigen::Vector2f& in_window_pos, + const std::function& in_hit_func) const; + +private: + inline static std::vector windows_; + + /** 窗口内容组件 */ + std::shared_ptr content_widget_; + + std::unique_ptr platform_window_; + mirage_paint_context context; ///< 渲染上下文 + + /** 窗口渲染状态 */ + std::unique_ptr state_; +}; diff --git a/third_party/mustache b/third_party/mustache deleted file mode 160000 index 3d4e571..0000000 --- a/third_party/mustache +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3d4e571876a0af6a1ca337ccfabc185ce41638cf