Merge branch 'master' into SDL2

This commit is contained in:
Jérôme Leclercq
2020-05-27 11:11:21 +02:00
committed by GitHub
211 changed files with 7628 additions and 2883 deletions

View File

@@ -15,6 +15,7 @@
#include <set>
#ifndef NDK_SERVER
#include <NDK/Canvas.hpp>
#include <NDK/Console.hpp>
#include <Nazara/Core/Log.hpp>
#include <Nazara/Lua/LuaInstance.hpp>
@@ -81,7 +82,7 @@ namespace Ndk
#ifndef NDK_SERVER
struct ConsoleOverlay
{
std::unique_ptr<Console> console;
Console* console;
Nz::LuaInstance lua;
NazaraSlot(Nz::EventHandler, OnEvent, eventSlot);
@@ -114,10 +115,11 @@ namespace Ndk
Nz::RenderTarget* renderTarget;
std::unique_ptr<Nz::Window> window;
std::unique_ptr<ConsoleOverlay> console;
std::unique_ptr<Canvas> canvas;
std::unique_ptr<FPSCounterOverlay> fpsCounter;
std::unique_ptr<World> overlayWorld;
};
void SetupConsole(WindowInfo& info);
void SetupFPSCounter(WindowInfo& info);
void SetupOverlay(WindowInfo& info);

View File

