From 88765aab936724cb01fa2ffd86d65181182a1cd2 Mon Sep 17 00:00:00 2001 From: crupest Date: Sun, 30 Sep 2018 20:38:10 +0800 Subject: Create border delegate. --- src/ui/controls/border_delegate.cpp | 85 +++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 src/ui/controls/border_delegate.cpp (limited to 'src/ui/controls/border_delegate.cpp') diff --git a/src/ui/controls/border_delegate.cpp b/src/ui/controls/border_delegate.cpp new file mode 100644 index 00000000..4e841a8c --- /dev/null +++ b/src/ui/controls/border_delegate.cpp @@ -0,0 +1,85 @@ +#include "border_delegate.h" + +namespace cru::ui::controls +{ + void BorderProperty::SetBrush(Microsoft::WRL::ComPtr 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 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, std::make_shared()) + { + + } + + BorderDelegate::BorderDelegate(Control* control, std::shared_ptr border_property)\ + : control_(control), + border_property_changed_listener_(CreateFunctionPtr([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_); + } + + void BorderDelegate::SetBorderProperty(std::shared_ptr border_property) + { + border_property_->RemovePropertyChangedListener(border_property_changed_listener_); + border_property_ = std::move(border_property); + border_property_->AddPropertyChangedListener(border_property_changed_listener_); + } + + void BorderDelegate::Draw(ID2D1DeviceContext* device_context, const Size& size) const + { + device_context->DrawRoundedRectangle( + D2D1::RoundedRect(D2D1::RectF(0.0f, 0.0f, size.width, size.height), + border_property_->GetRadiusX(), + border_property_->GetRadiusY() + ), + border_property_->GetBrush().Get(), + border_property_->GetWidth(), + border_property_->GetStrokeStyle().Get() + ); + } + + Size BorderDelegate::GetBorderSize() const + { + const auto width = border_property_->GetWidth(); + return Size(width * 2, width * 2); + } + + Rect BorderDelegate::CoerceLayoutRect(const Rect& rect) const + { + const auto width = border_property_->GetWidth(); + return Rect(rect.left + width, rect.top + width, rect.width - width * 2, rect.height - width * 2); + } +} -- cgit v1.2.3 From c9a423ef94f684ff21e79526f77f8ddc31a2100d Mon Sep 17 00:00:00 2001 From: crupest Date: Mon, 1 Oct 2018 23:20:39 +0800 Subject: Add comment for BorderDelegate and add destructor for it. --- src/ui/controls/border_delegate.cpp | 5 +++++ src/ui/controls/border_delegate.h | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'src/ui/controls/border_delegate.cpp') diff --git a/src/ui/controls/border_delegate.cpp b/src/ui/controls/border_delegate.cpp index 4e841a8c..4031d787 100644 --- a/src/ui/controls/border_delegate.cpp +++ b/src/ui/controls/border_delegate.cpp @@ -51,6 +51,11 @@ namespace cru::ui::controls border_property_->AddPropertyChangedListener(border_property_changed_listener_); } + BorderDelegate::~BorderDelegate() + { + border_property_->RemovePropertyChangedListener(border_property_changed_listener_); + } + void BorderDelegate::SetBorderProperty(std::shared_ptr border_property) { border_property_->RemovePropertyChangedListener(border_property_changed_listener_); diff --git a/src/ui/controls/border_delegate.h b/src/ui/controls/border_delegate.h index f22b303f..12c5444a 100644 --- a/src/ui/controls/border_delegate.h +++ b/src/ui/controls/border_delegate.h @@ -60,6 +60,10 @@ namespace cru::ui::controls 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: @@ -69,7 +73,7 @@ namespace cru::ui::controls BorderDelegate(BorderDelegate&& other) = delete; BorderDelegate& operator=(const BorderDelegate& other) = delete; BorderDelegate& operator=(BorderDelegate&& other) = delete; - ~BorderDelegate() override = default; + ~BorderDelegate() override; std::shared_ptr GetBorderProperty() const { -- cgit v1.2.3 From 3b7e3d0ca26b526c8c4e6fc335085ac28d63bbab Mon Sep 17 00:00:00 2001 From: crupest Date: Tue, 2 Oct 2018 00:15:52 +0800 Subject: Make button use border delegate. --- src/main.cpp | 8 ++++++++ src/ui/controls/border_delegate.cpp | 9 ++++++++- src/ui/controls/border_delegate.h | 6 ++++++ src/ui/controls/button.cpp | 22 ++++++++++++++++------ src/ui/controls/button.h | 8 +++++--- 5 files changed, 43 insertions(+), 10 deletions(-) (limited to 'src/ui/controls/border_delegate.cpp') diff --git a/src/main.cpp b/src/main.cpp index 4f711208..e11fbbe0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -143,12 +143,20 @@ int APIENTRY wWinMain( window.AddChild(layout); */ + /* window.AddChild( CreateWithLayout(LayoutSideParams::Exactly(200), LayoutSideParams::Content(), std::initializer_list{ CreateWithLayout(LayoutSideParams::Stretch(), LayoutSideParams::Content()) } )); + */ + + window.AddChild( + Button::Create( + {TextBlock::Create(L"Button")} + ) + ); window.Show(); diff --git a/src/ui/controls/border_delegate.cpp b/src/ui/controls/border_delegate.cpp index 4031d787..ec7ba437 100644 --- a/src/ui/controls/border_delegate.cpp +++ b/src/ui/controls/border_delegate.cpp @@ -61,12 +61,19 @@ namespace cru::ui::controls 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(0.0f, 0.0f, size.width, size.height), + 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() ), diff --git a/src/ui/controls/border_delegate.h b/src/ui/controls/border_delegate.h index 12c5444a..5a7dfbed 100644 --- a/src/ui/controls/border_delegate.h +++ b/src/ui/controls/border_delegate.h @@ -7,6 +7,12 @@ namespace cru::ui::controls class BorderProperty : public PropertyChangedNotifyObject { public: + using Ptr = std::shared_ptr; + static Ptr Create() + { + return std::make_shared(); + } + 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"; diff --git a/src/ui/controls/button.cpp b/src/ui/controls/button.cpp index b7614f93..630c0c39 100644 --- a/src/ui/controls/button.cpp +++ b/src/ui/controls/button.cpp @@ -8,26 +8,36 @@ namespace cru::ui::controls Button::Button() : Control(true) { - normal_border_brush_ = CreateSolidBrush(D2D1::ColorF(D2D1::ColorF::RoyalBlue)); - pressed_border_brush_ = CreateSolidBrush(D2D1::ColorF(D2D1::ColorF::MediumBlue)); - current_border_brush_ = normal_border_brush_.Get(); + 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(this, normal_border_border_); } void Button::OnDraw(ID2D1DeviceContext* device_context) { Control::OnDraw(device_context); - device_context->DrawRoundedRectangle(D2D1::RoundedRect(D2D1::RectF(0, 0, GetSize().width, GetSize().height), 6, 6), current_border_brush_, 2); + border_delegate_->Draw(device_context, GetSize()); } void Button::OnMouseClickBegin(MouseButton button) { - current_border_brush_ = pressed_border_brush_.Get(); + border_delegate_->SetBorderProperty(pressed_border_border_); Repaint(); } void Button::OnMouseClickEnd(MouseButton button) { - current_border_brush_ = normal_border_brush_.Get(); + border_delegate_->SetBorderProperty(normal_border_border_); Repaint(); } } diff --git a/src/ui/controls/button.h b/src/ui/controls/button.h index bd3f6eb3..7a303984 100644 --- a/src/ui/controls/button.h +++ b/src/ui/controls/button.h @@ -3,6 +3,7 @@ #include #include "ui/control.h" +#include "border_delegate.h" namespace cru::ui::controls { @@ -34,8 +35,9 @@ namespace cru::ui::controls void OnMouseClickEnd(MouseButton button) override final; private: - Microsoft::WRL::ComPtr normal_border_brush_; - Microsoft::WRL::ComPtr pressed_border_brush_; - ID2D1Brush* current_border_brush_; + std::unique_ptr border_delegate_; + + BorderProperty::Ptr normal_border_border_; + BorderProperty::Ptr pressed_border_border_; }; } -- cgit v1.2.3 From f55a20fd3bad952481339d47ff844ad816192f50 Mon Sep 17 00:00:00 2001 From: crupest Date: Tue, 2 Oct 2018 00:32:21 +0800 Subject: Fix measure system of border. --- src/ui/control.cpp | 113 ++++++++++++++++++++++++++++++++++++ src/ui/control.h | 3 + src/ui/controls/border_delegate.cpp | 12 ---- src/ui/controls/border_delegate.h | 6 +- src/ui/controls/button.cpp | 10 ++++ src/ui/controls/button.h | 3 + 6 files changed, 133 insertions(+), 14 deletions(-) (limited to 'src/ui/controls/border_delegate.cpp') diff --git a/src/ui/control.cpp b/src/ui/control.cpp index 480f7d46..86101f9a 100644 --- a/src/ui/control.cpp +++ b/src/ui/control.cpp @@ -639,6 +639,119 @@ namespace cru { }); } + 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 Control::DefaultMeasureWithPadding(const Size& available_size, const Thickness& padding) + { + const auto layout_params = GetLayoutParams(); + const auto padding_size = ThicknessToSize(padding); + + 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 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); + + 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) + ) + padding_size; + } + + void Control::DefaultLayoutWithPadding(const Rect& rect, const Thickness& padding) + { + const Rect final_rect( + rect.left + padding.left, + rect.top + padding.top, + rect.width - padding.left - padding.right, + rect.height - padding.top - padding.bottom + ); + + ForeachChild([final_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(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) + ), size)); + }); + } + void Control::CheckAndNotifyPositionChanged() { if (this->old_position_ != this->position_) diff --git a/src/ui/control.h b/src/ui/control.h index 98d41d0d..964b61c4 100644 --- a/src/ui/control.h +++ b/src/ui/control.h @@ -309,6 +309,9 @@ namespace cru virtual Size OnMeasure(const Size& available_size); virtual void OnLayout(const Rect& rect); + Size DefaultMeasureWithPadding(const Size& available_size, const Thickness& padding); + void DefaultLayoutWithPadding(const Rect& rect, const Thickness& padding); + private: // Only for layout manager to use. // Check if the old position is updated to current position. diff --git a/src/ui/controls/border_delegate.cpp b/src/ui/controls/border_delegate.cpp index ec7ba437..0b460997 100644 --- a/src/ui/controls/border_delegate.cpp +++ b/src/ui/controls/border_delegate.cpp @@ -82,16 +82,4 @@ namespace cru::ui::controls border_property_->GetStrokeStyle().Get() ); } - - Size BorderDelegate::GetBorderSize() const - { - const auto width = border_property_->GetWidth(); - return Size(width * 2, width * 2); - } - - Rect BorderDelegate::CoerceLayoutRect(const Rect& rect) const - { - const auto width = border_property_->GetWidth(); - return Rect(rect.left + width, rect.top + width, rect.width - width * 2, rect.height - width * 2); - } } diff --git a/src/ui/controls/border_delegate.h b/src/ui/controls/border_delegate.h index 5a7dfbed..6d3b31db 100644 --- a/src/ui/controls/border_delegate.h +++ b/src/ui/controls/border_delegate.h @@ -90,8 +90,10 @@ namespace cru::ui::controls void Draw(ID2D1DeviceContext* device_context, const Size& size) const; - Size GetBorderSize() const; - Rect CoerceLayoutRect(const Rect& rect) const; + Thickness GetBorderThickness() const + { + return Thickness(border_property_->GetWidth()); + } private: Control* control_; diff --git a/src/ui/controls/button.cpp b/src/ui/controls/button.cpp index 630c0c39..2ecc9f14 100644 --- a/src/ui/controls/button.cpp +++ b/src/ui/controls/button.cpp @@ -29,6 +29,16 @@ namespace cru::ui::controls 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()); + } + void Button::OnMouseClickBegin(MouseButton button) { border_delegate_->SetBorderProperty(pressed_border_border_); diff --git a/src/ui/controls/button.h b/src/ui/controls/button.h index 7a303984..4b57a5a3 100644 --- a/src/ui/controls/button.h +++ b/src/ui/controls/button.h @@ -31,6 +31,9 @@ namespace cru::ui::controls 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; -- cgit v1.2.3 From 040a6c18f18100b825a56443a73aa1de64e4518c Mon Sep 17 00:00:00 2001 From: crupest Date: Tue, 2 Oct 2018 01:09:35 +0800 Subject: Make text box use border delegate. Fix layout bug in margin container. --- src/main.cpp | 13 +++++-- src/ui/controls/border_delegate.cpp | 14 +++++++- src/ui/controls/border_delegate.h | 6 ++-- src/ui/controls/margin_container.cpp | 63 ++------------------------------ src/ui/controls/text_box.cpp | 28 ++++++++++----- src/ui/controls/text_box.h | 5 +++ src/ui/controls/text_control.cpp | 70 ++++++++++++++++++++++++++++++++++++ src/ui/controls/text_control.h | 4 ++- 8 files changed, 125 insertions(+), 78 deletions(-) (limited to 'src/ui/controls/border_delegate.cpp') diff --git a/src/main.cpp b/src/main.cpp index e11fbbe0..7a105d79 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -153,9 +153,16 @@ int APIENTRY wWinMain( */ window.AddChild( - Button::Create( - {TextBlock::Create(L"Button")} - ) + Border::Create({ + MarginContainer::Create(Thickness(50, 50), { + LinearLayout::Create(LinearLayout::Orientation::Vertical, { + Button::Create({ + TextBlock::Create(L"Button") + }), + TextBox::Create() + }) + }) + }) ); window.Show(); diff --git a/src/ui/controls/border_delegate.cpp b/src/ui/controls/border_delegate.cpp index 0b460997..c8855e0f 100644 --- a/src/ui/controls/border_delegate.cpp +++ b/src/ui/controls/border_delegate.cpp @@ -1,7 +1,19 @@ #include "border_delegate.h" +#include "graph/graph.h" namespace cru::ui::controls { + BorderProperty::Ptr BorderProperty::Create() + { + return std::make_shared(graph::CreateSolidBrush(D2D1::ColorF(D2D1::ColorF::Black))); + } + + BorderProperty::BorderProperty(Microsoft::WRL::ComPtr brush) + : brush_(std::move(brush)) + { + + } + void BorderProperty::SetBrush(Microsoft::WRL::ComPtr brush) { brush_ = std::move(brush); @@ -33,7 +45,7 @@ namespace cru::ui::controls } BorderDelegate::BorderDelegate(Control* control) - : BorderDelegate(control, std::make_shared()) + : BorderDelegate(control, BorderProperty::Create()) { } diff --git a/src/ui/controls/border_delegate.h b/src/ui/controls/border_delegate.h index 6d3b31db..6d8663e9 100644 --- a/src/ui/controls/border_delegate.h +++ b/src/ui/controls/border_delegate.h @@ -8,10 +8,7 @@ namespace cru::ui::controls { public: using Ptr = std::shared_ptr; - static Ptr Create() - { - return std::make_shared(); - } + static Ptr Create(); constexpr static auto brush_property_name = L"Brush"; constexpr static auto width_property_name = L"Width"; @@ -20,6 +17,7 @@ namespace cru::ui::controls constexpr static auto radius_y_property_name = L"RadiusY"; BorderProperty() = default; + explicit BorderProperty(Microsoft::WRL::ComPtr brush); BorderProperty(const BorderProperty& other) = delete; BorderProperty(BorderProperty&& other) = delete; BorderProperty& operator=(const BorderProperty& other) = delete; diff --git a/src/ui/controls/margin_container.cpp b/src/ui/controls/margin_container.cpp index 26acb172..12dde025 100644 --- a/src/ui/controls/margin_container.cpp +++ b/src/ui/controls/margin_container.cpp @@ -25,70 +25,11 @@ namespace cru::ui::controls Size MarginContainer::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)); - - const auto margin_size = Size(margin_.left + margin_.right, margin_.top + margin_.bottom); - const auto coerced_size_for_children = AtLeast0(size_for_children - margin_size); - - auto max_child_size = Size::Zero(); - ForeachChild([coerced_size_for_children, &max_child_size](Control* control) - { - control->Measure(coerced_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, coerced_size_for_children.width, max_child_size.width), - calculate_final_length(layout_params->height, coerced_size_for_children.height, max_child_size.height) - ); + return DefaultMeasureWithPadding(available_size, margin_); } void MarginContainer::OnLayout(const Rect& rect) { - const auto anchor = Point(margin_.left, margin_.top); - const auto margin_size = Size(margin_.left + margin_.right, margin_.top + margin_.bottom); - Control::OnLayout(Rect(anchor, AtLeast0(rect.GetSize() - margin_size))); + DefaultLayoutWithPadding(rect, margin_); } } diff --git a/src/ui/controls/text_box.cpp b/src/ui/controls/text_box.cpp index 4a4114ab..0d65f1ad 100644 --- a/src/ui/controls/text_box.cpp +++ b/src/ui/controls/text_box.cpp @@ -26,21 +26,28 @@ namespace cru::ui::controls is_caret_show_ = !is_caret_show_; Repaint(); }); + + border_delegate_ = std::make_unique(this); } TextBox::~TextBox() = default; void TextBox::OnDraw(ID2D1DeviceContext* device_context) { - TextControl::OnDraw(device_context); - if (is_caret_show_) + 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) { - 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()); - } + 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()); + } + }); } void TextBox::OnGetFocusCore(events::FocusChangeEventArgs& args) @@ -131,6 +138,11 @@ 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 07c4abe4..a6e4566d 100644 --- a/src/ui/controls/text_box.h +++ b/src/ui/controls/text_box.h @@ -2,6 +2,7 @@ #include "text_control.h" #include "timer.h" +#include "border_delegate.h" namespace cru::ui::controls { @@ -36,6 +37,8 @@ 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: @@ -44,5 +47,7 @@ namespace cru::ui::controls ActionPtr caret_action_; Microsoft::WRL::ComPtr caret_brush_; bool is_caret_show_ = false; + + std::unique_ptr border_delegate_; }; } diff --git a/src/ui/controls/text_control.cpp b/src/ui/controls/text_control.cpp index 692c4451..0b730500 100644 --- a/src/ui/controls/text_control.cpp +++ b/src/ui/controls/text_control.cpp @@ -259,6 +259,76 @@ namespace cru::ui::controls 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)); + + 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 + padding_size; + } + void TextControl::RequestChangeCaretPosition(unsigned position) { diff --git a/src/ui/controls/text_control.h b/src/ui/controls/text_control.h index a24766dc..2ead7c54 100644 --- a/src/ui/controls/text_control.h +++ b/src/ui/controls/text_control.h @@ -76,7 +76,9 @@ namespace cru::ui::controls void OnLoseFocusCore(events::FocusChangeEventArgs& args) override; - Size OnMeasure(const Size& available_size) override final; + Size OnMeasure(const Size& available_size) override; + + Size TextMeasureWithPadding(const Size& available_size, const Thickness& padding); virtual void RequestChangeCaretPosition(unsigned position); -- cgit v1.2.3