From a66f6faed1f36e5ac7f336616f7549ee09eeddbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Leclercq?= Date: Mon, 4 Oct 2021 09:25:45 +0200 Subject: [PATCH] Add widgets (WIP) --- examples/PhysicsDemo/main.cpp | 39 ++- examples/PhysicsDemo/xmake.lua | 2 +- include/Nazara/Core/ECS.hpp | 1 + include/Nazara/Graphics.hpp | 5 + include/Nazara/Graphics/Sprite.hpp | 11 + include/Nazara/Graphics/Sprite.inl | 47 +++ include/Nazara/Math/Enums.hpp | 2 + include/Nazara/Widgets.hpp | 38 +++ include/Nazara/Widgets/BaseWidget.hpp | 181 ++++++++++ include/Nazara/Widgets/BaseWidget.inl | 276 ++++++++++++++++ include/Nazara/Widgets/ButtonWidget.hpp | 72 ++++ include/Nazara/Widgets/ButtonWidget.inl | 71 ++++ include/Nazara/Widgets/Canvas.hpp | 88 +++++ include/Nazara/Widgets/Canvas.inl | 99 ++++++ include/Nazara/Widgets/Config.hpp | 53 +++ include/Nazara/Widgets/ConfigCheck.hpp | 23 ++ include/Nazara/Widgets/Debug.hpp | 8 + include/Nazara/Widgets/DebugOff.hpp | 9 + include/Nazara/Widgets/LabelWidget.hpp | 39 +++ include/Nazara/Widgets/LabelWidget.inl | 17 + include/Nazara/Widgets/Widgets.hpp | 39 +++ include/Nazara/Widgets/Widgets.inl | 12 + src/Nazara/Graphics/BakedFrameGraph.cpp | 2 +- src/Nazara/Graphics/Sprite.cpp | 22 +- src/Nazara/Widgets/BaseWidget.cpp | 401 +++++++++++++++++++++++ src/Nazara/Widgets/ButtonWidget.cpp | 97 ++++++ src/Nazara/Widgets/Canvas.cpp | 230 +++++++++++++ src/Nazara/Widgets/Debug/NewOverload.cpp | 31 ++ src/Nazara/Widgets/LabelWidget.cpp | 36 ++ src/Nazara/Widgets/Widgets.cpp | 22 ++ xmake.lua | 6 +- 31 files changed, 1955 insertions(+), 24 deletions(-) create mode 100644 include/Nazara/Widgets.hpp create mode 100644 include/Nazara/Widgets/BaseWidget.hpp create mode 100644 include/Nazara/Widgets/BaseWidget.inl create mode 100644 include/Nazara/Widgets/ButtonWidget.hpp create mode 100644 include/Nazara/Widgets/ButtonWidget.inl create mode 100644 include/Nazara/Widgets/Canvas.hpp create mode 100644 include/Nazara/Widgets/Canvas.inl create mode 100644 include/Nazara/Widgets/Config.hpp create mode 100644 include/Nazara/Widgets/ConfigCheck.hpp create mode 100644 include/Nazara/Widgets/Debug.hpp create mode 100644 include/Nazara/Widgets/DebugOff.hpp create mode 100644 include/Nazara/Widgets/LabelWidget.hpp create mode 100644 include/Nazara/Widgets/LabelWidget.inl create mode 100644 include/Nazara/Widgets/Widgets.hpp create mode 100644 include/Nazara/Widgets/Widgets.inl create mode 100644 src/Nazara/Widgets/BaseWidget.cpp create mode 100644 src/Nazara/Widgets/ButtonWidget.cpp create mode 100644 src/Nazara/Widgets/Canvas.cpp create mode 100644 src/Nazara/Widgets/Debug/NewOverload.cpp create mode 100644 src/Nazara/Widgets/LabelWidget.cpp create mode 100644 src/Nazara/Widgets/Widgets.cpp diff --git a/examples/PhysicsDemo/main.cpp b/examples/PhysicsDemo/main.cpp index 658fc5991..0b402f7cd 100644 --- a/examples/PhysicsDemo/main.cpp +++ b/examples/PhysicsDemo/main.cpp @@ -15,9 +15,11 @@ #include #include #include +#include #include #include #include +#include NAZARA_REQUEST_DEDICATED_GPU() @@ -34,7 +36,9 @@ int main() else rendererConfig.preferredAPI = Nz::RenderAPI::OpenGL; - Nz::Modules nazara(rendererConfig); + std::cin.ignore(std::numeric_limits::max(), '\n'); + + Nz::Modules nazara(rendererConfig); Nz::RenderWindow window; @@ -125,20 +129,10 @@ int main() spriteMaterial->AddPass("ForwardPass", spriteMaterialPass); std::shared_ptr sprite = std::make_shared(spriteMaterial); + sprite->Update(Nz::SimpleTextDrawer::Draw("Voix ambiguë d'un cœur qui, au zéphyr, préfère les jattes de kiwis", 72), 0.01f); - std::u32string str = Nz::ToUtf32String("Dès Noël, où un zéphyr haï me vêt de glaçons würmiens, je dîne d’exquis rôtis de bœuf au kir, à l’aÿ d’âge mûr, &cætera"); - - Nz::RichTextDrawer richText; - unsigned int size = 16; - for (char32_t character : str) - { - richText.SetDefaultCharacterSize(size); - richText.AppendText(Nz::FromUtf32String(std::u32string_view(&character, 1))); - - size += 2; - } - - sprite->Update(richText, 0.02f); + //for (std::size_t i = 16; i < 200; i += 3) + // sprite->Update(Nz::SimpleTextDrawer::Draw("Voix ambiguë d'un cœur qui, au zéphyr, préfère les jattes de kiwis", i), 0.01f); Nz::Vector2ui windowSize = window.GetSize(); @@ -152,6 +146,11 @@ int main() Nz::RenderSystem renderSystem(registry); Nz::RenderSystem renderSystem2D(registry2D); + Nz::Canvas canvas(registry, window.GetEventHandler(), window.GetCursorController().CreateHandle()); + Nz::LabelWidget* labelWidget = canvas.Add(); + //labelWidget->EnableBackground(true); + labelWidget->UpdateText(Nz::SimpleTextDrawer::Draw("Bonjour Paris !", 72), 0.1f); + entt::entity viewer2D = registry2D.create(); registry2D.emplace(viewer2D); registry2D.emplace(viewer2D, window.GetRenderTarget(), Nz::ProjectionType::Orthographic); @@ -188,13 +187,21 @@ int main() colliderModel->SetMaterial(i, colliderMat); } + + entt::entity textEntity = registry.create(); + { + auto& entityGfx = registry.emplace(textEntity); + entityGfx.AttachRenderable(sprite); + + auto& entityNode = registry.emplace(textEntity); + entityNode.SetPosition(0.f, 5.f, 0.f); + } entt::entity playerEntity = registry.create(); entt::entity headingEntity = registry.create(); { auto& entityGfx = registry.emplace(playerEntity); entityGfx.AttachRenderable(model); - entityGfx.AttachRenderable(sprite); auto& entityNode = registry.emplace(playerEntity); entityNode.SetPosition(Nz::Vector3f(12.5f, 0.f, 25.f)); @@ -220,7 +227,7 @@ int main() entt::entity entity = registry.create(); auto& entityGfx = registry.emplace(entity); entityGfx.AttachRenderable(model); - entityGfx.AttachRenderable(sprite); + //entityGfx.AttachRenderable(sprite); auto& entityNode = registry.emplace(entity); entityNode.SetPosition(Nz::Vector3f(x * 2.f, y * 1.5f, z * 2.f)); diff --git a/examples/PhysicsDemo/xmake.lua b/examples/PhysicsDemo/xmake.lua index 65a6f99f7..c3b1b6667 100644 --- a/examples/PhysicsDemo/xmake.lua +++ b/examples/PhysicsDemo/xmake.lua @@ -1,6 +1,6 @@ target("PhysicsDemo") set_group("Examples") set_kind("binary") - add_deps("NazaraGraphics", "NazaraPhysics3D") + add_deps("NazaraGraphics", "NazaraPhysics3D", "NazaraWidgets") add_packages("entt") add_files("main.cpp") diff --git a/include/Nazara/Core/ECS.hpp b/include/Nazara/Core/ECS.hpp index 60b9cd49b..bfbb93521 100644 --- a/include/Nazara/Core/ECS.hpp +++ b/include/Nazara/Core/ECS.hpp @@ -26,6 +26,7 @@ namespace Nz friend class Physics2D; friend class Physics3D; friend class Utility; + friend class Widgets; public: using Dependencies = TypeList; diff --git a/include/Nazara/Graphics.hpp b/include/Nazara/Graphics.hpp index 0bfeb7b60..81199025a 100644 --- a/include/Nazara/Graphics.hpp +++ b/include/Nazara/Graphics.hpp @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -57,8 +58,12 @@ #include #include #include +#include #include +#include +#include #include +#include #include #include #include diff --git a/include/Nazara/Graphics/Sprite.hpp b/include/Nazara/Graphics/Sprite.hpp index 3326c2071..3bca1d62a 100644 --- a/include/Nazara/Graphics/Sprite.hpp +++ b/include/Nazara/Graphics/Sprite.hpp @@ -27,17 +27,28 @@ namespace Nz void BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, std::vector>& elements) const override; + inline const Color& GetColor() const; + inline const Color& GetCornerColor(RectCorner corner) const; const std::shared_ptr& GetMaterial(std::size_t i) const; std::size_t GetMaterialCount() const; + inline void SetColor(const Color& color); + inline void SetCornerColor(RectCorner corner, const Color& color); inline void SetMaterial(std::shared_ptr material); + inline void SetSize(const Vector2f& size); Sprite& operator=(const Sprite&) = delete; Sprite& operator=(Sprite&&) noexcept = default; private: + inline void UpdateVertices(); + + std::array m_cornerColor; std::array m_vertices; std::shared_ptr m_material; + Color m_color; + Rectf m_textureCoords; + Vector2f m_size; }; } diff --git a/include/Nazara/Graphics/Sprite.inl b/include/Nazara/Graphics/Sprite.inl index 858c9cfb7..ae439c7e2 100644 --- a/include/Nazara/Graphics/Sprite.inl +++ b/include/Nazara/Graphics/Sprite.inl @@ -8,10 +8,57 @@ namespace Nz { + inline void Sprite::SetColor(const Color& color) + { + m_color = color; + + UpdateVertices(); + } + + inline void Sprite::SetCornerColor(RectCorner corner, const Color& color) + { + m_cornerColor[UnderlyingCast(corner)] = color; + + UpdateVertices(); + } + inline void Sprite::SetMaterial(std::shared_ptr material) { m_material = std::move(material); } + + inline void Sprite::SetSize(const Vector2f& size) + { + m_size = size; + + UpdateVertices(); + } + + inline void Sprite::UpdateVertices() + { + VertexStruct_XYZ_Color_UV* vertices = m_vertices.data(); + + Vector3f origin = Vector3f::Zero(); + + vertices->color = m_color * m_cornerColor[UnderlyingCast(RectCorner::LeftTop)]; + vertices->position = Vector3f(-origin); + vertices->uv = m_textureCoords.GetCorner(RectCorner::LeftTop); + vertices++; + + vertices->color = m_color * m_cornerColor[UnderlyingCast(RectCorner::RightTop)]; + vertices->position = m_size.x * Vector3f::Right() - origin; + vertices->uv = m_textureCoords.GetCorner(RectCorner::RightTop); + vertices++; + + vertices->color = m_color * m_cornerColor[UnderlyingCast(RectCorner::LeftBottom)]; + vertices->position = m_size.y * Vector3f::Down() - origin; + vertices->uv = m_textureCoords.GetCorner(RectCorner::LeftBottom); + vertices++; + + vertices->color = m_color * m_cornerColor[UnderlyingCast(RectCorner::RightBottom)]; + vertices->position = m_size.x * Vector3f::Right() + m_size.y * Vector3f::Down() - origin; + vertices->uv = m_textureCoords.GetCorner(RectCorner::RightBottom); + } } #include diff --git a/include/Nazara/Math/Enums.hpp b/include/Nazara/Math/Enums.hpp index 5ed60a81c..9ff0edffe 100644 --- a/include/Nazara/Math/Enums.hpp +++ b/include/Nazara/Math/Enums.hpp @@ -74,6 +74,8 @@ namespace Nz Max = RightTop }; + + constexpr std::size_t RectCornerCount = UnderlyingCast(RectCorner::Max) + 1; } #endif // NAZARA_ENUMS_MATH_HPP diff --git a/include/Nazara/Widgets.hpp b/include/Nazara/Widgets.hpp new file mode 100644 index 000000000..40789c49a --- /dev/null +++ b/include/Nazara/Widgets.hpp @@ -0,0 +1,38 @@ +// This file was automatically generated + +/* + Nazara Engine - Widgets module + + Copyright (C) 2020 Jérôme "Lynix" Leclercq (Lynix680@gmail.com) + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do + so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#pragma once + +#ifndef NAZARA_GLOBAL_WIDGETS_HPP +#define NAZARA_GLOBAL_WIDGETS_HPP + +#include +#include +#include +#include +#include + +#endif // NAZARA_GLOBAL_WIDGETS_HPP diff --git a/include/Nazara/Widgets/BaseWidget.hpp b/include/Nazara/Widgets/BaseWidget.hpp new file mode 100644 index 000000000..dfe710a99 --- /dev/null +++ b/include/Nazara/Widgets/BaseWidget.hpp @@ -0,0 +1,181 @@ +// 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 NAZARA_BASEWIDGET_HPP +#define NAZARA_BASEWIDGET_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + class Canvas; + + class NAZARA_WIDGETS_API BaseWidget : public Nz::Node + { + friend Canvas; + + public: + BaseWidget(BaseWidget* parent); + BaseWidget(const BaseWidget&) = delete; + BaseWidget(BaseWidget&&) = delete; + virtual ~BaseWidget(); + + template T* Add(Args&&... args); + inline void AddChild(std::unique_ptr&& widget); + + inline void Center(); + inline void CenterHorizontal(); + inline void CenterVertical(); + + void ClearFocus(); + inline void ClearRenderingRect(); + + void Destroy(); + + void EnableBackground(bool enable); + + template void ForEachWidgetChild(F iterator); + template void ForEachWidgetChild(F iterator) const; + + //virtual BaseWidget* Clone() const = 0; + + inline const Color& GetBackgroundColor() const; + inline Canvas* GetCanvas(); + inline Nz::SystemCursor GetCursor() 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 const Nz::Rectf& GetRenderingRect() const; + + inline Nz::Vector2f GetSize() const; + inline float GetWidth() const; + inline std::size_t GetWidgetChildCount() const; + + bool HasFocus() const; + + inline void Hide(); + inline bool IsVisible() const; + + void Resize(const Nz::Vector2f& size); + + void SetBackgroundColor(const Color& color); + void SetCursor(Nz::SystemCursor systemCursor); + void SetFocus(); + void SetParent(BaseWidget* widget); + + 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); + + virtual void SetRenderingRect(const Nz::Rectf& renderingRect); + + void Show(bool show = true); + + BaseWidget& operator=(const BaseWidget&) = delete; + BaseWidget& operator=(BaseWidget&&) = delete; + + protected: + entt::entity CreateEntity(); + void DestroyEntity(entt::entity entity); + virtual void Layout(); + + void InvalidateNode() override; + + inline entt::registry& GetRegistry(); + inline const entt::registry& GetRegistry() const; + Nz::Rectf GetScissorRect() const; + + virtual bool IsFocusable() const; + virtual void OnFocusLost(); + virtual void OnFocusReceived(); + virtual bool OnKeyPressed(const Nz::WindowEvent::KeyEvent& key); + virtual void OnKeyReleased(const Nz::WindowEvent::KeyEvent& key); + virtual void OnMouseEnter(); + virtual void OnMouseMoved(int x, int y, int deltaX, int deltaY); + virtual void OnMouseButtonPress(int x, int y, Nz::Mouse::Button button); + virtual void OnMouseButtonRelease(int x, int y, Nz::Mouse::Button button); + virtual void OnMouseWheelMoved(int x, int y, float delta); + virtual void OnMouseExit(); + virtual void OnParentResized(const Nz::Vector2f& newSize); + virtual void OnTextEntered(char32_t character, bool repeated); + virtual void OnTextEdited(const std::array& characters, int length); + + inline void SetPreferredSize(const Nz::Vector2f& preferredSize); + + virtual void ShowChildren(bool show); + + private: + inline BaseWidget(); + + void DestroyChild(BaseWidget* widget); + void DestroyChildren(); + inline bool IsRegisteredToCanvas() const; + inline void NotifyParentResized(const Nz::Vector2f& newSize); + void RegisterToCanvas(); + inline void UpdateCanvasIndex(std::size_t index); + void UnregisterFromCanvas(); + void UpdatePositionAndSize(); + + struct WidgetEntity + { + entt::entity handle; + bool isEnabled = true; + + //NazaraSlot(Ndk::Entity, OnEntityDisabled, onDisabledSlot); + //NazaraSlot(Ndk::Entity, OnEntityEnabled, onEnabledSlot); + }; + + static constexpr std::size_t InvalidCanvasIndex = std::numeric_limits::max(); + + std::optional m_backgroundEntity; + std::size_t m_canvasIndex; + std::shared_ptr m_backgroundSprite; + std::vector m_entities; + std::vector> m_children; + entt::registry* m_registry; + Canvas* m_canvas; + Color m_backgroundColor; + Nz::Rectf m_renderingRect; + Nz::SystemCursor m_cursor; + Nz::Vector2f m_maximumSize; + Nz::Vector2f m_minimumSize; + Nz::Vector2f m_preferredSize; + Nz::Vector2f m_size; + BaseWidget* m_widgetParent; + bool m_visible; + }; +} + +#include + +#endif // NAZARA_BASEWIDGET_HPP diff --git a/include/Nazara/Widgets/BaseWidget.inl b/include/Nazara/Widgets/BaseWidget.inl new file mode 100644 index 000000000..37f1eaeff --- /dev/null +++ b/include/Nazara/Widgets/BaseWidget.inl @@ -0,0 +1,276 @@ +// 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 Nz +{ + inline BaseWidget::BaseWidget() : + m_canvasIndex(InvalidCanvasIndex), + m_registry(nullptr), + m_canvas(nullptr), + m_backgroundColor(Color(230, 230, 230, 255)), + m_renderingRect(-std::numeric_limits::infinity(), -std::numeric_limits::infinity(), std::numeric_limits::infinity(), std::numeric_limits::infinity()), + m_cursor(Nz::SystemCursor::Default), + m_maximumSize(std::numeric_limits::infinity()), + m_minimumSize(0.f), + m_preferredSize(-1), + m_size(50.f, 50.f), + m_widgetParent(nullptr), + m_visible(true) + { + } + + template + inline T* BaseWidget::Add(Args&&... args) + { + std::unique_ptr widget = std::make_unique(this, std::forward(args)...); + T* widgetPtr = widget.get(); + AddChild(std::move(widget)); + + return widgetPtr; + } + + inline void BaseWidget::AddChild(std::unique_ptr&& widget) + { + widget->Show(m_visible); + widget->SetParent(this); + m_children.emplace_back(std::move(widget)); + } + + inline void BaseWidget::Center() + { + NazaraAssert(m_widgetParent, "Widget has no parent"); + + Nz::Vector2f parentSize = m_widgetParent->GetSize(); + Nz::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"); + + Nz::Vector2f parentSize = m_widgetParent->GetSize(); + Nz::Vector2f mySize = GetSize(); + SetPosition((parentSize.x - mySize.x) / 2.f, GetPosition(Nz::CoordSys::Local).y); + } + + inline void BaseWidget::CenterVertical() + { + NazaraAssert(m_widgetParent, "Widget has no parent"); + + Nz::Vector2f parentSize = m_widgetParent->GetSize(); + Nz::Vector2f mySize = GetSize(); + SetPosition(GetPosition(Nz::CoordSys::Local).x, (parentSize.y - mySize.y) / 2.f); + } + + inline void BaseWidget::ClearRenderingRect() + { + SetRenderingRect(Nz::Rectf(-std::numeric_limits::infinity(), -std::numeric_limits::infinity(), std::numeric_limits::infinity(), std::numeric_limits::infinity())); + } + + 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 Color& BaseWidget::GetBackgroundColor() const + { + return m_backgroundColor; + } + + inline Canvas* BaseWidget::GetCanvas() + { + return m_canvas; + } + + inline Nz::SystemCursor BaseWidget::GetCursor() const + { + return m_cursor; + } + + inline float BaseWidget::GetHeight() const + { + return m_size.y; + } + + inline float BaseWidget::GetMaximumHeight() const + { + return m_maximumSize.y; + } + + inline Nz::Vector2f BaseWidget::GetMaximumSize() const + { + 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 const Nz::Rectf& BaseWidget::GetRenderingRect() const + { + return m_renderingRect; + } + + inline Nz::Vector2f BaseWidget::GetSize() const + { + 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 void BaseWidget::Hide() + { + return Show(false); + } + + inline bool BaseWidget::IsVisible() const + { + return m_visible; + } + + inline void BaseWidget::SetFixedHeight(float fixedHeight) + { + SetMaximumHeight(fixedHeight); + SetMinimumHeight(fixedHeight); + } + + inline void BaseWidget::SetFixedSize(const Nz::Vector2f& fixedSize) + { + SetMaximumSize(fixedSize); + SetMinimumSize(fixedSize); + } + + 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 + { + return m_canvas && m_canvasIndex != InvalidCanvasIndex; + } + + 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/include/Nazara/Widgets/ButtonWidget.hpp b/include/Nazara/Widgets/ButtonWidget.hpp new file mode 100644 index 000000000..602520682 --- /dev/null +++ b/include/Nazara/Widgets/ButtonWidget.hpp @@ -0,0 +1,72 @@ +// 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 NAZARA_BUTTONWIDGET_HPP +#define NAZARA_BUTTONWIDGET_HPP + +#include +#include +#include +#include + +namespace Nz +{ + class AbstractTextDrawer; + + class NAZARA_WIDGETS_API ButtonWidget : public BaseWidget + { + public: + ButtonWidget(BaseWidget* parent); + ButtonWidget(const ButtonWidget&) = delete; + ButtonWidget(ButtonWidget&&) = default; + ~ButtonWidget() = default; + + inline const Color& GetColor() const; + inline const Color& GetCornerColor() const; + inline const Color& GetHoverColor() const; + inline const Color& GetHoverCornerColor() const; + inline const Color& GetPressColor() const; + inline const Color& GetPressCornerColor() const; + + inline const std::shared_ptr& GetTexture() const; + inline const std::shared_ptr& GetHoverTexture() const; + inline const std::shared_ptr& GetPressTexture() const; + + inline void SetColor(const Color& color, const Color& cornerColor); + inline void SetHoverColor(const Color& color, const Color& cornerColor); + inline void SetPressColor(const Color& color, const Color& cornerColor); + + inline void UpdateText(const AbstractTextDrawer& drawer); + + ButtonWidget& operator=(const ButtonWidget&) = delete; + ButtonWidget& operator=(ButtonWidget&&) = default; + + NazaraSignal(OnButtonTrigger, const ButtonWidget* /*button*/); + + private: + void Layout() override; + + void OnMouseEnter() override; + void OnMouseButtonPress(int x, int y, Mouse::Button button) override; + void OnMouseButtonRelease(int x, int y, Mouse::Button button) override; + void OnMouseExit() override; + + std::shared_ptr m_gradientSprite; + std::shared_ptr m_textSprite; + entt::entity m_textEntity; + entt::entity m_gradientEntity; + Color m_color; + Color m_cornerColor; + Color m_hoverColor; + Color m_hoverCornerColor; + Color m_pressColor; + Color m_pressCornerColor; + }; +} + +#include + +#endif // NDK_WIDGETS_BUTTONWIDGET_HPP diff --git a/include/Nazara/Widgets/ButtonWidget.inl b/include/Nazara/Widgets/ButtonWidget.inl new file mode 100644 index 000000000..ea914c397 --- /dev/null +++ b/include/Nazara/Widgets/ButtonWidget.inl @@ -0,0 +1,71 @@ +// 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 Nz +{ + inline const Color& ButtonWidget::GetColor() const + { + return m_color; + } + + inline const Color& ButtonWidget::GetCornerColor() const + { + return m_cornerColor; + } + + inline const Color& ButtonWidget::GetHoverColor() const + { + return m_hoverColor; + } + + inline const Color& ButtonWidget::GetHoverCornerColor() const + { + return m_hoverCornerColor; + } + + inline const Color& ButtonWidget::GetPressColor() const + { + return m_pressColor; + } + + inline const Color& ButtonWidget::GetPressCornerColor() const + { + return m_pressCornerColor; + } + + inline void ButtonWidget::SetColor(const Color& color, const Color& cornerColor) + { + m_color = color; + m_cornerColor = cornerColor; + + m_gradientSprite->SetColor(m_color); + m_gradientSprite->SetCornerColor(Nz::RectCorner::LeftBottom, m_cornerColor); + m_gradientSprite->SetCornerColor(Nz::RectCorner::RightBottom, m_cornerColor); + } + + inline void ButtonWidget::SetHoverColor(const Color& color, const Color& cornerColor) + { + m_hoverColor = color; + m_hoverCornerColor = cornerColor; + } + + inline void ButtonWidget::SetPressColor(const Color& color, const Color& cornerColor) + { + m_pressColor = color; + m_pressCornerColor = cornerColor; + } + + inline void ButtonWidget::UpdateText(const Nz::AbstractTextDrawer& drawer) + { + m_textSprite->Update(drawer); + + Nz::Vector2f textSize = Nz::Vector2f(m_textSprite->GetAABB().GetLengths()); + SetMinimumSize(textSize); + SetPreferredSize(textSize + Nz::Vector2f(20.f, 10.f)); + + Layout(); + } +} diff --git a/include/Nazara/Widgets/Canvas.hpp b/include/Nazara/Widgets/Canvas.hpp new file mode 100644 index 000000000..38837ed66 --- /dev/null +++ b/include/Nazara/Widgets/Canvas.hpp @@ -0,0 +1,88 @@ +// Copyright (C) 2020 Jérôme Leclercq +// This file is part of the "Nazara Engine - Widgets module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_CANVAS_HPP +#define NAZARA_CANVAS_HPP + +#include +#include +#include +#include + +namespace Nz +{ + class NAZARA_WIDGETS_API Canvas : public BaseWidget + { + friend BaseWidget; + + public: + inline Canvas(entt::registry& registry, Nz::EventHandler& eventHandler, Nz::CursorControllerHandle cursorController); + Canvas(const Canvas&) = delete; + Canvas(Canvas&&) = delete; + inline ~Canvas(); + + inline entt::registry& GetRegistry(); + inline const entt::registry& GetRegistry() const; + + Canvas& operator=(const Canvas&) = delete; + Canvas& operator=(Canvas&&) = delete; + + NazaraSignal(OnUnhandledKeyPressed, const Nz::EventHandler* /*eventHandler*/, const Nz::WindowEvent::KeyEvent& /*event*/); + NazaraSignal(OnUnhandledKeyReleased, const Nz::EventHandler* /*eventHandler*/, const Nz::WindowEvent::KeyEvent& /*event*/); + + protected: + inline void ClearKeyboardOwner(std::size_t canvasIndex); + + inline bool IsKeyboardOwner(std::size_t canvasIndex) const; + + inline void NotifyWidgetBoxUpdate(std::size_t index); + inline void NotifyWidgetCursorUpdate(std::size_t index); + + std::size_t RegisterWidget(BaseWidget* widget); + + inline void SetKeyboardOwner(std::size_t canvasIndex); + + void UnregisterWidget(std::size_t index); + + private: + void OnEventMouseButtonPressed(const Nz::EventHandler* eventHandler, const Nz::WindowEvent::MouseButtonEvent& event); + void OnEventMouseButtonRelease(const Nz::EventHandler* eventHandler, const Nz::WindowEvent::MouseButtonEvent& event); + void OnEventMouseLeft(const Nz::EventHandler* eventHandler); + void OnEventMouseMoved(const Nz::EventHandler* eventHandler, const Nz::WindowEvent::MouseMoveEvent& event); + void OnEventMouseWheelMoved(const Nz::EventHandler* eventHandler, const Nz::WindowEvent::MouseWheelEvent& event); + void OnEventKeyPressed(const Nz::EventHandler* eventHandler, const Nz::WindowEvent::KeyEvent& event); + void OnEventKeyReleased(const Nz::EventHandler* eventHandler, const Nz::WindowEvent::KeyEvent& event); + void OnEventTextEntered(const Nz::EventHandler* eventHandler, const Nz::WindowEvent::TextEvent& event); + void OnEventTextEdited(const Nz::EventHandler* eventHandler, const Nz::WindowEvent::EditEvent& event); + + struct WidgetEntry + { + BaseWidget* widget; + Nz::Boxf box; + Nz::SystemCursor cursor; + }; + + NazaraSlot(Nz::EventHandler, OnKeyPressed, m_keyPressedSlot); + NazaraSlot(Nz::EventHandler, OnKeyReleased, m_keyReleasedSlot); + NazaraSlot(Nz::EventHandler, OnMouseButtonPressed, m_mouseButtonPressedSlot); + NazaraSlot(Nz::EventHandler, OnMouseButtonReleased, m_mouseButtonReleasedSlot); + NazaraSlot(Nz::EventHandler, OnMouseLeft, m_mouseLeftSlot); + NazaraSlot(Nz::EventHandler, OnMouseMoved, m_mouseMovedSlot); + NazaraSlot(Nz::EventHandler, OnMouseWheelMoved, m_mouseWheelMovedSlot); + NazaraSlot(Nz::EventHandler, OnTextEntered, m_textEnteredSlot); + NazaraSlot(Nz::EventHandler, OnTextEdited, m_textEditedSlot); + + std::size_t m_keyboardOwner; + std::size_t m_hoveredWidget; + std::vector m_widgetEntries; + entt::registry& m_registry; + Nz::CursorControllerHandle m_cursorController; + }; +} + +#include + +#endif // NDK_CANVAS_HPP diff --git a/include/Nazara/Widgets/Canvas.inl b/include/Nazara/Widgets/Canvas.inl new file mode 100644 index 000000000..3cbb75eea --- /dev/null +++ b/include/Nazara/Widgets/Canvas.inl @@ -0,0 +1,99 @@ +// Copyright (C) 2020 Jérôme Leclercq +// This file is part of the "Nazara Engine - Widgets module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include + +namespace Nz +{ + inline Canvas::Canvas(entt::registry& registry, Nz::EventHandler& eventHandler, Nz::CursorControllerHandle cursorController) : + m_keyboardOwner(InvalidCanvasIndex), + m_hoveredWidget(InvalidCanvasIndex), + m_registry(registry), + m_cursorController(cursorController) + { + m_canvas = this; + m_widgetParent = nullptr; + + // Register ourselves as a widget to handle cursor change + RegisterToCanvas(); + + // Connect to every meaningful event + m_keyPressedSlot.Connect(eventHandler.OnKeyPressed, this, &Canvas::OnEventKeyPressed); + m_keyReleasedSlot.Connect(eventHandler.OnKeyReleased, this, &Canvas::OnEventKeyReleased); + m_mouseButtonPressedSlot.Connect(eventHandler.OnMouseButtonPressed, this, &Canvas::OnEventMouseButtonPressed); + m_mouseButtonReleasedSlot.Connect(eventHandler.OnMouseButtonReleased, this, &Canvas::OnEventMouseButtonRelease); + m_mouseLeftSlot.Connect(eventHandler.OnMouseLeft, this, &Canvas::OnEventMouseLeft); + m_mouseMovedSlot.Connect(eventHandler.OnMouseMoved, this, &Canvas::OnEventMouseMoved); + m_mouseWheelMovedSlot.Connect(eventHandler.OnMouseWheelMoved, this, &Canvas::OnEventMouseWheelMoved); + m_textEnteredSlot.Connect(eventHandler.OnTextEntered, this, &Canvas::OnEventTextEntered); + m_textEditedSlot.Connect(eventHandler.OnTextEdited, this, &Canvas::OnEventTextEdited); + } + + inline Canvas::~Canvas() + { + // Destroy children explicitly because they signal us when getting destroyed, and that can't happen after our own destruction + DestroyChildren(); + + // Prevent our parent from trying to call us + m_canvasIndex = InvalidCanvasIndex; + } + + inline entt::registry& Canvas::GetRegistry() + { + return m_registry; + } + + inline const entt::registry& Canvas::GetRegistry() const + { + return m_registry; + } + + inline void Canvas::ClearKeyboardOwner(std::size_t canvasIndex) + { + if (m_keyboardOwner == canvasIndex) + SetKeyboardOwner(InvalidCanvasIndex); + } + + inline bool Canvas::IsKeyboardOwner(std::size_t canvasIndex) const + { + return m_keyboardOwner == canvasIndex; + } + + inline void Canvas::NotifyWidgetBoxUpdate(std::size_t index) + { + WidgetEntry& entry = m_widgetEntries[index]; + + Nz::Vector3f pos = entry.widget->GetPosition(Nz::CoordSys::Global); + Nz::Vector2f size = entry.widget->GetSize(); + + entry.box.Set(pos.x, pos.y, pos.z, size.x, size.y, 1.f); + } + + inline void Canvas::NotifyWidgetCursorUpdate(std::size_t index) + { + WidgetEntry& entry = m_widgetEntries[index]; + + entry.cursor = entry.widget->GetCursor(); + if (m_cursorController && m_hoveredWidget == index) + m_cursorController->UpdateCursor(Nz::Cursor::Get(entry.cursor)); + } + + inline void Canvas::SetKeyboardOwner(std::size_t canvasIndex) + { + if (m_keyboardOwner != canvasIndex) + { + if (m_keyboardOwner != InvalidCanvasIndex) + m_widgetEntries[m_keyboardOwner].widget->OnFocusLost(); + + m_keyboardOwner = canvasIndex; + + if (m_keyboardOwner != InvalidCanvasIndex) + m_widgetEntries[m_keyboardOwner].widget->OnFocusReceived(); + } + } +} + +#include diff --git a/include/Nazara/Widgets/Config.hpp b/include/Nazara/Widgets/Config.hpp new file mode 100644 index 000000000..5c9bfee99 --- /dev/null +++ b/include/Nazara/Widgets/Config.hpp @@ -0,0 +1,53 @@ +/* + Nazara Engine - Widgets module + + Copyright (C) 2020 Jérôme "Lynix" Leclercq (Lynix680@gmail.com) + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do + so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#pragma once + +#ifndef NAZARA_CONFIG_WIDGETS_HPP +#define NAZARA_CONFIG_WIDGETS_HPP + +/*! +* \defgroup widgets (NazaraWidgets) Widgets module +* Widgets module including classes to handle widgets... +*/ + +/// Each modification of a parameter needs a recompilation of the module + +// Use the MemoryManager to manage dynamic allocations (can detect memory leak but allocations/frees are slower) +#define NAZARA_WIDGETS_MANAGE_MEMORY 0 + +/// Checking the values and types of certain constants +#include + +#if !defined(NAZARA_STATIC) + #ifdef NAZARA_WIDGETS_BUILD + #define NAZARA_WIDGETS_API NAZARA_EXPORT + #else + #define NAZARA_WIDGETS_API NAZARA_IMPORT + #endif +#else + #define NAZARA_WIDGETS_API +#endif + +#endif // NAZARA_CONFIG_WIDGETS_HPP diff --git a/include/Nazara/Widgets/ConfigCheck.hpp b/include/Nazara/Widgets/ConfigCheck.hpp new file mode 100644 index 000000000..a925ba173 --- /dev/null +++ b/include/Nazara/Widgets/ConfigCheck.hpp @@ -0,0 +1,23 @@ +// Copyright (C) 2020 Jérôme Leclercq +// This file is part of the "Nazara Engine - Widgets module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_CONFIG_CHECK_WIDGETS_HPP +#define NAZARA_CONFIG_CHECK_WIDGETS_HPP + +/// This file is used to check the constant values defined in Config.hpp + +#include +#define NazaraCheckTypeAndVal(name, type, op, val, err) static_assert(std::is_ ##type ::value && name op val, #type err) + +// We force the value of MANAGE_MEMORY in debug +#if defined(NAZARA_DEBUG) && !NAZARA_WIDGETS_MANAGE_MEMORY + #undef NAZARA_WIDGETS_MANAGE_MEMORY + #define NAZARA_WIDGETS_MANAGE_MEMORY 0 +#endif + +#undef NazaraCheckTypeAndVal + +#endif // NAZARA_CONFIG_CHECK_WIDGETS_HPP diff --git a/include/Nazara/Widgets/Debug.hpp b/include/Nazara/Widgets/Debug.hpp new file mode 100644 index 000000000..93304d1e0 --- /dev/null +++ b/include/Nazara/Widgets/Debug.hpp @@ -0,0 +1,8 @@ +// Copyright (C) 2020 Jérôme Leclercq +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#if NAZARA_WIDGETS_MANAGE_MEMORY + #include +#endif diff --git a/include/Nazara/Widgets/DebugOff.hpp b/include/Nazara/Widgets/DebugOff.hpp new file mode 100644 index 000000000..9745d0d9a --- /dev/null +++ b/include/Nazara/Widgets/DebugOff.hpp @@ -0,0 +1,9 @@ +// Copyright (C) 2020 Jérôme Leclercq +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +// We assume that Debug.hpp has already been included, same thing for Config.hpp +#if NAZARA_WIDGETS_MANAGE_MEMORY + #undef delete + #undef new +#endif diff --git a/include/Nazara/Widgets/LabelWidget.hpp b/include/Nazara/Widgets/LabelWidget.hpp new file mode 100644 index 000000000..b6811fb09 --- /dev/null +++ b/include/Nazara/Widgets/LabelWidget.hpp @@ -0,0 +1,39 @@ +// 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 NAZARA_LABELWIDGET_HPP +#define NAZARA_LABELWIDGET_HPP + +#include +#include + +namespace Nz +{ + class AbstractTextDrawer; + class TextSprite; + + class NAZARA_WIDGETS_API LabelWidget : public BaseWidget + { + public: + LabelWidget(BaseWidget* parent); + LabelWidget(const LabelWidget&) = delete; + LabelWidget(LabelWidget&&) = default; + ~LabelWidget() = default; + + inline void UpdateText(const AbstractTextDrawer& drawer, float scale = 1.f); + + LabelWidget& operator=(const LabelWidget&) = delete; + LabelWidget& operator=(LabelWidget&&) = default; + + private: + entt::entity m_textEntity; + std::shared_ptr m_textSprite; + }; +} + +#include + +#endif diff --git a/include/Nazara/Widgets/LabelWidget.inl b/include/Nazara/Widgets/LabelWidget.inl new file mode 100644 index 000000000..541f80126 --- /dev/null +++ b/include/Nazara/Widgets/LabelWidget.inl @@ -0,0 +1,17 @@ +// 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 Nz +{ + inline void LabelWidget::UpdateText(const Nz::AbstractTextDrawer& drawer, float scale) + { + m_textSprite->Update(drawer, scale); + + Nz::Vector2f size = Nz::Vector2f(m_textSprite->GetAABB().GetLengths()); + SetMinimumSize(size); + SetPreferredSize(size); + } +} diff --git a/include/Nazara/Widgets/Widgets.hpp b/include/Nazara/Widgets/Widgets.hpp new file mode 100644 index 000000000..ff0076557 --- /dev/null +++ b/include/Nazara/Widgets/Widgets.hpp @@ -0,0 +1,39 @@ +// Copyright (C) 2020 Jérôme Leclercq +// This file is part of the "Nazara Engine - Widgets module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_WIDGETS_HPP +#define NAZARA_WIDGETS_HPP + +#include +#include +#include +#include +#include + +namespace Nz +{ + class NAZARA_WIDGETS_API Widgets : public ModuleBase + { + friend ModuleBase; + + public: + using Dependencies = TypeList; + + struct Config; + + Widgets(Config config); + ~Widgets() = default; + + struct Config {}; + + private: + static Widgets* s_instance; + }; +} + +#include + +#endif diff --git a/include/Nazara/Widgets/Widgets.inl b/include/Nazara/Widgets/Widgets.inl new file mode 100644 index 000000000..1cc5a07ae --- /dev/null +++ b/include/Nazara/Widgets/Widgets.inl @@ -0,0 +1,12 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ +} + +#include diff --git a/src/Nazara/Graphics/BakedFrameGraph.cpp b/src/Nazara/Graphics/BakedFrameGraph.cpp index 24c543748..5c3dfd792 100644 --- a/src/Nazara/Graphics/BakedFrameGraph.cpp +++ b/src/Nazara/Graphics/BakedFrameGraph.cpp @@ -65,7 +65,7 @@ namespace Nz builder.BeginRenderPass(*passData.framebuffer, *passData.renderPass, passData.renderRect); if (!passData.name.empty()) - builder.BeginDebugRegion(passData.name, Nz::Color::Green); + builder.BeginDebugRegion(passData.name, Color::Green); bool first = true; for (auto& subpass : passData.subpasses) diff --git a/src/Nazara/Graphics/Sprite.cpp b/src/Nazara/Graphics/Sprite.cpp index 5a3477d53..85d88cae2 100644 --- a/src/Nazara/Graphics/Sprite.cpp +++ b/src/Nazara/Graphics/Sprite.cpp @@ -12,12 +12,14 @@ namespace Nz { Sprite::Sprite(std::shared_ptr material) : InstancedRenderable(Nz::Boxf(-1000.f, -1000.f, -1000.f, 2000.f, 2000.f, 2000.f)), - m_material(std::move(material)) + m_material(std::move(material)), + m_color(Color::White), + m_textureCoords(0.f, 0.f, 1.f, 1.f), + m_size(64.f, 64.f) { - m_vertices[0] = VertexStruct_XYZ_Color_UV{ Vector3f(0.f, 0.f, 0.f), Nz::Color::White, Vector2f(0.f, 0.f) }; - m_vertices[1] = VertexStruct_XYZ_Color_UV{ Vector3f(1.f, 0.f, 0.f), Nz::Color::White, Vector2f(1.f, 0.f) }; - m_vertices[2] = VertexStruct_XYZ_Color_UV{ Vector3f(0.f, 1.f, 0.f), Nz::Color::White, Vector2f(0.f, 1.f) }; - m_vertices[3] = VertexStruct_XYZ_Color_UV{ Vector3f(1.f, 1.f, 0.f), Nz::Color::White, Vector2f(1.f, 1.f) }; + m_cornerColor.fill(Color::White); + + UpdateVertices(); } void Sprite::BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, std::vector>& elements) const @@ -43,6 +45,16 @@ namespace Nz elements.emplace_back(std::make_unique(0, renderPipeline, vertexDeclaration, whiteTexture, 1, m_vertices.data(), materialPass->GetShaderBinding(), worldInstance, materialPass->GetFlags())); } + inline const Color& Sprite::GetColor() const + { + return m_color; + } + + inline const Color& Sprite::GetCornerColor(RectCorner corner) const + { + return m_cornerColor[UnderlyingCast(corner)]; + } + const std::shared_ptr& Sprite::GetMaterial(std::size_t i) const { assert(i == 0); diff --git a/src/Nazara/Widgets/BaseWidget.cpp b/src/Nazara/Widgets/BaseWidget.cpp new file mode 100644 index 000000000..a79618625 --- /dev/null +++ b/src/Nazara/Widgets/BaseWidget.cpp @@ -0,0 +1,401 @@ +// 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 +#include +#include +#include + +namespace Nz +{ + /*! + * \ingroup NDK + * \class Ndk::BaseWidget + * \brief Abstract class serving as a base class for all widgets + */ + + /*! + * \brief Constructs a BaseWidget object using another widget as its parent + * + * \param parent Parent widget, must be valid and attached to a canvas + * + * Constructs a BaseWidget object using another widget as a base. + * This will also register the widget to the canvas owning the top-most widget. + */ + BaseWidget::BaseWidget(BaseWidget* parent) : + BaseWidget() + { + NazaraAssert(parent, "Invalid parent"); + NazaraAssert(parent->GetCanvas(), "Parent has no canvas"); + + m_canvas = parent->GetCanvas(); + m_widgetParent = parent; + m_registry = &m_canvas->GetRegistry(); + + RegisterToCanvas(); + } + + /*! + * \brief Frees the widget, unregistering it from its canvas + */ + BaseWidget::~BaseWidget() + { + UnregisterFromCanvas(); + } + + /*! + * \brief Clears keyboard focus if and only if this widget owns it. + */ + void BaseWidget::ClearFocus() + { + if (IsRegisteredToCanvas()) + m_canvas->ClearKeyboardOwner(m_canvasIndex); + } + + /*! + * \brief Destroy the widget, deleting it in the process. + * + * Calling this function immediately destroys the widget, freeing its memory. + */ + void BaseWidget::Destroy() + { + NazaraAssert(this != m_canvas, "Canvas cannot be destroyed by calling Destroy()"); + + m_widgetParent->DestroyChild(this); //< This does delete us + } + + /*! + * \brief Enable or disables the widget background. + */ + void BaseWidget::EnableBackground(bool enable) + { + if (m_backgroundEntity.has_value() == enable) + return; + + if (enable) + { + auto material = std::make_shared(); + material->AddPass("ForwardPass", std::make_shared(BasicMaterial::GetSettings())); + + m_backgroundSprite = std::make_shared(std::move(material)); + 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 + + entt::entity backgroundEntity = CreateEntity(); + m_registry->emplace(backgroundEntity).AttachRenderable(m_backgroundSprite); + m_registry->emplace(backgroundEntity).SetParent(this); + + m_backgroundEntity = backgroundEntity; + + BaseWidget::Layout(); // Only layout background + } + else + { + assert(m_backgroundEntity); + + DestroyEntity(*m_backgroundEntity); + m_backgroundSprite.reset(); + } + } + + /*! + * \brief Checks if this widget has keyboard focus + * \return true if widget has keyboard focus, false otherwise + */ + bool BaseWidget::HasFocus() const + { + if (!IsRegisteredToCanvas()) + return false; + + 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 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::SetCursor(Nz::SystemCursor systemCursor) + { + m_cursor = systemCursor; + + if (IsRegisteredToCanvas()) + m_canvas->NotifyWidgetCursorUpdate(m_canvasIndex); + } + + void BaseWidget::SetFocus() + { + if (IsRegisteredToCanvas()) + m_canvas->SetKeyboardOwner(m_canvasIndex); + } + + void BaseWidget::SetParent(BaseWidget* widget) + { + Canvas* oldCanvas = m_canvas; + Canvas* newCanvas = widget->GetCanvas(); + + // Changing a widget canvas is a problem because of the canvas entities + NazaraAssert(oldCanvas == newCanvas, "Transferring a widget between canvas is not yet supported"); + + Node::SetParent(widget); + m_widgetParent = widget; + + Layout(); + } + + void BaseWidget::SetRenderingRect(const Nz::Rectf& renderingRect) + { + m_renderingRect = renderingRect; + + UpdatePositionAndSize(); + for (const auto& widgetPtr : m_children) + widgetPtr->UpdatePositionAndSize(); + } + + void BaseWidget::Show(bool show) + { + if (m_visible != show) + { + m_visible = show; + + if (m_visible) + RegisterToCanvas(); + else + UnregisterFromCanvas(); + + for (WidgetEntity& entity : m_entities) + { + if (entity.isEnabled) + { + //entity.handle->Enable(show); //< This will override isEnabled, so reset it next line + entity.isEnabled = true; + } + } + + ShowChildren(show); + } + } + + entt::entity BaseWidget::CreateEntity() + { + entt::entity newEntity = m_registry->create(); + //newEntity->Enable(m_visible); + + m_entities.emplace_back(); + WidgetEntity& newWidgetEntity = m_entities.back(); + newWidgetEntity.handle = newEntity; + /*newWidgetEntity.onDisabledSlot.Connect(newEntity->OnEntityDisabled, [this](Entity* entity) + { + auto it = std::find_if(m_entities.begin(), m_entities.end(), [&](const WidgetEntity& widgetEntity) { return widgetEntity.handle == entity; }); + NazaraAssert(it != m_entities.end(), "Entity does not belong to this widget"); + + it->isEnabled = false; + }); + + newWidgetEntity.onEnabledSlot.Connect(newEntity->OnEntityEnabled, [this](Entity* entity) + { + auto it = std::find_if(m_entities.begin(), m_entities.end(), [&](const WidgetEntity& widgetEntity) { return widgetEntity.handle == entity; }); + NazaraAssert(it != m_entities.end(), "Entity does not belong to this widget"); + + if (!IsVisible()) + entity->Disable(); // Next line will override isEnabled status + + it->isEnabled = true; + });*/ + + return newEntity; + } + + void BaseWidget::DestroyEntity(entt::entity entity) + { + auto it = std::find_if(m_entities.begin(), m_entities.end(), [&](const WidgetEntity& widgetEntity) { return widgetEntity.handle == entity; }); + NazaraAssert(it != m_entities.end(), "Entity does not belong to this widget"); + + m_entities.erase(it); + m_registry->destroy(entity); + } + + void BaseWidget::Layout() + { + if (m_backgroundSprite) + m_backgroundSprite->SetSize({ m_size.x, m_size.y }); + + UpdatePositionAndSize(); + } + + void BaseWidget::InvalidateNode() + { + Node::InvalidateNode(); + + UpdatePositionAndSize(); + } + + Nz::Rectf BaseWidget::GetScissorRect() const + { + Nz::Vector2f widgetPos = Nz::Vector2f(GetPosition(Nz::CoordSys::Global)); + Nz::Vector2f widgetSize = GetSize(); + + Nz::Rectf widgetRect(widgetPos.x, widgetPos.y, widgetSize.x, widgetSize.y); + Nz::Rectf widgetRenderingRect(widgetPos.x + m_renderingRect.x, widgetPos.y + m_renderingRect.y, m_renderingRect.width, m_renderingRect.height); + + widgetRect.Intersect(widgetRenderingRect, &widgetRect); + + return widgetRect; + } + + bool BaseWidget::IsFocusable() const + { + return false; + } + + void BaseWidget::OnFocusLost() + { + } + + void BaseWidget::OnFocusReceived() + { + } + + bool BaseWidget::OnKeyPressed(const Nz::WindowEvent::KeyEvent& key) + { + return false; + } + + void BaseWidget::OnKeyReleased(const Nz::WindowEvent::KeyEvent& /*key*/) + { + } + + void BaseWidget::OnMouseEnter() + { + } + + void BaseWidget::OnMouseMoved(int /*x*/, int /*y*/, int /*deltaX*/, int /*deltaY*/) + { + } + + void BaseWidget::OnMouseButtonPress(int /*x*/, int /*y*/, Nz::Mouse::Button /*button*/) + { + } + + void BaseWidget::OnMouseButtonRelease(int /*x*/, int /*y*/, Nz::Mouse::Button /*button*/) + { + } + + void BaseWidget::OnMouseWheelMoved(int /*x*/, int /*y*/, float /*delta*/) + { + } + + void BaseWidget::OnMouseExit() + { + } + + void BaseWidget::OnParentResized(const Nz::Vector2f& /*newSize*/) + { + } + + void BaseWidget::OnTextEntered(char32_t /*character*/, bool /*repeated*/) + { + } + + void BaseWidget::OnTextEdited(const std::array& /*characters*/, int /*length*/) + { + } + + inline entt::registry& BaseWidget::GetRegistry() + { + assert(m_registry); + return *m_registry; + } + + inline const entt::registry& BaseWidget::GetRegistry() const + { + assert(m_registry); + return *m_registry; + } + + void BaseWidget::ShowChildren(bool show) + { + for (const auto& widgetPtr : m_children) + 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 + { + 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(); + } + + void BaseWidget::RegisterToCanvas() + { + NazaraAssert(!IsRegisteredToCanvas(), "Widget is already registered to canvas"); + + m_canvasIndex = m_canvas->RegisterWidget(this); + } + + void BaseWidget::UnregisterFromCanvas() + { + if (IsRegisteredToCanvas()) + { + m_canvas->UnregisterWidget(m_canvasIndex); + m_canvasIndex = InvalidCanvasIndex; + } + } + + void BaseWidget::UpdatePositionAndSize() + { + if (IsRegisteredToCanvas()) + m_canvas->NotifyWidgetBoxUpdate(m_canvasIndex); + + Nz::Rectf scissorRect = GetScissorRect(); + + if (m_widgetParent) + { + Nz::Rectf parentScissorRect = m_widgetParent->GetScissorRect(); + + if (!scissorRect.Intersect(parentScissorRect, &scissorRect)) + scissorRect = parentScissorRect; + } + + /*Nz::Recti fullBounds(scissorRect); + for (WidgetEntity& widgetEntity : m_entities) + { + const Ndk::EntityHandle& entity = widgetEntity.handle; + if (entity->HasComponent()) + entity->GetComponent().SetScissorRect(fullBounds); + }*/ + } +} diff --git a/src/Nazara/Widgets/ButtonWidget.cpp b/src/Nazara/Widgets/ButtonWidget.cpp new file mode 100644 index 000000000..2efa184b5 --- /dev/null +++ b/src/Nazara/Widgets/ButtonWidget.cpp @@ -0,0 +1,97 @@ +// 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 + +#if 0 + +#include +#include +#include + +namespace Nz +{ + ButtonWidget::ButtonWidget(BaseWidget* parent) : + BaseWidget(parent), + m_color { 74, 74, 74 }, + m_cornerColor { 180, 180, 180 }, + m_hoverColor { 128, 128, 128 }, + m_hoverCornerColor { 180, 180, 180 }, + m_pressColor { 180, 180, 180 }, + m_pressCornerColor { 74, 74, 74 } + { + entt::registry& registry = GetRegistry(); + + m_gradientSprite = std::make_shared(); + m_gradientSprite->SetColor(m_color); + m_gradientSprite->SetCornerColor(Nz::RectCorner::LeftBottom, m_cornerColor); + m_gradientSprite->SetCornerColor(Nz::RectCorner::RightBottom, m_cornerColor); + m_gradientSprite->SetMaterial(Nz::Material::New("Basic2D")); + + m_gradientEntity = CreateEntity(); + registry.emplace(m_gradientEntity).SetParent(this); + registry.emplace(m_gradientEntity).AttachRenderable(m_gradientSprite); + + m_textSprite = std::make_shared(); + + m_textEntity = CreateEntity(); + registry.emplace(m_textEntity).SetParent(this); + registry.emplace(m_textEntity).AttachRenderable(m_textSprite); + + Layout(); + } + + void ButtonWidget::Layout() + { + BaseWidget::Layout(); + + Nz::Vector2f size = GetSize(); + m_gradientSprite->SetSize(size); + + entt::registry& registry = GetRegistry(); + + Nz::Boxf textBox = m_textSprite->GetAABB(); + registry.get(m_textEntity).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) + { + if (button == Nz::Mouse::Left) + { + m_gradientSprite->SetColor(m_pressColor); + m_gradientSprite->SetCornerColor(Nz::RectCorner::LeftBottom, m_pressCornerColor); + m_gradientSprite->SetCornerColor(Nz::RectCorner::RightBottom, m_pressCornerColor); + m_gradientSprite->SetTexture(m_pressTexture, false); + } + } + + void ButtonWidget::OnMouseButtonRelease(int /*x*/, int /*y*/, Nz::Mouse::Button button) + { + if (button == Nz::Mouse::Left) + { + m_gradientSprite->SetColor(m_hoverColor); + m_gradientSprite->SetCornerColor(Nz::RectCorner::LeftBottom, m_hoverCornerColor); + m_gradientSprite->SetCornerColor(Nz::RectCorner::RightBottom, m_hoverCornerColor); + m_gradientSprite->SetTexture(m_hoverTexture, false); + + OnButtonTrigger(this); + } + } + + void ButtonWidget::OnMouseEnter() + { + m_gradientSprite->SetColor(m_hoverColor); + m_gradientSprite->SetCornerColor(Nz::RectCorner::LeftBottom, m_hoverCornerColor); + m_gradientSprite->SetCornerColor(Nz::RectCorner::RightBottom, m_hoverCornerColor); + m_gradientSprite->SetTexture(m_hoverTexture, false); + } + + void ButtonWidget::OnMouseExit() + { + m_gradientSprite->SetColor(m_color); + m_gradientSprite->SetCornerColor(Nz::RectCorner::LeftBottom, m_cornerColor); + m_gradientSprite->SetCornerColor(Nz::RectCorner::RightBottom, m_cornerColor); + m_gradientSprite->SetTexture(m_texture, false); + } +} + +#endif diff --git a/src/Nazara/Widgets/Canvas.cpp b/src/Nazara/Widgets/Canvas.cpp new file mode 100644 index 000000000..55028b09c --- /dev/null +++ b/src/Nazara/Widgets/Canvas.cpp @@ -0,0 +1,230 @@ +// Copyright (C) 2020 Jérôme Leclercq +// This file is part of the "Nazara Engine - Widgets module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ + std::size_t Canvas::RegisterWidget(BaseWidget* widget) + { + WidgetEntry box; + box.cursor = widget->GetCursor(); + box.widget = widget; + + std::size_t index = m_widgetEntries.size(); + m_widgetEntries.emplace_back(box); + + NotifyWidgetBoxUpdate(index); + return index; + } + + void Canvas::UnregisterWidget(std::size_t index) + { + WidgetEntry& entry = m_widgetEntries[index]; + + if (m_hoveredWidget == index) + m_hoveredWidget = InvalidCanvasIndex; + + if (m_keyboardOwner == index) + m_keyboardOwner = InvalidCanvasIndex; + + if (m_widgetEntries.size() > 1U) + { + WidgetEntry& lastEntry = m_widgetEntries.back(); + std::size_t lastEntryIndex = m_widgetEntries.size() - 1; + + entry = std::move(lastEntry); + entry.widget->UpdateCanvasIndex(index); + + if (m_hoveredWidget == lastEntryIndex) + m_hoveredWidget = index; + + if (m_keyboardOwner == lastEntryIndex) + m_keyboardOwner = index; + } + + m_widgetEntries.pop_back(); + } + + void Canvas::OnEventMouseButtonPressed(const Nz::EventHandler* /*eventHandler*/, const Nz::WindowEvent::MouseButtonEvent& event) + { + if (m_hoveredWidget != InvalidCanvasIndex) + { + WidgetEntry& hoveredWidget = m_widgetEntries[m_hoveredWidget]; + + int x = static_cast(std::round(event.x - hoveredWidget.box.x)); + int y = static_cast(std::round(event.y - hoveredWidget.box.y)); + + hoveredWidget.widget->OnMouseButtonPress(x, y, event.button); + } + } + + void Canvas::OnEventMouseButtonRelease(const Nz::EventHandler* /*eventHandler*/, const Nz::WindowEvent::MouseButtonEvent& event) + { + if (m_hoveredWidget != InvalidCanvasIndex) + { + WidgetEntry& hoveredWidget = m_widgetEntries[m_hoveredWidget]; + + int x = static_cast(std::round(event.x - hoveredWidget.box.x)); + int y = static_cast(std::round(event.y - hoveredWidget.box.y)); + + hoveredWidget.widget->OnMouseButtonRelease(x, y, event.button); + } + } + + void Canvas::OnEventMouseMoved(const Nz::EventHandler* /*eventHandler*/, const Nz::WindowEvent::MouseMoveEvent& event) + { + std::size_t bestEntry = InvalidCanvasIndex; + float bestEntryArea = std::numeric_limits::infinity(); + + Nz::Vector3f mousePos(float(event.x), float(event.y), 0.f); + for (std::size_t i = 0; i < m_widgetEntries.size(); ++i) + { + const Nz::Boxf& box = m_widgetEntries[i].box; + + if (box.Contains(mousePos)) + { + float area = box.width * box.height; + if (area < bestEntryArea) + { + bestEntry = i; + bestEntryArea = area; + } + } + } + + if (bestEntry != InvalidCanvasIndex) + { + if (m_hoveredWidget != bestEntry) + { + if (m_hoveredWidget != InvalidCanvasIndex) + { + WidgetEntry& previouslyHovered = m_widgetEntries[m_hoveredWidget]; + previouslyHovered.widget->OnMouseExit(); + } + + m_hoveredWidget = bestEntry; + m_widgetEntries[m_hoveredWidget].widget->OnMouseEnter(); + + if (m_cursorController) + m_cursorController->UpdateCursor(Nz::Cursor::Get(m_widgetEntries[m_hoveredWidget].cursor)); + } + + WidgetEntry& hoveredWidget = m_widgetEntries[m_hoveredWidget]; + + int x = static_cast(std::round(event.x - hoveredWidget.box.x)); + int y = static_cast(std::round(event.y - hoveredWidget.box.y)); + hoveredWidget.widget->OnMouseMoved(x, y, event.deltaX, event.deltaY); + } + else if (m_hoveredWidget != InvalidCanvasIndex) + { + m_widgetEntries[m_hoveredWidget].widget->OnMouseExit(); + m_hoveredWidget = InvalidCanvasIndex; + + if (m_cursorController) + m_cursorController->UpdateCursor(Nz::Cursor::Get(Nz::SystemCursor::Default)); + } + } + + void Canvas::OnEventMouseWheelMoved(const Nz::EventHandler* /*eventHandler*/, const Nz::WindowEvent::MouseWheelEvent& event) + { + if (m_hoveredWidget != InvalidCanvasIndex) + { + WidgetEntry& hoveredWidget = m_widgetEntries[m_hoveredWidget]; + + int x = static_cast(std::round(event.x - hoveredWidget.box.x)); + int y = static_cast(std::round(event.y - hoveredWidget.box.y)); + + hoveredWidget.widget->OnMouseWheelMoved(x, y, event.delta); + } + } + + void Canvas::OnEventMouseLeft(const Nz::EventHandler* /*eventHandler*/) + { + if (m_hoveredWidget != InvalidCanvasIndex) + { + m_widgetEntries[m_hoveredWidget].widget->OnMouseExit(); + m_hoveredWidget = InvalidCanvasIndex; + } + } + + void Canvas::OnEventKeyPressed(const Nz::EventHandler* eventHandler, const Nz::WindowEvent::KeyEvent& event) + { + if (m_keyboardOwner != InvalidCanvasIndex) + { + if (m_widgetEntries[m_keyboardOwner].widget->OnKeyPressed(event)) + return; + + if (event.virtualKey == Nz::Keyboard::VKey::Tab) + { + if (!event.shift) + { + // Forward + for (std::size_t i = m_keyboardOwner + 1; i < m_widgetEntries.size(); ++i) + { + if (m_widgetEntries[i].widget->IsFocusable()) + { + SetKeyboardOwner(i); + return; + } + } + + for (std::size_t i = 0; i < m_keyboardOwner; ++i) + { + if (m_widgetEntries[i].widget->IsFocusable()) + { + SetKeyboardOwner(i); + return; + } + } + } + else + { + // Backward + for (decltype(m_widgetEntries)::reverse_iterator rit{ m_widgetEntries.begin() + m_keyboardOwner }; rit != m_widgetEntries.rend(); ++rit) + { + if (rit->widget->IsFocusable()) + { + SetKeyboardOwner(std::distance(m_widgetEntries.begin(), rit.base()) - 1); + return; + } + } + + decltype(m_widgetEntries)::reverse_iterator rend { m_widgetEntries.begin() + m_keyboardOwner }; + for (auto rit = m_widgetEntries.rbegin(); rit != rend; ++rit) + { + if (rit->widget->IsFocusable()) + { + SetKeyboardOwner(std::distance(m_widgetEntries.begin(), rit.base()) - 1); + return; + } + } + } + } + } + + OnUnhandledKeyPressed(eventHandler, event); + } + + void Canvas::OnEventKeyReleased(const Nz::EventHandler* eventHandler, const Nz::WindowEvent::KeyEvent& event) + { + if (m_keyboardOwner != InvalidCanvasIndex) + m_widgetEntries[m_keyboardOwner].widget->OnKeyReleased(event); + + OnUnhandledKeyReleased(eventHandler, event); + } + + void Canvas::OnEventTextEntered(const Nz::EventHandler* /*eventHandler*/, const Nz::WindowEvent::TextEvent& event) + { + if (m_keyboardOwner != InvalidCanvasIndex) + m_widgetEntries[m_keyboardOwner].widget->OnTextEntered(event.character, event.repeated); + } + + void Canvas::OnEventTextEdited(const Nz::EventHandler* /*eventHandler*/, const Nz::WindowEvent::EditEvent& event) + { + if (m_keyboardOwner != InvalidCanvasIndex) + m_widgetEntries[m_keyboardOwner].widget->OnTextEdited(event.text, event.length); + } +} diff --git a/src/Nazara/Widgets/Debug/NewOverload.cpp b/src/Nazara/Widgets/Debug/NewOverload.cpp new file mode 100644 index 000000000..b89d66791 --- /dev/null +++ b/src/Nazara/Widgets/Debug/NewOverload.cpp @@ -0,0 +1,31 @@ +// Copyright (C) 2020 Jérôme Leclercq +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#if NAZARA_WIDGETS_MANAGE_MEMORY + +#include +#include // Nécessaire ? + +void* operator new(std::size_t size) +{ + return Nz::MemoryManager::Allocate(size, false); +} + +void* operator new[](std::size_t size) +{ + return Nz::MemoryManager::Allocate(size, true); +} + +void operator delete(void* pointer) noexcept +{ + Nz::MemoryManager::Free(pointer, false); +} + +void operator delete[](void* pointer) noexcept +{ + Nz::MemoryManager::Free(pointer, true); +} + +#endif diff --git a/src/Nazara/Widgets/LabelWidget.cpp b/src/Nazara/Widgets/LabelWidget.cpp new file mode 100644 index 000000000..f3792ae08 --- /dev/null +++ b/src/Nazara/Widgets/LabelWidget.cpp @@ -0,0 +1,36 @@ +// 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 +#include +#include + +namespace Nz +{ + LabelWidget::LabelWidget(BaseWidget* parent) : + BaseWidget(parent) + { + auto materialPass = std::make_shared(BasicMaterial::GetSettings()); + materialPass->EnableFlag(Nz::MaterialPassFlag::Transparent); + materialPass->EnableDepthBuffer(true); + materialPass->EnableDepthWrite(false); + materialPass->EnableBlending(true); + materialPass->SetBlendEquation(Nz::BlendEquation::Add, Nz::BlendEquation::Add); + materialPass->SetBlendFunc(Nz::BlendFunc::SrcAlpha, Nz::BlendFunc::InvSrcAlpha, Nz::BlendFunc::One, Nz::BlendFunc::Zero); + + auto material = std::make_shared(); + material->AddPass("ForwardPass", std::move(materialPass)); + + m_textSprite = std::make_shared(std::move(material)); + + m_textEntity = CreateEntity(); + GetRegistry().emplace(m_textEntity).AttachRenderable(m_textSprite); + GetRegistry().emplace(m_textEntity).SetParent(this); + + Layout(); + } +} diff --git a/src/Nazara/Widgets/Widgets.cpp b/src/Nazara/Widgets/Widgets.cpp new file mode 100644 index 000000000..72147d66c --- /dev/null +++ b/src/Nazara/Widgets/Widgets.cpp @@ -0,0 +1,22 @@ +// Copyright (C) 2020 Jérôme Leclercq +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ + /*! + * \ingroup widgets + * \class Nz::Widgets + * \brief Widgets class that represents the module initializer of Widgets + */ + Widgets::Widgets(Config config) : + ModuleBase("Widgets", this) + { + ECS::RegisterComponents(); + } + + Widgets* Widgets::s_instance = nullptr; +} diff --git a/xmake.lua b/xmake.lua index 90fc86036..34d9ecf07 100644 --- a/xmake.lua +++ b/xmake.lua @@ -96,6 +96,10 @@ local modules = { add_defines("VK_USE_PLATFORM_MACOS_MVK") end end + }, + Widgets = { + Deps = {"NazaraGraphics"}, + Packages = {"entt", "kiwisolver"} } } @@ -103,7 +107,7 @@ set_xmakever("2.5.6") add_repositories("local-repo xmake-repo") -add_requires("chipmunk2d", "dr_wav", "entt", "libflac", "libsdl", "minimp3", "stb") +add_requires("chipmunk2d", "dr_wav", "entt", "kiwisolver", "libflac", "libsdl", "minimp3", "stb") add_requires("freetype", { configs = { bzip2 = true, png = true, woff2 = true, zlib = true, debug = is_mode("debug") } }) add_requires("libvorbis", { configs = { with_vorbisenc = false } }) add_requires("openal-soft", { configs = { shared = true }})