diff --git a/SDK/include/NDK/BaseWidget.hpp b/SDK/include/NDK/BaseWidget.hpp index 1e6fe7c64..2bdf0dd81 100644 --- a/SDK/include/NDK/BaseWidget.hpp +++ b/SDK/include/NDK/BaseWidget.hpp @@ -32,28 +32,36 @@ namespace Ndk BaseWidget(BaseWidget&&) = default; virtual ~BaseWidget(); - template T& Add(Args&&... args); + template T* Add(Args&&... args); inline void AddChild(std::unique_ptr&& widget); inline void Center(); + inline void Destroy(); + void EnableBackground(bool enable); //virtual BaseWidget* Clone() const = 0; + inline const Nz::Color& GetBackgroundColor() const; inline Canvas* GetCanvas(); inline const Padding& GetPadding() const; inline const Nz::Vector2f& GetContentSize() const; inline Nz::Vector2f GetSize() const; + inline bool IsVisible() const; + void GrabKeyboard(); virtual void ResizeToContent() = 0; + void SetBackgroundColor(const Nz::Color& color); inline void SetContentSize(const Nz::Vector2f& size); inline void SetPadding(float left, float top, float right, float bottom); void SetSize(const Nz::Vector2f& size); + void Show(bool show = true); + BaseWidget& operator=(const BaseWidget&) = delete; BaseWidget& operator=(BaseWidget&&) = default; @@ -78,11 +86,15 @@ namespace Ndk virtual void OnMouseButtonPress(int x, int y, Nz::Mouse::Button button); virtual void OnMouseButtonRelease(int x, int y, Nz::Mouse::Button button); virtual void OnMouseExit(); + virtual void OnParentResized(const Nz::Vector2f& newSize); virtual void OnTextEntered(char32_t character, bool repeated); private: inline BaseWidget(); + inline void DestroyChild(BaseWidget* widget); + void DestroyChildren(); + inline void NotifyParentResized(const Nz::Vector2f& newSize); inline void UpdateCanvasIndex(std::size_t index); std::size_t m_canvasIndex; @@ -96,6 +108,7 @@ namespace Ndk Nz::SpriteRef m_backgroundSprite; Nz::Vector2f m_contentSize; BaseWidget* m_widgetParent; + bool m_visible; }; } diff --git a/SDK/include/NDK/BaseWidget.inl b/SDK/include/NDK/BaseWidget.inl index 0b13ee7c7..041abe9bd 100644 --- a/SDK/include/NDK/BaseWidget.inl +++ b/SDK/include/NDK/BaseWidget.inl @@ -5,30 +5,34 @@ #include #include #include +#include namespace Ndk { inline BaseWidget::BaseWidget() : + m_canvasIndex(std::numeric_limits::max()), m_backgroundColor(Nz::Color(230, 230, 230, 255)), m_canvas(nullptr), m_contentSize(50.f, 50.f), - m_widgetParent(nullptr) + m_widgetParent(nullptr), + m_visible(true) { SetPadding(5.f, 5.f, 5.f, 5.f); } template - inline T& BaseWidget::Add(Args&&... args) + inline T* BaseWidget::Add(Args&&... args) { std::unique_ptr widget = std::make_unique(this, std::forward(args)...); - T& widgetRef = *widget; + T* widgetPtr = widget.get(); AddChild(std::move(widget)); - return widgetRef; + return widgetPtr; } inline void BaseWidget::AddChild(std::unique_ptr&& widget) { + widget->Show(m_visible); m_children.emplace_back(std::move(widget)); } @@ -41,6 +45,11 @@ namespace Ndk SetPosition((parentSize.x - mySize.x) / 2.f, (parentSize.y - mySize.y) / 2.f); } + inline const Nz::Color& BaseWidget::GetBackgroundColor() const + { + return m_backgroundColor; + } + inline Canvas* BaseWidget::GetCanvas() { return m_canvas; @@ -61,10 +70,16 @@ namespace Ndk return Nz::Vector2f(m_contentSize.x + m_padding.left + m_padding.right, m_contentSize.y + m_padding.top + m_padding.bottom); } + inline bool BaseWidget::IsVisible() const + { + return m_visible; + } + inline void BaseWidget::SetContentSize(const Nz::Vector2f& size) { + NotifyParentResized(size); m_contentSize = size; - + Layout(); } @@ -78,6 +93,12 @@ namespace Ndk Layout(); } + inline void BaseWidget::NotifyParentResized(const Nz::Vector2f& newSize) + { + for (const auto& widgetPtr : m_children) + widgetPtr->OnParentResized(newSize); + } + inline void BaseWidget::UpdateCanvasIndex(std::size_t index) { m_canvasIndex = index; diff --git a/SDK/include/NDK/Canvas.hpp b/SDK/include/NDK/Canvas.hpp index a8da5d04e..db271051f 100644 --- a/SDK/include/NDK/Canvas.hpp +++ b/SDK/include/NDK/Canvas.hpp @@ -23,7 +23,7 @@ namespace Ndk inline Canvas(WorldHandle world, Nz::EventHandler& eventHandler); Canvas(const Canvas&) = delete; Canvas(Canvas&&) = delete; - ~Canvas() = default; + inline ~Canvas(); inline const WorldHandle& GetWorld() const; diff --git a/SDK/include/NDK/Canvas.inl b/SDK/include/NDK/Canvas.inl index d0d05bf12..173474bdf 100644 --- a/SDK/include/NDK/Canvas.inl +++ b/SDK/include/NDK/Canvas.inl @@ -22,6 +22,12 @@ namespace Ndk m_textEnteredSlot.Connect(eventHandler.OnTextEntered, this, &Canvas::OnTextEntered); } + inline Canvas::~Canvas() + { + // Destroy children explicitly because they signal us when getting destroyed, and that can't happend after our own destruction + DestroyChildren(); + } + inline const WorldHandle& Canvas::GetWorld() const { return m_world; diff --git a/SDK/include/NDK/Widgets/TextAreaWidget.hpp b/SDK/include/NDK/Widgets/TextAreaWidget.hpp index 8236b1689..2b0774ec8 100644 --- a/SDK/include/NDK/Widgets/TextAreaWidget.hpp +++ b/SDK/include/NDK/Widgets/TextAreaWidget.hpp @@ -35,6 +35,7 @@ namespace Ndk inline std::size_t GetCursorPosition() const; inline std::size_t GetLineCount() const; inline const Nz::String& GetText() const; + inline const Nz::Color& GetTextColor() const; std::size_t GetHoveredGlyph(float x, float y) const; @@ -47,7 +48,8 @@ namespace Ndk inline void SetCursorPosition(std::size_t cursorPosition); inline void SetReadOnly(bool readOnly = true); - void SetText(const Nz::String& text); + inline void SetText(const Nz::String& text); + inline void SetTextColor(const Nz::Color& text); void Write(const Nz::String& text); diff --git a/SDK/include/NDK/Widgets/TextAreaWidget.inl b/SDK/include/NDK/Widgets/TextAreaWidget.inl index d923e7223..711f8de8c 100644 --- a/SDK/include/NDK/Widgets/TextAreaWidget.inl +++ b/SDK/include/NDK/Widgets/TextAreaWidget.inl @@ -35,6 +35,11 @@ namespace Ndk return m_drawer.GetText(); } + inline const Nz::Color& TextAreaWidget::GetTextColor() const + { + return m_drawer.GetColor(); + } + inline bool Ndk::TextAreaWidget::IsMultilineEnabled() const { return m_multiLineEnabled; @@ -72,4 +77,18 @@ namespace Ndk m_cursorEntity->Enable(!m_readOnly); } + + inline void TextAreaWidget::SetText(const Nz::String& text) + { + m_drawer.SetText(text); + + m_textSprite->Update(m_drawer); + } + + inline void TextAreaWidget::SetTextColor(const Nz::Color& text) + { + m_drawer.SetColor(text); + + m_textSprite->Update(m_drawer); + } } diff --git a/SDK/src/NDK/BaseWidget.cpp b/SDK/src/NDK/BaseWidget.cpp index 43ffbeb52..69d60965e 100644 --- a/SDK/src/NDK/BaseWidget.cpp +++ b/SDK/src/NDK/BaseWidget.cpp @@ -7,6 +7,7 @@ #include #include #include +#include namespace Ndk { @@ -25,10 +26,18 @@ namespace Ndk BaseWidget::~BaseWidget() { - m_canvas->UnregisterWidget(m_canvasIndex); + if (m_canvasIndex != std::numeric_limits::max()) + m_canvas->UnregisterWidget(m_canvasIndex); } - inline void BaseWidget::EnableBackground(bool enable) + void BaseWidget::Destroy() + { + NazaraAssert(this != m_canvas, "Canvas cannot be destroyed by calling Destroy()"); + + m_widgetParent->DestroyChild(this); //< This does delete us + } + + void BaseWidget::EnableBackground(bool enable) { if (m_backgroundEntity.IsValid() == enable) return; @@ -39,7 +48,7 @@ namespace Ndk m_backgroundSprite->SetColor(m_backgroundColor); m_backgroundSprite->SetMaterial(Nz::Material::New((m_backgroundColor.IsOpaque()) ? "Basic2D" : "Translucent2D")); - m_backgroundEntity = m_world->CreateEntity(); + m_backgroundEntity = CreateEntity(); m_backgroundEntity->AddComponent().Attach(m_backgroundSprite, -1); m_backgroundEntity->AddComponent().SetParent(this); @@ -52,15 +61,48 @@ namespace Ndk } } + void BaseWidget::GrabKeyboard() + { + m_canvas->SetKeyboardOwner(this); + } + + void BaseWidget::SetBackgroundColor(const Nz::Color& color) + { + m_backgroundColor = color; + + if (m_backgroundSprite) + { + m_backgroundSprite->SetColor(color); + m_backgroundSprite->GetMaterial()->Configure((color.IsOpaque()) ? "Basic2D" : "Translucent2D"); //< Our sprite has its own material (see EnableBackground) + } + } + 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) + { + m_visible = show; + + for (const EntityHandle& entity : m_entities) + entity->Enable(show); + + for (const auto& widgetPtr : m_children) + widgetPtr->Show(show); + } + } + EntityHandle BaseWidget::CreateEntity() { - m_entities.emplace_back(m_world->CreateEntity()); - return m_entities.back(); + EntityHandle newEntity = m_world->CreateEntity(); + newEntity->Enable(m_visible); + + m_entities.emplace_back(newEntity); + return newEntity; } void BaseWidget::DestroyEntity(Entity* entity) @@ -71,11 +113,6 @@ namespace Ndk m_entities.erase(it); } - void BaseWidget::GrabKeyboard() - { - m_canvas->SetKeyboardOwner(this); - } - void BaseWidget::Layout() { if (m_canvas) @@ -126,7 +163,28 @@ namespace Ndk { } + void BaseWidget::OnParentResized(const Nz::Vector2f& newSize) + { + } + void BaseWidget::OnTextEntered(char32_t character, bool repeated) { } + + void BaseWidget::DestroyChild(BaseWidget* widget) + { + auto it = std::find_if(m_children.begin(), m_children.end(), [widget] (const std::unique_ptr& widgetPtr) -> bool + { + return widgetPtr.get() == widget; + }); + + NazaraAssert(it != m_children.end(), "Child widget not found in parent"); + + m_children.erase(it); + } + + void BaseWidget::DestroyChildren() + { + m_children.clear(); + } } diff --git a/SDK/src/NDK/Canvas.cpp b/SDK/src/NDK/Canvas.cpp index c06d320c1..b13b7ef6a 100644 --- a/SDK/src/NDK/Canvas.cpp +++ b/SDK/src/NDK/Canvas.cpp @@ -49,21 +49,23 @@ namespace Ndk void Canvas::UnregisterWidget(std::size_t index) { + WidgetBox& entry = m_widgetBoxes[index]; + + if (m_hoveredWidget == &entry) + m_hoveredWidget = nullptr; + + if (m_keyboardOwner == entry.widget) + m_keyboardOwner = nullptr; + if (m_widgetBoxes.size() > 1U) { - WidgetBox& entry = m_widgetBoxes[index]; WidgetBox& lastEntry = m_widgetBoxes.back(); - if (m_hoveredWidget == &entry) - m_hoveredWidget = nullptr; - - if (m_keyboardOwner == entry.widget) - m_keyboardOwner = nullptr; - entry = std::move(lastEntry); entry.widget->UpdateCanvasIndex(index); - m_widgetBoxes.pop_back(); } + + m_widgetBoxes.pop_back(); } void Canvas::OnMouseButtonPressed(const Nz::EventHandler* /*eventHandler*/, const Nz::WindowEvent::MouseButtonEvent& event) diff --git a/SDK/src/NDK/Widgets/TextAreaWidget.cpp b/SDK/src/NDK/Widgets/TextAreaWidget.cpp index 0e0f82f57..0c9b7b3d5 100644 --- a/SDK/src/NDK/Widgets/TextAreaWidget.cpp +++ b/SDK/src/NDK/Widgets/TextAreaWidget.cpp @@ -77,13 +77,6 @@ namespace Ndk SetContentSize(Nz::Vector2f(m_textSprite->GetBoundingVolume().obb.localBox.GetLengths())); } - void TextAreaWidget::SetText(const Nz::String& text) - { - m_drawer.SetText(text); - - m_textSprite->Update(m_drawer); - } - void TextAreaWidget::Write(const Nz::String& text) { if (m_cursorPosition >= m_drawer.GetGlyphCount()) diff --git a/include/Nazara/Core/Signal.hpp b/include/Nazara/Core/Signal.hpp index 613502f80..897c32ef9 100644 --- a/include/Nazara/Core/Signal.hpp +++ b/include/Nazara/Core/Signal.hpp @@ -33,7 +33,7 @@ namespace Nz Signal(); Signal(const Signal&) = delete; - Signal(Signal&& signal); + Signal(Signal&& signal) noexcept; ~Signal() = default; void Clear(); @@ -48,7 +48,7 @@ namespace Nz void operator()(Args... args) const; Signal& operator=(const Signal&) = delete; - Signal& operator=(Signal&& signal); + Signal& operator=(Signal&& signal) noexcept; private: struct Slot; diff --git a/include/Nazara/Core/Signal.inl b/include/Nazara/Core/Signal.inl index 93e0166c0..07a950d89 100644 --- a/include/Nazara/Core/Signal.inl +++ b/include/Nazara/Core/Signal.inl @@ -32,7 +32,7 @@ namespace Nz */ template - Signal::Signal(Signal&& signal) + Signal::Signal(Signal&& signal) noexcept { operator=(std::move(signal)); } @@ -182,7 +182,7 @@ namespace Nz */ template - Signal& Signal::operator=(Signal&& signal) + Signal& Signal::operator=(Signal&& signal) noexcept { m_slots = std::move(signal.m_slots); m_slotIterator = signal.m_slotIterator;