Remove Graphics module and fix compilation

This commit is contained in:
Jérôme Leclercq
2020-08-27 19:43:31 +02:00
parent bc92d030ed
commit 2a875c8bdc
283 changed files with 16 additions and 32474 deletions

View File

@@ -7,12 +7,8 @@
#include <regex>
#ifndef NDK_SERVER
#include <Nazara/Graphics/ForwardRenderTechnique.hpp>
#include <Nazara/Utility/SimpleTextDrawer.hpp>
#include <NazaraSDK/Components/CameraComponent.hpp>
#include <NazaraSDK/Components/GraphicsComponent.hpp>
#include <NazaraSDK/Components/NodeComponent.hpp>
#include <NazaraSDK/Systems/RenderSystem.hpp>
#endif
namespace Ndk
@@ -63,14 +59,6 @@ namespace Ndk
else
NazaraWarning("Ignored command-line argument #" + Nz::String::Number(i) + " \"" + argument + '"');
}
#ifndef NDK_SERVER
if (HasOption("console"))
EnableConsole(true);
if (HasOption("fpscounter"))
EnableFPSCounter(true);
#endif
}
/*!
@@ -111,119 +99,8 @@ namespace Ndk
for (World& world : m_worlds)
world.Update(m_updateTime);
#ifndef NDK_SERVER
for (WindowInfo& info : m_windows)
{
if (!info.overlayWorld)
continue;
if (info.fpsCounter)
{
FPSCounterOverlay& fpsCounter = *info.fpsCounter;
fpsCounter.frameCount++;
fpsCounter.elapsedTime += m_updateTime;
if (fpsCounter.elapsedTime >= 1.f)
{
fpsCounter.sprite->Update(Nz::SimpleTextDrawer::Draw("FPS: " + Nz::String::Number(fpsCounter.frameCount), 36));
fpsCounter.frameCount = 0;
fpsCounter.elapsedTime = 0.f;
}
}
info.overlayWorld->Update(m_updateTime);
}
#endif
return true;
}
#ifndef NDK_SERVER
void Application::SetupConsole(WindowInfo& info)
{
std::unique_ptr<ConsoleOverlay> overlay = std::make_unique<ConsoleOverlay>();
Nz::Vector2ui windowDimensions;
if (info.window->IsValid())
windowDimensions = info.window->GetSize();
else
windowDimensions.MakeZero();
overlay->console = info.canvas->Add<Console>();
Console& consoleRef = *overlay->console;
consoleRef.Resize({float(windowDimensions.x), windowDimensions.y / 4.f});
consoleRef.Show(false);
// Redirect logs toward the console
overlay->logSlot.Connect(Nz::Log::OnLogWrite, [&consoleRef] (const Nz::String& str)
{
consoleRef.AddLine(str);
});
// Setup a few event callback to handle the console
Nz::EventHandler& eventHandler = info.window->GetEventHandler();
overlay->keyPressedSlot.Connect(eventHandler.OnKeyPressed, [&consoleRef] (const Nz::EventHandler*, const Nz::WindowEvent::KeyEvent& event)
{
if (event.virtualKey == Nz::Keyboard::VKey::F9)
{
// Toggle console visibility and focus
if (consoleRef.IsVisible())
{
consoleRef.ClearFocus();
consoleRef.Show(false);
}
else
{
consoleRef.Show(true);
consoleRef.SetFocus();
}
}
});
overlay->resizedSlot.Connect(info.renderTarget->OnRenderTargetSizeChange, [&consoleRef] (const Nz::RenderTarget* renderTarget)
{
Nz::Vector2ui size = renderTarget->GetSize();
consoleRef.Resize({float(size.x), size.y / 4.f});
});
info.console = std::move(overlay);
}
void Application::SetupFPSCounter(WindowInfo& info)
{
std::unique_ptr<FPSCounterOverlay> fpsCounter = std::make_unique<FPSCounterOverlay>();
fpsCounter->sprite = Nz::TextSprite::New();
fpsCounter->entity = info.overlayWorld->CreateEntity();
fpsCounter->entity->AddComponent<NodeComponent>();
fpsCounter->entity->AddComponent<GraphicsComponent>().Attach(fpsCounter->sprite);
info.fpsCounter = std::move(fpsCounter);
}
void Application::SetupOverlay(WindowInfo& info)
{
info.overlayWorld = std::make_unique<World>(false); //< No default system
if (info.window->IsValid())
info.canvas = std::make_unique<Canvas>(info.overlayWorld->CreateHandle(), info.window->GetEventHandler(), info.window->GetCursorController().CreateHandle());
RenderSystem& renderSystem = info.overlayWorld->AddSystem<RenderSystem>();
renderSystem.ChangeRenderTechnique<Nz::ForwardRenderTechnique>();
renderSystem.SetDefaultBackground(nullptr);
renderSystem.SetGlobalUp(Nz::Vector3f::Down());
EntityHandle viewer = info.overlayWorld->CreateEntity();
CameraComponent& camComponent = viewer->AddComponent<CameraComponent>();
viewer->AddComponent<NodeComponent>();
camComponent.SetProjectionType(Nz::ProjectionType_Orthogonal);
camComponent.SetTarget(info.renderTarget);
}
#endif
Application* Application::s_application = nullptr;
}

View File

@@ -1,381 +0,0 @@
// Copyright (C) 2020 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 <NazaraSDK/BaseWidget.hpp>
#include <NazaraSDK/Canvas.hpp>
#include <NazaraSDK/Components/GraphicsComponent.hpp>
#include <NazaraSDK/Components/NodeComponent.hpp>
#include <NazaraSDK/World.hpp>
#include <algorithm>
namespace Ndk
{
/*!
* \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_world = m_canvas->GetWorld();
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.IsValid() == enable)
return;
if (enable)
{
m_backgroundSprite = Nz::Sprite::New();
m_backgroundSprite->SetColor(m_backgroundColor);
m_backgroundSprite->SetMaterial(Nz::Material::New((m_backgroundColor.IsOpaque()) ? "Basic2D" : "Translucent2D")); //< TODO: Use a shared material instead of creating one everytime
m_backgroundEntity = CreateEntity();
m_backgroundEntity->AddComponent<GraphicsComponent>().Attach(m_backgroundSprite, -1);
m_backgroundEntity->AddComponent<NodeComponent>().SetParent(this);
BaseWidget::Layout(); // Only layout background
}
else
{
DestroyEntity(m_backgroundEntity);
m_backgroundEntity.Reset();
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 Nz::Color& color)
{
m_backgroundColor = color;
if (m_backgroundSprite)
{
m_backgroundSprite->SetColor(color);
m_backgroundSprite->GetMaterial()->Configure((color.IsOpaque()) ? "Basic2D" : "Translucent2D"); //< Our sprite has its own material (see EnableBackground)
}
}
void BaseWidget::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
entity.isEnabled = true;
}
}
ShowChildren(show);
}
}
const EntityHandle& BaseWidget::CreateEntity()
{
const EntityHandle& newEntity = m_world->CreateEntity();
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(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);
}
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*/)
{
}
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

