hittest和修复布局系统

This commit is contained in:
Nanako 2025-03-22 14:47:57 +08:00
parent c4000c11a5
commit 403c48dac8
34 changed files with 2000 additions and 554 deletions

69
.clang-format Normal file
View 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
...

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

@ -75,7 +75,7 @@ public:
*
* Direct3D与窗口表面的连接
*/
bool setup_surface(mirage_window* in_window) override;
bool setup_surface(mwindow* in_window) override;
private:
/** Direct3D 11设备 */

View File

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

View File

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

View File

@ -7,7 +7,7 @@
* 使Direct3D 11DXGI处理窗口的渲染表面
*/
#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

View File

@ -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;
}
//-------------- 子项操作 --------------
/**

View File

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

View File

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

View File

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

View File

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

View File

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

View 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);

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

View File

@ -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, ///< 强制从右到左
};
/**

View File

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

View File

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

View 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_;
}

View 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}; ///< 缓存是否有效
};

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

View 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}; ///< 是否为悬停测试
};

View File

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

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

View File

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

View File

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

View File

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