From 81fd0725d020e9f302c0d40fd5a5700d3dc871aa Mon Sep 17 00:00:00 2001 From: crupest Date: Thu, 6 Dec 2018 05:44:38 +0800 Subject: Done. --- src/ui/controls/button.cpp | 2 +- src/ui/controls/button.hpp | 7 ++-- src/ui/controls/frame_layout.cpp | 62 +++++++++++++++++++++++++++++++++--- src/ui/controls/frame_layout.hpp | 6 +++- src/ui/controls/linear_layout.cpp | 12 +++---- src/ui/controls/linear_layout.hpp | 2 +- src/ui/controls/list_item.cpp | 2 +- src/ui/controls/list_item.hpp | 7 ++-- src/ui/controls/popup_menu.cpp | 4 +-- src/ui/controls/scroll_control.cpp | 65 +++++++++++++++++++------------------- src/ui/controls/scroll_control.hpp | 7 ++-- src/ui/controls/text_control.cpp | 2 +- src/ui/controls/text_control.hpp | 2 +- src/ui/controls/toggle_button.hpp | 2 +- 14 files changed, 118 insertions(+), 64 deletions(-) (limited to 'src/ui/controls') diff --git a/src/ui/controls/button.cpp b/src/ui/controls/button.cpp index a9f101f8..d4537f54 100644 --- a/src/ui/controls/button.cpp +++ b/src/ui/controls/button.cpp @@ -5,7 +5,7 @@ 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} { diff --git a/src/ui/controls/button.hpp b/src/ui/controls/button.hpp index c53f7ed9..82694fe8 100644 --- a/src/ui/controls/button.hpp +++ b/src/ui/controls/button.hpp @@ -9,16 +9,15 @@ 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& children = std::initializer_list()) + static Button* Create(Control* child = nullptr) { const auto button = new Button(); - for (const auto control : children) - button->AddChild(control); + button->SetChild(child); return button; } diff --git a/src/ui/controls/frame_layout.cpp b/src/ui/controls/frame_layout.cpp index 32d25edc..d68bc338 100644 --- a/src/ui/controls/frame_layout.cpp +++ b/src/ui/controls/frame_layout.cpp @@ -2,10 +2,7 @@ namespace cru::ui::controls { - FrameLayout::FrameLayout() : Control(true) - { - - } + FrameLayout::FrameLayout() = default; FrameLayout::~FrameLayout() = default; @@ -13,4 +10,61 @@ 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); + } + } } diff --git a/src/ui/controls/frame_layout.hpp b/src/ui/controls/frame_layout.hpp index 45971584..c2d6f0d6 100644 --- a/src/ui/controls/frame_layout.hpp +++ b/src/ui/controls/frame_layout.hpp @@ -9,7 +9,7 @@ namespace cru::ui::controls { - class FrameLayout : public Control + class FrameLayout : public MultiChildControl { public: static constexpr auto control_type = L"FrameLayout"; @@ -32,5 +32,9 @@ 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; }; } diff --git a/src/ui/controls/linear_layout.cpp b/src/ui/controls/linear_layout.cpp index 2b8f3e43..d3fdc9b5 100644 --- a/src/ui/controls/linear_layout.cpp +++ b/src/ui/controls/linear_layout.cpp @@ -7,7 +7,7 @@ namespace cru::ui::controls { LinearLayout::LinearLayout(const Orientation orientation) - : Control(true), orientation_(orientation) + : orientation_(orientation) { } @@ -27,7 +27,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) @@ -42,7 +42,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) @@ -82,7 +82,7 @@ namespace cru::ui::controls if (orientation_ == Orientation::Horizontal) { - for (auto control : GetChildren()) + for (auto control : GetInternalChildren()) { if (control->GetLayoutParams()->height.mode == MeasureMode::Stretch) { @@ -93,7 +93,7 @@ namespace cru::ui::controls } else { - for (auto control : GetChildren()) + for (auto control : GetInternalChildren()) { if (control->GetLayoutParams()->width.mode == MeasureMode::Stretch) { @@ -110,7 +110,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(); diff --git a/src/ui/controls/linear_layout.hpp b/src/ui/controls/linear_layout.hpp index 96becc6f..ceb1c4e6 100644 --- a/src/ui/controls/linear_layout.hpp +++ b/src/ui/controls/linear_layout.hpp @@ -9,7 +9,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"; diff --git a/src/ui/controls/list_item.cpp b/src/ui/controls/list_item.cpp index 8234da30..e0ca28a9 100644 --- a/src/ui/controls/list_item.cpp +++ b/src/ui/controls/list_item.cpp @@ -5,7 +5,7 @@ namespace cru::ui::controls { - ListItem::ListItem() : Control(true) + ListItem::ListItem() { const auto predefine_resources = UiManager::GetInstance()->GetPredefineResources(); diff --git a/src/ui/controls/list_item.hpp b/src/ui/controls/list_item.hpp index e150efbb..a50b2496 100644 --- a/src/ui/controls/list_item.hpp +++ b/src/ui/controls/list_item.hpp @@ -10,7 +10,7 @@ namespace cru::ui::controls { - class ListItem : public Control + class ListItem : public SingleChildControl { public: static constexpr auto control_type = L"ListItem"; @@ -30,11 +30,10 @@ namespace cru::ui::controls }; public: - static ListItem* Create(const std::initializer_list& 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; } diff --git a/src/ui/controls/popup_menu.cpp b/src/ui/controls/popup_menu.cpp index 7f4b9d08..fbe9039d 100644 --- a/src/ui/controls/popup_menu.cpp +++ b/src/ui/controls/popup_menu.cpp @@ -26,7 +26,7 @@ namespace cru::ui::controls auto list_item = CreateWithLayout( 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) @@ -48,7 +48,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); diff --git a/src/ui/controls/scroll_control.cpp b/src/ui/controls/scroll_control.cpp index 8358abc5..ae99f414 100644 --- a/src/ui/controls/scroll_control.cpp +++ b/src/ui/controls/scroll_control.cpp @@ -3,7 +3,6 @@ #include #include "cru_debug.hpp" -#include "format.hpp" #include "ui/convert_util.hpp" #include "exception.hpp" #include "math_util.hpp" @@ -14,7 +13,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); @@ -245,38 +244,25 @@ namespace cru::ui::controls available_size_for_children.height = std::numeric_limits::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; } @@ -292,18 +278,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); } } @@ -327,7 +326,7 @@ namespace cru::ui::controls if (update_children) { - for (auto child : GetChildren()) + if (const auto child = GetChild()) { const auto old_position = child->GetPositionRelative(); child->SetRect(Rect(Point( diff --git a/src/ui/controls/scroll_control.hpp b/src/ui/controls/scroll_control.hpp index db29b141..7138add6 100644 --- a/src/ui/controls/scroll_control.hpp +++ b/src/ui/controls/scroll_control.hpp @@ -17,7 +17,7 @@ namespace cru::ui::controls // Done: API // Done: ScrollBar // Done: MouseEvent - class ScrollControl : public Control + class ScrollControl : public SingleChildControl { private: struct ScrollBarInfo @@ -40,11 +40,10 @@ namespace cru::ui::controls Always }; - static ScrollControl* Create(const std::initializer_list& children = std::initializer_list{}) + static ScrollControl* Create(Control* child = nullptr) { const auto control = new ScrollControl(true); - for (auto child : children) - control->AddChild(child); + control->SetChild(child); return control; } diff --git a/src/ui/controls/text_control.cpp b/src/ui/controls/text_control.cpp index 6412eec9..e53d3c69 100644 --- a/src/ui/controls/text_control.cpp +++ b/src/ui/controls/text_control.cpp @@ -44,7 +44,7 @@ namespace cru::ui::controls } TextControl::TextControl(const Microsoft::WRL::ComPtr& init_text_format, - const Microsoft::WRL::ComPtr& init_brush) : Control(false) + const Microsoft::WRL::ComPtr& init_brush) { text_format_ = init_text_format; diff --git a/src/ui/controls/text_control.hpp b/src/ui/controls/text_control.hpp index 9b83f4bb..58d48c13 100644 --- a/src/ui/controls/text_control.hpp +++ b/src/ui/controls/text_control.hpp @@ -7,7 +7,7 @@ namespace cru::ui::controls { - class TextControl : public Control + class TextControl : public NoChildControl { protected: TextControl( diff --git a/src/ui/controls/toggle_button.hpp b/src/ui/controls/toggle_button.hpp index 091f908a..dee655d4 100644 --- a/src/ui/controls/toggle_button.hpp +++ b/src/ui/controls/toggle_button.hpp @@ -7,7 +7,7 @@ namespace cru::ui::controls { - class ToggleButton : public Control + class ToggleButton : public NoChildControl { public: static constexpr auto control_type = L"ToggleButton"; -- cgit v1.2.3 From 03ef76f769a55b694905898c16a176fc6bd4b0d7 Mon Sep 17 00:00:00 2001 From: crupest Date: Thu, 6 Dec 2018 21:09:12 +0800 Subject: Fix position cache bug. --- src/ui/control.cpp | 33 ++++++++++++++++++++++++++++++--- src/ui/control.hpp | 16 +++++++++++----- src/ui/controls/scroll_control.cpp | 3 ++- src/ui/window.cpp | 2 +- src/ui/window.hpp | 2 +- 5 files changed, 45 insertions(+), 11 deletions(-) (limited to 'src/ui/controls') diff --git a/src/ui/control.cpp b/src/ui/control.cpp index 32168891..3987e818 100644 --- a/src/ui/control.cpp +++ b/src/ui/control.cpp @@ -125,7 +125,7 @@ namespace cru::ui TraverseDescendantsInternal(this, predicate); } - Point Control::GetPositionRelative() + Point Control::GetOffset() { return rect_.GetLeftTop(); } @@ -186,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; @@ -230,7 +257,7 @@ namespace cru::ui 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) @@ -254,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); diff --git a/src/ui/control.hpp b/src/ui/control.hpp index 41d1cfe9..6abcc365 100644 --- a/src/ui/control.hpp +++ b/src/ui/control.hpp @@ -18,7 +18,6 @@ namespace cru::ui { - class Control; class Window; @@ -68,6 +67,8 @@ namespace cru::ui //*************** region: tree *************** virtual StringView GetControlType() const = 0; + virtual const std::vector& GetInternalChildren() const = 0; + Control* GetParent() const { return parent_ == nullptr ? internal_parent_ : parent_; @@ -84,9 +85,6 @@ namespace cru::ui return window_; } - - virtual const std::vector& GetInternalChildren() const = 0; - void SetParent(Control* parent); void SetInternalParent(Control* internal_parent); @@ -100,11 +98,12 @@ namespace cru::ui //*************** 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 @@ -118,6 +117,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); diff --git a/src/ui/controls/scroll_control.cpp b/src/ui/controls/scroll_control.cpp index ae99f414..622b4e4c 100644 --- a/src/ui/controls/scroll_control.cpp +++ b/src/ui/controls/scroll_control.cpp @@ -328,11 +328,12 @@ namespace cru::ui::controls { 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(); diff --git a/src/ui/window.cpp b/src/ui/window.cpp index 6f1c4071..86fa4436 100644 --- a/src/ui/window.cpp +++ b/src/ui/window.cpp @@ -469,7 +469,7 @@ namespace cru::ui return PiToDip(point); } - Point Window::GetPositionRelative() + Point Window::GetOffset() { return Point(); } diff --git a/src/ui/window.hpp b/src/ui/window.hpp index e4c910e8..e96d4d92 100644 --- a/src/ui/window.hpp +++ b/src/ui/window.hpp @@ -189,7 +189,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; -- cgit v1.2.3