@@ -112,7 +112,6 @@ namespace Ndk
}
m_overlayFlags |= OverlayFlags_Console;
}
else
{

View File

@@ -26,8 +26,6 @@ namespace Ndk
friend Canvas;
public:
struct Padding;
BaseWidget(BaseWidget* parent);
BaseWidget(const BaseWidget&) = delete;
BaseWidget(BaseWidget&&) = delete;
@@ -41,6 +39,7 @@ namespace Ndk
inline void CenterVertical();
void ClearFocus();
inline void ClearRenderingRect();
void Destroy();
@@ -68,12 +67,15 @@ namespace Ndk
inline Nz::Vector2f GetPreferredSize() const;
inline float GetPreferredWidth() const;
inline const Nz::Rectf& GetRenderingRect() const;
inline Nz::Vector2f GetSize() const;
inline float GetWidth() const;
inline std::size_t GetWidgetChildCount() const;
bool HasFocus() const;
inline void Hide();
inline bool IsVisible() const;
void Resize(const Nz::Vector2f& size);
@@ -81,6 +83,7 @@ namespace Ndk
void SetBackgroundColor(const Nz::Color& color);
void SetCursor(Nz::SystemCursor systemCursor);
void SetFocus();
void SetParent(BaseWidget* widget);
inline void SetFixedHeight(float fixedHeight);
inline void SetFixedSize(const Nz::Vector2f& fixedSize);
@@ -94,6 +97,8 @@ namespace Ndk
inline void SetMinimumSize(const Nz::Vector2f& minimumSize);
inline void SetMinimumWidth(float minimumWidth);
virtual void SetRenderingRect(const Nz::Rectf& renderingRect);
void Show(bool show = true);
BaseWidget& operator=(const BaseWidget&) = delete;
@@ -106,6 +111,8 @@ namespace Ndk
void InvalidateNode() override;
Nz::Rectf GetScissorRect() const;
virtual bool IsFocusable() const;
virtual void OnFocusLost();
virtual void OnFocusReceived();
@@ -115,6 +122,7 @@ namespace Ndk
virtual void OnMouseMoved(int x, int y, int deltaX, int deltaY);
virtual void OnMouseButtonPress(int x, int y, Nz::Mouse::Button button);
virtual void OnMouseButtonRelease(int x, int y, Nz::Mouse::Button button);
virtual void OnMouseWheelMoved(int x, int y, float delta);
virtual void OnMouseExit();
virtual void OnParentResized(const Nz::Vector2f& newSize);
virtual void OnTextEntered(char32_t character, bool repeated);
@@ -122,6 +130,8 @@ namespace Ndk
inline void SetPreferredSize(const Nz::Vector2f& preferredSize);
virtual void ShowChildren(bool show);
private:
inline BaseWidget();
@@ -137,6 +147,10 @@ namespace Ndk
struct WidgetEntity
{
EntityOwner handle;
bool isEnabled = true;
NazaraSlot(Ndk::Entity, OnEntityDisabled, onDisabledSlot);
NazaraSlot(Ndk::Entity, OnEntityEnabled, onEnabledSlot);
};
static constexpr std::size_t InvalidCanvasIndex = std::numeric_limits<std::size_t>::max();
@@ -148,6 +162,7 @@ namespace Ndk
EntityOwner m_backgroundEntity;
WorldHandle m_world;
Nz::Color m_backgroundColor;
Nz::Rectf m_renderingRect;
Nz::SpriteRef m_backgroundSprite;
Nz::SystemCursor m_cursor;
Nz::Vector2f m_maximumSize;

View File

@@ -5,6 +5,7 @@
#include <NDK/BaseWidget.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Math/Algorithm.hpp>
#include <limits>
namespace Ndk
{
@@ -12,11 +13,12 @@ namespace Ndk
m_canvasIndex(InvalidCanvasIndex),
m_canvas(nullptr),
m_backgroundColor(Nz::Color(230, 230, 230, 255)),
m_renderingRect(-std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity()),
m_cursor(Nz::SystemCursor_Default),
m_size(50.f, 50.f),
m_maximumSize(std::numeric_limits<float>::infinity()),
m_minimumSize(0.f),
m_preferredSize(-1),
m_size(50.f, 50.f),
m_widgetParent(nullptr),
m_visible(true)
{
@@ -66,6 +68,11 @@ namespace Ndk
SetPosition(GetPosition(Nz::CoordSys_Local).x, (parentSize.y - mySize.y) / 2.f);
}
inline void BaseWidget::ClearRenderingRect()
{
SetRenderingRect(Nz::Rectf(-std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity()));
}
template<typename F>
inline void BaseWidget::ForEachWidgetChild(F iterator)
{
@@ -145,6 +152,11 @@ namespace Ndk
return m_preferredSize.x;
}
inline const Nz::Rectf& BaseWidget::GetRenderingRect() const
{
return m_renderingRect;
}
inline Nz::Vector2f BaseWidget::GetSize() const
{
return Nz::Vector2f(GetWidth(), GetHeight());
@@ -160,6 +172,11 @@ namespace Ndk
return m_children.size();
}
inline void BaseWidget::Hide()
{
return Show(false);
}
inline bool BaseWidget::IsVisible() const
{
return m_visible;
@@ -237,7 +254,7 @@ namespace Ndk
{
m_preferredSize = preferredSize;
Resize(m_preferredSize);
//Resize(m_preferredSize);
}
inline bool BaseWidget::IsRegisteredToCanvas() const

View File

@@ -31,6 +31,9 @@ namespace Ndk
Canvas& operator=(const Canvas&) = delete;
Canvas& operator=(Canvas&&) = delete;
NazaraSignal(OnUnhandledKeyPressed, const Nz::EventHandler* /*eventHandler*/, const Nz::WindowEvent::KeyEvent& /*event*/);
NazaraSignal(OnUnhandledKeyReleased, const Nz::EventHandler* /*eventHandler*/, const Nz::WindowEvent::KeyEvent& /*event*/);
protected:
inline void ClearKeyboardOwner(std::size_t canvasIndex);
@@ -48,8 +51,9 @@ namespace Ndk
private:
void OnEventMouseButtonPressed(const Nz::EventHandler* eventHandler, const Nz::WindowEvent::MouseButtonEvent& event);
void OnEventMouseButtonRelease(const Nz::EventHandler* eventHandler, const Nz::WindowEvent::MouseButtonEvent& event);
void OnEventMouseMoved(const Nz::EventHandler* eventHandler, const Nz::WindowEvent::MouseMoveEvent& event);
void OnEventMouseLeft(const Nz::EventHandler* eventHandler);
void OnEventMouseMoved(const Nz::EventHandler* eventHandler, const Nz::WindowEvent::MouseMoveEvent& event);
void OnEventMouseWheelMoved(const Nz::EventHandler* eventHandler, const Nz::WindowEvent::MouseWheelEvent& event);
void OnEventKeyPressed(const Nz::EventHandler* eventHandler, const Nz::WindowEvent::KeyEvent& event);
void OnEventKeyReleased(const Nz::EventHandler* eventHandler, const Nz::WindowEvent::KeyEvent& event);
void OnEventTextEntered(const Nz::EventHandler* eventHandler, const Nz::WindowEvent::TextEvent& event);
@@ -66,8 +70,9 @@ namespace Ndk
NazaraSlot(Nz::EventHandler, OnKeyReleased, m_keyReleasedSlot);
NazaraSlot(Nz::EventHandler, OnMouseButtonPressed, m_mouseButtonPressedSlot);
NazaraSlot(Nz::EventHandler, OnMouseButtonReleased, m_mouseButtonReleasedSlot);
NazaraSlot(Nz::EventHandler, OnMouseMoved, m_mouseMovedSlot);
NazaraSlot(Nz::EventHandler, OnMouseLeft, m_mouseLeftSlot);
NazaraSlot(Nz::EventHandler, OnMouseMoved, m_mouseMovedSlot);
NazaraSlot(Nz::EventHandler, OnMouseWheelMoved, m_mouseWheelMovedSlot);
NazaraSlot(Nz::EventHandler, OnTextEntered, m_textEnteredSlot);
NazaraSlot(Nz::EventHandler, OnTextEdited, m_textEditedSlot);

View File

@@ -24,8 +24,9 @@ namespace Ndk
m_keyReleasedSlot.Connect(eventHandler.OnKeyReleased, this, &Canvas::OnEventKeyReleased);
m_mouseButtonPressedSlot.Connect(eventHandler.OnMouseButtonPressed, this, &Canvas::OnEventMouseButtonPressed);
m_mouseButtonReleasedSlot.Connect(eventHandler.OnMouseButtonReleased, this, &Canvas::OnEventMouseButtonRelease);
m_mouseMovedSlot.Connect(eventHandler.OnMouseMoved, this, &Canvas::OnEventMouseMoved);
m_mouseLeftSlot.Connect(eventHandler.OnMouseLeft, this, &Canvas::OnEventMouseLeft);
m_mouseMovedSlot.Connect(eventHandler.OnMouseMoved, this, &Canvas::OnEventMouseMoved);
m_mouseWheelMovedSlot.Connect(eventHandler.OnMouseWheelMoved, this, &Canvas::OnEventMouseWheelMoved);
m_textEnteredSlot.Connect(eventHandler.OnTextEntered, this, &Canvas::OnEventTextEntered);
m_textEditedSlot.Connect(eventHandler.OnTextEdited, this, &Canvas::OnEventTextEdited);
}
@@ -59,7 +60,7 @@ namespace Ndk
{
WidgetEntry& entry = m_widgetEntries[index];
Nz::Vector3f pos = entry.widget->GetPosition();
Nz::Vector3f pos = entry.widget->GetPosition(Nz::CoordSys_Global);
Nz::Vector2f size = entry.widget->GetSize();
entry.box.Set(pos.x, pos.y, pos.z, size.x, size.y, 1.f);

View File

@@ -11,6 +11,7 @@
#include <NDK/Components/ConstraintComponent2D.hpp>
#include <NDK/Components/DebugComponent.hpp>
#include <NDK/Components/GraphicsComponent.hpp>
#include <NDK/Components/LifetimeComponent.hpp>
#include <NDK/Components/LightComponent.hpp>
#include <NDK/Components/ListenerComponent.hpp>
#include <NDK/Components/NodeComponent.hpp>

View File

@@ -43,6 +43,7 @@ namespace Ndk
const Nz::Frustumf& GetFrustum() const override;
inline unsigned int GetLayer() const;
const Nz::Matrix4f& GetProjectionMatrix() const override;
inline const Nz::Vector3f& GetProjectionScale() const;
Nz::ProjectionType GetProjectionType() const override;
inline const Nz::Vector2f& GetSize() const;
const Nz::RenderTarget* GetTarget() const override;
@@ -54,6 +55,7 @@ namespace Ndk
inline void SetFOV(float fov);
void SetLayer(unsigned int layer);
inline void SetProjectionScale(const Nz::Vector3f& scale);
inline void SetProjectionType(Nz::ProjectionType projection);
inline void SetSize(const Nz::Vector2f& size);
inline void SetSize(float width, float height);
@@ -99,6 +101,7 @@ namespace Ndk
mutable Nz::Recti m_viewport;
const Nz::RenderTarget* m_target;
Nz::Vector2f m_size;
Nz::Vector3f m_projectionScale;
mutable bool m_frustumUpdated;
mutable bool m_projectionMatrixUpdated;
mutable bool m_viewMatrixUpdated;

View File

@@ -17,6 +17,7 @@ namespace Ndk
m_targetRegion(0.f, 0.f, 1.f, 1.f),
m_target(nullptr),
m_size(0.f),
m_projectionScale(1.f, 1.f, 1.f),
m_frustumUpdated(false),
m_projectionMatrixUpdated(false),
m_viewMatrixUpdated(false),
@@ -43,6 +44,7 @@ namespace Ndk
m_targetRegion(camera.m_targetRegion),
m_target(nullptr),
m_size(camera.m_size),
m_projectionScale(camera.m_projectionScale),
m_frustumUpdated(false),
m_projectionMatrixUpdated(false),
m_viewMatrixUpdated(false),
@@ -115,11 +117,19 @@ namespace Ndk
return m_layer;
}
/*!
* \brief Gets the projection scale of the camera
* \return Projection scale
*/
const Nz::Vector3f& CameraComponent::GetProjectionScale() const
{
return m_projectionScale;
}
/*!
* \brief Gets the size of the camera
* \return Size of the camera
*/
inline const Nz::Vector2f & CameraComponent::GetSize() const
{
return m_size;
@@ -151,12 +161,23 @@ namespace Ndk
InvalidateProjectionMatrix();
}
/*!
* \brief Sets the camera projection scale
*
* \param scale New projection scale
*/
inline void CameraComponent::SetProjectionScale(const Nz::Vector3f& scale)
{
m_projectionScale = scale;
InvalidateProjectionMatrix();
}
/*!
* \brief Sets the projection type of the camera
*
* \param projectionType Projection type of the camera
*/
inline void CameraComponent::SetProjectionType(Nz::ProjectionType projectionType)
{
m_projectionType = projectionType;

View File

@@ -20,8 +20,9 @@ namespace Ndk
class NDK_API CollisionComponent2D : public Component<CollisionComponent2D>
{
friend class PhysicsSystem2D;
friend class ConstraintComponent2D;
friend class PhysicsComponent2D;
friend class PhysicsSystem2D;
public:
CollisionComponent2D(Nz::Collider2DRef geom = Nz::Collider2DRef());
@@ -30,8 +31,12 @@ namespace Ndk
Nz::Rectf GetAABB() const;
const Nz::Collider2DRef& GetGeom() const;
const Nz::Vector2f& GetGeomOffset() const;
void Recenter(const Nz::Vector2f& origin);
void SetGeom(Nz::Collider2DRef geom);
void SetGeomOffset(const Nz::Vector2f& geomOffset);
CollisionComponent2D& operator=(Nz::Collider2DRef geom);
CollisionComponent2D& operator=(CollisionComponent2D&& collision) = default;
@@ -40,7 +45,10 @@ namespace Ndk
private:
void InitializeStaticBody();
Nz::RigidBody2D* GetRigidBody();
const Nz::RigidBody2D* GetRigidBody() const;
Nz::RigidBody2D* GetStaticBody();
const Nz::RigidBody2D* GetStaticBody() const;
void OnAttached() override;
void OnComponentAttached(BaseComponent& component) override;

View File

@@ -28,16 +28,6 @@ namespace Ndk
{
}
/*!
* \brief Gets the collision box representing the entity
* \return The physics collision box
*/
inline Nz::Rectf CollisionComponent2D::GetAABB() const
{
return m_staticBody->GetAABB();
}
/*!
* \brief Gets the geometry representing the entity
* \return A constant reference to the physics geometry
@@ -62,13 +52,13 @@ namespace Ndk
return *this;
}
/*!
* \brief Gets the static body used by the entity
* \return A pointer to the entity
*/
inline Nz::RigidBody2D* CollisionComponent2D::GetStaticBody()
{
return m_staticBody.get();
}
inline const Nz::RigidBody2D* CollisionComponent2D::GetStaticBody() const
{
return m_staticBody.get();
}
}

View File

@@ -1,13 +1,18 @@
// 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
#pragma once
#ifndef NDK_COMPONENTS_CONSTRAINTCOMPONENT2D_HPP
#define NDK_COMPONENTS_CONSTRAINTCOMPONENT2D_HPP
#include <NDK/Component.hpp>
#include <Nazara/Physics2D/Constraint2D.hpp>
#include <Nazara/Math/Vector3.hpp>
#include <vector>
#include <NDK/Component.hpp>
#include <NDK/Entity.hpp>
#include <memory>
#include <vector>
namespace Ndk
{
@@ -19,16 +24,24 @@ namespace Ndk
{
public:
ConstraintComponent2D() = default;
ConstraintComponent2D(const ConstraintComponent2D& joint) = default;
ConstraintComponent2D(const ConstraintComponent2D& joint);
ConstraintComponent2D(ConstraintComponent2D&& joint) = default;
template<typename T, typename... Args> inline Nz::ObjectRef<T> CreateConstraint(const Ndk::EntityHandle& first, const Ndk::EntityHandle& second, Args&&... args);
template<typename T, typename... Args> T* CreateConstraint(const Ndk::EntityHandle& first, const Ndk::EntityHandle& second, Args&&... args);
bool RemoveConstraint(Nz::Constraint2D* constraint);
static ComponentIndex componentIndex;
private:
struct ConstraintData
{
std::unique_ptr<Nz::Constraint2D> constraint;
std::vector<Nz::Constraint2DRef> m_constraints;
NazaraSlot(Ndk::Entity, OnEntityDestruction, onBodyADestruction);
NazaraSlot(Ndk::Entity, OnEntityDestruction, onBodyBDestruction);
};
std::vector<ConstraintData> m_constraints;
};
}

View File

@@ -1,3 +1,7 @@
// 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>
#include <NDK/Components/PhysicsComponent2D.hpp>
#include <NDK/Components/CollisionComponent2D.hpp>
@@ -5,7 +9,7 @@
namespace Ndk
{
template<typename T, typename ...Args>
Nz::ObjectRef<T> ConstraintComponent2D::CreateConstraint(const Ndk::EntityHandle& first, const Ndk::EntityHandle& second, Args && ...args)
T* ConstraintComponent2D::CreateConstraint(const Ndk::EntityHandle& first, const Ndk::EntityHandle& second, Args&& ...args)
{
auto FetchBody = [](const Ndk::EntityHandle& entity) -> Nz::RigidBody2D*
{
@@ -23,9 +27,20 @@ namespace Ndk
Nz::RigidBody2D* secondBody = FetchBody(second);
NazaraAssert(secondBody, "Second entity has no CollisionComponent2D nor PhysicsComponent2D component");
Nz::ObjectRef<T> constraint = T::New(*firstBody, *secondBody, std::forward<Args>(args)...);
m_constraints.push_back(constraint);
m_constraints.emplace_back();
auto& constraintData = m_constraints.back();
constraintData.constraint = std::make_unique<T>(*firstBody, *secondBody, std::forward<Args>(args)...);
return constraint;
constraintData.onBodyADestruction.Connect(first->OnEntityDestruction, [this, constraint = constraintData.constraint.get()](const Ndk::Entity* /*entity*/)
{
RemoveConstraint(constraint);
});
constraintData.onBodyBDestruction.Connect(second->OnEntityDestruction, [this, constraint = constraintData.constraint.get()](const Ndk::Entity* /*entity*/)
{
RemoveConstraint(constraint);
});
return static_cast<T*>(constraintData.constraint.get());
}
}

View File

@@ -16,7 +16,7 @@ namespace Ndk
{
enum class DebugDraw
{
//TODO: Collider2D
Collider2D,
Collider3D,
GraphicsAABB,
GraphicsOBB,
@@ -41,6 +41,7 @@ namespace Ndk
constexpr DebugDrawFlags DebugDraw_None = 0;
class DebugComponent;
class GraphicsComponent;
using DebugComponentHandle = Nz::ObjectHandle<DebugComponent>;
@@ -65,8 +66,14 @@ namespace Ndk
static ComponentIndex componentIndex;
private:
void DetachDebugRenderables(GraphicsComponent& gfxComponent);
inline const Nz::InstancedRenderableRef& GetDebugRenderable(DebugDraw option) const;
inline DebugDrawFlags GetEnabledFlags() const;
void OnComponentDetached(BaseComponent& component) override;
void OnDetached() override;
inline void UpdateDebugRenderable(DebugDraw option, Nz::InstancedRenderableRef renderable);
inline void UpdateEnabledFlags(DebugDrawFlags flags);

View File

@@ -0,0 +1,40 @@
// Copyright (C) 2017 Jérôme Leclercq
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
#pragma once
#ifndef NDK_COMPONENTS_LIFETIMECOMPONENT_HPP
#define NDK_COMPONENTS_LIFETIMECOMPONENT_HPP
#include <NDK/Component.hpp>
namespace Ndk
{
class LifetimeComponent;
using LifetimeComponentHandle = Nz::ObjectHandle<LifetimeComponent>;
class NDK_API LifetimeComponent : public Component<LifetimeComponent>
{
friend class LifetimeSystem;
public:
inline LifetimeComponent(float lifetime);
LifetimeComponent(const LifetimeComponent&) = default;
~LifetimeComponent() = default;
inline float GetRemainingTime() const;
static ComponentIndex componentIndex;
private:
inline bool UpdateLifetime(float elapsedTime);
float m_lifetime;
};
}
#include <NDK/Components/LifetimeComponent.inl>
#endif // NDK_COMPONENTS_LIFETIMECOMPONENT_HPP

View File

@@ -0,0 +1,24 @@
// 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
{
inline LifetimeComponent::LifetimeComponent(float lifetime) :
m_lifetime(lifetime)
{
}
inline float Ndk::LifetimeComponent::GetRemainingTime() const
{
return m_lifetime;
}
inline bool LifetimeComponent::UpdateLifetime(float elapsedTime)
{
m_lifetime -= elapsedTime;
return m_lifetime < 0.f;
}
}

View File

@@ -40,6 +40,7 @@ namespace Ndk
inline void EnableNodeSynchronization(bool nodeSynchronization);
inline void ForceSleep();
inline void ForEachArbiter(const std::function<void(Nz::Arbiter2D&)>& callback);
inline Nz::Rectf GetAABB() const;
@@ -83,10 +84,15 @@ namespace Ndk
inline void UpdateVelocity(const Nz::Vector2f& gravity, float damping, float deltaTime);
inline void Wakeup();
static ComponentIndex componentIndex;
private:
inline void ApplyPhysicsState(Nz::RigidBody2D& rigidBody) const;
inline void CopyPhysicsState(const Nz::RigidBody2D& rigidBody);
Nz::RigidBody2D* GetRigidBody();
const Nz::RigidBody2D* GetRigidBody() const;
void OnAttached() override;
void OnComponentAttached(BaseComponent& component) override;
@@ -94,7 +100,27 @@ namespace Ndk
void OnDetached() override;
void OnEntityDestruction() override;
struct PendingPhysObjectStates
{
struct ShapeStates
{
Nz::Vector2f surfaceVelocity;
float elasticity;
float friction;
};
VelocityFunc velocityFunc;
std::vector<ShapeStates> shapes;
Nz::RadianAnglef angularVelocity;
Nz::Vector2f massCenter;
Nz::Vector2f velocity;
bool valid = false;
float mass;
float momentOfInertia;
};
std::unique_ptr<Nz::RigidBody2D> m_object;
PendingPhysObjectStates m_pendingStates;
bool m_nodeSynchronizationEnabled;
};
}

View File

@@ -20,10 +20,10 @@ namespace Ndk
*
* \param physics PhysicsComponent2D to copy
*/
inline PhysicsComponent2D::PhysicsComponent2D(const PhysicsComponent2D& physics)
inline PhysicsComponent2D::PhysicsComponent2D(const PhysicsComponent2D& physics) :
m_nodeSynchronizationEnabled(physics.m_nodeSynchronizationEnabled)
{
// No copy of physical object (because we only create it when attached to an entity)
NazaraUnused(physics);
CopyPhysicsState(*physics.GetRigidBody());
}
/*!
@@ -138,6 +138,16 @@ namespace Ndk
m_entity->Invalidate();
}
/*!
TODO
*/
inline void PhysicsComponent2D::ForceSleep()
{
NazaraAssert(m_object, "Invalid physics object");
return m_object->ForceSleep();
}
/*!
TODO
*/
@@ -649,6 +659,57 @@ namespace Ndk
m_object->UpdateVelocity(gravity, damping, deltaTime);
}
/*!
TODO
*/
inline void PhysicsComponent2D::Wakeup()
{
NazaraAssert(m_object, "Invalid physics object");
return m_object->Wakeup();
}
inline void PhysicsComponent2D::ApplyPhysicsState(Nz::RigidBody2D& rigidBody) const
{
assert(m_pendingStates.valid);
rigidBody.SetAngularVelocity(m_pendingStates.angularVelocity);
rigidBody.SetMass(m_pendingStates.mass);
rigidBody.SetMassCenter(m_pendingStates.massCenter);
rigidBody.SetMomentOfInertia(m_pendingStates.momentOfInertia);
rigidBody.SetVelocity(m_pendingStates.velocity);
rigidBody.SetVelocityFunction(m_pendingStates.velocityFunc);
for (std::size_t i = 0; i < m_pendingStates.shapes.size(); ++i)
{
auto& shapeData = m_pendingStates.shapes[i];
rigidBody.SetElasticity(i, shapeData.elasticity);
rigidBody.SetFriction(i, shapeData.friction);
rigidBody.SetSurfaceVelocity(i, shapeData.surfaceVelocity);
}
}
inline void PhysicsComponent2D::CopyPhysicsState(const Nz::RigidBody2D& rigidBody)
{
m_pendingStates.valid = true;
m_pendingStates.angularVelocity = rigidBody.GetAngularVelocity();
m_pendingStates.mass = rigidBody.GetMass();
m_pendingStates.massCenter = rigidBody.GetMassCenter();
m_pendingStates.momentOfInertia = rigidBody.GetMomentOfInertia();
m_pendingStates.velocity = rigidBody.GetVelocity();
m_pendingStates.velocityFunc = rigidBody.GetVelocityFunction();
m_pendingStates.shapes.resize(rigidBody.GetShapeCount());
for (std::size_t i = 0; i < m_pendingStates.shapes.size(); ++i)
{
auto& shapeData = m_pendingStates.shapes[i];
shapeData.elasticity = rigidBody.GetElasticity(i);
shapeData.friction = rigidBody.GetFriction(i);
shapeData.surfaceVelocity = rigidBody.GetSurfaceVelocity(i);
}
}
/*!
* \brief Gets the underlying physics object
* \return A reference to the physics object
@@ -657,4 +718,9 @@ namespace Ndk
{
return m_object.get();
}
inline const Nz::RigidBody2D* PhysicsComponent2D::GetRigidBody() const
{
return m_object.get();
}
}

View File

@@ -14,24 +14,29 @@
#include <Nazara/Graphics/TextSprite.hpp>
#include <Nazara/Utility/Node.hpp>
#include <Nazara/Utility/SimpleTextDrawer.hpp>
#include <NDK/BaseWidget.hpp>
#include <NDK/EntityOwner.hpp>
namespace Nz
{
class LuaState;
struct WindowEvent;
}
namespace Ndk
{
class AbstractTextAreaWidget;
class Console;
class Entity;
class RichTextAreaWidget;
class ScrollAreaWidget;
class TextAreaWidget;
using ConsoleHandle = Nz::ObjectHandle<Console>;
class NDK_API Console : public Nz::Node, public Nz::HandledObject<Console>
class NDK_API Console : public BaseWidget, public Nz::HandledObject<Console>
{
public:
Console(World& world, const Nz::Vector2f& size, Nz::LuaState& state);
Console(BaseWidget* parent);
Console(const Console& console) = delete;
Console(Console&& console) = default;
~Console() = default;
@@ -39,34 +44,25 @@ namespace Ndk
void AddLine(const Nz::String& text, const Nz::Color& color = Nz::Color::White);
void Clear();
void ClearFocus();
inline unsigned int GetCharacterSize() const;
inline const EntityHandle& GetHistory() const;
inline const EntityHandle& GetHistoryBackground() const;
inline const EntityHandle& GetInput() const;
inline const EntityHandle& GetInputBackground() const;
inline const Nz::Vector2f& GetSize() const;
inline const RichTextAreaWidget* GetHistory() const;
inline const TextAreaWidget* GetInput() const;
inline const Nz::FontRef& GetTextFont() const;
inline bool IsVisible() const;
void SendCharacter(char32_t character);
void SendEvent(const Nz::WindowEvent& event);
void SetCharacterSize(unsigned int size);
void SetSize(const Nz::Vector2f& size);
void SetFocus();
void SetTextFont(Nz::FontRef font);
void Show(bool show = true);
Console& operator=(const Console& console) = delete;
Console& operator=(Console&& console) = default;
NazaraSignal(OnCommand, Console* /*console*/, const Nz::String& /*command*/);
private:
void AddLineInternal(const Nz::String& text, const Nz::Color& color = Nz::Color::White);
void ExecuteInput();
void Layout();
void RefreshHistory();
void ExecuteInput(const AbstractTextAreaWidget* textArea, bool* ignoreDefaultAction);
void Layout() override;
struct Line
{
@@ -77,20 +73,10 @@ namespace Ndk
std::size_t m_historyPosition;
std::vector<Nz::String> m_commandHistory;
std::vector<Line> m_historyLines;
EntityOwner m_historyBackground;
EntityOwner m_history;
EntityOwner m_input;
EntityOwner m_inputBackground;
ScrollAreaWidget* m_historyArea;
RichTextAreaWidget* m_history;
TextAreaWidget* m_input;
Nz::FontRef m_defaultFont;
Nz::LuaState& m_state;
Nz::SpriteRef m_historyBackgroundSprite;
Nz::SpriteRef m_inputBackgroundSprite;
Nz::SimpleTextDrawer m_historyDrawer;
Nz::SimpleTextDrawer m_inputDrawer;
Nz::TextSpriteRef m_historyTextSprite;
Nz::TextSpriteRef m_inputTextSprite;
Nz::Vector2f m_size;
bool m_opened;
unsigned int m_characterSize;
unsigned int m_maxHistoryLines;
};

View File

@@ -19,51 +19,21 @@ namespace Ndk
* \return History of the console
*/
inline const EntityHandle& Console::GetHistory() const
inline const RichTextAreaWidget* Console::GetHistory() const
{
return m_history;
}
/*!
* \brief Gets the entity representing the background of the console's history
* \return Background history of the console
*/
inline const EntityHandle& Console::GetHistoryBackground() const
{
return m_historyBackground;
}
/*!
* \brief Gets the entity representing the input of the console
* \return Input of the console
*/
inline const EntityHandle& Console::GetInput() const
inline const TextAreaWidget* Console::GetInput() const
{
return m_input;
}
/*!
* \brief Gets the entity representing the background of the console's input
* \return Background input of the console
*/
inline const EntityHandle& Console::GetInputBackground() const
{
return m_inputBackground;
}
/*!
* \brief Gets the size of the console
* \return Size (Width, Height) of the console
*/
inline const Nz::Vector2f& Console::GetSize() const
{
return m_size;
}
/*!
* \brief Gets the font used by the console
* \return A reference to the font currenty used
@@ -73,14 +43,4 @@ namespace Ndk
{
return m_defaultFont;
}
/*!
* \brief Checks whether the console is visible
* \return true If it is the case
*/
inline bool Console::IsVisible() const
{
return m_opened;
}
}

View File

@@ -43,6 +43,10 @@ namespace Ndk
const EntityHandle& Clone() const;
inline void Disable();
std::unique_ptr<BaseComponent> DropComponent(ComponentIndex index);
template<typename ComponentType> std::unique_ptr<BaseComponent> DropComponent();
void Enable(bool enable = true);
inline BaseComponent& GetComponent(ComponentIndex index);
@@ -74,6 +78,8 @@ namespace Ndk
Entity& operator=(Entity&&) = delete;
NazaraSignal(OnEntityDestruction, Entity* /*entity*/);
NazaraSignal(OnEntityDisabled, Entity* /*entity*/);
NazaraSignal(OnEntityEnabled, Entity* /*entity*/);
private:
Entity(World* world, EntityId id);
@@ -81,8 +87,6 @@ namespace Ndk
void Create();
void Destroy();
void DestroyComponent(ComponentIndex index);
inline Nz::Bitset<>& GetRemovedComponentBits();
inline void RegisterEntityList(EntityList* list);

View File

@@ -64,6 +64,15 @@ namespace Ndk
* \remark Produces a NazaraAssert if component is not available in this entity
*/
template<typename ComponentType>
std::unique_ptr<BaseComponent> Entity::DropComponent()
{
static_assert(std::is_base_of<BaseComponent, ComponentType>::value, "ComponentType is not a component");
ComponentIndex index = GetComponentIndex<ComponentType>();
return DropComponent(index);
}
template<typename ComponentType>
ComponentType& Entity::GetComponent()
{

View File

@@ -55,7 +55,7 @@ namespace Ndk
World* m_world;
};
class NDK_API EntityList::iterator : public std::iterator<std::forward_iterator_tag, const EntityHandle>
class NDK_API EntityList::iterator
{
friend EntityList;
@@ -73,6 +73,12 @@ namespace Ndk
friend inline void swap(iterator& lhs, iterator& rhs);
using difference_type = std::ptrdiff_t;
using iterator_category = std::forward_iterator_tag;
using pointer = EntityHandle*;
using reference = EntityHandle&;
using value_type = EntityHandle;
private:
inline iterator(const EntityList* world, std::size_t nextId);

View File

@@ -125,14 +125,14 @@ namespace Nz
state.Pop();
}
mat->Set(values);
*mat = Matrix4d(values);
return 1;
}
default:
{
if (state.IsOfType(index, "Matrix4"))
mat->Set(*static_cast<Matrix4d*>(state.ToUserdata(index)));
*mat = *static_cast<Matrix4d*>(state.ToUserdata(index));
return 1;
}

