Add widgets (WIP)

This commit is contained in:
Jérôme Leclercq 2021-10-04 09:25:45 +02:00
parent 68708c54f7
commit a66f6faed1
31 changed files with 1955 additions and 24 deletions

View File

@ -15,9 +15,11 @@
#include <Nazara/Shader/SpirvPrinter.hpp>
#include <Nazara/Utility.hpp>
#include <Nazara/Utility/Components.hpp>
#include <Nazara/Widgets.hpp>
#include <entt/entt.hpp>
#include <array>
#include <iostream>
#include <limits>
NAZARA_REQUEST_DEDICATED_GPU()
@ -34,7 +36,9 @@ int main()
else
rendererConfig.preferredAPI = Nz::RenderAPI::OpenGL;
Nz::Modules<Nz::Graphics, Nz::Physics3D, Nz::ECS> nazara(rendererConfig);
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
Nz::Modules<Nz::Graphics, Nz::Physics3D, Nz::ECS, Nz::Widgets> nazara(rendererConfig);
Nz::RenderWindow window;
@ -125,20 +129,10 @@ int main()
spriteMaterial->AddPass("ForwardPass", spriteMaterialPass);
std::shared_ptr<Nz::TextSprite> sprite = std::make_shared<Nz::TextSprite>(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 dexquis rôtis de bœuf au kir, à laÿ 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<Nz::LabelWidget>();
//labelWidget->EnableBackground(true);
labelWidget->UpdateText(Nz::SimpleTextDrawer::Draw("Bonjour Paris !", 72), 0.1f);
entt::entity viewer2D = registry2D.create();
registry2D.emplace<Nz::NodeComponent>(viewer2D);
registry2D.emplace<Nz::CameraComponent>(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<Nz::GraphicsComponent>(textEntity);
entityGfx.AttachRenderable(sprite);
auto& entityNode = registry.emplace<Nz::NodeComponent>(textEntity);
entityNode.SetPosition(0.f, 5.f, 0.f);
}
entt::entity playerEntity = registry.create();
entt::entity headingEntity = registry.create();
{
auto& entityGfx = registry.emplace<Nz::GraphicsComponent>(playerEntity);
entityGfx.AttachRenderable(model);
entityGfx.AttachRenderable(sprite);
auto& entityNode = registry.emplace<Nz::NodeComponent>(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<Nz::GraphicsComponent>(entity);
entityGfx.AttachRenderable(model);
entityGfx.AttachRenderable(sprite);
//entityGfx.AttachRenderable(sprite);
auto& entityNode = registry.emplace<Nz::NodeComponent>(entity);
entityNode.SetPosition(Nz::Vector3f(x * 2.f, y * 1.5f, z * 2.f));

View File

@ -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")

View File

@ -26,6 +26,7 @@ namespace Nz
friend class Physics2D;
friend class Physics3D;
friend class Utility;
friend class Widgets;
public:
using Dependencies = TypeList<Core>;

View File

@ -45,6 +45,7 @@
#include <Nazara/Graphics/FramePipeline.hpp>
#include <Nazara/Graphics/GraphicalMesh.hpp>
#include <Nazara/Graphics/Graphics.hpp>
#include <Nazara/Graphics/GuillotineTextureAtlas.hpp>
#include <Nazara/Graphics/InstancedRenderable.hpp>
#include <Nazara/Graphics/Material.hpp>
#include <Nazara/Graphics/MaterialPass.hpp>
@ -57,8 +58,12 @@
#include <Nazara/Graphics/RenderElement.hpp>
#include <Nazara/Graphics/RenderQueue.hpp>
#include <Nazara/Graphics/RenderQueueRegistry.hpp>
#include <Nazara/Graphics/RenderSpriteChain.hpp>
#include <Nazara/Graphics/RenderSubmesh.hpp>
#include <Nazara/Graphics/Sprite.hpp>
#include <Nazara/Graphics/SpriteChainRenderer.hpp>
#include <Nazara/Graphics/SubmeshRenderer.hpp>
#include <Nazara/Graphics/TextSprite.hpp>
#include <Nazara/Graphics/TextureSamplerCache.hpp>
#include <Nazara/Graphics/UberShader.hpp>
#include <Nazara/Graphics/ViewerInstance.hpp>

View File

@ -27,17 +27,28 @@ namespace Nz
void BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, std::vector<std::unique_ptr<RenderElement>>& elements) const override;
inline const Color& GetColor() const;
inline const Color& GetCornerColor(RectCorner corner) const;
const std::shared_ptr<Material>& 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> material);
inline void SetSize(const Vector2f& size);
Sprite& operator=(const Sprite&) = delete;
Sprite& operator=(Sprite&&) noexcept = default;
private:
inline void UpdateVertices();
std::array<Color, RectCornerCount> m_cornerColor;
std::array<VertexStruct_XYZ_Color_UV, 4> m_vertices;
std::shared_ptr<Material> m_material;
Color m_color;
Rectf m_textureCoords;
Vector2f m_size;
};
}

View File

@ -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> 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 <Nazara/Graphics/DebugOff.hpp>

