diff options
Diffstat (limited to 'src/ui/control.cpp')
-rw-r--r-- | src/ui/control.cpp | 386 |
1 files changed, 218 insertions, 168 deletions
diff --git a/src/ui/control.cpp b/src/ui/control.cpp index 8c9578df..3987e818 100644 --- a/src/ui/control.cpp +++ b/src/ui/control.cpp @@ -1,6 +1,7 @@ #include "control.hpp" #include <algorithm> +#include <cassert> #include "window.hpp" #include "application.hpp" @@ -16,8 +17,7 @@ namespace cru::ui { - Control::Control(const bool container) : - is_container_(container) + Control::Control() { mouse_leave_event.bubble.AddHandler([this](events::MouseEventArgs& args) { @@ -64,121 +64,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) - { - ThrowIfNotContainer(); - AddChildCheck(control); - this->children_.push_back(control); - - control->parent_ = this; - - this->OnAddChild(control); - } - - void Control::AddChild(Control* control, int position) + void Control::SetParent(Control* 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_parent = GetParent(); + parent_ = parent; + const auto new_parent = GetParent(); + if (old_parent != new_parent) + OnParentChanged(old_parent, new_parent); } - void Control::RemoveChild(Control* child) + void Control::SetInternalParent(Control* internal_parent) { - 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); + 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(const int position) + void Control::SetDescendantWindow(Window* window) { - 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(); } @@ -239,6 +186,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; @@ -279,11 +253,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) @@ -307,7 +281,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); @@ -318,7 +292,7 @@ namespace cru::ui OnDrawCore(device_context); - for (auto child : GetChildren()) + for (auto child : GetInternalChildren()) child->Draw(device_context); if (set_layer) @@ -465,26 +439,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) @@ -736,40 +698,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) + { + std::list<Control*> l; + while (control != nullptr) + { + l.push_front(control); + control = control->GetInternalParent(); + } + return l; + } + + 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) { - auto max_child_size = Size::Zero(); - for (auto control: GetChildren()) + if (child == child_) + return; + + const auto window = GetWindow(); + const auto old_child = child_; + child_ = child; + if (old_child) { - 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; + old_child->SetInternalParent(nullptr); + old_child->SetDescendantWindow(nullptr); } + if (child) + { + child->SetInternalParent(this); + child->SetDescendantWindow(window); + } + OnChildChanged(old_child, child); + } - // coerce size fro stretch. - for (auto control: GetChildren()) + void SingleChildControl::OnChildChanged(Control* old_child, Control* new_child) + { + + } + + Size SingleChildControl::OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo& additional_info) + { + auto child_size = Size::Zero(); + if (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); + child_->Measure(available_size, additional_info); + child_size = child_->GetDesiredSize(); } - return max_child_size; + return child_size; } - void Control::OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info) + void SingleChildControl::OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info) { - for (auto control: GetChildren()) + if (child_) { - const auto layout_params = control->GetLayoutParams(); - const auto size = control->GetDesiredSize(); + 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 { @@ -786,22 +786,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) @@ -831,25 +902,4 @@ 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; - } } |