View File

@@ -159,8 +159,13 @@ namespace Ndk
*/
inline bool StateMachine::Update(float elapsedTime)
{
for (StateTransition& transition : m_transitions)
// Use a classic for instead of a range-for because some state may push/pop on enter/leave, adding new transitions as we iterate
// (range-for is a problem here because it doesn't handle mutable containers)
for (std::size_t i = 0; i < m_transitions.size(); ++i)
{
StateTransition& transition = m_transitions[i];
switch (transition.type)
{
case TransitionType::Pop:

View File

@@ -6,6 +6,7 @@
#define NDK_SYSTEMS_GLOBAL_HPP
#include <NDK/Systems/DebugSystem.hpp>
#include <NDK/Systems/LifetimeSystem.hpp>
#include <NDK/Systems/ListenerSystem.hpp>
#include <NDK/Systems/ParticleSystem.hpp>
#include <NDK/Systems/PhysicsSystem2D.hpp>

View File

@@ -22,20 +22,24 @@ namespace Ndk
DebugSystem();
~DebugSystem() = default;
void EnableDepthBuffer(bool enable);
inline bool IsDepthBufferEnabled() const;
static SystemIndex systemIndex;
private:
Nz::InstancedRenderableRef GenerateBox(Nz::Boxf box);
Nz::InstancedRenderableRef GenerateCollision2DMesh(Entity* entity, Nz::Vector3f* offset);
Nz::InstancedRenderableRef GenerateCollision3DMesh(Entity* entity);
std::pair<Nz::IndexBufferRef, Nz::VertexBufferRef> GetBoxMesh();
Nz::MaterialRef GetCollisionMaterial();
Nz::MaterialRef GetGlobalAABBMaterial();
Nz::MaterialRef GetLocalAABBMaterial();
Nz::MaterialRef GetOBBMaterial();
std::pair<Nz::IndexBufferRef, Nz::VertexBufferRef> GetBoxMesh();
void OnEntityValidation(Entity* entity, bool justAdded) override;
void OnUpdate(float elapsedTime) override;
Nz::MaterialRef m_globalAabbMaterial;
@@ -44,6 +48,7 @@ namespace Ndk
Nz::MaterialRef m_obbMaterial;
Nz::IndexBufferRef m_boxMeshIndexBuffer;
Nz::VertexBufferRef m_boxMeshVertexBuffer;
bool m_isDepthBufferEnabled;
};
}

View File

@@ -4,3 +4,10 @@
#include <NDK/Systems/DebugSystem.hpp>
namespace Ndk
{
inline bool DebugSystem::IsDepthBufferEnabled() const
{
return m_isDepthBufferEnabled;
}
}

View File

@@ -0,0 +1,29 @@
// Copyright (C) 2017 Jérôme Leclercq
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
#pragma once
#ifndef NDK_SYSTEMS_LIFETIMESYSTEM_HPP
#define NDK_SYSTEMS_LIFETIMESYSTEM_HPP
#include <NDK/System.hpp>
namespace Ndk
{
class NDK_API LifetimeSystem : public System<LifetimeSystem>
{
public:
LifetimeSystem();
~LifetimeSystem() = default;
static SystemIndex systemIndex;
private:
void OnUpdate(float elapsedTime) override;
};
}
#include <NDK/Systems/LifetimeSystem.inl>
#endif // NDK_SYSTEMS_LIFETIMESYSTEM_HPP

View File

@@ -0,0 +1,3 @@
// 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

View File

@@ -51,9 +51,11 @@ namespace Ndk
bool NearestBodyQuery(const Nz::Vector2f& from, float maxDistance, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, EntityHandle* nearestBody = nullptr);
bool NearestBodyQuery(const Nz::Vector2f& from, float maxDistance, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, NearestQueryResult* result);
void 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);
bool RaycastQuery(const Nz::Vector2f& from, const Nz::Vector2f& to, float radius, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, std::vector<RaycastHit>* hitInfos);
bool RaycastQueryFirst(const Nz::Vector2f& from, const Nz::Vector2f& to, float radius, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, RaycastHit* hitInfo = nullptr);
void RegionQuery(const Nz::Rectf& boundingBox, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, const std::function<void(const EntityHandle&)>& callback);
void RegionQuery(const Nz::Rectf& boundingBox, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, std::vector<EntityHandle>* bodies);
void RegisterCallbacks(unsigned int collisionId, Callback callbacks);
@@ -63,6 +65,7 @@ namespace Ndk
inline void SetGravity(const Nz::Vector2f& gravity);
inline void SetIterationCount(std::size_t iterationCount);
inline void SetMaxStepCount(std::size_t maxStepCount);
inline void SetSleepTime(float sleepTime);
inline void SetStepSize(float stepSize);
inline void UseSpatialHash(float cellSize, std::size_t entityCount);

