From 9f7de7f0775b86e3c82d4c5e3427a6f2fd98810b Mon Sep 17 00:00:00 2001 From: crupest Date: Wed, 7 Nov 2018 17:23:43 +0800 Subject: Improve layout system. --- CruUI.vcxproj | 3 +- CruUI.vcxproj.filters | 9 ++++-- src/cru_debug.cpp | 11 +++++++ src/cru_debug.h | 44 +++++++++++++++++++++++++++ src/debug_base.h | 44 --------------------------- src/main.cpp | 8 +++-- src/ui/control.cpp | 64 ++++++++++++++++++--------------------- src/ui/controls/linear_layout.cpp | 31 ++++++++----------- src/ui/controls/text_block.h | 2 ++ src/ui/layout_base.h | 9 ++++-- 10 files changed, 119 insertions(+), 106 deletions(-) create mode 100644 src/cru_debug.cpp create mode 100644 src/cru_debug.h delete mode 100644 src/debug_base.h diff --git a/CruUI.vcxproj b/CruUI.vcxproj index 03bb8e3d..e5eb1950 100644 --- a/CruUI.vcxproj +++ b/CruUI.vcxproj @@ -117,6 +117,7 @@ + @@ -144,7 +145,7 @@ - + diff --git a/CruUI.vcxproj.filters b/CruUI.vcxproj.filters index 2696b52c..17672509 100644 --- a/CruUI.vcxproj.filters +++ b/CruUI.vcxproj.filters @@ -72,6 +72,9 @@ Source Files + + Source Files + @@ -83,9 +86,6 @@ Header Files - - Header Files - Header Files @@ -146,6 +146,9 @@ Header Files + + Header Files + diff --git a/src/cru_debug.cpp b/src/cru_debug.cpp new file mode 100644 index 00000000..0007d21b --- /dev/null +++ b/src/cru_debug.cpp @@ -0,0 +1,11 @@ +#include "cru_debug.h" + +#include "system_headers.h" + +namespace cru::debug +{ + void DebugMessage(const StringView& message) + { + ::OutputDebugStringW(message.data()); + } +} diff --git a/src/cru_debug.h b/src/cru_debug.h new file mode 100644 index 00000000..a9800774 --- /dev/null +++ b/src/cru_debug.h @@ -0,0 +1,44 @@ +#pragma once + +#include + +#include "base.h" +#include "format.h" + +namespace cru::debug +{ + void DebugMessage(const StringView& message); + +#ifdef CRU_DEBUG + inline void DebugTime(const std::function& action, const StringView& hint_message) + { + const auto before = std::chrono::steady_clock::now(); + action(); + const auto after = std::chrono::steady_clock::now(); + const auto duration = std::chrono::duration_cast(after - before); + DebugMessage(Format(L"{}: {}ms.\n", hint_message, duration.count())); + } + + template + TReturn DebugTime(const std::function& action, const StringView& hint_message) + { + const auto before = std::chrono::steady_clock::now(); + auto&& result = action(); + const auto after = std::chrono::steady_clock::now(); + const auto duration = std::chrono::duration_cast(after - before); + DebugMessage(Format(L"{}: {}ms.\n", hint_message, duration.count())); + return std::move(result); + } +#else + inline void DebugTime(Function&& action, const StringView& hint_message) + { + action(); + } + + template + TReturn DebugTime(Function&& action, const StringView& hint_message) + { + return action(); + } +#endif +} diff --git a/src/debug_base.h b/src/debug_base.h deleted file mode 100644 index c750ad31..00000000 --- a/src/debug_base.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - - -#include "system_headers.h" -#include - -#include "base.h" -#include "format.h" - -namespace cru::debug -{ -#ifdef CRU_DEBUG - inline void DebugTime(const std::function& action, const StringView& hint_message) - { - const auto before = std::chrono::steady_clock::now(); - action(); - const auto after = std::chrono::steady_clock::now(); - const auto duration = std::chrono::duration_cast(after - before); - OutputDebugStringW(Format(L"{}: {}ms.\n", hint_message, duration.count()).c_str()); - } - - template - TReturn DebugTime(const std::function& action, const StringView& hint_message) - { - const auto before = std::chrono::steady_clock::now(); - auto&& result = action(); - const auto after = std::chrono::steady_clock::now(); - const auto duration = std::chrono::duration_cast(after - before); - OutputDebugStringW(Format(L"{}: {}ms.\n", hint_message, duration.count()).c_str()); - return std::move(result); - } -#else - inline void DebugTime(Function&& action, const StringView& hint_message) - { - action(); - } - - template - TReturn DebugTime(Function&& action, const StringView& hint_message) - { - return action(); - } -#endif -} diff --git a/src/main.cpp b/src/main.cpp index ab641075..a15fa4da 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -84,7 +84,7 @@ int APIENTRY wWinMain( }); */ - /* + //test 2 const auto layout = CreateWithLayout(LayoutSideParams::Exactly(500), LayoutSideParams::Content()); @@ -112,7 +112,8 @@ int APIENTRY wWinMain( { const auto button = Button::Create(); - button->AddChild(MarginContainer::Create(Thickness(20, 5), { TextBlock::Create(L"button") })); + button->GetLayoutParams()->padding = Thickness(20, 5); + button->AddChild(TextBlock::Create(L"button")); layout->AddChild(button); } @@ -138,7 +139,6 @@ int APIENTRY wWinMain( window.AddChild(layout); - */ /* window.AddChild( @@ -149,6 +149,7 @@ int APIENTRY wWinMain( )); */ + /* test 3 const auto linear_layout = CreateWithLayout(Thickness(50, 50), Thickness(50, 50), LinearLayout::Orientation::Vertical, ControlList{ Button::Create({ TextBlock::Create(L"Button") @@ -159,6 +160,7 @@ int APIENTRY wWinMain( linear_layout->SetBordered(true); window.AddChild(linear_layout); + */ window.Show(); diff --git a/src/ui/control.cpp b/src/ui/control.cpp index 93d26e81..79def066 100644 --- a/src/ui/control.cpp +++ b/src/ui/control.cpp @@ -5,6 +5,7 @@ #include "window.h" #include "graph/graph.h" #include "exception.h" +#include "cru_debug.h" namespace cru { namespace ui { @@ -697,6 +698,9 @@ namespace cru { { const auto layout_params = GetLayoutParams(); + if (!layout_params->Validate()) + throw std::runtime_error("LayoutParams is not valid. Please check it."); + auto border_size = Size::Zero(); if (is_bordered_) { @@ -704,54 +708,39 @@ namespace cru { border_size = Size(border_width * 2.0f, border_width * 2.0f); } + // the total size of padding, border and margin const auto outer_size = ThicknessToSize(layout_params->padding) + ThicknessToSize(layout_params->margin) + border_size; - if (!layout_params->Validate()) - throw std::runtime_error("LayoutParams is not valid. Please check it."); - auto&& get_available_length_for_child = [](const LayoutSideParams& layout_length, const float available_length) -> float + auto&& get_content_measure_length = [](const LayoutSideParams& layout_length, const float available_length, const float outer_length) -> float { - switch (layout_length.mode) - { - case MeasureMode::Exactly: - { - return std::min(layout_length.length, available_length); - } - case MeasureMode::Stretch: - case MeasureMode::Content: - { - return available_length; - } - default: - UnreachableCode(); - } + if (layout_length.mode == MeasureMode::Exactly) + return layout_length.length; + if (available_length > outer_length) + return available_length - outer_length; + return 0.0; }; - const auto size_for_children = AtLeast0(Size( - get_available_length_for_child(layout_params->width, available_size.width), - get_available_length_for_child(layout_params->height, available_size.height) - ) - outer_size); + // if padding, margin and border exceeded, then content size is 0. + const auto content_measure_size = Size( + get_content_measure_length(layout_params->width, available_size.width, outer_size.width), + get_content_measure_length(layout_params->height, available_size.height, outer_size.height) + ); - const auto actual_size_for_children = OnMeasureContent(size_for_children); + const auto content_actual_size = OnMeasureContent(content_measure_size); - auto&& calculate_final_length = [](const LayoutSideParams& layout_length, const float length_for_children, const float actual_length_for_children) -> float + auto&& calculate_final_length = [](const LayoutSideParams& layout_length, const float measure_length, const float actual_length) -> float { - switch (layout_length.mode) - { - case MeasureMode::Exactly: - case MeasureMode::Stretch: - return length_for_children; - case MeasureMode::Content: - return actual_length_for_children; - default: - UnreachableCode(); - } + // only use measure length when stretch and actual length is smaller than measure length, that is "stretch" + if (layout_length.mode == MeasureMode::Stretch && actual_length < measure_length) + return measure_length; + return actual_length; }; const auto final_size = Size( - calculate_final_length(layout_params->width, size_for_children.width, actual_size_for_children.width), - calculate_final_length(layout_params->height, size_for_children.height, actual_size_for_children.height) + calculate_final_length(layout_params->width, content_measure_size.width, content_actual_size.width), + calculate_final_length(layout_params->height, content_measure_size.height, content_actual_size.height) ) + outer_size; return final_size; @@ -774,6 +763,11 @@ namespace cru { rect.height - layout_params->padding.GetVerticalTotal() - layout_params->margin.GetVerticalTotal() - border_width * 2.0f ); + if (content_rect.width < 0.0) + throw std::runtime_error(Format("Width to layout must sufficient. But in {}, width for content is {}.", ToUtf8String(GetControlType()), content_rect.width)); + if (content_rect.height < 0.0) + throw std::runtime_error(Format("Height to layout must sufficient. But in {}, height for content is {}.", ToUtf8String(GetControlType()), content_rect.height)); + OnLayoutContent(content_rect); } diff --git a/src/ui/controls/linear_layout.cpp b/src/ui/controls/linear_layout.cpp index c23957ed..ed445f4c 100644 --- a/src/ui/controls/linear_layout.cpp +++ b/src/ui/controls/linear_layout.cpp @@ -27,7 +27,7 @@ namespace cru::ui::controls Size LinearLayout::OnMeasureContent(const Size& available_size) { - auto rest_available_size_for_children = available_size; + auto actual_size_for_children = Size::Zero(); float secondary_side_child_max_length = 0; @@ -40,9 +40,10 @@ namespace cru::ui::controls const auto mode = control->GetLayoutParams()->width.mode; if (mode == MeasureMode::Content || mode == MeasureMode::Exactly) { - control->Measure(AtLeast0(rest_available_size_for_children)); + Size current_available_size(AtLeast0(available_size.width - actual_size_for_children.width), available_size.height); + control->Measure(current_available_size); const auto size = control->GetDesiredSize(); - rest_available_size_for_children.width -= size.width; + actual_size_for_children.width += size.width; secondary_side_child_max_length = std::max(size.height, secondary_side_child_max_length); } else @@ -54,9 +55,10 @@ namespace cru::ui::controls const auto mode = control->GetLayoutParams()->height.mode; if (mode == MeasureMode::Content || mode == MeasureMode::Exactly) { - control->Measure(AtLeast0(rest_available_size_for_children)); + Size current_available_size(available_size.width, AtLeast0(available_size.height - actual_size_for_children.height)); + control->Measure(current_available_size); const auto size = control->GetDesiredSize(); - rest_available_size_for_children.height -= size.height; + actual_size_for_children.height += size.height; secondary_side_child_max_length = std::max(size.width, secondary_side_child_max_length); } else @@ -65,38 +67,31 @@ namespace cru::ui::controls if (orientation_ == Orientation::Horizontal) { - const auto available_width = rest_available_size_for_children.width / stretch_control_list.size(); + const auto available_width = AtLeast0(available_size.width - actual_size_for_children.width) / stretch_control_list.size(); for (const auto control : stretch_control_list) { - control->Measure(Size(AtLeast0(available_width), rest_available_size_for_children.height)); + control->Measure(Size(available_width, available_size.height)); const auto size = control->GetDesiredSize(); - rest_available_size_for_children.width -= size.width; + actual_size_for_children.width += size.width; secondary_side_child_max_length = std::max(size.height, secondary_side_child_max_length); } } else { - const auto available_height = rest_available_size_for_children.height / stretch_control_list.size(); + const auto available_height = AtLeast0(available_size.height - actual_size_for_children.height) / stretch_control_list.size(); for (const auto control : stretch_control_list) { - control->Measure(Size(rest_available_size_for_children.width, AtLeast0(available_height))); + control->Measure(Size(available_size.width, available_height)); const auto size = control->GetDesiredSize(); - rest_available_size_for_children.height -= size.height; + actual_size_for_children.height += size.height; secondary_side_child_max_length = std::max(size.width, secondary_side_child_max_length); } } - auto actual_size_for_children = available_size; if (orientation_ == Orientation::Horizontal) - { - actual_size_for_children.width -= rest_available_size_for_children.width; actual_size_for_children.height = secondary_side_child_max_length; - } else - { actual_size_for_children.width = secondary_side_child_max_length; - actual_size_for_children.height -= rest_available_size_for_children.height; - } return actual_size_for_children; } diff --git a/src/ui/controls/text_block.h b/src/ui/controls/text_block.h index cd0af1cc..681dc47b 100644 --- a/src/ui/controls/text_block.h +++ b/src/ui/controls/text_block.h @@ -19,6 +19,8 @@ namespace cru::ui::controls return text_block; } + using TextControl::SetSelectable; // Make this public. + protected: TextBlock( const Microsoft::WRL::ComPtr& init_text_format, diff --git a/src/ui/layout_base.h b/src/ui/layout_base.h index 0d924436..662210bd 100644 --- a/src/ui/layout_base.h +++ b/src/ui/layout_base.h @@ -63,6 +63,11 @@ namespace cru return top + bottom; } + float Validate() const + { + return left >= 0.0 && top >= 0.0 && right >= 0.0 && bottom >= 0.0; + } + float left; float top; float right; @@ -96,7 +101,7 @@ namespace cru constexpr bool Validate() const { - return !(mode == MeasureMode::Exactly && length < 0.0); + return length >= 0.0; } float length = 0.0; @@ -115,7 +120,7 @@ namespace cru bool Validate() const { - return width.Validate() && height.Validate(); + return width.Validate() && height.Validate() && margin.Validate() && padding.Validate(); } LayoutSideParams width; -- cgit v1.2.3