@@ -1,230 +0,0 @@
// Copyright (C) 2020 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 <NazaraSDK/Canvas.hpp>
#include <limits>
namespace Ndk
{
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

@@ -1,363 +0,0 @@
// Copyright (C) 2020 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 <NazaraSDK/Components/CameraComponent.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <NazaraSDK/Algorithm.hpp>
#include <NazaraSDK/Components/NodeComponent.hpp>
namespace Ndk
{
/*!
* \ingroup NDK
* \class Ndk::CameraComponent
* \brief NDK class that represents the component for camera
*/
/*!
* \brief Applys the view of the camera
*
* \remark Produces a NazaraAssert if the camera has no target
*/
void CameraComponent::ApplyView() const
{
NazaraAssert(m_target, "CameraComponent has no target");
EnsureProjectionMatrixUpdate();
EnsureViewMatrixUpdate();
EnsureViewportUpdate();
Nz::Renderer::SetMatrix(Nz::MatrixType_Projection, m_projectionMatrix);
Nz::Renderer::SetMatrix(Nz::MatrixType_View, m_viewMatrix);
Nz::Renderer::SetTarget(m_target);
Nz::Renderer::SetViewport(m_viewport);
}
/*!
* \brief Gets the aspect ratio of the camera
* \return Aspect ratio of the camera
*/
float CameraComponent::GetAspectRatio() const
{
EnsureViewportUpdate();
return m_aspectRatio;
}
/*!
* \brief Gets the eye position of the camera
*
* \remark Produces a NazaraAssert if entity is invalid or has no NodeComponent
*/
Nz::Vector3f CameraComponent::GetEyePosition() const
{
NazaraAssert(m_entity && m_entity->HasComponent<NodeComponent>(), "CameraComponent requires NodeComponent");
return m_entity->GetComponent<NodeComponent>().GetPosition();
}
/*!
* \brief Gets the forward direction of the camera
*
* \remark Produces a NazaraAssert if entity is invalid or has no NodeComponent
*/
Nz::Vector3f CameraComponent::GetForward() const
{
NazaraAssert(m_entity && m_entity->HasComponent<NodeComponent>(), "CameraComponent requires NodeComponent");
return m_entity->GetComponent<NodeComponent>().GetForward();
}
/*!
* \brief Gets the frutum of the camera
* \return A constant reference to the frustum of the camera
*/
const Nz::Frustumf& CameraComponent::GetFrustum() const
{
EnsureFrustumUpdate();
return m_frustum;
}
/*!
* \brief Gets the projection matrix of the camera
* \return A constant reference to the projection matrix of the camera
*/
const Nz::Matrix4f& CameraComponent::GetProjectionMatrix() const
{
EnsureProjectionMatrixUpdate();
return m_projectionMatrix;
}
/*!
* \brief Gets the projection type of the camera
* \return Projection type of the camera
*/
Nz::ProjectionType CameraComponent::GetProjectionType() const
{
return m_projectionType;
}
/*!
* \brief Gets the target of the camera
* \return A constant reference to the render target of the camera
*/
const Nz::RenderTarget* CameraComponent::GetTarget() const
{
return m_target;
}
/*!
* \brief Gets the view matrix of the camera
* \return A constant reference to the view matrix of the camera
*/
const Nz::Matrix4f& CameraComponent::GetViewMatrix() const
{
EnsureViewMatrixUpdate();
return m_viewMatrix;
}
/*!
* \brief Gets the view port of the camera
* \return A constant reference to the view port of the camera
*/
const Nz::Recti& CameraComponent::GetViewport() const
{
EnsureViewportUpdate();
return m_viewport;
}
/*!
* \brief Gets the Z far distance of the camera
* \return Z far distance of the camera
*/
float CameraComponent::GetZFar() const
{
return m_zFar;
}
/*!
* \brief Gets the Z near distance of the camera
* \return Z near distance of the camera
*/
float CameraComponent::GetZNear() const
{
return m_zNear;
}
/*!
* \brief Sets the layer of the camera in case of multiples layers
*
* \param layer Layer of the camera
*/
void CameraComponent::SetLayer(unsigned int layer)
{
m_layer = layer;
m_entity->Invalidate(); // Invalidate the entity to make it passes through RenderSystem validation
}
/*!
* \brief Operation to perform when component is attached to an entity
*/
void CameraComponent::OnAttached()
{
if (m_entity->HasComponent<NodeComponent>())
m_nodeInvalidationSlot.Connect(m_entity->GetComponent<NodeComponent>().OnNodeInvalidation, this, &CameraComponent::OnNodeInvalidated);
InvalidateViewMatrix();
}
/*!
* \brief Operation to perform when component is attached to this component
*
* \param component Component being attached
*/
void CameraComponent::OnComponentAttached(BaseComponent& component)
{
if (IsComponent<NodeComponent>(component))
{
NodeComponent& nodeComponent = static_cast<NodeComponent&>(component);
m_nodeInvalidationSlot.Connect(nodeComponent.OnNodeInvalidation, this, &CameraComponent::OnNodeInvalidated);
InvalidateViewMatrix();
}
}
/*!
* \brief Operation to perform when component is detached from this component
*
* \param component Component being detached
*/
void CameraComponent::OnComponentDetached(BaseComponent& component)
{
if (IsComponent<NodeComponent>(component))
{
m_nodeInvalidationSlot.Disconnect();
InvalidateViewMatrix();
}
}
/*!
* \brief Operation to perform when component is detached from an entity
*/
void CameraComponent::OnDetached()
{
m_nodeInvalidationSlot.Disconnect();
InvalidateViewMatrix();
}
/*!
* \brief Operation to perform when the node is invalidated
*
* \param node Pointer to the node
*/
void CameraComponent::OnNodeInvalidated(const Nz::Node* node)
{
NazaraUnused(node);
// Our view matrix depends on NodeComponent position/rotation
InvalidateViewMatrix();
}
/*!
* \brief Operation to perform when the render target is released
*
* \param renderTarget Pointer to the RenderTarget
*/
void CameraComponent::OnRenderTargetRelease(const Nz::RenderTarget* renderTarget)
{
if (renderTarget == m_target)
m_target = nullptr;
else
NazaraInternalError("Not listening to " + Nz::String::Pointer(renderTarget));
}
/*!
* \brief Operation to perform when the render target has its size changed
*
* \param renderTarget Pointer to the RenderTarget
*/
void CameraComponent::OnRenderTargetSizeChange(const Nz::RenderTarget* renderTarget)
{
if (renderTarget == m_target)
InvalidateViewport();
else
NazaraInternalError("Not listening to " + Nz::String::Pointer(renderTarget));
}
/*!
* \brief Updates the frustum of the camera
*/
void CameraComponent::UpdateFrustum() const
{
EnsureProjectionMatrixUpdate();
EnsureViewMatrixUpdate();
// Extract the frustum from the view and projection matrices
m_frustum.Extract(m_viewMatrix, m_projectionMatrix);
m_frustumUpdated = true;
}
/*!
* \brief Updates the project matrix of the camera
*/
void CameraComponent::UpdateProjectionMatrix() const
{
switch (m_projectionType)
{
case Nz::ProjectionType_Orthogonal:
if (m_size.x <= 0.f || m_size.y <= 0.f)
{
EnsureViewportUpdate();
m_projectionMatrix.MakeOrtho(0.f, static_cast<float>(m_viewport.width), 0.f, static_cast<float>(m_viewport.height), m_zNear, m_zFar);
}
else
m_projectionMatrix.MakeOrtho(0.f, m_size.x, 0.f, m_size.y, m_zNear, m_zFar);
break;
case Nz::ProjectionType_Perspective:
EnsureViewportUpdate(); // Can affect aspect ratio
m_projectionMatrix.MakePerspective(m_fov, m_aspectRatio, m_zNear, m_zFar);
break;
}
m_projectionMatrix *= Nz::Matrix4f::Scale(m_projectionScale);
m_projectionMatrixUpdated = true;
}
/*!
* \brief Updates the view matrix of the camera
*
* \remark Produces a NazaraAssert if entity is invalid or has no NodeComponent
*/
void CameraComponent::UpdateViewMatrix() const
{
NazaraAssert(m_entity && m_entity->HasComponent<NodeComponent>(), "CameraComponent requires NodeComponent");
NodeComponent& nodeComponent = m_entity->GetComponent<NodeComponent>();
// Build the view matrix using the NodeComponent position/rotation
m_viewMatrix.MakeViewMatrix(nodeComponent.GetPosition(Nz::CoordSys_Global), nodeComponent.GetRotation(Nz::CoordSys_Global));
m_viewMatrixUpdated = true;
}
/*!
* \brief Updates the view port of the camera
*
* \remark Produces a NazaraAssert if entity has no target
*/
void CameraComponent::UpdateViewport() const
{
NazaraAssert(m_target, "CameraComponent has no target");
Nz::Vector2ui targetSize = m_target->GetSize();
targetSize.y = std::max(targetSize.y, 1U); // Let's make sure we won't divide by zero
// Our target region is expressed as % of the viewport dimensions, let's compute it in pixels
Nz::Rectf fViewport(m_targetRegion);
fViewport.x *= targetSize.x;
fViewport.y *= targetSize.y;
fViewport.width *= targetSize.x;
fViewport.height *= targetSize.y;
// Compute the new aspect ratio, if it's different we need to invalidate the projection matrix
float aspectRatio = fViewport.width/fViewport.height;
if (!Nz::NumberEquals(m_aspectRatio, aspectRatio, 0.001f))
{
m_aspectRatio = aspectRatio;
if (m_projectionType == Nz::ProjectionType_Perspective)
InvalidateProjectionMatrix();
}
// Convert it back to int
m_viewport.Set(fViewport);
m_viewportUpdated = true;
}
ComponentIndex CameraComponent::componentIndex;
}

View File

@@ -1,35 +0,0 @@
// Copyright (C) 2020 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 <NazaraSDK/Components/DebugComponent.hpp>
#include <NazaraSDK/Components/GraphicsComponent.hpp>
namespace Ndk
{
void DebugComponent::DetachDebugRenderables(GraphicsComponent& gfxComponent)
{
for (auto& renderable : m_debugRenderables)
{
if (renderable)
{
gfxComponent.Detach(renderable);
renderable.Reset();
}
}
}
void DebugComponent::OnComponentDetached(BaseComponent& component)
{
if (IsComponent<GraphicsComponent>(component))
DetachDebugRenderables(static_cast<GraphicsComponent&>(component));
}
void DebugComponent::OnDetached()
{
if (m_entity->HasComponent<GraphicsComponent>())
DetachDebugRenderables(m_entity->GetComponent<GraphicsComponent>());
}
ComponentIndex DebugComponent::componentIndex;
}

View File

@@ -1,348 +0,0 @@
// Copyright (C) 2020 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 <NazaraSDK/Components/GraphicsComponent.hpp>
#include <NazaraSDK/World.hpp>
#include <NazaraSDK/Systems/RenderSystem.hpp>
#include <NazaraSDK/Components/NodeComponent.hpp>
namespace Ndk
{
/*!
* \ingroup NDK
* \class Ndk::GraphicsComponent
* \brief NDK class that represents the component for graphics
*/
/*!
* \brief Adds the renderable elements to the render queue
*
* \param renderQueue Queue to be added
*/
void GraphicsComponent::AddToRenderQueue(Nz::AbstractRenderQueue* renderQueue) const
{
EnsureBoundingVolumesUpdate();
EnsureTransformMatrixUpdate();
RenderSystem& renderSystem = m_entity->GetWorld()->GetSystem<RenderSystem>();
for (const Renderable& object : m_renderables)
{
if (!object.dataUpdated)
{
object.renderable->UpdateData(&object.data);
object.dataUpdated = true;
}
object.renderable->AddToRenderQueue(renderQueue, object.data, m_scissorRect);
}
}
/*!
* \brief Adds the renderable elements to the render queue if their bounding volume intersects with the frustum
*
* \param frustum Queue to be added
* \param renderQueue Queue to be added
*/
void GraphicsComponent::AddToRenderQueueByCulling(const Nz::Frustumf& frustum, Nz::AbstractRenderQueue* renderQueue) const
{
EnsureBoundingVolumesUpdate();
EnsureTransformMatrixUpdate();
RenderSystem& renderSystem = m_entity->GetWorld()->GetSystem<RenderSystem>();
for (const Renderable& object : m_renderables)
{
if (frustum.Contains(object.boundingVolume))
{
if (!object.dataUpdated)
{
object.renderable->UpdateData(&object.data);
object.dataUpdated = true;
}
object.renderable->AddToRenderQueue(renderQueue, object.data, m_scissorRect);
}
}
}
/*!
* \brief Attaches a renderable to the entity with a specific matrix
*
* \param renderable Reference to a renderable element
* \param localMatrix Local matrix that will be applied to the instanced renderable
* \param renderOrder Render order of the element
*/
void GraphicsComponent::Attach(Nz::InstancedRenderableRef renderable, const Nz::Matrix4f& localMatrix, int renderOrder)
{
m_renderables.emplace_back(m_transformMatrix);
Renderable& entry = m_renderables.back();
entry.data.localMatrix = localMatrix;
entry.data.renderOrder = renderOrder;
entry.renderable = std::move(renderable);
ConnectInstancedRenderableSignals(entry);
std::size_t materialCount = entry.renderable->GetMaterialCount();
for (std::size_t i = 0; i < materialCount; ++i)
RegisterMaterial(entry.renderable->GetMaterial(i));
InvalidateAABB();
ForceCullingInvalidation();
}
void GraphicsComponent::ConnectInstancedRenderableSignals(Renderable& entry)
{
entry.renderableBoundingVolumeInvalidationSlot.Connect(entry.renderable->OnInstancedRenderableInvalidateBoundingVolume, [this](const Nz::InstancedRenderable*) { InvalidateAABB(); });
entry.renderableDataInvalidationSlot.Connect(entry.renderable->OnInstancedRenderableInvalidateData, std::bind(&GraphicsComponent::InvalidateRenderableData, this, std::placeholders::_1, std::placeholders::_2, m_renderables.size() - 1));
entry.renderableMaterialInvalidationSlot.Connect(entry.renderable->OnInstancedRenderableInvalidateMaterial, this, &GraphicsComponent::InvalidateRenderableMaterial);
entry.renderableReleaseSlot.Connect(entry.renderable->OnInstancedRenderableRelease, this, &GraphicsComponent::Detach);
entry.renderableResetMaterialsSlot.Connect(entry.renderable->OnInstancedRenderableResetMaterials, this, &GraphicsComponent::OnInstancedRenderableResetMaterials);
entry.renderableSkinChangeSlot.Connect(entry.renderable->OnInstancedRenderableSkinChange, this, &GraphicsComponent::OnInstancedRenderableSkinChange);
}
void GraphicsComponent::InvalidateRenderableData(const Nz::InstancedRenderable* renderable , Nz::UInt32 flags, std::size_t index)
{
NazaraAssert(index < m_renderables.size(), "Invalid renderable index");
NazaraUnused(renderable);
Renderable& r = m_renderables[index];
r.dataUpdated = false;
r.renderable->InvalidateData(&r.data, flags);
ForceCullingInvalidation();
}
void GraphicsComponent::InvalidateRenderableMaterial(const Nz::InstancedRenderable* renderable, std::size_t skinIndex, std::size_t matIndex, const Nz::MaterialRef& newMat)
{
// Don't listen to dormant materials
if (renderable->GetSkin() != skinIndex)
return;
RegisterMaterial(newMat);
const Nz::MaterialRef& oldMat = renderable->GetMaterial(skinIndex, matIndex);
UnregisterMaterial(oldMat);
ForceCullingInvalidation();
}
void GraphicsComponent::InvalidateReflectionMap()
{
m_entity->Invalidate();
if (m_reflectiveMaterialCount > 0)
{
if (!m_reflectionMap)
{
m_reflectionMap = Nz::Texture::New();
if (!m_reflectionMap->Create(Nz::ImageType_Cubemap, Nz::PixelFormat_RGB8, m_reflectionMapSize, m_reflectionMapSize))
{
NazaraWarning("Failed to create reflection map, reflections will be disabled for this entity");
return;
}
}
}
else
m_reflectionMap.Reset();
}
void GraphicsComponent::RegisterMaterial(Nz::Material* material, std::size_t count)
{
auto it = m_materialEntries.find(material);
if (it == m_materialEntries.end())
{
MaterialEntry matEntry;
matEntry.reflectionModelChangeSlot.Connect(material->OnMaterialReflectionModeChange, this, &GraphicsComponent::OnMaterialReflectionChange);
matEntry.renderableCounter = count;
if (material->GetReflectionMode() == Nz::ReflectionMode_RealTime)
{
if (m_reflectiveMaterialCount++ == 0)
InvalidateReflectionMap();
}
m_materialEntries.emplace(material, std::move(matEntry));
}
else
it->second.renderableCounter += count;
}
/*!
* \brief Operation to perform when component is attached to an entity
*/
void GraphicsComponent::OnAttached()
{
if (m_entity->HasComponent<NodeComponent>())
m_nodeInvalidationSlot.Connect(m_entity->GetComponent<NodeComponent>().OnNodeInvalidation, this, &GraphicsComponent::OnNodeInvalidated);
InvalidateTransformMatrix();
}
/*!
* \brief Operation to perform when component is attached to this component
*
* \param component Component being attached
*/
void GraphicsComponent::OnComponentAttached(BaseComponent& component)
{
if (IsComponent<NodeComponent>(component))
{
NodeComponent& nodeComponent = static_cast<NodeComponent&>(component);
m_nodeInvalidationSlot.Connect(nodeComponent.OnNodeInvalidation, this, &GraphicsComponent::OnNodeInvalidated);
InvalidateTransformMatrix();
}
}
/*!
* \brief Operation to perform when component is detached from this component
*
* \param component Component being detached
*/
void GraphicsComponent::OnComponentDetached(BaseComponent& component)
{
if (IsComponent<NodeComponent>(component))
{
m_nodeInvalidationSlot.Disconnect();
InvalidateTransformMatrix();
}
}
/*!
* \brief Operation to perform when component is detached from an entity
*/
void GraphicsComponent::OnDetached()
{
m_nodeInvalidationSlot.Disconnect();
InvalidateTransformMatrix();
}
void GraphicsComponent::OnInstancedRenderableResetMaterials(const Nz::InstancedRenderable* renderable, std::size_t newMaterialCount)
{
RegisterMaterial(Nz::Material::GetDefault(), newMaterialCount);
std::size_t materialCount = renderable->GetMaterialCount();
for (std::size_t i = 0; i < materialCount; ++i)
UnregisterMaterial(renderable->GetMaterial(i));
ForceCullingInvalidation();
}
void GraphicsComponent::OnInstancedRenderableSkinChange(const Nz::InstancedRenderable* renderable, std::size_t newSkinIndex)
{
std::size_t materialCount = renderable->GetMaterialCount();
for (std::size_t i = 0; i < materialCount; ++i)
RegisterMaterial(renderable->GetMaterial(newSkinIndex, i));
for (std::size_t i = 0; i < materialCount; ++i)
UnregisterMaterial(renderable->GetMaterial(i));
ForceCullingInvalidation();
}
void GraphicsComponent::OnMaterialReflectionChange(const Nz::Material* material, Nz::ReflectionMode reflectionMode)
{
// Since this signal is only called when the new reflection mode is different from the current one, no need to compare both
if (material->GetReflectionMode() == Nz::ReflectionMode_RealTime)
{
if (--m_reflectiveMaterialCount == 0)
InvalidateReflectionMap();
}
else if (reflectionMode == Nz::ReflectionMode_RealTime)
{
if (m_reflectiveMaterialCount++ == 0)
InvalidateReflectionMap();
}
}
void GraphicsComponent::OnNodeInvalidated(const Nz::Node* node)
{
NazaraUnused(node);
// Our view matrix depends on NodeComponent position/rotation
InvalidateAABB();
InvalidateTransformMatrix();
ForceCullingInvalidation(); //< Force invalidation on movement for now (FIXME)
}
void GraphicsComponent::UnregisterMaterial(Nz::Material* material)
{
auto it = m_materialEntries.find(material);
NazaraAssert(it != m_materialEntries.end(), "Material not registered");
MaterialEntry& matEntry = it->second;
if (--matEntry.renderableCounter == 0)
{
if (material->GetReflectionMode() == Nz::ReflectionMode_RealTime)
{
if (--m_reflectiveMaterialCount == 0)
InvalidateReflectionMap();
}
m_materialEntries.erase(it);
}
}
/*!
* \brief Updates the bounding volume
*/
void GraphicsComponent::UpdateBoundingVolumes() const
{
EnsureTransformMatrixUpdate();
RenderSystem& renderSystem = m_entity->GetWorld()->GetSystem<RenderSystem>();
m_aabb.Set(-1.f, -1.f, -1.f);
bool isAabbSet = false;
for (const Renderable& r : m_renderables)
{
r.boundingVolume = r.renderable->GetBoundingVolume();
r.data.transformMatrix = Nz::Matrix4f::ConcatenateAffine(renderSystem.GetCoordinateSystemMatrix(), Nz::Matrix4f::ConcatenateAffine(r.data.localMatrix, m_transformMatrix));
if (r.boundingVolume.IsFinite())
{
r.boundingVolume.Update(r.data.transformMatrix);
if (isAabbSet)
m_aabb.ExtendTo(r.boundingVolume.aabb);
else
{
m_aabb.Set(r.boundingVolume.aabb);
isAabbSet = true;
}
}
}
m_boundingVolumesUpdated = true;
for (CullingBoxEntry& entry : m_cullingBoxEntries)
entry.listEntry.UpdateBox(m_aabb);
}
/*!
* \brief Updates the transform matrix of the renderable
*
* \remark Produces a NazaraAssert if entity is invalid or has no NodeComponent
*/
void GraphicsComponent::UpdateTransformMatrix() const
{
NazaraAssert(m_entity && m_entity->HasComponent<NodeComponent>(), "GraphicsComponent requires NodeComponent");
m_transformMatrix = m_entity->GetComponent<NodeComponent>().GetTransformMatrix();
m_transformMatrixUpdated = true;
}
ComponentIndex GraphicsComponent::componentIndex;
}

View File

@@ -1,10 +0,0 @@
// Copyright (C) 2020 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 <NazaraSDK/Components/LightComponent.hpp>
namespace Ndk
{
ComponentIndex LightComponent::componentIndex;
}

View File

@@ -1,30 +0,0 @@
// Copyright (C) 2020 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 <NazaraSDK/Components/ParticleEmitterComponent.hpp>
#include <Nazara/Graphics/ParticleGroup.hpp>
namespace Ndk
{
/*!
* \ingroup NDK
* \class Ndk::ParticleEmitterComponent
* \brief NDK class that represents the component emitter of particles
*/
/*!
* \brief Sets up the particles
*
* \param mapper Mapper containing layout information of each particle
* \param count Number of particles
*/
void ParticleEmitterComponent::SetupParticles(Nz::ParticleMapper& mapper, unsigned int count) const
{
if (m_isActive && m_setupFunc)
m_setupFunc(m_entity, mapper, count);
}
ComponentIndex ParticleEmitterComponent::componentIndex;
}

View File

@@ -1,10 +0,0 @@
// Copyright (C) 2020 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 <NazaraSDK/Components/ParticleGroupComponent.hpp>
namespace Ndk
{
ComponentIndex ParticleGroupComponent::componentIndex;
}

View File

@@ -1,232 +0,0 @@
// Copyright (C) 2020 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 <NazaraSDK/Console.hpp>
#include <Nazara/Core/Unicode.hpp>
#include <Nazara/Platform/Event.hpp>
#include <NazaraSDK/Components/GraphicsComponent.hpp>
#include <NazaraSDK/Components/NodeComponent.hpp>
#include <NazaraSDK/Widgets.hpp>
#include <NazaraSDK/World.hpp>
namespace Ndk
{
namespace
{
const char s_inputPrefix[] = "> ";
constexpr std::size_t s_inputPrefixSize = Nz::CountOf(s_inputPrefix) - 1;
}
/*!
* \ingroup NDK
* \class Ndk::Console
* \brief NDK class that represents a console to help development with Lua scripting
*/
/*!
* \brief Constructs a Console object with a world to interact with
*
* \param world World to interact with
* \param size (Width, Height) of the console
* \param instance Lua instance that will interact with the world
*/
Console::Console(BaseWidget* parent) :
BaseWidget(parent),
m_historyPosition(0),
m_defaultFont(Nz::Font::GetDefault()),
m_characterSize(24),
m_maxHistoryLines(200)
{
// History
m_history = Add<RichTextAreaWidget>();
m_history->EnableBackground(true);
m_history->EnableLineWrap(true);
m_history->SetReadOnly(true);
m_history->SetBackgroundColor(Nz::Color(80, 80, 160, 128));
m_historyArea = Add<ScrollAreaWidget>(m_history);
// Input
m_input = Add<TextAreaWidget>();
m_input->EnableBackground(true);
m_input->SetText(s_inputPrefix);
m_input->SetTextColor(Nz::Color::Black);
m_input->OnTextAreaKeyReturn.Connect(this, &Console::ExecuteInput);
// Protect input prefix from erasure/selection
m_input->SetCursorPosition(s_inputPrefixSize);
m_input->OnTextAreaCursorMove.Connect([](const AbstractTextAreaWidget* textArea, Nz::Vector2ui* newCursorPos)
{
newCursorPos->x = std::max(newCursorPos->x, static_cast<unsigned int>(s_inputPrefixSize));
});
m_input->OnTextAreaSelection.Connect([](const AbstractTextAreaWidget* textArea, Nz::Vector2ui* start, Nz::Vector2ui* end)
{
start->x = std::max(start->x, static_cast<unsigned int>(s_inputPrefixSize));
end->x = std::max(end->x, static_cast<unsigned int>(s_inputPrefixSize));
});
m_input->OnTextAreaKeyBackspace.Connect([](const AbstractTextAreaWidget* textArea, bool* ignoreDefaultAction)
{
if (textArea->GetGlyphIndex() < s_inputPrefixSize)
*ignoreDefaultAction = true;
});
// Handle history
m_input->OnTextAreaKeyUp.Connect([&] (const AbstractTextAreaWidget* textArea, bool* ignoreDefaultAction)
{
*ignoreDefaultAction = true;
if (m_commandHistory.empty())
return;
if (m_historyPosition > 0)
m_historyPosition--;
m_input->SetText(s_inputPrefix + m_commandHistory[m_historyPosition]);
});
m_input->OnTextAreaKeyDown.Connect([&] (const AbstractTextAreaWidget* textArea, bool* ignoreDefaultAction)
{
*ignoreDefaultAction = true;
if (m_commandHistory.empty())
return;
if (++m_historyPosition >= m_commandHistory.size())
m_historyPosition = 0;
m_input->SetText(s_inputPrefix + m_commandHistory[m_historyPosition]);
});
}
/*!
* \brief Adds a line to the console
*
* \param text New line of text
* \param color Color for the text
*/
void Console::AddLine(const Nz::String& text, const Nz::Color& color)
{
if (m_historyLines.size() >= m_maxHistoryLines)
m_historyLines.erase(m_historyLines.begin());
m_historyLines.emplace_back(Line{ color, text });
m_history->SetTextColor(color);
m_history->AppendText(text + '\n');
m_history->Resize(m_history->GetPreferredSize());
m_historyArea->Resize(m_historyArea->GetSize());
m_historyArea->ScrollToRatio(1.f);
}
/*!
* \brief Clears the console
*
* Clears the console history and input
*/
void Console::Clear()
{
m_historyLines.clear();
m_history->Clear();
m_history->Resize(m_history->GetPreferredSize());
m_historyArea->Resize(m_historyArea->GetSize());
m_input->SetText(s_inputPrefix);
}
/*!
* \brief Clears the console focus
*
* Clear console input widget focus (if owned)
*/
void Console::ClearFocus()
{
m_input->ClearFocus();
}
/*!
* \brief Sets the character size
*
* \param size Size of the font
*/
void Console::SetCharacterSize(unsigned int size)
{
m_characterSize = size;
m_history->SetCharacterSize(size);
m_input->SetCharacterSize(size);
Layout();
}
/*!
* \brief Give the console input focus
*
*/
void Console::SetFocus()
{
m_input->SetFocus();
}
/*!
* \brief Sets the text font
*
* \param font Reference to a valid font
*
* \remark Produces a NazaraAssert if font is invalid or null
*/
void Console::SetTextFont(Nz::FontRef font)
{
NazaraAssert(font && font->IsValid(), "Invalid font");
m_defaultFont = std::move(font);
m_history->SetTextFont(m_defaultFont);
m_input->SetTextFont(m_defaultFont);
Layout();
}
/*!
* \brief Performs this action when an input is added to the console
*/
void Console::ExecuteInput(const AbstractTextAreaWidget* textArea, bool* ignoreDefaultAction)
{
NazaraAssert(textArea == m_input, "Unexpected signal from an other text area");
*ignoreDefaultAction = true;
Nz::String input = m_input->GetText();
Nz::String inputCmd = input.SubString(s_inputPrefixSize);
m_input->SetText(s_inputPrefix);
if (m_commandHistory.empty() || m_commandHistory.back() != inputCmd)
m_commandHistory.push_back(inputCmd);
m_historyPosition = m_commandHistory.size();
AddLine(input); //< With the input prefix
OnCommand(this, inputCmd);
}
/*!
* \brief Places the console according to its layout
*/
void Console::Layout()
{
Nz::Vector2f origin = Nz::Vector2f(GetPosition());
const Nz::Vector2f& size = GetSize();
unsigned int lineHeight = m_defaultFont->GetSizeInfo(m_characterSize).lineHeight;
float historyHeight = size.y - lineHeight;
m_historyArea->SetPosition(origin.x, origin.y);
m_historyArea->Resize({ size.x, historyHeight - 4.f });
m_input->Resize({size.x, size.y - historyHeight});
m_input->SetPosition(origin.x, origin.y + historyHeight);
}
}

View File

@@ -6,7 +6,6 @@
#include <Nazara/Audio/Audio.hpp>
#include <Nazara/Core/ErrorFlags.hpp>
#include <Nazara/Core/Log.hpp>
#include <Nazara/Graphics/Graphics.hpp>
#include <Nazara/Physics2D/Physics2D.hpp>
#include <Nazara/Physics3D/Physics3D.hpp>
#include <Nazara/Platform/Platform.hpp>
@@ -27,18 +26,8 @@
#include <NazaraSDK/Systems/VelocitySystem.hpp>
#ifndef NDK_SERVER
#include <NazaraSDK/Components/CameraComponent.hpp>
#include <NazaraSDK/Components/DebugComponent.hpp>
#include <NazaraSDK/Components/LightComponent.hpp>
#include <NazaraSDK/Components/ListenerComponent.hpp>
#include <NazaraSDK/Components/GraphicsComponent.hpp>
#include <NazaraSDK/Components/ParticleEmitterComponent.hpp>
#include <NazaraSDK/Components/ParticleGroupComponent.hpp>
#include <NazaraSDK/Systems/DebugSystem.hpp>
#include <NazaraSDK/Systems/ParticleSystem.hpp>
#include <NazaraSDK/Systems/ListenerSystem.hpp>
#include <NazaraSDK/Systems/RenderSystem.hpp>
#include <NazaraSDK/Widgets/CheckboxWidget.hpp>
#endif
namespace Ndk
@@ -75,7 +64,6 @@ namespace Ndk
#ifndef NDK_SERVER
// Client modules
Nz::Audio::Initialize();
Nz::Graphics::Initialize();
#endif
// SDK Initialization
@@ -95,13 +83,7 @@ namespace Ndk
#ifndef NDK_SERVER
// Client components
InitializeComponent<CameraComponent>("NdkCam");
InitializeComponent<DebugComponent>("NdkDebug");
InitializeComponent<LightComponent>("NdkLight");
InitializeComponent<ListenerComponent>("NdkList");
InitializeComponent<GraphicsComponent>("NdkGfx");
InitializeComponent<ParticleEmitterComponent>("NdkPaEmi");
InitializeComponent<ParticleGroupComponent>("NdkPaGrp");
#endif
// Systems
@@ -116,17 +98,7 @@ namespace Ndk
#ifndef NDK_SERVER
// Client systems
InitializeSystem<DebugSystem>();
InitializeSystem<ListenerSystem>();
InitializeSystem<ParticleSystem>();
InitializeSystem<RenderSystem>();
// Widgets
if (!CheckboxWidget::Initialize())
{
NazaraError("Failed to initialize Checkbox Widget");
return false;
}
#endif
NazaraNotice("Initialized: SDK");
@@ -170,7 +142,6 @@ namespace Ndk
#ifndef NDK_SERVER
// Client modules
Nz::Audio::Uninitialize();
Nz::Graphics::Uninitialize();
#endif
// Shared modules
@@ -179,8 +150,6 @@ namespace Ndk
Nz::Utility::Uninitialize();
#ifndef NDK_SERVER
// Widgets
CheckboxWidget::Uninitialize();
#endif
NazaraNotice("Uninitialized: SDK");

View File

@@ -1,526 +0,0 @@
// Copyright (C) 2020 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 <NazaraSDK/Systems/DebugSystem.hpp>
#include <Nazara/Core/Primitive.hpp>
#include <Nazara/Graphics/Model.hpp>
#include <Nazara/Utility/IndexIterator.hpp>
#include <Nazara/Utility/Mesh.hpp>
#include <Nazara/Utility/StaticMesh.hpp>
#include <NazaraSDK/Components/CollisionComponent2D.hpp>
#include <NazaraSDK/Components/CollisionComponent3D.hpp>
#include <NazaraSDK/Components/DebugComponent.hpp>
#include <NazaraSDK/Components/GraphicsComponent.hpp>
#include <NazaraSDK/Components/NodeComponent.hpp>
#include <NazaraSDK/Components/PhysicsComponent2D.hpp>
namespace Ndk
{
namespace
{
class DebugRenderable : public Nz::InstancedRenderable
{
public:
DebugRenderable(Ndk::Entity* owner, Nz::IndexBufferRef indexBuffer, Nz::VertexBufferRef vertexBuffer) :
m_entityOwner(owner),
m_indexBuffer(std::move(indexBuffer)),
m_vertexBuffer(std::move(vertexBuffer))
{
ResetMaterials(1);
m_meshData.indexBuffer = m_indexBuffer;
m_meshData.primitiveMode = Nz::PrimitiveMode_LineList;
m_meshData.vertexBuffer = m_vertexBuffer;
}
void UpdateBoundingVolume(InstanceData* /*instanceData*/) const override
{
}
void MakeBoundingVolume() const override
{
// We generate an infinite bounding volume so that we're always considered for rendering when culling does occurs
// (bounding volume culling happens only if GraphicsComponent AABB partially fail)
m_boundingVolume.MakeInfinite();
}
protected:
Ndk::EntityHandle m_entityOwner;
Nz::IndexBufferRef m_indexBuffer;
Nz::MeshData m_meshData;
Nz::VertexBufferRef m_vertexBuffer;
};
class AABBDebugRenderable : public DebugRenderable
{
public:
AABBDebugRenderable(Ndk::Entity* owner, Nz::MaterialRef globalMaterial, Nz::MaterialRef localMaterial, Nz::IndexBufferRef indexBuffer, Nz::VertexBufferRef vertexBuffer) :
DebugRenderable(owner, std::move(indexBuffer), std::move(vertexBuffer)),
m_globalMaterial(std::move(globalMaterial)),
m_localMaterial(std::move(localMaterial))
{
}
void AddToRenderQueue(Nz::AbstractRenderQueue* renderQueue, const InstanceData& instanceData, const Nz::Recti& scissorRect) const override
{
NazaraAssert(m_entityOwner, "DebugRenderable has no owner");
const DebugComponent& entityDebug = m_entityOwner->GetComponent<DebugComponent>();
const GraphicsComponent& entityGfx = m_entityOwner->GetComponent<GraphicsComponent>();
auto DrawBox = [&](const Nz::Boxf& box, const Nz::MaterialRef& mat)
{
Nz::Matrix4f transformMatrix = Nz::Matrix4f::Identity();
transformMatrix.SetScale(box.GetLengths());
transformMatrix.SetTranslation(box.GetCenter());
renderQueue->AddMesh(0, mat, m_meshData, Nz::Boxf::Zero(), transformMatrix, scissorRect);
};
DrawBox(entityGfx.GetAABB(), m_globalMaterial);
for (std::size_t i = 0; i < entityGfx.GetAttachedRenderableCount(); ++i)
{
const Nz::BoundingVolumef& boundingVolume = entityGfx.GetBoundingVolume(i);
if (boundingVolume.IsFinite())
DrawBox(boundingVolume.aabb, m_localMaterial);
}
}
std::unique_ptr<InstancedRenderable> Clone() const override
{
return nullptr;
}
private:
Nz::MaterialRef m_globalMaterial;
Nz::MaterialRef m_localMaterial;
};
class OBBDebugRenderable : public DebugRenderable
{
public:
OBBDebugRenderable(Ndk::Entity* owner, Nz::MaterialRef material, Nz::IndexBufferRef indexBuffer, Nz::VertexBufferRef vertexBuffer) :
DebugRenderable(owner, std::move(indexBuffer), std::move(vertexBuffer)),
m_material(std::move(material))
{
}
void AddToRenderQueue(Nz::AbstractRenderQueue* renderQueue, const InstanceData& instanceData, const Nz::Recti& scissorRect) const override
{
NazaraAssert(m_entityOwner, "DebugRenderable has no owner");
const DebugComponent& entityDebug = m_entityOwner->GetComponent<DebugComponent>();
const GraphicsComponent& entityGfx = m_entityOwner->GetComponent<GraphicsComponent>();
auto DrawBox = [&](const Nz::Boxf& box, const Nz::Matrix4f& transformMatrix)
{
Nz::Matrix4f boxMatrix = Nz::Matrix4f::Identity();
boxMatrix.SetScale(box.GetLengths());
boxMatrix.SetTranslation(box.GetCenter());
boxMatrix.ConcatenateAffine(transformMatrix);
renderQueue->AddMesh(0, m_material, m_meshData, Nz::Boxf::Zero(), boxMatrix, scissorRect);
};
for (std::size_t i = 0; i < entityGfx.GetAttachedRenderableCount(); ++i)
{
const Nz::BoundingVolumef& boundingVolume = entityGfx.GetBoundingVolume(i);
if (boundingVolume.IsFinite())
DrawBox(boundingVolume.obb.localBox, entityGfx.GetTransformMatrix(i));
}
}
std::unique_ptr<InstancedRenderable> Clone() const override
{
return nullptr;
}
private:
Nz::MaterialRef m_material;
};
}
/*!
* \ingroup NDK
* \class Ndk::DebugSystem
* \brief NDK class that represents the debug system
*
* \remark This system is enabled if the entity owns the trait: DebugComponent and GraphicsComponent
*/
/*!
* \brief Constructs an DebugSystem object by default
*/
DebugSystem::DebugSystem() :
m_isDepthBufferEnabled(true)
{
Requires<DebugComponent, GraphicsComponent, NodeComponent>();
SetUpdateOrder(1000); //< Update last
}
void DebugSystem::EnableDepthBuffer(bool enable)
{
m_isDepthBufferEnabled = enable;
if (m_collisionMaterial)
m_collisionMaterial->EnableDepthBuffer(enable);
if (m_globalAabbMaterial)
m_globalAabbMaterial->EnableDepthBuffer(enable);
if (m_localAabbMaterial)
m_localAabbMaterial->EnableDepthBuffer(enable);
if (m_obbMaterial)
m_obbMaterial->EnableDepthBuffer(enable);
}
Nz::InstancedRenderableRef DebugSystem::GenerateBox(Nz::Boxf box)
{
Nz::MeshRef mesh = Nz::Mesh::New();
mesh->CreateStatic();
mesh->BuildSubMesh(Nz::Primitive::Box(box.GetLengths()));
mesh->SetMaterialCount(1);
Nz::ModelRef model = Nz::Model::New();
model->SetMesh(mesh);
model->SetMaterial(0, GetOBBMaterial());
return model;
}
Nz::InstancedRenderableRef DebugSystem::GenerateCollision2DMesh(Entity* entity, Nz::Vector3f* offset)
{
if (entity->HasComponent<CollisionComponent2D>())
{
CollisionComponent2D& entityCollision = entity->GetComponent<CollisionComponent2D>();
const Nz::Collider2DRef& geom = entityCollision.GetGeom();
std::vector<Nz::Vector3f> vertices;
std::vector<std::size_t> indices;
geom->ForEachPolygon([&](const Nz::Vector2f* polygonVertices, std::size_t vertexCount)
{
std::size_t firstIndex = vertices.size();
// Don't reserve and let the vector handle its own capacity
for (std::size_t i = 0; i < vertexCount; ++i)
vertices.emplace_back(*polygonVertices++);
for (std::size_t i = 0; i < vertexCount - 1; ++i)
{
indices.push_back(firstIndex + i);
indices.push_back(firstIndex + i + 1);
}
indices.push_back(firstIndex + vertexCount - 1);
indices.push_back(firstIndex);
});
Nz::IndexBufferRef indexBuffer = Nz::IndexBuffer::New(vertices.size() > 0xFFFF, Nz::UInt32(indices.size()), Nz::DataStorage_Hardware, 0);
Nz::IndexMapper indexMapper(indexBuffer, Nz::BufferAccess_WriteOnly);
Nz::IndexIterator indexPtr = indexMapper.begin();
for (std::size_t index : indices)
*indexPtr++ = static_cast<Nz::UInt32>(index);
indexMapper.Unmap();
Nz::VertexBufferRef vertexBuffer = Nz::VertexBuffer::New(Nz::VertexDeclaration::Get(Nz::VertexLayout_XYZ), Nz::UInt32(vertices.size()), Nz::DataStorage_Hardware, 0);
vertexBuffer->Fill(vertices.data(), 0, Nz::UInt32(vertices.size()));
Nz::MeshRef mesh = Nz::Mesh::New();
mesh->CreateStatic();
Nz::StaticMeshRef subMesh = Nz::StaticMesh::New(vertexBuffer, indexBuffer);
subMesh->SetPrimitiveMode(Nz::PrimitiveMode_LineList);
subMesh->SetMaterialIndex(0);
subMesh->GenerateAABB();
mesh->SetMaterialCount(1);
mesh->AddSubMesh(subMesh);
Nz::ModelRef model = Nz::Model::New();
model->SetMesh(mesh);
model->SetMaterial(0, GetCollisionMaterial());
// Find center of mass
if (entity->HasComponent<PhysicsComponent2D>())
{
const PhysicsComponent2D& entityPhys = entity->GetComponent<PhysicsComponent2D>();
*offset = entityPhys.GetMassCenter(Nz::CoordSys_Local) + entityCollision.GetGeomOffset();
}
else
*offset = entityCollision.GetGeomOffset();
return model;
}
else
return nullptr;
}
Nz::InstancedRenderableRef DebugSystem::GenerateCollision3DMesh(Entity* entity)
{
if (entity->HasComponent<CollisionComponent3D>())
{
CollisionComponent3D& entityCollision = entity->GetComponent<CollisionComponent3D>();
const Nz::Collider3DRef& geom = entityCollision.GetGeom();
std::vector<Nz::Vector3f> vertices;
std::vector<std::size_t> indices;
geom->ForEachPolygon([&](const Nz::Vector3f* polygonVertices, std::size_t vertexCount)
{
std::size_t firstIndex = vertices.size();
vertices.resize(firstIndex + vertexCount);
std::copy(polygonVertices, polygonVertices + vertexCount, &vertices[firstIndex]);
for (std::size_t i = 0; i < vertexCount - 1; ++i)
{
indices.push_back(firstIndex + i);
indices.push_back(firstIndex + i + 1);
}
indices.push_back(firstIndex + vertexCount - 1);
indices.push_back(firstIndex);
});
Nz::IndexBufferRef indexBuffer = Nz::IndexBuffer::New(vertices.size() > 0xFFFF, Nz::UInt32(indices.size()), Nz::DataStorage_Hardware, 0);
Nz::IndexMapper indexMapper(indexBuffer, Nz::BufferAccess_WriteOnly);
Nz::IndexIterator indexPtr = indexMapper.begin();
for (std::size_t index : indices)
*indexPtr++ = static_cast<Nz::UInt32>(index);
indexMapper.Unmap();
Nz::VertexBufferRef vertexBuffer = Nz::VertexBuffer::New(Nz::VertexDeclaration::Get(Nz::VertexLayout_XYZ), Nz::UInt32(vertices.size()), Nz::DataStorage_Hardware, 0);
vertexBuffer->Fill(vertices.data(), 0, Nz::UInt32(vertices.size()));
Nz::MeshRef mesh = Nz::Mesh::New();
mesh->CreateStatic();
Nz::StaticMeshRef subMesh = Nz::StaticMesh::New(vertexBuffer, indexBuffer);
subMesh->SetPrimitiveMode(Nz::PrimitiveMode_LineList);
subMesh->SetMaterialIndex(0);
subMesh->GenerateAABB();
mesh->SetMaterialCount(1);
mesh->AddSubMesh(subMesh);
Nz::ModelRef model = Nz::Model::New();
model->SetMesh(mesh);
model->SetMaterial(0, GetCollisionMaterial());
return model;
}
else
return nullptr;
}
std::pair<Nz::IndexBufferRef, Nz::VertexBufferRef> DebugSystem::GetBoxMesh()
{
if (!m_boxMeshIndexBuffer)
{
std::array<Nz::UInt16, 24> indices = {
{
0, 1,
1, 2,
2, 3,
3, 0,
4, 5,
5, 6,
6, 7,
7, 4,
0, 4,
1, 5,
2, 6,
3, 7
}
};
m_boxMeshIndexBuffer = Nz::IndexBuffer::New(false, Nz::UInt32(indices.size()), Nz::DataStorage_Hardware, 0);
m_boxMeshIndexBuffer->Fill(indices.data(), 0, Nz::UInt32(indices.size()));
}
if (!m_boxMeshVertexBuffer)
{
Nz::Boxf box(-0.5f, -0.5f, -0.5f, 1.f, 1.f, 1.f);
std::array<Nz::Vector3f, 8> positions = {
{
box.GetCorner(Nz::BoxCorner_FarLeftBottom),
box.GetCorner(Nz::BoxCorner_NearLeftBottom),
box.GetCorner(Nz::BoxCorner_NearRightBottom),
box.GetCorner(Nz::BoxCorner_FarRightBottom),
box.GetCorner(Nz::BoxCorner_FarLeftTop),
box.GetCorner(Nz::BoxCorner_NearLeftTop),
box.GetCorner(Nz::BoxCorner_NearRightTop),
box.GetCorner(Nz::BoxCorner_FarRightTop)
}
};
m_boxMeshVertexBuffer = Nz::VertexBuffer::New(Nz::VertexDeclaration::Get(Nz::VertexLayout_XYZ), Nz::UInt32(positions.size()), Nz::DataStorage_Hardware, 0);
m_boxMeshVertexBuffer->Fill(positions.data(), 0, Nz::UInt32(positions.size()));
}
return { m_boxMeshIndexBuffer, m_boxMeshVertexBuffer };
}
Nz::MaterialRef DebugSystem::GetGlobalAABBMaterial()
{
if (!m_globalAabbMaterial)
{
m_globalAabbMaterial = Nz::Material::New();
m_globalAabbMaterial->EnableFaceCulling(false);
m_globalAabbMaterial->EnableDepthBuffer(true);
m_globalAabbMaterial->SetDiffuseColor(Nz::Color::Orange);
m_globalAabbMaterial->SetFaceFilling(Nz::FaceFilling_Line);
//m_globalAabbMaterial->SetLineWidth(2.f);
}
return m_globalAabbMaterial;
}
Nz::MaterialRef DebugSystem::GetLocalAABBMaterial()
{
if (!m_localAabbMaterial)
{
m_localAabbMaterial = Nz::Material::New();
m_localAabbMaterial->EnableFaceCulling(false);
m_localAabbMaterial->EnableDepthBuffer(true);
m_localAabbMaterial->SetDiffuseColor(Nz::Color::Red);
m_localAabbMaterial->SetFaceFilling(Nz::FaceFilling_Line);
//m_localAabbMaterial->SetLineWidth(2.f);
}
return m_localAabbMaterial;
}
Nz::MaterialRef DebugSystem::GetCollisionMaterial()
{
if (!m_collisionMaterial)
{
m_collisionMaterial = Nz::Material::New();
m_collisionMaterial->EnableFaceCulling(false);
m_collisionMaterial->EnableDepthBuffer(true);
m_collisionMaterial->SetDiffuseColor(Nz::Color::Blue);
m_collisionMaterial->SetFaceFilling(Nz::FaceFilling_Line);
//m_collisionMaterial->SetLineWidth(2.f);
}
return m_collisionMaterial;
}
Nz::MaterialRef DebugSystem::GetOBBMaterial()
{
if (!m_obbMaterial)
{
m_obbMaterial = Nz::Material::New();
m_obbMaterial->EnableFaceCulling(false);
m_obbMaterial->EnableDepthBuffer(true);
m_obbMaterial->SetDiffuseColor(Nz::Color::Green);
m_obbMaterial->SetFaceFilling(Nz::FaceFilling_Line);
//m_obbMaterial->SetLineWidth(2.f);
}
return m_obbMaterial;
}
void DebugSystem::OnEntityValidation(Entity* entity, bool /*justAdded*/)
{
static constexpr int DebugDrawOrder = 1'000;
DebugComponent& entityDebug = entity->GetComponent<DebugComponent>();
GraphicsComponent& entityGfx = entity->GetComponent<GraphicsComponent>();
NodeComponent& entityNode = entity->GetComponent<NodeComponent>();
DebugDrawFlags enabledFlags = entityDebug.GetEnabledFlags();
DebugDrawFlags flags = entityDebug.GetFlags();
DebugDrawFlags flagsToEnable = flags & ~enabledFlags;
for (std::size_t i = 0; i <= static_cast<std::size_t>(DebugDraw::Max); ++i)
{
DebugDraw option = static_cast<DebugDraw>(i);
if (flagsToEnable & option)
{
switch (option)
{
case DebugDraw::Collider2D:
{
Nz::Vector3f offset;
Nz::InstancedRenderableRef renderable = GenerateCollision2DMesh(entity, &offset);
if (renderable)
entityGfx.Attach(renderable, Nz::Matrix4f::Translate(offset), DebugDrawOrder);
entityDebug.UpdateDebugRenderable(option, std::move(renderable));
break;
}
case DebugDraw::Collider3D:
{
const Nz::Boxf& obb = entityGfx.GetAABB();
Nz::InstancedRenderableRef renderable = GenerateCollision3DMesh(entity);
if (renderable)
entityGfx.Attach(renderable, Nz::Matrix4f::Translate(obb.GetCenter() - entityNode.GetPosition()), DebugDrawOrder);
entityDebug.UpdateDebugRenderable(option, std::move(renderable));
break;
}
case DebugDraw::GraphicsAABB:
{
auto indexVertexBuffers = GetBoxMesh();
Nz::InstancedRenderableRef renderable = new AABBDebugRenderable(entity, GetGlobalAABBMaterial(), GetLocalAABBMaterial(), indexVertexBuffers.first, indexVertexBuffers.second);
renderable->SetPersistent(false);
entityGfx.Attach(renderable, Nz::Matrix4f::Identity(), DebugDrawOrder);
entityDebug.UpdateDebugRenderable(option, std::move(renderable));
break;
}
case DebugDraw::GraphicsOBB:
{
auto indexVertexBuffers = GetBoxMesh();
Nz::InstancedRenderableRef renderable = new OBBDebugRenderable(entity, GetOBBMaterial(), indexVertexBuffers.first, indexVertexBuffers.second);
renderable->SetPersistent(false);
entityGfx.Attach(renderable, Nz::Matrix4f::Identity(), DebugDrawOrder);
entityDebug.UpdateDebugRenderable(option, std::move(renderable));
break;
}
default:
break;
}
}
}
DebugDrawFlags flagsToDisable = enabledFlags & ~flags;
for (std::size_t i = 0; i <= static_cast<std::size_t>(DebugDraw::Max); ++i)
{
DebugDraw option = static_cast<DebugDraw>(i);
if (flagsToDisable & option)
entityGfx.Detach(entityDebug.GetDebugRenderable(option));
}
entityDebug.UpdateEnabledFlags(flags);
}
void DebugSystem::OnUpdate(float elapsedTime)
{
// Nothing to do
}
SystemIndex DebugSystem::systemIndex;
}

View File

@@ -1,44 +0,0 @@
// Copyright (C) 2020 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 <NazaraSDK/Systems/ParticleSystem.hpp>
#include <NazaraSDK/Components/ParticleGroupComponent.hpp>
namespace Ndk
{
/*!
* \ingroup NDK
* \class Ndk::ParticleSystem
* \brief NDK class that represents the particle system
*
* \remark This system is enabled if the entity has the trait: NodeComponent and any of these two: ParticleGroupComponent
*/
/*!
* \brief Constructs an ParticleSystem object by default
*/
ParticleSystem::ParticleSystem()
{
Requires<ParticleGroupComponent>();
}
/*!
* \brief Operation to perform when system is updated
*
* \param elapsedTime Delta time used for the update
*/
void ParticleSystem::OnUpdate(float elapsedTime)
{
for (const Ndk::EntityHandle& entity : GetEntities())
{
ParticleGroupComponent& group = entity->GetComponent<ParticleGroupComponent>();
group.Update(elapsedTime);
}
}
SystemIndex ParticleSystem::systemIndex;
}

View File

@@ -1,428 +0,0 @@
// Copyright (C) 2020 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 <NazaraSDK/Systems/RenderSystem.hpp>
#include <Nazara/Graphics/ColorBackground.hpp>
#include <Nazara/Graphics/ForwardRenderTechnique.hpp>
#include <Nazara/Graphics/SceneData.hpp>
#include <Nazara/Graphics/SkinningManager.hpp>
#include <Nazara/Graphics/SkyboxBackground.hpp>
#include <Nazara/Math/Rect.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <NazaraSDK/Components/CameraComponent.hpp>
#include <NazaraSDK/Components/GraphicsComponent.hpp>
#include <NazaraSDK/Components/LightComponent.hpp>
#include <NazaraSDK/Components/NodeComponent.hpp>
#include <NazaraSDK/Components/ParticleGroupComponent.hpp>
namespace Ndk
{
/*!
* \ingroup NDK
* \class Ndk::RenderSystem
* \brief NDK class that represents the rendering system
*
* \remark This system is enabled if the entity is a 'camera' with the trait: CameraComponent and NodeComponent
* or a drawable element with trait: GraphicsComponent and NodeComponent
* or a light element with trait: LightComponent and NodeComponent
* or a set of particles with trait: ParticleGroupComponent
*/
/*!
* \brief Constructs an RenderSystem object by default
*/
RenderSystem::RenderSystem() :
m_coordinateSystemMatrix(Nz::Matrix4f::Identity()),
m_coordinateSystemInvalidated(true),
m_forceRenderQueueInvalidation(false),
m_isCullingEnabled(true)
{
ChangeRenderTechnique<Nz::ForwardRenderTechnique>();
SetDefaultBackground(Nz::ColorBackground::New());
SetUpdateOrder(100); //< Render last, after every movement is done
SetMaximumUpdateRate(0.f); //< We don't want any rate limit
}
/*!
* \brief Operation to perform when an entity is removed
*
* \param entity Pointer to the entity
*/
void RenderSystem::OnEntityRemoved(Entity* entity)
{
m_forceRenderQueueInvalidation = true; //< Hackfix until lights and particles are handled by culling list
for (auto it = m_cameras.begin(); it != m_cameras.end(); ++it)
{
if (it->GetObject() == entity)
{
m_cameras.erase(it);
break;
}
}
if (entity->HasComponent<GraphicsComponent>())
{
GraphicsComponent& gfxComponent = entity->GetComponent<GraphicsComponent>();
gfxComponent.RemoveFromCullingList(&m_drawableCulling);
}
}
/*!
* \brief Operation to perform when entity is validated for the system
*
* \param entity Pointer to the entity
* \param justAdded Is the entity newly added
*/
void RenderSystem::OnEntityValidation(Entity* entity, bool justAdded)
{
NazaraUnused(justAdded);
if (entity->HasComponent<CameraComponent>() && entity->HasComponent<NodeComponent>())
{
m_cameras.emplace_back(entity);
std::sort(m_cameras.begin(), m_cameras.end(), [](const EntityHandle& handle1, const EntityHandle& handle2)
{
return handle1->GetComponent<CameraComponent>().GetLayer() < handle2->GetComponent<CameraComponent>().GetLayer();
});
}
else
{
for (auto it = m_cameras.begin(); it != m_cameras.end(); ++it)
{
if (it->GetObject() == entity)
{
m_cameras.erase(it);
break;
}
}
}
if (entity->HasComponent<GraphicsComponent>() && entity->HasComponent<NodeComponent>())
{
m_drawables.Insert(entity);
GraphicsComponent& gfxComponent = entity->GetComponent<GraphicsComponent>();
if (justAdded)
gfxComponent.AddToCullingList(&m_drawableCulling);
if (gfxComponent.DoesRequireRealTimeReflections())
m_realtimeReflected.Insert(entity);
else
m_realtimeReflected.Remove(entity);
}
else
{
m_drawables.Remove(entity);
m_realtimeReflected.Remove(entity);
if (entity->HasComponent<GraphicsComponent>())
{
GraphicsComponent& gfxComponent = entity->GetComponent<GraphicsComponent>();
gfxComponent.RemoveFromCullingList(&m_drawableCulling);
}
}
if (entity->HasComponent<LightComponent>() && entity->HasComponent<NodeComponent>())
{
m_forceRenderQueueInvalidation = true; //< Hackfix until lights and particles are handled by culling list
LightComponent& lightComponent = entity->GetComponent<LightComponent>();
if (lightComponent.GetLightType() == Nz::LightType_Directional)
{
m_directionalLights.Insert(entity);
m_pointSpotLights.Remove(entity);
}
else
{
m_directionalLights.Remove(entity);
m_pointSpotLights.Insert(entity);
}
m_lights.Insert(entity);
}
else
{
m_forceRenderQueueInvalidation = true; //< Hackfix until lights and particles are handled by culling list
m_directionalLights.Remove(entity);
m_lights.Remove(entity);
m_pointSpotLights.Remove(entity);
}
if (entity->HasComponent<ParticleGroupComponent>())
{
m_forceRenderQueueInvalidation = true; //< Hackfix until lights and particles are handled by culling list
m_particleGroups.Insert(entity);
}
else
{
m_forceRenderQueueInvalidation = true; //< Hackfix until lights and particles are handled by culling list
m_particleGroups.Remove(entity);
}
}
/*!
* \brief Operation to perform when system is updated
*
* \param elapsedTime Delta time used for the update
*/
void RenderSystem::OnUpdate(float /*elapsedTime*/)
{
// Invalidate every renderable if the coordinate system changed
if (m_coordinateSystemInvalidated)
{
for (const Ndk::EntityHandle& drawable : m_drawables)
{
GraphicsComponent& graphicsComponent = drawable->GetComponent<GraphicsComponent>();
graphicsComponent.InvalidateTransformMatrix();
}
m_coordinateSystemInvalidated = false;
}
Nz::SkinningManager::Skin();
UpdateDynamicReflections();
UpdatePointSpotShadowMaps();
for (const Ndk::EntityHandle& camera : m_cameras)
{
CameraComponent& camComponent = camera->GetComponent<CameraComponent>();
//UpdateDirectionalShadowMaps(camComponent);
Nz::AbstractRenderQueue* renderQueue = m_renderTechnique->GetRenderQueue();
// To make sure the bounding volumes used by the culling list is updated
for (const Ndk::EntityHandle& drawable : m_drawables)
{
GraphicsComponent& graphicsComponent = drawable->GetComponent<GraphicsComponent>();
graphicsComponent.EnsureBoundingVolumesUpdate();
}
bool forceInvalidation = false;
const Nz::Frustumf& frustum = camComponent.GetFrustum();
std::size_t visibilityHash;
if (m_isCullingEnabled)
visibilityHash = m_drawableCulling.Cull(frustum, &forceInvalidation);
else
visibilityHash = m_drawableCulling.FillWithAllEntries(&forceInvalidation);
// Always regenerate renderqueue if particle groups are present for now (FIXME)
if (!m_lights.empty() || !m_particleGroups.empty())
forceInvalidation = true;
if (camComponent.UpdateVisibility(visibilityHash) || m_forceRenderQueueInvalidation || forceInvalidation)
{
renderQueue->Clear();
for (const GraphicsComponent* gfxComponent : m_drawableCulling.GetFullyVisibleResults())
gfxComponent->AddToRenderQueue(renderQueue);
for (const GraphicsComponent* gfxComponent : m_drawableCulling.GetPartiallyVisibleResults())
gfxComponent->AddToRenderQueueByCulling(frustum, renderQueue);
for (const Ndk::EntityHandle& light : m_lights)
{
LightComponent& lightComponent = light->GetComponent<LightComponent>();
NodeComponent& lightNode = light->GetComponent<NodeComponent>();
///TODO: Cache somehow?
lightComponent.AddToRenderQueue(renderQueue, Nz::Matrix4f::ConcatenateAffine(m_coordinateSystemMatrix, lightNode.GetTransformMatrix()));
}
for (const Ndk::EntityHandle& particleGroup : m_particleGroups)
{
ParticleGroupComponent& groupComponent = particleGroup->GetComponent<ParticleGroupComponent>();
groupComponent.AddToRenderQueue(renderQueue, Nz::Matrix4f::Identity()); //< ParticleGroup doesn't use any transform matrix (yet)
}
m_forceRenderQueueInvalidation = false;
}
camComponent.ApplyView();
Nz::SceneData sceneData;
sceneData.ambientColor = Nz::Color(25, 25, 25);
sceneData.background = m_background;
sceneData.globalReflectionTexture = nullptr;
sceneData.viewer = &camComponent;
if (m_background && m_background->GetBackgroundType() == Nz::BackgroundType_Skybox)
sceneData.globalReflectionTexture = static_cast<Nz::SkyboxBackground*>(m_background.Get())->GetTexture();
m_renderTechnique->Clear(sceneData);
m_renderTechnique->Draw(sceneData);
}
}
/*!
* \brief Updates the directional shadow maps according to the position of the viewer
*
* \param viewer Viewer of the scene
*/
void RenderSystem::UpdateDynamicReflections()
{
Nz::SceneData dummySceneData;
dummySceneData.ambientColor = Nz::Color(0, 0, 0);
dummySceneData.background = nullptr;
dummySceneData.viewer = nullptr; //< Depth technique doesn't require any viewer
for (const Ndk::EntityHandle& handle : m_realtimeReflected)
{
//NazaraWarning("Realtime reflected: #" + handle->ToString());
}
}
void RenderSystem::UpdateDirectionalShadowMaps(const Nz::AbstractViewer& /*viewer*/)
{
if (!m_shadowRT.IsValid())
m_shadowRT.Create();
Nz::SceneData dummySceneData;
dummySceneData.ambientColor = Nz::Color(0, 0, 0);
dummySceneData.background = nullptr;
dummySceneData.viewer = nullptr; //< Depth technique doesn't require any viewer
for (const Ndk::EntityHandle& light : m_directionalLights)
{
LightComponent& lightComponent = light->GetComponent<LightComponent>();
NodeComponent& lightNode = light->GetComponent<NodeComponent>();
if (!lightComponent.IsShadowCastingEnabled())
continue;
Nz::Vector2ui shadowMapSize(lightComponent.GetShadowMap()->GetSize());
m_shadowRT.AttachTexture(Nz::AttachmentPoint_Depth, 0, lightComponent.GetShadowMap());
Nz::Renderer::SetTarget(&m_shadowRT);
Nz::Renderer::SetViewport(Nz::Recti(0, 0, shadowMapSize.x, shadowMapSize.y));
Nz::AbstractRenderQueue* renderQueue = m_shadowTechnique.GetRenderQueue();
renderQueue->Clear();
///TODO: Culling
for (const Ndk::EntityHandle& drawable : m_drawables)
{
GraphicsComponent& graphicsComponent = drawable->GetComponent<GraphicsComponent>();
graphicsComponent.AddToRenderQueue(renderQueue);
}
///TODO: Cache the matrices in the light?
Nz::Renderer::SetMatrix(Nz::MatrixType_Projection, Nz::Matrix4f::Ortho(0.f, 100.f, 100.f, 0.f, 1.f, 100.f));
Nz::Renderer::SetMatrix(Nz::MatrixType_View, Nz::Matrix4f::ViewMatrix(lightNode.GetRotation() * Nz::Vector3f::Forward() * 100.f, lightNode.GetRotation()));
m_shadowTechnique.Clear(dummySceneData);
m_shadowTechnique.Draw(dummySceneData);
}
}
/*!
* \brief Updates the point spot shadow maps
*/
void RenderSystem::UpdatePointSpotShadowMaps()
{
if (!m_shadowRT.IsValid())
m_shadowRT.Create();
Nz::SceneData dummySceneData;
dummySceneData.ambientColor = Nz::Color(0, 0, 0);
dummySceneData.background = nullptr;
dummySceneData.viewer = nullptr; //< Depth technique doesn't require any viewer
for (const Ndk::EntityHandle& light : m_pointSpotLights)
{
LightComponent& lightComponent = light->GetComponent<LightComponent>();
NodeComponent& lightNode = light->GetComponent<NodeComponent>();
if (!lightComponent.IsShadowCastingEnabled())
continue;
Nz::Vector2ui shadowMapSize(lightComponent.GetShadowMap()->GetSize());
switch (lightComponent.GetLightType())
{
case Nz::LightType_Directional:
NazaraInternalError("Directional lights included in point/spot light list");
break;
case Nz::LightType_Point:
{
static Nz::Quaternionf rotations[6] =
{
Nz::Quaternionf::RotationBetween(Nz::Vector3f::Forward(), Nz::Vector3f::UnitX()), // CubemapFace_PositiveX
Nz::Quaternionf::RotationBetween(Nz::Vector3f::Forward(), -Nz::Vector3f::UnitX()), // CubemapFace_NegativeX
Nz::Quaternionf::RotationBetween(Nz::Vector3f::Forward(), -Nz::Vector3f::UnitY()), // CubemapFace_PositiveY
Nz::Quaternionf::RotationBetween(Nz::Vector3f::Forward(), Nz::Vector3f::UnitY()), // CubemapFace_NegativeY
Nz::Quaternionf::RotationBetween(Nz::Vector3f::Forward(), -Nz::Vector3f::UnitZ()), // CubemapFace_PositiveZ
Nz::Quaternionf::RotationBetween(Nz::Vector3f::Forward(), Nz::Vector3f::UnitZ()) // CubemapFace_NegativeZ
};
for (unsigned int face = 0; face < 6; ++face)
{
m_shadowRT.AttachTexture(Nz::AttachmentPoint_Depth, 0, lightComponent.GetShadowMap(), face);
Nz::Renderer::SetTarget(&m_shadowRT);
Nz::Renderer::SetViewport(Nz::Recti(0, 0, shadowMapSize.x, shadowMapSize.y));
///TODO: Cache the matrices in the light?
Nz::Renderer::SetMatrix(Nz::MatrixType_Projection, Nz::Matrix4f::Perspective(Nz::FromDegrees(90.f), 1.f, 0.1f, lightComponent.GetRadius()));
Nz::Renderer::SetMatrix(Nz::MatrixType_View, Nz::Matrix4f::ViewMatrix(lightNode.GetPosition(), rotations[face]));
Nz::AbstractRenderQueue* renderQueue = m_shadowTechnique.GetRenderQueue();
renderQueue->Clear();
///TODO: Culling
for (const Ndk::EntityHandle& drawable : m_drawables)
{
GraphicsComponent& graphicsComponent = drawable->GetComponent<GraphicsComponent>();
graphicsComponent.AddToRenderQueue(renderQueue);
}
m_shadowTechnique.Clear(dummySceneData);
m_shadowTechnique.Draw(dummySceneData);
}
break;
}
case Nz::LightType_Spot:
{
m_shadowRT.AttachTexture(Nz::AttachmentPoint_Depth, 0, lightComponent.GetShadowMap());
Nz::Renderer::SetTarget(&m_shadowRT);
Nz::Renderer::SetViewport(Nz::Recti(0, 0, shadowMapSize.x, shadowMapSize.y));
///TODO: Cache the matrices in the light?
Nz::Renderer::SetMatrix(Nz::MatrixType_Projection, Nz::Matrix4f::Perspective(lightComponent.GetOuterAngle()*2.f, 1.f, 0.1f, lightComponent.GetRadius()));
Nz::Renderer::SetMatrix(Nz::MatrixType_View, Nz::Matrix4f::ViewMatrix(lightNode.GetPosition(), lightNode.GetRotation()));
Nz::AbstractRenderQueue* renderQueue = m_shadowTechnique.GetRenderQueue();
renderQueue->Clear();
///TODO: Culling
for (const Ndk::EntityHandle& drawable : m_drawables)
{
GraphicsComponent& graphicsComponent = drawable->GetComponent<GraphicsComponent>();
graphicsComponent.AddToRenderQueue(renderQueue);
}
m_shadowTechnique.Clear(dummySceneData);
m_shadowTechnique.Draw(dummySceneData);
break;
}
}
}
}
SystemIndex RenderSystem::systemIndex;
}

View File

@@ -1,489 +0,0 @@
// Copyright (C) 2020 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 <NazaraSDK/Widgets/AbstractTextAreaWidget.hpp>
#include <Nazara/Core/Unicode.hpp>
#include <Nazara/Utility/Font.hpp>
#include <NazaraSDK/Components/GraphicsComponent.hpp>
#include <NazaraSDK/Components/NodeComponent.hpp>
namespace Ndk
{
namespace
{
constexpr float paddingWidth = 5.f;
constexpr float paddingHeight = 3.f;
}
AbstractTextAreaWidget::AbstractTextAreaWidget(BaseWidget* parent) :
BaseWidget(parent),
m_characterFilter(),
m_echoMode(EchoMode_Normal),
m_cursorPositionBegin(0U, 0U),
m_cursorPositionEnd(0U, 0U),
m_isLineWrapEnabled(false),
m_isMouseButtonDown(false),
m_multiLineEnabled(false),
m_readOnly(false),
m_tabEnabled(false)
{
m_textSprite = Nz::TextSprite::New();
m_textEntity = CreateEntity();
m_textEntity->AddComponent<GraphicsComponent>().Attach(m_textSprite);
auto& textNode = m_textEntity->AddComponent<NodeComponent>();
textNode.SetParent(this);
textNode.SetPosition(paddingWidth, paddingHeight);
m_cursorEntity = CreateEntity();
m_cursorEntity->AddComponent<GraphicsComponent>();
m_cursorEntity->AddComponent<NodeComponent>().SetParent(m_textEntity);
m_cursorEntity->GetComponent<NodeComponent>();
m_cursorEntity->Enable(false);
SetCursor(Nz::SystemCursor_Text);
EnableBackground(true);
}
void AbstractTextAreaWidget::Clear()
{
Nz::AbstractTextDrawer& textDrawer = GetTextDrawer();
m_cursorPositionBegin.MakeZero();
m_cursorPositionEnd.MakeZero();
textDrawer.Clear();
m_textSprite->Update(textDrawer);
SetPreferredSize(Nz::Vector2f(m_textSprite->GetBoundingVolume().obb.localBox.GetLengths()));
RefreshCursor();
}
void AbstractTextAreaWidget::EnableLineWrap(bool enable)
{
if (m_isLineWrapEnabled != enable)
{
m_isLineWrapEnabled = enable;
Nz::AbstractTextDrawer& textDrawer = GetTextDrawer();
if (enable)
textDrawer.SetMaxLineWidth(GetWidth());
else
textDrawer.SetMaxLineWidth(std::numeric_limits<float>::infinity());
UpdateTextSprite();
}
}
Nz::Vector2ui AbstractTextAreaWidget::GetHoveredGlyph(float x, float y) const
{
const Nz::AbstractTextDrawer& textDrawer = GetTextDrawer();
auto& textNode = m_textEntity->GetComponent<Ndk::NodeComponent>();
Nz::Vector2f textPosition = Nz::Vector2f(textNode.GetPosition(Nz::CoordSys_Local));
x -= textPosition.x;
y -= textPosition.y;
std::size_t glyphCount = textDrawer.GetGlyphCount();
if (glyphCount > 0)
{
std::size_t lineCount = textDrawer.GetLineCount();
std::size_t line = 0U;
for (; line < lineCount - 1; ++line)
{
Nz::Rectf lineBounds = textDrawer.GetLine(line).bounds;
if (lineBounds.GetMaximum().y > y)
break;
}
std::size_t upperLimit = (line != lineCount - 1) ? textDrawer.GetLine(line + 1).glyphIndex : glyphCount + 1;
std::size_t firstLineGlyph = textDrawer.GetLine(line).glyphIndex;
std::size_t i = firstLineGlyph;
for (; i < upperLimit - 1; ++i)
{
Nz::Rectf bounds = textDrawer.GetGlyph(i).bounds;
if (x < bounds.x + bounds.width * 0.75f)
break;
}
return Nz::Vector2ui(Nz::Vector2<std::size_t>(i - firstLineGlyph, line));
}
return Nz::Vector2ui::Zero();
}
void AbstractTextAreaWidget::Layout()
{
BaseWidget::Layout();
if (m_isLineWrapEnabled)
{
Nz::AbstractTextDrawer& textDrawer = GetTextDrawer();
textDrawer.SetMaxLineWidth(GetWidth());
UpdateTextSprite();
}
RefreshCursor();
}
bool AbstractTextAreaWidget::IsFocusable() const
{
return !m_readOnly;
}
void AbstractTextAreaWidget::OnFocusLost()
{
m_cursorEntity->Disable();
}
void AbstractTextAreaWidget::OnFocusReceived()
{
if (!m_readOnly)
m_cursorEntity->Enable(true);
}
bool AbstractTextAreaWidget::OnKeyPressed(const Nz::WindowEvent::KeyEvent& key)
{
const Nz::AbstractTextDrawer& textDrawer = GetTextDrawer();
switch (key.virtualKey)
{
case Nz::Keyboard::VKey::Backspace:
{
bool ignoreDefaultAction = false;
OnTextAreaKeyBackspace(this, &ignoreDefaultAction);
std::size_t cursorGlyphEnd = GetGlyphIndex(m_cursorPositionEnd);
if (ignoreDefaultAction || cursorGlyphEnd == 0)
return true;
// When a text is selected, delete key does the same as delete and leave the character behind it
if (HasSelection())
EraseSelection();
else
{
MoveCursor(-1);
Erase(GetGlyphIndex(m_cursorPositionBegin));
}
return true;
}
case Nz::Keyboard::VKey::Delete:
{
if (HasSelection())
EraseSelection();
else
Erase(GetGlyphIndex(m_cursorPositionBegin));
return true;
}
case Nz::Keyboard::VKey::Down:
{
bool ignoreDefaultAction = false;
OnTextAreaKeyDown(this, &ignoreDefaultAction);
if (ignoreDefaultAction)
return true;
if (HasSelection())
SetCursorPosition(m_cursorPositionEnd);
MoveCursor({0, 1});
return true;
}
case Nz::Keyboard::VKey::End:
{
bool ignoreDefaultAction = false;
OnTextAreaKeyEnd(this, &ignoreDefaultAction);
if (ignoreDefaultAction)
return true;
std::size_t lineCount = textDrawer.GetLineCount();
if (key.control && lineCount > 0)
SetCursorPosition({ static_cast<unsigned int>(textDrawer.GetLineGlyphCount(lineCount - 1)), static_cast<unsigned int>(lineCount - 1) });
else
SetCursorPosition({ static_cast<unsigned int>(textDrawer.GetLineGlyphCount(m_cursorPositionEnd.y)), m_cursorPositionEnd.y });
return true;
}
case Nz::Keyboard::VKey::Home:
{
bool ignoreDefaultAction = false;
OnTextAreaKeyHome(this, &ignoreDefaultAction);
if (ignoreDefaultAction)
return true;
SetCursorPosition({ 0U, key.control ? 0U : m_cursorPositionEnd.y });
return true;
}
case Nz::Keyboard::VKey::Left:
{
bool ignoreDefaultAction = false;
OnTextAreaKeyLeft(this, &ignoreDefaultAction);
if (ignoreDefaultAction)
return true;
if (HasSelection())
SetCursorPosition(m_cursorPositionBegin);
else if (key.control)
HandleWordCursorMove(true);
else
MoveCursor(-1);
return true;
}
case Nz::Keyboard::VKey::Return:
{
bool ignoreDefaultAction = false;
OnTextAreaKeyReturn(this, &ignoreDefaultAction);
if (ignoreDefaultAction)
return true;
if (!m_multiLineEnabled)
break;
if (HasSelection())
EraseSelection();
Write(Nz::String('\n'));
return true;;
}
case Nz::Keyboard::VKey::Right:
{
bool ignoreDefaultAction = false;
OnTextAreaKeyRight(this, &ignoreDefaultAction);
if (ignoreDefaultAction)
return true;
if (HasSelection())
SetCursorPosition(m_cursorPositionEnd);
else if (key.control)
HandleWordCursorMove(false);
else
MoveCursor(1);
return true;
}
case Nz::Keyboard::VKey::Up:
{
bool ignoreDefaultAction = false;
OnTextAreaKeyUp(this, &ignoreDefaultAction);
if (ignoreDefaultAction)
return true;
if (HasSelection())
SetCursorPosition(m_cursorPositionBegin);
MoveCursor({0, -1});
return true;
}
case Nz::Keyboard::VKey::Tab:
{
if (!m_tabEnabled)
return false;
if (HasSelection())
HandleSelectionIndentation(!key.shift);
else
HandleIndentation(!key.shift);
return true;
}
default:
break;
}
return false;
}
void AbstractTextAreaWidget::OnKeyReleased(const Nz::WindowEvent::KeyEvent& /*key*/)
{
}
void AbstractTextAreaWidget::OnMouseButtonPress(int x, int y, Nz::Mouse::Button button)
{
if (button == Nz::Mouse::Left)
{
SetFocus();
Nz::Vector2ui hoveredGlyph = GetHoveredGlyph(float(x), float(y));
// Shift extends selection
if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::LShift) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::RShift))
SetSelection(hoveredGlyph, m_selectionCursor);
else
{
SetCursorPosition(hoveredGlyph);
m_selectionCursor = m_cursorPositionBegin;
}
m_isMouseButtonDown = true;
}
}
void AbstractTextAreaWidget::OnMouseButtonRelease(int, int, Nz::Mouse::Button button)
{
if (button == Nz::Mouse::Left)
m_isMouseButtonDown = false;
}
void AbstractTextAreaWidget::OnMouseEnter()
{
if (!Nz::Mouse::IsButtonPressed(Nz::Mouse::Left))
m_isMouseButtonDown = false;
}
void AbstractTextAreaWidget::OnMouseMoved(int x, int y, int deltaX, int deltaY)
{
if (m_isMouseButtonDown)
SetSelection(m_selectionCursor, GetHoveredGlyph(float(x), float(y)));
}
void AbstractTextAreaWidget::OnTextEntered(char32_t character, bool /*repeated*/)
{
if (m_readOnly)
return;
if (Nz::Unicode::GetCategory(character) == Nz::Unicode::Category_Other_Control || (m_characterFilter && !m_characterFilter(character)))
return;
if (HasSelection())
EraseSelection();
Write(Nz::String::Unicode(character));
}
void AbstractTextAreaWidget::RefreshCursor()
{
if (m_readOnly)
return;
const Nz::AbstractTextDrawer& textDrawer = GetTextDrawer();
auto GetGlyph = [&](const Nz::Vector2ui& glyphPosition, std::size_t* glyphIndex) -> const Nz::AbstractTextDrawer::Glyph*
{
if (glyphPosition.y >= textDrawer.GetLineCount())
return nullptr;
const auto& lineInfo = textDrawer.GetLine(glyphPosition.y);
std::size_t cursorGlyph = GetGlyphIndex({ glyphPosition.x, glyphPosition.y });
if (glyphIndex)
*glyphIndex = cursorGlyph;
std::size_t glyphCount = textDrawer.GetGlyphCount();
if (glyphCount > 0 && lineInfo.glyphIndex < cursorGlyph)
{
const auto& glyph = textDrawer.GetGlyph(std::min(cursorGlyph, glyphCount - 1));
return &glyph;
}
else
return nullptr;
};
// Move text so that cursor is always visible
const auto* lastGlyph = GetGlyph(m_cursorPositionEnd, nullptr);
float glyphPos = (lastGlyph) ? lastGlyph->bounds.x : 0.f;
float glyphWidth = (lastGlyph) ? lastGlyph->bounds.width : 0.f;
auto& node = m_textEntity->GetComponent<Ndk::NodeComponent>();
float textPosition = node.GetPosition(Nz::CoordSys_Local).x - paddingWidth;
float cursorPosition = glyphPos + textPosition;
float width = GetWidth();
if (width <= textDrawer.GetBounds().width)
{
if (cursorPosition + glyphWidth > width)
node.Move(width - cursorPosition - glyphWidth, 0.f);
else if (cursorPosition - glyphWidth < 0.f)
node.Move(-cursorPosition + glyphWidth, 0.f);
}
else
node.Move(-textPosition, 0.f); // Reset text position if we have enough room to show everything
// Show cursor/selection
std::size_t selectionLineCount = m_cursorPositionEnd.y - m_cursorPositionBegin.y + 1;
std::size_t oldSpriteCount = m_cursorSprites.size();
if (m_cursorSprites.size() != selectionLineCount)
{
m_cursorSprites.resize(m_cursorPositionEnd.y - m_cursorPositionBegin.y + 1);
for (std::size_t i = oldSpriteCount; i < m_cursorSprites.size(); ++i)
{
m_cursorSprites[i] = Nz::Sprite::New();
m_cursorSprites[i]->SetMaterial(Nz::Material::New("Translucent2D"));
}
}
GraphicsComponent& gfxComponent = m_cursorEntity->GetComponent<GraphicsComponent>();
gfxComponent.Clear();
for (unsigned int i = m_cursorPositionBegin.y; i <= m_cursorPositionEnd.y; ++i)
{
const auto& lineInfo = textDrawer.GetLine(i);
Nz::SpriteRef& cursorSprite = m_cursorSprites[i - m_cursorPositionBegin.y];
if (i == m_cursorPositionBegin.y || i == m_cursorPositionEnd.y)
{
auto GetGlyphPos = [&](const Nz::Vector2ui& glyphPosition)
{
std::size_t glyphIndex;
const auto* glyph = GetGlyph(glyphPosition, &glyphIndex);
if (glyph)
{
float position = glyph->bounds.x;
if (glyphIndex >= textDrawer.GetGlyphCount())
position += glyph->bounds.width;
return position;
}
else
return 0.f;
};
float beginX = (i == m_cursorPositionBegin.y) ? GetGlyphPos({ m_cursorPositionBegin.x, i }) : 0.f;
float endX = (i == m_cursorPositionEnd.y) ? GetGlyphPos({ m_cursorPositionEnd.x, i }) : lineInfo.bounds.width;
float spriteSize = std::max(endX - beginX, 1.f);
cursorSprite->SetColor((m_cursorPositionBegin == m_cursorPositionEnd) ? Nz::Color::Black : Nz::Color(0, 0, 0, 50));
cursorSprite->SetSize(spriteSize, lineInfo.bounds.height);
gfxComponent.Attach(cursorSprite, Nz::Matrix4f::Translate({ beginX, lineInfo.bounds.y, 0.f }));
}
else
{
cursorSprite->SetColor(Nz::Color(0, 0, 0, 50));
cursorSprite->SetSize(lineInfo.bounds.width, lineInfo.bounds.height);
gfxComponent.Attach(cursorSprite, Nz::Matrix4f::Translate({ 0.f, lineInfo.bounds.y, 0.f }));
}
}
}
void AbstractTextAreaWidget::UpdateTextSprite()
{
m_textSprite->Update(GetTextDrawer());
SetPreferredSize(Nz::Vector2f(m_textSprite->GetBoundingVolume().obb.localBox.GetLengths()));
}
}

