删除ECS模式

This commit is contained in:
Nanako 2025-03-26 17:25:55 +08:00
parent 79363ce141
commit e44a9bda57
65 changed files with 1250 additions and 1841 deletions

3
.gitmodules vendored
View File

@ -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

View File

@ -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)

View File

@ -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()

View File

@ -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<mwindow>();
window->create_window(800, 600, L"Hello, World!");
window->show();
auto window = std::make_shared<mwindow>(800, 600, L"Hello, World!");
window->set_content(
std::make_shared<mbutton>()
);
mirage_app::get_render_context()->setup_surface(window.get());
widget_manager::get().init_window(window);
auto weak_vbox = widget_manager::get().new_widget<mv_box>(window.get());
auto vbox = weak_vbox.lock();
vbox->add_child<mbutton>().margin({ 5 });
vbox->add_child<mbutton>().margin({ 5 }).stretch(1);
auto weak_hbox = vbox->add_child<mh_box>().stretch(2).get();
auto hbox = std::static_pointer_cast<mh_box>(weak_hbox.lock());
hbox->add_child<mbutton>().margin({ 5 });
hbox->add_child<mbutton>().margin({ 5 }).stretch();
hbox->add_child<mbutton>().margin({ 5 });
vbox->add_child<mbutton>().margin({ 5 });
vbox->add_child<mbutton>().margin({ 5 });
window->set_content(vbox);
app.run();
mirage_app::get().run();
return 0;
}

View File

@ -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)

90
src/mirage_app/mirage.cpp Normal file
View File

@ -0,0 +1,90 @@
#include "mirage.h"
#include <iostream>
#include <thread>
#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<std::chrono::milliseconds>(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;
}

View File

@ -1,6 +1,7 @@
#pragma once
#include <mutex>
#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;
};

View File

@ -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})

View File

@ -1,59 +0,0 @@
#include "mirage.h"
#include <iostream>
#include <thread>
#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<std::chrono::milliseconds>(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;
}

View File

@ -1,6 +0,0 @@
/**
* @file hit_test_result.cpp
* @brief
*/
#include "hit_test_result.h"

View File

