aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2018-12-06 21:24:45 +0800
committercrupest <crupest@outlook.com>2018-12-06 21:24:45 +0800
commitde5d84f64ba7673a5155aab67244fedc04753b94 (patch)
tree5b15e3f97c67e81ad99fb531f30681300a79b5a9
parent03ef76f769a55b694905898c16a176fc6bd4b0d7 (diff)
downloadcru-de5d84f64ba7673a5155aab67244fedc04753b94.tar.gz
cru-de5d84f64ba7673a5155aab67244fedc04753b94.tar.bz2
cru-de5d84f64ba7673a5155aab67244fedc04753b94.zip
Generate merged source.
-rw-r--r--CruUI-Generate/cru_ui.cpp565
-rw-r--r--CruUI-Generate/cru_ui.hpp237
2 files changed, 501 insertions, 301 deletions
diff --git a/CruUI-Generate/cru_ui.cpp b/CruUI-Generate/cru_ui.cpp
index 8f64a791..9a4e335d 100644
--- a/CruUI-Generate/cru_ui.cpp
+++ b/CruUI-Generate/cru_ui.cpp
@@ -380,7 +380,7 @@ int APIENTRY wWinMain(
{
const auto button = Button::Create();
button->GetLayoutParams()->padding = Thickness(20, 5);
- button->AddChild(TextBlock::Create(L"Show popup window parenting this."));
+ button->SetChild(TextBlock::Create(L"Show popup window parenting this."));
button->mouse_click_event.bubble.AddHandler([window, button](auto)
{
std::vector<cru::ui::controls::MenuItemInfo> items;
@@ -396,7 +396,7 @@ int APIENTRY wWinMain(
{
const auto button = Button::Create();
button->GetLayoutParams()->padding = Thickness(20, 5);
- button->AddChild(TextBlock::Create(L"Show popup window parenting null."));
+ button->SetChild(TextBlock::Create(L"Show popup window parenting null."));
button->SetBackgroundBrush(cru::graph::CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Gold)));
button->mouse_click_event.bubble.AddHandler([](auto)
{
@@ -410,7 +410,7 @@ int APIENTRY wWinMain(
{
const auto button = Button::Create();
button->GetLayoutParams()->padding = Thickness(20, 5);
- button->AddChild(TextBlock::Create(L"Show popup window with caption."));
+ button->SetChild(TextBlock::Create(L"Show popup window with caption."));
button->mouse_click_event.bubble.AddHandler([](auto)
{
auto popup = Window::CreatePopup(nullptr, true);
@@ -452,7 +452,7 @@ int APIENTRY wWinMain(
L"Love myself I do. Not everything, but I love the good as well as the bad. I love my crazy lifestyle, and I love my hard discipline. I love my freedom of speech and the way my eyes get dark when I'm tired. I love that I have learned to trust people with my heart, even if it will get broken. I am proud of everything that I am and will become.");
text_block->SetSelectable(true);
- scroll_view->AddChild(text_block);
+ scroll_view->SetChild(text_block);
layout->AddChild(scroll_view);
}
@@ -460,7 +460,7 @@ int APIENTRY wWinMain(
layout->AddChild(CreateWithLayout<TextBlock>(LayoutSideParams::Content(Alignment::End), LayoutSideParams::Stretch(), L"By crupest!!!"));
- window->AddChild(layout);
+ window->SetChild(layout);
/*
window.AddChild(
@@ -819,6 +819,7 @@ namespace cru::ui
//--------------------------------------------------------
#include <algorithm>
+#include <cassert>
#ifdef CRU_DEBUG_LAYOUT
@@ -826,8 +827,7 @@ namespace cru::ui
namespace cru::ui
{
- Control::Control(const bool container) :
- is_container_(container)
+ Control::Control()
{
mouse_leave_event.bubble.AddHandler([this](events::MouseEventArgs& args)
{
@@ -874,121 +874,68 @@ namespace cru::ui
});
}
- Control::Control(WindowConstructorTag, Window* window) : Control(true)
- {
- window_ = window;
- }
-
- Control::~Control()
- {
- for (auto control: GetChildren())
- {
- delete control;
- }
- }
-
- void AddChildCheck(Control* control)
- {
- if (control->GetParent() != nullptr)
- throw std::invalid_argument("The control already has a parent.");
-
- if (dynamic_cast<Window*>(control))
- throw std::invalid_argument("Can't add a window as child.");
- }
-
- const std::vector<Control*>& Control::GetChildren() const
- {
- return children_;
- }
- void Control::AddChild(Control* control)
+ void Control::SetParent(Control* parent)
{
- ThrowIfNotContainer();
- AddChildCheck(control);
-
- this->children_.push_back(control);
-
- control->parent_ = this;
-
- this->OnAddChild(control);
+ const auto old_parent = GetParent();
+ parent_ = parent;
+ const auto new_parent = GetParent();
+ if (old_parent != new_parent)
+ OnParentChanged(old_parent, new_parent);
}
- void Control::AddChild(Control* control, int position)
+ void Control::SetInternalParent(Control* internal_parent)
{
- ThrowIfNotContainer();
- AddChildCheck(control);
-
- if (position < 0 || static_cast<decltype(this->children_.size())>(position) > this->children_.size())
- throw std::invalid_argument("The position is out of range.");
-
- this->children_.insert(this->children_.cbegin() + position, control);
-
- control->parent_ = this;
-
- this->OnAddChild(this);
+ const auto old_internal_parent = GetInternalParent();
+ const auto old_parent = GetParent();
+ internal_parent_ = internal_parent;
+ const auto new_internal_parent = GetInternalParent();
+ const auto new_parent = GetParent();
+ if (old_parent != new_parent)
+ OnParentChanged(old_parent, new_parent);
+ if (old_internal_parent != new_internal_parent)
+ OnInternalParentChanged(old_internal_parent, new_internal_parent);
}
- void Control::RemoveChild(Control* child)
+ void Control::SetDescendantWindow(Window* window)
{
- ThrowIfNotContainer();
- const auto i = std::find(this->children_.cbegin(), this->children_.cend(), child);
- if (i == this->children_.cend())
- throw std::invalid_argument("The argument child is not a child of this control.");
-
- this->children_.erase(i);
-
- child->parent_ = nullptr;
-
- this->OnRemoveChild(this);
- }
-
- void Control::RemoveChild(const int position)
- {
- ThrowIfNotContainer();
- if (position < 0 || static_cast<decltype(this->children_.size())>(position) >= this->children_.size())
- throw std::invalid_argument("The position is out of range.");
-
- const auto p = children_.cbegin() + position;
- const auto child = *p;
- children_.erase(p);
+ if (window == nullptr && window_ == nullptr)
+ return;
- child->parent_ = nullptr;
+ //You can only attach or detach window.
+ assert((window != nullptr && window_ == nullptr) || (window == nullptr && window_ != nullptr));
- this->OnRemoveChild(child);
+ if (window == nullptr)
+ {
+ const auto old = window_;
+ TraverseDescendants([old](Control* control)
+ {
+ control->window_ = nullptr;
+ control->OnDetachToWindow(old);
+ });
+ }
+ else
+ TraverseDescendants([window](Control* control)
+ {
+ control->window_ = window;
+ control->OnAttachToWindow(window);
+ });
}
- Control* Control::GetAncestor()
- {
- // if attached to window, the window is the ancestor.
- if (window_)
- return window_;
-
- // otherwise find the ancestor
- auto ancestor = this;
- while (const auto parent = ancestor->GetParent())
- ancestor = parent;
- return ancestor;
- }
void TraverseDescendantsInternal(Control* control, const std::function<void(Control*)>& predicate)
{
predicate(control);
- if (control->IsContainer())
- for (auto c: control->GetChildren())
- {
- TraverseDescendantsInternal(c, predicate);
- }
+ for (auto c: control->GetInternalChildren())
+ TraverseDescendantsInternal(c, predicate);
}
void Control::TraverseDescendants(const std::function<void(Control*)>& predicate)
{
- if (is_container_)
- TraverseDescendantsInternal(this, predicate);
- else
- predicate(this);
+ TraverseDescendantsInternal(this, predicate);
}
- Point Control::GetPositionRelative()
+ Point Control::GetOffset()
{
return rect_.GetLeftTop();
}
@@ -1049,6 +996,33 @@ namespace cru::ui
point.y - position_cache_.lefttop_position_absolute.y);
}
+ void Control::RefreshDescendantPositionCache()
+ {
+ auto point = Point::Zero();
+ auto parent = this;
+ while ((parent = parent->GetParent()))
+ {
+ const auto p = parent->GetOffset();
+ point.x += p.x;
+ point.y += p.y;
+ }
+ RefreshControlPositionCacheInternal(this, point);
+ }
+
+ void Control::RefreshControlPositionCacheInternal(Control* control, const Point& parent_lefttop_absolute)
+ {
+ const auto position = control->GetOffset();
+ const Point lefttop(
+ parent_lefttop_absolute.x + position.x,
+ parent_lefttop_absolute.y + position.y
+ );
+ control->position_cache_.lefttop_position_absolute = lefttop;
+ for(auto c : control->GetInternalChildren())
+ {
+ RefreshControlPositionCacheInternal(c, lefttop);
+ }
+ }
+
bool Control::IsPointInside(const Point & point)
{
const auto border_geometry = geometry_info_.border_geometry;
@@ -1089,11 +1063,11 @@ namespace cru::ui
}
}
- const auto& children = GetChildren();
+ const auto& children = GetInternalChildren();
for (auto i = children.crbegin(); i != children.crend(); ++i)
{
- const auto&& lefttop = (*i)->GetPositionRelative();
+ const auto&& lefttop = (*i)->GetOffset();
const auto&& coerced_point = Point(point.x - lefttop.x, point.y - lefttop.y);
const auto child_hit_test_result = (*i)->HitTest(coerced_point);
if (child_hit_test_result != nullptr)
@@ -1117,7 +1091,7 @@ namespace cru::ui
D2D1::Matrix3x2F old_transform;
device_context->GetTransform(&old_transform);
- const auto position = GetPositionRelative();
+ const auto position = GetOffset();
device_context->SetTransform(old_transform * D2D1::Matrix3x2F::Translation(position.x, position.y));
OnDrawDecoration(device_context);
@@ -1128,7 +1102,7 @@ namespace cru::ui
OnDrawCore(device_context);
- for (auto child : GetChildren())
+ for (auto child : GetInternalChildren())
child->Draw(device_context);
if (set_layer)
@@ -1275,26 +1249,14 @@ namespace cru::ui
}
}
- void Control::OnAddChild(Control* child)
+ void Control::OnParentChanged(Control* old_parent, Control* new_parent)
{
- if (auto window = GetWindow())
- {
- child->TraverseDescendants([window](Control* control) {
- control->OnAttachToWindow(window);
- });
- InvalidateLayout();
- }
+
}
- void Control::OnRemoveChild(Control* child)
+ void Control::OnInternalParentChanged(Control* old_internal_parent, Control* new_internal_parent)
{
- if (auto window = GetWindow())
- {
- child->TraverseDescendants([window](Control* control) {
- control->OnDetachToWindow(window);
- });
- InvalidateLayout();
- }
+
}
void Control::OnAttachToWindow(Window* window)
@@ -1546,40 +1508,78 @@ namespace cru::ui
OnLayoutContent(content_rect, additional_info);
}
- Size Control::OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo& additional_info)
+ const std::vector<Control*> NoChildControl::empty_control_vector{};
+
+ std::list<Control*> GetAncestorList(Control* control)
{
- auto max_child_size = Size::Zero();
- for (auto control: GetChildren())
+ std::list<Control*> l;
+ while (control != nullptr)
{
- control->Measure(available_size, additional_info);
- 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;
+ l.push_front(control);
+ control = control->GetInternalParent();
}
+ return l;
+ }
- // coerce size fro stretch.
- for (auto control: GetChildren())
+ void NoChildControl::OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info)
+ {
+
+ }
+
+ SingleChildControl::SingleChildControl() : child_vector_{nullptr}, child_(child_vector_[0])
+ {
+
+ }
+
+ SingleChildControl::~SingleChildControl()
+ {
+ delete child_;
+ }
+
+ void SingleChildControl::SetChild(Control* child)
+ {
+ if (child == child_)
+ return;
+
+ const auto window = GetWindow();
+ const auto old_child = child_;
+ child_ = child;
+ if (old_child)
{
- auto size = control->GetDesiredSize();
- const auto layout_params = control->GetLayoutParams();
- if (layout_params->width.mode == MeasureMode::Stretch)
- size.width = max_child_size.width;
- if (layout_params->height.mode == MeasureMode::Stretch)
- size.height = max_child_size.height;
- control->SetDesiredSize(size);
+ old_child->SetInternalParent(nullptr);
+ old_child->SetDescendantWindow(nullptr);
}
+ if (child)
+ {
+ child->SetInternalParent(this);
+ child->SetDescendantWindow(window);
+ }
+ OnChildChanged(old_child, child);
+ }
+
+ void SingleChildControl::OnChildChanged(Control* old_child, Control* new_child)
+ {
- return max_child_size;
}
- void Control::OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info)
+ Size SingleChildControl::OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo& additional_info)
{
- for (auto control: GetChildren())
+ auto child_size = Size::Zero();
+ if (child_)
{
- const auto layout_params = control->GetLayoutParams();
- const auto size = control->GetDesiredSize();
+ child_->Measure(available_size, additional_info);
+ child_size = child_->GetDesiredSize();
+ }
+
+ return child_size;
+ }
+
+ void SingleChildControl::OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info)
+ {
+ if (child_)
+ {
+ const auto layout_params = child_->GetLayoutParams();
+ const auto size = child_->GetDesiredSize();
auto&& calculate_anchor = [](const float anchor, const Alignment alignment, const float layout_length, const float control_length) -> float
{
@@ -1596,22 +1596,93 @@ namespace cru::ui
}
};
- control->Layout(Rect(Point(
+ child_->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), additional_info);
}
}
- std::list<Control*> GetAncestorList(Control* control)
+ void AddChildCheck(Control* control)
{
- std::list<Control*> l;
- while (control != nullptr)
- {
- l.push_front(control);
- control = control->GetParent();
- }
- return l;
+ if (control->GetInternalParent() != nullptr)
+ throw std::invalid_argument("The control already has a parent.");
+
+ if (dynamic_cast<Window*>(control))
+ throw std::invalid_argument("Can't add a window as child.");
+ }
+
+ MultiChildControl::~MultiChildControl()
+ {
+ for (const auto child : children_)
+ delete child;
+ }
+
+ void MultiChildControl::AddChild(Control* control)
+ {
+ AddChildCheck(control);
+
+ children_.push_back(control);
+
+ control->SetInternalParent(this);
+ control->SetDescendantWindow(GetWindow());
+
+ OnAddChild(control);
+ }
+
+ void MultiChildControl::AddChild(Control* control, const int position)
+ {
+ AddChildCheck(control);
+
+ if (position < 0 || static_cast<decltype(children_.size())>(position) > this->children_.size())
+ throw std::invalid_argument("The position is out of range.");
+
+ children_.insert(this->children_.cbegin() + position, control);
+
+ control->SetInternalParent(this);
+ control->SetDescendantWindow(GetWindow());
+
+ OnAddChild(control);
+ }
+
+ void MultiChildControl::RemoveChild(Control* child)
+ {
+ const auto i = std::find(this->children_.cbegin(), this->children_.cend(), child);
+ if (i == this->children_.cend())
+ throw std::invalid_argument("The argument child is not a child of this control.");
+
+ children_.erase(i);
+
+ child->SetInternalParent(nullptr);
+ child->SetDescendantWindow(nullptr);
+
+ OnRemoveChild(child);
+ }
+
+ void MultiChildControl::RemoveChild(const int position)
+ {
+ if (position < 0 || static_cast<decltype(this->children_.size())>(position) >= this->children_.size())
+ throw std::invalid_argument("The position is out of range.");
+
+ const auto i = children_.cbegin() + position;
+ const auto child = *i;
+
+ children_.erase(i);
+
+ child->SetInternalParent(nullptr);
+ child->SetDescendantWindow(nullptr);
+
+ OnRemoveChild(child);
+ }
+
+ void MultiChildControl::OnAddChild(Control* child)
+ {
+
+ }
+
+ void MultiChildControl::OnRemoveChild(Control* child)
+ {
+
}
Control* FindLowestCommonAncestor(Control * left, Control * right)
@@ -1641,27 +1712,6 @@ namespace cru::ui
++right_i;
}
}
-
- Control * IsAncestorOrDescendant(Control * left, Control * right)
- {
- //Search up along the trunk from "left". Return if find "right".
- auto control = left;
- while (control != nullptr)
- {
- if (control == right)
- return control;
- control = control->GetParent();
- }
- //Search up along the trunk from "right". Return if find "left".
- control = right;
- while (control != nullptr)
- {
- if (control == left)
- return control;
- control = control->GetParent();
- }
- return nullptr;
- }
}
//--------------------------------------------------------
//-------end of file: src\ui\control.cpp
@@ -1964,7 +2014,7 @@ namespace cru::ui
const auto cursor = control->GetCursor();
if (cursor != nullptr)
return cursor;
- control = control->GetParent();
+ control = control->GetInternalParent();
}
return cursors::arrow;
}
@@ -1980,8 +2030,11 @@ namespace cru::ui
return new Window(tag_popup_constructor{}, parent, caption);
}
- Window::Window(tag_overlapped_constructor) : Control(WindowConstructorTag{}, this)
+
+ Window::Window(tag_overlapped_constructor)
{
+ BeforeCreateHwnd();
+
const auto window_manager = WindowManager::GetInstance();
hwnd_ = CreateWindowEx(0,
@@ -1997,11 +2050,13 @@ namespace cru::ui
AfterCreateHwnd(window_manager);
}
- Window::Window(tag_popup_constructor, Window* parent, const bool caption) : Control(WindowConstructorTag{}, this)
+ Window::Window(tag_popup_constructor, Window* parent, const bool caption)
{
if (parent != nullptr && !parent->IsWindowValid())
throw std::runtime_error("Parent window is not valid.");
+ BeforeCreateHwnd();
+
parent_window_ = parent;
const auto window_manager = WindowManager::GetInstance();
@@ -2020,6 +2075,11 @@ namespace cru::ui
AfterCreateHwnd(window_manager);
}
+ void Window::BeforeCreateHwnd()
+ {
+ window_ = this;
+ }
+
void Window::AfterCreateHwnd(WindowManager* window_manager)
{
window_manager->RegisterWindow(hwnd_, this);
@@ -2307,7 +2367,7 @@ namespace cru::ui
return PiToDip(point);
}
- Point Window::GetPositionRelative()
+ Point Window::GetOffset()
{
return Point();
}
@@ -2812,7 +2872,7 @@ namespace cru::ui::animations
namespace cru::ui::controls
{
- Button::Button() : Control(true),
+ Button::Button() :
normal_border_{UiManager::GetInstance()->GetPredefineResources()->button_normal_border},
pressed_border_{UiManager::GetInstance()->GetPredefineResources()->button_press_border}
{
@@ -2848,10 +2908,7 @@ namespace cru::ui::controls
namespace cru::ui::controls
{
- FrameLayout::FrameLayout() : Control(true)
- {
-
- }
+ FrameLayout::FrameLayout() = default;
FrameLayout::~FrameLayout() = default;
@@ -2859,6 +2916,63 @@ namespace cru::ui::controls
{
return control_type;
}
+
+ Size FrameLayout::OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo& additional_info)
+ {
+ auto max_child_size = Size::Zero();
+ for (auto control: GetChildren())
+ {
+ control->Measure(available_size, additional_info);
+ 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;
+ }
+
+ // coerce size fro stretch.
+ for (auto control: GetChildren())
+ {
+ auto size = control->GetDesiredSize();
+ const auto layout_params = control->GetLayoutParams();
+ if (layout_params->width.mode == MeasureMode::Stretch)
+ size.width = max_child_size.width;
+ if (layout_params->height.mode == MeasureMode::Stretch)
+ size.height = max_child_size.height;
+ control->SetDesiredSize(size);
+ }
+
+ return max_child_size;
+ }
+
+ void FrameLayout::OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info)
+ {
+ for (auto control: GetChildren())
+ {
+ 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), additional_info);
+ }
+ }
}
//--------------------------------------------------------
//-------end of file: src\ui\controls\frame_layout.cpp
@@ -2873,7 +2987,7 @@ namespace cru::ui::controls
namespace cru::ui::controls
{
LinearLayout::LinearLayout(const Orientation orientation)
- : Control(true), orientation_(orientation)
+ : orientation_(orientation)
{
}
@@ -2893,7 +3007,7 @@ namespace cru::ui::controls
// First measure Content and Exactly and count Stretch.
if (orientation_ == Orientation::Horizontal)
- for(auto control: GetChildren())
+ for(auto control: GetInternalChildren())
{
const auto mode = control->GetLayoutParams()->width.mode;
if (mode == MeasureMode::Content || mode == MeasureMode::Exactly)
@@ -2908,7 +3022,7 @@ namespace cru::ui::controls
stretch_control_list.push_back(control);
}
else
- for(auto control: GetChildren())
+ for(auto control: GetInternalChildren())
{
const auto mode = control->GetLayoutParams()->height.mode;
if (mode == MeasureMode::Content || mode == MeasureMode::Exactly)
@@ -2948,7 +3062,7 @@ namespace cru::ui::controls
if (orientation_ == Orientation::Horizontal)
{
- for (auto control : GetChildren())
+ for (auto control : GetInternalChildren())
{
if (control->GetLayoutParams()->height.mode == MeasureMode::Stretch)
{
@@ -2959,7 +3073,7 @@ namespace cru::ui::controls
}
else
{
- for (auto control : GetChildren())
+ for (auto control : GetInternalChildren())
{
if (control->GetLayoutParams()->width.mode == MeasureMode::Stretch)
{
@@ -2976,7 +3090,7 @@ namespace cru::ui::controls
void LinearLayout::OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info)
{
float current_main_side_anchor = 0;
- for(auto control: GetChildren())
+ for(auto control: GetInternalChildren())
{
const auto layout_params = control->GetLayoutParams();
const auto size = control->GetDesiredSize();
@@ -3025,7 +3139,7 @@ namespace cru::ui::controls
namespace cru::ui::controls
{
- ListItem::ListItem() : Control(true)
+ ListItem::ListItem()
{
const auto predefine_resources = UiManager::GetInstance()->GetPredefineResources();
@@ -3109,7 +3223,7 @@ namespace cru::ui::controls
auto list_item = CreateWithLayout<ListItem>(
LayoutSideParams::Stretch(Alignment::Center),
LayoutSideParams::Content(Alignment::Start),
- ControlList{ text_block }
+ text_block
);
list_item->mouse_click_event.bubble.AddHandler([popup, action](events::MouseButtonEventArgs& args)
@@ -3131,7 +3245,7 @@ namespace cru::ui::controls
for (const auto& item : items)
menu->AddChild(create_menu_item(item.first, item.second));
- popup->AddChild(menu);
+ popup->SetChild(menu);
popup->SetSizeFitContent();
popup->SetWindowPosition(anchor);
@@ -3153,7 +3267,7 @@ namespace cru::ui::controls
{
constexpr auto scroll_bar_width = 15.0f;
- ScrollControl::ScrollControl(const bool container) : Control(container)
+ ScrollControl::ScrollControl(const bool container)
{
SetClipContent(true);
@@ -3384,38 +3498,25 @@ namespace cru::ui::controls
available_size_for_children.height = std::numeric_limits<float>::max();
}
- auto max_child_size = Size::Zero();
- for (auto control: GetChildren())
- {
- control->Measure(available_size_for_children, AdditionalMeasureInfo{false, false});
- 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 child = GetChild();
- // coerce size for stretch.
- for (auto control: GetChildren())
+ auto size = Size::Zero();
+ if (child)
{
- auto size = control->GetDesiredSize();
- const auto child_layout_params = control->GetLayoutParams();
- if (child_layout_params->width.mode == MeasureMode::Stretch)
- size.width = max_child_size.width;
- if (child_layout_params->height.mode == MeasureMode::Stretch)
- size.height = max_child_size.height;
- control->SetDesiredSize(size);
+ child->Measure(available_size_for_children, AdditionalMeasureInfo{false, false});
+ size = child->GetDesiredSize();
}
- auto result = max_child_size;
+
+ auto result = size;
if (IsHorizontalScrollEnabled())
{
- SetViewWidth(max_child_size.width);
+ SetViewWidth(size.width);
result.width = available_size.width;
}
if (IsVerticalScrollEnabled())
{
- SetViewHeight(max_child_size.height);
+ SetViewHeight(size.height);
result.height = available_size.height;
}
@@ -3431,18 +3532,31 @@ namespace cru::ui::controls
if (IsVerticalScrollEnabled())
layout_rect.height = GetViewHeight();
- for (auto control: GetChildren())
+ const auto child = GetChild();
+
+ if (child)
{
- const auto size = control->GetDesiredSize();
- // Ignore alignment, always center aligned.
- auto&& calculate_anchor = [](const float anchor, const float layout_length, const float control_length, const float offset) -> float
+ const auto layout_params = child->GetLayoutParams();
+ const auto size = child->GetDesiredSize();
+
+ auto&& calculate_anchor = [](const float anchor, const Alignment alignment, const float layout_length, const float control_length) -> float
{
- return anchor + (layout_length - control_length) / 2 - offset;
+ 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_rect.width, size.width, offset_x_),
- calculate_anchor(rect.top, layout_rect.height, size.height, offset_y_)
+ child->Layout(Rect(Point(
+ IsHorizontalScrollEnabled() ? layout_rect.left + offset_x_ : calculate_anchor(layout_rect.left, layout_params->width.alignment, layout_rect.width, size.width),
+ IsVerticalScrollEnabled() ? layout_rect.top + offset_y_ : calculate_anchor(layout_rect.top, layout_params->height.alignment, layout_rect.height, size.height)
), size), additional_info);
}
}
@@ -3466,13 +3580,14 @@ namespace cru::ui::controls
if (update_children)
{
- for (auto child : GetChildren())
+ if (const auto child = GetChild())
{
- const auto old_position = child->GetPositionRelative();
+ const auto old_position = child->GetOffset();
child->SetRect(Rect(Point(
old_position.x + old_offset_x - offset_x_,
old_position.y + old_offset_y - offset_y_
), child->GetSize()));
+ child->RefreshDescendantPositionCache();
}
}
InvalidateDraw();
@@ -3791,7 +3906,7 @@ namespace cru::ui::controls
}
TextControl::TextControl(const Microsoft::WRL::ComPtr<IDWriteTextFormat>& init_text_format,
- const Microsoft::WRL::ComPtr<ID2D1Brush>& init_brush) : Control(false)
+ const Microsoft::WRL::ComPtr<ID2D1Brush>& init_brush)
{
text_format_ = init_text_format;
diff --git a/CruUI-Generate/cru_ui.hpp b/CruUI-Generate/cru_ui.hpp
index fe27a1e6..11222e6a 100644
--- a/CruUI-Generate/cru_ui.hpp
+++ b/CruUI-Generate/cru_ui.hpp
@@ -1462,7 +1462,6 @@ namespace cru::ui
namespace cru::ui
{
- class Control;
class Window;
@@ -1499,53 +1498,30 @@ namespace cru::ui
protected:
- struct WindowConstructorTag {}; //Used for constructor for class Window.
-
- explicit Control(bool container = false);
-
- // Used only for creating Window. It will set window_ as window.
- Control(WindowConstructorTag, Window* window);
-
+ Control();
public:
Control(const Control& other) = delete;
Control(Control&& other) = delete;
Control& operator=(const Control& other) = delete;
Control& operator=(Control&& other) = delete;
- ~Control() override;
+ ~Control() override = default;
public:
//*************** region: tree ***************
virtual StringView GetControlType() const = 0;
- bool IsContainer() const
- {
- return is_container_;
- }
+ virtual const std::vector<Control*>& GetInternalChildren() const = 0;
- //Get parent of control, return nullptr if it has no parent.
Control* GetParent() const
{
- return parent_;
+ return parent_ == nullptr ? internal_parent_ : parent_;
}
- //Return a immutable vector of all children.
- const std::vector<Control*>& GetChildren() const;
-
- //Add a child at tail.
- void AddChild(Control* control);
-
- //Add a child before the position.
- void AddChild(Control* control, int position);
-
- //Remove a child.
- void RemoveChild(Control* child);
-
- //Remove a child at specified position.
- void RemoveChild(int position);
-
- //Get the ancestor of the control.
- Control* GetAncestor();
+ Control* GetInternalParent() const
+ {
+ return internal_parent_;
+ }
//Get the window if attached, otherwise, return nullptr.
Window* GetWindow() const
@@ -1553,17 +1529,25 @@ namespace cru::ui
return window_;
}
+ void SetParent(Control* parent);
+
+ void SetInternalParent(Control* internal_parent);
+
+ void SetDescendantWindow(Window* window);
+
+
//Traverse the tree rooted the control including itself.
void TraverseDescendants(const std::function<void(Control*)>& predicate);
//*************** region: position and size ***************
//Get the lefttop relative to its parent.
- virtual Point GetPositionRelative();
+ virtual Point GetOffset();
//Get the actual size.
virtual Size GetSize();
+ // If offset changes, call RefreshDescendantPositionCache.
virtual void SetRect(const Rect& rect);
//Get lefttop relative to ancestor. This is only valid when
@@ -1577,6 +1561,13 @@ namespace cru::ui
//Absolute point to local point.
Point WindowToControl(const Point& point) const;
+ void RefreshDescendantPositionCache();
+
+ private:
+ static void RefreshControlPositionCacheInternal(Control* control, const Point& parent_lefttop_absolute);
+
+ public:
+
// Default implement in Control is test point in border geometry's
// fill and stroke with width of border.
virtual bool IsPointInside(const Point& point);
@@ -1724,10 +1715,9 @@ namespace cru::ui
//*************** region: tree event ***************
protected:
- //Invoked when a child is added. Overrides should invoke base.
- virtual void OnAddChild(Control* child);
- //Invoked when a child is removed. Overrides should invoke base.
- virtual void OnRemoveChild(Control* child);
+ virtual void OnParentChanged(Control* old_parent, Control* new_parent);
+
+ virtual void OnInternalParentChanged(Control* old_internal_parent, Control* new_internal_parent);
//Invoked when the control is attached to a window. Overrides should invoke base.
virtual void OnAttachToWindow(Window* window);
@@ -1765,24 +1755,13 @@ namespace cru::ui
void OnLayoutCore(const Rect& rect, const AdditionalLayoutInfo& additional_info);
protected:
- virtual Size OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo& additional_info);
- virtual void OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info);
-
+ virtual Size OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo& additional_info) = 0;
+ virtual void OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info) = 0;
private:
- void ThrowIfNotContainer() const
- {
- if (!is_container_)
- throw std::runtime_error("You can't perform such operation on a non-container control.");
- }
-
- private:
- bool is_container_;
-
Window * window_ = nullptr;
-
- Control * parent_ = nullptr;
- std::vector<Control*> children_{};
+ Control* parent_ = nullptr; // when parent and internal parent are the same, parent_ is nullptr.
+ Control * internal_parent_ = nullptr;
Rect rect_{};
@@ -1821,6 +1800,113 @@ namespace cru::ui
};
+
+ class NoChildControl : public Control
+ {
+ private:
+ // used in GetInternalChildren.
+ static const std::vector<Control*> empty_control_vector;
+
+ protected:
+ NoChildControl() = default;
+ public:
+ NoChildControl(const NoChildControl& other) = delete;
+ NoChildControl(NoChildControl&& other) = delete;
+ NoChildControl& operator=(const NoChildControl& other) = delete;
+ NoChildControl& operator=(NoChildControl&& other) = delete;
+ ~NoChildControl() override = default;
+
+ const std::vector<Control*>& GetInternalChildren() const override final
+ {
+ return empty_control_vector;
+ }
+
+ protected:
+ void OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info) override;
+ };
+
+
+ class SingleChildControl : public Control
+ {
+ protected:
+ SingleChildControl();
+ public:
+ SingleChildControl(const SingleChildControl& other) = delete;
+ SingleChildControl(SingleChildControl&& other) = delete;
+ SingleChildControl& operator=(const SingleChildControl& other) = delete;
+ SingleChildControl& operator=(SingleChildControl&& other) = delete;
+ ~SingleChildControl() override;
+
+ const std::vector<Control*>& GetInternalChildren() const override final
+ {
+ return child_vector_;
+ }
+
+ Control* GetChild() const
+ {
+ return child_;
+ }
+
+ void SetChild(Control* child);
+
+ protected:
+ // Override should call base.
+ virtual void OnChildChanged(Control* old_child, Control* new_child);
+
+ Size OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo& additional_info) override;
+ void OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info) override;
+
+ private:
+ std::vector<Control*> child_vector_;
+ Control*& child_;
+ };
+
+
+ class MultiChildControl : public Control
+ {
+ protected:
+ MultiChildControl() = default;
+ public:
+ MultiChildControl(const MultiChildControl& other) = delete;
+ MultiChildControl(MultiChildControl&& other) = delete;
+ MultiChildControl& operator=(const MultiChildControl& other) = delete;
+ MultiChildControl& operator=(MultiChildControl&& other) = delete;
+ ~MultiChildControl() override;
+
+ const std::vector<Control*>& GetInternalChildren() const override final
+ {
+ return children_;
+ }
+
+ const std::vector<Control*>& GetChildren() const
+ {
+ return children_;
+ }
+
+ //Add a child at tail.
+ void AddChild(Control* control);
+
+ //Add a child before the position.
+ void AddChild(Control* control, int position);
+
+ //Remove a child.
+ void RemoveChild(Control* child);
+
+ //Remove a child at specified position.
+ void RemoveChild(int position);
+
+ protected:
+ //Invoked when a child is added. Overrides should invoke base.
+ virtual void OnAddChild(Control* child);
+ //Invoked when a child is removed. Overrides should invoke base.
+ virtual void OnRemoveChild(Control* child);
+
+ private:
+ std::vector<Control*> children_;
+ };
+
+
+
//*************** region: event dispatcher helper ***************
// Dispatch the event.
@@ -1845,7 +1931,7 @@ namespace cru::ui
while (parent != last_receiver)
{
receive_list.push_back(parent);
- parent = parent->GetParent();
+ parent = parent->GetInternalParent();
}
auto handled = false;
@@ -1889,8 +1975,8 @@ namespace cru::ui
// Return nullptr if "left" and "right" are not in the same tree.
Control* FindLowestCommonAncestor(Control* left, Control* right);
- // Return the ancestor if one control is the ancestor of the other one, otherwise nullptr.
- Control* IsAncestorOrDescendant(Control* left, Control* right);
+
+ //*************** region: create helper ***************
template <typename TControl, typename... Args>
TControl* CreateWithLayout(const LayoutSideParams& width, const LayoutSideParams& height, Args&&... args)
@@ -1902,9 +1988,6 @@ namespace cru::ui
return control;
}
-
- //*************** region: create helper ***************
-
template <typename TControl, typename... Args>
TControl* CreateWithLayout(const Thickness& padding, const Thickness& margin, Args&&... args)
{
@@ -2008,7 +2091,7 @@ namespace cru::ui
- class Window final : public Control
+ class Window final : public SingleChildControl
{
friend class WindowManager;
public:
@@ -2025,6 +2108,7 @@ namespace cru::ui
explicit Window(tag_overlapped_constructor);
Window(tag_popup_constructor, Window* parent, bool caption);
+ void BeforeCreateHwnd();
void AfterCreateHwnd(WindowManager* window_manager);
public:
@@ -2111,7 +2195,7 @@ namespace cru::ui
//*************** region: position and size ***************
//Always return (0, 0) for a window.
- Point GetPositionRelative() override final;
+ Point GetOffset() override final;
//Get the size of client area for a window.
Size GetSize() override final;
@@ -2287,7 +2371,7 @@ namespace cru::ui::controls
{
// Min length of main side in layout params is of no meaning.
// All children will layout from start and redundant length is blank.
- class LinearLayout : public Control
+ class LinearLayout : public MultiChildControl
{
public:
static constexpr auto control_type = L"LinearLayout";
@@ -2344,7 +2428,7 @@ namespace cru::ui::controls
namespace cru::ui::controls
{
- class TextControl : public Control
+ class TextControl : public NoChildControl
{
protected:
TextControl(
@@ -2474,7 +2558,7 @@ namespace cru::ui::controls
namespace cru::ui::controls
{
- class ToggleButton : public Control
+ class ToggleButton : public NoChildControl
{
public:
static constexpr auto control_type = L"ToggleButton";
@@ -2536,16 +2620,15 @@ namespace cru::ui::controls
namespace cru::ui::controls
{
- class Button : public Control
+ class Button : public SingleChildControl
{
public:
static constexpr auto control_type = L"Button";
- static Button* Create(const std::initializer_list<Control*>& children = std::initializer_list<Control*>())
+ static Button* Create(Control* child = nullptr)
{
const auto button = new Button();
- for (const auto control : children)
- button->AddChild(control);
+ button->SetChild(child);
return button;
}
@@ -2634,7 +2717,7 @@ namespace cru::ui::controls
namespace cru::ui::controls
{
- class ListItem : public Control
+ class ListItem : public SingleChildControl
{
public:
static constexpr auto control_type = L"ListItem";
@@ -2654,11 +2737,10 @@ namespace cru::ui::controls
};
public:
- static ListItem* Create(const std::initializer_list<Control*>& children)
+ static ListItem* Create(Control* child = nullptr)
{
const auto list_item = new ListItem();
- for (auto control : children)
- list_item->AddChild(control);
+ list_item->SetChild(child);
return list_item;
}
@@ -2724,7 +2806,7 @@ namespace cru::ui::controls
namespace cru::ui::controls
{
- class FrameLayout : public Control
+ class FrameLayout : public MultiChildControl
{
public:
static constexpr auto control_type = L"FrameLayout";
@@ -2747,6 +2829,10 @@ namespace cru::ui::controls
~FrameLayout() override;
StringView GetControlType() const override final;
+
+ protected:
+ Size OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo& additional_info) override;
+ void OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info) override;
};
}
//--------------------------------------------------------
@@ -2771,7 +2857,7 @@ namespace cru::ui::controls
// Done: API
// Done: ScrollBar
// Done: MouseEvent
- class ScrollControl : public Control
+ class ScrollControl : public SingleChildControl
{
private:
struct ScrollBarInfo
@@ -2794,11 +2880,10 @@ namespace cru::ui::controls
Always
};
- static ScrollControl* Create(const std::initializer_list<Control*>& children = std::initializer_list<Control*>{})
+ static ScrollControl* Create(Control* child = nullptr)
{
const auto control = new ScrollControl(true);
- for (auto child : children)
- control->AddChild(child);
+ control->SetChild(child);
return control;
}