View File

@@ -1,122 +0,0 @@
// Copyright (C) 2020 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 <NazaraSDK/Widgets/BoxLayout.hpp>
#include <Nazara/Core/Log.hpp>
#include <Nazara/Core/MemoryHelper.hpp>
#include <cassert>
namespace Ndk
{
void BoxLayout::Layout()
{
std::size_t axis1, axis2;
switch (m_orientation)
{
case BoxLayoutOrientation_Horizontal:
axis1 = 0; //< x
axis2 = 1; //< y
break;
case BoxLayoutOrientation_Vertical:
axis1 = 1; //< y
axis2 = 0; //< x
break;
default:
assert(false);
break;
}
m_childInfos.clear();
// Handle size
ForEachWidgetChild([&](BaseWidget* child)
{
if (!child->IsVisible())
return;
m_childInfos.emplace_back();
auto& info = m_childInfos.back();
info.isConstrained = false;
info.maximumSize = child->GetMaximumSize()[axis1];
info.minimumSize = child->GetMinimumSize()[axis1];
info.size = info.minimumSize;
info.widget = child;
});
Nz::Vector2f layoutSize = GetSize();
float availableSpace = layoutSize[axis1] - m_spacing * (m_childInfos.size() - 1);
float remainingSize = availableSpace;
for (auto& info : m_childInfos)
remainingSize -= info.minimumSize;
// Okay this algorithm is FAR from perfect but I couldn't figure a way other than this one
std::size_t unconstrainedChildCount = m_childInfos.size();
bool hasUnconstrainedChilds = false;
for (std::size_t i = 0; i < m_childInfos.size(); ++i)
{
if (remainingSize <= 0.0001f)
break;
float evenSize = remainingSize / unconstrainedChildCount;
for (auto& info : m_childInfos)
{
if (info.isConstrained)
continue;
float previousSize = info.size;
info.size += evenSize;
if (info.size > info.maximumSize)
{
unconstrainedChildCount--;
evenSize += (info.size - info.maximumSize) / unconstrainedChildCount;
info.isConstrained = true;
info.size = info.maximumSize;
}
else
hasUnconstrainedChilds = true;
remainingSize -= info.size - previousSize;
}
if (!hasUnconstrainedChilds)
break;
}
float spacing = m_spacing + remainingSize / (m_childInfos.size() - 1);
for (auto& info : m_childInfos)
{
Nz::Vector2f newSize = info.widget->GetSize();
newSize[axis1] = info.size;
info.widget->Resize(newSize);
}
// Handle position
float cursor = 0.f;
bool first = true;
for (auto& info : m_childInfos)
{
if (first)
first = false;
else
cursor += spacing;
Nz::Vector2f position = Nz::Vector2f(0.f, 0.f);
position[axis1] = cursor;
info.widget->SetPosition(position);
cursor += info.size;
};
}
}