@ -1,6 +1,6 @@
#pragma once
/**
* @file render_elements.h
* @file mirage_type.h
* @brief UI相关的基础类型
*
* UI系统中用于渲染和布局的基础类型

View File

@ -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<widget_ptr>(key_, widget_ptr{ shared_from_this() });
in_entity_manager.assign<widget_layout>(key_);
in_entity_manager.assign<widget_visibility>(key_);
in_entity_manager.assign<widget_hierarchy>(key_);
in_entity_manager.assign<widget_invalidate>(key_);
}
void mwidget::set_geometry(const geometry_t& in_geometry) {
get_component_ref<widget_layout>().geometry = in_geometry;
}
void mwidget::cache_desired_size(float in_layout_scale_multiplier, bool in_force) {
if (auto* layout = get_component<widget_layout>()) {
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> mwidget::get_parent_widget() const {
if (const auto parent = get_parent())
return widget_manager::get().get_component<widget_ptr>(parent)->widget;
return {};
}
void mwidget::set_parent(const widget_key& in_parent) {
auto& manager = widget_manager::get();
auto* parent_hierarchy = manager.get_component<widget_hierarchy>(in_parent);
auto* hierarchy = manager.get_component<widget_hierarchy>(key_);
// 1. 如果当前有父节点,先从父节点中移除
if (hierarchy->parent) {
auto* old_parent_hierarchy = manager.get_component<widget_hierarchy>(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<widget_invalidate>()) {
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<widget_layout>()) {
layout->desired_size.reset();
}
}
}

View File

@ -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;
}

View File

@ -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<mwidget> widget;
operator std::shared_ptr<mwidget>() const { return widget; }
auto operator->() const { return widget.get(); }
operator bool() const { return widget != nullptr; }
};
struct widget_layout {
geometry_t geometry; // 几何信息
std::optional<Eigen::Vector2f> 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<widget_key> 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; }
};

View File

@ -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<mwindow>& 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<widget_layout_system>();
const auto hit_test_system = std::make_shared<widget_hit_test_system>();
const auto render_system = std::make_shared<widget_render_system>();
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();
}
}

View File

@ -1,85 +0,0 @@
#pragma once
#include <map>
#include <mustache/ecs/ecs.hpp>
#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<mwindow>& in_window);
auto& get_registry(mwindow* in_window) {
return registries[in_window];
}
template<typename T>
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<T>();
widget->set_key(key);
widget->init_component(entity_manager);
return std::weak_ptr{ widget };
}
template<typename T, typename... Args>
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<T>(std::forward<Args>(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<typename T>
auto& add_component(const widget_key& in_key) {
return registries[in_key].entities().assign<T>(in_key);
}
template<typename T, typename... Args>
auto& add_component(const widget_key& in_key, Args&&... args) {
return registries[in_key].entities().assign<T>(in_key, std::forward<Args>(args)...);
}
template<typename T>
auto* get_component(const widget_key& in_key) {
return registries[in_key].entities().getComponent<T>(in_key);
}
template<typename T>
auto& get_component_ref(const widget_key& in_key) {
return *registries[in_key].entities().getComponent<T>(in_key);
}
template<typename T>
auto get_system(mwindow* in_window) {
auto shared = registries[in_window].systems().findSystem<T>();
return std::static_pointer_cast<T>(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<mwindow*, mustache::World> registries;
};

View File

@ -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<mwindow>& 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_<layout_changed>(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<mwidget>& 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<widget_ptr>(last_hover_widget_))
ptr->widget->on_mouse_leave();
// 更新悬停控件并触发鼠标进入事件
last_hover_widget_ = result;
if (auto ptr = widget_manager::get().get_component<widget_ptr>(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<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_ = widget_key::invalid();
return;
}
const auto& ptr = widget_manager::get().get_component_ref<widget_ptr>(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<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;
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<widget_ptr>(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<widget_ptr>(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<widget_ptr>(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<hit_test_handle(const std::shared_ptr<mwidget>&, 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<hit_test_handle(const std::shared_ptr<mwidget>&, const Eigen::Vector2f&)>& in_hit_func) {
// 获取控件布局
const auto& layout = widget_manager::get().get_component_ref<widget_layout>(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<widget_ptr>(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<widget_hierarchy>(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<mwindow>& 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<widget_ptr>(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<widget_hierarchy>(in_key).children;
for (const auto& child : children) {
paint(child);
}
}

View File

@ -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<widget_layout_system> {
public:
/**
* @brief
* @param in_window
*/
void set_window(std::shared_ptr<mwindow> 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<mwindow> window_{}; ///< 持有窗口的弱引用
};
/**
* @class widget_hit_test_system
* @brief
*
*
*/
class widget_hit_test_system : public mustache::System<widget_hit_test_system>,
public mustache::Receiver<layout_changed> {
public:
virtual ~widget_hit_test_system() override;
/**
* @brief
* @param in_window
*/
void set_window(const std::shared_ptr<mwindow>& 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<hit_test_handle(const std::shared_ptr<mwidget>&, 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<hit_test_handle(const std::shared_ptr<mwidget>&, const Eigen::Vector2f&)>& in_hit_func);
private:
// 窗口引用
std::weak_ptr<mwindow> 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<widget_render_system> {
public:
virtual ~widget_render_system() override;
/**
* @brief
* @param in_window
*/
void set_window(const std::shared_ptr<mwindow>& 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<mwindow> window_{}; ///< 持有窗口的弱引用
};

View File

@ -1,4 +0,0 @@
//
// Created by Administrator on 25-3-1.
//
#include "core/window/render_window.h"

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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<float>(), 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<float>(), 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<float>());
content_widget_->set_geometry(child_geo);
}
}
void mwindow::set_content(const std::shared_ptr<mwidget>& 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<float>(), 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<mwidget>& 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<widget_ptr>(child);
invalidate_recursive(ptr, in_reason);
}
// 如果是布局失效, 重置期望大小
if (has_any_flag(in_reason, invalidate_reason::layout)) {
if (auto* layout = in_widget->get_component<widget_layout>()) {
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();
}

View File

@ -1,431 +0,0 @@
#pragma once
/**
* @file mwindow.h
* @brief UI系统的窗口类
*
* mirage_window类UI系统的窗口容器
* UI组件
*/
#include "render/windows/windows_render_context.h"
#include <Eigen/Eigen>
#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<mwindow*>& 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<mirage_window_state>&& 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<float>(), 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<float>(); }
/**
* @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<mwidget>& 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<const Eigen::Vector2i&> on_resize_delegate;
multicast_delegate<mwindow*> on_close_delegate;
multicast_delegate<const Eigen::Vector2f&> on_mouse_move_delegate;
multicast_delegate<const Eigen::Vector2f&, mouse_button> on_mouse_button_down_delegate;
multicast_delegate<const Eigen::Vector2f&, mouse_button> on_mouse_button_up_delegate;
multicast_delegate<const Eigen::Vector2f&, mouse_button> on_mouse_button_dbl_delegate;
multicast_delegate<const Eigen::Vector2f&, wheel_event> on_mouse_wheel_delegate;
multicast_delegate<> on_mouse_leave_delegate;
private:
/** 原生窗口句柄 */
void* window_handle_{};
/** 窗口渲染状态 */
std::unique_ptr<mirage_window_state> state_;
/** 窗口内容组件 */
std::shared_ptr<mwidget> content_widget_;
};