View File

@@ -2,6 +2,8 @@
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
#include <NDK/Systems/PhysicsSystem2D.hpp>
namespace Ndk
{
inline float PhysicsSystem2D::GetDamping() const
@@ -49,6 +51,11 @@ namespace Ndk
GetPhysWorld().SetMaxStepCount(maxStepCount);
}
inline void PhysicsSystem2D::SetSleepTime(float sleepTime)
{
GetPhysWorld().SetSleepTime(sleepTime);
}
inline void PhysicsSystem2D::SetStepSize(float stepSize)
{
GetPhysWorld().SetStepSize(stepSize);

View File

@@ -5,12 +5,16 @@
#ifndef NDK_WIDGETS_GLOBAL_HPP
#define NDK_WIDGETS_GLOBAL_HPP
#include <NDK/Widgets/AbstractTextAreaWidget.hpp>
#include <NDK/Widgets/BoxLayout.hpp>
#include <NDK/Widgets/ButtonWidget.hpp>
#include <NDK/Widgets/CheckboxWidget.hpp>
#include <NDK/Widgets/Enums.hpp>
#include <NDK/Widgets/ImageWidget.hpp>
#include <NDK/Widgets/LabelWidget.hpp>
#include <NDK/Widgets/ProgressBarWidget.hpp>
#include <NDK/Widgets/RichTextAreaWidget.hpp>
#include <NDK/Widgets/ScrollAreaWidget.hpp>
#include <NDK/Widgets/TextAreaWidget.hpp>
#endif // NDK_WIDGETS_GLOBAL_HPP

View File

@@ -0,0 +1,135 @@
// Copyright (C) 2017 Jérôme Leclercq
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
#pragma once
#ifndef NDK_WIDGETS_ABSTRACTTEXTAREAWIDGET_HPP
#define NDK_WIDGETS_ABSTRACTTEXTAREAWIDGET_HPP
#include <Nazara/Graphics/TextSprite.hpp>
#include <Nazara/Utility/AbstractTextDrawer.hpp>
#include <NDK/BaseWidget.hpp>
#include <NDK/Widgets/Enums.hpp>
#include <functional>
#include <vector>
namespace Ndk
{
class NDK_API AbstractTextAreaWidget : public BaseWidget
{
public:
using CharacterFilter = std::function<bool(char32_t)>;
AbstractTextAreaWidget(BaseWidget* parent);
AbstractTextAreaWidget(const AbstractTextAreaWidget&) = delete;
AbstractTextAreaWidget(AbstractTextAreaWidget&&) = default;
~AbstractTextAreaWidget() = default;
virtual void Clear();
//virtual TextAreaWidget* Clone() const = 0;
void EnableLineWrap(bool enable = true);
inline void EnableMultiline(bool enable = true);
inline void EnableTabWriting(bool enable = true);
inline void Erase(std::size_t glyphPosition);
virtual void Erase(std::size_t firstGlyph, std::size_t lastGlyph) = 0;
inline void EraseSelection();
inline const CharacterFilter& GetCharacterFilter() const;
inline const Nz::Vector2ui& GetCursorPosition() const;
inline Nz::Vector2ui GetCursorPosition(std::size_t glyphIndex) const;
inline EchoMode GetEchoMode() const;
inline std::size_t GetGlyphIndex() const;
inline std::size_t GetGlyphIndex(const Nz::Vector2ui& cursorPosition) const;
inline const Nz::String& GetText() const;
Nz::Vector2ui GetHoveredGlyph(float x, float y) const;
inline bool HasSelection() const;
inline bool IsLineWrapEnabled() const;
inline bool IsMultilineEnabled() const;
inline bool IsReadOnly() const;
inline bool IsTabWritingEnabled() const;
inline void MoveCursor(int offset);
inline void MoveCursor(const Nz::Vector2i& offset);
inline Nz::Vector2ui NormalizeCursorPosition(Nz::Vector2ui cursorPosition) const;
inline void SetCharacterFilter(CharacterFilter filter);
inline void SetCursorPosition(std::size_t glyphIndex);
inline void SetCursorPosition(Nz::Vector2ui cursorPosition);
inline void SetEchoMode(EchoMode echoMode);
inline void SetReadOnly(bool readOnly = true);
inline void SetSelection(Nz::Vector2ui fromPosition, Nz::Vector2ui toPosition);
inline void Write(const Nz::String& text);
inline void Write(const Nz::String& text, const Nz::Vector2ui& glyphPosition);
virtual void Write(const Nz::String& text, std::size_t glyphPosition) = 0;
AbstractTextAreaWidget& operator=(const AbstractTextAreaWidget&) = delete;
AbstractTextAreaWidget& operator=(AbstractTextAreaWidget&&) = default;
NazaraSignal(OnTextAreaCursorMove, const AbstractTextAreaWidget* /*textArea*/, Nz::Vector2ui* /*newCursorPosition*/);
NazaraSignal(OnTextAreaKeyBackspace, const AbstractTextAreaWidget* /*textArea*/, bool* /*ignoreDefaultAction*/);
NazaraSignal(OnTextAreaKeyDown, const AbstractTextAreaWidget* /*textArea*/, bool* /*ignoreDefaultAction*/);
NazaraSignal(OnTextAreaKeyEnd, const AbstractTextAreaWidget* /*textArea*/, bool* /*ignoreDefaultAction*/);
NazaraSignal(OnTextAreaKeyHome, const AbstractTextAreaWidget* /*textArea*/, bool* /*ignoreDefaultAction*/);
NazaraSignal(OnTextAreaKeyLeft, const AbstractTextAreaWidget* /*textArea*/, bool* /*ignoreDefaultAction*/);
NazaraSignal(OnTextAreaKeyReturn, const AbstractTextAreaWidget* /*textArea*/, bool* /*ignoreDefaultAction*/);
NazaraSignal(OnTextAreaKeyRight, const AbstractTextAreaWidget* /*textArea*/, bool* /*ignoreDefaultAction*/);
NazaraSignal(OnTextAreaKeyUp, const AbstractTextAreaWidget* /*textArea*/, bool* /*ignoreDefaultAction*/);
NazaraSignal(OnTextAreaSelection, const AbstractTextAreaWidget* /*textArea*/, Nz::Vector2ui* /*start*/, Nz::Vector2ui* /*end*/);
protected:
virtual Nz::AbstractTextDrawer& GetTextDrawer() = 0;
virtual const Nz::AbstractTextDrawer& GetTextDrawer() const = 0;
void Layout() override;
virtual void HandleIndentation(bool add) = 0;
virtual void HandleSelectionIndentation(bool add) = 0;
virtual void HandleWordCursorMove(bool left) = 0;
bool IsFocusable() const override;
void OnFocusLost() override;
void OnFocusReceived() override;
bool OnKeyPressed(const Nz::WindowEvent::KeyEvent& key) override;
void OnKeyReleased(const Nz::WindowEvent::KeyEvent& key) override;
void OnMouseButtonPress(int /*x*/, int /*y*/, Nz::Mouse::Button button) override;
void OnMouseButtonRelease(int /*x*/, int /*y*/, Nz::Mouse::Button button) override;
void OnMouseEnter() override;
void OnMouseMoved(int x, int y, int deltaX, int deltaY) override;
void OnTextEntered(char32_t character, bool repeated) override;
inline void SetCursorPositionInternal(std::size_t glyphIndex);
inline void SetCursorPositionInternal(Nz::Vector2ui cursorPosition);
void RefreshCursor();
virtual void UpdateDisplayText() = 0;
void UpdateTextSprite();
CharacterFilter m_characterFilter;
EchoMode m_echoMode;
EntityHandle m_cursorEntity;
EntityHandle m_textEntity;
Nz::TextSpriteRef m_textSprite;
Nz::Vector2ui m_cursorPositionBegin;
Nz::Vector2ui m_cursorPositionEnd;
Nz::Vector2ui m_selectionCursor;
std::vector<Nz::SpriteRef> m_cursorSprites;
bool m_isLineWrapEnabled;
bool m_isMouseButtonDown;
bool m_multiLineEnabled;
bool m_readOnly;
bool m_tabEnabled; // writes (Shift+)Tab character if set to true
};
}
#include <NDK/Widgets/AbstractTextAreaWidget.inl>
#endif // NDK_WIDGETS_ABSTRACTTEXTAREAWIDGET_HPP

View File

@@ -0,0 +1,252 @@
// 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>
namespace Ndk
{
inline void AbstractTextAreaWidget::EnableMultiline(bool enable)
{
m_multiLineEnabled = enable;
}
inline void AbstractTextAreaWidget::EnableTabWriting(bool enable)
{
m_tabEnabled = enable;
}
inline void AbstractTextAreaWidget::Erase(std::size_t glyphPosition)
{
Erase(glyphPosition, glyphPosition + 1U);
}
inline void AbstractTextAreaWidget::EraseSelection()
{
if (!HasSelection())
return;
Erase(GetGlyphIndex(m_cursorPositionBegin), GetGlyphIndex(m_cursorPositionEnd));
}
inline const AbstractTextAreaWidget::CharacterFilter& AbstractTextAreaWidget::GetCharacterFilter() const
{
return m_characterFilter;
}
inline const Nz::Vector2ui& AbstractTextAreaWidget::GetCursorPosition() const
{
return m_cursorPositionBegin;
}
Nz::Vector2ui AbstractTextAreaWidget::GetCursorPosition(std::size_t glyphIndex) const
{
const Nz::AbstractTextDrawer& textDrawer = GetTextDrawer();
glyphIndex = std::min(glyphIndex, GetTextDrawer().GetGlyphCount());
std::size_t lineCount = textDrawer.GetLineCount();
std::size_t line = 0U;
for (std::size_t i = line + 1; i < lineCount; ++i)
{
if (textDrawer.GetLine(i).glyphIndex > glyphIndex)
break;
line = i;
}
const auto& lineInfo = textDrawer.GetLine(line);
Nz::Vector2ui cursorPos;
cursorPos.y = static_cast<unsigned int>(line);
cursorPos.x = static_cast<unsigned int>(glyphIndex - lineInfo.glyphIndex);
return cursorPos;
}
inline EchoMode AbstractTextAreaWidget::GetEchoMode() const
{
return m_echoMode;
}
inline std::size_t AbstractTextAreaWidget::GetGlyphIndex() const
{
return GetGlyphIndex(m_cursorPositionBegin);
}
inline std::size_t AbstractTextAreaWidget::GetGlyphIndex(const Nz::Vector2ui& cursorPosition) const
{
const Nz::AbstractTextDrawer& textDrawer = GetTextDrawer();
std::size_t glyphIndex = textDrawer.GetLine(cursorPosition.y).glyphIndex + cursorPosition.x;
if (textDrawer.GetLineCount() > cursorPosition.y + 1)
glyphIndex = std::min(glyphIndex, textDrawer.GetLine(cursorPosition.y + 1).glyphIndex - 1);
else
glyphIndex = std::min(glyphIndex, textDrawer.GetGlyphCount());
return glyphIndex;
}
inline bool AbstractTextAreaWidget::HasSelection() const
{
return m_cursorPositionBegin != m_cursorPositionEnd;
}
inline bool AbstractTextAreaWidget::IsLineWrapEnabled() const
{
return m_isLineWrapEnabled;
}
inline bool AbstractTextAreaWidget::IsMultilineEnabled() const
{
return m_multiLineEnabled;
}
inline bool AbstractTextAreaWidget::IsTabWritingEnabled() const
{
return m_tabEnabled;
}
inline bool AbstractTextAreaWidget::IsReadOnly() const
{
return m_readOnly;
}
inline void AbstractTextAreaWidget::MoveCursor(int offset)
{
std::size_t cursorGlyph = GetGlyphIndex(m_cursorPositionBegin);
if (offset >= 0)
SetCursorPosition(cursorGlyph + static_cast<std::size_t>(offset));
else
{
std::size_t nOffset = static_cast<std::size_t>(-offset);
if (nOffset >= cursorGlyph)
SetCursorPosition(0);
else
SetCursorPosition(cursorGlyph - nOffset);
}
}
inline void AbstractTextAreaWidget::MoveCursor(const Nz::Vector2i& offset)
{
auto ClampOffset = [] (unsigned int cursorPosition, int cursorOffset) -> unsigned int
{
if (cursorOffset >= 0)
return cursorPosition + cursorOffset;
else
{
unsigned int nOffset = static_cast<unsigned int>(-cursorOffset);
if (nOffset >= cursorPosition)
return 0;
else
return cursorPosition - nOffset;
}
};
Nz::Vector2ui cursorPosition = m_cursorPositionBegin;
cursorPosition.x = ClampOffset(static_cast<unsigned int>(cursorPosition.x), offset.x);
cursorPosition.y = ClampOffset(static_cast<unsigned int>(cursorPosition.y), offset.y);
SetCursorPosition(cursorPosition);
}
inline Nz::Vector2ui AbstractTextAreaWidget::NormalizeCursorPosition(Nz::Vector2ui cursorPosition) const
{
const Nz::AbstractTextDrawer& textDrawer = GetTextDrawer();
std::size_t lineCount = textDrawer.GetLineCount();
if (cursorPosition.y >= lineCount)
cursorPosition.y = static_cast<unsigned int>(lineCount - 1);
const auto& lineInfo = textDrawer.GetLine(cursorPosition.y);
if (cursorPosition.y + 1 < lineCount)
{
const auto& nextLineInfo = textDrawer.GetLine(cursorPosition.y + 1);
cursorPosition.x = std::min(cursorPosition.x, static_cast<unsigned int>(nextLineInfo.glyphIndex - lineInfo.glyphIndex - 1));
}
return cursorPosition;
}
inline void AbstractTextAreaWidget::SetCharacterFilter(CharacterFilter filter)
{
m_characterFilter = std::move(filter);
}
inline void AbstractTextAreaWidget::SetCursorPosition(std::size_t glyphIndex)
{
Nz::Vector2ui position = GetCursorPosition(glyphIndex);
Nz::Vector2ui newPosition = position;
OnTextAreaCursorMove(this, &newPosition);
if (position == newPosition)
SetCursorPositionInternal(position);
else
SetCursorPositionInternal(GetGlyphIndex(newPosition));
}
inline void AbstractTextAreaWidget::SetCursorPosition(Nz::Vector2ui cursorPosition)
{
OnTextAreaCursorMove(this, &cursorPosition);
return SetCursorPositionInternal(NormalizeCursorPosition(cursorPosition));
}
inline void AbstractTextAreaWidget::SetEchoMode(EchoMode echoMode)
{
m_echoMode = echoMode;
UpdateDisplayText();
}
inline void AbstractTextAreaWidget::SetReadOnly(bool readOnly)
{
m_readOnly = readOnly;
m_cursorEntity->Enable(!m_readOnly && HasFocus());
}
inline void AbstractTextAreaWidget::SetSelection(Nz::Vector2ui fromPosition, Nz::Vector2ui toPosition)
{
// Ensure begin is before end
if (toPosition.y < fromPosition.y || (toPosition.y == fromPosition.y && toPosition.x < fromPosition.x))
std::swap(fromPosition, toPosition);
if (m_cursorPositionBegin != fromPosition || m_cursorPositionEnd != toPosition)
{
OnTextAreaSelection(this, &fromPosition, &toPosition);
// Ensure begin is before end a second time (in case signal changed it)
if (toPosition.y < fromPosition.y || (toPosition.y == fromPosition.y && toPosition.x < fromPosition.x))
std::swap(fromPosition, toPosition);
m_cursorPositionBegin = NormalizeCursorPosition(fromPosition);
m_cursorPositionEnd = NormalizeCursorPosition(toPosition);
RefreshCursor();
}
}
inline void AbstractTextAreaWidget::Write(const Nz::String& text)
{
Write(text, GetGlyphIndex(m_cursorPositionBegin));
}
inline void AbstractTextAreaWidget::Write(const Nz::String& text, const Nz::Vector2ui& glyphPosition)
{
Write(text, GetGlyphIndex(glyphPosition));
}
void AbstractTextAreaWidget::SetCursorPositionInternal(std::size_t glyphIndex)
{
return SetCursorPositionInternal(GetCursorPosition(glyphIndex));
}
inline void AbstractTextAreaWidget::SetCursorPositionInternal(Nz::Vector2ui cursorPosition)
{
m_cursorPositionBegin = cursorPosition;
m_cursorPositionEnd = m_cursorPositionBegin;
RefreshCursor();
}
}

View File

@@ -10,7 +10,8 @@ namespace Ndk
{
m_textSprite->Update(drawer);
SetMinimumSize(Nz::Vector2f(m_textSprite->GetBoundingVolume().obb.localBox.GetLengths()));
SetPreferredSize(Nz::Vector2f(m_textSprite->GetBoundingVolume().obb.localBox.GetLengths()));
Nz::Vector2f size = Nz::Vector2f(m_textSprite->GetBoundingVolume().obb.localBox.GetLengths());
SetMinimumSize(size);
SetPreferredSize(size);
}
}

