diff options
29 files changed, 500 insertions, 877 deletions
diff --git a/CruUI.vcxproj b/CruUI.vcxproj index 975d74d8..23205792 100644 --- a/CruUI.vcxproj +++ b/CruUI.vcxproj @@ -121,15 +121,12 @@ <ClCompile Include="src\timer.cpp" /> <ClCompile Include="src\ui\animations\animation.cpp" /> <ClCompile Include="src\ui\control.cpp" /> - <ClCompile Include="src\ui\controls\border.cpp" /> - <ClCompile Include="src\ui\controls\border_delegate.cpp" /> + <ClCompile Include="src\ui\border_property.cpp" /> <ClCompile Include="src\ui\controls\button.cpp" /> <ClCompile Include="src\ui\controls\linear_layout.cpp" /> - <ClCompile Include="src\ui\controls\margin_container.cpp" /> <ClCompile Include="src\ui\controls\text_block.cpp" /> <ClCompile Include="src\ui\controls\text_box.cpp" /> - <ClInclude Include="src\ui\controls\border.h" /> - <ClInclude Include="src\ui\controls\border_delegate.h" /> + <ClInclude Include="src\ui\border_property.h" /> <ClInclude Include="src\ui\controls\text_control.h" /> <ClCompile Include="src\ui\controls\toggle_button.cpp" /> <ClCompile Include="src\ui\events\ui_event.cpp" /> @@ -151,7 +148,6 @@ <ClInclude Include="src\ui\control.h" /> <ClInclude Include="src\ui\controls\button.h" /> <ClInclude Include="src\ui\controls\linear_layout.h" /> - <ClInclude Include="src\ui\controls\margin_container.h" /> <ClInclude Include="src\ui\controls\text_block.h" /> <ClInclude Include="src\ui\controls\text_box.h" /> <ClCompile Include="src\ui\controls\text_control.cpp" /> diff --git a/CruUI.vcxproj.filters b/CruUI.vcxproj.filters index 683b3686..4c7ea868 100644 --- a/CruUI.vcxproj.filters +++ b/CruUI.vcxproj.filters @@ -54,9 +54,6 @@ <ClCompile Include="src\ui\controls\linear_layout.cpp"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="src\ui\controls\margin_container.cpp"> - <Filter>Source Files</Filter> - </ClCompile> <ClCompile Include="src\ui\controls\text_block.cpp"> <Filter>Source Files</Filter> </ClCompile> @@ -69,10 +66,7 @@ <ClCompile Include="src\ui\events\ui_event.cpp"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="src\ui\controls\border.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="src\ui\controls\border_delegate.cpp"> + <ClCompile Include="src\ui\border_property.cpp"> <Filter>Source Files</Filter> </ClCompile> </ItemGroup> @@ -125,9 +119,6 @@ <ClInclude Include="src\ui\controls\linear_layout.h"> <Filter>Header Files</Filter> </ClInclude> - <ClInclude Include="src\ui\controls\margin_container.h"> - <Filter>Header Files</Filter> - </ClInclude> <ClInclude Include="src\ui\controls\text_block.h"> <Filter>Header Files</Filter> </ClInclude> @@ -143,10 +134,7 @@ <ClInclude Include="src\ui\controls\text_control.h"> <Filter>Header Files</Filter> </ClInclude> - <ClInclude Include="src\ui\controls\border.h"> - <Filter>Header Files</Filter> - </ClInclude> - <ClInclude Include="src\ui\controls\border_delegate.h"> + <ClInclude Include="src\ui\border_property.h"> <Filter>Header Files</Filter> </ClInclude> </ItemGroup> diff --git a/src/base.cpp b/src/base.cpp index 57a4848e..ce5554aa 100644 --- a/src/base.cpp +++ b/src/base.cpp @@ -18,12 +18,12 @@ namespace cru return result; } - void PropertyChangedNotifyObject::AddPropertyChangedListener(FunctionPtr<void(String)> listener) + void PropertyChangedNotifyObject::AddPropertyChangedListener(FunctionPtr<void(const StringView&)> listener) { listeners_.push_back(std::move(listener)); } - void PropertyChangedNotifyObject::RemovePropertyChangedListener(const FunctionPtr<void(String)>& listener) + void PropertyChangedNotifyObject::RemovePropertyChangedListener(const FunctionPtr<void(const StringView&)>& listener) { for (auto i = listeners_.cbegin(); i != listeners_.cend(); ++i) if (*i == listener) @@ -33,7 +33,7 @@ namespace cru } } - void PropertyChangedNotifyObject::RaisePropertyChangedEvent(String property_name) + void PropertyChangedNotifyObject::RaisePropertyChangedEvent(const StringView& property_name) { for (const auto& listener : listeners_) (*listener)(property_name); @@ -116,6 +116,9 @@ namespace cru class PropertyChangedNotifyObject : public Object { public: + using PropertyChangedHandler = Function<void(const StringView&)>; + using PropertyChangedHandlerPtr = FunctionPtr<void(const StringView&)>; + PropertyChangedNotifyObject() = default; PropertyChangedNotifyObject(const PropertyChangedNotifyObject& other) = delete; PropertyChangedNotifyObject(PropertyChangedNotifyObject&& other) = delete; @@ -123,14 +126,14 @@ namespace cru PropertyChangedNotifyObject& operator = (PropertyChangedNotifyObject&& other) = delete; ~PropertyChangedNotifyObject() override = default; - void AddPropertyChangedListener(FunctionPtr<void(String)> listener); + void AddPropertyChangedListener(FunctionPtr<void(const StringView&)> listener); - void RemovePropertyChangedListener(const FunctionPtr<void(String)>& listener); + void RemovePropertyChangedListener(const FunctionPtr<void(const StringView&)>& listener); protected: - void RaisePropertyChangedEvent(String property_name); + void RaisePropertyChangedEvent(const StringView& property_name); private: - std::list<FunctionPtr<void(String)>> listeners_; + std::list<FunctionPtr<void(const StringView&)>> listeners_; }; } diff --git a/src/main.cpp b/src/main.cpp index 7a105d79..06110457 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,9 +5,7 @@ #include "ui/controls/text_block.h" #include "ui/controls/toggle_button.h" #include "ui/controls/button.h" -#include "ui/controls/margin_container.h" #include "ui/controls/text_box.h" -#include "ui/controls/border.h" using cru::String; using cru::Application; @@ -15,14 +13,13 @@ using cru::ui::Window; using cru::ui::Alignment; using cru::ui::LayoutSideParams; using cru::ui::Thickness; +using cru::ui::ControlList; using cru::ui::CreateWithLayout; using cru::ui::controls::LinearLayout; using cru::ui::controls::TextBlock; using cru::ui::controls::ToggleButton; using cru::ui::controls::Button; -using cru::ui::controls::MarginContainer; using cru::ui::controls::TextBox; -using cru::ui::controls::Border; int APIENTRY wWinMain( HINSTANCE hInstance, @@ -31,6 +28,7 @@ int APIENTRY wWinMain( int nCmdShow) { Application application(hInstance); + Window window; /* window.native_message_event.AddHandler([](cru::ui::events::WindowNativeMessageEventArgs& args) @@ -152,18 +150,18 @@ int APIENTRY wWinMain( )); */ - window.AddChild( - Border::Create({ - MarginContainer::Create(Thickness(50, 50), { - LinearLayout::Create(LinearLayout::Orientation::Vertical, { - Button::Create({ - TextBlock::Create(L"Button") - }), - TextBox::Create() - }) - }) - }) - ); + const auto linear_layout = CreateWithLayout<LinearLayout>(Thickness(50, 50), Thickness(50, 50), LinearLayout::Orientation::Vertical, ControlList({ + Button::Create({ + TextBlock::Create(L"Button") + }), + TextBox::Create() + })); + + linear_layout->SetBordered(true); + + window.AddChild(linear_layout); + + //window.SetDebugDrawControlBorder(true); window.Show(); diff --git a/src/ui/border_property.cpp b/src/ui/border_property.cpp new file mode 100644 index 00000000..03cae16e --- /dev/null +++ b/src/ui/border_property.cpp @@ -0,0 +1,43 @@ +#include "border_property.h" + +#include "graph/graph.h" + +namespace cru::ui +{ + BorderProperty::BorderProperty() + { + brush_ = graph::CreateSolidBrush(D2D1::ColorF(D2D1::ColorF::Black)); + } + + void BorderProperty::SetBrush(Microsoft::WRL::ComPtr<ID2D1Brush> brush) + { + if (brush == nullptr) + throw std::invalid_argument("Brush of BorderProperty mustn't be null."); + brush_ = std::move(brush); + RaisePropertyChangedEvent(brush_property_name); + } + + void BorderProperty::SetWidth(const float width) + { + stroke_width_ = width; + RaisePropertyChangedEvent(width_property_name); + } + + void BorderProperty::SetStrokeStyle(Microsoft::WRL::ComPtr<ID2D1StrokeStyle> stroke_style) + { + stroke_style_ = std::move(stroke_style); + RaisePropertyChangedEvent(stroke_style_property_name); + } + + void BorderProperty::SetRadiusX(const float radius_x) + { + radius_x_ = radius_x; + RaisePropertyChangedEvent(radius_x_property_name); + } + + void BorderProperty::SetRadiusY(const float radius_y) + { + radius_y_ = radius_y; + RaisePropertyChangedEvent(radius_y_property_name); + } +} diff --git a/src/ui/border_property.h b/src/ui/border_property.h new file mode 100644 index 00000000..71ec0e7d --- /dev/null +++ b/src/ui/border_property.h @@ -0,0 +1,72 @@ +#pragma once + +#include "system_headers.h" + +#include "base.h" + + +namespace cru::ui +{ + class BorderProperty final : public PropertyChangedNotifyObject + { + public: + constexpr static auto brush_property_name = L"Brush"; + constexpr static auto width_property_name = L"StrokeWidth"; + constexpr static auto stroke_style_property_name = L"StrokeStyle"; + constexpr static auto radius_x_property_name = L"RadiusX"; + constexpr static auto radius_y_property_name = L"RadiusY"; + + using Ptr = std::shared_ptr<BorderProperty>; + + static Ptr Create() + { + return std::make_shared<BorderProperty>(); + } + + BorderProperty(); + BorderProperty(const BorderProperty& other) = delete; + BorderProperty(BorderProperty&& other) = delete; + BorderProperty& operator=(const BorderProperty& other) = delete; + BorderProperty& operator=(BorderProperty&& other) = delete; + ~BorderProperty() override = default; + + + Microsoft::WRL::ComPtr<ID2D1Brush> GetBrush() const + { + return brush_; + } + + float GetStrokeWidth() const + { + return stroke_width_; + } + + Microsoft::WRL::ComPtr<ID2D1StrokeStyle> GetStrokeStyle() const + { + return stroke_style_; + } + + float GetRadiusX() const + { + return radius_x_; + } + + float GetRadiusY() const + { + return radius_y_; + } + + void SetBrush(Microsoft::WRL::ComPtr<ID2D1Brush> brush); + void SetWidth(float width); + void SetStrokeStyle(Microsoft::WRL::ComPtr<ID2D1StrokeStyle> stroke_style); + void SetRadiusX(float radius_x); + void SetRadiusY(float radius_y); + + private: + Microsoft::WRL::ComPtr<ID2D1Brush> brush_ = nullptr; + float stroke_width_ = 1.0f; + Microsoft::WRL::ComPtr<ID2D1StrokeStyle> stroke_style_ = nullptr; + float radius_x_ = 0.0f; + float radius_y_ = 0.0f; + }; +} diff --git a/src/ui/control.cpp b/src/ui/control.cpp index 86101f9a..1264b15f 100644 --- a/src/ui/control.cpp +++ b/src/ui/control.cpp @@ -1,19 +1,20 @@ #include "control.h" -#include <string> -#include <algorithm> -#include <chrono> - #include "window.h" -#include "timer.h" -#include "debug_base.h" +#include "graph/graph.h" namespace cru { namespace ui { using namespace events; Control::Control(const bool container) : - is_container_(container) + is_container_(container), + border_property_changed_listener_(CreatePtr<PropertyChangedNotifyObject::PropertyChangedHandlerPtr>([this](const StringView& property_name) + { + if (property_name == BorderProperty::width_property_name) + InvalidateLayout(); + Repaint(); + })) { } @@ -209,8 +210,15 @@ namespace cru { device_context->SetTransform(old_transform * D2D1::Matrix3x2F::Translation(position.x, position.y)); OnDraw(device_context); - DrawEventArgs args(this, this, device_context); - draw_event.Raise(args); + + const auto rect = GetRect(RectRange::Content); + graph::WithTransform(device_context, D2D1::Matrix3x2F::Translation(rect.left, rect.top), + [this](ID2D1DeviceContext* device_context) + { + OnDrawContent(device_context); + DrawEventArgs args(this, this, device_context); + draw_event.Raise(args); + }); for (auto child : GetChildren()) child->Draw(device_context); @@ -250,14 +258,14 @@ namespace cru { void Control::Measure(const Size& available_size) { - SetDesiredSize(OnMeasure(available_size)); + SetDesiredSize(OnMeasureCore(available_size)); } void Control::Layout(const Rect& rect) { SetPositionRelative(rect.GetLeftTop()); SetSize(rect.GetSize()); - OnLayout(Rect(Point::Zero(), rect.GetSize())); + OnLayoutCore(Rect(Point::Zero(), rect.GetSize())); } Size Control::GetDesiredSize() const @@ -270,6 +278,77 @@ namespace cru { desired_size_ = desired_size; } + inline void Shrink(Rect& rect, const Thickness& thickness) + { + rect.left += thickness.left; + rect.top += thickness.top; + rect.width -= thickness.GetHorizontalTotal(); + rect.height -= thickness.GetVerticalTotal(); + } + + Rect Control::GetRect(RectRange range) + { + if (GetSize() == Size::Zero()) + return Rect(); + + const auto layout_params = GetLayoutParams(); + + auto result = Rect(Point::Zero(), GetSize()); + + if (range == RectRange::Margin) + return result; + + Shrink(result, layout_params->margin); + + if (range == RectRange::FullBorder) + return result; + + if (is_bordered_) + Shrink(result, Thickness(border_property_->GetStrokeWidth() / 2.0f)); + + if (range == RectRange::HalfBorder) + return result; + + if (is_bordered_) + Shrink(result, Thickness(border_property_->GetStrokeWidth() / 2.0f)); + + if (range == RectRange::Padding) + return result; + + Shrink(result, layout_params->padding); + + return result; + } + + void Control::SetBorderProperty(BorderProperty::Ptr border_property) + { + if (border_property == nullptr) + throw std::invalid_argument("Border property mustn't be null."); + + if (border_property_ != nullptr) + border_property_->RemovePropertyChangedListener(border_property_changed_listener_); + border_property_ = std::move(border_property); + border_property_->AddPropertyChangedListener(border_property_changed_listener_); + InvalidateLayout(); + Repaint(); + } + + void Control::SetBordered(const bool bordered) + { + if (bordered != is_bordered_) + { + if (bordered && border_property_ == nullptr) // create border property. + { + border_property_ = BorderProperty::Create(); + border_property_->AddPropertyChangedListener(border_property_changed_listener_); + } + + is_bordered_ = bordered; + InvalidateLayout(); + Repaint(); + } + } + void Control::OnAddChild(Control* child) { if (auto window = GetWindow()) @@ -304,16 +383,38 @@ namespace cru { window_ = nullptr; } - void Control::OnDraw(ID2D1DeviceContext * device_context) + inline D2D1_RECT_F Convert(const Rect& rect) + { + return D2D1::RectF(rect.left, rect.top, rect.left + rect.width, rect.top + rect.height); + } + + void Control::OnDraw(ID2D1DeviceContext* device_context) { #ifdef CRU_DEBUG_DRAW_CONTROL_BORDER if (GetWindow()->GetDebugDrawControlBorder()) { auto brush = Application::GetInstance()->GetDebugBorderBrush(); const auto size = GetSize(); - device_context->DrawRectangle(D2D1::RectF(0, 0, size.width, size.height), brush.Get()); + device_context->DrawRectangle(Convert(GetRect(RectRange::Margin)), brush.Get()); } #endif + + if (is_bordered_) + device_context->DrawRoundedRectangle( + D2D1::RoundedRect( + Convert(GetRect(RectRange::HalfBorder)), + border_property_->GetRadiusX(), + border_property_->GetRadiusY() + ), + border_property_->GetBrush().Get(), + border_property_->GetStrokeWidth(), + border_property_->GetStrokeStyle().Get() + ); + } + + void Control::OnDrawContent(ID2D1DeviceContext * device_context) + { + } void Control::OnPositionChanged(PositionChangedEventArgs & args) @@ -551,94 +652,6 @@ namespace cru { lose_focus_event.Raise(args); } - Size Control::OnMeasure(const Size& available_size) - { - const auto layout_params = GetLayoutParams(); - - if (!layout_params->Validate()) - throw std::runtime_error("LayoutParams is not valid. Please check it."); - - auto&& get_available_length_for_child = [](const LayoutSideParams& layout_length, const float available_length) -> float - { - switch (layout_length.mode) - { - case MeasureMode::Exactly: - { - return std::min(layout_length.length, available_length); - } - case MeasureMode::Stretch: - case MeasureMode::Content: - { - return available_length; - } - default: - UnreachableCode(); - } - }; - - const Size size_for_children(get_available_length_for_child(layout_params->width, available_size.width), - get_available_length_for_child(layout_params->height, available_size.height)); - - auto max_child_size = Size::Zero(); - ForeachChild([&](Control* control) - { - control->Measure(size_for_children); - const auto&& size = control->GetDesiredSize(); - if (max_child_size.width < size.width) - max_child_size.width = size.width; - if (max_child_size.height < size.height) - max_child_size.height = size.height; - }); - - auto&& calculate_final_length = [](const LayoutSideParams& layout_length, const float length_for_children, const float max_child_length) -> float - { - switch (layout_length.mode) - { - case MeasureMode::Exactly: - case MeasureMode::Stretch: - return length_for_children; - case MeasureMode::Content: - return max_child_length; - default: - UnreachableCode(); - } - }; - - return Size( - calculate_final_length(layout_params->width, size_for_children.width, max_child_size.width), - calculate_final_length(layout_params->height, size_for_children.height, max_child_size.height) - ); - } - - void Control::OnLayout(const Rect& rect) - { - ForeachChild([rect](Control* control) - { - const auto layout_params = control->GetLayoutParams(); - const auto size = control->GetDesiredSize(); - - auto&& calculate_anchor = [](const float anchor, const Alignment alignment, const float layout_length, const float control_length) -> float - { - switch (alignment) - { - case Alignment::Center: - return anchor + (layout_length - control_length) / 2; - case Alignment::Start: - return anchor; - case Alignment::End: - return anchor + layout_length - control_length; - default: - UnreachableCode(); - } - }; - - control->Layout(Rect(Point( - calculate_anchor(rect.left, layout_params->width.alignment, rect.width, size.width), - calculate_anchor(rect.top, layout_params->height.alignment, rect.height, size.height) - ), size)); - }); - } - inline Size ThicknessToSize(const Thickness& thickness) { return Size(thickness.left + thickness.right, thickness.top + thickness.bottom); @@ -654,10 +667,19 @@ namespace cru { return Size(AtLeast0(size.width), AtLeast0(size.height)); } - Size Control::DefaultMeasureWithPadding(const Size& available_size, const Thickness& padding) + Size Control::OnMeasureCore(const Size& available_size) { const auto layout_params = GetLayoutParams(); - const auto padding_size = ThicknessToSize(padding); + + auto border_size = Size::Zero(); + if (is_bordered_) + { + const auto border_width = border_property_->GetStrokeWidth(); + border_size = Size(border_width, border_width); + } + + const auto outer_size = ThicknessToSize(layout_params->padding) + + ThicknessToSize(layout_params->margin) + border_size; if (!layout_params->Validate()) throw std::runtime_error("LayoutParams is not valid. Please check it."); @@ -680,23 +702,14 @@ namespace cru { } }; - Size size_for_children(get_available_length_for_child(layout_params->width, available_size.width), - get_available_length_for_child(layout_params->height, available_size.height)); - - size_for_children = AtLeast0(size_for_children - padding_size); + const auto size_for_children = AtLeast0(Size( + get_available_length_for_child(layout_params->width, available_size.width), + get_available_length_for_child(layout_params->height, available_size.height) + ) - outer_size); - auto max_child_size = Size::Zero(); - ForeachChild([&](Control* control) - { - control->Measure(size_for_children); - const auto&& size = control->GetDesiredSize(); - if (max_child_size.width < size.width) - max_child_size.width = size.width; - if (max_child_size.height < size.height) - max_child_size.height = size.height; - }); + const auto actual_size_for_children = OnMeasureContent(size_for_children); - auto&& calculate_final_length = [](const LayoutSideParams& layout_length, const float length_for_children, const float max_child_length) -> float + auto&& calculate_final_length = [](const LayoutSideParams& layout_length, const float length_for_children, const float actual_length_for_children) -> float { switch (layout_length.mode) { @@ -704,28 +717,54 @@ namespace cru { case MeasureMode::Stretch: return length_for_children; case MeasureMode::Content: - return max_child_length; + return actual_length_for_children; default: UnreachableCode(); } }; return Size( - calculate_final_length(layout_params->width, size_for_children.width, max_child_size.width), - calculate_final_length(layout_params->height, size_for_children.height, max_child_size.height) - ) + padding_size; + calculate_final_length(layout_params->width, size_for_children.width, actual_size_for_children.width), + calculate_final_length(layout_params->height, size_for_children.height, actual_size_for_children.height) + ) + outer_size; } - void Control::DefaultLayoutWithPadding(const Rect& rect, const Thickness& padding) + void Control::OnLayoutCore(const Rect& rect) { - const Rect final_rect( - rect.left + padding.left, - rect.top + padding.top, - rect.width - padding.left - padding.right, - rect.height - padding.top - padding.bottom - ); + const auto layout_params = GetLayoutParams(); - ForeachChild([final_rect](Control* control) + auto border_width = 0.0f; + if (is_bordered_) + { + border_width = border_property_->GetStrokeWidth(); + } + + OnLayoutContent(Rect( + rect.left + layout_params->padding.left + layout_params->margin.right + border_width, + rect.top + layout_params->padding.top + layout_params->margin.top + border_width, + rect.width - layout_params->padding.GetHorizontalTotal() - layout_params->margin.GetHorizontalTotal() + border_width * 2.0f, + rect.height - layout_params->padding.GetVerticalTotal() - layout_params->margin.GetVerticalTotal() + border_width * 2.0f + )); + } + + Size Control::OnMeasureContent(const Size& available_size) + { + auto max_child_size = Size::Zero(); + ForeachChild([&max_child_size, available_size](Control* control) + { + control->Measure(available_size); + const auto&& size = control->GetDesiredSize(); + if (max_child_size.width < size.width) + max_child_size.width = size.width; + if (max_child_size.height < size.height) + max_child_size.height = size.height; + }); + return max_child_size; + } + + void Control::OnLayoutContent(const Rect& rect) + { + ForeachChild([rect](Control* control) { const auto layout_params = control->GetLayoutParams(); const auto size = control->GetDesiredSize(); @@ -746,8 +785,8 @@ namespace cru { }; control->Layout(Rect(Point( - calculate_anchor(final_rect.left, layout_params->width.alignment, final_rect.width, size.width), - calculate_anchor(final_rect.top, layout_params->height.alignment, final_rect.height, size.height) + calculate_anchor(rect.left, layout_params->width.alignment, rect.width, size.width), + calculate_anchor(rect.top, layout_params->height.alignment, rect.height, size.height) ), size)); }); } diff --git a/src/ui/control.h b/src/ui/control.h index 964b61c4..18ca4b69 100644 --- a/src/ui/control.h +++ b/src/ui/control.h @@ -11,6 +11,7 @@ #include "ui_base.h" #include "layout_base.h" #include "events/ui_event.h" +#include "border_property.h" namespace cru { @@ -28,6 +29,16 @@ namespace cru }; + enum class RectRange + { + Content, // content excluding padding, border and margin + Padding, // only including content and padding + HalfBorder, // including content, padding and half border + FullBorder, // including content, padding and full border + Margin // including content, padding, border and margin + }; + + class Control : public Object { friend class Window; @@ -170,6 +181,26 @@ namespace cru return &layout_params_; } + Rect GetRect(RectRange range); + + + //*************** region: border *************** + + BorderProperty::Ptr GetBorderProperty() const + { + return border_property_; + } + + void SetBorderProperty(BorderProperty::Ptr border_property); + + bool IsBordered() const + { + return is_bordered_; + } + + void SetBordered(bool bordered); + + //*************** region: additional properties *************** template <typename T> std::optional<T> GetAdditionalProperty(const String& key) @@ -237,8 +268,11 @@ namespace cru //Invoked when the control is detached to a window. Overrides should invoke base. virtual void OnDetachToWindow(Window* window); - virtual void OnDraw(ID2D1DeviceContext* device_context); + private: + void OnDraw(ID2D1DeviceContext* device_context); + protected: + virtual void OnDrawContent(ID2D1DeviceContext* device_context); // For a event, the window event system will first dispatch event to core functions. // Therefore for particular controls, you should do essential actions in core functions, @@ -306,11 +340,11 @@ namespace cru void RaiseLoseFocusEvent(events::FocusChangeEventArgs& args); //*************** region: layout *************** - virtual Size OnMeasure(const Size& available_size); - virtual void OnLayout(const Rect& rect); + Size OnMeasureCore(const Size& available_size); + void OnLayoutCore(const Rect& rect); - Size DefaultMeasureWithPadding(const Size& available_size, const Thickness& padding); - void DefaultLayoutWithPadding(const Rect& rect, const Thickness& padding); + virtual Size OnMeasureContent(const Size& available_size); + virtual void OnLayoutContent(const Rect& rect); private: // Only for layout manager to use. @@ -358,6 +392,10 @@ namespace cru BasicLayoutParams layout_params_{}; Size desired_size_ = Size::Zero(); + bool is_bordered_ = false; + BorderProperty::Ptr border_property_; + PropertyChangedNotifyObject::PropertyChangedHandlerPtr border_property_changed_listener_; + std::unordered_map<String, std::any> additional_properties_{}; bool is_focus_on_pressed_ = true; @@ -379,5 +417,30 @@ namespace cru control->GetLayoutParams()->height = height; return control; } + + + template <typename TControl, typename... Args> + TControl* CreateWithLayout(const Thickness& padding, const Thickness& margin, Args&&... args) + { + static_assert(std::is_base_of_v<Control, TControl>, "TControl is not a control class."); + TControl* control = TControl::Create(std::forward<Args>(args)...); + control->GetLayoutParams()->padding = padding; + control->GetLayoutParams()->margin = margin; + return control; + } + + template <typename TControl, typename... Args> + TControl* CreateWithLayout(const LayoutSideParams& width, const LayoutSideParams& height, const Thickness& padding, const Thickness& margin, Args&&... args) + { + static_assert(std::is_base_of_v<Control, TControl>, "TControl is not a control class."); + TControl* control = TControl::Create(std::forward<Args>(args)...); + control->GetLayoutParams()->width = width; + control->GetLayoutParams()->height = height; + control->GetLayoutParams()->padding = padding; + control->GetLayoutParams()->margin = margin; + return control; + } + + constexpr std::initializer_list<Control*> ControlList(std::initializer_list<Control*> &&li) { return li; } } } diff --git a/src/ui/controls/border.cpp b/src/ui/controls/border.cpp deleted file mode 100644 index 1caed91d..00000000 --- a/src/ui/controls/border.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include "border.h" - -#include "graph/graph.h" - -namespace cru::ui::controls -{ - using graph::CreateSolidBrush; - - Border::Border() : Control(true), border_delegate_(this) - { - - } - - void Border::SetDrawBorder(const bool draw_border) - { - draw_border_ = draw_border; - Repaint(); - } - - void Border::OnDraw(ID2D1DeviceContext* device_context) - { - if (draw_border_) - { - border_delegate_.Draw(device_context, GetSize()); - } - } - - Size Border::OnMeasure(const Size& available_size) - { - return Control::DefaultMeasureWithPadding(available_size, border_delegate_.GetBorderThickness()); - } - - void Border::OnLayout(const Rect& rect) - { - Control::DefaultLayoutWithPadding(rect, border_delegate_.GetBorderThickness()); - } -} diff --git a/src/ui/controls/border.h b/src/ui/controls/border.h deleted file mode 100644 index 7880e690..00000000 --- a/src/ui/controls/border.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -#include <initializer_list> - -#include "ui/control.h" -#include "border_delegate.h" - -namespace cru::ui::controls -{ - class Border : public Control - { - public: - static Border* Create(const std::initializer_list<Control*>& children = std::initializer_list<Control*>()) - { - const auto border = new Border(); - for (const auto control : children) - border->AddChild(control); - return border; - } - - protected: - Border(); - - public: - Border(const Border& other) = delete; - Border(Border&& other) = delete; - Border& operator=(const Border& other) = delete; - Border& operator=(Border&& other) = delete; - ~Border() override = default; - - bool IsDrawBorder() const - { - return draw_border_; - } - - void SetDrawBorder(bool draw_border); - - BorderProperty::Ptr GetBorderProperty() const - { - return border_delegate_.GetBorderProperty(); - } - - protected: - void OnDraw(ID2D1DeviceContext* device_context) override; - - Size OnMeasure(const Size& available_size) override; - void OnLayout(const Rect& rect) override; - - private: - bool draw_border_ = true; - - BorderDelegate border_delegate_; - }; -} diff --git a/src/ui/controls/border_delegate.cpp b/src/ui/controls/border_delegate.cpp deleted file mode 100644 index c8855e0f..00000000 --- a/src/ui/controls/border_delegate.cpp +++ /dev/null @@ -1,97 +0,0 @@ -#include "border_delegate.h" -#include "graph/graph.h" - -namespace cru::ui::controls -{ - BorderProperty::Ptr BorderProperty::Create() - { - return std::make_shared<BorderProperty>(graph::CreateSolidBrush(D2D1::ColorF(D2D1::ColorF::Black))); - } - - BorderProperty::BorderProperty(Microsoft::WRL::ComPtr<ID2D1Brush> brush) - : brush_(std::move(brush)) - { - - } - - void BorderProperty::SetBrush(Microsoft::WRL::ComPtr<ID2D1Brush> brush) - { - brush_ = std::move(brush); - RaisePropertyChangedEvent(brush_property_name); - } - - void BorderProperty::SetWidth(const float width) - { - width_ = width; - RaisePropertyChangedEvent(width_property_name); - } - - void BorderProperty::SetStrokeStyle(Microsoft::WRL::ComPtr<ID2D1StrokeStyle> stroke_style) - { - stroke_style_ = std::move(stroke_style); - RaisePropertyChangedEvent(stroke_style_property_name); - } - - void BorderProperty::SetRadiusX(const float radius_x) - { - radius_x_ = radius_x; - RaisePropertyChangedEvent(radius_x_property_name); - } - - void BorderProperty::SetRadiusY(const float radius_y) - { - radius_y_ = radius_y; - RaisePropertyChangedEvent(radius_y_property_name); - } - - BorderDelegate::BorderDelegate(Control* control) - : BorderDelegate(control, BorderProperty::Create()) - { - - } - - BorderDelegate::BorderDelegate(Control* control, std::shared_ptr<BorderProperty> border_property)\ - : control_(control), - border_property_changed_listener_(CreateFunctionPtr<void(String)>([this](String property_name) - { - if (property_name == BorderProperty::width_property_name) - control_->InvalidateLayout(); - control_->Repaint(); - })) - { - border_property_ = std::move(border_property); - border_property_->AddPropertyChangedListener(border_property_changed_listener_); - } - - BorderDelegate::~BorderDelegate() - { - border_property_->RemovePropertyChangedListener(border_property_changed_listener_); - } - - void BorderDelegate::SetBorderProperty(std::shared_ptr<BorderProperty> border_property) - { - border_property_->RemovePropertyChangedListener(border_property_changed_listener_); - border_property_ = std::move(border_property); - border_property_->AddPropertyChangedListener(border_property_changed_listener_); - control_->Repaint(); - } - - void BorderDelegate::Draw(ID2D1DeviceContext* device_context, const Size& size) const - { - device_context->DrawRoundedRectangle( - D2D1::RoundedRect( - D2D1::RectF( - border_property_->GetWidth() / 2.0f, - border_property_->GetWidth() / 2.0f, - size.width - border_property_->GetWidth(), - size.height - border_property_->GetWidth() - ), - border_property_->GetRadiusX(), - border_property_->GetRadiusY() - ), - border_property_->GetBrush().Get(), - border_property_->GetWidth(), - border_property_->GetStrokeStyle().Get() - ); - } -} diff --git a/src/ui/controls/border_delegate.h b/src/ui/controls/border_delegate.h deleted file mode 100644 index 6d8663e9..00000000 --- a/src/ui/controls/border_delegate.h +++ /dev/null @@ -1,101 +0,0 @@ -#pragma once - -#include "ui/control.h" - -namespace cru::ui::controls -{ - class BorderProperty : public PropertyChangedNotifyObject - { - public: - using Ptr = std::shared_ptr<BorderProperty>; - static Ptr Create(); - - constexpr static auto brush_property_name = L"Brush"; - constexpr static auto width_property_name = L"Width"; - constexpr static auto stroke_style_property_name = L"StrokeStyle"; - constexpr static auto radius_x_property_name = L"RadiusX"; - constexpr static auto radius_y_property_name = L"RadiusY"; - - BorderProperty() = default; - explicit BorderProperty(Microsoft::WRL::ComPtr<ID2D1Brush> brush); - BorderProperty(const BorderProperty& other) = delete; - BorderProperty(BorderProperty&& other) = delete; - BorderProperty& operator=(const BorderProperty& other) = delete; - BorderProperty& operator=(BorderProperty&& other) = delete; - ~BorderProperty() override = default; - - - Microsoft::WRL::ComPtr<ID2D1Brush> GetBrush() const - { - return brush_; - } - - float GetWidth() const - { - return width_; - } - - Microsoft::WRL::ComPtr<ID2D1StrokeStyle> GetStrokeStyle() const - { - return stroke_style_; - } - - float GetRadiusX() const - { - return radius_x_; - } - - float GetRadiusY() const - { - return radius_y_; - } - - void SetBrush(Microsoft::WRL::ComPtr<ID2D1Brush> brush); - void SetWidth(float width); - void SetStrokeStyle(Microsoft::WRL::ComPtr<ID2D1StrokeStyle> stroke_style); - void SetRadiusX(float radius_x); - void SetRadiusY(float radius_y); - - private: - Microsoft::WRL::ComPtr<ID2D1Brush> brush_ = nullptr; - float width_ = 1.0f; - Microsoft::WRL::ComPtr<ID2D1StrokeStyle> stroke_style_ = nullptr; - float radius_x_ = 0.0f; - float radius_y_ = 0.0f; - }; - - - // BorderDelegate is a delegate for border painting and layout. - // It must bind a control and not change the binding. - // But multiple BorderDelegate may share a common BorderProperty. - class BorderDelegate : public Object - { - public: - explicit BorderDelegate(Control* control); - BorderDelegate(Control* control, std::shared_ptr<BorderProperty> border_property); - BorderDelegate(const BorderDelegate& other) = delete; - BorderDelegate(BorderDelegate&& other) = delete; - BorderDelegate& operator=(const BorderDelegate& other) = delete; - BorderDelegate& operator=(BorderDelegate&& other) = delete; - ~BorderDelegate() override; - - std::shared_ptr<BorderProperty> GetBorderProperty() const - { - return border_property_; - } - - void SetBorderProperty(std::shared_ptr<BorderProperty> border_property); - - void Draw(ID2D1DeviceContext* device_context, const Size& size) const; - - Thickness GetBorderThickness() const - { - return Thickness(border_property_->GetWidth()); - } - - private: - Control* control_; - std::shared_ptr<BorderProperty> border_property_; - FunctionPtr<void(String)> border_property_changed_listener_; - }; -} diff --git a/src/ui/controls/button.cpp b/src/ui/controls/button.cpp index 2ecc9f14..db0b71c2 100644 --- a/src/ui/controls/button.cpp +++ b/src/ui/controls/button.cpp @@ -8,46 +8,31 @@ namespace cru::ui::controls Button::Button() : Control(true) { - normal_border_border_ = BorderProperty::Create(); - normal_border_border_->SetBrush(CreateSolidBrush(D2D1::ColorF(D2D1::ColorF::RoyalBlue))); - normal_border_border_->SetWidth(2); - normal_border_border_->SetRadiusX(6); - normal_border_border_->SetRadiusY(6); - - pressed_border_border_ = BorderProperty::Create(); - pressed_border_border_->SetBrush(CreateSolidBrush(D2D1::ColorF(D2D1::ColorF::Blue))); - pressed_border_border_->SetWidth(2); - pressed_border_border_->SetRadiusX(6); - pressed_border_border_->SetRadiusY(6); - - border_delegate_ = std::make_unique<BorderDelegate>(this, normal_border_border_); - } - - void Button::OnDraw(ID2D1DeviceContext* device_context) - { - Control::OnDraw(device_context); - border_delegate_->Draw(device_context, GetSize()); - } - - Size Button::OnMeasure(const Size& available_size) - { - return Control::DefaultMeasureWithPadding(available_size, border_delegate_->GetBorderThickness()); - } - - void Button::OnLayout(const Rect& rect) - { - Control::DefaultLayoutWithPadding(rect, border_delegate_->GetBorderThickness()); + normal_border_ = BorderProperty::Create(); + normal_border_->SetBrush(CreateSolidBrush(D2D1::ColorF(D2D1::ColorF::RoyalBlue))); + normal_border_->SetWidth(2); + normal_border_->SetRadiusX(6); + normal_border_->SetRadiusY(6); + + pressed_border_ = BorderProperty::Create(); + pressed_border_->SetBrush(CreateSolidBrush(D2D1::ColorF(D2D1::ColorF::Blue))); + pressed_border_->SetWidth(2); + pressed_border_->SetRadiusX(6); + pressed_border_->SetRadiusY(6); + + SetBordered(true); + SetBorderProperty(normal_border_); } void Button::OnMouseClickBegin(MouseButton button) { - border_delegate_->SetBorderProperty(pressed_border_border_); + SetBorderProperty(pressed_border_); Repaint(); } void Button::OnMouseClickEnd(MouseButton button) { - border_delegate_->SetBorderProperty(normal_border_border_); + SetBorderProperty(normal_border_); Repaint(); } } diff --git a/src/ui/controls/button.h b/src/ui/controls/button.h index 4b57a5a3..011f97d2 100644 --- a/src/ui/controls/button.h +++ b/src/ui/controls/button.h @@ -3,7 +3,6 @@ #include <initializer_list> #include "ui/control.h" -#include "border_delegate.h" namespace cru::ui::controls { @@ -29,18 +28,11 @@ namespace cru::ui::controls ~Button() override = default; protected: - void OnDraw(ID2D1DeviceContext* device_context) override; - - Size OnMeasure(const Size& available_size) override; - void OnLayout(const Rect& rect) override; - void OnMouseClickBegin(MouseButton button) override final; void OnMouseClickEnd(MouseButton button) override final; private: - std::unique_ptr<BorderDelegate> border_delegate_; - - BorderProperty::Ptr normal_border_border_; - BorderProperty::Ptr pressed_border_border_; + BorderProperty::Ptr normal_border_; + BorderProperty::Ptr pressed_border_; }; } diff --git a/src/ui/controls/linear_layout.cpp b/src/ui/controls/linear_layout.cpp index 8f537ea8..681ed087 100644 --- a/src/ui/controls/linear_layout.cpp +++ b/src/ui/controls/linear_layout.cpp @@ -18,37 +18,9 @@ namespace cru::ui::controls return Size(AtLeast0(size.width), AtLeast0(size.height)); } - Size LinearLayout::OnMeasure(const Size& available_size) + Size LinearLayout::OnMeasureContent(const Size& available_size) { - const auto layout_params = GetLayoutParams(); - - if (!layout_params->Validate()) - throw std::runtime_error("LayoutParams is not valid. Please check it."); - - auto&& get_available_length_for_child = [](const LayoutSideParams& layout_length, const float available_length) -> float - { - switch (layout_length.mode) - { - case MeasureMode::Exactly: - { - return std::min(layout_length.length, available_length); - } - case MeasureMode::Stretch: - case MeasureMode::Content: - { - return available_length; - } - default: - UnreachableCode(); - } - }; - - Size total_available_size_for_children( - get_available_length_for_child(layout_params->width, available_size.width), - get_available_length_for_child(layout_params->height, available_size.height) - ); - - auto rest_available_size_for_children = total_available_size_for_children; + auto rest_available_size_for_children = available_size; float secondary_side_child_max_length = 0; @@ -107,7 +79,7 @@ namespace cru::ui::controls } } - auto actual_size_for_children = total_available_size_for_children; + auto actual_size_for_children = available_size; if (orientation_ == Orientation::Horizontal) { actual_size_for_children.width -= rest_available_size_for_children.width; @@ -119,36 +91,19 @@ namespace cru::ui::controls actual_size_for_children.height -= rest_available_size_for_children.height; } - auto&& calculate_final_length = [](const LayoutSideParams& layout_length, const float length_for_children, const float max_child_length) -> float - { - switch (layout_length.mode) - { - case MeasureMode::Exactly: - case MeasureMode::Stretch: - return length_for_children; - case MeasureMode::Content: - return max_child_length; - default: - UnreachableCode(); - } - }; - - return Size( - calculate_final_length(layout_params->width, total_available_size_for_children.width, actual_size_for_children.width), - calculate_final_length(layout_params->height, total_available_size_for_children.height, actual_size_for_children.height) - ); + return actual_size_for_children; } - void LinearLayout::OnLayout(const Rect& rect) + void LinearLayout::OnLayoutContent(const Rect& rect) { - float current_anchor_length = 0; - ForeachChild([this, ¤t_anchor_length, rect](Control* control) + float current_main_side_anchor = 0; + ForeachChild([this, ¤t_main_side_anchor, rect](Control* control) { const auto layout_params = control->GetLayoutParams(); - const auto size = control->GetDesiredSize(); + const auto size = control->GetSize(); const auto alignment = orientation_ == Orientation::Horizontal ? layout_params->height.alignment : layout_params->width.alignment; - auto&& calculate_anchor = [alignment](const float layout_length, const float control_length) -> float + auto&& calculate_secondary_side_anchor = [alignment](const float layout_length, const float control_length) -> float { switch (alignment) { @@ -163,15 +118,20 @@ namespace cru::ui::controls } }; + auto&& calculate_rect = [rect, size](const float anchor_left, const float anchor_top) + { + return Rect(Point(rect.left + anchor_left, rect.top + anchor_top), size); + }; + if (orientation_ == Orientation::Horizontal) { - control->Layout(Rect(Point(current_anchor_length, calculate_anchor(rect.height, size.height)), size)); - current_anchor_length += size.width; + control->Layout(calculate_rect(current_main_side_anchor, calculate_secondary_side_anchor(rect.height, size.height))); + current_main_side_anchor += size.width; } else { - control->Layout(Rect(Point(calculate_anchor(rect.width, size.width), current_anchor_length), size)); - current_anchor_length += size.height; + control->Layout(calculate_rect(calculate_secondary_side_anchor(rect.width, size.width), current_main_side_anchor)); + current_main_side_anchor += size.height; } }); } diff --git a/src/ui/controls/linear_layout.h b/src/ui/controls/linear_layout.h index 369824d4..997f28ea 100644 --- a/src/ui/controls/linear_layout.h +++ b/src/ui/controls/linear_layout.h @@ -32,8 +32,8 @@ namespace cru::ui::controls ~LinearLayout() override = default; protected: - Size OnMeasure(const Size& available_size) override; - void OnLayout(const Rect& rect) override; + Size OnMeasureContent(const Size& available_size) override; + void OnLayoutContent(const Rect& rect) override; private: Orientation orientation_; diff --git a/src/ui/controls/margin_container.cpp b/src/ui/controls/margin_container.cpp deleted file mode 100644 index 12dde025..00000000 --- a/src/ui/controls/margin_container.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "margin_container.h" - -namespace cru::ui::controls -{ - inline float AtLeast0(const float value) - { - return value < 0 ? 0 : value; - } - - inline Size AtLeast0(const Size& size) - { - return Size(AtLeast0(size.width), AtLeast0(size.height)); - } - - MarginContainer::MarginContainer(const Thickness& margin) - : Control(true), margin_(margin) - { - } - - void MarginContainer::SetMargin(const Thickness& margin) - { - margin_ = margin; - InvalidateLayout(); - } - - Size MarginContainer::OnMeasure(const Size& available_size) - { - return DefaultMeasureWithPadding(available_size, margin_); - } - - void MarginContainer::OnLayout(const Rect& rect) - { - DefaultLayoutWithPadding(rect, margin_); - } -} diff --git a/src/ui/controls/margin_container.h b/src/ui/controls/margin_container.h deleted file mode 100644 index 0eafc40e..00000000 --- a/src/ui/controls/margin_container.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include <initializer_list> - -#include "ui/control.h" - -namespace cru::ui::controls -{ - class MarginContainer : public Control - { - public: - static MarginContainer* Create(const Thickness& margin = Thickness::Zero(), const std::initializer_list<Control*>& children = std::initializer_list<Control*>()) - { - const auto margin_container = new MarginContainer(margin); - for (const auto control : children) - margin_container->AddChild(control); - return margin_container; - } - - protected: - explicit MarginContainer(const Thickness& margin); - - public: - MarginContainer(const MarginContainer& other) = delete; - MarginContainer(MarginContainer&& other) = delete; - MarginContainer& operator=(const MarginContainer& other) = delete; - MarginContainer& operator=(MarginContainer&& other) = delete; - ~MarginContainer() override = default; - - Thickness GetMargin() const - { - return margin_; - } - - void SetMargin(const Thickness& margin); - - protected: - Size OnMeasure(const Size& available_size) override; - void OnLayout(const Rect& rect) override; - - private: - Thickness margin_; - }; -} diff --git a/src/ui/controls/text_box.cpp b/src/ui/controls/text_box.cpp index 0d65f1ad..30b9069a 100644 --- a/src/ui/controls/text_box.cpp +++ b/src/ui/controls/text_box.cpp @@ -27,27 +27,22 @@ namespace cru::ui::controls Repaint(); }); - border_delegate_ = std::make_unique<BorderDelegate>(this); + SetBordered(true); } TextBox::~TextBox() = default; - void TextBox::OnDraw(ID2D1DeviceContext* device_context) + void TextBox::OnDrawContent(ID2D1DeviceContext* device_context) { - border_delegate_->Draw(device_context, GetSize()); - const auto border_thickness = border_delegate_->GetBorderThickness(); - graph::WithTransform(device_context, D2D1::Matrix3x2F::Translation(border_thickness.left, border_thickness.top), [this](ID2D1DeviceContext* device_context) + TextControl::OnDrawContent(device_context); + if (is_caret_show_) { - TextControl::OnDraw(device_context); - if (is_caret_show_) - { - const auto caret_half_width = Application::GetInstance()->GetCaretInfo().half_caret_width; - FLOAT x, y; - DWRITE_HIT_TEST_METRICS metrics{}; - ThrowIfFailed(text_layout_->HitTestTextPosition(caret_position_, FALSE, &x, &y, &metrics)); - device_context->FillRectangle(D2D1::RectF(metrics.left - caret_half_width, metrics.top, metrics.left + caret_half_width, metrics.top + metrics.height), caret_brush_.Get()); - } - }); + const auto caret_half_width = Application::GetInstance()->GetCaretInfo().half_caret_width; + FLOAT x, y; + DWRITE_HIT_TEST_METRICS metrics{}; + ThrowIfFailed(text_layout_->HitTestTextPosition(caret_position_, FALSE, &x, &y, &metrics)); + device_context->FillRectangle(D2D1::RectF(metrics.left - caret_half_width, metrics.top, metrics.left + caret_half_width, metrics.top + metrics.height), caret_brush_.Get()); + } } void TextBox::OnGetFocusCore(events::FocusChangeEventArgs& args) @@ -138,11 +133,6 @@ namespace cru::ui::controls } } - Size TextBox::OnMeasure(const Size& available_size) - { - return TextMeasureWithPadding(available_size, border_delegate_->GetBorderThickness()); - } - void TextBox::RequestChangeCaretPosition(const unsigned position) { caret_position_ = position; diff --git a/src/ui/controls/text_box.h b/src/ui/controls/text_box.h index a6e4566d..540ac758 100644 --- a/src/ui/controls/text_box.h +++ b/src/ui/controls/text_box.h @@ -2,7 +2,6 @@ #include "text_control.h" #include "timer.h" -#include "border_delegate.h" namespace cru::ui::controls { @@ -29,7 +28,7 @@ namespace cru::ui::controls ~TextBox() override; protected: - void OnDraw(ID2D1DeviceContext* device_context) override; + void OnDrawContent(ID2D1DeviceContext* device_context) override; void OnGetFocusCore(events::FocusChangeEventArgs& args) override final; void OnLoseFocusCore(events::FocusChangeEventArgs& args) override final; @@ -37,8 +36,6 @@ namespace cru::ui::controls void OnKeyDownCore(events::KeyEventArgs& args) override final; void OnCharCore(events::CharEventArgs& args) override final; - Size OnMeasure(const Size& available_size) override; - void RequestChangeCaretPosition(unsigned position) override; private: @@ -47,7 +44,5 @@ namespace cru::ui::controls ActionPtr caret_action_; Microsoft::WRL::ComPtr<ID2D1Brush> caret_brush_; bool is_caret_show_ = false; - - std::unique_ptr<BorderDelegate> border_delegate_; }; } diff --git a/src/ui/controls/text_control.cpp b/src/ui/controls/text_control.cpp index 0b730500..da0113f3 100644 --- a/src/ui/controls/text_control.cpp +++ b/src/ui/controls/text_control.cpp @@ -130,9 +130,9 @@ namespace cru::ui::controls } } } - void TextControl::OnDraw(ID2D1DeviceContext* device_context) + void TextControl::OnDrawContent(ID2D1DeviceContext* device_context) { - Control::OnDraw(device_context); + Control::OnDrawContent(device_context); DrawSelectionRect(device_context, text_layout_.Get(), selection_brush_.Get(), selected_range_); device_context->DrawTextLayout(D2D1::Point2F(), text_layout_.Get(), brush_.Get()); } @@ -207,103 +207,10 @@ namespace cru::ui::controls } - Size TextControl::OnMeasure(const Size& available_size) + Size TextControl::OnMeasureContent(const Size& available_size) { - const auto layout_params = GetLayoutParams(); - - auto&& get_measure_length = [](const LayoutSideParams& layout_length, const float available_length) -> float - { - switch (layout_length.mode) - { - case MeasureMode::Exactly: - { - return std::min(layout_length.length, available_length); - } - case MeasureMode::Stretch: - case MeasureMode::Content: - { - return available_length; - } - default: - UnreachableCode(); - } - }; - - const Size measure_size(get_measure_length(layout_params->width, available_size.width), - get_measure_length(layout_params->height, available_size.height)); - - ThrowIfFailed(text_layout_->SetMaxWidth(measure_size.width)); - ThrowIfFailed(text_layout_->SetMaxHeight(measure_size.height)); - - DWRITE_TEXT_METRICS metrics{}; - - ThrowIfFailed(text_layout_->GetMetrics(&metrics)); - - const Size measure_result(metrics.width, metrics.height); - - auto&& calculate_final_length = [](const LayoutSideParams& layout_length, const float measure_length, const float measure_result_length) -> float - { - if ((layout_length.mode == MeasureMode::Stretch || - layout_length.mode == MeasureMode::Exactly) - && measure_result_length < measure_length) - return measure_length; - else - return measure_result_length; - }; - - const Size result_size( - calculate_final_length(layout_params->width, measure_size.width, measure_result.width), - calculate_final_length(layout_params->height, measure_size.height, measure_result.height) - ); - - return result_size; - } - - inline Size ThicknessToSize(const Thickness& thickness) - { - return Size(thickness.left + thickness.right, thickness.top + thickness.bottom); - } - - inline float AtLeast0(const float value) - { - return value < 0 ? 0 : value; - } - - inline Size AtLeast0(const Size& size) - { - return Size(AtLeast0(size.width), AtLeast0(size.height)); - } - - Size TextControl::TextMeasureWithPadding(const Size& available_size, const Thickness& padding) - { - const auto layout_params = GetLayoutParams(); - const auto padding_size = ThicknessToSize(padding); - - auto&& get_measure_length = [](const LayoutSideParams& layout_length, const float available_length) -> float - { - switch (layout_length.mode) - { - case MeasureMode::Exactly: - { - return std::min(layout_length.length, available_length); - } - case MeasureMode::Stretch: - case MeasureMode::Content: - { - return available_length; - } - default: - UnreachableCode(); - } - }; - - Size measure_size(get_measure_length(layout_params->width, available_size.width), - get_measure_length(layout_params->height, available_size.height)); - - measure_size = AtLeast0(measure_size - padding_size); - - ThrowIfFailed(text_layout_->SetMaxWidth(measure_size.width)); - ThrowIfFailed(text_layout_->SetMaxHeight(measure_size.height)); + ThrowIfFailed(text_layout_->SetMaxWidth(available_size.width)); + ThrowIfFailed(text_layout_->SetMaxHeight(available_size.height)); DWRITE_TEXT_METRICS metrics{}; @@ -311,22 +218,7 @@ namespace cru::ui::controls const Size measure_result(metrics.width, metrics.height); - auto&& calculate_final_length = [](const LayoutSideParams& layout_length, const float measure_length, const float measure_result_length) -> float - { - if ((layout_length.mode == MeasureMode::Stretch || - layout_length.mode == MeasureMode::Exactly) - && measure_result_length < measure_length) - return measure_length; - else - return measure_result_length; - }; - - const Size result_size( - calculate_final_length(layout_params->width, measure_size.width, measure_result.width), - calculate_final_length(layout_params->height, measure_size.height, measure_result.height) - ); - - return result_size + padding_size; + return measure_result; } void TextControl::RequestChangeCaretPosition(unsigned position) diff --git a/src/ui/controls/text_control.h b/src/ui/controls/text_control.h index 2ead7c54..01bb5565 100644 --- a/src/ui/controls/text_control.h +++ b/src/ui/controls/text_control.h @@ -68,7 +68,7 @@ namespace cru::ui::controls protected: void OnSizeChangedCore(events::SizeChangedEventArgs& args) override final; - void OnDraw(ID2D1DeviceContext* device_context) override; + void OnDrawContent(ID2D1DeviceContext* device_context) override; void OnMouseDownCore(events::MouseButtonEventArgs& args) override final; void OnMouseMoveCore(events::MouseEventArgs& args) override final; @@ -76,9 +76,8 @@ namespace cru::ui::controls void OnLoseFocusCore(events::FocusChangeEventArgs& args) override; - Size OnMeasure(const Size& available_size) override; + Size OnMeasureContent(const Size& available_size) override; - Size TextMeasureWithPadding(const Size& available_size, const Thickness& padding); virtual void RequestChangeCaretPosition(unsigned position); diff --git a/src/ui/controls/toggle_button.cpp b/src/ui/controls/toggle_button.cpp index 68bd0fc9..3cd5d3ef 100644 --- a/src/ui/controls/toggle_button.cpp +++ b/src/ui/controls/toggle_button.cpp @@ -1,4 +1,4 @@ -#include "toggle_button.h" +#include "toggle_button.h" #include <fmt/format.h> @@ -83,9 +83,9 @@ namespace cru::ui::controls } - void ToggleButton::OnDraw(ID2D1DeviceContext* device_context) + void ToggleButton::OnDrawContent(ID2D1DeviceContext* device_context) { - Control::OnDraw(device_context); + Control::OnDrawContent(device_context); const auto size = GetSize(); graph::WithTransform(device_context, D2D1::Matrix3x2F::Translation(size.width / 2, size.height / 2), [this](ID2D1DeviceContext* device_context) { @@ -108,34 +108,11 @@ namespace cru::ui::controls Toggle(); } - Size ToggleButton::OnMeasure(const Size& available_size) + Size ToggleButton::OnMeasureContent(const Size& available_size) { - const auto layout_params = GetLayoutParams(); - - auto&& get_measure_length = [](const LayoutSideParams& layout_length, const float available_length, const float fix_length) -> float - { - switch (layout_length.mode) - { - case MeasureMode::Exactly: - { - return std::max(std::min(layout_length.length, available_length), fix_length); - } - case MeasureMode::Stretch: - { - return std::max(available_length, fix_length); - } - case MeasureMode::Content: - { - return fix_length; - } - default: - UnreachableCode(); - } - }; - const Size result_size( - get_measure_length(layout_params->width, available_size.width, half_width * 2 + stroke_width), - get_measure_length(layout_params->height, available_size.height, half_height * 2 + stroke_width) + half_width * 2 + stroke_width, + half_height * 2 + stroke_width ); return result_size; diff --git a/src/ui/controls/toggle_button.h b/src/ui/controls/toggle_button.h index d496f21a..90bbd49b 100644 --- a/src/ui/controls/toggle_button.h +++ b/src/ui/controls/toggle_button.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include "ui/control.h" @@ -40,11 +40,11 @@ namespace cru::ui::controls virtual void OnToggle(events::ToggleEventArgs& args); protected: - void OnDraw(ID2D1DeviceContext* device_context) override; + void OnDrawContent(ID2D1DeviceContext* device_context) override; void OnMouseClickCore(events::MouseButtonEventArgs& args) override; - Size OnMeasure(const Size& available_size) override; + Size OnMeasureContent(const Size& available_size) override; private: void RaiseToggleEvent(bool new_state); diff --git a/src/ui/layout_base.h b/src/ui/layout_base.h index 126437cd..e1759da2 100644 --- a/src/ui/layout_base.h +++ b/src/ui/layout_base.h @@ -1,6 +1,5 @@ #pragma once -#include "system_headers.h" #include <unordered_set> #include "base.h" @@ -27,6 +26,40 @@ namespace cru Stretch }; + struct Thickness + { + constexpr static Thickness Zero() + { + return Thickness(0); + } + + constexpr Thickness() : Thickness(0) { } + + constexpr explicit Thickness(const float width) + : left(width), top(width), right(width), bottom(width) { } + + constexpr explicit Thickness(const float horizontal, const float vertical) + : left(horizontal), top(vertical), right(horizontal), bottom(vertical) { } + + constexpr Thickness(const float left, const float top, const float right, const float bottom) + : left(left), top(top), right(right), bottom(bottom) { } + + float GetHorizontalTotal() const + { + return left + right; + } + + float GetVerticalTotal() const + { + return top + bottom; + } + + float left; + float top; + float right; + float bottom; + }; + struct LayoutSideParams final { constexpr static LayoutSideParams Exactly(const float length, const Alignment alignment = Alignment::Center) @@ -54,14 +87,7 @@ namespace cru constexpr bool Validate() const { - if (mode == MeasureMode::Exactly && length < 0.0) - { -#ifdef CRU_DEBUG - ::OutputDebugStringW(L"LayoutSideParams validation error: mode is Exactly but length is less than 0.\n"); -#endif - return false; - } - return true; + return !(mode == MeasureMode::Exactly && length < 0.0); } float length = 0.0; @@ -80,25 +106,13 @@ namespace cru bool Validate() const { - if (!width.Validate()) - { -#ifdef CRU_DEBUG - ::OutputDebugStringW(L"Width(LayoutSideParams) is not valid."); -#endif - return false; - } - if (!height.Validate()) - { -#ifdef CRU_DEBUG - ::OutputDebugStringW(L"Height(LayoutSideParams) is not valid."); -#endif - return false; - } - return true; + return width.Validate() && height.Validate(); } LayoutSideParams width; LayoutSideParams height; + Thickness padding; + Thickness margin; }; diff --git a/src/ui/ui_base.h b/src/ui/ui_base.h index 43f3c498..51ae4084 100644 --- a/src/ui/ui_base.h +++ b/src/ui/ui_base.h @@ -53,6 +53,16 @@ namespace cru return Size(left.width - right.width, left.height - right.height); } + constexpr bool operator==(const Size& left, const Size& right) + { + return left.width == right.width && left.height == right.height; + } + + constexpr bool operator!=(const Size& left, const Size& right) + { + return !(left == right); + } + struct Rect { constexpr Rect() = default; @@ -106,31 +116,6 @@ namespace cru float height = 0.0f; }; - struct Thickness - { - constexpr static Thickness Zero() - { - return Thickness(0); - } - - constexpr Thickness() : Thickness(0) { } - - constexpr explicit Thickness(const float width) - : left(width), top(width), right(width), bottom(width) { } - - constexpr explicit Thickness(const float horizontal, const float vertical) - : left(horizontal), top(vertical), right(horizontal), bottom(vertical) { } - - constexpr Thickness(const float left, const float top, const float right, const float bottom) - : left(left), top(top), right(right), bottom(bottom) { } - - - float left; - float top; - float right; - float bottom; - }; - enum class MouseButton { Left, diff --git a/src/ui/window.cpp b/src/ui/window.cpp index 39528550..5e0dd694 100644 --- a/src/ui/window.cpp +++ b/src/ui/window.cpp @@ -354,8 +354,8 @@ namespace cru void Window::Relayout() { - OnMeasure(GetSize()); - OnLayout(Rect(Point::Zero(), GetSize())); + OnMeasureContent(GetSize()); + OnLayoutContent(Rect(Point::Zero(), GetSize())); } void Window::RefreshControlList() { @@ -462,7 +462,7 @@ namespace cru return ::PeekMessageW(&msg, hwnd_, message, message, PM_NOREMOVE) != 0; } - Size Window::OnMeasure(const Size& available_size) + Size Window::OnMeasureContent(const Size& available_size) { ForeachChild([available_size](Control* control) { diff --git a/src/ui/window.h b/src/ui/window.h index 4d0a07bc..b4433e85 100644 --- a/src/ui/window.h +++ b/src/ui/window.h @@ -211,7 +211,7 @@ namespace cru { //*************** region: layout *************** - Size OnMeasure(const Size& available_size) override; + Size OnMeasureContent(const Size& available_size) override; //*************** region: native messages *************** |