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