View File

@ -74,6 +74,8 @@ namespace Nz
Max = RightTop
};
constexpr std::size_t RectCornerCount = UnderlyingCast(RectCorner::Max) + 1;
}
#endif // NAZARA_ENUMS_MATH_HPP

View File

@ -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 <Nazara/Widgets/BaseWidget.hpp>
#include <Nazara/Widgets/Canvas.hpp>
#include <Nazara/Widgets/Config.hpp>
#include <Nazara/Widgets/LabelWidget.hpp>
#include <Nazara/Widgets/Widgets.hpp>
#endif // NAZARA_GLOBAL_WIDGETS_HPP

View File

@ -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 <Nazara/Graphics/Sprite.hpp>
#include <Nazara/Platform/Event.hpp>
#include <Nazara/Platform/Mouse.hpp>
#include <Nazara/Utility/Node.hpp>
#include <Nazara/Widgets/Config.hpp>
#include <entt/entity/entity.hpp>
#include <entt/entity/registry.hpp>
#include <limits>
#include <optional>
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<typename T, typename... Args> T* Add(Args&&... args);
inline void AddChild(std::unique_ptr<BaseWidget>&& widget);
inline void Center();
inline void CenterHorizontal();
inline void CenterVertical();
void ClearFocus();
inline void ClearRenderingRect();
void Destroy();
void EnableBackground(bool enable);
template<typename F> void ForEachWidgetChild(F iterator);
template<typename F> 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<char, 32>& 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<std::size_t>::max();
std::optional<entt::entity> m_backgroundEntity;
std::size_t m_canvasIndex;
std::shared_ptr<Nz::Sprite> m_backgroundSprite;
std::vector<WidgetEntity> m_entities;
std::vector<std::unique_ptr<BaseWidget>> 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 <Nazara/Widgets/BaseWidget.inl>
#endif // NAZARA_BASEWIDGET_HPP

View File

@ -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 <Nazara/Widgets/BaseWidget.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Math/Algorithm.hpp>
#include <limits>
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<float>::infinity(), -std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity()),
m_cursor(Nz::SystemCursor::Default),
m_maximumSize(std::numeric_limits<float>::infinity()),
m_minimumSize(0.f),
m_preferredSize(-1),
m_size(50.f, 50.f),
m_widgetParent(nullptr),
m_visible(true)
{
}
template<typename T, typename... Args>
inline T* BaseWidget::Add(Args&&... args)
{
std::unique_ptr<T> widget = std::make_unique<T>(this, std::forward<Args>(args)...);
T* widgetPtr = widget.get();
AddChild(std::move(widget));
return widgetPtr;
}
inline void BaseWidget::AddChild(std::unique_ptr<BaseWidget>&& 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<float>::infinity(), -std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity()));
}
template<typename F>
inline void BaseWidget::ForEachWidgetChild(F iterator)
{
for (const auto& child : m_children)
iterator(child.get());
}
template<typename F>
inline void BaseWidget::ForEachWidgetChild(F iterator) const
{
for (const auto& child : m_children)
iterator(static_cast<const BaseWidget*>(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;
}
}

View File

@ -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 <Nazara/Core/Color.hpp>
#include <Nazara/Graphics/Sprite.hpp>
#include <Nazara/Graphics/TextSprite.hpp>
#include <Nazara/Widgets/BaseWidget.hpp>
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<Texture>& GetTexture() const;
inline const std::shared_ptr<Texture>& GetHoverTexture() const;
inline const std::shared_ptr<Texture>& 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<Sprite> m_gradientSprite;
std::shared_ptr<TextSprite> 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 <Nazara/Widgets/ButtonWidget.inl>
#endif // NDK_WIDGETS_BUTTONWIDGET_HPP

View File

@ -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 <Nazara/Widgets/ButtonWidget.hpp>
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();
}
}

View File

@ -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 <Nazara/Platform/CursorController.hpp>
#include <Nazara/Platform/EventHandler.hpp>
#include <Nazara/Widgets/BaseWidget.hpp>
#include <entt/entity/registry.hpp>
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<WidgetEntry> m_widgetEntries;
entt::registry& m_registry;
Nz::CursorControllerHandle m_cursorController;
};
}
#include <Nazara/Widgets/Canvas.inl>
#endif // NDK_CANVAS_HPP

View File

@ -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 <Nazara/Widgets/Canvas.hpp>
#include <Nazara/Platform/Cursor.hpp>
#include <Nazara/Widgets/Debug.hpp>
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 <Nazara/Widgets/DebugOff.hpp>

View File

@ -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 <Nazara/Widgets/ConfigCheck.hpp>
#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

View File

@ -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 <type_traits>
#define NazaraCheckTypeAndVal(name, type, op, val, err) static_assert(std::is_ ##type <decltype(name)>::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

View File

@ -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 <Nazara/Widgets/Config.hpp>
#if NAZARA_WIDGETS_MANAGE_MEMORY
#include <Nazara/Core/Debug/NewRedefinition.hpp>
#endif

View File

@ -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

View File

