diff options
-rw-r--r-- | CruUI/CruUI.vcxproj | 2 | ||||
-rw-r--r-- | CruUI/CruUI.vcxproj.filters | 6 | ||||
-rw-r--r-- | CruUI/main.cpp | 62 | ||||
-rw-r--r-- | CruUI/ui/control.cpp | 13 | ||||
-rw-r--r-- | CruUI/ui/control.h | 2 | ||||
-rw-r--r-- | CruUI/ui/controls/linear_layout.cpp | 88 | ||||
-rw-r--r-- | CruUI/ui/controls/linear_layout.h | 25 | ||||
-rw-r--r-- | CruUI/ui/controls/text_block.cpp | 18 | ||||
-rw-r--r-- | CruUI/ui/layout_base.h | 2 | ||||
-rw-r--r-- | CruUI/ui/ui_base.h | 5 | ||||
-rw-r--r-- | CruUI/ui/window.cpp | 5 | ||||
-rw-r--r-- | CruUI/ui/window.h | 2 |
12 files changed, 192 insertions, 38 deletions
diff --git a/CruUI/CruUI.vcxproj b/CruUI/CruUI.vcxproj index 7d02c7c0..ba46d963 100644 --- a/CruUI/CruUI.vcxproj +++ b/CruUI/CruUI.vcxproj @@ -160,6 +160,7 @@ <ClInclude Include="timer.h" /> <ClInclude Include="ui\control.h" /> <ClInclude Include="global_macros.h" /> + <ClInclude Include="ui\controls\linear_layout.h" /> <ClInclude Include="ui\controls\text_block.h" /> <ClInclude Include="ui\events\ui_event.h" /> <ClInclude Include="ui\layout_base.h" /> @@ -174,6 +175,7 @@ <ClCompile Include="graph\graph.cpp" /> <ClCompile Include="timer.cpp" /> <ClCompile Include="ui\control.cpp" /> + <ClCompile Include="ui\controls\linear_layout.cpp" /> <ClCompile Include="ui\controls\text_block.cpp" /> <ClCompile Include="ui\events\ui_event.cpp" /> <ClCompile Include="ui\window.cpp" /> diff --git a/CruUI/CruUI.vcxproj.filters b/CruUI/CruUI.vcxproj.filters index 682c6b5c..a282cfda 100644 --- a/CruUI/CruUI.vcxproj.filters +++ b/CruUI/CruUI.vcxproj.filters @@ -57,6 +57,9 @@ <ClInclude Include="ui\controls\text_block.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="ui\controls\linear_layout.h"> + <Filter>Header Files</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ClCompile Include="application.cpp"> @@ -92,5 +95,8 @@ <ClCompile Include="ui\controls\text_block.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="ui\controls\linear_layout.cpp"> + <Filter>Source Files</Filter> + </ClCompile> </ItemGroup> </Project>
\ No newline at end of file diff --git a/CruUI/main.cpp b/CruUI/main.cpp index 77ee44c0..bd0d3247 100644 --- a/CruUI/main.cpp +++ b/CruUI/main.cpp @@ -1,14 +1,13 @@ -#include <random> - #include "application.h" #include "ui/window.h" -#include "timer.h" +#include "ui/controls/linear_layout.h" #include "ui/controls/text_block.h" using cru::Application; using cru::ui::Window; - +using cru::ui::controls::LinearLayout; +using cru::ui::controls::TextBlock; int APIENTRY wWinMain( @@ -17,9 +16,11 @@ int APIENTRY wWinMain( LPWSTR lpCmdLine, int nCmdShow) { - Application application(hInstance); + Application application(hInstance); Window window; + /* + // test1 cru::ui::controls::TextBlock text_block; text_block.SetText(L"Hello world!"); text_block.SetSize(cru::ui::Size(200, 30)); @@ -38,29 +39,46 @@ int APIENTRY wWinMain( std::uniform_int_distribution<decltype(colors.size())> uni(0, colors.size() - 1); // guaranteed unbiased - window.draw_event.AddHandler([&](cru::ui::events::DrawEventArgs& args) { - auto device_context = args.GetDeviceContext(); + window.draw_event.AddHandler([&](cru::ui::events::DrawEventArgs& args) { + auto device_context = args.GetDeviceContext(); + + ID2D1SolidColorBrush* brush; + device_context->CreateSolidColorBrush(colors[uni(rng)], &brush); + + device_context->FillRectangle(D2D1::RectF(100.0f, 100.0f, 300.0f, 200.0f), brush); + + brush->Release(); + }); + + cru::SetTimeout(2.0, [&window]() { + window.Repaint(); - ID2D1SolidColorBrush* brush; - device_context->CreateSolidColorBrush(colors[uni(rng)], &brush); + auto task = cru::SetInterval(0.5, [&window]() { + window.Repaint(); + }); - device_context->FillRectangle(D2D1::RectF(100.0f, 100.0f, 300.0f, 200.0f), brush); + cru::SetTimeout(4, [task]() { + task->Cancel(); + task->Cancel(); // test for idempotency. + }); + }); + */ - brush->Release(); - }); + //test 2 - cru::SetTimeout(2.0, [&window]() { - window.Repaint(); + LinearLayout layout; + TextBlock text_block_1; + text_block_1.SetText(L"Hello World!!!"); + TextBlock text_block_2; + text_block_2.SetText(L"This is a very very very very very long sentence!!!"); + TextBlock text_block_3; + text_block_3.SetText(L"By crupest!!!"); - auto task = cru::SetInterval(0.5, [&window]() { - window.Repaint(); - }); + layout.AddChild(&text_block_1); + layout.AddChild(&text_block_2); + layout.AddChild(&text_block_3); - cru::SetTimeout(4, [task]() { - task->Cancel(); - task->Cancel(); // test for idempotency. - }); - }); + window.AddChild(&layout); window.Show(); diff --git a/CruUI/ui/control.cpp b/CruUI/ui/control.cpp index 20aeb900..ee6db284 100644 --- a/CruUI/ui/control.cpp +++ b/CruUI/ui/control.cpp @@ -19,7 +19,7 @@ namespace cru { size_(Size::zero), position_cache_(), is_mouse_inside_(false), - layout_params_(nullptr), + layout_params_(new BasicLayoutParams()), desired_size_(Size::zero) { @@ -27,14 +27,12 @@ namespace cru { void Control::ForeachChild(Action<Control*>&& predicate) const { - ThrowIfNotContainer(); for (const auto child : children_) predicate(child); } void Control::ForeachChild(FlowControlAction<Control*>&& predicate) const { - ThrowIfNotContainer(); for (const auto child : children_) { if (predicate(child) == FlowControl::Break) @@ -130,7 +128,6 @@ namespace cru { void Control::TraverseDescendants(Action<Control*>&& predicate) { - ThrowIfNotContainer(); TraverseDescendantsInternal(this, predicate); } @@ -231,6 +228,12 @@ namespace cru { return window->GetFocusControl() == this; } + void Control::Relayout() + { + OnMeasure(GetSize()); + OnLayout(Rect(GetPositionRelative(), GetSize())); + } + void Control::Measure(const Size& available_size) { SetDesiredSize(OnMeasure(available_size)); @@ -409,7 +412,7 @@ namespace cru { } }; - Size size_for_children; + Size size_for_children; // NOLINT(cppcoreguidelines-pro-type-member-init) size_for_children.width = get_available_length_for_child(layout_params->width, available_size.width); size_for_children.height = get_available_length_for_child(layout_params->height, available_size.height); diff --git a/CruUI/ui/control.h b/CruUI/ui/control.h index b9dccf19..f4302e80 100644 --- a/CruUI/ui/control.h +++ b/CruUI/ui/control.h @@ -136,6 +136,8 @@ namespace cru //*************** region: layout *************** + void Relayout(); + void Measure(const Size& available_size); void Layout(const Rect& rect); diff --git a/CruUI/ui/controls/linear_layout.cpp b/CruUI/ui/controls/linear_layout.cpp new file mode 100644 index 00000000..f639720d --- /dev/null +++ b/CruUI/ui/controls/linear_layout.cpp @@ -0,0 +1,88 @@ +#include "linear_layout.h" + +namespace cru::ui::controls +{ + LinearLayout::LinearLayout(const Orientation orientation) + : Control(true), orientation_(orientation) + { + + } + + Size LinearLayout::OnMeasure(const Size& available_size) + { + const auto layout_params = GetLayoutParams(); + + if (!layout_params->Validate()) + throw std::runtime_error("LayoutParams is not valid. Please check it."); + + auto&& get_available_length_for_child = [](const MeasureLength& layout_length, const float available_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(); + } + }; + + Size total_available_size_for_children( + get_available_length_for_child(layout_params->width, available_size.width), + get_available_length_for_child(layout_params->height, available_size.height) + ); + + auto rest_available_size_for_children = total_available_size_for_children; + + ForeachChild([this, &rest_available_size_for_children](Control* const control) + { + control->Measure(rest_available_size_for_children); + if (orientation_ == Orientation::Horizontal) + rest_available_size_for_children.width -= control->GetDesiredSize().width; + else + rest_available_size_for_children.height -= control->GetDesiredSize().height; + }); + + auto actual_size_for_children = total_available_size_for_children - rest_available_size_for_children; + + auto&& calculate_final_length = [](const MeasureLength& layout_length, const float length_for_children, const float max_child_length) -> float + { + switch (layout_length.mode) + { + case MeasureMode::Exactly: + case MeasureMode::Stretch: + return length_for_children; + case MeasureMode::Content: + return max_child_length; + default: + UnreachableCode(); + } + }; + + return Size( + calculate_final_length(layout_params->width, total_available_size_for_children.width, actual_size_for_children.width), + calculate_final_length(layout_params->height, total_available_size_for_children.height, actual_size_for_children.height) + ); + } + + void LinearLayout::OnLayout(const Rect& rect) + { + auto current_anchor = Point::zero; + ForeachChild([this, ¤t_anchor](Control* control) + { + const auto size = control->GetDesiredSize(); + control->Layout(Rect(current_anchor, size)); + + if (orientation_ == Orientation::Horizontal) + current_anchor.x += size.width; + else + current_anchor.y += size.height; + }); + } +} diff --git a/CruUI/ui/controls/linear_layout.h b/CruUI/ui/controls/linear_layout.h new file mode 100644 index 00000000..b8722b6f --- /dev/null +++ b/CruUI/ui/controls/linear_layout.h @@ -0,0 +1,25 @@ +#pragma once + +#include "ui/control.h" + +namespace cru::ui::controls +{ + class LinearLayout : public Control + { + public: + enum class Orientation + { + Horizontal, + Vertical + }; + + explicit LinearLayout(Orientation orientation = Orientation::Vertical); + + protected: + Size OnMeasure(const Size& available_size) override; + void OnLayout(const Rect& rect) override; + + private: + Orientation orientation_; + }; +} diff --git a/CruUI/ui/controls/text_block.cpp b/CruUI/ui/controls/text_block.cpp index 86d01896..3649aea6 100644 --- a/CruUI/ui/controls/text_block.cpp +++ b/CruUI/ui/controls/text_block.cpp @@ -53,7 +53,8 @@ namespace cru void TextBlock::OnDraw(ID2D1DeviceContext* device_context) { - device_context->DrawTextLayout(D2D1::Point2F(), text_layout_.Get(), brush_.Get()); + if (text_layout_ != nullptr) + device_context->DrawTextLayout(D2D1::Point2F(), text_layout_.Get(), brush_.Get()); } Size TextBlock::OnMeasure(const Size& available_size) @@ -67,7 +68,7 @@ namespace cru if (layout_params->width.mode == MeasureMode::Stretch && layout_params->height.mode == MeasureMode::Stretch) return available_size; - Size measure_size; + Size measure_size; // NOLINT(cppcoreguidelines-pro-type-member-init) auto&& get_measure_length = [](const MeasureLength& layout_length, const float available_length) -> float { @@ -95,8 +96,9 @@ namespace cru const auto dwrite_factory = GetDWriteFactory(); - dwrite_factory->CreateTextLayout(text_.c_str(), text_.size(), - text_format_.Get(), measure_size.width, measure_size.height, &measure_text_layout); + ThrowIfFailed(dwrite_factory->CreateTextLayout(text_.c_str(), text_.size(), + text_format_.Get(), measure_size.width, measure_size.height, &measure_text_layout) + ); DWRITE_TEXT_METRICS metrics{}; measure_text_layout->GetMetrics(&metrics); @@ -113,10 +115,12 @@ namespace cru return measure_result_length; }; - return Size( + const Size result_size( calculate_final_length(layout_params->width, measure_size.width, measure_result.width), calculate_final_length(layout_params->height, measure_size.height, measure_result.height) ); + + return result_size; } void TextBlock::OnTextChangedCore(const String& old_text, const String& new_text) @@ -162,12 +166,12 @@ namespace cru const auto&& size = GetSize(); - dwrite_factory->CreateTextLayout( + ThrowIfFailed(dwrite_factory->CreateTextLayout( text_.c_str(), text_.size(), text_format_.Get(), size.width, size.height, &text_layout_ - ); + )); std::for_each(text_layout_handlers_.cbegin(), text_layout_handlers_.cend(), [this](const std::shared_ptr<TextLayoutHandler>& handler) { diff --git a/CruUI/ui/layout_base.h b/CruUI/ui/layout_base.h index 0213b879..bae01949 100644 --- a/CruUI/ui/layout_base.h +++ b/CruUI/ui/layout_base.h @@ -13,7 +13,7 @@ namespace cru struct MeasureLength final { - explicit MeasureLength(const float length = 0.0, const MeasureMode mode = MeasureMode::Exactly) + explicit MeasureLength(const float length = 0.0, const MeasureMode mode = MeasureMode::Content) : length(length), mode(mode) { diff --git a/CruUI/ui/ui_base.h b/CruUI/ui/ui_base.h index 43b4a6dc..15847c1d 100644 --- a/CruUI/ui/ui_base.h +++ b/CruUI/ui/ui_base.h @@ -36,6 +36,11 @@ namespace cru float height; }; + inline Size operator - (const Size& left, const Size& right) + { + return Size(left.width - right.width, left.height - right.height); + } + struct Rect { Rect() = default; diff --git a/CruUI/ui/window.cpp b/CruUI/ui/window.cpp index 4ab38cad..2c731a1e 100644 --- a/CruUI/ui/window.cpp +++ b/CruUI/ui/window.cpp @@ -136,7 +136,7 @@ namespace cru }); } - Window::Window() : layout_manager_(new WindowLayoutManager()), control_list_({ this }) { + Window::Window() : Control(true), layout_manager_(new WindowLayoutManager()), control_list_({ this }) { auto app = Application::GetInstance(); hwnd_ = CreateWindowEx(0, app->GetWindowManager()->GetGeneralWindowClass()->GetName(), @@ -342,7 +342,7 @@ namespace cru void Window::SetSize(const Size & size) { - SetClientSize(size); + } void Window::RefreshControlList() { @@ -429,6 +429,7 @@ namespace cru void Window::OnResizeInternal(int new_width, int new_height) { render_target_->ResizeBuffer(new_width, new_height); + Relayout(); } void Window::OnSetFocusInternal() diff --git a/CruUI/ui/window.h b/CruUI/ui/window.h index cdc67362..7ecea456 100644 --- a/CruUI/ui/window.h +++ b/CruUI/ui/window.h @@ -184,7 +184,7 @@ namespace cru { //Get the size of client area for a window. Size GetSize() override final; - //Set the size of client area for a window. + //This method has no effect for a window. Use SetClientSize instead. void SetSize(const Size& size) override final; |