View File

@@ -1,126 +0,0 @@
// Copyright (C) 2020 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 <NazaraSDK/Widgets/ButtonWidget.hpp>
#include <NazaraSDK/Components/GraphicsComponent.hpp>
#include <NazaraSDK/Components/NodeComponent.hpp>
namespace Ndk
{
Nz::Color ButtonWidget::s_color { 74, 74, 74 };
Nz::Color ButtonWidget::s_cornerColor { 180, 180, 180 };
Nz::Color ButtonWidget::s_hoverColor { 128, 128, 128 };
Nz::Color ButtonWidget::s_hoverCornerColor { s_cornerColor };
Nz::Color ButtonWidget::s_pressColor { s_cornerColor };
Nz::Color ButtonWidget::s_pressCornerColor { s_color };
ButtonWidget::ButtonWidget(BaseWidget* parent) :
BaseWidget(parent),
m_color { s_color },
m_cornerColor { s_cornerColor },
m_hoverColor { s_hoverColor },
m_hoverCornerColor { s_hoverCornerColor },
m_pressColor { s_pressColor },
m_pressCornerColor { s_pressCornerColor }
{
m_gradientSprite = Nz::Sprite::New();
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();
m_gradientEntity->AddComponent<NodeComponent>().SetParent(this);
m_gradientEntity->AddComponent<GraphicsComponent>().Attach(m_gradientSprite);
m_textSprite = Nz::TextSprite::New();
m_textEntity = CreateEntity();
m_textEntity->AddComponent<NodeComponent>().SetParent(this);
m_textEntity->AddComponent<GraphicsComponent>().Attach(m_textSprite, 1);
Layout();
}
const Nz::Color& ButtonWidget::GetDefaultColor()
{
return s_color;
}
const Nz::Color& ButtonWidget::GetDefaultCornerColor()
{
return s_cornerColor;
}
const Nz::Color& ButtonWidget::GetDefaultHoverColor()
{
return s_hoverColor;
}
const Nz::Color& ButtonWidget::GetDefaultHoverCornerColor()
{
return s_hoverCornerColor;
}
const Nz::Color& ButtonWidget::GetDefaultPressColor()
{
return s_pressColor;
}
const Nz::Color& ButtonWidget::GetDefaultPressCornerColor()
{
return s_pressCornerColor;
}
void ButtonWidget::Layout()
{
BaseWidget::Layout();
Nz::Vector2f size = GetSize();
m_gradientSprite->SetSize(size);
Nz::Boxf textBox = m_textEntity->GetComponent<GraphicsComponent>().GetAABB();
m_textEntity->GetComponent<NodeComponent>().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);
}
}

