aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CruUI.vcxproj8
-rw-r--r--CruUI.vcxproj.filters16
-rw-r--r--src/base.cpp6
-rw-r--r--src/base.h11
-rw-r--r--src/main.cpp30
-rw-r--r--src/ui/border_property.cpp43
-rw-r--r--src/ui/border_property.h72
-rw-r--r--src/ui/control.cpp303
-rw-r--r--src/ui/control.h73
-rw-r--r--src/ui/controls/border.cpp37
-rw-r--r--src/ui/controls/border.h54
-rw-r--r--src/ui/controls/border_delegate.cpp97
-rw-r--r--src/ui/controls/border_delegate.h101
-rw-r--r--src/ui/controls/button.cpp47
-rw-r--r--src/ui/controls/button.h12
-rw-r--r--src/ui/controls/linear_layout.cpp76
-rw-r--r--src/ui/controls/linear_layout.h4
-rw-r--r--src/ui/controls/margin_container.cpp35
-rw-r--r--src/ui/controls/margin_container.h44
-rw-r--r--src/ui/controls/text_box.cpp30
-rw-r--r--src/ui/controls/text_box.h7
-rw-r--r--src/ui/controls/text_control.cpp120
-rw-r--r--src/ui/controls/text_control.h5
-rw-r--r--src/ui/controls/toggle_button.cpp35
-rw-r--r--src/ui/controls/toggle_button.h6
-rw-r--r--src/ui/layout_base.h62
-rw-r--r--src/ui/ui_base.h35
-rw-r--r--src/ui/window.cpp6
-rw-r--r--src/ui/window.h2
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);
diff --git a/src/base.h b/src/base.h
index 18bf9cc5..0a77e5b9 100644
--- a/src/base.h
+++ b/src/base.h
@@ -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, &current_anchor_length, rect](Control* control)
+ float current_main_side_anchor = 0;
+ ForeachChild([this, &current_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 ***************