View File

@ -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})

View File

@ -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();
}

View File

@ -0,0 +1,242 @@
#pragma once
#include "misc/delegates.h"
#include <Eigen/Eigen>
#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<platform_window*>& 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<const Eigen::Vector2i&> on_move_delegate;
multicast_delegate<const Eigen::Vector2i&> on_resize_delegate;
multicast_delegate<platform_window*> on_close_delegate;
multicast_delegate<const Eigen::Vector2f&> on_mouse_move_delegate;
multicast_delegate<const Eigen::Vector2f&, mouse_button> on_mouse_button_down_delegate;
multicast_delegate<const Eigen::Vector2f&, mouse_button> on_mouse_button_up_delegate;
multicast_delegate<const Eigen::Vector2f&, mouse_button> on_mouse_button_dbl_delegate;
multicast_delegate<const Eigen::Vector2f&, wheel_event> on_mouse_wheel_delegate;
multicast_delegate<> on_mouse_leave_delegate;
private:
/** 原生窗口句柄 */
void* window_handle_{};
};

View File

@ -1,16 +1,15 @@
//
// Created by Administrator on 25-3-1.
//
#include <iostream>
#include "window/mwindow.h"
#include <Windows.h>
#include "platform_window/platform_window.h"
#include <windows.h>
#define WINDOW_HANDLE static_cast<HWND>(window_handle_)
std::vector<mwindow*> windows;
bool mouse_tracking_ = FALSE;
mwindow* get_window_from_hwnd(const HWND hwnd) {
bool mouse_tracking_ = FALSE;
std::vector<platform_window*> 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*>& mwindow::get_windows() { return windows; }
const std::vector<platform_window*>& platform_window::get_windows() { return windows; }

View File

@ -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;
}

View File

@ -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_{};

View File

@ -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<mirage_window_state> 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_{};
};
/**

View File

@ -1,12 +1,11 @@
#include "render/render_image.h"
#include <dxgi1_6.h>
#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;

View File

@ -1,14 +1,9 @@
//
// Created by Administrator on 25-3-1.
//
#include "windows_render_context.h"
#include <iostream>
#include <ranges>
#include "misc/angle_literals.h"
#include "window/mwindow.h"
#include <windows.h>
#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<mirage_window_state> windows_mirage_render_context::create_window_state(const Eigen::Vector2i& in_window_frame_size, void* in_window_handle) {
auto state = std::make_unique<windows_render_state>();
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(); }

View File

@ -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<mirage_window_state> create_window_state(const Eigen::Vector2i& in_window_frame_size, void* in_window_handle) override;
private:
/** Direct3D 11设备 */
ID3D11Device* device{};

View File

@ -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

View File

@ -7,10 +7,11 @@
* 使Direct3D 11DXGI处理窗口的渲染表面
*/
#include "window/mwindow.h"
#include <d3d11.h>
#include <dxgi1_6.h>
#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

View File

@ -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)

View File

@ -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(),

View File