View File

@@ -1,178 +0,0 @@
// Copyright (C) 2017 Samy Bensaid
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
#include <NazaraSDK/Widgets/CheckboxWidget.hpp>
#include <NazaraSDK/Components/GraphicsComponent.hpp>
#include <Nazara/Graphics/Material.hpp>
#include <algorithm>
namespace Ndk
{
Nz::Color CheckboxWidget::s_backgroundColor { Nz::Color::White };
Nz::Color CheckboxWidget::s_disabledBackgroundColor { 201, 201, 201 };
Nz::Color CheckboxWidget::s_disabledBorderColor { 62, 62, 62 };
Nz::Color CheckboxWidget::s_borderColor { Nz::Color::Black };
float CheckboxWidget::s_borderScale { 16.f };
CheckboxWidget::CheckboxWidget(BaseWidget* parent) :
BaseWidget(parent),
m_adaptativeMargin { true },
m_checkboxEnabled { true },
m_tristateEnabled { false },
m_textMargin { 16.f },
m_state { CheckboxState_Unchecked }
{
m_checkboxBorderSprite = Nz::Sprite::New(Nz::Material::New("Basic2D"));
m_checkboxBackgroundSprite = Nz::Sprite::New(Nz::Material::New("Basic2D"));
m_checkboxContentSprite = Nz::Sprite::New(Nz::Material::New("Translucent2D"));
m_textSprite = Nz::TextSprite::New();
m_checkboxBorderEntity = CreateEntity();
m_checkboxBorderEntity->AddComponent<NodeComponent>().SetParent(this);
m_checkboxBorderEntity->AddComponent<GraphicsComponent>().Attach(m_checkboxBorderSprite);
m_checkboxBackgroundEntity = CreateEntity();
m_checkboxBackgroundEntity->AddComponent<NodeComponent>().SetParent(this);
m_checkboxBackgroundEntity->AddComponent<GraphicsComponent>().Attach(m_checkboxBackgroundSprite, 1);
m_checkboxContentEntity = CreateEntity();
m_checkboxContentEntity->AddComponent<NodeComponent>().SetParent(this);
m_checkboxContentEntity->AddComponent<GraphicsComponent>().Attach(m_checkboxContentSprite, 2);
m_textEntity = CreateEntity();
m_textEntity->AddComponent<NodeComponent>().SetParent(this);
m_textEntity->AddComponent<GraphicsComponent>().Attach(m_textSprite);
m_checkMark = Nz::TextureLibrary::Get("Ndk::CheckboxWidget::checkmark");
SetCheckboxSize({ 32.f, 32.f });
UpdateCheckbox();
}
bool CheckboxWidget::Initialize()
{
const Nz::UInt8 r_checkmark[] =
{
#include <NazaraSDK/Resources/checkmark.png.h>
};
Nz::TextureRef checkmarkTexture = Nz::Texture::LoadFromMemory(r_checkmark, sizeof(r_checkmark) / sizeof(r_checkmark[0]));
if (!checkmarkTexture)
{
NazaraError("Failed to load embedded checkmark");
return false;
}
Nz::TextureLibrary::Register("Ndk::CheckboxWidget::checkmark", std::move(checkmarkTexture));
return true;
}
void CheckboxWidget::Uninitialize()
{
Nz::TextureLibrary::Unregister("Ndk::CheckboxWidget::checkmark");
}
void CheckboxWidget::SetState(CheckboxState state)
{
if (!m_checkboxEnabled)
return;
if (state == CheckboxState_Tristate)
m_tristateEnabled = true;
m_state = state;
UpdateCheckbox();
}
CheckboxState CheckboxWidget::SwitchToNextState()
{
if (!m_checkboxEnabled)
return m_state;
switch (m_state)
{
case CheckboxState_Unchecked:
SetState(CheckboxState_Checked);
break;
case CheckboxState_Checked:
SetState(m_tristateEnabled ? CheckboxState_Tristate : CheckboxState_Unchecked);
break;
case CheckboxState_Tristate:
SetState(CheckboxState_Unchecked);
break;
}
return m_state;
}
void CheckboxWidget::Layout()
{
BaseWidget::Layout();
Nz::Vector2f checkboxSize = GetCheckboxSize();
Nz::Vector2f borderSize = GetCheckboxBorderSize();
m_checkboxBackgroundEntity->GetComponent<NodeComponent>().SetPosition(borderSize);
Nz::Vector3f checkboxBox = m_checkboxContentSprite->GetBoundingVolume().obb.localBox.GetLengths();
m_checkboxContentEntity->GetComponent<NodeComponent>().SetPosition(checkboxSize.x / 2.f - checkboxBox.x / 2.f, checkboxSize.y / 2.f - checkboxBox.y / 2.f);
Nz::Vector3f textBox = m_textSprite->GetBoundingVolume().obb.localBox.GetLengths();
m_textEntity->GetComponent<NodeComponent>().SetPosition(checkboxSize.x + (m_adaptativeMargin ? checkboxSize.x / 2.f : m_textMargin), checkboxSize.y / 2.f - textBox.y / 2.f);
}
void CheckboxWidget::OnMouseButtonRelease(int x, int y, Nz::Mouse::Button button)
{
if (button == Nz::Mouse::Left && ContainsCheckbox(x, y) && IsCheckboxEnabled())
{
SwitchToNextState();
OnStateChanged(this);
}
}
void CheckboxWidget::UpdateCheckbox()
{
if (m_checkboxEnabled)
{
m_checkboxBorderSprite->SetColor(s_borderColor);
m_checkboxBackgroundSprite->SetColor(s_backgroundColor);
}
else
{
m_checkboxBorderSprite->SetColor(s_disabledBorderColor);
m_checkboxBackgroundSprite->SetColor(s_disabledBackgroundColor);
}
if (m_state == CheckboxState_Unchecked)
{
m_checkboxContentEntity->Enable(false);
return;
}
else if (m_state == CheckboxState_Checked)
{
m_checkboxContentEntity->Enable();
m_checkboxContentSprite->SetColor(Nz::Color::White);
m_checkboxContentSprite->SetTexture(m_checkMark, false);
}
else // Tristate
{
m_checkboxContentEntity->Enable();
m_checkboxContentSprite->SetColor(Nz::Color::Black);
m_checkboxContentSprite->SetTexture(Nz::TextureRef {});
}
}
void CheckboxWidget::UpdateSize()
{
Nz::Vector3f textSize = m_textSprite->GetBoundingVolume().obb.localBox.GetLengths();
Nz::Vector2f checkboxSize = GetCheckboxSize();
Nz::Vector2f finalSize { checkboxSize.x + (m_adaptativeMargin ? checkboxSize.x / 2.f : m_textMargin) + textSize.x, std::max(textSize.y, checkboxSize.y) };
SetMinimumSize(finalSize);
SetPreferredSize(finalSize);
}
}