View File

@@ -0,0 +1,68 @@
// Copyright (C) 2017 Jérôme Leclercq
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
#pragma once
#ifndef NDK_WIDGETS_RICHTEXTAREAWIDGET_HPP
#define NDK_WIDGETS_RICHTEXTAREAWIDGET_HPP
#include <Nazara/Utility/RichTextDrawer.hpp>
#include <NDK/Widgets/AbstractTextAreaWidget.hpp>
namespace Ndk
{
class NDK_API RichTextAreaWidget : public AbstractTextAreaWidget
{
public:
RichTextAreaWidget(BaseWidget* parent);
RichTextAreaWidget(const RichTextAreaWidget&) = delete;
RichTextAreaWidget(RichTextAreaWidget&&) = default;
~RichTextAreaWidget() = default;
void AppendText(const Nz::String& text);
void Clear() override;
void Erase(std::size_t firstGlyph, std::size_t lastGlyph) override;
inline unsigned int GetCharacterSize() const;
inline float GetCharacterSpacingOffset() const;
inline float GetLineSpacingOffset() const;
inline const Nz::Color& GetTextColor() const;
inline Nz::Font* GetTextFont() const;
inline const Nz::Color& GetTextOutlineColor() const;
inline float GetTextOutlineThickness() const;
inline Nz::TextStyleFlags GetTextStyle() const;
inline void SetCharacterSize(unsigned int characterSize);
inline void SetCharacterSpacingOffset(float offset);
inline void SetLineSpacingOffset(float offset);
inline void SetTextColor(const Nz::Color& color);
inline void SetTextFont(Nz::FontRef font);
inline void SetTextOutlineColor(const Nz::Color& color);
inline void SetTextOutlineThickness(float thickness);
inline void SetTextStyle(Nz::TextStyleFlags style);
void Write(const Nz::String& text, std::size_t glyphPosition) override;
RichTextAreaWidget& operator=(const RichTextAreaWidget&) = delete;
RichTextAreaWidget& operator=(RichTextAreaWidget&&) = default;
private:
Nz::AbstractTextDrawer& GetTextDrawer() override;
const Nz::AbstractTextDrawer& GetTextDrawer() const override;
void HandleIndentation(bool add) override;
void HandleSelectionIndentation(bool add) override;
void HandleWordCursorMove(bool left) override;
void UpdateDisplayText() override;
Nz::RichTextDrawer m_drawer;
};
}
#include <NDK/Widgets/RichTextAreaWidget.inl>
#endif // NDK_WIDGETS_TEXTAREAWIDGET_HPP