@ -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<mh_box> hbox = std::dynamic_pointer_cast<mh_box>(parent.lock())) {
hbox->add_child<mbutton>()
.stretch()
.margin({ 5 });
}
if (std::shared_ptr<mv_box> vbox = std::dynamic_pointer_cast<mv_box>(parent.lock())) {
vbox->add_child<mbutton>()
.stretch()
.margin({ 5 });
}
// const auto& parent = get_parent();
// if (std::shared_ptr<mh_box> hbox = std::dynamic_pointer_cast<mh_box>(parent)) {
// hbox->add_child<mbutton>()
// .stretch()
// .margin({ 5 });
// }
// if (std::shared_ptr<mv_box> vbox = std::dynamic_pointer_cast<mv_box>(parent)) {
// vbox->add_child<mbutton>()
// .stretch()
// .margin({ 5 });
// }
invalidate(invalidate_reason::paint);
}

View File

@ -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;

View File

@ -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<T&>(*this);
}
widget_key slot_owner;
std::weak_ptr<mwidget> slot_owner;
// 插槽功能宏 - 定义内容管理和对齐属性
SLOT_CONTENT()
@ -48,7 +49,7 @@ struct mcompound_widget_slot {
template<typename SlotType>
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>(); }
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<mwidget>& in_widget) {}
SlotType slot_;
};
template<typename SlotType>
void mcompound_widget<SlotType>::init_component(mustache::EntityManager& in_entity_manager) {
mwidget::init_component(in_entity_manager);
auto& slot = add_component<SlotType>();
slot.slot_owner = get_key();
void mcompound_widget<SlotType>::init() {
mwidget::init();
slot_.slot_owner = shared_from_this();
}
/**
@ -123,7 +126,7 @@ void mcompound_widget<SlotType>::init_component(mustache::EntityManager& in_enti
*/
template<typename SlotType>
Eigen::Vector2f mcompound_widget<SlotType>::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<SlotType>::compute_desired_size(float in_layout
*/
template<typename SlotType>
void mcompound_widget<SlotType>::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(), {});
}
}

View File

@ -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<T&>(*this);
}
widget_key slot_owner;
std::weak_ptr<mwidget> slot_owner;
// 插槽功能宏 - 定义内容管理
SLOT_CONTENT()
@ -74,61 +73,31 @@ public:
*
*/
template<typename T>
auto add_child() -> SlotType& {
auto& manager = widget_manager::get();
auto widget_entity = manager.new_widget<T>(get_window());
auto w = widget_entity.lock();
auto& slot = w->template add_component<SlotType>();
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<typename T, typename... Args>
auto add_child(Args&&... in_args) -> SlotType& {
auto& manager = widget_manager::get();
auto widget_entity = manager.new_widget<T>(get_window(), std::forward<Args>(in_args)...);
auto w = widget_entity.lock();
auto& slot = w->template add_component<SlotType>();
slot.slot_owner = key_;
slot.set(w);
auto& slot = slots_.emplace_back(std::forward<Args>(in_args)...);
slot.slot_owner = shared_from_this();
invalidate(invalidate_reason::all);
return slot;
}
auto get_slots_ref() {
std::vector<SlotType&> children;
if (auto hierarchy = get_component<widget_hierarchy>()) {
auto& manager = widget_manager::get();
for (const auto& child : hierarchy->children) {
if (auto slot = manager.get_component<SlotType>(child)) {
children.push_back(*slot);
}
}
}
return children;
}
auto get_slots() const {
std::vector<SlotType*> children;
if (auto hierarchy = get_component<widget_hierarchy>()) {
auto& manager = widget_manager::get();
for (const auto& child : hierarchy->children) {
if (auto slot = manager.get_component<SlotType>(child)) {
children.push_back(slot);
}
}
}
return children;
const auto& get_slots() const {
return slots_;
}
private:
std::vector<SlotType> slots_;
};
template<typename SlotType>
void mpanel_widget<SlotType>::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<widget_hierarchy>();
for (const auto& child: hierarchy.children) {
const auto& ptr = manager.get_component_ref<widget_ptr>(child);
for (const auto& slot: slots_) {
const auto& ptr = slot.get();
ptr->cache_desired_size(in_layout_scale_multiplier, in_force);
}
}

View File

@ -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<mwidget>& 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<mwidget>& 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<mwidget>& 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;
}

View File