@ -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 <Nazara/Graphics/TextSprite.hpp>
#include <Nazara/Widgets/BaseWidget.hpp>
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<TextSprite> m_textSprite;
};
}
#include <Nazara/Widgets/LabelWidget.inl>
#endif

View File

@ -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 <Nazara/Widgets/LabelWidget.hpp>
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);
}
}

View File

@ -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 <Nazara/Prerequisites.hpp>
#include <Nazara/Core/ModuleBase.hpp>
#include <Nazara/Core/ECS.hpp>
#include <Nazara/Graphics/Graphics.hpp>
#include <Nazara/Widgets/Config.hpp>
namespace Nz
{
class NAZARA_WIDGETS_API Widgets : public ModuleBase<Widgets>
{
friend ModuleBase;
public:
using Dependencies = TypeList<ECS, Graphics>;
struct Config;
Widgets(Config config);
~Widgets() = default;
struct Config {};
private:
static Widgets* s_instance;
};
}
#include <Nazara/Widgets/Widgets.inl>
#endif

View File

@ -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 <Nazara/Widgets/Widgets.hpp>
#include <Nazara/Widgets/Debug.hpp>
namespace Nz
{
}
#include <Nazara/Widgets/DebugOff.hpp>

View File

@ -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)

View File

@ -12,12 +12,14 @@ namespace Nz
{
Sprite::Sprite(std::shared_ptr<Material> 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<std::unique_ptr<RenderElement>>& elements) const
@ -43,6 +45,16 @@ namespace Nz
elements.emplace_back(std::make_unique<RenderSpriteChain>(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<Material>& Sprite::GetMaterial(std::size_t i) const
{
assert(i == 0);

View File

@ -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 <Nazara/Widgets/BaseWidget.hpp>
#include <Nazara/Widgets/Canvas.hpp>
#include <Nazara/Graphics/BasicMaterial.hpp>
#include <Nazara/Graphics/Material.hpp>
#include <Nazara/Graphics/Components/GraphicsComponent.hpp>
#include <Nazara/Utility/Components/NodeComponent.hpp>
#include <algorithm>
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>();
material->AddPass("ForwardPass", std::make_shared<MaterialPass>(BasicMaterial::GetSettings()));
m_backgroundSprite = std::make_shared<Nz::Sprite>(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<GraphicsComponent>(backgroundEntity).AttachRenderable(m_backgroundSprite);
m_registry->emplace<NodeComponent>(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<char, 32>& /*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<BaseWidget>& 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<GraphicsComponent>())
entity->GetComponent<GraphicsComponent>().SetScissorRect(fullBounds);
}*/
}
}

View File

@ -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 <Nazara/Widgets/ButtonWidget.hpp>
#include <Nazara/Graphics/Components/GraphicsComponent.hpp>
#include <Nazara/Utility/Components/NodeComponent.hpp>
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<Sprite>();
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<NodeComponent>(m_gradientEntity).SetParent(this);
registry.emplace<GraphicsComponent>(m_gradientEntity).AttachRenderable(m_gradientSprite);
m_textSprite = std::make_shared<TextSprite>();
m_textEntity = CreateEntity();
registry.emplace<NodeComponent>(m_textEntity).SetParent(this);
registry.emplace<GraphicsComponent>(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<NodeComponent>(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

View File

@ -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 <Nazara/Widgets/Canvas.hpp>
#include <limits>
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<int>(std::round(event.x - hoveredWidget.box.x));
int y = static_cast<int>(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<int>(std::round(event.x - hoveredWidget.box.x));
int y = static_cast<int>(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<float>::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<int>(std::round(event.x - hoveredWidget.box.x));
int y = static_cast<int>(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<int>(std::round(event.x - hoveredWidget.box.x));
int y = static_cast<int>(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);
}
}

View File

@ -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 <Nazara/Widgets/Config.hpp>
#if NAZARA_WIDGETS_MANAGE_MEMORY
#include <Nazara/Core/MemoryManager.hpp>
#include <new> // 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

View File

@ -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 <Nazara/Widgets/LabelWidget.hpp>
#include <Nazara/Graphics/BasicMaterial.hpp>
#include <Nazara/Graphics/Material.hpp>
#include <Nazara/Graphics/MaterialPass.hpp>
#include <Nazara/Graphics/Components/GraphicsComponent.hpp>
#include <Nazara/Utility/Components/NodeComponent.hpp>
namespace Nz
{
LabelWidget::LabelWidget(BaseWidget* parent) :
BaseWidget(parent)
{
auto materialPass = std::make_shared<MaterialPass>(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>();
material->AddPass("ForwardPass", std::move(materialPass));
m_textSprite = std::make_shared<TextSprite>(std::move(material));
m_textEntity = CreateEntity();
GetRegistry().emplace<GraphicsComponent>(m_textEntity).AttachRenderable(m_textSprite);
GetRegistry().emplace<NodeComponent>(m_textEntity).SetParent(this);
Layout();
}
}

View File

@ -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 <Nazara/Widgets/Widgets.hpp>
#include <Nazara/Widgets/Debug.hpp>
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;
}

View File

@ -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 }})