优化控件布局语法
This commit is contained in:
parent
900db6dd70
commit
542949e9f5
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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());
|
||||
|
@ -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 },
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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 };
|
||||
};
|
||||
|
@ -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(
|
||||
|
@ -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_;
|
||||
|
@ -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(), {});
|
||||
|
@ -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; }
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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; \
|
||||
} \
|
||||
|
@ -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: \
|
||||
|
@ -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__)
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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>;
|
||||
|
||||
//-------------- 几何与变换 --------------
|
||||
/**
|
||||
|
@ -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_;
|
||||
|
Loading…
x
Reference in New Issue
Block a user