Merge branch 'master' into SDL2
This commit is contained in:
@@ -147,16 +147,22 @@ namespace Ndk
|
||||
|
||||
Nz::Vector2ui windowDimensions;
|
||||
if (info.window->IsValid())
|
||||
{
|
||||
windowDimensions = info.window->GetSize();
|
||||
windowDimensions.y /= 4;
|
||||
}
|
||||
else
|
||||
windowDimensions.MakeZero();
|
||||
|
||||
overlay->console = std::make_unique<Console>(*info.overlayWorld, Nz::Vector2f(windowDimensions), overlay->lua);
|
||||
Nz::LuaInstance& lua = overlay->lua;
|
||||
|
||||
overlay->console = info.canvas->Add<Console>();
|
||||
overlay->console->OnCommand.Connect([&lua](Ndk::Console* console, const Nz::String& command)
|
||||
{
|
||||
if (!lua.Execute(command))
|
||||
console->AddLine(lua.GetLastError(), Nz::Color::Red);
|
||||
});
|
||||
|
||||
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)
|
||||
@@ -164,11 +170,11 @@ namespace Ndk
|
||||
consoleRef.AddLine(str);
|
||||
});
|
||||
|
||||
overlay->lua.LoadLibraries();
|
||||
LuaAPI::RegisterClasses(overlay->lua);
|
||||
lua.LoadLibraries();
|
||||
LuaAPI::RegisterClasses(lua);
|
||||
|
||||
// Override "print" function to add a line in the console
|
||||
overlay->lua.PushFunction([&consoleRef] (Nz::LuaState& state)
|
||||
lua.PushFunction([&consoleRef] (Nz::LuaState& state)
|
||||
{
|
||||
Nz::StringStream stream;
|
||||
|
||||
@@ -192,31 +198,37 @@ namespace Ndk
|
||||
consoleRef.AddLine(stream);
|
||||
return 0;
|
||||
});
|
||||
overlay->lua.SetGlobal("print");
|
||||
lua.SetGlobal("print");
|
||||
|
||||
// Define a few base variables to allow our interface to interact with the application
|
||||
overlay->lua.PushGlobal("Application", Ndk::Application::Instance());
|
||||
overlay->lua.PushGlobal("Console", consoleRef.CreateHandle());
|
||||
lua.PushGlobal("Application", Ndk::Application::Instance());
|
||||
lua.PushGlobal("Console", consoleRef.CreateHandle());
|
||||
|
||||
// Setup a few event callback to handle the console
|
||||
Nz::EventHandler& eventHandler = info.window->GetEventHandler();
|
||||
|
||||
overlay->eventSlot.Connect(eventHandler.OnEvent, [&consoleRef] (const Nz::EventHandler*, const Nz::WindowEvent& event)
|
||||
{
|
||||
if (consoleRef.IsVisible())
|
||||
consoleRef.SendEvent(event);
|
||||
});
|
||||
|
||||
overlay->keyPressedSlot.Connect(eventHandler.OnKeyPressed, [&consoleRef] (const Nz::EventHandler*, const Nz::WindowEvent::KeyEvent& event)
|
||||
{
|
||||
if (event.virtualKey == Nz::Keyboard::VKey::F9)
|
||||
consoleRef.Show(!consoleRef.IsVisible());
|
||||
{
|
||||
// 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.SetSize({float(size.x), size.y / 4.f});
|
||||
consoleRef.Resize({float(size.x), size.y / 4.f});
|
||||
});
|
||||
|
||||
info.console = std::move(overlay);
|
||||
@@ -238,6 +250,9 @@ namespace Ndk
|
||||
{
|
||||
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);
|
||||
|
||||
@@ -89,6 +89,7 @@ namespace Ndk
|
||||
}
|
||||
else
|
||||
{
|
||||
DestroyEntity(m_backgroundEntity);
|
||||
m_backgroundEntity.Reset();
|
||||
m_backgroundSprite.Reset();
|
||||
}
|
||||
@@ -144,6 +145,29 @@ namespace Ndk
|
||||
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)
|
||||
@@ -156,21 +180,44 @@ namespace Ndk
|
||||
UnregisterFromCanvas();
|
||||
|
||||
for (WidgetEntity& entity : m_entities)
|
||||
entity.handle->Enable(show);
|
||||
{
|
||||
if (entity.isEnabled)
|
||||
{
|
||||
entity.handle->Enable(show); //< This will override isEnabled
|
||||
entity.isEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& widgetPtr : m_children)
|
||||
widgetPtr->Show(show);
|
||||
ShowChildren(show);
|
||||
}
|
||||
}
|
||||
|
||||
const Ndk::EntityHandle& BaseWidget::CreateEntity()
|
||||
const EntityHandle& BaseWidget::CreateEntity()
|
||||
{
|
||||
const EntityHandle& newEntity = m_world->CreateEntity();
|
||||
newEntity->Enable(m_visible);
|
||||
|
||||
m_entities.emplace_back();
|
||||
WidgetEntity& widgetEntity = m_entities.back();
|
||||
widgetEntity.handle = newEntity;
|
||||
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;
|
||||
}
|
||||
@@ -185,7 +232,7 @@ namespace Ndk
|
||||
|
||||
void BaseWidget::Layout()
|
||||
{
|
||||
if (m_backgroundEntity)
|
||||
if (m_backgroundSprite)
|
||||
m_backgroundSprite->SetSize(m_size.x, m_size.y);
|
||||
|
||||
UpdatePositionAndSize();
|
||||
@@ -198,6 +245,19 @@ namespace Ndk
|
||||
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;
|
||||
@@ -236,6 +296,10 @@ namespace Ndk
|
||||
{
|
||||
}
|
||||
|
||||
void BaseWidget::OnMouseWheelMoved(int /*x*/, int /*y*/, float /*delta*/)
|
||||
{
|
||||
}
|
||||
|
||||
void BaseWidget::OnMouseExit()
|
||||
{
|
||||
}
|
||||
@@ -250,6 +314,12 @@ namespace Ndk
|
||||
|
||||
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)
|
||||
@@ -290,10 +360,17 @@ namespace Ndk
|
||||
if (IsRegisteredToCanvas())
|
||||
m_canvas->NotifyWidgetBoxUpdate(m_canvasIndex);
|
||||
|
||||
Nz::Vector2f widgetPos = Nz::Vector2f(GetPosition());
|
||||
Nz::Vector2f widgetSize = GetSize();
|
||||
Nz::Rectf scissorRect = GetScissorRect();
|
||||
|
||||
Nz::Recti fullBounds(Nz::Rectf(widgetPos.x, widgetPos.y, widgetSize.x, widgetSize.y));
|
||||
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;
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace Ndk
|
||||
}
|
||||
}
|
||||
|
||||
void Canvas::OnEventMouseButtonRelease(const Nz::EventHandler* /*eventHandler*/, const Nz::WindowEvent::MouseButtonEvent & event)
|
||||
void Canvas::OnEventMouseButtonRelease(const Nz::EventHandler* /*eventHandler*/, const Nz::WindowEvent::MouseButtonEvent& event)
|
||||
{
|
||||
if (m_hoveredWidget != InvalidCanvasIndex)
|
||||
{
|
||||
@@ -128,6 +128,19 @@ namespace Ndk
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -137,7 +150,7 @@ namespace Ndk
|
||||
}
|
||||
}
|
||||
|
||||
void Canvas::OnEventKeyPressed(const Nz::EventHandler* /*eventHandler*/, const Nz::WindowEvent::KeyEvent& event)
|
||||
void Canvas::OnEventKeyPressed(const Nz::EventHandler* eventHandler, const Nz::WindowEvent::KeyEvent& event)
|
||||
{
|
||||
if (m_keyboardOwner != InvalidCanvasIndex)
|
||||
{
|
||||
@@ -191,12 +204,16 @@ namespace Ndk
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OnUnhandledKeyPressed(eventHandler, event);
|
||||
}
|
||||
|
||||
void Canvas::OnEventKeyReleased(const Nz::EventHandler* /*eventHandler*/, const Nz::WindowEvent::KeyEvent& 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)
|
||||
|
||||
@@ -154,11 +154,10 @@ namespace Ndk
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Sets the layer of the camera in case of multiples fields
|
||||
* \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;
|
||||
@@ -169,7 +168,6 @@ namespace Ndk
|
||||
/*!
|
||||
* \brief Operation to perform when component is attached to an entity
|
||||
*/
|
||||
|
||||
void CameraComponent::OnAttached()
|
||||
{
|
||||
if (m_entity->HasComponent<NodeComponent>())
|
||||
@@ -304,6 +302,8 @@ namespace Ndk
|
||||
break;
|
||||
}
|
||||
|
||||
m_projectionMatrix *= Nz::Matrix4f::Scale(m_projectionScale);
|
||||
|
||||
m_projectionMatrixUpdated = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,28 +17,57 @@ namespace Ndk
|
||||
* \brief NDK class that represents a two-dimensional collision geometry
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \brief Gets the collision box representing the entity
|
||||
* \return The physics collision box
|
||||
*/
|
||||
Nz::Rectf CollisionComponent2D::GetAABB() const
|
||||
{
|
||||
return GetRigidBody()->GetAABB();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Gets the position offset between the actual rigid body center of mass position and the origin of the geometry
|
||||
* \return Position offset
|
||||
*/
|
||||
const Nz::Vector2f& CollisionComponent2D::GetGeomOffset() const
|
||||
{
|
||||
return GetRigidBody()->GetPositionOffset();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Convenience function to align center of geometry to a specific point
|
||||
*
|
||||
* \param geomOffset Position offset
|
||||
*
|
||||
* \remark This does not change the center of mass
|
||||
*/
|
||||
void CollisionComponent2D::Recenter(const Nz::Vector2f& origin)
|
||||
{
|
||||
const Nz::RigidBody2D* rigidBody = GetRigidBody();
|
||||
SetGeomOffset(origin - rigidBody->GetAABB().GetCenter() + rigidBody->GetPositionOffset());
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Sets geometry for the entity
|
||||
*
|
||||
* \param geom Geometry used for collisions
|
||||
*
|
||||
* \remark Produces a NazaraAssert if the entity has no physics component and has no static body
|
||||
*/
|
||||
void CollisionComponent2D::SetGeom(Nz::Collider2DRef geom)
|
||||
{
|
||||
m_geom = std::move(geom);
|
||||
|
||||
if (m_entity->HasComponent<PhysicsComponent2D>())
|
||||
{
|
||||
// We update the geometry of the PhysiscsObject linked to the PhysicsComponent2D
|
||||
PhysicsComponent2D& physComponent = m_entity->GetComponent<PhysicsComponent2D>();
|
||||
physComponent.GetRigidBody()->SetGeom(m_geom);
|
||||
}
|
||||
else
|
||||
{
|
||||
NazaraAssert(m_staticBody, "An entity without physics component should have a static body");
|
||||
m_staticBody->SetGeom(m_geom);
|
||||
}
|
||||
GetRigidBody()->SetGeom(m_geom);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Sets the position offset between the actual rigid body center of mass position and the origin of the geometry
|
||||
*
|
||||
* \param geomOffset Position offset
|
||||
*/
|
||||
void CollisionComponent2D::SetGeomOffset(const Nz::Vector2f& geomOffset)
|
||||
{
|
||||
GetRigidBody()->SetPositionOffset(geomOffset);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -47,7 +76,6 @@ namespace Ndk
|
||||
* \remark Produces a NazaraAssert if entity is invalid
|
||||
* \remark Produces a NazaraAssert if entity is not linked to a world, or the world has no physics system
|
||||
*/
|
||||
|
||||
void CollisionComponent2D::InitializeStaticBody()
|
||||
{
|
||||
NazaraAssert(m_entity, "Invalid entity");
|
||||
@@ -67,7 +95,34 @@ namespace Ndk
|
||||
matrix.MakeIdentity();
|
||||
|
||||
m_staticBody->SetPosition(Nz::Vector2f(matrix.GetTranslation()));
|
||||
}
|
||||
|
||||
Nz::RigidBody2D* CollisionComponent2D::GetRigidBody()
|
||||
{
|
||||
if (m_entity->HasComponent<PhysicsComponent2D>())
|
||||
{
|
||||
PhysicsComponent2D& physComponent = m_entity->GetComponent<PhysicsComponent2D>();
|
||||
return physComponent.GetRigidBody();
|
||||
}
|
||||
else
|
||||
{
|
||||
NazaraAssert(m_staticBody, "An entity without physics component should have a static body");
|
||||
return m_staticBody.get();
|
||||
}
|
||||
}
|
||||
|
||||
const Nz::RigidBody2D* CollisionComponent2D::GetRigidBody() const
|
||||
{
|
||||
if (m_entity->HasComponent<PhysicsComponent2D>())
|
||||
{
|
||||
PhysicsComponent2D& physComponent = m_entity->GetComponent<PhysicsComponent2D>();
|
||||
return physComponent.GetRigidBody();
|
||||
}
|
||||
else
|
||||
{
|
||||
NazaraAssert(m_staticBody, "An entity without physics component should have a static body");
|
||||
return m_staticBody.get();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
@@ -1,6 +1,23 @@
|
||||
// Copyright (C) 2019 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 <NDK/Components/ConstraintComponent2D.hpp>
|
||||
|
||||
namespace Ndk
|
||||
{
|
||||
ConstraintComponent2D::ConstraintComponent2D(const ConstraintComponent2D& /*joint*/)
|
||||
{
|
||||
}
|
||||
|
||||
bool ConstraintComponent2D::RemoveConstraint(Nz::Constraint2D* constraintPtr)
|
||||
{
|
||||
auto it = std::find_if(m_constraints.begin(), m_constraints.end(), [constraintPtr](const ConstraintData& constraintData) { return constraintData.constraint.get() == constraintPtr; });
|
||||
if (it != m_constraints.end())
|
||||
m_constraints.erase(it);
|
||||
|
||||
return !m_constraints.empty();
|
||||
}
|
||||
|
||||
ComponentIndex ConstraintComponent2D::componentIndex;
|
||||
}
|
||||
|
||||
@@ -3,8 +3,33 @@
|
||||
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
|
||||
|
||||
#include <NDK/Components/DebugComponent.hpp>
|
||||
#include <NDK/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;
|
||||
}
|
||||
|
||||
10
SDK/src/NDK/Components/LifetimeComponent.cpp
Normal file
10
SDK/src/NDK/Components/LifetimeComponent.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
// Copyright (C) 2017 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Development Kit"
|
||||
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
|
||||
|
||||
#include <NDK/Components/LifetimeComponent.hpp>
|
||||
|
||||
namespace Ndk
|
||||
{
|
||||
ComponentIndex LifetimeComponent::componentIndex;
|
||||
}
|
||||
@@ -31,9 +31,17 @@ namespace Ndk
|
||||
|
||||
Nz::PhysWorld2D& world = entityWorld->GetSystem<PhysicsSystem2D>().GetPhysWorld();
|
||||
|
||||
Nz::Vector2f positionOffset;
|
||||
|
||||
Nz::Collider2DRef geom;
|
||||
if (m_entity->HasComponent<CollisionComponent2D>())
|
||||
geom = m_entity->GetComponent<CollisionComponent2D>().GetGeom();
|
||||
{
|
||||
const CollisionComponent2D& entityCollision = m_entity->GetComponent<CollisionComponent2D>();
|
||||
geom = entityCollision.GetGeom();
|
||||
positionOffset = entityCollision.GetStaticBody()->GetPositionOffset(); //< Calling GetGeomOffset would retrieve current component which is not yet initialized
|
||||
}
|
||||
else
|
||||
positionOffset = Nz::Vector2f::Zero();
|
||||
|
||||
Nz::Matrix4f matrix;
|
||||
if (m_entity->HasComponent<NodeComponent>())
|
||||
@@ -42,8 +50,12 @@ namespace Ndk
|
||||
matrix.MakeIdentity();
|
||||
|
||||
m_object = std::make_unique<Nz::RigidBody2D>(&world, 1.f, geom);
|
||||
m_object->SetPositionOffset(positionOffset);
|
||||
m_object->SetPosition(Nz::Vector2f(matrix.GetTranslation()));
|
||||
m_object->SetUserdata(reinterpret_cast<void*>(static_cast<std::ptrdiff_t>(m_entity->GetId())));
|
||||
|
||||
if (m_pendingStates.valid)
|
||||
ApplyPhysicsState(*m_object);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -86,7 +98,11 @@ namespace Ndk
|
||||
|
||||
void PhysicsComponent2D::OnDetached()
|
||||
{
|
||||
m_object.reset();
|
||||
if (m_object)
|
||||
{
|
||||
CopyPhysicsState(*m_object);
|
||||
m_object.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void PhysicsComponent2D::OnEntityDestruction()
|
||||
|
||||
@@ -4,14 +4,12 @@
|
||||
|
||||
#include <NDK/Console.hpp>
|
||||
#include <Nazara/Core/Unicode.hpp>
|
||||
#include <Nazara/Lua/LuaState.hpp>
|
||||
#include <Nazara/Platform/Event.hpp>
|
||||
#include <NDK/Components/GraphicsComponent.hpp>
|
||||
#include <NDK/Components/NodeComponent.hpp>
|
||||
#include <NDK/Widgets.hpp>
|
||||
#include <NDK/World.hpp>
|
||||
|
||||
///TODO: For now is unable to display different color in the history, it needs a RichTextDrawer to do so
|
||||
|
||||
namespace Ndk
|
||||
{
|
||||
namespace
|
||||
@@ -34,71 +32,76 @@ namespace Ndk
|
||||
* \param instance Lua instance that will interact with the world
|
||||
*/
|
||||
|
||||
Console::Console(World& world, const Nz::Vector2f& size, Nz::LuaState& state) :
|
||||
Console::Console(BaseWidget* parent) :
|
||||
BaseWidget(parent),
|
||||
m_historyPosition(0),
|
||||
m_defaultFont(Nz::Font::GetDefault()),
|
||||
m_state(state),
|
||||
m_size(size),
|
||||
m_opened(false),
|
||||
m_characterSize(24)
|
||||
m_characterSize(24),
|
||||
m_maxHistoryLines(200)
|
||||
{
|
||||
Nz::MaterialRef backgroundMaterial = Nz::Material::New();
|
||||
backgroundMaterial->EnableBlending(true);
|
||||
backgroundMaterial->EnableDepthBuffer(false);
|
||||
backgroundMaterial->SetDstBlend(Nz::BlendFunc_InvSrcAlpha);
|
||||
backgroundMaterial->SetSrcBlend(Nz::BlendFunc_SrcAlpha);
|
||||
|
||||
// History bakckground
|
||||
m_historyBackgroundSprite = Nz::Sprite::New();
|
||||
m_historyBackgroundSprite->SetColor(Nz::Color(80, 80, 160, 128));
|
||||
m_historyBackgroundSprite->SetMaterial(backgroundMaterial);
|
||||
|
||||
m_historyBackground = world.CreateEntity();
|
||||
m_historyBackground->Enable(m_opened);
|
||||
m_historyBackground->AddComponent<Ndk::GraphicsComponent>().Attach(m_historyBackgroundSprite, -1);
|
||||
m_historyBackground->AddComponent<Ndk::NodeComponent>().SetParent(this);
|
||||
|
||||
// History
|
||||
m_historyDrawer.SetCharacterSize(m_characterSize);
|
||||
m_historyDrawer.SetColor(Nz::Color(200, 200, 200));
|
||||
m_historyDrawer.SetFont(m_defaultFont);
|
||||
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_historyTextSprite = Nz::TextSprite::New();
|
||||
|
||||
m_history = world.CreateEntity();
|
||||
m_history->Enable(m_opened);
|
||||
m_history->AddComponent<Ndk::GraphicsComponent>().Attach(m_historyTextSprite);
|
||||
|
||||
Ndk::NodeComponent& historyNode = m_history->AddComponent<Ndk::NodeComponent>();
|
||||
historyNode.SetParent(this);
|
||||
|
||||
// Input background
|
||||
m_inputBackgroundSprite = Nz::Sprite::New();
|
||||
m_inputBackgroundSprite->SetColor(Nz::Color(255, 255, 255, 200));
|
||||
m_inputBackgroundSprite->SetMaterial(backgroundMaterial);
|
||||
|
||||
m_inputBackground = world.CreateEntity();
|
||||
m_inputBackground->Enable(m_opened);
|
||||
m_inputBackground->AddComponent<Ndk::GraphicsComponent>().Attach(m_inputBackgroundSprite, -1);
|
||||
m_inputBackground->AddComponent<Ndk::NodeComponent>().SetParent(this);
|
||||
m_historyArea = Add<ScrollAreaWidget>(m_history);
|
||||
|
||||
// Input
|
||||
m_inputDrawer.SetColor(Nz::Color::Black);
|
||||
m_inputDrawer.SetCharacterSize(m_characterSize);
|
||||
m_inputDrawer.SetFont(m_defaultFont);
|
||||
m_inputDrawer.SetText(s_inputPrefix);
|
||||
m_input = Add<TextAreaWidget>();
|
||||
m_input->EnableBackground(true);
|
||||
m_input->SetText(s_inputPrefix);
|
||||
m_input->SetTextColor(Nz::Color::Black);
|
||||
|
||||
m_inputTextSprite = Nz::TextSprite::New();
|
||||
m_inputTextSprite->Update(m_inputDrawer);
|
||||
m_input->OnTextAreaKeyReturn.Connect(this, &Console::ExecuteInput);
|
||||
|
||||
m_input = world.CreateEntity();
|
||||
m_input->Enable(m_opened);
|
||||
m_input->AddComponent<Ndk::GraphicsComponent>().Attach(m_inputTextSprite);
|
||||
// Protect input prefix from erasure/selection
|
||||
m_input->SetCursorPosition(s_inputPrefixSize);
|
||||
|
||||
Ndk::NodeComponent& inputNode = m_input->AddComponent<Ndk::NodeComponent>();
|
||||
inputNode.SetParent(this);
|
||||
m_input->OnTextAreaCursorMove.Connect([](const AbstractTextAreaWidget* textArea, Nz::Vector2ui* newCursorPos)
|
||||
{
|
||||
newCursorPos->x = std::max(newCursorPos->x, static_cast<unsigned int>(s_inputPrefixSize));
|
||||
});
|
||||
|
||||
Layout();
|
||||
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]);
|
||||
});
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -107,141 +110,65 @@ namespace Ndk
|
||||
* \param text New line of text
|
||||
* \param color Color for the text
|
||||
*/
|
||||
|
||||
void Console::AddLine(const Nz::String& text, const Nz::Color& color)
|
||||
{
|
||||
AddLineInternal(text, color);
|
||||
RefreshHistory();
|
||||
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();
|
||||
RefreshHistory();
|
||||
m_history->Clear();
|
||||
m_history->Resize(m_history->GetPreferredSize());
|
||||
m_historyArea->Resize(m_historyArea->GetSize());
|
||||
m_input->SetText(s_inputPrefix);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Sends a character to the console
|
||||
* \brief Clears the console focus
|
||||
*
|
||||
* \param character Character that will be added to the console
|
||||
* Clear console input widget focus (if owned)
|
||||
*/
|
||||
|
||||
void Console::SendCharacter(char32_t character)
|
||||
void Console::ClearFocus()
|
||||
{
|
||||
switch (character)
|
||||
{
|
||||
case '\b':
|
||||
{
|
||||
Nz::String input = m_inputDrawer.GetText();
|
||||
if (input.GetLength() <= s_inputPrefixSize) // Prevent removal of the input prefix
|
||||
return; // Ignore if no user character is there
|
||||
|
||||
input.Resize(-1, Nz::String::HandleUtf8);
|
||||
m_inputDrawer.SetText(input);
|
||||
break;
|
||||
}
|
||||
|
||||
case '\r':
|
||||
case '\n':
|
||||
ExecuteInput();
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
if (Nz::Unicode::GetCategory(character) == Nz::Unicode::Category_Other_Control)
|
||||
return;
|
||||
|
||||
m_inputDrawer.AppendText(Nz::String::Unicode(character));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_inputTextSprite->Update(m_inputDrawer);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Sends an event to the console
|
||||
*
|
||||
* \param event Event to be takin into consideration by the console
|
||||
*/
|
||||
void Console::SendEvent(const Nz::WindowEvent& event)
|
||||
{
|
||||
switch (event.type)
|
||||
{
|
||||
case Nz::WindowEventType_TextEntered:
|
||||
SendCharacter(event.text.character);
|
||||
break;
|
||||
|
||||
case Nz::WindowEventType_KeyPressed:
|
||||
{
|
||||
switch (event.key.virtualKey)
|
||||
{
|
||||
case Nz::Keyboard::VKey::Down:
|
||||
case Nz::Keyboard::VKey::Up:
|
||||
{
|
||||
if (event.key.virtualKey == Nz::Keyboard::VKey::Up)
|
||||
m_historyPosition = std::min<std::size_t>(m_commandHistory.size(), m_historyPosition + 1);
|
||||
else
|
||||
{
|
||||
if (m_historyPosition > 1)
|
||||
m_historyPosition--;
|
||||
else if (m_historyPosition == 0)
|
||||
m_historyPosition = 1;
|
||||
}
|
||||
|
||||
if (!m_commandHistory.empty())
|
||||
{
|
||||
Nz::String text = m_commandHistory[m_commandHistory.size() - m_historyPosition];
|
||||
m_inputDrawer.SetText(s_inputPrefix + text);
|
||||
m_inputTextSprite->Update(m_inputDrawer);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_input->ClearFocus();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Sets the character size
|
||||
*
|
||||
* \param size Size of the font
|
||||
*/
|
||||
|
||||
void Console::SetCharacterSize(unsigned int size)
|
||||
{
|
||||
m_characterSize = size;
|
||||
|
||||
m_historyDrawer.SetCharacterSize(m_characterSize);
|
||||
m_historyTextSprite->Update(m_historyDrawer);
|
||||
m_inputDrawer.SetCharacterSize(m_characterSize);
|
||||
m_inputTextSprite->Update(m_inputDrawer);
|
||||
m_history->SetCharacterSize(size);
|
||||
m_input->SetCharacterSize(size);
|
||||
|
||||
Layout();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Sets the console size
|
||||
* \brief Give the console input focus
|
||||
*
|
||||
* \param size (Width, Height) of the console
|
||||
*/
|
||||
|
||||
void Console::SetSize(const Nz::Vector2f& size)
|
||||
void Console::SetFocus()
|
||||
{
|
||||
m_size = size;
|
||||
m_historyBackgroundSprite->SetSize(m_size);
|
||||
Layout();
|
||||
m_input->SetFocus();
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -251,121 +178,55 @@ namespace Ndk
|
||||
*
|
||||
* \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_historyDrawer.SetFont(m_defaultFont);
|
||||
m_inputDrawer.SetFont(m_defaultFont);
|
||||
m_history->SetTextFont(m_defaultFont);
|
||||
m_input->SetTextFont(m_defaultFont);
|
||||
|
||||
Layout();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Shows the console
|
||||
*
|
||||
* \param show Should the console be showed
|
||||
*/
|
||||
|
||||
void Console::Show(bool show)
|
||||
{
|
||||
if (m_opened != show)
|
||||
{
|
||||
m_historyBackground->Enable(show);
|
||||
m_history->Enable(show);
|
||||
m_input->Enable(show);
|
||||
m_inputBackground->Enable(show);
|
||||
|
||||
m_opened = show;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Adds a line to the history of the console
|
||||
*
|
||||
* \param text New line of text
|
||||
* \param color Color for the text
|
||||
*/
|
||||
|
||||
void Console::AddLineInternal(const Nz::String& text, const Nz::Color& color)
|
||||
{
|
||||
m_historyLines.emplace_back(Line{color, text});
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Performs this action when an input is added to the console
|
||||
*/
|
||||
|
||||
void Console::ExecuteInput()
|
||||
void Console::ExecuteInput(const AbstractTextAreaWidget* textArea, bool* ignoreDefaultAction)
|
||||
{
|
||||
Nz::String input = m_inputDrawer.GetText();
|
||||
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_inputDrawer.SetText(s_inputPrefix);
|
||||
m_input->SetText(s_inputPrefix);
|
||||
|
||||
if (m_commandHistory.empty() || m_commandHistory.back() != inputCmd)
|
||||
m_commandHistory.push_back(inputCmd);
|
||||
|
||||
m_historyPosition = 0;
|
||||
m_historyPosition = m_commandHistory.size();
|
||||
|
||||
AddLineInternal(input); //< With the input prefix
|
||||
AddLine(input); //< With the input prefix
|
||||
|
||||
if (!m_state.Execute(inputCmd))
|
||||
AddLineInternal(m_state.GetLastError(), Nz::Color::Red);
|
||||
|
||||
RefreshHistory();
|
||||
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;
|
||||
|
||||
Ndk::NodeComponent& inputNode = m_input->GetComponent<Ndk::NodeComponent>();
|
||||
inputNode.SetPosition(0.f, m_size.y - lineHeight - 5.f);
|
||||
m_historyArea->SetPosition(origin.x, origin.y);
|
||||
m_historyArea->Resize({ size.x, historyHeight - 4.f });
|
||||
|
||||
float historyHeight = m_size.y - lineHeight - 5.f - 2.f;
|
||||
m_historyBackgroundSprite->SetSize(m_size.x, historyHeight);
|
||||
|
||||
m_maxHistoryLines = static_cast<unsigned int>(std::ceil(historyHeight / lineHeight));
|
||||
|
||||
Ndk::NodeComponent& historyNode = m_history->GetComponent<Ndk::NodeComponent>();
|
||||
historyNode.SetPosition(0.f, historyHeight - m_maxHistoryLines * lineHeight);
|
||||
|
||||
Ndk::NodeComponent& inputBackgroundNode = m_inputBackground->GetComponent<Ndk::NodeComponent>();
|
||||
inputBackgroundNode.SetPosition(0.f, historyHeight + 2.f);
|
||||
|
||||
m_inputBackgroundSprite->SetSize(m_size.x, m_size.y - historyHeight);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Refreshes the history of the console
|
||||
*/
|
||||
|
||||
void Console::RefreshHistory()
|
||||
{
|
||||
m_historyDrawer.Clear();
|
||||
auto it = m_historyLines.end();
|
||||
if (m_historyLines.size() > m_maxHistoryLines)
|
||||
it -= m_maxHistoryLines;
|
||||
else
|
||||
it = m_historyLines.begin();
|
||||
|
||||
for (unsigned int i = 0; i < m_maxHistoryLines; ++i)
|
||||
{
|
||||
if (m_maxHistoryLines - i <= m_historyLines.size() && it != m_historyLines.end())
|
||||
{
|
||||
m_historyDrawer.AppendText(it->text);
|
||||
++it;
|
||||
}
|
||||
|
||||
m_historyDrawer.AppendText(Nz::String('\n'));
|
||||
}
|
||||
|
||||
m_historyTextSprite->Update(m_historyDrawer);
|
||||
m_input->Resize({size.x, size.y - historyHeight});
|
||||
m_input->SetPosition(origin.x, origin.y + historyHeight);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +97,36 @@ namespace Ndk
|
||||
return m_world->CloneEntity(m_id);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Detaches a component from the entity
|
||||
* \return An owning pointer to the component
|
||||
*
|
||||
* Instantly detaches a component from the entity and returns it, allowing to attach it to another entity
|
||||
*
|
||||
* \remark Unlike RemoveComponent, this function instantly removes the component
|
||||
*/
|
||||
std::unique_ptr<BaseComponent> Entity::DropComponent(ComponentIndex index)
|
||||
{
|
||||
if (!HasComponent(index))
|
||||
return nullptr;
|
||||
|
||||
// We get the component and we alert existing components of the deleted one
|
||||
std::unique_ptr<BaseComponent> component = std::move(m_components[index]);
|
||||
m_components[index].reset();
|
||||
|
||||
for (std::size_t i = m_componentBits.FindFirst(); i != m_componentBits.npos; i = m_componentBits.FindNext(i))
|
||||
{
|
||||
if (i != index)
|
||||
m_components[i]->OnComponentDetached(*component);
|
||||
}
|
||||
m_componentBits.Reset(index);
|
||||
m_removedComponentBits.UnboundedReset(index);
|
||||
|
||||
component->SetEntity(nullptr);
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Enables the entity
|
||||
*
|
||||
@@ -111,11 +141,15 @@ namespace Ndk
|
||||
{
|
||||
for (std::size_t i = m_componentBits.FindFirst(); i != m_componentBits.npos; i = m_componentBits.FindNext(i))
|
||||
m_components[i]->OnEntityEnabled();
|
||||
|
||||
OnEntityEnabled(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (std::size_t i = m_componentBits.FindFirst(); i != m_componentBits.npos; i = m_componentBits.FindNext(i))
|
||||
m_components[i]->OnEntityDisabled();
|
||||
|
||||
OnEntityDisabled(this);
|
||||
}
|
||||
|
||||
Invalidate();
|
||||
@@ -204,32 +238,4 @@ namespace Ndk
|
||||
|
||||
m_valid = false;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Destroys a component by index
|
||||
*
|
||||
* \param index Index of the component
|
||||
*
|
||||
* \remark If component is not available, no action is performed
|
||||
*/
|
||||
|
||||
void Entity::DestroyComponent(ComponentIndex index)
|
||||
{
|
||||
if (HasComponent(index))
|
||||
{
|
||||
// We get the component and we alert existing components of the deleted one
|
||||
BaseComponent& component = *m_components[index].get();
|
||||
for (std::size_t i = m_componentBits.FindFirst(); i != m_componentBits.npos; i = m_componentBits.FindNext(i))
|
||||
{
|
||||
if (i != index)
|
||||
m_components[i]->OnComponentDetached(component);
|
||||
}
|
||||
|
||||
component.SetEntity(nullptr);
|
||||
|
||||
m_components[index].reset();
|
||||
m_componentBits.Reset(index);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -282,7 +282,7 @@ namespace Ndk
|
||||
{
|
||||
case 1:
|
||||
if (lua.IsOfType(argIndex, "Matrix4"))
|
||||
instance.Set(*static_cast<Nz::Matrix4d*>(lua.ToUserdata(argIndex)));
|
||||
instance = *static_cast<Nz::Matrix4d*>(lua.ToUserdata(argIndex));
|
||||
break;
|
||||
|
||||
case 16:
|
||||
@@ -291,7 +291,7 @@ namespace Ndk
|
||||
for (std::size_t i = 0; i < 16; ++i)
|
||||
values[i] = lua.CheckNumber(argIndex++);
|
||||
|
||||
instance.Set(values);
|
||||
instance = Nz::Matrix4d(values);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -62,24 +62,14 @@ namespace Ndk
|
||||
console.BindMethod("AddLine", &Console::AddLine, Nz::Color::White);
|
||||
console.BindMethod("Clear", &Console::Clear);
|
||||
console.BindMethod("GetCharacterSize", &Console::GetCharacterSize);
|
||||
console.BindMethod("GetHistory", &Console::GetHistory);
|
||||
console.BindMethod("GetHistoryBackground", &Console::GetHistoryBackground);
|
||||
console.BindMethod("GetInput", &Console::GetInput);
|
||||
console.BindMethod("GetInputBackground", &Console::GetInputBackground);
|
||||
console.BindMethod("GetSize", &Console::GetSize);
|
||||
//console.BindMethod("GetHistory", &Console::GetHistory);
|
||||
//console.BindMethod("GetInput", &Console::GetInput);
|
||||
console.BindMethod("GetTextFont", &Console::GetTextFont);
|
||||
|
||||
console.BindMethod("IsValidHandle", &ConsoleHandle::IsValid);
|
||||
console.BindMethod("IsVisible", &Console::IsVisible);
|
||||
|
||||
console.BindMethod("SendCharacter", &Console::SendCharacter);
|
||||
//consoleClass.SetMethod("SendEvent", &Console::SendEvent);
|
||||
|
||||
console.BindMethod("SetCharacterSize", &Console::SetCharacterSize);
|
||||
console.BindMethod("SetSize", &Console::SetSize);
|
||||
console.BindMethod("SetTextFont", &Console::SetTextFont);
|
||||
|
||||
console.BindMethod("Show", &Console::Show, true);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -123,12 +123,13 @@ namespace Ndk
|
||||
lua.Push(instance->GetCachedGlyphCount());
|
||||
return 1;
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
{
|
||||
unsigned int characterSize = lua.Check<unsigned int>(&argIndex);
|
||||
Nz::UInt32 style = lua.Check<Nz::UInt32>(&argIndex);
|
||||
Nz::TextStyleFlags style = lua.Check<Nz::TextStyleFlags>(&argIndex);
|
||||
float outlineThickness = lua.Check<float>(&argIndex);
|
||||
|
||||
lua.Push(instance->GetCachedGlyphCount(characterSize, style));
|
||||
lua.Push(instance->GetCachedGlyphCount(characterSize, style, outlineThickness));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -146,7 +147,7 @@ namespace Ndk
|
||||
|
||||
font.BindMethod("IsValid", &Nz::Font::IsValid);
|
||||
|
||||
font.BindMethod("Precache", (bool(Nz::Font::*)(unsigned int, Nz::UInt32, const Nz::String&) const) &Nz::Font::Precache);
|
||||
font.BindMethod("Precache", (bool(Nz::Font::*)(unsigned int, Nz::TextStyleFlags, float, const Nz::String&) const) &Nz::Font::Precache);
|
||||
|
||||
font.BindMethod("SetGlyphBorder", &Nz::Font::SetGlyphBorder);
|
||||
font.BindMethod("SetMinimumStepSize", &Nz::Font::SetMinimumStepSize);
|
||||
|
||||
@@ -17,11 +17,13 @@
|
||||
#include <NDK/BaseSystem.hpp>
|
||||
#include <NDK/Components/CollisionComponent2D.hpp>
|
||||
#include <NDK/Components/CollisionComponent3D.hpp>
|
||||
#include <NDK/Components/LifetimeComponent.hpp>
|
||||
#include <NDK/Components/NodeComponent.hpp>
|
||||
#include <NDK/Components/PhysicsComponent2D.hpp>
|
||||
#include <NDK/Components/PhysicsComponent3D.hpp>
|
||||
#include <NDK/Components/VelocityComponent.hpp>
|
||||
#include <NDK/Components/ConstraintComponent2D.hpp>
|
||||
#include <NDK/Systems/LifetimeSystem.hpp>
|
||||
#include <NDK/Systems/PhysicsSystem2D.hpp>
|
||||
#include <NDK/Systems/PhysicsSystem3D.hpp>
|
||||
#include <NDK/Systems/VelocitySystem.hpp>
|
||||
@@ -88,6 +90,7 @@ namespace Ndk
|
||||
// Shared components
|
||||
InitializeComponent<CollisionComponent2D>("NdkColl2");
|
||||
InitializeComponent<CollisionComponent3D>("NdkColl3");
|
||||
InitializeComponent<LifetimeComponent>("NdkLiftm");
|
||||
InitializeComponent<NodeComponent>("NdkNode");
|
||||
InitializeComponent<PhysicsComponent2D>("NdkPhys2");
|
||||
InitializeComponent<PhysicsComponent3D>("NdkPhys3");
|
||||
@@ -110,6 +113,7 @@ namespace Ndk
|
||||
BaseSystem::Initialize();
|
||||
|
||||
// Shared systems
|
||||
InitializeSystem<LifetimeSystem>();
|
||||
InitializeSystem<PhysicsSystem2D>();
|
||||
InitializeSystem<PhysicsSystem3D>();
|
||||
InitializeSystem<VelocitySystem>();
|
||||
|
||||
@@ -8,10 +8,12 @@
|
||||
#include <Nazara/Utility/IndexIterator.hpp>
|
||||
#include <Nazara/Utility/Mesh.hpp>
|
||||
#include <Nazara/Utility/StaticMesh.hpp>
|
||||
#include <NDK/Components/CollisionComponent2D.hpp>
|
||||
#include <NDK/Components/CollisionComponent3D.hpp>
|
||||
#include <NDK/Components/DebugComponent.hpp>
|
||||
#include <NDK/Components/GraphicsComponent.hpp>
|
||||
#include <NDK/Components/NodeComponent.hpp>
|
||||
#include <NDK/Components/PhysicsComponent2D.hpp>
|
||||
|
||||
namespace Ndk
|
||||
{
|
||||
@@ -151,12 +153,174 @@ namespace Ndk
|
||||
/*!
|
||||
* \brief Constructs an DebugSystem object by default
|
||||
*/
|
||||
DebugSystem::DebugSystem()
|
||||
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)
|
||||
@@ -208,6 +372,66 @@ namespace Ndk
|
||||
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;
|
||||
@@ -227,17 +451,24 @@ namespace Ndk
|
||||
{
|
||||
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)
|
||||
{
|
||||
renderable->SetPersistent(false);
|
||||
|
||||
entityGfx.Attach(renderable, Nz::Matrix4f::Translate(obb.GetCenter() - entityNode.GetPosition()), DebugDrawOrder);
|
||||
}
|
||||
|
||||
entityDebug.UpdateDebugRenderable(option, std::move(renderable));
|
||||
break;
|
||||
@@ -291,143 +522,5 @@ namespace Ndk
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
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::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 float* polygonVertices, std::size_t vertexCount)
|
||||
{
|
||||
std::size_t firstIndex = vertices.size();
|
||||
|
||||
for (std::size_t i = 0; i < vertexCount; ++i)
|
||||
{
|
||||
const float* vertexData = &polygonVertices[i * 3];
|
||||
vertices.emplace_back(vertexData[0], vertexData[1], vertexData[2]);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
SystemIndex DebugSystem::systemIndex;
|
||||
}
|
||||
|
||||
27
SDK/src/NDK/Systems/LifetimeSystem.cpp
Normal file
27
SDK/src/NDK/Systems/LifetimeSystem.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright (C) 2017 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Development Kit"
|
||||
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
|
||||
|
||||
#include <NDK/Systems/LifetimeSystem.hpp>
|
||||
#include <NDK/Components/LifetimeComponent.hpp>
|
||||
|
||||
namespace Ndk
|
||||
{
|
||||
LifetimeSystem::LifetimeSystem()
|
||||
{
|
||||
Requires<LifetimeComponent>();
|
||||
}
|
||||
|
||||
void LifetimeSystem::OnUpdate(float elapsedTime)
|
||||
{
|
||||
for (const Ndk::EntityHandle& entity : GetEntities())
|
||||
{
|
||||
auto& lifetime = entity->GetComponent<LifetimeComponent>();
|
||||
|
||||
if (lifetime.UpdateLifetime(elapsedTime))
|
||||
entity->Kill();
|
||||
}
|
||||
}
|
||||
|
||||
SystemIndex LifetimeSystem::systemIndex;
|
||||
}
|
||||
@@ -86,14 +86,30 @@ namespace Ndk
|
||||
bool PhysicsSystem2D::NearestBodyQuery(const Nz::Vector2f& from, float maxDistance, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, NearestQueryResult* result)
|
||||
{
|
||||
Nz::PhysWorld2D::NearestQueryResult queryResult;
|
||||
bool res = GetPhysWorld().NearestBodyQuery(from, maxDistance, collisionGroup, categoryMask, collisionMask, &queryResult);
|
||||
if (GetPhysWorld().NearestBodyQuery(from, maxDistance, collisionGroup, categoryMask, collisionMask, &queryResult))
|
||||
{
|
||||
result->nearestBody = GetEntityFromBody(*queryResult.nearestBody);
|
||||
result->closestPoint = std::move(queryResult.closestPoint);
|
||||
result->fraction = std::move(queryResult.fraction);
|
||||
result->distance = queryResult.distance;
|
||||
|
||||
result->nearestBody = GetEntityFromBody(*queryResult.nearestBody);
|
||||
result->closestPoint = std::move(queryResult.closestPoint);
|
||||
result->fraction = std::move(queryResult.fraction);
|
||||
result->distance = queryResult.distance;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
return res;
|
||||
void PhysicsSystem2D::RaycastQuery(const Nz::Vector2f & from, const Nz::Vector2f & to, float radius, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, const std::function<void(const RaycastHit&)>& callback)
|
||||
{
|
||||
return GetPhysWorld().RaycastQuery(from, to, radius, collisionGroup, categoryMask, collisionMask, [this, &callback](const Nz::PhysWorld2D::RaycastHit& hitInfo)
|
||||
{
|
||||
callback({
|
||||
GetEntityFromBody(*hitInfo.nearestBody),
|
||||
hitInfo.hitPos,
|
||||
hitInfo.hitNormal,
|
||||
hitInfo.fraction
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
bool PhysicsSystem2D::RaycastQuery(const Nz::Vector2f& from, const Nz::Vector2f& to, float radius, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, std::vector<RaycastHit>* hitInfos)
|
||||
@@ -108,7 +124,7 @@ namespace Ndk
|
||||
std::move(hitResult.hitPos),
|
||||
std::move(hitResult.hitNormal),
|
||||
hitResult.fraction
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return res;
|
||||
@@ -117,14 +133,25 @@ namespace Ndk
|
||||
bool PhysicsSystem2D::RaycastQueryFirst(const Nz::Vector2f& from, const Nz::Vector2f& to, float radius, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, RaycastHit* hitInfo)
|
||||
{
|
||||
Nz::PhysWorld2D::RaycastHit queryResult;
|
||||
bool res = GetPhysWorld().RaycastQueryFirst(from, to, radius, collisionGroup, categoryMask, collisionMask, &queryResult);
|
||||
if (GetPhysWorld().RaycastQueryFirst(from, to, radius, collisionGroup, categoryMask, collisionMask, &queryResult))
|
||||
{
|
||||
hitInfo->body = GetEntityFromBody(*queryResult.nearestBody);
|
||||
hitInfo->hitPos = std::move(queryResult.hitPos);
|
||||
hitInfo->hitNormal = std::move(queryResult.hitNormal);
|
||||
hitInfo->fraction = queryResult.fraction;
|
||||
|
||||
hitInfo->body = GetEntityFromBody(*queryResult.nearestBody);
|
||||
hitInfo->hitPos = std::move(queryResult.hitPos);
|
||||
hitInfo->hitNormal = std::move(queryResult.hitNormal);
|
||||
hitInfo->fraction = queryResult.fraction;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
return res;
|
||||
void PhysicsSystem2D::RegionQuery(const Nz::Rectf& boundingBox, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, const std::function<void(const EntityHandle&)>& callback)
|
||||
{
|
||||
return GetPhysWorld().RegionQuery(boundingBox, collisionGroup, categoryMask, collisionMask, [this, &callback](Nz::RigidBody2D* body)
|
||||
{
|
||||
callback(GetEntityFromBody(*body));
|
||||
});
|
||||
}
|
||||
|
||||
void PhysicsSystem2D::RegionQuery(const Nz::Rectf& boundingBox, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, std::vector<EntityHandle>* bodies)
|
||||
@@ -169,7 +196,7 @@ namespace Ndk
|
||||
auto& node = entity->GetComponent<NodeComponent>();
|
||||
|
||||
Nz::RigidBody2D* physObj = collision.GetStaticBody();
|
||||
physObj->SetPosition(Nz::Vector2f(node.GetPosition()));
|
||||
physObj->SetPosition(Nz::Vector2f(node.GetPosition(Nz::CoordSys_Global)));
|
||||
//physObj->SetRotation(node.GetRotation());
|
||||
}
|
||||
}
|
||||
@@ -213,7 +240,7 @@ namespace Ndk
|
||||
Nz::Vector2f newPosition = Nz::Vector2f(node.GetPosition(Nz::CoordSys_Global));
|
||||
|
||||
// To move static objects and ensure their collisions, we have to specify them a velocity
|
||||
// (/!\: the physical motor does not apply the speed on static objects)
|
||||
// (/!\: the physical engine does not apply the speed on static objects)
|
||||
if (newPosition != oldPosition)
|
||||
{
|
||||
body->SetPosition(newPosition);
|
||||
@@ -222,8 +249,7 @@ namespace Ndk
|
||||
else
|
||||
body->SetVelocity(Nz::Vector2f::Zero());
|
||||
|
||||
/*
|
||||
if (newRotation != oldRotation)
|
||||
/*if (newRotation != oldRotation)
|
||||
{
|
||||
Nz::Quaternionf transition = newRotation * oldRotation.GetConjugate();
|
||||
Nz::EulerAnglesf angles = transition.ToEulerAngles();
|
||||
@@ -235,8 +261,7 @@ namespace Ndk
|
||||
physObj->SetAngularVelocity(angularVelocity);
|
||||
}
|
||||
else
|
||||
physObj->SetAngularVelocity(Nz::Vector3f::Zero());
|
||||
*/
|
||||
physObj->SetAngularVelocity(Nz::Vector3f::Zero());*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,7 +308,7 @@ namespace Ndk
|
||||
|
||||
void PhysicsSystem2D::RegisterCallbacks(unsigned int collisionIdA, unsigned int collisionIdB, Callback callbacks)
|
||||
{
|
||||
Nz::PhysWorld2D::Callback worldCallbacks{};
|
||||
Nz::PhysWorld2D::Callback worldCallbacks;
|
||||
|
||||
if (callbacks.endCallback)
|
||||
{
|
||||
|
||||
@@ -69,8 +69,8 @@ namespace Ndk
|
||||
auto& node = entity->GetComponent<NodeComponent>();
|
||||
|
||||
Nz::RigidBody3D* physObj = collision.GetStaticBody();
|
||||
physObj->SetPosition(node.GetPosition());
|
||||
physObj->SetRotation(node.GetRotation());
|
||||
physObj->SetPosition(node.GetPosition(Nz::CoordSys_Global));
|
||||
physObj->SetRotation(node.GetRotation(Nz::CoordSys_Global));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
489
SDK/src/NDK/Widgets/AbstractTextAreaWidget.cpp
Normal file
489
SDK/src/NDK/Widgets/AbstractTextAreaWidget.cpp
Normal file
@@ -0,0 +1,489 @@
|
||||
// Copyright (C) 2017 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Development Kit"
|
||||
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
|
||||
|
||||
#include <NDK/Widgets/AbstractTextAreaWidget.hpp>
|
||||
#include <Nazara/Core/Unicode.hpp>
|
||||
#include <Nazara/Utility/Font.hpp>
|
||||
#include <NDK/Components/GraphicsComponent.hpp>
|
||||
#include <NDK/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.code)
|
||||
{
|
||||
case Nz::Keyboard::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::Delete:
|
||||
{
|
||||
if (HasSelection())
|
||||
EraseSelection();
|
||||
else
|
||||
Erase(GetGlyphIndex(m_cursorPositionBegin));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case Nz::Keyboard::Down:
|
||||
{
|
||||
bool ignoreDefaultAction = false;
|
||||
OnTextAreaKeyDown(this, &ignoreDefaultAction);
|
||||
|
||||
if (ignoreDefaultAction)
|
||||
return true;
|
||||
|
||||
if (HasSelection())
|
||||
SetCursorPosition(m_cursorPositionEnd);
|
||||
|
||||
MoveCursor({0, 1});
|
||||
return true;
|
||||
}
|
||||
|
||||
case Nz::Keyboard::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::Home:
|
||||
{
|
||||
bool ignoreDefaultAction = false;
|
||||
OnTextAreaKeyHome(this, &ignoreDefaultAction);
|
||||
|
||||
if (ignoreDefaultAction)
|
||||
return true;
|
||||
|
||||
SetCursorPosition({ 0U, key.control ? 0U : m_cursorPositionEnd.y });
|
||||
return true;
|
||||
}
|
||||
|
||||
case Nz::Keyboard::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::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::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::Up:
|
||||
{
|
||||
bool ignoreDefaultAction = false;
|
||||
OnTextAreaKeyUp(this, &ignoreDefaultAction);
|
||||
|
||||
if (ignoreDefaultAction)
|
||||
return true;
|
||||
|
||||
if (HasSelection())
|
||||
SetCursorPosition(m_cursorPositionBegin);
|
||||
|
||||
MoveCursor({0, -1});
|
||||
return true;
|
||||
}
|
||||
|
||||
case Nz::Keyboard::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::LShift) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::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()));
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ namespace Ndk
|
||||
BaseWidget(parent)
|
||||
{
|
||||
m_entity = CreateEntity();
|
||||
m_entity->AddComponent<NodeComponent>();
|
||||
m_entity->AddComponent<NodeComponent>().SetParent(this);
|
||||
auto& gfx = m_entity->AddComponent<GraphicsComponent>();
|
||||
|
||||
m_sprite = Nz::Sprite::New();
|
||||
|
||||
196
SDK/src/NDK/Widgets/RichTextAreaWidget.cpp
Normal file
196
SDK/src/NDK/Widgets/RichTextAreaWidget.cpp
Normal file
@@ -0,0 +1,196 @@
|
||||
// Copyright (C) 2017 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Development Kit"
|
||||
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
|
||||
|
||||
#include <NDK/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)
|
||||
}
|
||||
}
|
||||
198
SDK/src/NDK/Widgets/ScrollAreaWidget.cpp
Normal file
198
SDK/src/NDK/Widgets/ScrollAreaWidget.cpp
Normal file
@@ -0,0 +1,198 @@
|
||||
// Copyright (C) 2019 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 <NDK/Widgets/ScrollAreaWidget.hpp>
|
||||
#include <Nazara/Math/Algorithm.hpp>
|
||||
#include <NDK/Components/GraphicsComponent.hpp>
|
||||
#include <NDK/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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,36 +11,13 @@
|
||||
namespace Ndk
|
||||
{
|
||||
TextAreaWidget::TextAreaWidget(BaseWidget* parent) :
|
||||
BaseWidget(parent),
|
||||
m_characterFilter(),
|
||||
m_echoMode(EchoMode_Normal),
|
||||
m_cursorPositionBegin(0U, 0U),
|
||||
m_cursorPositionEnd(0U, 0U),
|
||||
m_isMouseButtonDown(false),
|
||||
m_multiLineEnabled(false),
|
||||
m_readOnly(false),
|
||||
m_tabEnabled(false)
|
||||
AbstractTextAreaWidget(parent)
|
||||
{
|
||||
m_cursorEntity = CreateEntity();
|
||||
m_cursorEntity->AddComponent<GraphicsComponent>();
|
||||
m_cursorEntity->AddComponent<NodeComponent>().SetParent(this);
|
||||
m_cursorEntity->GetComponent<NodeComponent>().SetPosition(5.f, 3.f);
|
||||
m_cursorEntity->Enable(false);
|
||||
|
||||
m_textSprite = Nz::TextSprite::New();
|
||||
|
||||
m_textEntity = CreateEntity();
|
||||
m_textEntity->AddComponent<GraphicsComponent>().Attach(m_textSprite);
|
||||
m_textEntity->AddComponent<NodeComponent>().SetParent(this);
|
||||
m_textEntity->GetComponent<NodeComponent>().SetPosition(5.f, 3.f);
|
||||
|
||||
SetCursor(Nz::SystemCursor_Text);
|
||||
SetCharacterSize(GetCharacterSize()); //< Actualize minimum / preferred size
|
||||
|
||||
EnableBackground(true);
|
||||
Layout();
|
||||
}
|
||||
|
||||
|
||||
void TextAreaWidget::AppendText(const Nz::String& text)
|
||||
{
|
||||
m_text += text;
|
||||
@@ -72,11 +49,19 @@ namespace Ndk
|
||||
}
|
||||
}
|
||||
|
||||
m_textSprite->Update(m_drawer);
|
||||
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)
|
||||
@@ -89,10 +74,10 @@ namespace Ndk
|
||||
Nz::String newText;
|
||||
if (firstGlyph > 0)
|
||||
{
|
||||
std::size_t characterPosition = m_text.GetCharacterPosition(firstGlyph - 1);
|
||||
std::size_t characterPosition = m_text.GetCharacterPosition(firstGlyph);
|
||||
NazaraAssert(characterPosition != Nz::String::npos, "Invalid character position");
|
||||
|
||||
newText.Append(m_text.SubString(0, characterPosition));
|
||||
newText.Append(m_text.SubString(0, characterPosition - 1));
|
||||
}
|
||||
|
||||
if (lastGlyph < textLength)
|
||||
@@ -106,70 +91,11 @@ namespace Ndk
|
||||
SetText(newText);
|
||||
}
|
||||
|
||||
void TextAreaWidget::EraseSelection()
|
||||
{
|
||||
if (!HasSelection())
|
||||
return;
|
||||
|
||||
Erase(GetGlyphIndex(m_cursorPositionBegin), GetGlyphIndex(m_cursorPositionEnd));
|
||||
}
|
||||
|
||||
Nz::Vector2ui TextAreaWidget::GetHoveredGlyph(float x, float y) const
|
||||
{
|
||||
std::size_t glyphCount = m_drawer.GetGlyphCount();
|
||||
if (glyphCount > 0)
|
||||
{
|
||||
std::size_t lineCount = m_drawer.GetLineCount();
|
||||
std::size_t line = 0U;
|
||||
for (; line < lineCount - 1; ++line)
|
||||
{
|
||||
Nz::Rectf lineBounds = m_drawer.GetLine(line).bounds;
|
||||
if (lineBounds.GetMaximum().y > y)
|
||||
break;
|
||||
}
|
||||
|
||||
std::size_t upperLimit = (line != lineCount - 1) ? m_drawer.GetLine(line + 1).glyphIndex : glyphCount + 1;
|
||||
|
||||
std::size_t firstLineGlyph = m_drawer.GetLine(line).glyphIndex;
|
||||
std::size_t i = firstLineGlyph;
|
||||
for (; i < upperLimit - 1; ++i)
|
||||
{
|
||||
Nz::Rectf bounds = m_drawer.GetGlyph(i).bounds;
|
||||
if (x < bounds.x + bounds.width * 0.75f)
|
||||
break;
|
||||
}
|
||||
|
||||
return Nz::Vector2ui(i - firstLineGlyph, line);
|
||||
}
|
||||
|
||||
return Nz::Vector2ui::Zero();
|
||||
}
|
||||
|
||||
void TextAreaWidget::SetCharacterSize(unsigned int characterSize)
|
||||
{
|
||||
m_drawer.SetCharacterSize(characterSize);
|
||||
|
||||
std::size_t fontCount = m_drawer.GetFontCount();
|
||||
unsigned int 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(characterSize);
|
||||
lineHeight = std::max(lineHeight, sizeInfo.lineHeight);
|
||||
spaceAdvance = std::max(spaceAdvance, sizeInfo.spaceAdvance);
|
||||
}
|
||||
|
||||
Nz::Vector2f size = { float(spaceAdvance), float(lineHeight) + 5.f };
|
||||
SetMinimumSize(size);
|
||||
SetPreferredSize({ size.x * 6.f, size.y });
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
@@ -182,412 +108,109 @@ namespace Ndk
|
||||
}
|
||||
}
|
||||
|
||||
void TextAreaWidget::Layout()
|
||||
Nz::AbstractTextDrawer& TextAreaWidget::GetTextDrawer()
|
||||
{
|
||||
BaseWidget::Layout();
|
||||
|
||||
RefreshCursor();
|
||||
return m_drawer;
|
||||
}
|
||||
|
||||
bool TextAreaWidget::IsFocusable() const
|
||||
const Nz::AbstractTextDrawer& TextAreaWidget::GetTextDrawer() const
|
||||
{
|
||||
return !m_readOnly;
|
||||
return m_drawer;
|
||||
}
|
||||
|
||||
void TextAreaWidget::OnFocusLost()
|
||||
void TextAreaWidget::HandleIndentation(bool add)
|
||||
{
|
||||
m_cursorEntity->Disable();
|
||||
}
|
||||
|
||||
void TextAreaWidget::OnFocusReceived()
|
||||
{
|
||||
if (!m_readOnly)
|
||||
m_cursorEntity->Enable(true);
|
||||
}
|
||||
|
||||
bool TextAreaWidget::OnKeyPressed(const Nz::WindowEvent::KeyEvent& key)
|
||||
{
|
||||
switch (key.virtualKey)
|
||||
if (add)
|
||||
Write(Nz::String('\t'));
|
||||
else
|
||||
{
|
||||
case Nz::Keyboard::VKey::Delete:
|
||||
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
|
||||
{
|
||||
if (HasSelection())
|
||||
EraseSelection();
|
||||
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
|
||||
Erase(GetGlyphIndex(m_cursorPositionBegin));
|
||||
|
||||
return true;
|
||||
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);
|
||||
|
||||
case Nz::Keyboard::VKey::Down:
|
||||
if (spaceIndex < endlIndex && spaceIndex != Nz::String::npos)
|
||||
{
|
||||
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 = m_drawer.GetLineCount();
|
||||
if (key.control && lineCount > 0)
|
||||
SetCursorPosition({ static_cast<unsigned int>(m_drawer.GetLineGlyphCount(lineCount - 1)), static_cast<unsigned int>(lineCount - 1) });
|
||||
if (m_text.GetSize() > spaceIndex)
|
||||
SetCursorPosition(spaceIndex + 1);
|
||||
else
|
||||
SetCursorPosition({ static_cast<unsigned int>(m_drawer.GetLineGlyphCount(m_cursorPositionEnd.y)), m_cursorPositionEnd.y });
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case Nz::Keyboard::VKey::Home:
|
||||
else if (endlIndex != Nz::String::npos)
|
||||
{
|
||||
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)
|
||||
{
|
||||
std::size_t index = GetGlyphIndex(m_cursorPositionBegin);
|
||||
|
||||
if (index == 0)
|
||||
return true;
|
||||
|
||||
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 });
|
||||
}
|
||||
if (index == endlIndex)
|
||||
SetCursorPosition(endlIndex + 1);
|
||||
else
|
||||
MoveCursor(-1);
|
||||
|
||||
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)
|
||||
{
|
||||
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 });
|
||||
}
|
||||
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())
|
||||
{
|
||||
for(unsigned line = m_cursorPositionBegin.y; line <= m_cursorPositionEnd.y; ++line)
|
||||
{
|
||||
const Nz::Vector2ui cursorPositionBegin = m_cursorPositionBegin;
|
||||
const Nz::Vector2ui cursorPositionEnd = m_cursorPositionEnd;
|
||||
|
||||
if (key.shift)
|
||||
{
|
||||
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 {}));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
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 (key.shift)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
else
|
||||
Write(Nz::String('\t'));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void TextAreaWidget::OnKeyReleased(const Nz::WindowEvent::KeyEvent& /*key*/)
|
||||
{
|
||||
}
|
||||
|
||||
void TextAreaWidget::OnMouseButtonPress(int x, int y, Nz::Mouse::Button button)
|
||||
{
|
||||
if (button == Nz::Mouse::Left)
|
||||
{
|
||||
SetFocus();
|
||||
|
||||
Nz::Vector2ui hoveredGlyph = GetHoveredGlyph(float(x) - 5.f, float(y) - 5.f);
|
||||
|
||||
// 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 TextAreaWidget::OnMouseButtonRelease(int, int, Nz::Mouse::Button button)
|
||||
{
|
||||
if (button == Nz::Mouse::Left)
|
||||
m_isMouseButtonDown = false;
|
||||
}
|
||||
|
||||
void TextAreaWidget::OnMouseEnter()
|
||||
{
|
||||
if (!Nz::Mouse::IsButtonPressed(Nz::Mouse::Left))
|
||||
m_isMouseButtonDown = false;
|
||||
}
|
||||
|
||||
void TextAreaWidget::OnMouseMoved(int x, int y, int deltaX, int deltaY)
|
||||
{
|
||||
if (m_isMouseButtonDown)
|
||||
SetSelection(m_selectionCursor, GetHoveredGlyph(float(x) - 5.f, float(y) - 3.f));
|
||||
}
|
||||
|
||||
void TextAreaWidget::OnTextEntered(char32_t character, bool /*repeated*/)
|
||||
{
|
||||
if (m_readOnly)
|
||||
return;
|
||||
|
||||
switch (character)
|
||||
{
|
||||
case '\b':
|
||||
{
|
||||
bool ignoreDefaultAction = false;
|
||||
OnTextAreaKeyBackspace(this, &ignoreDefaultAction);
|
||||
|
||||
std::size_t cursorGlyphBegin = GetGlyphIndex(m_cursorPositionBegin);
|
||||
std::size_t cursorGlyphEnd = GetGlyphIndex(m_cursorPositionEnd);
|
||||
|
||||
if (ignoreDefaultAction || cursorGlyphEnd == 0)
|
||||
break;
|
||||
|
||||
// When a text is selected, delete key does the same as delete and leave the character behind it
|
||||
if (HasSelection())
|
||||
EraseSelection();
|
||||
else
|
||||
{
|
||||
Nz::String newText;
|
||||
|
||||
if (cursorGlyphBegin > 1)
|
||||
newText.Append(m_text.SubString(0, m_text.GetCharacterPosition(cursorGlyphBegin - 1) - 1));
|
||||
|
||||
if (cursorGlyphEnd < m_text.GetLength())
|
||||
newText.Append(m_text.SubString(m_text.GetCharacterPosition(cursorGlyphEnd)));
|
||||
|
||||
// Move cursor before setting text (to prevent SetText to move our cursor)
|
||||
MoveCursor(-1);
|
||||
|
||||
SetText(newText);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case '\r':
|
||||
case '\n':
|
||||
{
|
||||
bool ignoreDefaultAction = false;
|
||||
OnTextAreaKeyReturn(this, &ignoreDefaultAction);
|
||||
|
||||
if (ignoreDefaultAction || !m_multiLineEnabled)
|
||||
break;
|
||||
|
||||
if (HasSelection())
|
||||
EraseSelection();
|
||||
|
||||
Write(Nz::String('\n'));
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
if (Nz::Unicode::GetCategory(character) == Nz::Unicode::Category_Other_Control || (m_characterFilter && !m_characterFilter(character)))
|
||||
break;
|
||||
|
||||
if (HasSelection())
|
||||
EraseSelection();
|
||||
|
||||
Write(Nz::String::Unicode(character));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TextAreaWidget::RefreshCursor()
|
||||
{
|
||||
if (m_readOnly)
|
||||
return;
|
||||
|
||||
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"));
|
||||
}
|
||||
}
|
||||
|
||||
float lineHeight = float(m_drawer.GetFont()->GetSizeInfo(m_drawer.GetCharacterSize()).lineHeight);
|
||||
|
||||
GraphicsComponent& gfxComponent = m_cursorEntity->GetComponent<GraphicsComponent>();
|
||||
gfxComponent.Clear();
|
||||
|
||||
for (unsigned int i = m_cursorPositionBegin.y; i <= m_cursorPositionEnd.y; ++i)
|
||||
{
|
||||
const auto& lineInfo = m_drawer.GetLine(i);
|
||||
|
||||
Nz::SpriteRef& cursorSprite = m_cursorSprites[i - m_cursorPositionBegin.y];
|
||||
if (i == m_cursorPositionBegin.y || i == m_cursorPositionEnd.y)
|
||||
{
|
||||
auto GetGlyphPos = [&](unsigned int localGlyphPos)
|
||||
{
|
||||
std::size_t cursorGlyph = GetGlyphIndex({ localGlyphPos, i });
|
||||
|
||||
std::size_t glyphCount = m_drawer.GetGlyphCount();
|
||||
float position;
|
||||
if (glyphCount > 0 && lineInfo.glyphIndex < cursorGlyph)
|
||||
{
|
||||
const auto& glyph = m_drawer.GetGlyph(std::min(cursorGlyph, glyphCount - 1));
|
||||
position = glyph.bounds.x;
|
||||
if (cursorGlyph >= glyphCount)
|
||||
position += glyph.bounds.width;
|
||||
}
|
||||
else
|
||||
position = 0.f;
|
||||
|
||||
return position;
|
||||
};
|
||||
|
||||
float beginX = (i == m_cursorPositionBegin.y) ? GetGlyphPos(m_cursorPositionBegin.x) : 0.f;
|
||||
float endX = (i == m_cursorPositionEnd.y) ? GetGlyphPos(m_cursorPositionEnd.x) : 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, float(m_drawer.GetFont()->GetSizeInfo(m_drawer.GetCharacterSize()).lineHeight));
|
||||
|
||||
gfxComponent.Attach(cursorSprite, Nz::Matrix4f::Translate({ beginX, lineInfo.bounds.y, 0.f }));
|
||||
SetCursorPosition(endlIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
cursorSprite->SetColor(Nz::Color(0, 0, 0, 50));
|
||||
cursorSprite->SetSize(lineInfo.bounds.width, float(m_drawer.GetFont()->GetSizeInfo(m_drawer.GetCharacterSize()).lineHeight));
|
||||
|
||||
gfxComponent.Attach(cursorSprite, Nz::Matrix4f::Translate({ 0.f, lineInfo.bounds.y, 0.f }));
|
||||
}
|
||||
SetCursorPosition({ static_cast<unsigned int>(m_drawer.GetLineGlyphCount(m_cursorPositionEnd.y)), m_cursorPositionEnd.y });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -605,8 +228,26 @@ namespace Ndk
|
||||
break;
|
||||
}
|
||||
|
||||
m_textSprite->Update(m_drawer);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <Nazara/Core/Clock.hpp>
|
||||
#include <Nazara/Core/Error.hpp>
|
||||
#include <NDK/BaseComponent.hpp>
|
||||
#include <NDK/Systems/LifetimeSystem.hpp>
|
||||
#include <NDK/Systems/PhysicsSystem2D.hpp>
|
||||
#include <NDK/Systems/PhysicsSystem3D.hpp>
|
||||
#include <NDK/Systems/VelocitySystem.hpp>
|
||||
@@ -43,6 +44,7 @@ namespace Ndk
|
||||
|
||||
void World::AddDefaultSystems()
|
||||
{
|
||||
AddSystem<LifetimeSystem>();
|
||||
AddSystem<PhysicsSystem2D>();
|
||||
AddSystem<PhysicsSystem3D>();
|
||||
AddSystem<VelocitySystem>();
|
||||
@@ -133,9 +135,9 @@ namespace Ndk
|
||||
m_waitingEntities.clear();
|
||||
|
||||
m_aliveEntities.Clear();
|
||||
m_dirtyEntities.Clear();
|
||||
m_dirtyEntities.front.Clear();
|
||||
m_freeEntityIds.Clear();
|
||||
m_killedEntities.Clear();
|
||||
m_killedEntities.front.Clear();
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -155,16 +157,26 @@ namespace Ndk
|
||||
return EntityHandle::InvalidHandle;
|
||||
}
|
||||
|
||||
return CloneEntity(original);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Clones the entity
|
||||
* \return The clone newly created
|
||||
*
|
||||
* \param original Entity handle
|
||||
*
|
||||
* \remark Cloning a disabled entity will produce an enabled clone
|
||||
*/
|
||||
const EntityHandle& World::CloneEntity(const EntityHandle& original)
|
||||
{
|
||||
const EntityHandle& clone = CreateEntity();
|
||||
if (!original->IsEnabled())
|
||||
clone->Disable();
|
||||
|
||||
const Nz::Bitset<>& componentBits = original->GetComponentBits();
|
||||
for (std::size_t i = componentBits.FindFirst(); i != componentBits.npos; i = componentBits.FindNext(i))
|
||||
{
|
||||
std::unique_ptr<BaseComponent> component(original->GetComponent(ComponentIndex(i)).Clone());
|
||||
clone->AddComponent(std::move(component));
|
||||
}
|
||||
clone->AddComponent(original->GetComponent(ComponentIndex(i)).Clone());
|
||||
|
||||
clone->Enable();
|
||||
|
||||
@@ -208,7 +220,8 @@ namespace Ndk
|
||||
}
|
||||
|
||||
// Handle killed entities before last call
|
||||
for (std::size_t i = m_killedEntities.FindFirst(); i != m_killedEntities.npos; i = m_killedEntities.FindNext(i))
|
||||
std::swap(m_killedEntities.front, m_killedEntities.back);
|
||||
for (std::size_t i = m_killedEntities.back.FindFirst(); i != m_killedEntities.back.npos; i = m_killedEntities.back.FindNext(i))
|
||||
{
|
||||
NazaraAssert(i < m_entityBlocks.size(), "Entity index out of range");
|
||||
|
||||
@@ -218,12 +231,13 @@ namespace Ndk
|
||||
entity->Destroy();
|
||||
|
||||
// Send back the identifier of the entity to the free queue
|
||||
m_freeEntityIds.UnboundedSet(entity->GetId());
|
||||
m_freeEntityIds.UnboundedSet(i);
|
||||
}
|
||||
m_killedEntities.Reset();
|
||||
m_killedEntities.back.Clear();
|
||||
|
||||
// Handle of entities which need an update from the systems
|
||||
for (std::size_t i = m_dirtyEntities.FindFirst(); i != m_dirtyEntities.npos; i = m_dirtyEntities.FindNext(i))
|
||||
std::swap(m_dirtyEntities.front, m_dirtyEntities.back);
|
||||
for (std::size_t i = m_dirtyEntities.back.FindFirst(); i != m_dirtyEntities.back.npos; i = m_dirtyEntities.back.FindNext(i))
|
||||
{
|
||||
NazaraAssert(i < m_entityBlocks.size(), "Entity index out of range");
|
||||
|
||||
@@ -234,9 +248,8 @@ namespace Ndk
|
||||
continue;
|
||||
|
||||
Nz::Bitset<>& removedComponents = entity->GetRemovedComponentBits();
|
||||
for (std::size_t j = removedComponents.FindFirst(); j != m_dirtyEntities.npos; j = removedComponents.FindNext(j))
|
||||
entity->DestroyComponent(static_cast<Ndk::ComponentIndex>(j));
|
||||
removedComponents.Reset();
|
||||
for (std::size_t j = removedComponents.FindFirst(); j != m_dirtyEntities.back.npos; j = removedComponents.FindNext(j))
|
||||
entity->DropComponent(static_cast<Ndk::ComponentIndex>(j));
|
||||
|
||||
for (auto& system : m_orderedSystems)
|
||||
{
|
||||
@@ -260,7 +273,7 @@ namespace Ndk
|
||||
}
|
||||
}
|
||||
}
|
||||
m_dirtyEntities.Reset();
|
||||
m_dirtyEntities.back.Clear();
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
Reference in New Issue
Block a user