View File

@@ -1,28 +0,0 @@
// Copyright (C) 2017 Samy Bensaid
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
#include <NazaraSDK/Widgets/ImageWidget.hpp>
#include <NazaraSDK/Components/NodeComponent.hpp>
#include <NazaraSDK/Components/GraphicsComponent.hpp>
namespace Ndk
{
ImageWidget::ImageWidget(BaseWidget* parent) :
BaseWidget(parent)
{
m_entity = CreateEntity();
m_entity->AddComponent<NodeComponent>().SetParent(this);
auto& gfx = m_entity->AddComponent<GraphicsComponent>();
m_sprite = Nz::Sprite::New();
gfx.Attach(m_sprite);
}
void ImageWidget::Layout()
{
BaseWidget::Layout();
m_sprite->SetSize(GetSize());
}
}

View File

@@ -1,22 +0,0 @@
// Copyright (C) 2020 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 <NazaraSDK/Widgets/LabelWidget.hpp>
#include <NazaraSDK/Components/GraphicsComponent.hpp>
#include <NazaraSDK/Components/NodeComponent.hpp>
namespace Ndk
{
LabelWidget::LabelWidget(BaseWidget* parent) :
BaseWidget(parent)
{
m_textSprite = Nz::TextSprite::New();
m_textEntity = CreateEntity();
m_textEntity->AddComponent<GraphicsComponent>().Attach(m_textSprite);
m_textEntity->AddComponent<NodeComponent>().SetParent(this);
Layout();
}
}