@ -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<mwidget> {
public:
virtual ~mwidget();
virtual const mwindow* get_window() const;
virtual mwindow* get_window();
template<typename T>
auto& add_component() {
return widget_manager::get().add_component<T>(key_);
}
template<typename T, typename... Args>
auto& add_component(Args&&... args) {
return widget_manager::get().add_component<T>(key_, std::forward<Args>(args)...);
}
template<typename T>
auto* get_component() const {
return widget_manager::get().get_component<T>(key_);
}
template<typename T>
auto& get_component_ref() const {
return *widget_manager::get().get_component<T>(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<widget_layout>().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<widget_layout>().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<widget_hierarchy>().parent; }
auto get_children() const { return get_component_ref<widget_hierarchy>().children; }
auto get_parent_widget() const -> std::weak_ptr<mwidget>;
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<mwidget>& in_parent);
void remove_from_parent();
void add_child(const std::shared_ptr<mwidget>& in_child);
void remove_child(const std::shared_ptr<mwidget>& in_child);
virtual void invalidate(invalidate_reason in_reason);
auto has_invalidate_reason(invalidate_reason in_reason) const { return get_component_ref<widget_invalidate>().has(in_reason); }
void clear_invalidate_reason(invalidate_reason in_reason) { get_component_ref<widget_invalidate>().unset(in_reason); }
auto get_invalidation_reason() const { return get_component_ref<widget_invalidate>().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<widget_visibility>().is_enabled(); }
void set_enabled(bool in_enabled) { get_component_ref<widget_visibility>().set_enabled(in_enabled); }
auto get_visibility() const { return get_component_ref<widget_visibility>().get_visibility(); }
void set_visibility(visibility_t in_visibility) { get_component_ref<widget_visibility>().set_visibility(in_visibility); }
bool can_hit_test() const { return get_component_ref<widget_visibility>().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<Eigen::Vector2f> desired_size_{};
std::weak_ptr<mwidget> parent_;
std::vector<std::shared_ptr<mwidget>> children_;
geometry_t geometry_{};
};

View File

@ -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<orientation_t::horizontal>(in_allotted_geometry,

View File

@ -16,7 +16,8 @@
public: \
auto& set(const std::shared_ptr<mwidget>& 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<mwidget> widget_{};
std::shared_ptr<mwidget> widget_{};
#define SLOT_OPTIONAL_ATTRIBUTE(type, name) \
public: \

View File

@ -5,11 +5,9 @@
*/
#include <memory>
#include <vector>
#include <Eigen/Core>
#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<mwidget> widget;
Eigen::Vector2f widget_space_pos;
operator bool() const {
return widget;
return static_cast<bool>(widget);
}
operator widget_key() const { return widget; }
operator std::shared_ptr<mwidget>() const { return widget; }
};

View File

@ -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<SlotType*>& child_slots,
const std::vector<SlotType>& 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<mwidget> 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<orientation_t LayoutOrientation, typename SlotType>
Eigen::Vector2f compute_box_desired_size(const std::vector<SlotType*>& child_slots) {
Eigen::Vector2f compute_box_desired_size(const std::vector<SlotType>& 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();

View File

@ -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<platform_window>(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<float>(), 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<float>(), 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<float>(), 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<float>(), 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<float>();
}
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<float>());
content_widget_->set_geometry(child_geo);
}
}
void mwindow::set_content(const std::shared_ptr<mwidget>& 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<float>(), identity, identity };
const geometry_t new_geometry(in_size.cast<float>(), 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<hit_test_handle(mwidget*, const Eigen::Vector2f&)>& 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<hit_test_handle(mwidget*, const Eigen::Vector2f&)>& 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 {};
}

View File

@ -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<mwidget>& 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<mwindow*> 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<mwidget> last_hover_widget_;
std::weak_ptr<mwidget> 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<hit_test_handle(mwidget*, const Eigen::Vector2f&)>& in_hit_func) const;
hit_test_result perform_hit_test(
mwidget* in_widget,
const Eigen::Vector2f& in_window_pos,
const std::function<hit_test_handle(mwidget*, const Eigen::Vector2f&)>& in_hit_func) const;
private:
inline static std::vector<mwindow*> windows_;
/** 窗口内容组件 */
std::shared_ptr<mwidget> content_widget_;
std::unique_ptr<platform_window> platform_window_;
mirage_paint_context context; ///< 渲染上下文
/** 窗口渲染状态 */
std::unique_ptr<mirage_window_state> state_;
};

@ -1 +0,0 @@
Subproject commit 3d4e571876a0af6a1ca337ccfabc185ce41638cf