View File

@@ -0,0 +1,88 @@
// 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
{
inline unsigned int RichTextAreaWidget::GetCharacterSize() const
{
return m_drawer.GetDefaultCharacterSize();
}
inline float RichTextAreaWidget::GetCharacterSpacingOffset() const
{
return m_drawer.GetDefaultCharacterSpacingOffset();
}
inline float RichTextAreaWidget::GetLineSpacingOffset() const
{
return m_drawer.GetDefaultLineSpacingOffset();
}
inline const Nz::Color& RichTextAreaWidget::GetTextColor() const
{
return m_drawer.GetDefaultColor();
}
inline Nz::Font* RichTextAreaWidget::GetTextFont() const
{
return m_drawer.GetDefaultFont();
}
inline const Nz::Color& RichTextAreaWidget::GetTextOutlineColor() const
{
return m_drawer.GetDefaultOutlineColor();
}
inline float RichTextAreaWidget::GetTextOutlineThickness() const
{
return m_drawer.GetDefaultOutlineThickness();
}
inline Nz::TextStyleFlags RichTextAreaWidget::GetTextStyle() const
{
return m_drawer.GetDefaultStyle();
}
inline void RichTextAreaWidget::SetCharacterSize(unsigned int characterSize)
{
m_drawer.SetDefaultCharacterSize(characterSize);
}
inline void RichTextAreaWidget::SetCharacterSpacingOffset(float offset)
{
m_drawer.SetDefaultCharacterSpacingOffset(offset);
}
inline void RichTextAreaWidget::SetLineSpacingOffset(float offset)
{
m_drawer.SetDefaultLineSpacingOffset(offset);
}
inline void RichTextAreaWidget::SetTextColor(const Nz::Color& color)
{
m_drawer.SetDefaultColor(color);
}
inline void RichTextAreaWidget::SetTextFont(Nz::FontRef font)
{
m_drawer.SetDefaultFont(std::move(font));
}
inline void RichTextAreaWidget::SetTextOutlineColor(const Nz::Color& color)
{
m_drawer.SetDefaultOutlineColor(color);
}
inline void RichTextAreaWidget::SetTextOutlineThickness(float thickness)
{
m_drawer.SetDefaultOutlineThickness(thickness);
}
inline void RichTextAreaWidget::SetTextStyle(Nz::TextStyleFlags style)
{
m_drawer.SetDefaultStyle(style);
}
}

View File

@@ -0,0 +1,74 @@
// 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
#pragma once
#ifndef NDK_WIDGETS_SCROLLAREAWIDGET_HPP
#define NDK_WIDGETS_SCROLLAREAWIDGET_HPP
#include <NDK/Prerequisites.hpp>
#include <NDK/BaseWidget.hpp>
#include <Nazara/Graphics/TextSprite.hpp>
namespace Ndk
{
class NDK_API ScrollAreaWidget : public BaseWidget
{
public:
ScrollAreaWidget(BaseWidget* parent, BaseWidget* content);
ScrollAreaWidget(const ScrollAreaWidget&) = delete;
ScrollAreaWidget(ScrollAreaWidget&&) = default;
~ScrollAreaWidget() = default;
void EnableScrollbar(bool enable);
inline float GetScrollHeight() const;
inline float GetScrollRatio() const;
inline bool HasScrollbar() const;
inline bool IsScrollbarEnabled() const;
inline bool IsScrollbarVisible() const;
inline void ScrollToHeight(float height);
void ScrollToRatio(float ratio);
ScrollAreaWidget& operator=(const ScrollAreaWidget&) = delete;
ScrollAreaWidget& operator=(ScrollAreaWidget&&) = default;
private:
enum class ScrollBarStatus
{
Grabbed,
Hovered,
None
};
Nz::Rectf GetScrollbarRect() const;
void Layout() override;
void OnMouseButtonPress(int x, int y, Nz::Mouse::Button button) override;
void OnMouseButtonRelease(int x, int y, Nz::Mouse::Button button) override;
void OnMouseExit() override;
void OnMouseMoved(int x, int y, int deltaX, int deltaY) override;
void OnMouseWheelMoved(int x, int y, float delta) override;
void UpdateScrollbarStatus(ScrollBarStatus status);
BaseWidget* m_content;
EntityHandle m_scrollbarBackgroundEntity;
EntityHandle m_scrollbarEntity;
Nz::SpriteRef m_scrollbarBackgroundSprite;
Nz::SpriteRef m_scrollbarSprite;
Nz::Vector2i m_grabbedDelta;
ScrollBarStatus m_scrollbarStatus;
bool m_isScrollbarEnabled;
bool m_hasScrollbar;
float m_scrollRatio;
};
}
#include <NDK/Widgets/ScrollAreaWidget.inl>
#endif // NDK_WIDGETS_SCROLLAREAWIDGET_HPP