View File

@@ -1,100 +0,0 @@
// Copyright (C) 2017 Samy Bensaid
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
#include <NazaraSDK/Widgets/ProgressBarWidget.hpp>
#include <NazaraSDK/Components/NodeComponent.hpp>
#include <NazaraSDK/Components/GraphicsComponent.hpp>
namespace Ndk
{
float ProgressBarWidget::s_borderScale { 16.f };
Nz::Color ProgressBarWidget::s_borderColor { Nz::Color::Black };
Nz::Color ProgressBarWidget::s_barBackgroundColor { Nz::Color { 225, 225, 225 } };
Nz::Color ProgressBarWidget::s_barBackgroundCornerColor { Nz::Color { 255, 255, 255 } };
Nz::Color ProgressBarWidget::s_barColor { Nz::Color { 0, 225, 0 } };
Nz::Color ProgressBarWidget::s_barCornerColor { Nz::Color { 220, 255, 220 } };
ProgressBarWidget::ProgressBarWidget(BaseWidget* parent) :
BaseWidget(parent),
m_textColor { Nz::Color::Black },
m_textMargin { 16.f },
m_value { 0u }
{
m_borderSprite = Nz::Sprite::New(Nz::Material::New("Basic2D"));
m_barBackgroundSprite = Nz::Sprite::New(Nz::Material::New("Basic2D"));
m_barSprite = Nz::Sprite::New(Nz::Material::New("Basic2D"));
m_borderSprite->SetColor(s_borderColor);
SetBarBackgroundColor(s_barBackgroundColor, s_barBackgroundCornerColor);
SetBarColor(s_barColor, s_barCornerColor);
m_borderEntity = CreateEntity();
m_borderEntity->AddComponent<NodeComponent>().SetParent(this);
m_borderEntity->AddComponent<GraphicsComponent>().Attach(m_borderSprite);
m_barEntity = CreateEntity();
m_barEntity->AddComponent<NodeComponent>().SetParent(this);
GraphicsComponent& graphics = m_barEntity->AddComponent<GraphicsComponent>();
graphics.Attach(m_barBackgroundSprite, 1);
graphics.Attach(m_barSprite, 2);
m_textSprite = Nz::TextSprite::New();
m_textEntity = CreateEntity();
m_textEntity->AddComponent<NodeComponent>().SetParent(this);
m_textEntity->AddComponent<GraphicsComponent>().Attach(m_textSprite);
UpdateText();
Layout();
}
const Nz::Color& ProgressBarWidget::GetDefaultBarColor()
{
return s_barColor;
}
const Nz::Color& ProgressBarWidget::GetDefaultBarCornerColor()
{
return s_barCornerColor;
}
const Nz::Color& ProgressBarWidget::GetDefaultBarBackgroundColor()
{
return s_barBackgroundColor;
}
const Nz::Color& ProgressBarWidget::GetDefaultBarBackgroundCornerColor()
{
return s_barBackgroundCornerColor;
}
void ProgressBarWidget::Layout()
{
Nz::Vector2f size = GetSize();
Nz::Vector2f progressBarSize = size;
if (IsTextEnabled())
{
UpdateText();
Nz::Vector3f textSize = m_textSprite->GetBoundingVolume().obb.localBox.GetLengths();
m_textEntity->GetComponent<NodeComponent>().SetPosition(size.x - textSize.x, size.y / 2.f - textSize.y);
progressBarSize -= { textSize.x + m_textMargin, 0.f };
}
m_borderSprite->SetSize(progressBarSize);
Nz::Vector2f borderSize = GetProgressBarBorderSize();
m_barBackgroundSprite->SetSize(progressBarSize - (borderSize * 2.f));
m_barSprite->SetSize((progressBarSize.x - (borderSize.x * 2.f)) / 100.f * static_cast<float>(m_value), progressBarSize.y - (borderSize.y * 2.f));
m_barEntity->GetComponent<NodeComponent>().SetPosition(borderSize.x, borderSize.y);
}
}

View File

@@ -1,196 +0,0 @@
// Copyright (C) 2020 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 <NazaraSDK/Widgets/RichTextAreaWidget.hpp>
namespace Ndk
{
RichTextAreaWidget::RichTextAreaWidget(BaseWidget* parent) :
AbstractTextAreaWidget(parent)
{
Layout();
}
void RichTextAreaWidget::AppendText(const Nz::String& text)
{
//m_text += text;
switch (m_echoMode)
{
case EchoMode_Normal:
m_drawer.AppendText(text);
break;
case EchoMode_Password:
m_drawer.AppendText(Nz::String(text.GetLength(), '*'));
break;
case EchoMode_PasswordExceptLast:
{
/*m_drawer.Clear();
std::size_t textLength = m_text.GetLength();
if (textLength >= 2)
{
std::size_t lastCharacterPosition = m_text.GetCharacterPosition(textLength - 2);
if (lastCharacterPosition != Nz::String::npos)
m_drawer.AppendText(Nz::String(textLength - 1, '*'));
}
if (textLength >= 1)
m_drawer.AppendText(m_text.SubString(m_text.GetCharacterPosition(textLength - 1)));*/
break;
}
}
UpdateTextSprite();
//OnTextChanged(this, m_text);
}
void RichTextAreaWidget::Clear()
{
AbstractTextAreaWidget::Clear();
}
void RichTextAreaWidget::Erase(std::size_t firstGlyph, std::size_t lastGlyph)
{
if (firstGlyph > lastGlyph)
std::swap(firstGlyph, lastGlyph);
std::size_t textLength = m_drawer.GetGlyphCount();
if (firstGlyph > textLength)
return;
std::size_t firstBlock = m_drawer.FindBlock(firstGlyph);
std::size_t lastBlock = m_drawer.FindBlock((lastGlyph > 0) ? lastGlyph - 1 : lastGlyph);
if (firstBlock == lastBlock)
{
const Nz::String& blockText = m_drawer.GetBlockText(firstBlock);
std::size_t blockFirstGlyph = m_drawer.GetBlockFirstGlyphIndex(firstBlock);
Nz::String newText;
if (firstGlyph > blockFirstGlyph)
{
std::size_t characterPosition = blockText.GetCharacterPosition(firstGlyph - blockFirstGlyph);
NazaraAssert(characterPosition != Nz::String::npos, "Invalid character position");
newText.Append(blockText.SubString(0, characterPosition - 1));
}
if (lastGlyph < textLength)
newText.Append(blockText.SubString(blockText.GetCharacterPosition(lastGlyph - blockFirstGlyph)));
if (!newText.IsEmpty())
m_drawer.SetBlockText(firstBlock, std::move(newText));
else
m_drawer.RemoveBlock(firstBlock);
}
else
{
const Nz::String& lastBlockText = m_drawer.GetBlockText(lastBlock);
std::size_t lastBlockGlyphIndex = m_drawer.GetBlockFirstGlyphIndex(lastBlock);
// First, update/delete last block
std::size_t lastCharPos = lastBlockText.GetCharacterPosition(lastGlyph - lastBlockGlyphIndex);
if (lastCharPos != Nz::String::npos)
{
Nz::String newText = lastBlockText.SubString(lastCharPos);
if (!newText.IsEmpty())
m_drawer.SetBlockText(lastBlock, std::move(newText));
else
m_drawer.RemoveBlock(lastBlock);
}
// And then remove all middle blocks, remove in reverse order because of index shifting
assert(lastBlock > 0);
for (std::size_t i = lastBlock - 1; i > firstBlock; --i)
m_drawer.RemoveBlock(i);
const Nz::String& firstBlockText = m_drawer.GetBlockText(firstBlock);
std::size_t firstBlockGlyphIndex = m_drawer.GetBlockFirstGlyphIndex(firstBlock);
// And finally update/delete first block
if (firstGlyph > firstBlockGlyphIndex)
{
std::size_t firstCharPos = firstBlockText.GetCharacterPosition(firstGlyph - firstBlockGlyphIndex - 1);
if (firstCharPos != Nz::String::npos)
{
Nz::String newText = firstBlockText.SubString(0, firstCharPos);
if (!newText.IsEmpty())
m_drawer.SetBlockText(firstBlock, std::move(newText));
else
m_drawer.RemoveBlock(firstBlock);
}
}
else
m_drawer.RemoveBlock(firstBlock);
}
UpdateDisplayText();
}
void RichTextAreaWidget::Write(const Nz::String& text, std::size_t glyphPosition)
{
if (m_drawer.HasBlocks())
{
auto block = m_drawer.GetBlock(m_drawer.FindBlock((glyphPosition > 0) ? glyphPosition - 1 : glyphPosition));
std::size_t firstGlyph = block.GetFirstGlyphIndex();
assert(glyphPosition >= firstGlyph);
Nz::String blockText = block.GetText();
std::size_t characterPosition = blockText.GetCharacterPosition(glyphPosition - firstGlyph);
blockText.Insert(characterPosition, text);
block.SetText(blockText);
}
else
m_drawer.AppendText(text);
SetCursorPosition(glyphPosition + text.GetLength());
UpdateDisplayText();
}
Nz::AbstractTextDrawer& RichTextAreaWidget::GetTextDrawer()
{
return m_drawer;
}
const Nz::AbstractTextDrawer& RichTextAreaWidget::GetTextDrawer() const
{
return m_drawer;
}
void RichTextAreaWidget::HandleIndentation(bool add)
{
}
void RichTextAreaWidget::HandleSelectionIndentation(bool add)
{
}
void RichTextAreaWidget::HandleWordCursorMove(bool left)
{
}
void RichTextAreaWidget::UpdateDisplayText()
{
/*m_drawer.Clear();
switch (m_echoMode)
{
case EchoMode_Normal:
m_drawer.AppendText(m_text);
break;
case EchoMode_Password:
case EchoMode_PasswordExceptLast:
m_drawer.AppendText(Nz::String(m_text.GetLength(), '*'));
break;
}*/
UpdateTextSprite();
SetCursorPosition(m_cursorPositionBegin); //< Refresh cursor position (prevent it from being outside of the text)
}
}

View File

