优化控件布局语法

This commit is contained in:
daiqingshuang 2025-04-21 14:30:01 +08:00
parent 900db6dd70
commit 542949e9f5
22 changed files with 245 additions and 171 deletions

View File

@ -47,11 +47,11 @@ int main(int argc, char* argv[]) {
font_manager::instance().load_default_font();
auto name = mirage_style::get().name();
auto version = mirage_style::get().version();
auto author = mirage_style::get().author();
auto name = mirage_style::get().name();
auto version = mirage_style::get().version();
auto author = mirage_style::get().author();
auto description = mirage_style::get().description();
auto license = mirage_style::get().license();
auto license = mirage_style::get().license();
std::stringstream ss;
ss << "name: " << name << "\n";
@ -59,39 +59,46 @@ int main(int argc, char* argv[]) {
ss << "author: " << author << "\n";
ss << "description: " << description << "\n";
ss << "license: " << license << "\n";
// const char*转换为std::u32string
const auto& config_info_str = utf8::utf8to32(ss.str());
const auto& text_block = std::make_shared<mtext_block>();
text_block->set_text(config_info_str);
// text_block->set_text(U"Hello, World! 你好,世界!\n换行测试1111测试测试测试测试,测试测试😀🐵🙏 😃🐵🙏");
const auto& window = mwindow::create({ 800, 600 }, L"Hello, World!");
window->set_content(
mnew(mv_box)
[
mslot(mv_box)
.horizontal_alignment(horizontal_alignment_t::left)
+mnew(mbutton)
mnew(mv_box)
[
mslot(mbutton)
.margin({ 10 })
.visibility(visibility_t::visible)
+text_block
],
mslot(mv_box)
.horizontal_alignment(horizontal_alignment_t::left)
+mnew(mbutton)
[
mslot(mbutton)
.margin({ 10 })
.visibility(visibility_t::visible)
[
mnew(mtext_block,
.text(config_info_str)
.font_size(24)
)
]
],
mslot(mv_box)
.horizontal_alignment(horizontal_alignment_t::right)
+mnew(mbutton)
[
mslot(mbutton)
.margin({ 10 })
.visibility(visibility_t::visible)
+mnew(mtext_block)
->set_text(U"Hello, World!")
->set_font_size(24)
mslot(mv_box)
.horizontal_alignment(horizontal_alignment_t::right)
+mnew(mbutton)
[
mslot(mbutton)
.margin({ 10 })
.visibility(visibility_t::visible)
[
mnew(mtext_block,
.text(U"Hello, World!")
.font_size(24)
)
]
]
]
]
);
);
mirage_app::get().run();
return 0;

View File

@ -70,10 +70,11 @@ enum class visibility_t : uint32_t {
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, ///< 任何可命中测试状态
all = 0xFFFFFFFF, ///< 所有可见性标志
any_visible = visible | self_hit_test_invisible | hit_test_invisible, ///< 任何可见状态
any_invisible = collapsed | hidden, ///< 任何不可见状态
any_hit_testable = visible, ///< 任何可命中测试状态
any_layout = any_visible | hidden, ///< 任何布局状态
};
// 为visibility枚举启用位标志功能
DEFINE_ENUM_FLAGS(visibility_t)

View File

@ -41,7 +41,8 @@ struct mborder_slot : mcompound_widget_slot<mborder_slot> {
* mborder是一个容器组件
* margin属性可以控制子组件与边框之间的距离
*/
class mborder : public mcompound_widget<mborder_slot> {
template<typename ConstructArgs = null_args>
class mborder : public mcompound_widget<mborder_slot, ConstructArgs> {
public:
/**
* @brief
@ -51,7 +52,7 @@ public:
*
*/
virtual void arrange_children(const geometry_t& in_allotted_geometry) override {
const auto& slot = get_child_slot();
const auto& slot = this->get_child_slot();
if (const auto child_widget = slot.get())
arrange_single_child(in_allotted_geometry,
child_widget,
@ -61,10 +62,10 @@ public:
}
Eigen::Vector2f compute_desired_size(float in_layout_scale_multiplier) const override {
if (const auto& child = get_child_slot().get()) {
if (const auto& child = this->get_child_slot().get()) {
// 获取子部件的期望大小
const auto& child_size = child->compute_desired_size(in_layout_scale_multiplier);
const auto& margin = slot_.margin();
const auto& margin = this->slot_.margin();
// 添加边距的贡献
// return child_size + Eigen::Vector2f(margin.horizontal(), margin.vertical());

View File

@ -16,6 +16,10 @@ void mbutton::init() {
set_visibility(visibility_t::visible);
}
void mbutton::setup_widget(const button_args& in_args) {
mborder<button_args>::setup_widget(in_args);
}
void mbutton::on_paint(mirage_paint_context& in_context) {
in_context.drawer().make_rounded_rect(
{ 0, 0 },

View File

@ -1,11 +1,15 @@
#pragma once
#include "mborder.h"
class mbutton : public mborder {
struct button_args {
WARG(linear_color, color)
};
class mbutton : public mborder<button_args> {
public:
mbutton();
virtual void init() override;
void setup_widget(const button_args& in_args) override;
virtual void on_paint(mirage_paint_context& in_context) override;
virtual void on_click(const Eigen::Vector2f& in_position, mouse_button in_button) override;

View File

@ -2,6 +2,15 @@
#include "texture/texture.h"
void mimage::setup_widget(const image_args& in_args) {
set_color(in_args.color());
set_image(in_args.image());
if (in_args.sampler())
set_sampler(in_args.sampler());
else
set_sampler(texture_sampler_builder::get_sampler(sampler_type::default_));
}
void mimage::on_paint(mirage_paint_context& in_context) {
if (!image_) {
return;

View File

@ -4,8 +4,15 @@
class texture;
class mimage : public mleaf_widget {
struct image_args {
WARG(std::shared_ptr<texture>, image)
WARG(linear_color, color)
WARG(std::shared_ptr<texture_sampler>, sampler)
};
class mimage : public mleaf_widget<image_args> {
public:
void setup_widget(const image_args& in_args) override;
void on_paint(mirage_paint_context& in_context) override;
Eigen::Vector2f compute_desired_size(float in_layout_scale_multiplier) const override;
@ -19,6 +26,6 @@ public:
void set_sampler(sampler_type in_type);
private:
std::shared_ptr<texture> image_;
std::shared_ptr<texture_sampler> sampler_ = texture_sampler_builder::get_sampler(sampler_type::default_);
std::shared_ptr<texture_sampler> sampler_;
linear_color color_ = { 1, 1, 1, 1 };
};

View File

@ -6,6 +6,15 @@
#include "font/font_system.h"
void mtext_block::setup_widget(const text_block_args& in_args) {
text_ = in_args.text();
font_ = in_args.font();
font_size_ = in_args.font_size().value_or(15.f);
line_spacing_ = in_args.line_spacing().value_or(1.2f);
max_width_ = in_args.max_width().value_or(0.f);
update_layout();
}
void mtext_block::on_paint(mirage_paint_context& in_context) {
// 绘制文本
in_context.drawer().make_text(

View File

@ -4,44 +4,42 @@
class font_face_interface;
class mtext_block : public mleaf_widget {
public:
struct text_block_args {
WARG(std::u32string, text)
WARG(std::shared_ptr<font_face_interface>, font)
WARG(std::optional<float>, font_size)
WARG(std::optional<float>, line_spacing)
WARG(std::optional<float>, max_width)
};
class mtext_block : public mleaf_widget<text_block_args> {
public:
void setup_widget(const text_block_args& in_args) override;
void on_paint(mirage_paint_context& in_context) override;
auto set_text(const std::u32string& in_text) {
void set_text(const std::u32string& in_text) {
text_ = in_text;
update_layout();
using this_type = std::remove_reference_t<decltype(*this)>;
return std::static_pointer_cast<this_type>(shared_from_this());
}
auto set_font(const std::shared_ptr<font_face_interface>& in_font) {
void set_font(const std::shared_ptr<font_face_interface>& in_font) {
font_ = in_font;
update_layout();
using this_type = std::remove_reference_t<decltype(*this)>;
return std::static_pointer_cast<this_type>(shared_from_this());
}
auto set_font_size(float in_font_size) {
void set_font_size(float in_font_size) {
font_size_ = in_font_size;
update_layout();
using this_type = std::remove_reference_t<decltype(*this)>;
return std::static_pointer_cast<this_type>(shared_from_this());
}
auto set_line_spacing(float in_line_spacing) {
void set_line_spacing(float in_line_spacing) {
line_spacing_ = in_line_spacing;
update_layout();
using this_type = std::remove_reference_t<decltype(*this)>;
return std::static_pointer_cast<this_type>(shared_from_this());
}
auto set_max_width(float in_max_width) {
void set_max_width(float in_max_width) {
max_width_ = in_max_width;
update_layout();
using this_type = std::remove_reference_t<decltype(*this)>;
return std::static_pointer_cast<this_type>(shared_from_this());
}
const auto& get_text() const { return text_; }
@ -58,7 +56,7 @@ private:
std::u32string text_;
text_layout_t layout_{};
float font_size_ = 15.0f;
float font_size_ = .0f;
float line_spacing_ = 1.2f;
float max_width_ = 0.0f;
std::shared_ptr<font_face_interface> font_;

View File

@ -29,9 +29,9 @@ struct mcompound_widget_slot {
auto& me() {
return static_cast<T&>(*this);
}
std::weak_ptr<mwidget> slot_owner;
std::weak_ptr<mwidget_interface> slot_owner;
operator std::shared_ptr<mwidget>() const { return slot_owner.lock(); }
operator std::shared_ptr<mwidget_interface>() const { return slot_owner.lock(); }
// 插槽功能宏 - 定义内容管理和对齐属性
SLOT_CONTENT()
@ -49,8 +49,8 @@ struct mcompound_widget_slot {
*
*
*/
template<typename SlotType>
class mcompound_widget : public mwidget {
template<typename SlotType, typename ConstructArgs>
class mcompound_widget : public mwidget<ConstructArgs> {
public:
using slot_type = SlotType;
//-------------- 内容设置 --------------
@ -58,8 +58,8 @@ public:
auto& push_slot(const SlotType& in_slot) {
const auto& child_widget = in_slot.get();
on_set_content(child_widget);
invalidate(invalidate_reason::layout);
auto shared_this = shared_from_this();
this->invalidate(invalidate_reason::layout);
auto shared_this = this->shared_from_this();
slot_ = in_slot;
@ -73,7 +73,7 @@ public:
}
auto operator[](const SlotType& in_slot) {
push_slot(in_slot);
return shared_from_this();
return this->shared_from_this();
}
//-------------- 尺寸计算 --------------
@ -116,7 +116,7 @@ protected:
*
*
*/
virtual void on_set_content(const std::shared_ptr<mwidget>& in_widget) {}
virtual void on_set_content(const std::shared_ptr<mwidget_interface>& in_widget) {}
SlotType slot_;
};
@ -126,8 +126,8 @@ protected:
*
* collapsed状态
*/
template<typename SlotType>
Eigen::Vector2f mcompound_widget<SlotType>::compute_desired_size(float in_layout_scale_multiplier) const {
template<typename SlotType, typename ConstructArgs>
Eigen::Vector2f mcompound_widget<SlotType, ConstructArgs>::compute_desired_size(float in_layout_scale_multiplier) const {
if (const auto& child_widget = get_child_slot().get()) {
if (has_any_flag(child_widget->get_visibility(), visibility_t::any_visible)) {
return child_widget->compute_desired_size(in_layout_scale_multiplier);
@ -141,8 +141,8 @@ Eigen::Vector2f mcompound_widget<SlotType>::compute_desired_size(float in_layout
*
* 使layout_utils中的arrange_single_child函数根据对齐属性排列单个子组件
*/
template<typename SlotType>
void mcompound_widget<SlotType>::arrange_children(const geometry_t& in_allotted_geometry) {
template<typename SlotType, typename ConstructArgs>
void mcompound_widget<SlotType, ConstructArgs>::arrange_children(const geometry_t& in_allotted_geometry) {
const auto& slot = get_child_slot();
if (const auto& child_widget = slot.get()) {
arrange_single_child(in_allotted_geometry, child_widget, slot.h_alignment(), slot.v_alignment(), {});

View File

@ -5,7 +5,8 @@
* @class mleaf_widget
* @brief
*/
class mleaf_widget : public mwidget {
template<typename ConstructArgs>
class mleaf_widget : public mwidget<ConstructArgs> {
public:
void arrange_children(const geometry_t& in_allotted_geometry) override { (void)in_allotted_geometry; }
};

View File

@ -28,7 +28,7 @@ struct mpanel_widget_slot {
auto& me() {
return static_cast<T&>(*this);
}
std::weak_ptr<mwidget> slot_owner;
std::weak_ptr<mwidget_interface> slot_owner;
operator auto() const { return slot_owner.lock(); }
@ -47,8 +47,8 @@ struct mpanel_widget_slot {
*
*
*/
template<typename SlotType>
class mpanel_widget : public mwidget {
template<typename SlotType, typename ConstructArgs = null_args>
class mpanel_widget : public mwidget<ConstructArgs> {
public:
using slot_type = SlotType;
@ -74,7 +74,7 @@ public:
*/
auto& add_slot() {
auto& slot = slots_.emplace_back();
auto shared_this = shared_from_this();
auto shared_this = this->shared_from_this();
slot.slot_owner = shared_this;
@ -84,14 +84,14 @@ public:
if (slot.has_visibility()) {
child_widget->set_visibility(slot.visibility());
}
invalidate(invalidate_reason::all);
this->invalidate(invalidate_reason::all);
return slot;
}
template<typename... Args>
auto& add_slot(Args&&... in_args) {
auto& slot = slots_.emplace_back(std::forward<Args>(in_args)...);
auto shared_this = shared_from_this();
auto shared_this = this->shared_from_this();
slot.slot_owner = shared_this;
@ -101,12 +101,12 @@ public:
if (slot.has_visibility()) {
child_widget->set_visibility(slot.visibility());
}
invalidate(invalidate_reason::all);
this->invalidate(invalidate_reason::all);
return slot;
}
auto& push_slot(const SlotType& in_slot) {
auto& slot = slots_.emplace_back(in_slot);
auto shared_this = shared_from_this();
auto shared_this = this->shared_from_this();
slot.slot_owner = shared_this;
@ -116,7 +116,7 @@ public:
if (slot.has_visibility()) {
child_widget->set_visibility(slot.visibility());
}
invalidate(invalidate_reason::all);
this->invalidate(invalidate_reason::all);
return slot;
}
@ -126,14 +126,14 @@ public:
auto operator[](auto... in_slot) {
for (const auto& slot: { in_slot... }) { add_slot(slot); }
return shared_from_this();
return this->shared_from_this();
}
private:
std::vector<SlotType> slots_;
};
template<typename SlotType>
void mpanel_widget<SlotType>::cache_all_children_desired_size(float in_layout_scale_multiplier, bool in_force) const {
template<typename SlotType, typename ConstructArgs>
void mpanel_widget<SlotType, ConstructArgs>::cache_all_children_desired_size(float in_layout_scale_multiplier, bool in_force) const {
for (const auto& slot: slots_) {
const auto& ptr = slot.get();
ptr->cache_desired_size(in_layout_scale_multiplier, in_force);

View File

@ -3,41 +3,41 @@
#include "window/mwindow.h"
#include "geometry/dpi_helper.h"
mwidget::~mwidget() {
mwidget_interface::~mwidget_interface() {
remove_from_parent();
}
const mwindow* mwidget::get_window() const {
const mwindow* mwidget_interface::get_window() const {
if (const auto parent = get_parent()) {
return parent->get_window();
}
return nullptr;
}
mwindow* mwidget::get_window() {
mwindow* mwidget_interface::get_window() {
if (const auto parent = get_parent()) {
return parent->get_window();
}
return nullptr;
}
void mwidget::set_geometry(const geometry_t& in_geometry) {
void mwidget_interface::set_geometry(const geometry_t& in_geometry) {
geometry_ = in_geometry;
}
void mwidget::cache_desired_size(float in_layout_scale_multiplier, bool in_force) {
void mwidget_interface::cache_desired_size(float in_layout_scale_multiplier, bool in_force) {
if (!in_force && desired_size_.has_value())
return;
desired_size_ = compute_desired_size(in_layout_scale_multiplier);
}
auto mwidget::get_dpi_scale() const -> float {
auto mwidget_interface::get_dpi_scale() const -> float {
if (const auto window = get_window())
return window->get_dpi_scale();
return 1.f;
}
void mwidget::set_parent(const std::shared_ptr<mwidget>& in_parent) {
void mwidget_interface::set_parent(const std::shared_ptr<mwidget_interface>& in_parent) {
// 先从原父节点中移除
remove_from_parent();
@ -46,7 +46,7 @@ void mwidget::set_parent(const std::shared_ptr<mwidget>& in_parent) {
in_parent->add_child(shared_from_this());
}
void mwidget::remove_from_parent() {
void mwidget_interface::remove_from_parent() {
const auto parent = get_parent();
if (!parent)
return;
@ -54,7 +54,7 @@ void mwidget::remove_from_parent() {
parent->remove_child(shared_from_this());
}
void mwidget::add_child(const std::shared_ptr<mwidget>& in_child) {
void mwidget_interface::add_child(const std::shared_ptr<mwidget_interface>& in_child) {
#if DEBUG
// 避免重复添加
const auto& it = std::ranges::find_if(children_, [&](const auto& child) { return child == in_child; });
@ -64,7 +64,7 @@ void mwidget::add_child(const std::shared_ptr<mwidget>& in_child) {
invalidate(invalidate_reason::layout);
}
void mwidget::remove_child(const std::shared_ptr<mwidget>& in_child) {
void mwidget_interface::remove_child(const std::shared_ptr<mwidget_interface>& in_child) {
const auto& it = std::ranges::find_if(children_, [&](const auto& child) {
return child == in_child;
});
@ -74,7 +74,7 @@ void mwidget::remove_child(const std::shared_ptr<mwidget>& in_child) {
invalidate(invalidate_reason::layout);
}
void mwidget::invalidate(invalidate_reason in_reason) {
void mwidget_interface::invalidate(invalidate_reason in_reason) {
// 设置失效标记
set_flag(invalidate_, in_reason);
@ -89,7 +89,7 @@ void mwidget::invalidate(invalidate_reason in_reason) {
}
}
bool mwidget::can_hit_test() const {
bool mwidget_interface::can_hit_test() const {
// 不可见组件不参与命中测试
if (has_any_flag(visibility_, visibility_t::any_invisible)) {
return false;

View File

@ -7,12 +7,12 @@
class mwindow;
/**
* @class mwidget
* @class mwidget_interface
* @brief UI组件的基类
*/
class mwidget : public std::enable_shared_from_this<mwidget> {
class mwidget_interface : public std::enable_shared_from_this<mwidget_interface> {
public:
virtual ~mwidget();
virtual ~mwidget_interface();
virtual const mwindow* get_window() const;
virtual mwindow* get_window();
@ -32,11 +32,11 @@ public:
auto get_parent() const { return parent_.lock(); }
const auto& get_children() const { return children_; }
void set_parent(const std::shared_ptr<mwidget>& in_parent);
void set_parent(const std::shared_ptr<mwidget_interface>& in_parent);
void remove_from_parent();
void add_child(const std::shared_ptr<mwidget>& in_child);
void remove_child(const std::shared_ptr<mwidget>& in_child);
void add_child(const std::shared_ptr<mwidget_interface>& in_child);
void remove_child(const std::shared_ptr<mwidget_interface>& in_child);
virtual void invalidate(invalidate_reason in_reason);
auto has_invalidate_reason(invalidate_reason in_reason) const { return has_any_flag(invalidate_, in_reason); }
@ -113,12 +113,41 @@ public:
virtual hit_test_handle on_mouse_wheel(const Eigen::Vector2f& in_position, const wheel_event& in_delta) { return hit_test_handle::unhandled(); }
protected:
bool enabled_ = true;
invalidate_reason invalidate_{};
visibility_t visibility_ = visibility_t::self_hit_test_invisible;
std::optional<Eigen::Vector2f> desired_size_{};
std::weak_ptr<mwidget> parent_;
std::vector<std::shared_ptr<mwidget>> children_;
geometry_t geometry_{};
std::string tag{}; // 风格标签
bool enabled_ = true;
invalidate_reason invalidate_{};
visibility_t visibility_ = visibility_t::self_hit_test_invisible;
std::optional<Eigen::Vector2f> desired_size_{};
std::weak_ptr<mwidget_interface> parent_;
std::vector<std::shared_ptr<mwidget_interface>> children_;
geometry_t geometry_{};
std::string tag{}; // 风格标签
};
struct null_args {
};
template<typename ConstructArgs>
class mwidget : public mwidget_interface {
public:
using construct_args = ConstructArgs;
virtual void setup_widget(const ConstructArgs& in_args) {}
};
#define WARG(type, name) \
protected: \
type name##_{}; \
public: \
const auto& name() const { return name##_; } \
auto& name(const type& in_value) { \
name##_ = in_value; \
return *this; \
} \
auto& name(type&& in_value) { \
name##_ = std::move(in_value); \
return *this; \
} \
auto& name(const type* in_value) { \
name##_ = *in_value; \
return *this; \
} \

View File

@ -26,21 +26,21 @@
#define SLOT_CONTENT() \
public: \
auto& set(const std::shared_ptr<mwidget>& in_widget) { \
auto& set(const std::shared_ptr<mwidget_interface>& in_widget) { \
widget_ = in_widget; \
return me(); \
} \
const auto& get() const { return widget_; } \
auto& operator()(const std::shared_ptr<mwidget>& in_widget) { \
auto& operator[](const std::shared_ptr<mwidget_interface>& in_widget) { \
set(in_widget); \
return me(); \
} \
auto& operator+(const std::shared_ptr<mwidget>& in_widget) { \
auto& operator+(const std::shared_ptr<mwidget_interface>& in_widget) { \
set(in_widget); \
return me(); \
} \
protected: \
std::shared_ptr<mwidget> widget_{};
std::shared_ptr<mwidget_interface> widget_{};
#define SLOT_SIZE() \
public: \

View File

@ -13,6 +13,7 @@
*/
template<typename WidgetType>
struct mwidget_decl {
using construct_args = typename WidgetType::construct_args;
mwidget_decl() {
widget_ = std::make_shared<WidgetType>();
}
@ -30,12 +31,13 @@ struct mwidget_decl {
auto operator[](Args&&... in_args) {
return std::static_pointer_cast<WidgetType>((*widget_)[std::forward<Args>(in_args)...]);
}
auto operator->() {
return widget_.get();
auto& setup(const construct_args& in_args) {
widget_->setup_widget(in_args);
return *this;
}
operator std::shared_ptr<mwidget>() const { return widget_; }
std::shared_ptr<WidgetType> widget_;
operator std::shared_ptr<mwidget_interface>() const { return widget_; }
std::shared_ptr<WidgetType> widget_;
};
/**
@ -56,6 +58,8 @@ button->push_slot(
*/
#define mslot(type) type::slot_type()
#define margs(type) type::construct_args()
/**
* @param type
* @code
@ -75,4 +79,4 @@ mnew(mv_box)
* @endcode
* @note 使mnew方式使std::make_shared<type>()使mcompound_widget和mpanel_widget
*/
#define mnew(type, ...) mwidget_decl<type>(__VA_ARGS__)
#define mnew(type, ...) mwidget_decl<type>().setup(type::construct_args()__VA_ARGS__)

View File

@ -7,7 +7,7 @@
#include <memory>
#include <Eigen/Core>
class mwidget;
class mwidget_interface;
/**
* @enum hit_test_result_behavior
* @brief
@ -42,7 +42,7 @@ private:
};
struct hit_test_result {
std::shared_ptr<mwidget> widget;
std::shared_ptr<mwidget_interface> widget;
Eigen::Vector2f widget_space_pos;
operator bool() const {

View File

@ -3,7 +3,7 @@
//
#include "layout_utils.h"
void arrange_single_child(const geometry_t& in_allotted_geometry, const std::shared_ptr<mwidget>& child_widget,
void arrange_single_child(const geometry_t& in_allotted_geometry, const std::shared_ptr<mwidget_interface>& child_widget,
horizontal_alignment_t h_alignment, vertical_alignment_t v_alignment, const margin_t& margin) {
if (!child_widget) return;

View File

@ -14,7 +14,7 @@
*/
void arrange_single_child(
const geometry_t& in_allotted_geometry,
const std::shared_ptr<mwidget>& child_widget,
const std::shared_ptr<mwidget_interface>& child_widget,
horizontal_alignment_t h_alignment,
vertical_alignment_t v_alignment,
const margin_t& margin = margin_t() // 默认为空边距
@ -48,13 +48,13 @@ void arrange_box_children(
// 第一轮:收集可见部件信息并计算自动尺寸
struct slot_widget_info {
std::shared_ptr<mwidget> widget;
const SlotType* slot;
float size{};
float margin_start{}; // 主轴起始边缘的margin
float margin_end{}; // 主轴结束边缘的margin
float margin_cross_start{}; // 交叉轴起始边缘的margin
float margin_cross_end{}; // 交叉轴结束边缘的margin
std::shared_ptr<mwidget_interface> widget;
const SlotType* slot;
float size{};
float margin_start{}; // 主轴起始边缘的margin
float margin_end{}; // 主轴结束边缘的margin
float margin_cross_start{}; // 交叉轴起始边缘的margin
float margin_cross_end{}; // 交叉轴结束边缘的margin
};
std::vector<slot_widget_info> visible_widgets;
@ -71,23 +71,23 @@ void arrange_box_children(
if (!has_any_flag(widget->get_visibility(), in_visibility_filter))
continue;
const auto& margin = child_slot.margin();
const float margin_left = margin.left;
const float margin_right = margin.right;
const float margin_top = margin.top;
const auto& margin = child_slot.margin();
const float margin_left = margin.left;
const float margin_right = margin.right;
const float margin_top = margin.top;
const float margin_bottom = margin.bottom;
float margin_start, margin_end, margin_cross_start, margin_cross_end;
if (is_horizontal) {
margin_start = margin_left;
margin_end = margin_right;
margin_start = margin_left;
margin_end = margin_right;
margin_cross_start = margin_top;
margin_cross_end = margin_bottom;
margin_cross_end = margin_bottom;
} else {
margin_start = margin_top;
margin_end = margin_bottom;
margin_start = margin_top;
margin_end = margin_bottom;
margin_cross_start = margin_left;
margin_cross_end = margin_right;
margin_cross_end = margin_right;
}
total_margins += margin_start + margin_end;
@ -161,10 +161,10 @@ void arrange_box_children(
pos.x() = position;
// --- 交叉轴 (Y) ---
pos.y() = info.margin_cross_start;
pos.y() = info.margin_cross_start;
const float available_height = container_size.y() - (info.margin_cross_start + info.margin_cross_end);
const float widget_height = desired_size.y(); // 控件期望的高度
float final_height = available_height; // 默认拉伸交叉轴
const float widget_height = desired_size.y(); // 控件期望的高度
float final_height = available_height; // 默认拉伸交叉轴
if constexpr (has_vertical_alignment<SlotType>) {
const auto& v_alignment = info.slot->vertical_alignment();
@ -200,10 +200,10 @@ void arrange_box_children(
pos.y() = position;
// --- 交叉轴 (X) ---
pos.x() = info.margin_cross_start;
pos.x() = info.margin_cross_start;
const float available_width = container_size.x() - (info.margin_cross_start + info.margin_cross_end);
const float widget_width = desired_size.x(); // 控件期望的宽度
float final_width = available_width; // 默认拉伸交叉轴
const float widget_width = desired_size.x(); // 控件期望的宽度
float final_width = available_width; // 默认拉伸交叉轴
if constexpr (has_horizontal_alignment<SlotType>) {
const auto& h_alignment = info.slot->horizontal_alignment();

View File

@ -52,11 +52,11 @@ platform_window* mwindow::get_platform_window() const {
return pimpl_->get_platform_window();
}
void mwindow::set_content(const std::shared_ptr<mwidget>& content) {
void mwindow::set_content(const std::shared_ptr<mwidget_interface>& content) {
pimpl_->set_content(content);
}
std::shared_ptr<mwidget> mwindow::get_content() const {
std::shared_ptr<mwidget_interface> mwindow::get_content() const {
return pimpl_->get_content();
}

View File

@ -28,7 +28,7 @@ struct wheel_event;
* UI组件的窗口
* UI组件
*/
class mwindow : public mwidget {
class mwindow : public mwidget<null_args> {
public:
/**
@ -68,13 +68,13 @@ public:
* @brief
* @param content
*/
void set_content(const std::shared_ptr<mwidget>& content);
void set_content(const std::shared_ptr<mwidget_interface>& content);
/**
* @brief
* @return
*/
[[nodiscard]] auto get_content() const -> std::shared_ptr<mwidget>;
[[nodiscard]] auto get_content() const -> std::shared_ptr<mwidget_interface>;
//-------------- 几何与变换 --------------
/**

View File

@ -11,8 +11,8 @@ struct mouse_state {
Eigen::Vector2f press_position = Eigen::Vector2f::Zero();
time_type press_time{};
time_type last_click_time{};
std::weak_ptr<mwidget> hover_widget;
std::weak_ptr<mwidget> hit_widget;
std::weak_ptr<mwidget_interface> hover_widget;
std::weak_ptr<mwidget_interface> hit_widget;
void reset() {
position = Eigen::Vector2f::Zero();
@ -172,7 +172,7 @@ public:
}
// 内容管理
void set_content(const std::shared_ptr<mwidget>& widget) {
void set_content(const std::shared_ptr<mwidget_interface>& widget) {
content_widget_ = widget;
if (widget) {
widget->init();
@ -181,7 +181,7 @@ public:
}
}
std::shared_ptr<mwidget> get_content() const {
std::shared_ptr<mwidget_interface> get_content() const {
return content_widget_;
}
@ -198,7 +198,7 @@ public:
}
// 辅助函数
void invalidate_recursive(mwidget* widget, invalidate_reason reason) {
void invalidate_recursive(mwidget_interface* widget, invalidate_reason reason) {
if (!widget) return;
widget->invalidate(reason);
@ -213,7 +213,7 @@ public:
}
// 布局与绘制
void paint_all(mwidget* widget) {
void paint_all(mwidget_interface* widget) {
if (!widget || !is_visible_in_viewport(widget)) {
return;
}
@ -228,7 +228,7 @@ public:
}
}
bool is_visible_in_viewport(mwidget* widget) {
bool is_visible_in_viewport(mwidget_interface* widget) {
if (!widget) return false;
// 简化版视口测试,实际实现应检查裁剪区域重叠
@ -245,7 +245,7 @@ public:
pos.y() + size.y() <= 0.0f);
}
void layout_all(mwidget* widget) {
void layout_all(mwidget_interface* widget) {
if (!widget) return;
if (widget->has_invalidate_reason(invalidate_reason::layout)) {
@ -288,13 +288,13 @@ public:
mouse_.position = window_pos;
// 执行命中测试,查找鼠标位置下的控件
const auto& result = perform_hit_test(window_pos, [](mwidget* widget, const Eigen::Vector2f& local_pos) {
const auto& result = perform_hit_test(window_pos, [](mwidget_interface* widget, const Eigen::Vector2f& local_pos) {
return widget->on_mouse_move(local_pos);
});
// 如果悬停控件没有变化,直接返回
std::shared_ptr<mwidget> hover_widget = result.widget;
std::shared_ptr<mwidget> last_hover = mouse_.hover_widget.lock();
std::shared_ptr<mwidget_interface> hover_widget = result.widget;
std::shared_ptr<mwidget_interface> last_hover = mouse_.hover_widget.lock();
if (last_hover == hover_widget) {
return;
@ -314,7 +314,7 @@ public:
void process_mouse_button_down(const Eigen::Vector2f& window_pos, mouse_button button) {
// 执行命中测试,找出鼠标位置下的控件
const auto& result = perform_hit_test(window_pos, [button](mwidget* widget, const Eigen::Vector2f& local_pos) {
const auto& result = perform_hit_test(window_pos, [button](mwidget_interface* widget, const Eigen::Vector2f& local_pos) {
return widget->on_mouse_button_down(local_pos, button);
});
@ -326,14 +326,14 @@ public:
void process_mouse_button_up(const Eigen::Vector2f& window_pos, mouse_button button) {
// 执行命中测试,找出鼠标位置下的控件
const auto& hit_result = perform_hit_test(window_pos, [button](mwidget* widget, const Eigen::Vector2f& local_pos) {
const auto& hit_result = perform_hit_test(window_pos, [button](mwidget_interface* widget, const Eigen::Vector2f& local_pos) {
return widget->on_mouse_button_up(local_pos, button);
});
const auto& widget_local_pos = hit_result.widget_space_pos;
// 获取之前点击的控件
std::shared_ptr<mwidget> pressed_widget = mouse_.hit_widget.lock();
std::shared_ptr<mwidget_interface> pressed_widget = mouse_.hit_widget.lock();
// 如果没有点击到任何控件,重置状态并返回
if (!pressed_widget) {
@ -359,20 +359,20 @@ public:
}
void process_mouse_button_dbl(const Eigen::Vector2f& window_pos, mouse_button button) {
std::shared_ptr<mwidget> hover_widget = mouse_.hover_widget.lock();
std::shared_ptr<mwidget_interface> hover_widget = mouse_.hover_widget.lock();
if (hover_widget) {
hover_widget->on_double_click(window_pos, button);
}
}
void process_mouse_wheel(const Eigen::Vector2f& window_pos, wheel_event wheel_event) {
perform_hit_test(window_pos, [&](mwidget* widget, const Eigen::Vector2f& local_pos) {
perform_hit_test(window_pos, [&](mwidget_interface* widget, const Eigen::Vector2f& local_pos) {
return widget->on_mouse_wheel(local_pos, wheel_event);
});
}
void process_mouse_leave() {
std::shared_ptr<mwidget> hover_widget = mouse_.hover_widget.lock();
std::shared_ptr<mwidget_interface> hover_widget = mouse_.hover_widget.lock();
if (hover_widget) {
hover_widget->on_mouse_leave();
}
@ -382,7 +382,7 @@ public:
// 命中测试
hit_test_result perform_hit_test(
const Eigen::Vector2f& window_pos,
const std::function<hit_test_handle(mwidget*, const Eigen::Vector2f&)>& hit_func) const
const std::function<hit_test_handle(mwidget_interface*, const Eigen::Vector2f&)>& hit_func) const
{
if (!content_widget_) {
return {};
@ -391,9 +391,9 @@ public:
}
hit_test_result perform_hit_test(
mwidget* widget,
mwidget_interface* widget,
const Eigen::Vector2f& window_pos,
const std::function<hit_test_handle(mwidget*, const Eigen::Vector2f&)>& hit_func) const
const std::function<hit_test_handle(mwidget_interface*, const Eigen::Vector2f&)>& hit_func) const
{
if (!widget) {
return {};
@ -439,7 +439,7 @@ private:
mwindow* owner_;
std::unique_ptr<platform_window> platform_window_;
std::unique_ptr<mirage_window_state> window_state_;
std::shared_ptr<mwidget> content_widget_;
std::shared_ptr<mwidget_interface> content_widget_;
mirage_paint_context paint_context_;
render_state render_state_;
mouse_state mouse_;