diff --git a/SDK/include/NDK/BaseWidget.hpp b/SDK/include/NDK/BaseWidget.hpp index 12ab85cd1..26c7f5539 100644 --- a/SDK/include/NDK/BaseWidget.hpp +++ b/SDK/include/NDK/BaseWidget.hpp @@ -46,44 +46,61 @@ namespace Ndk void EnableBackground(bool enable); + template void ForEachWidgetChild(F iterator); + template void ForEachWidgetChild(F iterator) const; + //virtual BaseWidget* Clone() const = 0; inline const Nz::Color& GetBackgroundColor() const; inline Canvas* GetCanvas(); inline Nz::SystemCursor GetCursor() const; - inline const Padding& GetPadding() const; - inline Nz::Vector2f GetContentOrigin() const; - inline const Nz::Vector2f& GetContentSize() const; + inline float GetHeight() const; + + inline float GetMaximumHeight() const; + inline Nz::Vector2f GetMaximumSize() const; + inline float GetMaximumWidth() const; + + inline float GetMinimumHeight() const; + inline Nz::Vector2f GetMinimumSize() const; + inline float GetMinimumWidth() const; + + inline float GetPreferredHeight() const; + inline Nz::Vector2f GetPreferredSize() const; + inline float GetPreferredWidth() const; + inline Nz::Vector2f GetSize() const; + inline float GetWidth() const; + inline std::size_t GetWidgetChildCount() const; bool HasFocus() const; inline bool IsVisible() const; - virtual void ResizeToContent() = 0; + void Resize(const Nz::Vector2f& size); void SetBackgroundColor(const Nz::Color& color); void SetCursor(Nz::SystemCursor systemCursor); - inline void SetContentSize(const Nz::Vector2f& size); void SetFocus(); - inline void SetPadding(float left, float top, float right, float bottom); - void SetSize(const Nz::Vector2f& size); + + inline void SetFixedHeight(float fixedHeight); + inline void SetFixedSize(const Nz::Vector2f& fixedSize); + inline void SetFixedWidth(float fixedWidth); + + inline void SetMaximumHeight(float maximumHeight); + inline void SetMaximumSize(const Nz::Vector2f& maximumSize); + inline void SetMaximumWidth(float maximumWidth); + + inline void SetMinimumHeight(float minimumHeight); + inline void SetMinimumSize(const Nz::Vector2f& minimumSize); + inline void SetMinimumWidth(float minimumWidth); void Show(bool show = true); BaseWidget& operator=(const BaseWidget&) = delete; BaseWidget& operator=(BaseWidget&&) = delete; - struct Padding - { - float left; - float top; - float right; - float bottom; - }; - protected: - const EntityHandle& CreateEntity(bool isContentEntity); + const EntityHandle& CreateEntity(); void DestroyEntity(Entity* entity); virtual void Layout(); @@ -102,10 +119,12 @@ namespace Ndk virtual void OnParentResized(const Nz::Vector2f& newSize); virtual void OnTextEntered(char32_t character, bool repeated); + inline void SetPreferredSize(const Nz::Vector2f& preferredSize); + private: inline BaseWidget(); - inline void DestroyChild(BaseWidget* widget); + void DestroyChild(BaseWidget* widget); void DestroyChildren(); inline bool IsRegisteredToCanvas() const; inline void NotifyParentResized(const Nz::Vector2f& newSize); @@ -117,7 +136,6 @@ namespace Ndk struct WidgetEntity { EntityOwner handle; - bool isContent; }; static constexpr std::size_t InvalidCanvasIndex = std::numeric_limits::max(); @@ -127,12 +145,14 @@ namespace Ndk std::vector> m_children; Canvas* m_canvas; EntityOwner m_backgroundEntity; - Padding m_padding; WorldHandle m_world; Nz::Color m_backgroundColor; Nz::SpriteRef m_backgroundSprite; Nz::SystemCursor m_cursor; - Nz::Vector2f m_contentSize; + Nz::Vector2f m_maximumSize; + Nz::Vector2f m_minimumSize; + Nz::Vector2f m_preferredSize; + Nz::Vector2f m_size; BaseWidget* m_widgetParent; bool m_visible; }; diff --git a/SDK/include/NDK/BaseWidget.inl b/SDK/include/NDK/BaseWidget.inl index 6c7a0a3dd..e24b89285 100644 --- a/SDK/include/NDK/BaseWidget.inl +++ b/SDK/include/NDK/BaseWidget.inl @@ -13,11 +13,13 @@ namespace Ndk m_canvas(nullptr), m_backgroundColor(Nz::Color(230, 230, 230, 255)), m_cursor(Nz::SystemCursor_Default), - m_contentSize(50.f, 50.f), + m_size(50.f, 50.f), + m_maximumSize(std::numeric_limits::infinity()), + m_minimumSize(0.f), + m_preferredSize(-1), m_widgetParent(nullptr), m_visible(true) { - SetPadding(5.f, 5.f, 5.f, 5.f); } template @@ -64,6 +66,20 @@ namespace Ndk SetPosition(GetPosition(Nz::CoordSys_Local).x, (parentSize.y - mySize.y) / 2.f); } + template + inline void BaseWidget::ForEachWidgetChild(F iterator) + { + for (const auto& child : m_children) + iterator(child.get()); + } + + template + inline void BaseWidget::ForEachWidgetChild(F iterator) const + { + for (const auto& child : m_children) + iterator(static_cast(child.get())); + } + inline const Nz::Color& BaseWidget::GetBackgroundColor() const { return m_backgroundColor; @@ -79,24 +95,69 @@ namespace Ndk return m_cursor; } - inline const BaseWidget::Padding& BaseWidget::GetPadding() const + inline float BaseWidget::GetHeight() const { - return m_padding; + return m_size.y; } - inline Nz::Vector2f BaseWidget::GetContentOrigin() const + inline float BaseWidget::GetMaximumHeight() const { - return { m_padding.left, m_padding.top }; + return m_maximumSize.y; } - inline const Nz::Vector2f& BaseWidget::GetContentSize() const + inline Nz::Vector2f BaseWidget::GetMaximumSize() const { - return m_contentSize; + return m_maximumSize; + } + + inline float BaseWidget::GetMaximumWidth() const + { + return m_maximumSize.x; + } + + inline float BaseWidget::GetMinimumHeight() const + { + return m_minimumSize.y; + } + + inline Nz::Vector2f BaseWidget::GetMinimumSize() const + { + return m_minimumSize; + } + + inline float BaseWidget::GetMinimumWidth() const + { + return m_minimumSize.x; + } + + inline float BaseWidget::GetPreferredHeight() const + { + return m_preferredSize.y; + } + + inline Nz::Vector2f BaseWidget::GetPreferredSize() const + { + return m_preferredSize; + } + + inline float BaseWidget::GetPreferredWidth() const + { + return m_preferredSize.x; } inline Nz::Vector2f BaseWidget::GetSize() const { - return Nz::Vector2f(m_contentSize.x + m_padding.left + m_padding.right, m_contentSize.y + m_padding.top + m_padding.bottom); + return Nz::Vector2f(GetWidth(), GetHeight()); + } + + inline float BaseWidget::GetWidth() const + { + return m_size.x; + } + + inline std::size_t BaseWidget::GetWidgetChildCount() const + { + return m_children.size(); } inline bool BaseWidget::IsVisible() const @@ -104,22 +165,79 @@ namespace Ndk return m_visible; } - inline void BaseWidget::SetContentSize(const Nz::Vector2f& size) + inline void BaseWidget::SetFixedHeight(float fixedHeight) { - NotifyParentResized(size); - m_contentSize = size; - - Layout(); + SetMaximumHeight(fixedHeight); + SetMinimumHeight(fixedHeight); } - inline void BaseWidget::SetPadding(float left, float top, float right, float bottom) + inline void BaseWidget::SetFixedSize(const Nz::Vector2f& fixedSize) { - m_padding.left = left; - m_padding.top = top; - m_padding.bottom = bottom; - m_padding.right = right; + SetMaximumSize(fixedSize); + SetMinimumSize(fixedSize); + } - Layout(); + inline void BaseWidget::SetFixedWidth(float fixedWidth) + { + SetMaximumWidth(fixedWidth); + SetMinimumWidth(fixedWidth); + } + + inline void BaseWidget::SetMaximumHeight(float maximumHeight) + { + Nz::Vector2f maximumSize = GetMaximumSize(); + maximumSize.y = maximumHeight; + + SetMaximumSize(maximumSize); + } + + inline void BaseWidget::SetMaximumSize(const Nz::Vector2f& maximumSize) + { + m_maximumSize = maximumSize; + + Nz::Vector2f size = GetSize(); + if (size.x > m_maximumSize.x || size.y > m_maximumSize.y) + Resize(size); //< Will clamp automatically + } + + inline void BaseWidget::SetMaximumWidth(float maximumWidth) + { + Nz::Vector2f maximumSize = GetMaximumSize(); + maximumSize.x = maximumWidth; + + SetMaximumSize(maximumSize); + } + + inline void BaseWidget::SetMinimumHeight(float minimumHeight) + { + Nz::Vector2f minimumSize = GetMinimumSize(); + minimumSize.y = minimumHeight; + + SetMinimumSize(minimumSize); + } + + inline void BaseWidget::SetMinimumSize(const Nz::Vector2f& minimumSize) + { + m_minimumSize = minimumSize; + + Nz::Vector2f size = GetSize(); + if (size.x < m_minimumSize.x || size.y < m_minimumSize.y) + Resize(size); //< Will clamp automatically + } + + inline void BaseWidget::SetMinimumWidth(float minimumWidth) + { + Nz::Vector2f minimumSize = GetMinimumSize(); + minimumSize.x = minimumWidth; + + SetMinimumSize(minimumSize); + } + + inline void BaseWidget::SetPreferredSize(const Nz::Vector2f& preferredSize) + { + m_preferredSize = preferredSize; + + Resize(m_preferredSize); } inline bool BaseWidget::IsRegisteredToCanvas() const diff --git a/SDK/include/NDK/Canvas.hpp b/SDK/include/NDK/Canvas.hpp index f99900d30..d563c6cd3 100644 --- a/SDK/include/NDK/Canvas.hpp +++ b/SDK/include/NDK/Canvas.hpp @@ -28,8 +28,6 @@ namespace Ndk inline const WorldHandle& GetWorld() const; - void ResizeToContent() override; - Canvas& operator=(const Canvas&) = delete; Canvas& operator=(Canvas&&) = delete; diff --git a/SDK/include/NDK/Canvas.inl b/SDK/include/NDK/Canvas.inl index 7ac84a45d..7a602cffb 100644 --- a/SDK/include/NDK/Canvas.inl +++ b/SDK/include/NDK/Canvas.inl @@ -27,9 +27,6 @@ namespace Ndk m_mouseMovedSlot.Connect(eventHandler.OnMouseMoved, this, &Canvas::OnEventMouseMoved); m_mouseLeftSlot.Connect(eventHandler.OnMouseLeft, this, &Canvas::OnEventMouseLeft); m_textEnteredSlot.Connect(eventHandler.OnTextEntered, this, &Canvas::OnEventTextEntered); - - // Disable padding by default - SetPadding(0.f, 0.f, 0.f, 0.f); } inline Canvas::~Canvas() diff --git a/SDK/include/NDK/Widgets/BoxLayout.hpp b/SDK/include/NDK/Widgets/BoxLayout.hpp new file mode 100644 index 000000000..d27616814 --- /dev/null +++ b/SDK/include/NDK/Widgets/BoxLayout.hpp @@ -0,0 +1,48 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequisites.hpp + +#pragma once + +#ifndef NDK_WIDGETS_BOXLAYOUT_HPP +#define NDK_WIDGETS_BOXLAYOUT_HPP + +#include +#include +#include +#include + +namespace Ndk +{ + class NDK_API BoxLayout : public BaseWidget + { + public: + inline BoxLayout(BaseWidget* parent, BoxLayoutOrientation orientation); + BoxLayout(const BoxLayout&) = delete; + BoxLayout(BoxLayout&&) = default; + ~BoxLayout() = default; + + void Layout() override; + + BoxLayout& operator=(const BoxLayout&) = delete; + BoxLayout& operator=(BoxLayout&&) = default; + + private: + struct ChildInfo + { + BaseWidget* widget; + bool isConstrained; + float maximumSize; + float minimumSize; + float size; + }; + + std::vector m_childInfos; + BoxLayoutOrientation m_orientation; + float m_spacing; + }; +} + +#include + +#endif // NDK_WIDGETS_BOXLAYOUT_HPP diff --git a/SDK/include/NDK/Widgets/BoxLayout.inl b/SDK/include/NDK/Widgets/BoxLayout.inl new file mode 100644 index 000000000..72dfaaf5f --- /dev/null +++ b/SDK/include/NDK/Widgets/BoxLayout.inl @@ -0,0 +1,15 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequisites.hpp + +#include + +namespace Ndk +{ + BoxLayout::BoxLayout(BaseWidget* parent, BoxLayoutOrientation orientation) : + BaseWidget(parent), + m_orientation(orientation), + m_spacing(5.f) + { + } +} diff --git a/SDK/include/NDK/Widgets/ButtonWidget.hpp b/SDK/include/NDK/Widgets/ButtonWidget.hpp index 01eb4a90a..a65429290 100644 --- a/SDK/include/NDK/Widgets/ButtonWidget.hpp +++ b/SDK/include/NDK/Widgets/ButtonWidget.hpp @@ -28,10 +28,6 @@ namespace Ndk ButtonWidget(ButtonWidget&&) = default; ~ButtonWidget() = default; - //virtual ButtonWidget* Clone() const = 0; - - void ResizeToContent() override; - inline const Nz::Color& GetColor() const; inline const Nz::Color& GetCornerColor() const; inline const Nz::Color& GetHoverColor() const; diff --git a/SDK/include/NDK/Widgets/ButtonWidget.inl b/SDK/include/NDK/Widgets/ButtonWidget.inl index 757b33ab2..11cf202cf 100644 --- a/SDK/include/NDK/Widgets/ButtonWidget.inl +++ b/SDK/include/NDK/Widgets/ButtonWidget.inl @@ -93,6 +93,10 @@ namespace Ndk { m_textSprite->Update(drawer); + Nz::Vector2f textSize = Nz::Vector2f(m_textSprite->GetBoundingVolume().obb.localBox.GetLengths()); + SetMinimumSize(textSize); + SetPreferredSize(textSize + Nz::Vector2f(20.f, 10.f)); + Layout(); } } diff --git a/SDK/include/NDK/Widgets/CheckboxWidget.hpp b/SDK/include/NDK/Widgets/CheckboxWidget.hpp index d8382ba02..a9723c20b 100644 --- a/SDK/include/NDK/Widgets/CheckboxWidget.hpp +++ b/SDK/include/NDK/Widgets/CheckboxWidget.hpp @@ -53,7 +53,6 @@ namespace Ndk void SetState(CheckboxState state); inline void SetTextMargin(float margin); - void ResizeToContent() override; inline void UpdateText(const Nz::AbstractTextDrawer& drawer); @@ -68,6 +67,7 @@ namespace Ndk void Layout() override; void UpdateCheckbox(); + void UpdateSize(); void OnMouseButtonRelease(int x, int y, Nz::Mouse::Button button) override; inline bool ContainsCheckbox(int x, int y) const; diff --git a/SDK/include/NDK/Widgets/CheckboxWidget.inl b/SDK/include/NDK/Widgets/CheckboxWidget.inl index 3b4823fde..5fa9e30ed 100644 --- a/SDK/include/NDK/Widgets/CheckboxWidget.inl +++ b/SDK/include/NDK/Widgets/CheckboxWidget.inl @@ -65,6 +65,7 @@ namespace Ndk m_checkboxBackgroundSprite->SetSize(size - GetCheckboxBorderSize() * 2.f); m_checkboxContentSprite->SetSize(GetCheckboxSize() - GetCheckboxBorderSize() * 2.f - Nz::Vector2f { 4.f, 4.f }); + UpdateSize(); Layout(); } @@ -77,6 +78,8 @@ namespace Ndk inline void CheckboxWidget::UpdateText(const Nz::AbstractTextDrawer& drawer) { m_textSprite->Update(drawer); + + UpdateSize(); Layout(); } diff --git a/SDK/include/NDK/Widgets/Enums.hpp b/SDK/include/NDK/Widgets/Enums.hpp index 111ee43db..a11511694 100644 --- a/SDK/include/NDK/Widgets/Enums.hpp +++ b/SDK/include/NDK/Widgets/Enums.hpp @@ -9,6 +9,12 @@ namespace Ndk { + enum BoxLayoutOrientation + { + BoxLayoutOrientation_Horizontal, + BoxLayoutOrientation_Vertical + }; + enum CheckboxState { CheckboxState_Checked, diff --git a/SDK/include/NDK/Widgets/ImageWidget.hpp b/SDK/include/NDK/Widgets/ImageWidget.hpp index 93169c80d..fc36ca298 100644 --- a/SDK/include/NDK/Widgets/ImageWidget.hpp +++ b/SDK/include/NDK/Widgets/ImageWidget.hpp @@ -26,14 +26,14 @@ namespace Ndk //virtual ImageWidget* Clone() const = 0; - void ResizeToContent() override; + void ResizeToContent(); inline const Nz::Color& GetColor() const; inline const Nz::TextureRef& GetTexture() const; inline const Nz::Rectf& GetTextureCoords() const; inline void SetColor(const Nz::Color& color); - inline void SetTexture(const Nz::TextureRef& texture, bool resizeToContent = true); + inline void SetTexture(const Nz::TextureRef& texture); inline void SetTextureCoords(const Nz::Rectf& coords); inline void SetTextureRect(const Nz::Rectui& rect); diff --git a/SDK/include/NDK/Widgets/ImageWidget.inl b/SDK/include/NDK/Widgets/ImageWidget.inl index 1ba13d525..b1835941b 100644 --- a/SDK/include/NDK/Widgets/ImageWidget.inl +++ b/SDK/include/NDK/Widgets/ImageWidget.inl @@ -26,12 +26,13 @@ namespace Ndk m_sprite->SetColor(color); } - inline void ImageWidget::SetTexture(const Nz::TextureRef& texture, bool resizeToContent) + inline void ImageWidget::SetTexture(const Nz::TextureRef& texture) { m_sprite->SetTexture(texture, false); - if (resizeToContent) - ResizeToContent(); + Nz::Vector2f textureSize = Nz::Vector2f(Nz::Vector2ui(m_sprite->GetMaterial()->GetDiffuseMap()->GetSize())); + SetMinimumSize(textureSize); + SetPreferredSize(textureSize); } inline void ImageWidget::SetTextureCoords(const Nz::Rectf& coords) diff --git a/SDK/include/NDK/Widgets/LabelWidget.hpp b/SDK/include/NDK/Widgets/LabelWidget.hpp index f96960e15..aea711010 100644 --- a/SDK/include/NDK/Widgets/LabelWidget.hpp +++ b/SDK/include/NDK/Widgets/LabelWidget.hpp @@ -26,10 +26,6 @@ namespace Ndk LabelWidget(LabelWidget&&) = default; ~LabelWidget() = default; - //virtual LabelWidget* Clone() const = 0; - - void ResizeToContent() override; - inline void UpdateText(const Nz::AbstractTextDrawer& drawer); LabelWidget& operator=(const LabelWidget&) = delete; diff --git a/SDK/include/NDK/Widgets/LabelWidget.inl b/SDK/include/NDK/Widgets/LabelWidget.inl index 72d0ad1a6..db3d10a52 100644 --- a/SDK/include/NDK/Widgets/LabelWidget.inl +++ b/SDK/include/NDK/Widgets/LabelWidget.inl @@ -9,5 +9,8 @@ namespace Ndk inline void LabelWidget::UpdateText(const Nz::AbstractTextDrawer& drawer) { m_textSprite->Update(drawer); + + SetMinimumSize(Nz::Vector2f(m_textSprite->GetBoundingVolume().obb.localBox.GetLengths())); + SetPreferredSize(Nz::Vector2f(m_textSprite->GetBoundingVolume().obb.localBox.GetLengths())); } } diff --git a/SDK/include/NDK/Widgets/ProgressBarWidget.hpp b/SDK/include/NDK/Widgets/ProgressBarWidget.hpp index 6e19aa0cd..2ae334b8c 100644 --- a/SDK/include/NDK/Widgets/ProgressBarWidget.hpp +++ b/SDK/include/NDK/Widgets/ProgressBarWidget.hpp @@ -67,8 +67,6 @@ namespace Ndk inline void SetTextMargin(float margin); inline void SetTextColor(const Nz::Color& color); - inline void ResizeToContent() override {} - NazaraSignal(OnValueChanged, const ProgressBarWidget* /*progressBar*/); private: diff --git a/SDK/include/NDK/Widgets/ProgressBarWidget.inl b/SDK/include/NDK/Widgets/ProgressBarWidget.inl index a90641203..9fe2c8382 100644 --- a/SDK/include/NDK/Widgets/ProgressBarWidget.inl +++ b/SDK/include/NDK/Widgets/ProgressBarWidget.inl @@ -1,4 +1,4 @@ -// Copyright (C) 2017 Samy Bensaid +// Copyright (C) 2017 Samy Bensaid // This file is part of the "Nazara Development Kit" // For conditions of distribution and use, see copyright notice in Prerequisites.hpp @@ -148,9 +148,8 @@ namespace Ndk { if (IsTextEnabled()) { - Nz::Vector2f size = GetContentSize(); - m_textSprite->Update(Nz::SimpleTextDrawer::Draw(Nz::String::Number(m_value).Append('%'), - static_cast(std::min(size.x, size.y) / 2.f), 0u, m_textColor)); + Nz::Vector2f size = GetSize(); + m_textSprite->Update(Nz::SimpleTextDrawer::Draw(Nz::String::Number(m_value).Append('%'), static_cast(std::min(size.x, size.y) / 2.f), 0u, m_textColor)); } } } diff --git a/SDK/include/NDK/Widgets/TextAreaWidget.hpp b/SDK/include/NDK/Widgets/TextAreaWidget.hpp index f57e7a049..9a5d33335 100644 --- a/SDK/include/NDK/Widgets/TextAreaWidget.hpp +++ b/SDK/include/NDK/Widgets/TextAreaWidget.hpp @@ -11,8 +11,8 @@ #include #include #include -#include #include +#include namespace Ndk { @@ -40,7 +40,7 @@ namespace Ndk void Erase(std::size_t firstGlyph, std::size_t lastGlyph); void EraseSelection(); - inline CharacterFilter GetCharacterFilter() const; + inline const CharacterFilter& GetCharacterFilter() const; inline unsigned int GetCharacterSize() const; inline const Nz::Vector2ui& GetCursorPosition() const; inline Nz::Vector2ui GetCursorPosition(std::size_t glyphIndex) const; @@ -61,10 +61,8 @@ namespace Ndk inline void MoveCursor(int offset); inline void MoveCursor(const Nz::Vector2i& offset); - void ResizeToContent() override; - inline void SetCharacterFilter(CharacterFilter filter); - inline void SetCharacterSize(unsigned int characterSize); + void SetCharacterSize(unsigned int characterSize); inline void SetCursorPosition(std::size_t glyphIndex); inline void SetCursorPosition(Nz::Vector2ui cursorPosition); inline void SetEchoMode(EchoMode echoMode); @@ -108,7 +106,7 @@ namespace Ndk void RefreshCursor(); void UpdateDisplayText(); - std::function m_characterFilter; + CharacterFilter m_characterFilter; EchoMode m_echoMode; EntityHandle m_cursorEntity; EntityHandle m_textEntity; diff --git a/SDK/include/NDK/Widgets/TextAreaWidget.inl b/SDK/include/NDK/Widgets/TextAreaWidget.inl index 88be7520a..e4c9f2c30 100644 --- a/SDK/include/NDK/Widgets/TextAreaWidget.inl +++ b/SDK/include/NDK/Widgets/TextAreaWidget.inl @@ -33,7 +33,7 @@ namespace Ndk Erase(glyphPosition, glyphPosition + 1U); } - inline TextAreaWidget::CharacterFilter TextAreaWidget::GetCharacterFilter() const + inline const TextAreaWidget::CharacterFilter& TextAreaWidget::GetCharacterFilter() const { return m_characterFilter; } @@ -162,12 +162,7 @@ namespace Ndk inline void TextAreaWidget::SetCharacterFilter(CharacterFilter filter) { - m_characterFilter = filter; - } - - inline void TextAreaWidget::SetCharacterSize(unsigned int characterSize) - { - m_drawer.SetCharacterSize(characterSize); + m_characterFilter = std::move(filter); } inline void TextAreaWidget::SetCursorPosition(std::size_t glyphIndex) diff --git a/SDK/src/NDK/BaseWidget.cpp b/SDK/src/NDK/BaseWidget.cpp index dc33252e2..89ca6bb9c 100644 --- a/SDK/src/NDK/BaseWidget.cpp +++ b/SDK/src/NDK/BaseWidget.cpp @@ -81,7 +81,7 @@ namespace Ndk m_backgroundSprite->SetColor(m_backgroundColor); m_backgroundSprite->SetMaterial(Nz::Material::New((m_backgroundColor.IsOpaque()) ? "Basic2D" : "Translucent2D")); //< TODO: Use a shared material instead of creating one everytime - m_backgroundEntity = CreateEntity(false); + m_backgroundEntity = CreateEntity(); m_backgroundEntity->AddComponent().Attach(m_backgroundSprite, -1); m_backgroundEntity->AddComponent().SetParent(this); @@ -89,14 +89,14 @@ namespace Ndk } else { - m_backgroundEntity->Kill(); + m_backgroundEntity.Reset(); m_backgroundSprite.Reset(); } } /*! * \brief Checks if this widget has keyboard focus - * \return true if widget has keyboard focus, false otherwhise + * \return true if widget has keyboard focus, false otherwise */ bool BaseWidget::HasFocus() const { @@ -106,6 +106,19 @@ namespace Ndk return m_canvas->IsKeyboardOwner(m_canvasIndex); } + void BaseWidget::Resize(const Nz::Vector2f& size) + { + // Adjust new size + Nz::Vector2f newSize = size; + newSize.Maximize(m_minimumSize); + newSize.Minimize(m_maximumSize); + + NotifyParentResized(newSize); + m_size = newSize; + + Layout(); + } + void BaseWidget::SetBackgroundColor(const Nz::Color& color) { m_backgroundColor = color; @@ -131,11 +144,6 @@ namespace Ndk m_canvas->SetKeyboardOwner(m_canvasIndex); } - void BaseWidget::SetSize(const Nz::Vector2f& size) - { - SetContentSize({std::max(size.x - m_padding.left - m_padding.right, 0.f), std::max(size.y - m_padding.top - m_padding.bottom, 0.f)}); - } - void BaseWidget::Show(bool show) { if (m_visible != show) @@ -155,7 +163,7 @@ namespace Ndk } } - const Ndk::EntityHandle& BaseWidget::CreateEntity(bool isContentEntity) + const Ndk::EntityHandle& BaseWidget::CreateEntity() { const EntityHandle& newEntity = m_world->CreateEntity(); newEntity->Enable(m_visible); @@ -163,7 +171,6 @@ namespace Ndk m_entities.emplace_back(); WidgetEntity& widgetEntity = m_entities.back(); widgetEntity.handle = newEntity; - widgetEntity.isContent = isContentEntity; return newEntity; } @@ -179,7 +186,7 @@ namespace Ndk void BaseWidget::Layout() { if (m_backgroundEntity) - m_backgroundSprite->SetSize(m_contentSize.x + m_padding.left + m_padding.right, m_contentSize.y + m_padding.top + m_padding.bottom); + m_backgroundSprite->SetSize(m_size.x, m_size.y); UpdatePositionAndSize(); } @@ -282,16 +289,12 @@ namespace Ndk Nz::Vector2f widgetPos = Nz::Vector2f(GetPosition()); Nz::Vector2f widgetSize = GetSize(); - Nz::Vector2f contentPos = widgetPos + GetContentOrigin(); - Nz::Vector2f contentSize = GetContentSize(); - Nz::Recti fullBounds(Nz::Rectf(widgetPos.x, widgetPos.y, widgetSize.x, widgetSize.y)); - Nz::Recti contentBounds(Nz::Rectf(contentPos.x, contentPos.y, contentSize.x, contentSize.y)); for (WidgetEntity& widgetEntity : m_entities) { const Ndk::EntityHandle& entity = widgetEntity.handle; if (entity->HasComponent()) - entity->GetComponent().SetScissorRect((widgetEntity.isContent) ? contentBounds : fullBounds); + entity->GetComponent().SetScissorRect(fullBounds); } } } diff --git a/SDK/src/NDK/Canvas.cpp b/SDK/src/NDK/Canvas.cpp index 9a5996432..3959f1765 100644 --- a/SDK/src/NDK/Canvas.cpp +++ b/SDK/src/NDK/Canvas.cpp @@ -7,10 +7,6 @@ namespace Ndk { - void Canvas::ResizeToContent() - { - } - std::size_t Canvas::RegisterWidget(BaseWidget* widget) { WidgetEntry box; diff --git a/SDK/src/NDK/Widgets/BoxLayout.cpp b/SDK/src/NDK/Widgets/BoxLayout.cpp new file mode 100644 index 000000000..89564ed08 --- /dev/null +++ b/SDK/src/NDK/Widgets/BoxLayout.cpp @@ -0,0 +1,122 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequisites.hpp + +#include +#include +#include +#include + +namespace Ndk +{ + void BoxLayout::Layout() + { + std::size_t axis1, axis2; + + switch (m_orientation) + { + case BoxLayoutOrientation_Horizontal: + axis1 = 0; //< x + axis2 = 1; //< y + break; + + case BoxLayoutOrientation_Vertical: + axis1 = 1; //< y + axis2 = 0; //< x + break; + + default: + assert(false); + break; + } + + m_childInfos.clear(); + + // Handle size + ForEachWidgetChild([&](BaseWidget* child) + { + if (!child->IsVisible()) + return; + + m_childInfos.emplace_back(); + auto& info = m_childInfos.back(); + info.isConstrained = false; + info.maximumSize = child->GetMaximumSize()[axis1]; + info.minimumSize = child->GetMinimumSize()[axis1]; + info.size = info.minimumSize; + info.widget = child; + }); + + Nz::Vector2f layoutSize = GetSize(); + + float availableSpace = layoutSize[axis1] - m_spacing * (m_childInfos.size() - 1); + float remainingSize = availableSpace; + for (auto& info : m_childInfos) + remainingSize -= info.minimumSize; + + // Okay this algorithm is FAR from perfect but I couldn't figure a way other than this one + std::size_t unconstrainedChildCount = m_childInfos.size(); + + bool hasUnconstrainedChilds = false; + for (std::size_t i = 0; i < m_childInfos.size(); ++i) + { + if (remainingSize <= 0.0001f) + break; + + float evenSize = remainingSize / unconstrainedChildCount; + + for (auto& info : m_childInfos) + { + if (info.isConstrained) + continue; + + float previousSize = info.size; + + info.size += evenSize; + if (info.size > info.maximumSize) + { + unconstrainedChildCount--; + + evenSize += (info.size - info.maximumSize) / unconstrainedChildCount; + info.isConstrained = true; + info.size = info.maximumSize; + } + else + hasUnconstrainedChilds = true; + + remainingSize -= info.size - previousSize; + } + + if (!hasUnconstrainedChilds) + break; + } + + float spacing = m_spacing + remainingSize / (m_childInfos.size() - 1); + + for (auto& info : m_childInfos) + { + Nz::Vector2f newSize = info.widget->GetSize(); + newSize[axis1] = info.size; + + info.widget->Resize(newSize); + } + + // Handle position + float cursor = 0.f; + bool first = true; + for (auto& info : m_childInfos) + { + if (first) + first = false; + else + cursor += spacing; + + Nz::Vector2f position = Nz::Vector2f(0.f, 0.f); + position[axis1] = cursor; + + info.widget->SetPosition(position); + + cursor += info.size; + }; + } +} diff --git a/SDK/src/NDK/Widgets/ButtonWidget.cpp b/SDK/src/NDK/Widgets/ButtonWidget.cpp index f432d86b7..c88645e44 100644 --- a/SDK/src/NDK/Widgets/ButtonWidget.cpp +++ b/SDK/src/NDK/Widgets/ButtonWidget.cpp @@ -30,13 +30,13 @@ namespace Ndk m_gradientSprite->SetCornerColor(Nz::RectCorner_RightBottom, m_cornerColor); m_gradientSprite->SetMaterial(Nz::Material::New("Basic2D")); - m_gradientEntity = CreateEntity(false); + m_gradientEntity = CreateEntity(); m_gradientEntity->AddComponent().SetParent(this); m_gradientEntity->AddComponent().Attach(m_gradientSprite); m_textSprite = Nz::TextSprite::New(); - m_textEntity = CreateEntity(true); + m_textEntity = CreateEntity(); m_textEntity->AddComponent().SetParent(this); m_textEntity->AddComponent().Attach(m_textSprite, 1); @@ -73,22 +73,15 @@ namespace Ndk return s_pressCornerColor; } - void ButtonWidget::ResizeToContent() - { - SetContentSize(Nz::Vector2f(m_textSprite->GetBoundingVolume().obb.localBox.GetLengths())); - } - void ButtonWidget::Layout() { BaseWidget::Layout(); - m_gradientSprite->SetSize(GetSize()); - - Nz::Vector2f origin = GetContentOrigin(); - const Nz::Vector2f& contentSize = GetContentSize(); + Nz::Vector2f size = GetSize(); + m_gradientSprite->SetSize(size); Nz::Boxf textBox = m_textEntity->GetComponent().GetAABB(); - m_textEntity->GetComponent().SetPosition(origin.x + contentSize.x / 2 - textBox.width / 2, origin.y + contentSize.y / 2 - textBox.height / 2); + m_textEntity->GetComponent().SetPosition(size.x / 2.f - textBox.width / 2.f, size.y / 2.f - textBox.height / 2.f); } void ButtonWidget::OnMouseButtonPress(int /*x*/, int /*y*/, Nz::Mouse::Button button) diff --git a/SDK/src/NDK/Widgets/CheckboxWidget.cpp b/SDK/src/NDK/Widgets/CheckboxWidget.cpp index 3f20ff7c6..1bea0c9ce 100644 --- a/SDK/src/NDK/Widgets/CheckboxWidget.cpp +++ b/SDK/src/NDK/Widgets/CheckboxWidget.cpp @@ -28,19 +28,19 @@ namespace Ndk m_checkboxContentSprite = Nz::Sprite::New(Nz::Material::New("Translucent2D")); m_textSprite = Nz::TextSprite::New(); - m_checkboxBorderEntity = CreateEntity(false); + m_checkboxBorderEntity = CreateEntity(); m_checkboxBorderEntity->AddComponent().SetParent(this); m_checkboxBorderEntity->AddComponent().Attach(m_checkboxBorderSprite); - m_checkboxBackgroundEntity = CreateEntity(false); + m_checkboxBackgroundEntity = CreateEntity(); m_checkboxBackgroundEntity->AddComponent().SetParent(this); m_checkboxBackgroundEntity->AddComponent().Attach(m_checkboxBackgroundSprite, 1); - m_checkboxContentEntity = CreateEntity(true); + m_checkboxContentEntity = CreateEntity(); m_checkboxContentEntity->AddComponent().SetParent(this); m_checkboxContentEntity->AddComponent().Attach(m_checkboxContentSprite, 2); - m_textEntity = CreateEntity(true); + m_textEntity = CreateEntity(); m_textEntity->AddComponent().SetParent(this); m_textEntity->AddComponent().Attach(m_textSprite); @@ -108,20 +108,11 @@ namespace Ndk return m_state; } - void CheckboxWidget::ResizeToContent() - { - Nz::Vector3f textSize = m_textSprite->GetBoundingVolume().obb.localBox.GetLengths(); - Nz::Vector2f checkboxSize = GetCheckboxSize(); - - Nz::Vector2f finalSize { checkboxSize.x + (m_adaptativeMargin ? checkboxSize.x / 2.f : m_textMargin) + textSize.x, std::max(textSize.y, checkboxSize.y) }; - SetContentSize(finalSize); - } - void CheckboxWidget::Layout() { BaseWidget::Layout(); - Nz::Vector2f origin = GetContentOrigin(); + Nz::Vector2f origin = Nz::Vector2f(0.f); Nz::Vector2f checkboxSize = GetCheckboxSize(); Nz::Vector2f borderSize = GetCheckboxBorderSize(); @@ -178,4 +169,14 @@ namespace Ndk m_checkboxContentSprite->SetTexture(Nz::TextureRef {}); } } + + void CheckboxWidget::UpdateSize() + { + Nz::Vector3f textSize = m_textSprite->GetBoundingVolume().obb.localBox.GetLengths(); + Nz::Vector2f checkboxSize = GetCheckboxSize(); + + Nz::Vector2f finalSize{ checkboxSize.x + (m_adaptativeMargin ? checkboxSize.x / 2.f : m_textMargin) + textSize.x, std::max(textSize.y, checkboxSize.y) }; + SetMinimumSize(finalSize); + SetPreferredSize(finalSize); + } } diff --git a/SDK/src/NDK/Widgets/ImageWidget.cpp b/SDK/src/NDK/Widgets/ImageWidget.cpp index b43d5ba8d..8a766419b 100644 --- a/SDK/src/NDK/Widgets/ImageWidget.cpp +++ b/SDK/src/NDK/Widgets/ImageWidget.cpp @@ -11,7 +11,7 @@ namespace Ndk ImageWidget::ImageWidget(BaseWidget* parent) : BaseWidget(parent) { - m_entity = CreateEntity(true); + m_entity = CreateEntity(); m_entity->AddComponent(); auto& gfx = m_entity->AddComponent(); @@ -19,19 +19,10 @@ namespace Ndk gfx.Attach(m_sprite); } - void ImageWidget::ResizeToContent() - { - Nz::Vector3ui textureSize = m_sprite->GetMaterial()->GetDiffuseMap()->GetSize(); - SetSize({ static_cast(textureSize.x), static_cast(textureSize.y) }); - } - void ImageWidget::Layout() { BaseWidget::Layout(); - Nz::Vector2f origin = GetContentOrigin(); - Nz::Vector2f contentSize = GetContentSize(); - m_entity->GetComponent().SetPosition(origin); - m_sprite->SetSize(contentSize); + m_sprite->SetSize(GetSize()); } } diff --git a/SDK/src/NDK/Widgets/LabelWidget.cpp b/SDK/src/NDK/Widgets/LabelWidget.cpp index 98deb8da3..4c6553011 100644 --- a/SDK/src/NDK/Widgets/LabelWidget.cpp +++ b/SDK/src/NDK/Widgets/LabelWidget.cpp @@ -13,7 +13,7 @@ namespace Ndk { m_textSprite = Nz::TextSprite::New(); - m_textEntity = CreateEntity(true); + m_textEntity = CreateEntity(); m_textEntity->AddComponent().Attach(m_textSprite); m_textEntity->AddComponent().SetParent(this); @@ -23,12 +23,5 @@ namespace Ndk void LabelWidget::Layout() { BaseWidget::Layout(); - - m_textEntity->GetComponent().SetPosition(GetContentOrigin()); - } - - void LabelWidget::ResizeToContent() - { - SetContentSize(Nz::Vector2f(m_textSprite->GetBoundingVolume().obb.localBox.GetLengths())); } } diff --git a/SDK/src/NDK/Widgets/ProgressBarWidget.cpp b/SDK/src/NDK/Widgets/ProgressBarWidget.cpp index 80f01d43a..3e2f01440 100644 --- a/SDK/src/NDK/Widgets/ProgressBarWidget.cpp +++ b/SDK/src/NDK/Widgets/ProgressBarWidget.cpp @@ -30,11 +30,11 @@ namespace Ndk SetBarColor(s_barColor, s_barCornerColor); - m_borderEntity = CreateEntity(false); + m_borderEntity = CreateEntity(); m_borderEntity->AddComponent().SetParent(this); m_borderEntity->AddComponent().Attach(m_borderSprite); - m_barEntity = CreateEntity(true); + m_barEntity = CreateEntity(); m_barEntity->AddComponent().SetParent(this); GraphicsComponent& graphics = m_barEntity->AddComponent(); @@ -43,7 +43,7 @@ namespace Ndk m_textSprite = Nz::TextSprite::New(); - m_textEntity = CreateEntity(true); + m_textEntity = CreateEntity(); m_textEntity->AddComponent().SetParent(this); m_textEntity->AddComponent().Attach(m_textSprite); @@ -76,8 +76,8 @@ namespace Ndk void ProgressBarWidget::Layout() { - Nz::Vector2f origin = GetContentOrigin(); - Nz::Vector2f size = GetContentSize(); + Nz::Vector2f origin = Nz::Vector2f(0.f); + Nz::Vector2f size = GetSize(); Nz::Vector2f progressBarSize = size; if (IsTextEnabled()) diff --git a/SDK/src/NDK/Widgets/TextAreaWidget.cpp b/SDK/src/NDK/Widgets/TextAreaWidget.cpp index 34ebac579..09355c09c 100644 --- a/SDK/src/NDK/Widgets/TextAreaWidget.cpp +++ b/SDK/src/NDK/Widgets/TextAreaWidget.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -20,19 +21,23 @@ namespace Ndk m_readOnly(false), m_tabEnabled(false) { - m_cursorEntity = CreateEntity(true); + m_cursorEntity = CreateEntity(); m_cursorEntity->AddComponent(); m_cursorEntity->AddComponent().SetParent(this); + m_cursorEntity->GetComponent().SetPosition(5.f, 3.f); m_cursorEntity->Enable(false); m_textSprite = Nz::TextSprite::New(); - m_textEntity = CreateEntity(true); + m_textEntity = CreateEntity(); m_textEntity->AddComponent().Attach(m_textSprite); m_textEntity->AddComponent().SetParent(this); + m_textEntity->GetComponent().SetPosition(5.f, 3.f); SetCursor(Nz::SystemCursor_Text); + SetCharacterSize(GetCharacterSize()); //< Actualize minimum / preferred size + EnableBackground(true); Layout(); } @@ -140,9 +145,25 @@ namespace Ndk return Nz::Vector2ui::Zero(); } - void TextAreaWidget::ResizeToContent() + void TextAreaWidget::SetCharacterSize(unsigned int characterSize) { - SetContentSize(Nz::Vector2f(m_textSprite->GetBoundingVolume().obb.localBox.GetLengths())); + m_drawer.SetCharacterSize(characterSize); + + std::size_t fontCount = m_drawer.GetFontCount(); + unsigned int lineHeight = 0; + int spaceAdvance = 0; + for (std::size_t i = 0; i < fontCount; ++i) + { + Nz::Font* font = m_drawer.GetFont(i); + + const Nz::Font::SizeInfo& sizeInfo = font->GetSizeInfo(characterSize); + lineHeight = std::max(lineHeight, sizeInfo.lineHeight); + spaceAdvance = std::max(spaceAdvance, sizeInfo.spaceAdvance); + } + + Nz::Vector2f size = { float(spaceAdvance), float(lineHeight) + 5.f }; + SetMinimumSize(size); + SetPreferredSize({ size.x * 6.f, size.y }); } void TextAreaWidget::Write(const Nz::String& text, std::size_t glyphPosition) @@ -165,8 +186,6 @@ namespace Ndk { BaseWidget::Layout(); - m_textEntity->GetComponent().SetPosition(GetContentOrigin()); - RefreshCursor(); } @@ -403,8 +422,7 @@ namespace Ndk { SetFocus(); - const Padding& padding = GetPadding(); - Nz::Vector2ui hoveredGlyph = GetHoveredGlyph(float(x - padding.left), float(y - padding.top)); + Nz::Vector2ui hoveredGlyph = GetHoveredGlyph(float(x) - 5.f, float(y) - 5.f); // Shift extends selection if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::LShift) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::RShift)) @@ -434,10 +452,7 @@ namespace Ndk void TextAreaWidget::OnMouseMoved(int x, int y, int deltaX, int deltaY) { if (m_isMouseButtonDown) - { - const Padding& padding = GetPadding(); - SetSelection(m_selectionCursor, GetHoveredGlyph(float(x - padding.left), float(y - padding.top))); - } + SetSelection(m_selectionCursor, GetHoveredGlyph(float(x) - 5.f, float(y) - 3.f)); } void TextAreaWidget::OnTextEntered(char32_t character, bool /*repeated*/) @@ -514,8 +529,6 @@ namespace Ndk if (m_readOnly) return; - m_cursorEntity->GetComponent().SetPosition(GetContentOrigin()); - std::size_t selectionLineCount = m_cursorPositionEnd.y - m_cursorPositionBegin.y + 1; std::size_t oldSpriteCount = m_cursorSprites.size(); if (m_cursorSprites.size() != selectionLineCount) diff --git a/build/scripts/tools/ndk_server.lua b/build/scripts/tools/ndk_server.lua index e235b1343..2fd17e7f1 100644 --- a/build/scripts/tools/ndk_server.lua +++ b/build/scripts/tools/ndk_server.lua @@ -36,6 +36,7 @@ TOOL.FilesExcluded = { "../SDK/**/Particle*Component.*", "../SDK/**/ParticleSystem.*", "../SDK/**/RenderSystem.*", + "../SDK/**/*Layout*.*", "../SDK/**/*Widget*.*", "../SDK/**/LuaBinding_Audio.*", "../SDK/**/LuaBinding_Graphics.*",