@@ -1,198 +0,0 @@
// Copyright (C) 2020 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 <NazaraSDK/Widgets/ScrollAreaWidget.hpp>
#include <Nazara/Math/Algorithm.hpp>
#include <NazaraSDK/Components/GraphicsComponent.hpp>
#include <NazaraSDK/Components/NodeComponent.hpp>
namespace Ndk
{
namespace
{
constexpr float scrollbarPadding = 5.f;
}
ScrollAreaWidget::ScrollAreaWidget(BaseWidget* parent, BaseWidget* content) :
BaseWidget(parent),
m_content(content),
m_scrollbarStatus(ScrollBarStatus::None),
m_isScrollbarEnabled(true),
m_scrollRatio(0.f)
{
m_content->SetParent(this);
m_content->SetPosition(Nz::Vector3f::Zero());
m_scrollbarBackgroundSprite = Nz::Sprite::New();
m_scrollbarBackgroundSprite->SetColor(Nz::Color(62, 62, 62));
m_scrollbarBackgroundEntity = CreateEntity();
m_scrollbarBackgroundEntity->AddComponent<NodeComponent>().SetParent(this);
m_scrollbarBackgroundEntity->AddComponent<GraphicsComponent>().Attach(m_scrollbarBackgroundSprite, 1);
m_scrollbarSprite = Nz::Sprite::New();
m_scrollbarSprite->SetColor(Nz::Color(104, 104, 104));
m_scrollbarEntity = CreateEntity();
m_scrollbarEntity->AddComponent<NodeComponent>().SetParent(this);
m_scrollbarEntity->AddComponent<GraphicsComponent>().Attach(m_scrollbarSprite);
Resize(m_content->GetSize());
}
void ScrollAreaWidget::EnableScrollbar(bool enable)
{
if (m_isScrollbarEnabled != enable)
{
m_isScrollbarEnabled = enable;
bool isVisible = IsScrollbarVisible();
m_scrollbarEntity->Enable(isVisible);
m_scrollbarBackgroundEntity->Enable(isVisible);
}
}
void ScrollAreaWidget::ScrollToRatio(float ratio)
{
m_scrollRatio = Nz::Clamp(ratio, 0.f, 1.f);
float widgetHeight = GetHeight();
float maxHeight = widgetHeight - m_scrollbarSprite->GetSize().y - 2.f * scrollbarPadding;
auto& scrollbarNode = m_scrollbarEntity->GetComponent<Ndk::NodeComponent>();
scrollbarNode.SetPosition(Nz::Vector2f(scrollbarNode.GetPosition(Nz::CoordSys_Local).x, scrollbarPadding + m_scrollRatio * maxHeight));
float contentPosition = m_scrollRatio * (widgetHeight - m_content->GetHeight());
m_content->SetPosition(0.f, contentPosition);
m_content->SetRenderingRect(Nz::Rectf(-std::numeric_limits<float>::infinity(), -contentPosition, std::numeric_limits<float>::infinity(), widgetHeight));
}
Nz::Rectf ScrollAreaWidget::GetScrollbarRect() const
{
Nz::Vector2f scrollBarPosition = Nz::Vector2f(m_scrollbarEntity->GetComponent<Ndk::NodeComponent>().GetPosition(Nz::CoordSys_Local));
Nz::Vector2f scrollBarSize = m_scrollbarSprite->GetSize();
return Nz::Rectf(scrollBarPosition.x, scrollBarPosition.y, scrollBarSize.x, scrollBarSize.y);
}
void ScrollAreaWidget::Layout()
{
constexpr float scrollBarBackgroundWidth = 20.f;
constexpr float scrollBarWidth = scrollBarBackgroundWidth - 2.f * scrollbarPadding;
float areaHeight = GetHeight();
float contentHeight = m_content->GetHeight();
if (contentHeight > areaHeight)
{
m_hasScrollbar = true;
Nz::Vector2f contentSize(GetWidth() - scrollBarBackgroundWidth, contentHeight);
m_content->Resize(contentSize);
if (m_isScrollbarEnabled)
{
m_scrollbarEntity->Enable();
m_scrollbarBackgroundEntity->Enable();
}
float scrollBarHeight = std::max(std::floor(areaHeight * (areaHeight / contentHeight)), 20.f);
m_scrollbarBackgroundSprite->SetSize(scrollBarBackgroundWidth, areaHeight);
m_scrollbarSprite->SetSize(scrollBarWidth, scrollBarHeight);
m_scrollbarBackgroundEntity->GetComponent<Ndk::NodeComponent>().SetPosition(contentSize.x, 0.f);
m_scrollbarEntity->GetComponent<Ndk::NodeComponent>().SetPosition(contentSize.x + (scrollBarBackgroundWidth - scrollBarWidth) / 2.f, 0.f);
ScrollToRatio(m_scrollRatio);
}
else
{
m_hasScrollbar = false;
m_content->Resize(GetSize());
m_scrollbarEntity->Disable();
m_scrollbarBackgroundEntity->Disable();
ScrollToRatio(0.f);
}
BaseWidget::Layout();
}
void ScrollAreaWidget::OnMouseButtonPress(int x, int y, Nz::Mouse::Button button)
{
if (button != Nz::Mouse::Left)
return;
if (m_scrollbarStatus == ScrollBarStatus::Hovered)
{
UpdateScrollbarStatus(ScrollBarStatus::Grabbed);
auto& scrollbarNode = m_scrollbarEntity->GetComponent<Ndk::NodeComponent>();
m_grabbedDelta.Set(x, int(y - scrollbarNode.GetPosition(Nz::CoordSys_Local).y));
}
}
void ScrollAreaWidget::OnMouseButtonRelease(int x, int y, Nz::Mouse::Button button)
{
if (button != Nz::Mouse::Left)
return;
if (m_scrollbarStatus == ScrollBarStatus::Grabbed)
{
Nz::Rectf scrollBarRect = GetScrollbarRect();
UpdateScrollbarStatus((scrollBarRect.Contains(Nz::Vector2f(float(x), float(y)))) ? ScrollBarStatus::Hovered : ScrollBarStatus::None);
}
}
void ScrollAreaWidget::OnMouseExit()
{
//if (m_scrollbarStatus == ScrollBarStatus::Hovered)
UpdateScrollbarStatus(ScrollBarStatus::None);
}
void ScrollAreaWidget::OnMouseMoved(int x, int y, int /*deltaX*/, int /*deltaY*/)
{
if (m_scrollbarStatus == ScrollBarStatus::Grabbed)
{
float height = GetHeight();
float maxHeight = height - m_scrollbarSprite->GetSize().y;
float newHeight = Nz::Clamp(float(y - m_grabbedDelta.y), 0.f, maxHeight);
ScrollToHeight(newHeight / maxHeight * m_content->GetHeight());
}
else
{
Nz::Rectf scrollBarRect = GetScrollbarRect();
UpdateScrollbarStatus((scrollBarRect.Contains(Nz::Vector2f(float(x), float(y)))) ? ScrollBarStatus::Hovered : ScrollBarStatus::None);
}
}
void ScrollAreaWidget::OnMouseWheelMoved(int /*x*/, int /*y*/, float delta)
{
constexpr float scrollStep = 100.f;
ScrollToHeight(GetScrollHeight() - scrollStep * delta);
}
void ScrollAreaWidget::UpdateScrollbarStatus(ScrollBarStatus status)
{
if (m_scrollbarStatus != status)
{
Nz::Color newColor;
switch (status)
{
case ScrollBarStatus::Grabbed: newColor = Nz::Color(235, 235, 235); break;
case ScrollBarStatus::Hovered: newColor = Nz::Color(152, 152, 152); break;
case ScrollBarStatus::None: newColor = Nz::Color(104, 104, 104); break;
}
m_scrollbarSprite->SetColor(newColor);
m_scrollbarStatus = status;
}
}
}

View File

@@ -1,253 +0,0 @@
// Copyright (C) 2020 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 <NazaraSDK/Widgets/TextAreaWidget.hpp>
#include <Nazara/Core/Unicode.hpp>
#include <Nazara/Utility/Font.hpp>
#include <NazaraSDK/Components/GraphicsComponent.hpp>
#include <NazaraSDK/Components/NodeComponent.hpp>
namespace Ndk
{
TextAreaWidget::TextAreaWidget(BaseWidget* parent) :
AbstractTextAreaWidget(parent)
{
SetCharacterSize(GetCharacterSize()); //< Actualize minimum / preferred size
Layout();
}
void TextAreaWidget::AppendText(const Nz::String& text)
{
m_text += text;
switch (m_echoMode)
{
case EchoMode_Normal:
m_drawer.AppendText(text);
break;
case EchoMode_Password:
m_drawer.AppendText(Nz::String(text.GetLength(), '*'));
break;
case EchoMode_PasswordExceptLast:
{
m_drawer.Clear();
std::size_t textLength = m_text.GetLength();
if (textLength >= 2)
{
std::size_t lastCharacterPosition = m_text.GetCharacterPosition(textLength - 2);
if (lastCharacterPosition != Nz::String::npos)
m_drawer.AppendText(Nz::String(textLength - 1, '*'));
}
if (textLength >= 1)
m_drawer.AppendText(m_text.SubString(m_text.GetCharacterPosition(textLength - 1)));
break;
}
}
UpdateTextSprite();
OnTextChanged(this, m_text);
}
void TextAreaWidget::Clear()
{
AbstractTextAreaWidget::Clear();
m_text.Clear();
OnTextChanged(this, m_text);
}
void TextAreaWidget::Erase(std::size_t firstGlyph, std::size_t lastGlyph)
{
if (firstGlyph > lastGlyph)
std::swap(firstGlyph, lastGlyph);
std::size_t textLength = m_text.GetLength();
if (firstGlyph > textLength)
return;
Nz::String newText;
if (firstGlyph > 0)
{
std::size_t characterPosition = m_text.GetCharacterPosition(firstGlyph);
NazaraAssert(characterPosition != Nz::String::npos, "Invalid character position");
newText.Append(m_text.SubString(0, characterPosition - 1));
}
if (lastGlyph < textLength)
{
std::size_t characterPosition = m_text.GetCharacterPosition(lastGlyph);
NazaraAssert(characterPosition != Nz::String::npos, "Invalid character position");
newText.Append(m_text.SubString(characterPosition));
}
SetText(newText);
}
void TextAreaWidget::Write(const Nz::String& text, std::size_t glyphPosition)
{
if (glyphPosition >= m_drawer.GetGlyphCount())
{
// It's faster to append than to insert in the middle
AppendText(text);
SetCursorPosition(m_drawer.GetGlyphCount());
}
else
{
m_text.Insert(m_text.GetCharacterPosition(glyphPosition), text);
SetText(m_text);
SetCursorPosition(glyphPosition + text.GetLength());
}
}
Nz::AbstractTextDrawer& TextAreaWidget::GetTextDrawer()
{
return m_drawer;
}
const Nz::AbstractTextDrawer& TextAreaWidget::GetTextDrawer() const
{
return m_drawer;
}
void TextAreaWidget::HandleIndentation(bool add)
{
if (add)
Write(Nz::String('\t'));
else
{
std::size_t currentGlyph = GetGlyphIndex(m_cursorPositionBegin);
if (currentGlyph > 0 && m_text[m_text.GetCharacterPosition(currentGlyph - 1U)] == '\t') // Check if previous glyph is a tab
{
Erase(currentGlyph - 1U);
if (m_cursorPositionBegin.x < static_cast<unsigned int>(m_drawer.GetLineGlyphCount(m_cursorPositionBegin.y)))
MoveCursor(-1);
}
}
}
void TextAreaWidget::HandleSelectionIndentation(bool add)
{
for (unsigned line = m_cursorPositionBegin.y; line <= m_cursorPositionEnd.y; ++line)
{
const Nz::Vector2ui cursorPositionBegin = m_cursorPositionBegin;
const Nz::Vector2ui cursorPositionEnd = m_cursorPositionEnd;
if (add)
{
Write(Nz::String('\t'), { 0U, line });
SetSelection(cursorPositionBegin + (cursorPositionBegin.y == line && cursorPositionBegin.x != 0U ? Nz::Vector2ui{ 1U, 0U } : Nz::Vector2ui{}),
cursorPositionEnd + (cursorPositionEnd.y == line ? Nz::Vector2ui{ 1U, 0U } : Nz::Vector2ui{}));
}
else
{
if (m_drawer.GetLineGlyphCount(line) == 0)
continue;
std::size_t firstGlyph = GetGlyphIndex({ 0U, line });
if (m_text[m_text.GetCharacterPosition(firstGlyph)] == '\t')
{
Erase(firstGlyph);
SetSelection(cursorPositionBegin - (cursorPositionBegin.y == line && cursorPositionBegin.x != 0U ? Nz::Vector2ui{ 1U, 0U } : Nz::Vector2ui{}),
cursorPositionEnd - (cursorPositionEnd.y == line && cursorPositionEnd.x != 0U ? Nz::Vector2ui{ 1U, 0U } : Nz::Vector2ui{}));
}
}
}
}
void TextAreaWidget::HandleWordCursorMove(bool left)
{
if (left)
{
std::size_t index = GetGlyphIndex(m_cursorPositionBegin);
if (index == 0)
return;
std::size_t spaceIndex = m_text.FindLast(' ', index - 2);
std::size_t endlIndex = m_text.FindLast('\n', index - 1);
if ((spaceIndex > endlIndex || endlIndex == Nz::String::npos) && spaceIndex != Nz::String::npos)
SetCursorPosition(spaceIndex + 1);
else if (endlIndex != Nz::String::npos)
{
if (index == endlIndex + 1)
SetCursorPosition(endlIndex);
else
SetCursorPosition(endlIndex + 1);
}
else
SetCursorPosition({ 0U, m_cursorPositionBegin.y });
}
else
{
std::size_t index = GetGlyphIndex(m_cursorPositionEnd);
std::size_t spaceIndex = m_text.Find(' ', index);
std::size_t endlIndex = m_text.Find('\n', index);
if (spaceIndex < endlIndex && spaceIndex != Nz::String::npos)
{
if (m_text.GetSize() > spaceIndex)
SetCursorPosition(spaceIndex + 1);
else
SetCursorPosition({ static_cast<unsigned int>(m_drawer.GetLineGlyphCount(m_cursorPositionEnd.y)), m_cursorPositionEnd.y });
}
else if (endlIndex != Nz::String::npos)
{
if (index == endlIndex)
SetCursorPosition(endlIndex + 1);
else
SetCursorPosition(endlIndex);
}
else
SetCursorPosition({ static_cast<unsigned int>(m_drawer.GetLineGlyphCount(m_cursorPositionEnd.y)), m_cursorPositionEnd.y });
}
}
void TextAreaWidget::UpdateDisplayText()
{
switch (m_echoMode)
{
case EchoMode_Normal:
m_drawer.SetText(m_text);
break;
case EchoMode_Password:
case EchoMode_PasswordExceptLast:
m_drawer.SetText(Nz::String(m_text.GetLength(), '*'));
break;
}
UpdateTextSprite();
SetCursorPosition(m_cursorPositionBegin); //< Refresh cursor position (prevent it from being outside of the text)
}
void TextAreaWidget::UpdateMinimumSize()
{
std::size_t fontCount = m_drawer.GetFontCount();
float lineHeight = 0;
int spaceAdvance = 0;
for (std::size_t i = 0; i < fontCount; ++i)
{
Nz::Font* font = m_drawer.GetFont(i);
const Nz::Font::SizeInfo& sizeInfo = font->GetSizeInfo(m_drawer.GetCharacterSize());
lineHeight = std::max(lineHeight, m_drawer.GetLineHeight());
spaceAdvance = std::max(spaceAdvance, sizeInfo.spaceAdvance);
}
Nz::Vector2f size = { float(spaceAdvance), lineHeight + 5.f };
SetMinimumSize(size);
}
}

View File

@@ -12,10 +12,7 @@
#include <NazaraSDK/Systems/VelocitySystem.hpp>
#ifndef NDK_SERVER
#include <NazaraSDK/Systems/DebugSystem.hpp>
#include <NazaraSDK/Systems/ListenerSystem.hpp>
#include <NazaraSDK/Systems/ParticleSystem.hpp>
#include <NazaraSDK/Systems/RenderSystem.hpp>
#endif
namespace Ndk
@@ -50,10 +47,7 @@ namespace Ndk
AddSystem<VelocitySystem>();
#ifndef NDK_SERVER
AddSystem<DebugSystem>();
AddSystem<ListenerSystem>();
AddSystem<ParticleSystem>();
AddSystem<RenderSystem>();
#endif
}