View File

@@ -0,0 +1,39 @@
// 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>
namespace Ndk
{
inline float ScrollAreaWidget::GetScrollHeight() const
{
return m_scrollRatio * m_content->GetHeight();
}
inline float ScrollAreaWidget::GetScrollRatio() const
{
return m_scrollRatio;
}
inline bool ScrollAreaWidget::HasScrollbar() const
{
return m_hasScrollbar;
}
inline bool ScrollAreaWidget::IsScrollbarEnabled() const
{
return m_isScrollbarEnabled;
}
inline bool ScrollAreaWidget::IsScrollbarVisible() const
{
return HasScrollbar() && IsScrollbarEnabled();
}
inline void ScrollAreaWidget::ScrollToHeight(float height)
{
float contentHeight = m_content->GetHeight();
ScrollToRatio(height / contentHeight);
}
}

View File

@@ -7,20 +7,14 @@
#ifndef NDK_WIDGETS_TEXTAREAWIDGET_HPP
#define NDK_WIDGETS_TEXTAREAWIDGET_HPP
#include <Nazara/Graphics/TextSprite.hpp>
#include <Nazara/Utility/SimpleTextDrawer.hpp>
#include <NDK/BaseWidget.hpp>
#include <NDK/Widgets/Enums.hpp>
#include <functional>
#include <vector>
#include <NDK/Widgets/AbstractTextAreaWidget.hpp>
namespace Ndk
{
class NDK_API TextAreaWidget : public BaseWidget
class NDK_API TextAreaWidget : public AbstractTextAreaWidget
{
public:
using CharacterFilter = std::function<bool(char32_t)>;
TextAreaWidget(BaseWidget* parent);
TextAreaWidget(const TextAreaWidget&) = delete;
TextAreaWidget(TextAreaWidget&&) = default;
@@ -28,99 +22,53 @@ namespace Ndk
void AppendText(const Nz::String& text);
inline void Clear();
void Clear() override;
//virtual TextAreaWidget* Clone() const = 0;
using AbstractTextAreaWidget::Erase;
void Erase(std::size_t firstGlyph, std::size_t lastGlyph) override;
inline void EnableMultiline(bool enable = true);
inline void EnableTabWriting(bool enable = true);
inline void Erase(std::size_t glyphPosition);
void Erase(std::size_t firstGlyph, std::size_t lastGlyph);
void EraseSelection();
inline const CharacterFilter& GetCharacterFilter() const;
inline unsigned int GetCharacterSize() const;
inline const Nz::Vector2ui& GetCursorPosition() const;
inline Nz::Vector2ui GetCursorPosition(std::size_t glyphIndex) const;
inline const Nz::String& GetDisplayText() const;
inline EchoMode GetEchoMode() const;
inline std::size_t GetGlyphIndex(const Nz::Vector2ui& cursorPosition);
inline float GetCharacterSpacingOffset() const;
inline float GetLineSpacingOffset() const;
inline const Nz::String& GetText() const;
inline const Nz::Color& GetTextColor() const;
inline Nz::Font* GetTextFont() const;
inline const Nz::Color& GetTextOulineColor() const;
inline float GetTextOulineThickness() const;
inline Nz::TextStyleFlags GetTextStyle() const;
Nz::Vector2ui GetHoveredGlyph(float x, float y) const;
inline bool HasSelection() const;
inline bool IsMultilineEnabled() const;
inline bool IsReadOnly() const;
inline bool IsTabWritingEnabled() const;
inline void MoveCursor(int offset);
inline void MoveCursor(const Nz::Vector2i& offset);
inline void SetCharacterFilter(CharacterFilter filter);
void SetCharacterSize(unsigned int characterSize);
inline void SetCursorPosition(std::size_t glyphIndex);
inline void SetCursorPosition(Nz::Vector2ui cursorPosition);
inline void SetEchoMode(EchoMode echoMode);
inline void SetReadOnly(bool readOnly = true);
inline void SetSelection(Nz::Vector2ui fromPosition, Nz::Vector2ui toPosition);
inline void SetCharacterSize(unsigned int characterSize);
inline void SetCharacterSpacingOffset(float offset);
inline void SetLineSpacingOffset(float offset);
inline void SetText(const Nz::String& text);
inline void SetTextColor(const Nz::Color& text);
inline void SetTextFont(Nz::FontRef font);
inline void SetTextOutlineColor(const Nz::Color& color);
inline void SetTextOutlineThickness(float thickness);
inline void SetTextStyle(Nz::TextStyleFlags style);
inline void Write(const Nz::String& text);
inline void Write(const Nz::String& text, const Nz::Vector2ui& glyphPosition);
void Write(const Nz::String& text, std::size_t glyphPosition);
using AbstractTextAreaWidget::Write;
void Write(const Nz::String& text, std::size_t glyphPosition) override;
TextAreaWidget& operator=(const TextAreaWidget&) = delete;
TextAreaWidget& operator=(TextAreaWidget&&) = default;
NazaraSignal(OnTextAreaCursorMove, const TextAreaWidget* /*textArea*/, std::size_t* /*newCursorPosition*/);
NazaraSignal(OnTextAreaKeyBackspace, const TextAreaWidget* /*textArea*/, bool* /*ignoreDefaultAction*/);
NazaraSignal(OnTextAreaKeyDown, const TextAreaWidget* /*textArea*/, bool* /*ignoreDefaultAction*/);
NazaraSignal(OnTextAreaKeyEnd, const TextAreaWidget* /*textArea*/, bool* /*ignoreDefaultAction*/);
NazaraSignal(OnTextAreaKeyHome, const TextAreaWidget* /*textArea*/, bool* /*ignoreDefaultAction*/);
NazaraSignal(OnTextAreaKeyLeft, const TextAreaWidget* /*textArea*/, bool* /*ignoreDefaultAction*/);
NazaraSignal(OnTextAreaKeyReturn, const TextAreaWidget* /*textArea*/, bool* /*ignoreDefaultAction*/);
NazaraSignal(OnTextAreaKeyRight, const TextAreaWidget* /*textArea*/, bool* /*ignoreDefaultAction*/);
NazaraSignal(OnTextAreaKeyUp, const TextAreaWidget* /*textArea*/, bool* /*ignoreDefaultAction*/);
NazaraSignal(OnTextChanged, const TextAreaWidget* /*textArea*/, const Nz::String& /*text*/);
NazaraSignal(OnTextChanged, const AbstractTextAreaWidget* /*textArea*/, const Nz::String& /*text*/);
private:
void Layout() override;
Nz::AbstractTextDrawer& GetTextDrawer() override;
const Nz::AbstractTextDrawer& GetTextDrawer() const override;
bool IsFocusable() const override;
void OnFocusLost() override;
void OnFocusReceived() override;
bool OnKeyPressed(const Nz::WindowEvent::KeyEvent& key) override;
void OnKeyReleased(const Nz::WindowEvent::KeyEvent& key) override;
void OnMouseButtonPress(int /*x*/, int /*y*/, Nz::Mouse::Button button) override;
void OnMouseButtonRelease(int /*x*/, int /*y*/, Nz::Mouse::Button button) override;
void OnMouseEnter() override;
void OnMouseMoved(int x, int y, int deltaX, int deltaY) override;
void OnTextEntered(char32_t character, bool repeated) override;
void HandleIndentation(bool add) override;
void HandleSelectionIndentation(bool add) override;
void HandleWordCursorMove(bool left) override;
void RefreshCursor();
void UpdateDisplayText();
void UpdateDisplayText() override;
void UpdateMinimumSize();
CharacterFilter m_characterFilter;
EchoMode m_echoMode;
EntityHandle m_cursorEntity;
EntityHandle m_textEntity;
Nz::SimpleTextDrawer m_drawer;
Nz::String m_text;
Nz::TextSpriteRef m_textSprite;
Nz::Vector2ui m_cursorPositionBegin;
Nz::Vector2ui m_cursorPositionEnd;
Nz::Vector2ui m_selectionCursor;
std::vector<Nz::SpriteRef> m_cursorSprites;
bool m_isMouseButtonDown;
bool m_multiLineEnabled;
bool m_readOnly;
bool m_tabEnabled; // writes (Shift+)Tab character if set to true
};
}

View File

