删除ECS模式
This commit is contained in:
parent
79363ce141
commit
e44a9bda57
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
}
|
||||
|
8
src/mirage_app/CMakeLists.txt
Normal file
8
src/mirage_app/CMakeLists.txt
Normal 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
90
src/mirage_app/mirage.cpp
Normal 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;
|
||||
}
|
@ -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;
|
||||
};
|
@ -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})
|
||||
|
@ -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;
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
/**
|
||||
* @file hit_test_result.cpp
|
||||
* @brief 命中测试结果类的实现
|
||||
*/
|
||||
|
||||
#include "hit_test_result.h"
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
/**
|
||||
* @file render_elements.h
|
||||
* @file mirage_type.h
|
||||
* @brief 定义渲染元素和UI相关的基础类型
|
||||
*
|
||||
* 本文件定义了UI系统中用于渲染和布局的基础类型,包括:
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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; }
|
||||
};
|
@ -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();
|
||||
}
|
||||
}
|
@ -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;
|
||||
};
|
@ -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);
|
||||
}
|
||||
}
|
@ -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_{}; ///< 持有窗口的弱引用
|
||||
};
|
@ -1,4 +0,0 @@
|
||||
//
|
||||
// Created by Administrator on 25-3-1.
|
||||
//
|
||||
#include "core/window/render_window.h"
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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();
|
||||
}
|
@ -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_;
|
||||
};
|
18
src/mirage_render/CMakeLists.txt
Normal file
18
src/mirage_render/CMakeLists.txt
Normal 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})
|
36
src/mirage_render/platform_window/platform_window.cpp
Normal file
36
src/mirage_render/platform_window/platform_window.cpp
Normal 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();
|
||||
}
|
242
src/mirage_render/platform_window/platform_window.h
Normal file
242
src/mirage_render/platform_window/platform_window.h
Normal 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_{};
|
||||
};
|
@ -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; }
|
@ -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;
|
||||
}
|
@ -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_{};
|
||||
|
@ -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_{};
|
||||
};
|
||||
|
||||
/**
|
@ -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;
|
@ -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(); }
|
@ -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{};
|
@ -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
|
@ -7,10 +7,11 @@
|
||||
* 使用Direct3D 11和DXGI处理窗口的渲染表面、交换链和呈现操作。
|
||||
*/
|
||||
|
||||
#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 设置可变刷新率支持
|
8
src/mirage_widget/CMakeLists.txt
Normal file
8
src/mirage_widget/CMakeLists.txt
Normal 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)
|
@ -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(),
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
@ -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(), {});
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
109
src/mirage_widget/widget/mwidget.cpp
Normal file
109
src/mirage_widget/widget/mwidget.cpp
Normal 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;
|
||||
}
|
@ -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_{};
|
||||
};
|
@ -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,
|
@ -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: \
|
@ -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; }
|
||||
};
|
@ -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();
|
315
src/mirage_widget/window/mwindow.cpp
Normal file
315
src/mirage_widget/window/mwindow.cpp
Normal 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 {};
|
||||
}
|
173
src/mirage_widget/window/mwindow.h
Normal file
173
src/mirage_widget/window/mwindow.h
Normal 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
third_party/mustache
vendored
1
third_party/mustache
vendored
@ -1 +0,0 @@
|
||||
Subproject commit 3d4e571876a0af6a1ca337ccfabc185ce41638cf
|
Loading…
x
Reference in New Issue
Block a user