diff --git a/SDK/include/NDK/BaseWidget.hpp b/SDK/include/NDK/BaseWidget.hpp index 46690ba64..eb00d9ea4 100644 --- a/SDK/include/NDK/BaseWidget.hpp +++ b/SDK/include/NDK/BaseWidget.hpp @@ -16,12 +16,16 @@ namespace Ndk { + class Canvas; + class NDK_API BaseWidget : public Nz::Node { + friend Canvas; + public: struct Padding; - inline BaseWidget(WorldHandle world, BaseWidget* parent = nullptr); + BaseWidget(BaseWidget* parent); BaseWidget(const BaseWidget&) = delete; BaseWidget(BaseWidget&&) = default; virtual ~BaseWidget(); @@ -32,6 +36,7 @@ namespace Ndk //virtual BaseWidget* Clone() const = 0; + inline Canvas* GetCanvas(); inline const Padding& GetPadding() const; inline const Nz::Vector2f& GetContentSize() const; inline Nz::Vector2f GetSize() const; @@ -57,10 +62,21 @@ namespace Ndk EntityHandle CreateEntity(); void DestroyEntity(Entity* entity); virtual void Layout(); + void InvalidateNode() override; + + virtual void OnMouseEnter(); + virtual void OnMouseMoved(int x, int y, int deltaX, int deltaY); + virtual void OnMouseExit(); private: + inline BaseWidget(); + + inline void UpdateCanvasIndex(std::size_t index); + + std::size_t m_canvasIndex; std::vector m_entities; - std::vector m_children; + std::vector> m_children; + Canvas* m_canvas; EntityOwner m_backgroundEntity; Padding m_padding; WorldHandle m_world; diff --git a/SDK/include/NDK/BaseWidget.inl b/SDK/include/NDK/BaseWidget.inl index a3cfc9763..b5ab5b0f1 100644 --- a/SDK/include/NDK/BaseWidget.inl +++ b/SDK/include/NDK/BaseWidget.inl @@ -8,18 +8,23 @@ namespace Ndk { - inline BaseWidget::BaseWidget(WorldHandle world, BaseWidget* parent) : - m_world(std::move(world)), + inline BaseWidget::BaseWidget() : m_backgroundColor(Nz::Color(230, 230, 230, 255)), + m_canvas(nullptr), m_contentSize(50.f, 50.f), - m_widgetParent(parent) + m_widgetParent(nullptr) { SetPadding(5.f, 5.f, 5.f, 5.f); } inline void BaseWidget::AddChild(std::unique_ptr&& widget) { - m_children.push_back(widget.release()); + m_children.emplace_back(std::move(widget)); + } + + inline Canvas* BaseWidget::GetCanvas() + { + return m_canvas; } inline const BaseWidget::Padding& BaseWidget::GetPadding() const @@ -53,4 +58,9 @@ namespace Ndk Layout(); } + + 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 new file mode 100644 index 000000000..f307f3dd3 --- /dev/null +++ b/SDK/include/NDK/Canvas.hpp @@ -0,0 +1,61 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequesites.hpp + +#pragma once + +#ifndef NDK_CANVAS_HPP +#define NDK_CANVAS_HPP + +#include +#include +#include + +namespace Ndk +{ + class NDK_API Canvas : public BaseWidget + { + friend BaseWidget; + + public: + struct Padding; + + inline Canvas(WorldHandle world, Nz::EventHandler& eventHandler); + Canvas(const Canvas&) = delete; + Canvas(Canvas&&) = delete; + ~Canvas() = default; + + inline const WorldHandle& GetWorld() const; + + void ResizeToContent() override; + + Canvas& operator=(const Canvas&) = delete; + Canvas& operator=(Canvas&&) = delete; + + protected: + void Layout() override; + + void NotifyWidgetUpdate(std::size_t index); + std::size_t RegisterWidget(BaseWidget* widget); + void UnregisterWidget(std::size_t index); + + private: + void OnMouseMoved(const Nz::EventHandler* eventHandler, const Nz::WindowEvent::MouseMoveEvent& event); + + struct WidgetBox + { + BaseWidget* widget; + Nz::Boxf box; + }; + + NazaraSlot(Nz::EventHandler, OnMouseMoved, m_mouseMovedSlot); + + std::vector m_widgetBoxes; + BaseWidget* m_hoveredWidget; + WorldHandle m_world; + }; +} + +#include + +#endif // NDK_CANVAS_HPP diff --git a/SDK/include/NDK/Canvas.inl b/SDK/include/NDK/Canvas.inl new file mode 100644 index 000000000..c976c911b --- /dev/null +++ b/SDK/include/NDK/Canvas.inl @@ -0,0 +1,22 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequesites.hpp + +#include + +namespace Ndk +{ + inline Canvas::Canvas(WorldHandle world, Nz::EventHandler& eventHandler) : + m_hoveredWidget(nullptr), + m_world(std::move(world)) + { + m_canvas = this; + + m_mouseMovedSlot.Connect(eventHandler.OnMouseMoved, this, &Canvas::OnMouseMoved); + } + + inline const WorldHandle& Canvas::GetWorld() const + { + return m_world; + } +} diff --git a/SDK/include/NDK/Widgets/ButtonWidget.hpp b/SDK/include/NDK/Widgets/ButtonWidget.hpp index b70e9ca3d..157304738 100644 --- a/SDK/include/NDK/Widgets/ButtonWidget.hpp +++ b/SDK/include/NDK/Widgets/ButtonWidget.hpp @@ -20,7 +20,7 @@ namespace Ndk class NDK_API ButtonWidget : public BaseWidget { public: - ButtonWidget(const WorldHandle& world, BaseWidget* parent = nullptr); + ButtonWidget(BaseWidget* parent = nullptr); ButtonWidget(const ButtonWidget&) = delete; ButtonWidget(ButtonWidget&&) = default; ~ButtonWidget() = default; @@ -37,6 +37,10 @@ namespace Ndk private: void Layout() override; + void OnMouseEnter() override; + void OnMouseMoved(int x, int y, int deltaX, int deltaY) override; + void OnMouseExit() override; + EntityHandle m_textEntity; EntityHandle m_gradientEntity; Nz::SpriteRef m_gradientSprite; diff --git a/SDK/include/NDK/Widgets/LabelWidget.hpp b/SDK/include/NDK/Widgets/LabelWidget.hpp index 6b9fe32fb..736cfd650 100644 --- a/SDK/include/NDK/Widgets/LabelWidget.hpp +++ b/SDK/include/NDK/Widgets/LabelWidget.hpp @@ -19,7 +19,7 @@ namespace Ndk class NDK_API LabelWidget : public BaseWidget { public: - LabelWidget(const WorldHandle& world, BaseWidget* parent = nullptr); + LabelWidget(BaseWidget* parent = nullptr); LabelWidget(const LabelWidget&) = delete; LabelWidget(LabelWidget&&) = default; ~LabelWidget() = default; diff --git a/SDK/src/NDK/BaseWidget.cpp b/SDK/src/NDK/BaseWidget.cpp index 368c18190..e0c62aab2 100644 --- a/SDK/src/NDK/BaseWidget.cpp +++ b/SDK/src/NDK/BaseWidget.cpp @@ -3,16 +3,28 @@ // For conditions of distribution and use, see copyright notice in Prerequesites.hpp #include +#include #include #include #include namespace Ndk { + BaseWidget::BaseWidget(BaseWidget* parent) : + BaseWidget() + { + NazaraAssert(parent, "Invalid parent"); + NazaraAssert(parent->GetCanvas(), "Parent has no canvas"); + + m_canvas = parent->GetCanvas(); + m_world = m_canvas->GetWorld(); + + m_canvasIndex = m_canvas->RegisterWidget(this); + } + BaseWidget::~BaseWidget() { - for (BaseWidget* child : m_children) - delete child; + m_canvas->UnregisterWidget(m_canvasIndex); } inline void BaseWidget::EnableBackground(bool enable) @@ -60,6 +72,9 @@ namespace Ndk void BaseWidget::Layout() { + if (m_canvas) + m_canvas->NotifyWidgetUpdate(m_canvasIndex); + if (m_backgroundEntity) { NodeComponent& node = m_backgroundEntity->GetComponent(); @@ -68,4 +83,24 @@ namespace Ndk m_backgroundSprite->SetSize(m_contentSize.x + m_padding.left + m_padding.right, m_contentSize.y + m_padding.top + m_padding.bottom); } } + + void BaseWidget::InvalidateNode() + { + Node::InvalidateNode(); + + if (m_canvas) + m_canvas->NotifyWidgetUpdate(m_canvasIndex); + } + + void BaseWidget::OnMouseEnter() + { + } + + void BaseWidget::OnMouseMoved(int x, int y, int deltaX, int deltaY) + { + } + + void BaseWidget::OnMouseExit() + { + } } \ No newline at end of file diff --git a/SDK/src/NDK/Canvas.cpp b/SDK/src/NDK/Canvas.cpp new file mode 100644 index 000000000..79efc4cec --- /dev/null +++ b/SDK/src/NDK/Canvas.cpp @@ -0,0 +1,102 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequesites.hpp + +#include +#include +#include +#include +#include + +namespace Ndk +{ + void Ndk::Canvas::ResizeToContent() + { + } + + void Canvas::Layout() + { + if (m_backgroundEntity) + { + NodeComponent& node = m_backgroundEntity->GetComponent(); + node.SetPosition(-m_padding.left, -m_padding.top); + + m_backgroundSprite->SetSize(m_contentSize.x + m_padding.left + m_padding.right, m_contentSize.y + m_padding.top + m_padding.bottom); + } + } + + void Canvas::NotifyWidgetUpdate(std::size_t index) + { + WidgetBox& entry = m_widgetBoxes[index]; + + Nz::Vector3f pos = entry.widget->GetPosition(); + Nz::Vector2f size = entry.widget->GetContentSize(); + + entry.box.Set(pos.x, pos.y, pos.z, size.x, size.y, 1.f); + } + + std::size_t Canvas::RegisterWidget(BaseWidget* widget) + { + WidgetBox box; + box.widget = widget; + + std::size_t index = m_widgetBoxes.size(); + m_widgetBoxes.emplace_back(box); + + NotifyWidgetUpdate(index); + return index; + } + + void Canvas::UnregisterWidget(std::size_t index) + { + if (m_widgetBoxes.size() > 1U) + { + WidgetBox& entry = m_widgetBoxes[index]; + WidgetBox& lastEntry = m_widgetBoxes.back(); + + entry = std::move(lastEntry); + entry.widget->UpdateCanvasIndex(index); + m_widgetBoxes.pop_back(); + } + } + + void Canvas::OnMouseMoved(const Nz::EventHandler* eventHandler, const Nz::WindowEvent::MouseMoveEvent& event) + { + const WidgetBox* bestEntry = nullptr; + float bestEntryArea = std::numeric_limits::infinity(); + + for (const WidgetBox& entry : m_widgetBoxes) + { + const Nz::Boxf& box = entry.box; + + if (box.Contains(Nz::Vector3f(event.x, event.y, 0.f))) + { + float area = box.width * box.height; + if (area < bestEntryArea) + { + bestEntry = &entry; + bestEntryArea = area; + } + } + } + + if (bestEntry) + { + if (m_hoveredWidget != bestEntry->widget) + { + if (m_hoveredWidget) + m_hoveredWidget->OnMouseExit(); + + m_hoveredWidget = bestEntry->widget; + m_hoveredWidget->OnMouseEnter(); + } + + bestEntry->widget->OnMouseMoved(event.x - bestEntry->box.x, event.y - bestEntry->box.y, event.deltaX, event.deltaY); + } + else if (m_hoveredWidget) + { + m_hoveredWidget->OnMouseExit(); + m_hoveredWidget = nullptr; + } + } +} \ No newline at end of file diff --git a/SDK/src/NDK/Widgets/ButtonWidget.cpp b/SDK/src/NDK/Widgets/ButtonWidget.cpp index 5afcb8ad8..db7fa53b4 100644 --- a/SDK/src/NDK/Widgets/ButtonWidget.cpp +++ b/SDK/src/NDK/Widgets/ButtonWidget.cpp @@ -9,8 +9,8 @@ namespace Ndk { - ButtonWidget::ButtonWidget(const WorldHandle& world, BaseWidget* parent) : - BaseWidget(world, parent) + ButtonWidget::ButtonWidget(BaseWidget* parent) : + BaseWidget(parent) { m_gradientSprite = Nz::Sprite::New(); m_gradientSprite->SetColor(Nz::Color(74, 74, 74)); @@ -21,13 +21,14 @@ namespace Ndk m_gradientEntity = CreateEntity(); m_gradientEntity->AddComponent().SetParent(this); m_gradientEntity->AddComponent().Attach(m_gradientSprite); - m_gradientEntity->GetComponent().Attach(m_borderSprite, Nz::Matrix4f::Translate(Nz::Vector2f(-1.f, -1.f)), -1); m_textSprite = Nz::TextSprite::New(); m_textEntity = CreateEntity(); m_textEntity->AddComponent().SetParent(this); m_textEntity->AddComponent().Attach(m_textSprite, 1); + + Layout(); } void ButtonWidget::ResizeToContent() @@ -37,6 +38,8 @@ namespace Ndk void ButtonWidget::Layout() { + BaseWidget::Layout(); + const Nz::Vector2f& contentSize = GetContentSize(); m_gradientSprite->SetSize(contentSize); @@ -44,4 +47,19 @@ namespace Ndk Nz::Boxf textBox = m_textEntity->GetComponent().GetBoundingVolume().aabb; m_textEntity->GetComponent().SetPosition(contentSize.x / 2 - textBox.width / 2, contentSize.y / 2 - textBox.height / 2); } + + void ButtonWidget::OnMouseEnter() + { + m_gradientSprite->SetColor(Nz::Color(128, 128, 128)); + } + + void ButtonWidget::OnMouseMoved(int x, int y, int deltaX, int deltaY) + { + NazaraDebug(Nz::String::Number(x) + ", " + Nz::String::Number(y)); + } + + void ButtonWidget::OnMouseExit() + { + m_gradientSprite->SetColor(Nz::Color(74, 74, 74)); + } } diff --git a/SDK/src/NDK/Widgets/LabelWidget.cpp b/SDK/src/NDK/Widgets/LabelWidget.cpp index ac2a87bf5..df9c0e1c7 100644 --- a/SDK/src/NDK/Widgets/LabelWidget.cpp +++ b/SDK/src/NDK/Widgets/LabelWidget.cpp @@ -9,14 +9,16 @@ namespace Ndk { - LabelWidget::LabelWidget(const WorldHandle& world, BaseWidget* parent) : - BaseWidget(world, parent) + LabelWidget::LabelWidget(BaseWidget* parent) : + BaseWidget(parent) { m_textSprite = Nz::TextSprite::New(); m_textEntity = CreateEntity(); m_textEntity->AddComponent().Attach(m_textSprite); m_textEntity->AddComponent().SetParent(this); + + Layout(); } void LabelWidget::ResizeToContent()