@@ -6,90 +6,24 @@
namespace Ndk
{
inline void TextAreaWidget::Clear()
{
m_cursorPositionBegin.MakeZero();
m_cursorPositionEnd.MakeZero();
m_drawer.Clear();
m_text.Clear();
m_textSprite->Update(m_drawer);
RefreshCursor();
OnTextChanged(this, m_text);
}
inline void TextAreaWidget::EnableMultiline(bool enable)
{
m_multiLineEnabled = enable;
}
inline void TextAreaWidget::EnableTabWriting(bool enable)
{
m_tabEnabled = enable;
}
inline void TextAreaWidget::Erase(std::size_t glyphPosition)
{
Erase(glyphPosition, glyphPosition + 1U);
}
inline const TextAreaWidget::CharacterFilter& TextAreaWidget::GetCharacterFilter() const
{
return m_characterFilter;
}
inline unsigned int TextAreaWidget::GetCharacterSize() const
{
return m_drawer.GetCharacterSize();
}
inline const Nz::Vector2ui& TextAreaWidget::GetCursorPosition() const
{
return m_cursorPositionBegin;
}
Nz::Vector2ui TextAreaWidget::GetCursorPosition(std::size_t glyphIndex) const
{
glyphIndex = std::min(glyphIndex, m_drawer.GetGlyphCount());
std::size_t lineCount = m_drawer.GetLineCount();
std::size_t line = 0U;
for (std::size_t i = line + 1; i < lineCount; ++i)
{
if (m_drawer.GetLine(i).glyphIndex > glyphIndex)
break;
line = i;
}
const auto& lineInfo = m_drawer.GetLine(line);
Nz::Vector2ui cursorPos;
cursorPos.y = static_cast<unsigned int>(line);
cursorPos.x = static_cast<unsigned int>(glyphIndex - lineInfo.glyphIndex);
return cursorPos;
}
inline const Nz::String& TextAreaWidget::GetDisplayText() const
{
return m_drawer.GetText();
}
inline std::size_t TextAreaWidget::GetGlyphIndex(const Nz::Vector2ui& cursorPosition)
inline float TextAreaWidget::GetCharacterSpacingOffset() const
{
std::size_t glyphIndex = m_drawer.GetLine(cursorPosition.y).glyphIndex + cursorPosition.x;
if (m_drawer.GetLineCount() > cursorPosition.y + 1)
glyphIndex = std::min(glyphIndex, m_drawer.GetLine(cursorPosition.y + 1).glyphIndex - 1);
else
glyphIndex = std::min(glyphIndex, m_drawer.GetGlyphCount());
return glyphIndex;
return m_drawer.GetCharacterSpacingOffset();
}
inline EchoMode TextAreaWidget::GetEchoMode() const
inline float TextAreaWidget::GetLineSpacingOffset() const
{
return m_echoMode;
return m_drawer.GetLineSpacingOffset();
}
inline const Nz::String& TextAreaWidget::GetText() const
@@ -102,131 +36,47 @@ namespace Ndk
return m_drawer.GetColor();
}
inline bool TextAreaWidget::HasSelection() const
inline Nz::Font* TextAreaWidget::GetTextFont() const
{
return m_cursorPositionBegin != m_cursorPositionEnd;
return m_drawer.GetFont();
}
inline bool TextAreaWidget::IsMultilineEnabled() const
inline const Nz::Color& TextAreaWidget::GetTextOulineColor() const
{
return m_multiLineEnabled;
return m_drawer.GetOutlineColor();
}
inline bool TextAreaWidget::IsTabWritingEnabled() const
inline float TextAreaWidget::GetTextOulineThickness() const
{
return m_tabEnabled;
return m_drawer.GetOutlineThickness();
}
inline bool TextAreaWidget::IsReadOnly() const
inline Nz::TextStyleFlags TextAreaWidget::GetTextStyle() const
{
return m_readOnly;
return m_drawer.GetStyle();
}
inline void TextAreaWidget::MoveCursor(int offset)
inline void TextAreaWidget::SetCharacterSize(unsigned int characterSize)
{
std::size_t cursorGlyph = GetGlyphIndex(m_cursorPositionBegin);
if (offset >= 0)
SetCursorPosition(cursorGlyph + static_cast<std::size_t>(offset));
else
{
std::size_t nOffset = static_cast<std::size_t>(-offset);
if (nOffset >= cursorGlyph)
SetCursorPosition(0);
else
SetCursorPosition(cursorGlyph - nOffset);
}
}
inline void TextAreaWidget::MoveCursor(const Nz::Vector2i& offset)
{
auto ClampOffset = [] (unsigned int cursorPosition, int cursorOffset) -> unsigned int
{
if (cursorOffset >= 0)
return cursorPosition + cursorOffset;
else
{
unsigned int nOffset = static_cast<unsigned int>(-cursorOffset);
if (nOffset >= cursorPosition)
return 0;
else
return cursorPosition - nOffset;
}
};
Nz::Vector2ui cursorPosition = m_cursorPositionBegin;
cursorPosition.x = ClampOffset(static_cast<unsigned int>(cursorPosition.x), offset.x);
cursorPosition.y = ClampOffset(static_cast<unsigned int>(cursorPosition.y), offset.y);
SetCursorPosition(cursorPosition);
}
inline void TextAreaWidget::SetCharacterFilter(CharacterFilter filter)
{
m_characterFilter = std::move(filter);
}
inline void TextAreaWidget::SetCursorPosition(std::size_t glyphIndex)
{
OnTextAreaCursorMove(this, &glyphIndex);
m_cursorPositionBegin = GetCursorPosition(glyphIndex);
m_cursorPositionEnd = m_cursorPositionBegin;
RefreshCursor();
}
inline void TextAreaWidget::SetCursorPosition(Nz::Vector2ui cursorPosition)
{
std::size_t lineCount = m_drawer.GetLineCount();
if (cursorPosition.y >= lineCount)
cursorPosition.y = static_cast<unsigned int>(lineCount - 1);
m_cursorPositionBegin = cursorPosition;
const auto& lineInfo = m_drawer.GetLine(cursorPosition.y);
if (cursorPosition.y + 1 < lineCount)
{
const auto& nextLineInfo = m_drawer.GetLine(cursorPosition.y + 1);
cursorPosition.x = std::min(cursorPosition.x, static_cast<unsigned int>(nextLineInfo.glyphIndex - lineInfo.glyphIndex - 1));
}
m_cursorPositionEnd = m_cursorPositionBegin;
std::size_t glyphIndex = lineInfo.glyphIndex + cursorPosition.x;
OnTextAreaCursorMove(this, &glyphIndex);
RefreshCursor();
}
inline void TextAreaWidget::SetEchoMode(EchoMode echoMode)
{
m_echoMode = echoMode;
m_drawer.SetCharacterSize(characterSize);
UpdateMinimumSize();
UpdateDisplayText();
}
inline void TextAreaWidget::SetReadOnly(bool readOnly)
inline void TextAreaWidget::SetCharacterSpacingOffset(float offset)
{
m_readOnly = readOnly;
m_cursorEntity->Enable(!m_readOnly && HasFocus());
m_drawer.SetCharacterSpacingOffset(offset);
UpdateMinimumSize();
UpdateDisplayText();
}
inline void TextAreaWidget::SetSelection(Nz::Vector2ui fromPosition, Nz::Vector2ui toPosition)
inline void TextAreaWidget::SetLineSpacingOffset(float offset)
{
///TODO: Check if position are valid
m_drawer.SetLineSpacingOffset(offset);
// Ensure begin is before end
if (toPosition.y < fromPosition.y || (toPosition.y == fromPosition.y && toPosition.x < fromPosition.x))
std::swap(fromPosition, toPosition);
if (m_cursorPositionBegin != fromPosition || m_cursorPositionEnd != toPosition)
{
m_cursorPositionBegin = fromPosition;
m_cursorPositionEnd = toPosition;
RefreshCursor();
}
UpdateDisplayText();
}
inline void TextAreaWidget::SetText(const Nz::String& text)
@@ -241,16 +91,34 @@ namespace Ndk
{
m_drawer.SetColor(text);
m_textSprite->Update(m_drawer);
UpdateDisplayText();
}
inline void TextAreaWidget::Write(const Nz::String& text)
inline void TextAreaWidget::SetTextFont(Nz::FontRef font)
{
Write(text, GetGlyphIndex(m_cursorPositionBegin));
m_drawer.SetFont(font);
UpdateDisplayText();
}
inline void TextAreaWidget::Write(const Nz::String& text, const Nz::Vector2ui& glyphPosition)
inline void TextAreaWidget::SetTextOutlineColor(const Nz::Color& color)
{
Write(text, GetGlyphIndex(glyphPosition));
m_drawer.SetOutlineColor(color);
UpdateDisplayText();
}
inline void TextAreaWidget::SetTextOutlineThickness(float thickness)
{
m_drawer.SetOutlineThickness(thickness);
UpdateDisplayText();
}
inline void TextAreaWidget::SetTextStyle(Nz::TextStyleFlags style)
{
m_drawer.SetStyle(style);
UpdateDisplayText();
}
}

View File

@@ -46,6 +46,7 @@ namespace Ndk
void Clear() noexcept;
const EntityHandle& CloneEntity(EntityId id);
const EntityHandle& CloneEntity(const EntityHandle& entity);
inline void DisableProfiler();
inline void EnableProfiler(bool enable = true);
@@ -98,6 +99,12 @@ namespace Ndk
inline void InvalidateSystemOrder();
void ReorderSystems();
struct DoubleBitset
{
Nz::Bitset<Nz::UInt64> front;
Nz::Bitset<Nz::UInt64> back;
};
struct EntityBlock
{
EntityBlock(Entity&& e) :
@@ -119,9 +126,9 @@ namespace Ndk
std::vector<std::unique_ptr<EntityBlock>> m_waitingEntities;
EntityList m_aliveEntities;
ProfilerData m_profilerData;
Nz::Bitset<Nz::UInt64> m_dirtyEntities;
DoubleBitset m_dirtyEntities;
Nz::Bitset<Nz::UInt64> m_freeEntityIds;
Nz::Bitset<Nz::UInt64> m_killedEntities;
DoubleBitset m_killedEntities;
bool m_orderedSystemsUpdated;
bool m_isProfilerEnabled;
};

View File

@@ -308,7 +308,7 @@ namespace Ndk
inline void World::KillEntity(Entity* entity)
{
if (IsEntityValid(entity))
m_killedEntities.UnboundedSet(entity->GetId(), true);
m_killedEntities.front.UnboundedSet(entity->GetId(), true);
}
/*!
@@ -343,7 +343,7 @@ namespace Ndk
*/
inline bool World::IsEntityDying(EntityId id) const
{
return m_killedEntities.UnboundedTest(id);
return m_killedEntities.front.UnboundedTest(id);
}
/*!
@@ -467,13 +467,13 @@ namespace Ndk
inline void World::Invalidate()
{
m_dirtyEntities.Resize(m_entityBlocks.size(), false);
m_dirtyEntities.Set(true); // Activation of all bits
m_dirtyEntities.front.Resize(m_entityBlocks.size(), false);
m_dirtyEntities.front.Set(true); // Activation of all bits
}
inline void World::Invalidate(EntityId id)
{
m_dirtyEntities.UnboundedSet(id, true);
m_dirtyEntities.front.UnboundedSet(id, true);
}
inline void World::InvalidateSystemOrder()

View File

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

View File

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

View File

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

View File

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

View File

@@ -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();
}
}
/*!

View File

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

View File

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

View 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;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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;
}

View File

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

View File

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

View 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()));
}
}

View File

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

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

View 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;
}
}
}

View File

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

View File

@@ -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();
}
/*!