diff --git a/single_header/sw_all.cpp b/single_header/sw_all.cpp index cb97f59c..e1b5749d 100644 --- a/single_header/sw_all.cpp +++ b/single_header/sw_all.cpp @@ -231,6 +231,85 @@ void sw::ButtonBase::OnDoubleClicked() this->RaiseRoutedEvent(ButtonBase_DoubleClicked); } +// Canvas.cpp + +sw::Canvas::Canvas() +{ + this->_canvasLayout.Associate(this); + this->SetAlignment(HorizontalAlignment::Stretch, VerticalAlignment::Stretch); +} + +sw::CanvasLayoutTag sw::Canvas::GetCanvasLayoutTag(UIElement &element) +{ + return element.LayoutTag.Get(); +} + +void sw::Canvas::SetCanvasLayoutTag(UIElement &element, const CanvasLayoutTag &tag) +{ + element.LayoutTag.Set(tag); +} + +sw::LayoutHost *sw::Canvas::GetDefaultLayout() +{ + return &this->_canvasLayout; +} + +// CanvasLayout.cpp + +sw::CanvasLayoutTag::CanvasLayoutTag() + : left(0), top(0) +{ +} + +sw::CanvasLayoutTag::CanvasLayoutTag(float left, float top) + : left(left), top(top) +{ +} + +sw::CanvasLayoutTag::CanvasLayoutTag(uint64_t layoutTag) + : left(reinterpret_cast(&layoutTag)[0]), + top(reinterpret_cast(&layoutTag)[1]) +{ +} + +sw::CanvasLayoutTag::operator uint64_t() const +{ + uint64_t result; + reinterpret_cast(&result)[0] = left; + reinterpret_cast(&result)[1] = top; + return result; +} + +void sw::CanvasLayout::MeasureOverride(Size &availableSize) +{ + Size desireSize{}; + Size measureSize{INFINITY, INFINITY}; + + int childCount = this->GetChildLayoutCount(); + for (int i = 0; i < childCount; ++i) { + ILayout &item = this->GetChildLayoutAt(i); + + item.Measure(measureSize); + Size childDesireSize = item.GetDesireSize(); + CanvasLayoutTag tag = item.GetLayoutTag(); + desireSize.width = Utils::Max(tag.left + childDesireSize.width, desireSize.width); + desireSize.height = Utils::Max(tag.top + childDesireSize.height, desireSize.height); + } + + this->SetDesireSize(desireSize); +} + +void sw::CanvasLayout::ArrangeOverride(Size &finalSize) +{ + int childCount = this->GetChildLayoutCount(); + for (int i = 0; i < childCount; ++i) { + ILayout &item = this->GetChildLayoutAt(i); + Size childDesireSize = item.GetDesireSize(); + CanvasLayoutTag tag = item.GetLayoutTag(); + item.Arrange(Rect{tag.left, tag.top, childDesireSize.width, childDesireSize.height}); + } +} + // CheckableButton.cpp sw::CheckableButton::CheckableButton() @@ -1015,6 +1094,11 @@ sw::GridLayoutTag::GridLayoutTag(uint16_t row, uint16_t column, uint16_t rowSpan { } +sw::GridLayoutTag::GridLayoutTag(uint16_t row, uint16_t column) + : row(row), column(column), rowSpan(1), columnSpan(1) +{ +} + sw::GridLayoutTag::GridLayoutTag(uint64_t layoutTag) : row((layoutTag >> 0) & 0xffff), column((layoutTag >> 16) & 0xffff), rowSpan((layoutTag >> 32) & 0xffff), columnSpan((layoutTag >> 48) & 0xffff) @@ -3804,7 +3888,8 @@ bool sw::Panel::OnPaint() bool sw::Panel::OnSize(Size newClientSize) { - InvalidateRect(this->Handle, NULL, FALSE); + if (this->_borderStyle != sw::BorderStyle::None) + InvalidateRect(this->Handle, NULL, FALSE); return UIElement::OnSize(newClientSize); } diff --git a/single_header/sw_all.h b/single_header/sw_all.h index 80d6a58e..e6513281 100644 --- a/single_header/sw_all.h +++ b/single_header/sw_all.h @@ -1064,12 +1064,24 @@ namespace sw namespace sw { + template + class Property; // 向前声明 + /** * @brief 只读属性 */ template class ReadOnlyProperty { + // 添加Property类为友元类 + friend class Property; + + // 删除拷贝构造函数 + ReadOnlyProperty(const ReadOnlyProperty &) = delete; + + // 删除拷贝赋值运算符 + ReadOnlyProperty &operator=(const ReadOnlyProperty &) = delete; + private: /** * @brief 读取属性的函数 @@ -1108,6 +1120,14 @@ namespace sw { return &this->_funcGet(); } + + /** + * @brief 支持Utils::BuildStr + */ + friend std::wostream &operator<<(std::wostream &wos, const ReadOnlyProperty &prop) + { + return wos << prop._funcGet(); + } }; /** @@ -1116,6 +1136,15 @@ namespace sw template class WriteOnlyProperty { + // 添加Property类为友元类 + friend class Property; + + // 删除拷贝构造函数 + WriteOnlyProperty(const WriteOnlyProperty &) = delete; + + // 删除拷贝赋值运算符 + WriteOnlyProperty &operator=(const WriteOnlyProperty &) = delete; + private: /** * @brief 写属性的函数 @@ -1155,6 +1184,12 @@ namespace sw template class Property : public ReadOnlyProperty, public WriteOnlyProperty { + // 删除拷贝构造函数 + Property(const Property &) = delete; + + // 删除拷贝赋值运算符 + Property &operator=(const Property &) = delete; + public: /** * @brief 初始化Property @@ -1169,7 +1204,7 @@ namespace sw */ const Property &operator=(const T &value) const { - this->Set(value); + this->_funcSet(value); return *this; } @@ -1178,18 +1213,10 @@ namespace sw */ T *operator->() const { - const T &value = this->Get(); + const T &value = this->_funcGet(); return const_cast(&value); } }; - - /*================================================================================*/ - - template - inline std::wostream &operator<<(std::wostream &wos, const ReadOnlyProperty &prop) - { - return wos << prop.Get(); - } } // RoutedEvent.h @@ -3810,6 +3837,64 @@ namespace sw }; } +// CanvasLayout.h + + +namespace sw +{ + /** + * @brief 绝对位置布局方式的布局标记 + */ + struct CanvasLayoutTag { + /** + * @brief 左边 + */ + float left; + + /** + * @brief 顶边 + */ + float top; + + /** + * @brief 左边顶边均为0 + */ + CanvasLayoutTag(); + + /** + * @brief 指定左边和顶边 + */ + CanvasLayoutTag(float left, float top); + + /** + * @brief 从LayoutTag创建 + */ + CanvasLayoutTag(uint64_t layoutTag); + + /** + * @brief 隐式转换LayoutTag + */ + operator uint64_t() const; + }; + + /** + * @brief 绝对位置布局方式 + */ + class CanvasLayout : public LayoutHost + { + public: + /** + * @brief 计算所需尺寸 + */ + virtual void MeasureOverride(Size &availableSize) override; + + /** + * @brief 安排控件 + */ + virtual void ArrangeOverride(Size &finalSize) override; + }; +} + // DockLayout.h @@ -3911,6 +3996,11 @@ namespace sw */ GridLayoutTag(uint16_t row, uint16_t column, uint16_t rowSpan, uint16_t columnSpan); + /** + * @brief 初始化GridLayoutTag + */ + GridLayoutTag(uint16_t row, uint16_t column); + /** * @brief 从LayoutTag创建 */ @@ -3988,7 +4078,7 @@ namespace sw /** * @brief 初始化FillRemainGridRow */ - FillRemainGridRow(double proportion); + FillRemainGridRow(double proportion = 1); }; /** @@ -4048,7 +4138,7 @@ namespace sw /** * @brief 初始化FillRemainGridColumn */ - FillRemainGridColumn(double proportion); + FillRemainGridColumn(double proportion = 1); }; /** @@ -5473,41 +5563,53 @@ namespace sw /** * @brief 项数 */ - const ReadOnlyProperty ItemsCount = ReadOnlyProperty( - // get - [&]() -> const int & { - static int result; - result = this->GetItemsCount(); - return result; - }); + const ReadOnlyProperty ItemsCount; /** * @brief 选中项的索引,当无选中项时为-1 */ - const Property SelectedIndex = Property( - // get - [&]() -> const int & { - static int result; - result = this->GetSelectedIndex(); - return result; - }, - // set - [&](const int &value) { - this->SetSelectedIndex(value); - }); + const Property SelectedIndex; /** * @brief 选中项 */ - const ReadOnlyProperty SelectedItem = ReadOnlyProperty( - // get - [&]() -> const TItem & { - static TItem result; - result = this->GetSelectedItem(); - return result; - }); + const ReadOnlyProperty SelectedItem; protected: + /** + * @brief 初始化ItemsControl + */ + ItemsControl() + : ItemsCount( + // get + [&]() -> const int & { + static int result; + result = this->GetItemsCount(); + return result; + }), + + SelectedIndex( + // get + [&]() -> const int & { + static int result; + result = this->GetSelectedIndex(); + return result; + }, + // set + [&](const int &value) { + this->SetSelectedIndex(value); + }), + + SelectedItem( + // get + [&]() -> const TItem & { + static TItem result; + result = this->GetSelectedItem(); + return result; + }) + { + } + /** * @brief 选中项改变时调用该函数 */ @@ -7211,6 +7313,46 @@ namespace sw }; } +// Canvas.h + + +namespace sw +{ + /** + * @brief 一种可以为子元素设置绝对位置的面板,与普通Panel不同的是Canvas支持自动滚动条 + */ + class Canvas : public Panel + { + private: + /** + * @brief 默认布局对象 + */ + CanvasLayout _canvasLayout = CanvasLayout(); + + public: + /** + * @brief 初始化Canvas + */ + Canvas(); + + /** + * @brief 获取指定元素的布局标记 + */ + static CanvasLayoutTag GetCanvasLayoutTag(UIElement &element); + + /** + * @brief 给指定元素设置布局标记 + */ + static void SetCanvasLayoutTag(UIElement &element, const CanvasLayoutTag &tag); + + protected: + /** + * @brief 获取默认布局对象 + */ + virtual LayoutHost *GetDefaultLayout() override; + }; +} + // CheckBox.h diff --git a/sw/inc/Canvas.h b/sw/inc/Canvas.h new file mode 100644 index 00000000..8e7ac0e4 --- /dev/null +++ b/sw/inc/Canvas.h @@ -0,0 +1,41 @@ +#pragma once + +#include "CanvasLayout.h" +#include "Panel.h" + +namespace sw +{ + /** + * @brief 一种可以为子元素设置绝对位置的面板,与普通Panel不同的是Canvas支持自动滚动条 + */ + class Canvas : public Panel + { + private: + /** + * @brief 默认布局对象 + */ + CanvasLayout _canvasLayout = CanvasLayout(); + + public: + /** + * @brief 初始化Canvas + */ + Canvas(); + + /** + * @brief 获取指定元素的布局标记 + */ + static CanvasLayoutTag GetCanvasLayoutTag(UIElement &element); + + /** + * @brief 给指定元素设置布局标记 + */ + static void SetCanvasLayoutTag(UIElement &element, const CanvasLayoutTag &tag); + + protected: + /** + * @brief 获取默认布局对象 + */ + virtual LayoutHost *GetDefaultLayout() override; + }; +} diff --git a/sw/inc/CanvasLayout.h b/sw/inc/CanvasLayout.h new file mode 100644 index 00000000..3580d0ad --- /dev/null +++ b/sw/inc/CanvasLayout.h @@ -0,0 +1,58 @@ +#pragma once + +#include "LayoutHost.h" + +namespace sw +{ + /** + * @brief 绝对位置布局方式的布局标记 + */ + struct CanvasLayoutTag { + /** + * @brief 左边 + */ + float left; + + /** + * @brief 顶边 + */ + float top; + + /** + * @brief 左边顶边均为0 + */ + CanvasLayoutTag(); + + /** + * @brief 指定左边和顶边 + */ + CanvasLayoutTag(float left, float top); + + /** + * @brief 从LayoutTag创建 + */ + CanvasLayoutTag(uint64_t layoutTag); + + /** + * @brief 隐式转换LayoutTag + */ + operator uint64_t() const; + }; + + /** + * @brief 绝对位置布局方式 + */ + class CanvasLayout : public LayoutHost + { + public: + /** + * @brief 计算所需尺寸 + */ + virtual void MeasureOverride(Size &availableSize) override; + + /** + * @brief 安排控件 + */ + virtual void ArrangeOverride(Size &finalSize) override; + }; +} diff --git a/sw/inc/GridLayout.h b/sw/inc/GridLayout.h index 9ae28bed..43afaa6f 100644 --- a/sw/inc/GridLayout.h +++ b/sw/inc/GridLayout.h @@ -39,6 +39,11 @@ namespace sw */ GridLayoutTag(uint16_t row, uint16_t column, uint16_t rowSpan, uint16_t columnSpan); + /** + * @brief 初始化GridLayoutTag + */ + GridLayoutTag(uint16_t row, uint16_t column); + /** * @brief 从LayoutTag创建 */ @@ -116,7 +121,7 @@ namespace sw /** * @brief 初始化FillRemainGridRow */ - FillRemainGridRow(double proportion); + FillRemainGridRow(double proportion = 1); }; /** @@ -176,7 +181,7 @@ namespace sw /** * @brief 初始化FillRemainGridColumn */ - FillRemainGridColumn(double proportion); + FillRemainGridColumn(double proportion = 1); }; /** diff --git a/sw/inc/ItemsControl.h b/sw/inc/ItemsControl.h index b1616719..242372de 100644 --- a/sw/inc/ItemsControl.h +++ b/sw/inc/ItemsControl.h @@ -22,41 +22,53 @@ namespace sw /** * @brief 项数 */ - const ReadOnlyProperty ItemsCount = ReadOnlyProperty( - // get - [&]() -> const int & { - static int result; - result = this->GetItemsCount(); - return result; - }); + const ReadOnlyProperty ItemsCount; /** * @brief 选中项的索引,当无选中项时为-1 */ - const Property SelectedIndex = Property( - // get - [&]() -> const int & { - static int result; - result = this->GetSelectedIndex(); - return result; - }, - // set - [&](const int &value) { - this->SetSelectedIndex(value); - }); + const Property SelectedIndex; /** * @brief 选中项 */ - const ReadOnlyProperty SelectedItem = ReadOnlyProperty( - // get - [&]() -> const TItem & { - static TItem result; - result = this->GetSelectedItem(); - return result; - }); + const ReadOnlyProperty SelectedItem; protected: + /** + * @brief 初始化ItemsControl + */ + ItemsControl() + : ItemsCount( + // get + [&]() -> const int & { + static int result; + result = this->GetItemsCount(); + return result; + }), + + SelectedIndex( + // get + [&]() -> const int & { + static int result; + result = this->GetSelectedIndex(); + return result; + }, + // set + [&](const int &value) { + this->SetSelectedIndex(value); + }), + + SelectedItem( + // get + [&]() -> const TItem & { + static TItem result; + result = this->GetSelectedItem(); + return result; + }) + { + } + /** * @brief 选中项改变时调用该函数 */ diff --git a/sw/inc/Property.h b/sw/inc/Property.h index 7d457161..ac97f711 100644 --- a/sw/inc/Property.h +++ b/sw/inc/Property.h @@ -5,12 +5,24 @@ namespace sw { + template + class Property; // 向前声明 + /** * @brief 只读属性 */ template class ReadOnlyProperty { + // 添加Property类为友元类 + friend class Property; + + // 删除拷贝构造函数 + ReadOnlyProperty(const ReadOnlyProperty &) = delete; + + // 删除拷贝赋值运算符 + ReadOnlyProperty &operator=(const ReadOnlyProperty &) = delete; + private: /** * @brief 读取属性的函数 @@ -49,6 +61,14 @@ namespace sw { return &this->_funcGet(); } + + /** + * @brief 支持Utils::BuildStr + */ + friend std::wostream &operator<<(std::wostream &wos, const ReadOnlyProperty &prop) + { + return wos << prop._funcGet(); + } }; /** @@ -57,6 +77,15 @@ namespace sw template class WriteOnlyProperty { + // 添加Property类为友元类 + friend class Property; + + // 删除拷贝构造函数 + WriteOnlyProperty(const WriteOnlyProperty &) = delete; + + // 删除拷贝赋值运算符 + WriteOnlyProperty &operator=(const WriteOnlyProperty &) = delete; + private: /** * @brief 写属性的函数 @@ -96,6 +125,12 @@ namespace sw template class Property : public ReadOnlyProperty, public WriteOnlyProperty { + // 删除拷贝构造函数 + Property(const Property &) = delete; + + // 删除拷贝赋值运算符 + Property &operator=(const Property &) = delete; + public: /** * @brief 初始化Property @@ -110,7 +145,7 @@ namespace sw */ const Property &operator=(const T &value) const { - this->Set(value); + this->_funcSet(value); return *this; } @@ -119,16 +154,8 @@ namespace sw */ T *operator->() const { - const T &value = this->Get(); + const T &value = this->_funcGet(); return const_cast(&value); } }; - - /*================================================================================*/ - - template - inline std::wostream &operator<<(std::wostream &wos, const ReadOnlyProperty &prop) - { - return wos << prop.Get(); - } } diff --git a/sw/inc/SimpleWindow.h b/sw/inc/SimpleWindow.h index 84ccd286..56f52725 100644 --- a/sw/inc/SimpleWindow.h +++ b/sw/inc/SimpleWindow.h @@ -5,6 +5,8 @@ #include "App.h" #include "Button.h" #include "ButtonBase.h" +#include "Canvas.h" +#include "CanvasLayout.h" #include "CheckBox.h" #include "CheckableButton.h" #include "Color.h" diff --git a/sw/src/Canvas.cpp b/sw/src/Canvas.cpp new file mode 100644 index 00000000..1e984164 --- /dev/null +++ b/sw/src/Canvas.cpp @@ -0,0 +1,22 @@ +#include "Canvas.h" + +sw::Canvas::Canvas() +{ + this->_canvasLayout.Associate(this); + this->SetAlignment(HorizontalAlignment::Stretch, VerticalAlignment::Stretch); +} + +sw::CanvasLayoutTag sw::Canvas::GetCanvasLayoutTag(UIElement &element) +{ + return element.LayoutTag.Get(); +} + +void sw::Canvas::SetCanvasLayoutTag(UIElement &element, const CanvasLayoutTag &tag) +{ + element.LayoutTag.Set(tag); +} + +sw::LayoutHost *sw::Canvas::GetDefaultLayout() +{ + return &this->_canvasLayout; +} diff --git a/sw/src/CanvasLayout.cpp b/sw/src/CanvasLayout.cpp new file mode 100644 index 00000000..b8b179bf --- /dev/null +++ b/sw/src/CanvasLayout.cpp @@ -0,0 +1,57 @@ +#include "CanvasLayout.h" +#include "Utils.h" +#include + +sw::CanvasLayoutTag::CanvasLayoutTag() + : left(0), top(0) +{ +} + +sw::CanvasLayoutTag::CanvasLayoutTag(float left, float top) + : left(left), top(top) +{ +} + +sw::CanvasLayoutTag::CanvasLayoutTag(uint64_t layoutTag) + : left(reinterpret_cast(&layoutTag)[0]), + top(reinterpret_cast(&layoutTag)[1]) +{ +} + +sw::CanvasLayoutTag::operator uint64_t() const +{ + uint64_t result; + reinterpret_cast(&result)[0] = left; + reinterpret_cast(&result)[1] = top; + return result; +} + +void sw::CanvasLayout::MeasureOverride(Size &availableSize) +{ + Size desireSize{}; + Size measureSize{INFINITY, INFINITY}; + + int childCount = this->GetChildLayoutCount(); + for (int i = 0; i < childCount; ++i) { + ILayout &item = this->GetChildLayoutAt(i); + + item.Measure(measureSize); + Size childDesireSize = item.GetDesireSize(); + CanvasLayoutTag tag = item.GetLayoutTag(); + desireSize.width = Utils::Max(tag.left + childDesireSize.width, desireSize.width); + desireSize.height = Utils::Max(tag.top + childDesireSize.height, desireSize.height); + } + + this->SetDesireSize(desireSize); +} + +void sw::CanvasLayout::ArrangeOverride(Size &finalSize) +{ + int childCount = this->GetChildLayoutCount(); + for (int i = 0; i < childCount; ++i) { + ILayout &item = this->GetChildLayoutAt(i); + Size childDesireSize = item.GetDesireSize(); + CanvasLayoutTag tag = item.GetLayoutTag(); + item.Arrange(Rect{tag.left, tag.top, childDesireSize.width, childDesireSize.height}); + } +} diff --git a/sw/src/GridLayout.cpp b/sw/src/GridLayout.cpp index b46189f4..44b93490 100644 --- a/sw/src/GridLayout.cpp +++ b/sw/src/GridLayout.cpp @@ -13,6 +13,11 @@ sw::GridLayoutTag::GridLayoutTag(uint16_t row, uint16_t column, uint16_t rowSpan { } +sw::GridLayoutTag::GridLayoutTag(uint16_t row, uint16_t column) + : row(row), column(column), rowSpan(1), columnSpan(1) +{ +} + sw::GridLayoutTag::GridLayoutTag(uint64_t layoutTag) : row((layoutTag >> 0) & 0xffff), column((layoutTag >> 16) & 0xffff), rowSpan((layoutTag >> 32) & 0xffff), columnSpan((layoutTag >> 48) & 0xffff) diff --git a/sw/src/Panel.cpp b/sw/src/Panel.cpp index ffe7ce59..c5ca0b63 100644 --- a/sw/src/Panel.cpp +++ b/sw/src/Panel.cpp @@ -58,6 +58,7 @@ bool sw::Panel::OnPaint() bool sw::Panel::OnSize(Size newClientSize) { - InvalidateRect(this->Handle, NULL, FALSE); + if (this->_borderStyle != sw::BorderStyle::None) + InvalidateRect(this->Handle, NULL, FALSE); return UIElement::OnSize(newClientSize); } diff --git a/vs/sw.vcxproj b/vs/sw.vcxproj index 5b044d34..803ee4a3 100644 --- a/vs/sw.vcxproj +++ b/vs/sw.vcxproj @@ -155,6 +155,8 @@ + + @@ -229,6 +231,8 @@ + + diff --git a/vs/sw.vcxproj.filters b/vs/sw.vcxproj.filters index a7e9fee6..ee423dde 100644 --- a/vs/sw.vcxproj.filters +++ b/vs/sw.vcxproj.filters @@ -228,6 +228,12 @@ inc + + inc + + + inc + @@ -413,5 +419,11 @@ src + + src + + + src + \ No newline at end of file