diff --git a/include/Nazara/Widgets/BaseWidget.hpp b/include/Nazara/Widgets/BaseWidget.hpp index 6e5b33e62..738572b45 100644 --- a/include/Nazara/Widgets/BaseWidget.hpp +++ b/include/Nazara/Widgets/BaseWidget.hpp @@ -48,8 +48,8 @@ namespace Nz void EnableBackground(bool enable); - template void ForEachWidgetChild(F iterator); - template void ForEachWidgetChild(F iterator) const; + template void ForEachWidgetChild(F&& iterator, bool onlyVisible = true); + template void ForEachWidgetChild(F&& iterator, bool onlyVisible = true) const; //virtual BaseWidget* Clone() const = 0; @@ -74,6 +74,7 @@ namespace Nz inline Vector2f GetSize() const; const std::shared_ptr& GetTheme() const; + inline std::size_t GetVisibleWidgetChildCount() const; inline float GetWidth() const; inline std::size_t GetWidgetChildCount() const; @@ -108,6 +109,8 @@ namespace Nz BaseWidget& operator=(const BaseWidget&) = delete; BaseWidget& operator=(BaseWidget&&) = delete; + NazaraSignal(OnWidgetResized, const BaseWidget* /*widget*/, const Vector2f& /*size*/); + protected: virtual void Layout(); @@ -125,6 +128,10 @@ namespace Nz virtual bool IsFocusable() const; inline bool IsInside(float x, float y) const; + virtual void OnChildAdded(const BaseWidget* child); + virtual void OnChildPreferredSizeUpdated(const BaseWidget* child); + virtual void OnChildVisibilityUpdated(const BaseWidget* child); + virtual void OnChildRemoved(const BaseWidget* child); virtual void OnFocusLost(); virtual void OnFocusReceived(); virtual bool OnKeyPressed(const WindowEvent::KeyEvent& key); @@ -135,8 +142,8 @@ namespace Nz virtual bool OnMouseButtonPress(int x, int y, Mouse::Button button); virtual bool OnMouseButtonRelease(int x, int y, Mouse::Button button); virtual bool OnMouseButtonTriplePress(int x, int y, Mouse::Button button); - virtual bool OnMouseWheelMoved(int x, int y, float delta); virtual void OnMouseExit(); + virtual bool OnMouseWheelMoved(int x, int y, float delta); virtual void OnRenderLayerUpdated(int baseRenderLayer); virtual void OnParentResized(const Vector2f& newSize); virtual bool OnTextEntered(char32_t character, bool repeated); @@ -173,7 +180,7 @@ namespace Nz std::shared_ptr m_backgroundSprite; std::shared_ptr m_theme; std::vector m_entities; - std::vector> m_children; + std::vector> m_widgetChilds; entt::registry* m_registry; Canvas* m_canvas; Color m_backgroundColor; @@ -183,7 +190,7 @@ namespace Nz Vector2f m_minimumSize; Vector2f m_preferredSize; Vector2f m_size; - BaseWidget* m_widgetParent; + BaseWidget* m_parentWidget; bool m_visible; int m_baseRenderLayer; int m_renderLayerCount; diff --git a/include/Nazara/Widgets/BaseWidget.inl b/include/Nazara/Widgets/BaseWidget.inl index 02d8c20bb..a03ae3c45 100644 --- a/include/Nazara/Widgets/BaseWidget.inl +++ b/include/Nazara/Widgets/BaseWidget.inl @@ -21,7 +21,7 @@ namespace Nz m_minimumSize(0.f), m_preferredSize(-1), m_size(50.f, 50.f), - m_widgetParent(nullptr), + m_parentWidget(nullptr), m_visible(true), m_baseRenderLayer(0), m_renderLayerCount(1) @@ -44,32 +44,33 @@ namespace Nz widget->SetBaseRenderLayer(m_baseRenderLayer + m_renderLayerCount); widget->Show(widget->IsVisible() && m_visible); - m_children.emplace_back(std::move(widget)); + m_widgetChilds.emplace_back(std::move(widget)); + OnChildAdded(m_widgetChilds.back().get()); } inline void BaseWidget::Center() { - NazaraAssert(m_widgetParent, "Widget has no parent"); + NazaraAssert(m_parentWidget, "Widget has no parent"); - Vector2f parentSize = m_widgetParent->GetSize(); + Vector2f parentSize = m_parentWidget->GetSize(); Vector2f mySize = GetSize(); SetPosition((parentSize.x - mySize.x) / 2.f, (parentSize.y - mySize.y) / 2.f); } inline void BaseWidget::CenterHorizontal() { - NazaraAssert(m_widgetParent, "Widget has no parent"); + NazaraAssert(m_parentWidget, "Widget has no parent"); - Vector2f parentSize = m_widgetParent->GetSize(); + Vector2f parentSize = m_parentWidget->GetSize(); Vector2f mySize = GetSize(); SetPosition((parentSize.x - mySize.x) / 2.f, GetPosition(CoordSys::Local).y); } inline void BaseWidget::CenterVertical() { - NazaraAssert(m_widgetParent, "Widget has no parent"); + NazaraAssert(m_parentWidget, "Widget has no parent"); - Vector2f parentSize = m_widgetParent->GetSize(); + Vector2f parentSize = m_parentWidget->GetSize(); Vector2f mySize = GetSize(); SetPosition(GetPosition(CoordSys::Local).x, (parentSize.y - mySize.y) / 2.f); } @@ -80,17 +81,27 @@ namespace Nz } template - inline void BaseWidget::ForEachWidgetChild(F iterator) + void BaseWidget::ForEachWidgetChild(F&& iterator, bool onlyVisible) { - for (const auto& child : m_children) + for (const auto& child : m_widgetChilds) + { + if (onlyVisible && !child->IsVisible()) + continue; + iterator(child.get()); + } } template - inline void BaseWidget::ForEachWidgetChild(F iterator) const + void BaseWidget::ForEachWidgetChild(F&& iterator, bool onlyVisible) const { - for (const auto& child : m_children) - iterator(static_cast(child.get())); + for (const auto& child : m_widgetChilds) + { + if (onlyVisible && !child->IsVisible()) + continue; + + iterator(child.get()); + } } inline const Color& BaseWidget::GetBackgroundColor() const @@ -173,6 +184,15 @@ namespace Nz return m_theme; } + inline std::size_t BaseWidget::GetVisibleWidgetChildCount() const + { + std::size_t visibleChild = 0; + for (const auto& child : m_widgetChilds) + visibleChild += (child->IsVisible()) ? 1 : 0; + + return visibleChild; + } + inline float BaseWidget::GetWidth() const { return m_size.x; @@ -180,7 +200,7 @@ namespace Nz inline std::size_t BaseWidget::GetWidgetChildCount() const { - return m_children.size(); + return m_widgetChilds.size(); } inline void BaseWidget::Hide() @@ -227,7 +247,8 @@ namespace Nz inline void BaseWidget::SetMaximumSize(const Vector2f& maximumSize) { - m_maximumSize = maximumSize; + m_maximumSize.x = std::max(m_minimumSize.x, maximumSize.x); + m_maximumSize.y = std::max(m_minimumSize.y, maximumSize.y); Vector2f size = GetSize(); if (size.x > m_maximumSize.x || size.y > m_maximumSize.y) @@ -252,7 +273,8 @@ namespace Nz inline void BaseWidget::SetMinimumSize(const Vector2f& minimumSize) { - m_minimumSize = minimumSize; + m_minimumSize.x = std::min(minimumSize.x, m_maximumSize.x); + m_minimumSize.y = std::min(minimumSize.y, m_maximumSize.y); Vector2f size = GetSize(); if (size.x < m_minimumSize.x || size.y < m_minimumSize.y) @@ -294,16 +316,20 @@ namespace Nz OnRenderLayerUpdated(GetBaseRenderLayer()); - for (const auto& widgetPtr : m_children) + for (const auto& widgetPtr : m_widgetChilds) widgetPtr->SetBaseRenderLayer(m_baseRenderLayer + m_renderLayerCount); } } inline void BaseWidget::SetPreferredSize(const Vector2f& preferredSize) { - m_preferredSize = preferredSize; + if (m_preferredSize != preferredSize) + { + m_preferredSize = preferredSize; - //Resize(m_preferredSize); + if (m_parentWidget) + m_parentWidget->OnChildPreferredSizeUpdated(this); + } } inline void BaseWidget::SetRenderLayerCount(int renderLayerCount) @@ -311,7 +337,7 @@ namespace Nz if (m_renderLayerCount != renderLayerCount) { m_renderLayerCount = renderLayerCount; - for (const auto& widgetPtr : m_children) + for (const auto& widgetPtr : m_widgetChilds) widgetPtr->SetBaseRenderLayer(m_baseRenderLayer + m_renderLayerCount); } } @@ -323,7 +349,7 @@ namespace Nz inline void BaseWidget::NotifyParentResized(const Vector2f& newSize) { - for (const auto& widgetPtr : m_children) + for (const auto& widgetPtr : m_widgetChilds) widgetPtr->OnParentResized(newSize); } @@ -334,3 +360,4 @@ namespace Nz } #include + diff --git a/include/Nazara/Widgets/BoxLayout.hpp b/include/Nazara/Widgets/BoxLayout.hpp index f7a4c8fe0..16fd49b63 100644 --- a/include/Nazara/Widgets/BoxLayout.hpp +++ b/include/Nazara/Widgets/BoxLayout.hpp @@ -27,6 +27,13 @@ namespace Nz BoxLayout& operator=(BoxLayout&&) = delete; private: + void OnChildAdded(const BaseWidget* child) override; + void OnChildPreferredSizeUpdated(const BaseWidget* child) override; + void OnChildVisibilityUpdated(const BaseWidget* child) override; + void OnChildRemoved(const BaseWidget* child) override; + + void RecomputePreferredSize(); + struct State; std::unique_ptr m_state; diff --git a/include/Nazara/Widgets/Enums.hpp b/include/Nazara/Widgets/Enums.hpp index ccbd8b716..f46169290 100644 --- a/include/Nazara/Widgets/Enums.hpp +++ b/include/Nazara/Widgets/Enums.hpp @@ -11,8 +11,10 @@ namespace Nz { enum class BoxLayoutOrientation { - Horizontal, - Vertical + BottomToTop, + LeftToRight, + RightToLeft, + TopToBottom }; enum class CheckboxState diff --git a/src/Nazara/Widgets/AbstractTextAreaWidget.cpp b/src/Nazara/Widgets/AbstractTextAreaWidget.cpp index b2b89fab2..32fd7cb54 100644 --- a/src/Nazara/Widgets/AbstractTextAreaWidget.cpp +++ b/src/Nazara/Widgets/AbstractTextAreaWidget.cpp @@ -134,9 +134,9 @@ namespace Nz AbstractTextDrawer& textDrawer = GetTextDrawer(); textDrawer.SetMaxLineWidth(GetWidth()); - UpdateTextSprite(); } + UpdateTextSprite(); RefreshCursor(); } @@ -630,9 +630,17 @@ namespace Nz void AbstractTextAreaWidget::UpdateTextSprite() { - m_textSprite->Update(GetTextDrawer()); + const AbstractTextDrawer& textDrawer = GetTextDrawer(); + m_textSprite->Update(textDrawer); + + float preferredHeight = 0.f; + std::size_t lineCount = textDrawer.GetLineCount(); + for (std::size_t i = 0; i < lineCount; ++i) + preferredHeight += textDrawer.GetLine(i).bounds.height; + + SetPreferredSize({ -1.f, preferredHeight + s_textAreaPaddingHeight * 2.f }); + Vector2f textSize = Vector2f(m_textSprite->GetAABB().GetLengths()); - SetPreferredSize(textSize); auto& textNode = GetRegistry().get(m_textEntity); textNode.SetPosition(s_textAreaPaddingWidth, GetHeight() - s_textAreaPaddingHeight - textSize.y); diff --git a/src/Nazara/Widgets/BaseWidget.cpp b/src/Nazara/Widgets/BaseWidget.cpp index 8801920d7..adb2d0d14 100644 --- a/src/Nazara/Widgets/BaseWidget.cpp +++ b/src/Nazara/Widgets/BaseWidget.cpp @@ -34,7 +34,7 @@ namespace Nz NazaraAssert(parent->GetCanvas(), "Parent has no canvas"); m_canvas = parent->GetCanvas(); - m_widgetParent = parent; + m_parentWidget = parent; m_registry = &m_canvas->GetRegistry(); RegisterToCanvas(); @@ -75,7 +75,7 @@ namespace Nz { NazaraAssert(this != m_canvas, "Canvas cannot be destroyed by calling Destroy()"); - m_widgetParent->DestroyChild(this); //< This does delete us + m_parentWidget->DestroyChild(this); //< This does delete us } /*! @@ -110,7 +110,7 @@ namespace Nz OnRenderLayerUpdated(GetBaseRenderLayer()); - for (const auto& widgetPtr : m_children) + for (const auto& widgetPtr : m_widgetChilds) widgetPtr->SetBaseRenderLayer(m_baseRenderLayer + m_renderLayerCount); } @@ -128,14 +128,15 @@ namespace Nz std::unique_ptr BaseWidget::ReleaseFromParent() { - if (!m_widgetParent) + if (!m_parentWidget) return {}; - auto it = std::find_if(m_widgetParent->m_children.begin(), m_widgetParent->m_children.end(), [&](const std::unique_ptr& widgetPtr) { return widgetPtr.get() == this; }); - assert(it != m_widgetParent->m_children.end()); + auto it = std::find_if(m_parentWidget->m_widgetChilds.begin(), m_parentWidget->m_widgetChilds.end(), [&](const std::unique_ptr& widgetPtr) { return widgetPtr.get() == this; }); + assert(it != m_parentWidget->m_widgetChilds.end()); std::unique_ptr ownerPtr = std::move(*it); - m_widgetParent->m_children.erase(it); + m_parentWidget->m_widgetChilds.erase(it); + m_parentWidget->OnChildRemoved(this); return ownerPtr; } @@ -151,6 +152,8 @@ namespace Nz m_size = newSize; Layout(); + + OnWidgetResized(this, newSize); } void BaseWidget::SetBackgroundColor(const Color& color) @@ -183,7 +186,7 @@ namespace Nz m_renderingRect = renderingRect; UpdatePositionAndSize(); - for (const auto& widgetPtr : m_children) + for (const auto& widgetPtr : m_widgetChilds) widgetPtr->UpdatePositionAndSize(); } @@ -206,6 +209,9 @@ namespace Nz } ShowChildren(show); + + if (m_parentWidget) + m_parentWidget->OnChildVisibilityUpdated(this); } } @@ -262,6 +268,22 @@ namespace Nz return false; } + void BaseWidget::OnChildAdded(const BaseWidget* /*child*/) + { + } + + void BaseWidget::OnChildPreferredSizeUpdated(const BaseWidget* /*child*/) + { + } + + void BaseWidget::OnChildVisibilityUpdated(const BaseWidget* /*child*/) + { + } + + void BaseWidget::OnChildRemoved(const BaseWidget* /*child*/) + { + } + void BaseWidget::OnFocusLost() { } @@ -309,15 +331,15 @@ namespace Nz return OnMouseButtonPress(x, y, button); } + void BaseWidget::OnMouseExit() + { + } + bool BaseWidget::OnMouseWheelMoved(int /*x*/, int /*y*/, float /*delta*/) { return false; } - void BaseWidget::OnMouseExit() - { - } - void BaseWidget::OnRenderLayerUpdated(int /*firstRenderLayer*/) { } @@ -338,25 +360,25 @@ namespace Nz void BaseWidget::ShowChildren(bool show) { - for (const auto& widgetPtr : m_children) + for (const auto& widgetPtr : m_widgetChilds) widgetPtr->Show(show); } void BaseWidget::DestroyChild(BaseWidget* widget) { - auto it = std::find_if(m_children.begin(), m_children.end(), [widget] (const std::unique_ptr& widgetPtr) -> bool + auto it = std::find_if(m_widgetChilds.begin(), m_widgetChilds.end(), [widget] (const std::unique_ptr& widgetPtr) -> bool { return widgetPtr.get() == widget; }); - NazaraAssert(it != m_children.end(), "Child widget not found in parent"); + NazaraAssert(it != m_widgetChilds.end(), "Child widget not found in parent"); - m_children.erase(it); + m_widgetChilds.erase(it); } void BaseWidget::DestroyChildren() { - m_children.clear(); + m_widgetChilds.clear(); } void BaseWidget::RegisterToCanvas() @@ -375,7 +397,7 @@ namespace Nz NazaraAssert(oldCanvas == newCanvas, "Transferring a widget between canvas is not yet supported"); Node::SetParent(widget); - m_widgetParent = widget; + m_parentWidget = widget; Layout(); } @@ -396,9 +418,9 @@ namespace Nz Rectf scissorRect = GetScissorRect(); - if (m_widgetParent) + if (m_parentWidget) { - Rectf parentScissorRect = m_widgetParent->GetScissorRect(); + Rectf parentScissorRect = m_parentWidget->GetScissorRect(); if (!scissorRect.Intersect(parentScissorRect, &scissorRect)) scissorRect = parentScissorRect; diff --git a/src/Nazara/Widgets/BoxLayout.cpp b/src/Nazara/Widgets/BoxLayout.cpp index 33ced2c90..48599fcd8 100644 --- a/src/Nazara/Widgets/BoxLayout.cpp +++ b/src/Nazara/Widgets/BoxLayout.cpp @@ -33,20 +33,25 @@ namespace Nz BaseWidget::Layout(); std::size_t axis; + bool reversed; switch (m_orientation) { - case BoxLayoutOrientation::Horizontal: + case BoxLayoutOrientation::LeftToRight: + case BoxLayoutOrientation::RightToLeft: axis = 0; //< x + reversed = (m_orientation == BoxLayoutOrientation::RightToLeft); break; - case BoxLayoutOrientation::Vertical: + case BoxLayoutOrientation::BottomToTop: + case BoxLayoutOrientation::TopToBottom: axis = 1; //< y + reversed = (m_orientation == BoxLayoutOrientation::TopToBottom); break; } //TODO: Keep solver state when widgets don't change - std::size_t widgetChildCount = GetWidgetChildCount(); + std::size_t widgetChildCount = GetVisibleWidgetChildCount(); if (widgetChildCount == 0) return; @@ -58,27 +63,36 @@ namespace Nz kiwi::Expression sizeSum; Nz::Vector2f layoutSize = GetSize(); - float availableSpace = layoutSize[axis] - m_spacing * (widgetChildCount - 1); - float perfectSpacePerWidget = availableSpace / widgetChildCount; + float maxLayoutSize = layoutSize[axis]; + float availableSpace = maxLayoutSize - m_spacing * (widgetChildCount - 1); // Handle size ForEachWidgetChild([&](BaseWidget* child) { - if (!child->IsVisible()) - return; - float maximumSize = child->GetMaximumSize()[axis]; float minimumSize = child->GetMinimumSize()[axis]; + float preferredSize = child->GetPreferredSize()[axis]; m_state->sizeVar.emplace_back(); auto& sizeVar = m_state->sizeVar.back(); m_state->solver.addConstraint({ (sizeVar >= minimumSize) | kiwi::strength::required }); - if (maximumSize < std::numeric_limits::infinity()) + if (maximumSize < std::numeric_limits::infinity() && maximumSize <= minimumSize) m_state->solver.addConstraint({ (sizeVar <= maximumSize) | kiwi::strength::required }); - m_state->solver.addConstraint({ (sizeVar >= perfectSpacePerWidget) | kiwi::strength::medium }); + if (preferredSize > 0.f) + { + m_state->solver.addConstraint({ (sizeVar >= preferredSize) | kiwi::strength::medium }); + m_state->solver.addConstraint({ (sizeVar >= maxLayoutSize) | kiwi::strength::weak }); + } + else if (maximumSize < std::numeric_limits::infinity() && maximumSize <= minimumSize) + { + m_state->solver.addConstraint({ (sizeVar >= maximumSize) | kiwi::strength::medium }); + m_state->solver.addConstraint({ (sizeVar >= maxLayoutSize) | kiwi::strength::weak }); + } + else + m_state->solver.addConstraint({ (sizeVar >= maxLayoutSize) | kiwi::strength::medium }); sizeSum = sizeSum + sizeVar; }); @@ -98,11 +112,8 @@ namespace Nz ForEachWidgetChild([&](BaseWidget* child) { - if (!child->IsVisible()) - return; - Nz::Vector2f newSize = layoutSize; - newSize[axis] = SafeCast(m_state->sizeVar[varIndex].value()); + newSize[axis] = static_cast(m_state->sizeVar[varIndex].value()); child->Resize(newSize); remainingSize -= newSize[axis]; @@ -113,21 +124,81 @@ namespace Nz float spacing = m_spacing + remainingSize / (widgetChildCount - 1); // Handle position - float cursor = 0.f; - bool first = true; + float cursor = (reversed) ? layoutSize[axis] : 0.f; ForEachWidgetChild([&](BaseWidget* child) { - if (first) - first = false; - else - cursor += spacing; + if (reversed) + cursor -= child->GetSize()[axis]; - Nz::Vector2f position = Nz::Vector2f(0.f, 0.f); - position[axis] = cursor; + Nz::Vector2f position(0.f, 0.f); + position[axis] = cursor ; child->SetPosition(position); - cursor += child->GetSize()[axis]; + if (reversed) + cursor -= spacing; + else + cursor += child->GetSize()[axis] + spacing; }); } + + void BoxLayout::OnChildAdded(const BaseWidget* /*child*/) + { + RecomputePreferredSize(); + Layout(); + } + + void BoxLayout::OnChildPreferredSizeUpdated(const BaseWidget* /*child*/) + { + Layout(); + } + + void BoxLayout::OnChildVisibilityUpdated(const BaseWidget* /*child*/) + { + RecomputePreferredSize(); + Layout(); + } + + void BoxLayout::OnChildRemoved(const BaseWidget* /*child*/) + { + RecomputePreferredSize(); + Layout(); + } + + void BoxLayout::RecomputePreferredSize() + { + std::size_t axis; + std::size_t otherAxis; + switch (m_orientation) + { + case BoxLayoutOrientation::LeftToRight: + case BoxLayoutOrientation::RightToLeft: + axis = 0; + otherAxis = 1; + break; + + case BoxLayoutOrientation::BottomToTop: + case BoxLayoutOrientation::TopToBottom: + axis = 1; + otherAxis = 0; + break; + } + + float childCount = 0.f; + + Vector2f preferredSize; + ForEachWidgetChild([&](const BaseWidget* child) + { + const Vector2f& childPreferredSize = child->GetPreferredSize(); + preferredSize[axis] += childPreferredSize[axis]; + preferredSize[otherAxis] = std::max(preferredSize[otherAxis], childPreferredSize[otherAxis]); + + childCount += 1.f; + }); + + if (childCount > 1.f) + preferredSize[axis] += m_spacing * (childCount - 1.f); + + SetPreferredSize(preferredSize); + } } diff --git a/src/Nazara/Widgets/ButtonWidget.cpp b/src/Nazara/Widgets/ButtonWidget.cpp index 582cb9c10..66b62e87e 100644 --- a/src/Nazara/Widgets/ButtonWidget.cpp +++ b/src/Nazara/Widgets/ButtonWidget.cpp @@ -23,7 +23,7 @@ namespace Nz Vector2f size(drawer.GetBounds().GetLengths()); SetMinimumSize(size); - SetPreferredSize(size + Vector2f(20.f, 10.f)); + SetPreferredSize(size + Vector2f(20.f, 10.f)); //< corner size (TODO: Retrieve them from theme) Layout(); } diff --git a/src/Nazara/Widgets/Canvas.cpp b/src/Nazara/Widgets/Canvas.cpp index 377cc429f..1fcd32d7f 100644 --- a/src/Nazara/Widgets/Canvas.cpp +++ b/src/Nazara/Widgets/Canvas.cpp @@ -20,7 +20,7 @@ namespace Nz { m_canvas = this; BaseWidget::m_registry = &m_registry; - m_widgetParent = nullptr; + m_parentWidget = nullptr; SetBaseRenderLayer(initialRenderLayer); @@ -99,10 +99,10 @@ namespace Nz if (functor(targetWidget)) return; - if (!targetWidget.widget->m_widgetParent) + if (!targetWidget.widget->m_parentWidget) return; - widgetIndex = targetWidget.widget->m_widgetParent->m_canvasIndex; + widgetIndex = targetWidget.widget->m_parentWidget->m_canvasIndex; } } diff --git a/src/Nazara/Widgets/LabelWidget.cpp b/src/Nazara/Widgets/LabelWidget.cpp index b2841aea6..98ece0387 100644 --- a/src/Nazara/Widgets/LabelWidget.cpp +++ b/src/Nazara/Widgets/LabelWidget.cpp @@ -27,7 +27,7 @@ namespace Nz Vector2f size(drawer.GetBounds().GetLengths()); SetMinimumSize(size); - SetPreferredSize(size + Vector2f(20.f, 10.f)); + SetPreferredSize(size); Layout(); }