diff options
-rw-r--r-- | CruUI-Generate/cru_ui.cpp | 565 | ||||
-rw-r--r-- | CruUI-Generate/cru_ui.hpp | 237 |
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; } |