From 3bfa37019e2be364d5eff59ba891135832ca8726 Mon Sep 17 00:00:00 2001 From: NoriteSC <53096989+NoriteSC@users.noreply.github.com> Date: Tue, 12 Sep 2023 10:59:07 +0200 Subject: [PATCH 01/61] DebugDraw extra fixes ray direction was dictating the length of ray now ray is normalized and multiplied by length added default vaule for Color& color to be Color::White [info] c# because Color color = Color.White is not valid think to do on c# side the default value is not genereted [to do] generate extra overload binding in c# --- Source/Engine/Debug/DebugDraw.cpp | 9 +++++++-- Source/Engine/Debug/DebugDraw.h | 13 ++++++++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Debug/DebugDraw.cpp b/Source/Engine/Debug/DebugDraw.cpp index e8489f5b5..ccd247674 100644 --- a/Source/Engine/Debug/DebugDraw.cpp +++ b/Source/Engine/Debug/DebugDraw.cpp @@ -920,9 +920,14 @@ void DebugDraw::DrawActors(Actor** selectedActors, int32 selectedActorsCount, bo } } -void DebugDraw::DrawRay(const Vector3& origin, const Vector3& direction, const Color& color, float duration, bool depthTest) +void DebugDraw::DrawRay(const Vector3& origin, const Vector3& direction, Color& color, float length, float duration, bool depthTest) { - DrawLine(origin, origin + direction, color, duration, depthTest); + DrawLine(origin, origin + (direction.GetNormalized() * length), color, duration, depthTest); +} + +void DebugDraw::DrawRay(const Ray& ray, Color& color, float length, float duration, bool depthTest) +{ + DrawLine(ray.Position, ray.Position + (ray.Direction.GetNormalized() * length), color, duration, depthTest); } void DebugDraw::DrawLine(const Vector3& start, const Vector3& end, const Color& color, float duration, bool depthTest) diff --git a/Source/Engine/Debug/DebugDraw.h b/Source/Engine/Debug/DebugDraw.h index a4f69678a..5a811be7e 100644 --- a/Source/Engine/Debug/DebugDraw.h +++ b/Source/Engine/Debug/DebugDraw.h @@ -75,9 +75,20 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The origin of the line. /// The direction of the line. /// The color. + /// The length of the ray. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawRay(const Vector3& origin, const Vector3& direction, const Color& color, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawRay(const Vector3& origin, const Vector3& direction, Color& color = Color::White, float length = 3.402823466e+38f, float duration = 0.0f, bool depthTest = true); + + /// + /// Draws the line in a direction. + /// + /// The ray. + /// The color. + /// The length of the ray. + /// The duration (in seconds). Use 0 to draw it only once. + /// If set to true depth test will be performed, otherwise depth will be ignored. + API_FUNCTION() static void DrawRay(const Ray& ray, Color& color = Color::White, float length = 3.402823466e+38f, float duration = 0.0f, bool depthTest = true); /// /// Draws the line. From 78ffcc8ae5781d1b6171fbfd2921dc8a844f8a0f Mon Sep 17 00:00:00 2001 From: NoriteSC <53096989+NoriteSC@users.noreply.github.com> Date: Tue, 12 Sep 2023 11:06:46 +0200 Subject: [PATCH 02/61] added other defaults --- Source/Engine/Debug/DebugDraw.h | 104 ++++++++++++++++---------------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/Source/Engine/Debug/DebugDraw.h b/Source/Engine/Debug/DebugDraw.h index 5a811be7e..5bd51a578 100644 --- a/Source/Engine/Debug/DebugDraw.h +++ b/Source/Engine/Debug/DebugDraw.h @@ -98,7 +98,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawLine(const Vector3& start, const Vector3& end, const Color& color, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawLine(const Vector3& start, const Vector3& end, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the lines. Line positions are located one after another (e.g. l0.start, l0.end, l1.start, l1.end,...). @@ -108,7 +108,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawLines(const Span& lines, const Matrix& transform, const Color& color, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawLines(const Span& lines, const Matrix& transform, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the lines. Line positions are located one after another (e.g. l0.start, l0.end, l1.start, l1.end,...). @@ -118,7 +118,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - static void DrawLines(const Array& lines, const Matrix& transform, const Color& color, float duration = 0.0f, bool depthTest = true); + static void DrawLines(const Array& lines, const Matrix& transform, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the lines. Line positions are located one after another (e.g. l0.start, l0.end, l1.start, l1.end,...). @@ -128,7 +128,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawLines(const Span& lines, const Matrix& transform, const Color& color, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawLines(const Span& lines, const Matrix& transform, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the lines. Line positions are located one after another (e.g. l0.start, l0.end, l1.start, l1.end,...). @@ -138,7 +138,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - static void DrawLines(const Array& lines, const Matrix& transform, const Color& color, float duration = 0.0f, bool depthTest = true); + static void DrawLines(const Array& lines, const Matrix& transform, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws a Bezier curve. @@ -150,7 +150,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The line color /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawBezier(const Vector3& p1, const Vector3& p2, const Vector3& p3, const Vector3& p4, const Color& color, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawBezier(const Vector3& p1, const Vector3& p2, const Vector3& p3, const Vector3& p4, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the circle. @@ -161,7 +161,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawCircle(const Vector3& position, const Float3& normal, float radius, const Color& color, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawCircle(const Vector3& position, const Float3& normal, float radius, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe triangle. @@ -172,7 +172,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawWireTriangle(const Vector3& v0, const Vector3& v1, const Vector3& v2, const Color& color, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawWireTriangle(const Vector3& v0, const Vector3& v1, const Vector3& v2, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the triangle. @@ -183,7 +183,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawTriangle(const Vector3& v0, const Vector3& v1, const Vector3& v2, const Color& color, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawTriangle(const Vector3& v0, const Vector3& v1, const Vector3& v2, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the triangles. @@ -192,7 +192,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawTriangles(const Span& vertices, const Color& color, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawTriangles(const Span& vertices, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the triangles. @@ -202,7 +202,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawTriangles(const Span& vertices, const Matrix& transform, const Color& color, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawTriangles(const Span& vertices, const Matrix& transform, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the triangles. @@ -211,7 +211,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - static void DrawTriangles(const Array& vertices, const Color& color, float duration = 0.0f, bool depthTest = true); + static void DrawTriangles(const Array& vertices, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the triangles. @@ -221,7 +221,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - static void DrawTriangles(const Array& vertices, const Matrix& transform, const Color& color, float duration = 0.0f, bool depthTest = true); + static void DrawTriangles(const Array& vertices, const Matrix& transform, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the triangles using the given index buffer. @@ -231,7 +231,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawTriangles(const Span& vertices, const Span& indices, const Color& color, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawTriangles(const Span& vertices, const Span& indices, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the triangles using the given index buffer. @@ -242,7 +242,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawTriangles(const Span& vertices, const Span& indices, const Matrix& transform, const Color& color, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawTriangles(const Span& vertices, const Span& indices, const Matrix& transform, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the triangles using the given index buffer. @@ -252,7 +252,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - static void DrawTriangles(const Array& vertices, const Array& indices, const Color& color, float duration = 0.0f, bool depthTest = true); + static void DrawTriangles(const Array& vertices, const Array& indices, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the triangles using the given index buffer. @@ -263,7 +263,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - static void DrawTriangles(const Array& vertices, const Array& indices, const Matrix& transform, const Color& color, float duration = 0.0f, bool depthTest = true); + static void DrawTriangles(const Array& vertices, const Array& indices, const Matrix& transform, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the triangles. @@ -272,7 +272,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawTriangles(const Span& vertices, const Color& color, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawTriangles(const Span& vertices, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the triangles. @@ -282,7 +282,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawTriangles(const Span& vertices, const Matrix& transform, const Color& color, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawTriangles(const Span& vertices, const Matrix& transform, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the triangles. @@ -291,7 +291,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - static void DrawTriangles(const Array& vertices, const Color& color, float duration = 0.0f, bool depthTest = true); + static void DrawTriangles(const Array& vertices, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the triangles. @@ -301,7 +301,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - static void DrawTriangles(const Array& vertices, const Matrix& transform, const Color& color, float duration = 0.0f, bool depthTest = true); + static void DrawTriangles(const Array& vertices, const Matrix& transform, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the triangles using the given index buffer. @@ -311,7 +311,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawTriangles(const Span& vertices, const Span& indices, const Color& color, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawTriangles(const Span& vertices, const Span& indices, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the triangles using the given index buffer. @@ -322,7 +322,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawTriangles(const Span& vertices, const Span& indices, const Matrix& transform, const Color& color, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawTriangles(const Span& vertices, const Span& indices, const Matrix& transform, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the triangles using the given index buffer. @@ -332,7 +332,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - static void DrawTriangles(const Array& vertices, const Array& indices, const Color& color, float duration = 0.0f, bool depthTest = true); + static void DrawTriangles(const Array& vertices, const Array& indices, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the triangles using the given index buffer. @@ -343,7 +343,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - static void DrawTriangles(const Array& vertices, const Array& indices, const Matrix& transform, const Color& color, float duration = 0.0f, bool depthTest = true); + static void DrawTriangles(const Array& vertices, const Array& indices, const Matrix& transform, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe triangles. @@ -352,7 +352,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawWireTriangles(const Span& vertices, const Color& color, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawWireTriangles(const Span& vertices, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe triangles. @@ -361,7 +361,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - static void DrawWireTriangles(const Array& vertices, const Color& color, float duration = 0.0f, bool depthTest = true); + static void DrawWireTriangles(const Array& vertices, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe triangles using the given index buffer. @@ -371,7 +371,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawWireTriangles(const Span& vertices, const Span& indices, const Color& color, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawWireTriangles(const Span& vertices, const Span& indices, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe triangles using the given index buffer. @@ -381,7 +381,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - static void DrawWireTriangles(const Array& vertices, const Array& indices, const Color& color, float duration = 0.0f, bool depthTest = true); + static void DrawWireTriangles(const Array& vertices, const Array& indices, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe triangles. @@ -390,7 +390,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawWireTriangles(const Span& vertices, const Color& color, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawWireTriangles(const Span& vertices, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe triangles. @@ -399,7 +399,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - static void DrawWireTriangles(const Array& vertices, const Color& color, float duration = 0.0f, bool depthTest = true); + static void DrawWireTriangles(const Array& vertices, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe triangles using the given index buffer. @@ -409,7 +409,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawWireTriangles(const Span& vertices, const Span& indices, const Color& color, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawWireTriangles(const Span& vertices, const Span& indices, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe triangles using the given index buffer. @@ -419,7 +419,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - static void DrawWireTriangles(const Array& vertices, const Array& indices, const Color& color, float duration = 0.0f, bool depthTest = true); + static void DrawWireTriangles(const Array& vertices, const Array& indices, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe box. @@ -428,7 +428,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawWireBox(const BoundingBox& box, const Color& color, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawWireBox(const BoundingBox& box, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe frustum. @@ -437,7 +437,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawWireFrustum(const BoundingFrustum& frustum, const Color& color, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawWireFrustum(const BoundingFrustum& frustum, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe box. @@ -446,7 +446,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawWireBox(const OrientedBoundingBox& box, const Color& color, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawWireBox(const OrientedBoundingBox& box, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe sphere. @@ -455,7 +455,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawWireSphere(const BoundingSphere& sphere, const Color& color, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawWireSphere(const BoundingSphere& sphere, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the sphere. @@ -464,7 +464,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawSphere(const BoundingSphere& sphere, const Color& color, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawSphere(const BoundingSphere& sphere, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the tube. @@ -476,7 +476,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawTube(const Vector3& position, const Quaternion& orientation, float radius, float length, const Color& color, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawTube(const Vector3& position, const Quaternion& orientation, float radius, float length, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe tube. @@ -488,7 +488,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawWireTube(const Vector3& position, const Quaternion& orientation, float radius, float length, const Color& color, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawWireTube(const Vector3& position, const Quaternion& orientation, float radius, float length, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the cylinder. @@ -500,7 +500,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawCylinder(const Vector3& position, const Quaternion& orientation, float radius, float height, const Color& color, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawCylinder(const Vector3& position, const Quaternion& orientation, float radius, float height, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe cylinder. @@ -512,7 +512,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawWireCylinder(const Vector3& position, const Quaternion& orientation, float radius, float height, const Color& color, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawWireCylinder(const Vector3& position, const Quaternion& orientation, float radius, float height, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the cone. @@ -525,7 +525,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawCone(const Vector3& position, const Quaternion& orientation, float radius, float angleXY, float angleXZ, const Color& color, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawCone(const Vector3& position, const Quaternion& orientation, float radius, float angleXY, float angleXZ, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe cone. @@ -538,7 +538,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawWireCone(const Vector3& position, const Quaternion& orientation, float radius, float angleXY, float angleXZ, const Color& color, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawWireCone(const Vector3& position, const Quaternion& orientation, float radius, float angleXY, float angleXZ, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the arc. @@ -550,7 +550,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawArc(const Vector3& position, const Quaternion& orientation, float radius, float angle, const Color& color, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawArc(const Vector3& position, const Quaternion& orientation, float radius, float angle, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe arc. @@ -562,7 +562,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawWireArc(const Vector3& position, const Quaternion& orientation, float radius, float angle, const Color& color, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawWireArc(const Vector3& position, const Quaternion& orientation, float radius, float angle, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe arrow. @@ -573,7 +573,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawWireArrow(const Vector3& position, const Quaternion& orientation, float scale, const Color& color, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawWireArrow(const Vector3& position, const Quaternion& orientation, float scale, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the box. @@ -582,7 +582,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawBox(const BoundingBox& box, const Color& color, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawBox(const BoundingBox& box, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the box. @@ -591,7 +591,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawBox(const OrientedBoundingBox& box, const Color& color, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawBox(const OrientedBoundingBox& box, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the text on a screen (2D). @@ -601,7 +601,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The font size. /// The duration (in seconds). Use 0 to draw it only once. - API_FUNCTION() static void DrawText(const StringView& text, const Float2& position, const Color& color, int32 size = 20, float duration = 0.0f); + API_FUNCTION() static void DrawText(const StringView& text, const Float2& position, Color& color = Color::White, int32 size = 20, float duration = 0.0f); /// /// Draws the text (3D) that automatically faces the camera. @@ -612,7 +612,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The font size. /// The duration (in seconds). Use 0 to draw it only once. /// The text scale. - API_FUNCTION() static void DrawText(const StringView& text, const Vector3& position, const Color& color, int32 size = 32, float duration = 0.0f, float scale = 1.0f); + API_FUNCTION() static void DrawText(const StringView& text, const Vector3& position, Color& color = Color::White, int32 size = 32, float duration = 0.0f, float scale = 1.0f); /// /// Draws the text (3D). @@ -622,7 +622,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The font size. /// The duration (in seconds). Use 0 to draw it only once. - API_FUNCTION() static void DrawText(const StringView& text, const Transform& transform, const Color& color, int32 size = 32, float duration = 0.0f); + API_FUNCTION() static void DrawText(const StringView& text, const Transform& transform, Color& color = Color::White, int32 size = 32, float duration = 0.0f); }; #define DEBUG_DRAW_RAY(origin, direction, color, duration, depthTest) DebugDraw::DrawRay(origin, direction, color, duration, depthTest) From f11ea025506967f0dacd05e9ded8daac16a39ba6 Mon Sep 17 00:00:00 2001 From: NoriteSC <53096989+NoriteSC@users.noreply.github.com> Date: Tue, 12 Sep 2023 11:08:47 +0200 Subject: [PATCH 03/61] forgot about const :sweat_smile: --- Source/Engine/Debug/DebugDraw.h | 108 ++++++++++++++++---------------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/Source/Engine/Debug/DebugDraw.h b/Source/Engine/Debug/DebugDraw.h index 5bd51a578..b5af321c7 100644 --- a/Source/Engine/Debug/DebugDraw.h +++ b/Source/Engine/Debug/DebugDraw.h @@ -78,7 +78,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The length of the ray. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawRay(const Vector3& origin, const Vector3& direction, Color& color = Color::White, float length = 3.402823466e+38f, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawRay(const Vector3& origin, const Vector3& direction, const Color& color = Color::White, float length = 3.402823466e+38f, float duration = 0.0f, bool depthTest = true); /// /// Draws the line in a direction. @@ -88,7 +88,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The length of the ray. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawRay(const Ray& ray, Color& color = Color::White, float length = 3.402823466e+38f, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawRay(const Ray& ray,const Color& color = Color::White, float length = 3.402823466e+38f, float duration = 0.0f, bool depthTest = true); /// /// Draws the line. @@ -98,7 +98,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawLine(const Vector3& start, const Vector3& end, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawLine(const Vector3& start, const Vector3& end, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the lines. Line positions are located one after another (e.g. l0.start, l0.end, l1.start, l1.end,...). @@ -108,7 +108,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawLines(const Span& lines, const Matrix& transform, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawLines(const Span& lines, const Matrix& transform, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the lines. Line positions are located one after another (e.g. l0.start, l0.end, l1.start, l1.end,...). @@ -118,7 +118,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - static void DrawLines(const Array& lines, const Matrix& transform, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + static void DrawLines(const Array& lines, const Matrix& transform, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the lines. Line positions are located one after another (e.g. l0.start, l0.end, l1.start, l1.end,...). @@ -128,7 +128,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawLines(const Span& lines, const Matrix& transform, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawLines(const Span& lines, const Matrix& transform, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the lines. Line positions are located one after another (e.g. l0.start, l0.end, l1.start, l1.end,...). @@ -138,7 +138,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - static void DrawLines(const Array& lines, const Matrix& transform, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + static void DrawLines(const Array& lines, const Matrix& transform, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws a Bezier curve. @@ -150,7 +150,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The line color /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawBezier(const Vector3& p1, const Vector3& p2, const Vector3& p3, const Vector3& p4, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawBezier(const Vector3& p1, const Vector3& p2, const Vector3& p3, const Vector3& p4, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the circle. @@ -161,7 +161,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawCircle(const Vector3& position, const Float3& normal, float radius, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawCircle(const Vector3& position, const Float3& normal, float radius, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe triangle. @@ -172,7 +172,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawWireTriangle(const Vector3& v0, const Vector3& v1, const Vector3& v2, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawWireTriangle(const Vector3& v0, const Vector3& v1, const Vector3& v2, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the triangle. @@ -183,7 +183,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawTriangle(const Vector3& v0, const Vector3& v1, const Vector3& v2, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawTriangle(const Vector3& v0, const Vector3& v1, const Vector3& v2, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the triangles. @@ -192,7 +192,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawTriangles(const Span& vertices, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawTriangles(const Span& vertices, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the triangles. @@ -202,7 +202,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawTriangles(const Span& vertices, const Matrix& transform, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawTriangles(const Span& vertices, const Matrix& transform, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the triangles. @@ -211,7 +211,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - static void DrawTriangles(const Array& vertices, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + static void DrawTriangles(const Array& vertices, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the triangles. @@ -221,7 +221,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - static void DrawTriangles(const Array& vertices, const Matrix& transform, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + static void DrawTriangles(const Array& vertices, const Matrix& transform, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the triangles using the given index buffer. @@ -231,7 +231,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawTriangles(const Span& vertices, const Span& indices, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawTriangles(const Span& vertices, const Span& indices, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the triangles using the given index buffer. @@ -242,7 +242,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawTriangles(const Span& vertices, const Span& indices, const Matrix& transform, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawTriangles(const Span& vertices, const Span& indices, const Matrix& transform, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the triangles using the given index buffer. @@ -252,7 +252,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - static void DrawTriangles(const Array& vertices, const Array& indices, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + static void DrawTriangles(const Array& vertices, const Array& indices, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the triangles using the given index buffer. @@ -263,7 +263,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - static void DrawTriangles(const Array& vertices, const Array& indices, const Matrix& transform, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + static void DrawTriangles(const Array& vertices, const Array& indices, const Matrix& transform, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the triangles. @@ -272,7 +272,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawTriangles(const Span& vertices, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawTriangles(const Span& vertices, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the triangles. @@ -282,7 +282,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawTriangles(const Span& vertices, const Matrix& transform, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawTriangles(const Span& vertices, const Matrix& transform, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the triangles. @@ -291,7 +291,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - static void DrawTriangles(const Array& vertices, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + static void DrawTriangles(const Array& vertices, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the triangles. @@ -301,7 +301,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - static void DrawTriangles(const Array& vertices, const Matrix& transform, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + static void DrawTriangles(const Array& vertices, const Matrix& transform, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the triangles using the given index buffer. @@ -311,7 +311,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawTriangles(const Span& vertices, const Span& indices, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawTriangles(const Span& vertices, const Span& indices, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the triangles using the given index buffer. @@ -322,7 +322,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawTriangles(const Span& vertices, const Span& indices, const Matrix& transform, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawTriangles(const Span& vertices, const Span& indices, const Matrix& transform, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the triangles using the given index buffer. @@ -332,7 +332,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - static void DrawTriangles(const Array& vertices, const Array& indices, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + static void DrawTriangles(const Array& vertices, const Array& indices, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the triangles using the given index buffer. @@ -343,7 +343,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - static void DrawTriangles(const Array& vertices, const Array& indices, const Matrix& transform, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + static void DrawTriangles(const Array& vertices, const Array& indices, const Matrix& transform, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe triangles. @@ -352,7 +352,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawWireTriangles(const Span& vertices, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawWireTriangles(const Span& vertices, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe triangles. @@ -361,7 +361,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - static void DrawWireTriangles(const Array& vertices, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + static void DrawWireTriangles(const Array& vertices, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe triangles using the given index buffer. @@ -371,7 +371,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawWireTriangles(const Span& vertices, const Span& indices, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawWireTriangles(const Span& vertices, const Span& indices, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe triangles using the given index buffer. @@ -381,7 +381,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - static void DrawWireTriangles(const Array& vertices, const Array& indices, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + static void DrawWireTriangles(const Array& vertices, const Array& indices, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe triangles. @@ -390,7 +390,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawWireTriangles(const Span& vertices, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawWireTriangles(const Span& vertices, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe triangles. @@ -399,7 +399,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - static void DrawWireTriangles(const Array& vertices, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + static void DrawWireTriangles(const Array& vertices, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe triangles using the given index buffer. @@ -409,7 +409,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawWireTriangles(const Span& vertices, const Span& indices, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawWireTriangles(const Span& vertices, const Span& indices, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe triangles using the given index buffer. @@ -419,7 +419,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - static void DrawWireTriangles(const Array& vertices, const Array& indices, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + static void DrawWireTriangles(const Array& vertices, const Array& indices, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe box. @@ -428,7 +428,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawWireBox(const BoundingBox& box, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawWireBox(const BoundingBox& box, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe frustum. @@ -437,7 +437,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawWireFrustum(const BoundingFrustum& frustum, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawWireFrustum(const BoundingFrustum& frustum, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe box. @@ -446,7 +446,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawWireBox(const OrientedBoundingBox& box, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawWireBox(const OrientedBoundingBox& box, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe sphere. @@ -455,7 +455,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawWireSphere(const BoundingSphere& sphere, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawWireSphere(const BoundingSphere& sphere, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the sphere. @@ -464,7 +464,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawSphere(const BoundingSphere& sphere, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawSphere(const BoundingSphere& sphere, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the tube. @@ -476,7 +476,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawTube(const Vector3& position, const Quaternion& orientation, float radius, float length, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawTube(const Vector3& position, const Quaternion& orientation, float radius, float length, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe tube. @@ -488,7 +488,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawWireTube(const Vector3& position, const Quaternion& orientation, float radius, float length, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawWireTube(const Vector3& position, const Quaternion& orientation, float radius, float length, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the cylinder. @@ -500,7 +500,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawCylinder(const Vector3& position, const Quaternion& orientation, float radius, float height, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawCylinder(const Vector3& position, const Quaternion& orientation, float radius, float height, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe cylinder. @@ -512,7 +512,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawWireCylinder(const Vector3& position, const Quaternion& orientation, float radius, float height, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawWireCylinder(const Vector3& position, const Quaternion& orientation, float radius, float height, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the cone. @@ -525,7 +525,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawCone(const Vector3& position, const Quaternion& orientation, float radius, float angleXY, float angleXZ, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawCone(const Vector3& position, const Quaternion& orientation, float radius, float angleXY, float angleXZ, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe cone. @@ -538,7 +538,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawWireCone(const Vector3& position, const Quaternion& orientation, float radius, float angleXY, float angleXZ, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawWireCone(const Vector3& position, const Quaternion& orientation, float radius, float angleXY, float angleXZ, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the arc. @@ -550,7 +550,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawArc(const Vector3& position, const Quaternion& orientation, float radius, float angle, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawArc(const Vector3& position, const Quaternion& orientation, float radius, float angle, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe arc. @@ -562,7 +562,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawWireArc(const Vector3& position, const Quaternion& orientation, float radius, float angle, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawWireArc(const Vector3& position, const Quaternion& orientation, float radius, float angle, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe arrow. @@ -573,7 +573,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawWireArrow(const Vector3& position, const Quaternion& orientation, float scale, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawWireArrow(const Vector3& position, const Quaternion& orientation, float scale, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the box. @@ -582,7 +582,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawBox(const BoundingBox& box, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawBox(const BoundingBox& box, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the box. @@ -591,7 +591,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawBox(const OrientedBoundingBox& box, Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawBox(const OrientedBoundingBox& box, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the text on a screen (2D). @@ -601,7 +601,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The font size. /// The duration (in seconds). Use 0 to draw it only once. - API_FUNCTION() static void DrawText(const StringView& text, const Float2& position, Color& color = Color::White, int32 size = 20, float duration = 0.0f); + API_FUNCTION() static void DrawText(const StringView& text, const Float2& position, const Color& color = Color::White, int32 size = 20, float duration = 0.0f); /// /// Draws the text (3D) that automatically faces the camera. @@ -612,7 +612,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The font size. /// The duration (in seconds). Use 0 to draw it only once. /// The text scale. - API_FUNCTION() static void DrawText(const StringView& text, const Vector3& position, Color& color = Color::White, int32 size = 32, float duration = 0.0f, float scale = 1.0f); + API_FUNCTION() static void DrawText(const StringView& text, const Vector3& position, const Color& color = Color::White, int32 size = 32, float duration = 0.0f, float scale = 1.0f); /// /// Draws the text (3D). @@ -622,7 +622,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The color. /// The font size. /// The duration (in seconds). Use 0 to draw it only once. - API_FUNCTION() static void DrawText(const StringView& text, const Transform& transform, Color& color = Color::White, int32 size = 32, float duration = 0.0f); + API_FUNCTION() static void DrawText(const StringView& text, const Transform& transform, const Color& color = Color::White, int32 size = 32, float duration = 0.0f); }; #define DEBUG_DRAW_RAY(origin, direction, color, duration, depthTest) DebugDraw::DrawRay(origin, direction, color, duration, depthTest) From d107947ba9c40a851756db8ac6ca1085aea0d171 Mon Sep 17 00:00:00 2001 From: NoriteSC <53096989+NoriteSC@users.noreply.github.com> Date: Tue, 12 Sep 2023 11:19:19 +0200 Subject: [PATCH 04/61] Update DebugDraw.cpp --- Source/Engine/Debug/DebugDraw.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Debug/DebugDraw.cpp b/Source/Engine/Debug/DebugDraw.cpp index ccd247674..2afc9d047 100644 --- a/Source/Engine/Debug/DebugDraw.cpp +++ b/Source/Engine/Debug/DebugDraw.cpp @@ -920,12 +920,12 @@ void DebugDraw::DrawActors(Actor** selectedActors, int32 selectedActorsCount, bo } } -void DebugDraw::DrawRay(const Vector3& origin, const Vector3& direction, Color& color, float length, float duration, bool depthTest) +void DebugDraw::DrawRay(const Vector3& origin, const Vector3& direction,const Color& color, float length, float duration, bool depthTest) { DrawLine(origin, origin + (direction.GetNormalized() * length), color, duration, depthTest); } -void DebugDraw::DrawRay(const Ray& ray, Color& color, float length, float duration, bool depthTest) +void DebugDraw::DrawRay(const Ray& ray,const Color& color, float length, float duration, bool depthTest) { DrawLine(ray.Position, ray.Position + (ray.Direction.GetNormalized() * length), color, duration, depthTest); } From f71f214f36c0960a820c2be35d0e547ed9a52d54 Mon Sep 17 00:00:00 2001 From: NoriteSC <53096989+NoriteSC@users.noreply.github.com> Date: Wed, 13 Sep 2023 02:53:20 +0200 Subject: [PATCH 05/61] bug fix macro added extra macros --- Source/Engine/Debug/DebugDraw.cpp | 16 ++++ Source/Engine/Debug/DebugDraw.cs | 36 +++++++- Source/Engine/Debug/DebugDraw.h | 135 ++++++++++++++++++------------ 3 files changed, 132 insertions(+), 55 deletions(-) diff --git a/Source/Engine/Debug/DebugDraw.cpp b/Source/Engine/Debug/DebugDraw.cpp index 2afc9d047..733c4a643 100644 --- a/Source/Engine/Debug/DebugDraw.cpp +++ b/Source/Engine/Debug/DebugDraw.cpp @@ -920,6 +920,22 @@ void DebugDraw::DrawActors(Actor** selectedActors, int32 selectedActorsCount, bo } } +void DebugDraw::DrawAxisFromDirection(const Vector3& origin, const Vector3& direction, const Color& color , float Size, float duration, bool depthTest) +{ + auto rot = Quaternion::FromDirection(direction.GetNormalized()); + Vector3 Up = (rot * Vector3::Up ); + Vector3 Forward = (rot * Vector3::Forward); + Vector3 Right = (rot * Vector3::Right ); + + DrawLine(origin, origin + (Up * (Size * 0.5f)) + Up , Color::Green ,duration, depthTest); + DrawLine(origin, origin + (Forward * (Size * 0.5f)) + Forward, Color::Blue ,duration, depthTest); + DrawLine(origin, origin + (Right * (Size * 0.5f)) + Right , Color::Red ,duration, depthTest); +} + +void DebugDraw::DrawDirection(const Vector3& origin, const Vector3& direction, const Color& color, float duration, bool depthTest) +{ + DrawLine(origin, origin + direction, color, duration, depthTest); +} void DebugDraw::DrawRay(const Vector3& origin, const Vector3& direction,const Color& color, float length, float duration, bool depthTest) { DrawLine(origin, origin + (direction.GetNormalized() * length), color, duration, depthTest); diff --git a/Source/Engine/Debug/DebugDraw.cs b/Source/Engine/Debug/DebugDraw.cs index bc000e03d..f6a8a19f4 100644 --- a/Source/Engine/Debug/DebugDraw.cs +++ b/Source/Engine/Debug/DebugDraw.cs @@ -31,6 +31,17 @@ namespace FlaxEngine { } + /// + /// Draws the lines axis from direction. + /// + /// The origin of the line. + /// The direction of the line. + /// The color. + /// The size of the axis. + /// The duration (in seconds). Use 0 to draw it only once. + /// If set to true depth test will be performed, otherwise depth will be ignored. + static void DrawAxisFromDirection(Vector3 origin, Vector3 direction, Color color ,float Size = 100.0f, float duration = 0.0f, bool depthTest = true); + /// /// Draws the line in a direction. /// @@ -39,9 +50,28 @@ namespace FlaxEngine /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - public static void DrawRay(Vector3 origin, Vector3 direction, Color color, float duration = 0.0f, bool depthTest = true) - { - } + static void DrawDirection(Vector3 origin, Vector3 direction, Color color, float duration = 0.0f, bool depthTest = true); + + /// + /// Draws the line in a direction. + /// + /// The origin of the line. + /// The direction of the line. + /// The color. + /// The length of the ray. + /// The duration (in seconds). Use 0 to draw it only once. + /// If set to true depth test will be performed, otherwise depth will be ignored. + static void DrawRay(Vector3 origin, Vector3 direction, Color color, float length = 3.402823466e+38f, float duration = 0.0f, bool depthTest = true); + + /// + /// Draws the line in a direction. + /// + /// The ray. + /// The color. + /// The length of the ray. + /// The duration (in seconds). Use 0 to draw it only once. + /// If set to true depth test will be performed, otherwise depth will be ignored. + static void DrawRay(Ray ray,Color color, float length = 3.402823466e+38f, float duration = 0.0f, bool depthTest = true); /// /// Draws the line. diff --git a/Source/Engine/Debug/DebugDraw.h b/Source/Engine/Debug/DebugDraw.h index b5af321c7..5df31d831 100644 --- a/Source/Engine/Debug/DebugDraw.h +++ b/Source/Engine/Debug/DebugDraw.h @@ -69,6 +69,27 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// True if draw all debug shapes from scenes too or false if draw just from specified actor list. API_FUNCTION() static void DrawActors(Actor** selectedActors, int32 selectedActorsCount, bool drawScenes); + /// + /// Draws the lines axis from direction. + /// + /// The origin of the line. + /// The direction of the line. + /// The color. + /// The size of the axis. + /// The duration (in seconds). Use 0 to draw it only once. + /// If set to true depth test will be performed, otherwise depth will be ignored. + API_FUNCTION() static void DrawAxisFromDirection(const Vector3& origin, const Vector3& direction, const Color& color = Color::White ,float Size = 100.0f, float duration = 0.0f, bool depthTest = true); + + /// + /// Draws the line in a direction. + /// + /// The origin of the line. + /// The direction of the line. + /// The color. + /// The duration (in seconds). Use 0 to draw it only once. + /// If set to true depth test will be performed, otherwise depth will be ignored. + API_FUNCTION() static void DrawDirection(const Vector3& origin, const Vector3& direction, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + /// /// Draws the line in a direction. /// @@ -625,61 +646,71 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw API_FUNCTION() static void DrawText(const StringView& text, const Transform& transform, const Color& color = Color::White, int32 size = 32, float duration = 0.0f); }; -#define DEBUG_DRAW_RAY(origin, direction, color, duration, depthTest) DebugDraw::DrawRay(origin, direction, color, duration, depthTest) -#define DEBUG_DRAW_LINE(start, end, color, duration, depthTest) DebugDraw::DrawLine(start, end, color, duration, depthTest) -#define DEBUG_DRAW_LINES(lines, transform, color, duration, depthTest) DebugDraw::DrawLines(lines, transform, color, duration, depthTest) -#define DEBUG_DRAW_BEZIER(p1, p2, p3, p4, color, duration, depthTest) DebugDraw::DrawBezier(p1, p2, p3, p4, color, duration, depthTest) -#define DEBUG_DRAW_CIRCLE(position, normal, radius, color, duration, depthTest) DebugDraw::DrawCircle(position, normal, radius, color, duration, depthTest) -#define DEBUG_DRAW_TRIANGLE(v0, v1, v2, color, duration, depthTest) DebugDraw::DrawTriangle(v0, v1, v2, color, duration, depthTest) -#define DEBUG_DRAW_TRIANGLES(vertices, color, duration, depthTest) DebugDraw::DrawTriangles(vertices, color, duration, depthTest) -#define DEBUG_DRAW_TRIANGLES_EX(vertices, indices, color, duration, depthTest) DebugDraw::DrawTriangles(vertices, indices, color, duration, depthTest) -#define DEBUG_DRAW_TRIANGLES_EX2(vertices, indices, transform, color, duration, depthTest) DebugDraw::DrawTriangles(vertices, indices, transform, color, duration, depthTest) -#define DEBUG_DRAW_SPHERE(sphere, color, duration, depthTest) DebugDraw::DrawSphere(sphere, color, duration, depthTest) -#define DEBUG_DRAW_TUBE(position, orientation, radius, length, color, duration, depthTest) DebugDraw::DrawTube(position, orientation, radius, length, color, duration, depthTest) -#define DEBUG_DRAW_BOX(box, color, duration, depthTest) DebugDraw::DrawBox(box, color, duration, depthTest) -#define DEBUG_DRAW_CYLINDER(position, orientation, radius, height, color, duration, depthTest) DebugDraw::DrawCylinder(position, orientation, radius, height, color, duration, depthTest) -#define DEBUG_DRAW_CONE(position, orientation, radius, angleXY, angleXZ, color, duration, depthTest) DebugDraw::DrawCone(position, orientation, radius, angleXY, angleXZ, color, duration, depthTest) -#define DEBUG_DRAW_ARC(position, orientation, radius, angle, color, duration, depthTest) DebugDraw::DrawArc(position, orientation, radius, angle, color, duration, depthTest) -#define DEBUG_DRAW_WIRE_TRIANGLE(v0, v1, v2, color, duration, depthTest) DebugDraw::DrawWireTriangle(v0, v1, v2, color, duration, depthTest) -#define DEBUG_DRAW_WIRE_TRIANGLES(vertices, color, duration, depthTest) DebugDraw::DrawWireTriangles(vertices, color, duration, depthTest) -#define DEBUG_DRAW_WIRE_TRIANGLES_EX(vertices, indices, color, duration, depthTest) DebugDraw::DrawWireTriangles(vertices, indices, color, duration, depthTest) -#define DEBUG_DRAW_WIRE_BOX(box, color, duration, depthTest) DebugDraw::DrawWireBox(box, color, duration, depthTest) -#define DEBUG_DRAW_WIRE_FRUSTUM(frustum, color, duration, depthTest) DebugDraw::DrawWireFrustum(frustum, color, duration, depthTest) -#define DEBUG_DRAW_WIRE_SPHERE(sphere, color, duration, depthTest) DebugDraw::DrawWireSphere(sphere, color, duration, depthTest) -#define DEBUG_DRAW_WIRE_TUBE(position, orientation, radius, length, color, duration, depthTest) DebugDraw::DrawWireTube(position, orientation, radius, length, color, duration, depthTest) -#define DEBUG_DRAW_WIRE_CYLINDER(position, orientation, radius, height, color, duration, depthTest) DebugDraw::DrawWireCylinder(position, orientation, radius, height, color, duration, depthTest) -#define DEBUG_DRAW_WIRE_CONE(position, orientation, radius, angleXY, angleXZ, color, duration, depthTest) DebugDraw::DrawWireCone(position, orientation, radius, angleXY, angleXZ, color, duration, depthTest) -#define DEBUG_DRAW_WIRE_ARC(position, orientation, radius, angle, color, duration, depthTest) DebugDraw::DrawWireArc(position, orientation, radius, angle, color, duration, depthTest) -#define DEBUG_DRAW_WIRE_ARROW(position, orientation, scale, color, duration, depthTest) DebugDraw::DrawWireArrow(position, orientation, scale, color, duration, depthTest) -#define DEBUG_DRAW_TEXT(text, position, color, size, duration) DebugDraw::DrawText(text, position, color, size, duration) + +#define DEBUG_DRAW_AXIS_FROM_DIRECTION(origin,direction,color,Size,duration,depthTest) DebugDraw::DrawAxisFromDirection(origin,direction,color,Size,duration,depthTest); +#define DEBUG_DRAW_DIRECTION (origin,direction,color,duration,depthTest) DebugDraw::DrawDirection(origin,direction,color,duration,depthTest); +#define DEBUG_DRAW_RAY (origin,direction,color,length,duration,depthTest) DebugDraw::DrawRay(origin,direction,color,length,duration,depthTest); +#define DEBUG_DRAW_RAY (ray,color,length,duration,depthTest) DebugDraw::DrawRay(ray,color,length,duration,depthTest); + +#define DEBUG_DRAW_LINE(start, end, color, duration, depthTest) DebugDraw::DrawLine(start, end, color, duration, depthTest) +#define DEBUG_DRAW_LINES(lines, transform, color, duration, depthTest) DebugDraw::DrawLines(lines, transform, color, duration, depthTest) +#define DEBUG_DRAW_BEZIER(p1, p2, p3, p4, color, duration, depthTest) DebugDraw::DrawBezier(p1, p2, p3, p4, color, duration, depthTest) +#define DEBUG_DRAW_CIRCLE(position, normal, radius, color, duration, depthTest) DebugDraw::DrawCircle(position, normal, radius, color, duration, depthTest) +#define DEBUG_DRAW_TRIANGLE(v0, v1, v2, color, duration, depthTest) DebugDraw::DrawTriangle(v0, v1, v2, color, duration, depthTest) +#define DEBUG_DRAW_TRIANGLES(vertices, color, duration, depthTest) DebugDraw::DrawTriangles(vertices, color, duration, depthTest) +#define DEBUG_DRAW_TRIANGLES_EX(vertices, indices, color, duration, depthTest) DebugDraw::DrawTriangles(vertices, indices, color, duration, depthTest) +#define DEBUG_DRAW_TRIANGLES_EX2(vertices, indices, transform, color, duration, depthTest) DebugDraw::DrawTriangles(vertices, indices, transform, color, duration, depthTest) +#define DEBUG_DRAW_SPHERE(sphere, color, duration, depthTest) DebugDraw::DrawSphere(sphere, color, duration, depthTest) +#define DEBUG_DRAW_TUBE(position, orientation, radius, length, color, duration, depthTest) DebugDraw::DrawTube(position, orientation, radius, length, color, duration, depthTest) +#define DEBUG_DRAW_BOX(box, color, duration, depthTest) DebugDraw::DrawBox(box, color, duration, depthTest) +#define DEBUG_DRAW_CYLINDER(position, orientation, radius, height, color, duration, depthTest) DebugDraw::DrawCylinder(position, orientation, radius, height, color, duration, depthTest) +#define DEBUG_DRAW_CONE(position, orientation, radius, angleXY, angleXZ, color, duration, depthTest) DebugDraw::DrawCone(position, orientation, radius, angleXY, angleXZ, color, duration, depthTest) +#define DEBUG_DRAW_ARC(position, orientation, radius, angle, color, duration, depthTest) DebugDraw::DrawArc(position, orientation, radius, angle, color, duration, depthTest) +#define DEBUG_DRAW_WIRE_TRIANGLE(v0, v1, v2, color, duration, depthTest) DebugDraw::DrawWireTriangle(v0, v1, v2, color, duration, depthTest) +#define DEBUG_DRAW_WIRE_TRIANGLES(vertices, color, duration, depthTest) DebugDraw::DrawWireTriangles(vertices, color, duration, depthTest) +#define DEBUG_DRAW_WIRE_TRIANGLES_EX(vertices, indices, color, duration, depthTest) DebugDraw::DrawWireTriangles(vertices, indices, color, duration, depthTest) +#define DEBUG_DRAW_WIRE_BOX(box, color, duration, depthTest) DebugDraw::DrawWireBox(box, color, duration, depthTest) +#define DEBUG_DRAW_WIRE_FRUSTUM(frustum, color, duration, depthTest) DebugDraw::DrawWireFrustum(frustum, color, duration, depthTest) +#define DEBUG_DRAW_WIRE_SPHERE(sphere, color, duration, depthTest) DebugDraw::DrawWireSphere(sphere, color, duration, depthTest) +#define DEBUG_DRAW_WIRE_TUBE(position, orientation, radius, length, color, duration, depthTest) DebugDraw::DrawWireTube(position, orientation, radius, length, color, duration, depthTest) +#define DEBUG_DRAW_WIRE_CYLINDER(position, orientation, radius, height, color, duration, depthTest) DebugDraw::DrawWireCylinder(position, orientation, radius, height, color, duration, depthTest) +#define DEBUG_DRAW_WIRE_CONE(position, orientation, radius, angleXY, angleXZ, color, duration, depthTest) DebugDraw::DrawWireCone(position, orientation, radius, angleXY, angleXZ, color, duration, depthTest) +#define DEBUG_DRAW_WIRE_ARC(position, orientation, radius, angle, color, duration, depthTest) DebugDraw::DrawWireArc(position, orientation, radius, angle, color, duration, depthTest) +#define DEBUG_DRAW_WIRE_ARROW(position, orientation, scale, color, duration, depthTest) DebugDraw::DrawWireArrow(position, orientation, scale, color, duration, depthTest) +#define DEBUG_DRAW_TEXT(text, position, color, size, duration) DebugDraw::DrawText(text, position, color, size, duration) #else +#define DEBUG_DRAW_AXIS_FROM_DIRECTION(origin,direction,color,Size,duration,depthTest) +#define DEBUG_DRAW_DIRECTION (origin,direction,color,duration,depthTest) +#define DEBUG_DRAW_RAY (origin,direction,color,length,duration,depthTest) +#define DEBUG_DRAW_RAY (ray,color,length,duration,depthTest) -#define DEBUG_DRAW_LINE(start, end, color, duration, depthTest) -#define DEBUG_DRAW_LINES(lines, transform, color, duration, depthTest) -#define DEBUG_DRAW_BEZIER(p1, p2, p3, p4, color, duration, depthTest) -#define DEBUG_DRAW_CIRCLE(position, normal, radius, color, duration, depthTest) -#define DEBUG_DRAW_TRIANGLE(v0, v1, v2, color, duration, depthTest) -#define DEBUG_DRAW_TRIANGLES(vertices, color, duration, depthTest) -#define DEBUG_DRAW_TRIANGLES_EX(vertices, indices, color, duration, depthTest) -#define DEBUG_DRAW_TRIANGLES_EX2(vertices, indices, transform, color, duration, depthTest) -#define DEBUG_DRAW_SPHERE(sphere, color, duration, depthTest) -#define DEBUG_DRAW_TUBE(position, orientation, radius, length, color, duration, depthTest) -#define DEBUG_DRAW_BOX(box, color, duration, depthTest) -#define DEBUG_DRAW_CYLINDER(position, orientation, radius, height, color, duration, depthTest) -#define DEBUG_DRAW_CONE(position, orientation, radius, angleXY, angleXZ, color, duration, depthTest) -#define DEBUG_DRAW_ARC(position, orientation, radius, angle, color, duration, depthTest) -#define DEBUG_DRAW_WIRE_TRIANGLE(v0, v1, v2, color, duration, depthTest) -#define DEBUG_DRAW_WIRE_TRIANGLES(vertices, color, duration, depthTest) -#define DEBUG_DRAW_WIRE_TRIANGLES_EX(vertices, indices, color, duration, depthTest) -#define DEBUG_DRAW_WIRE_BOX(box, color, duration, depthTest) -#define DEBUG_DRAW_WIRE_FRUSTUM(frustum, color, duration, depthTest) -#define DEBUG_DRAW_WIRE_SPHERE(sphere, color, duration, depthTest) -#define DEBUG_DRAW_WIRE_TUBE(position, orientation, radius, length, color, duration, depthTest) -#define DEBUG_DRAW_WIRE_CYLINDER(position, orientation, radius, height, color, duration, depthTest) +#define DEBUG_DRAW_RAY(origin, direction, color, duration, depthTest) +#define DEBUG_DRAW_LINE(start, end, color, duration, depthTest) +#define DEBUG_DRAW_LINES(lines, transform, color, duration, depthTest) +#define DEBUG_DRAW_BEZIER(p1, p2, p3, p4, color, duration, depthTest) +#define DEBUG_DRAW_CIRCLE(position, normal, radius, color, duration, depthTest) +#define DEBUG_DRAW_TRIANGLE(v0, v1, v2, color, duration, depthTest) +#define DEBUG_DRAW_TRIANGLES(vertices, color, duration, depthTest) +#define DEBUG_DRAW_TRIANGLES_EX(vertices, indices, color, duration, depthTest) +#define DEBUG_DRAW_TRIANGLES_EX2(vertices, indices, transform, color, duration, depthTest) +#define DEBUG_DRAW_SPHERE(sphere, color, duration, depthTest) +#define DEBUG_DRAW_TUBE(position, orientation, radius, length, color, duration, depthTest) +#define DEBUG_DRAW_BOX(box, color, duration, depthTest) +#define DEBUG_DRAW_CYLINDER(position, orientation, radius, height, color, duration, depthTest) +#define DEBUG_DRAW_CONE(position, orientation, radius, angleXY, angleXZ, color, duration, depthTest) +#define DEBUG_DRAW_ARC(position, orientation, radius, angle, color, duration, depthTest) +#define DEBUG_DRAW_WIRE_TRIANGLE(v0, v1, v2, color, duration, depthTest) +#define DEBUG_DRAW_WIRE_TRIANGLES(vertices, color, duration, depthTest) +#define DEBUG_DRAW_WIRE_TRIANGLES_EX(vertices, indices, color, duration, depthTest) +#define DEBUG_DRAW_WIRE_BOX(box, color, duration, depthTest) +#define DEBUG_DRAW_WIRE_FRUSTUM(frustum, color, duration, depthTest) +#define DEBUG_DRAW_WIRE_SPHERE(sphere, color, duration, depthTest) +#define DEBUG_DRAW_WIRE_TUBE(position, orientation, radius, length, color, duration, depthTest) +#define DEBUG_DRAW_WIRE_CYLINDER(position, orientation, radius, height, color, duration, depthTest) #define DEBUG_DRAW_WIRE_CONE(position, orientation, radius, angleXY, angleXZ, color, duration, depthTest) -#define DEBUG_DRAW_WIRE_ARC(position, orientation, radius, angle, color, duration, depthTest) -#define DEBUG_DRAW_WIRE_ARROW(position, orientation, scale, color, duration, depthTest) -#define DEBUG_DRAW_TEXT(text, position, color, size, duration) +#define DEBUG_DRAW_WIRE_ARC(position, orientation, radius, angle, color, duration, depthTest) +#define DEBUG_DRAW_WIRE_ARROW(position, orientation, scale, color, duration, depthTest) +#define DEBUG_DRAW_TEXT(text, position, color, size, duration) #endif From 394a3696af4d4b040296b94de783791564faf77e Mon Sep 17 00:00:00 2001 From: NoriteSC <53096989+NoriteSC@users.noreply.github.com> Date: Wed, 13 Sep 2023 02:54:17 +0200 Subject: [PATCH 06/61] removed coppy --- Source/Engine/Debug/DebugDraw.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Source/Engine/Debug/DebugDraw.h b/Source/Engine/Debug/DebugDraw.h index 5df31d831..c557fa57c 100644 --- a/Source/Engine/Debug/DebugDraw.h +++ b/Source/Engine/Debug/DebugDraw.h @@ -684,8 +684,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw #define DEBUG_DRAW_DIRECTION (origin,direction,color,duration,depthTest) #define DEBUG_DRAW_RAY (origin,direction,color,length,duration,depthTest) #define DEBUG_DRAW_RAY (ray,color,length,duration,depthTest) - -#define DEBUG_DRAW_RAY(origin, direction, color, duration, depthTest) + #define DEBUG_DRAW_LINE(start, end, color, duration, depthTest) #define DEBUG_DRAW_LINES(lines, transform, color, duration, depthTest) #define DEBUG_DRAW_BEZIER(p1, p2, p3, p4, color, duration, depthTest) From 0b3b32195abc889a334eca24cc9abf106f194126 Mon Sep 17 00:00:00 2001 From: NoriteSC <53096989+NoriteSC@users.noreply.github.com> Date: Wed, 13 Sep 2023 12:17:17 +0200 Subject: [PATCH 07/61] Update DebugDraw.cs --- Source/Engine/Debug/DebugDraw.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Debug/DebugDraw.cs b/Source/Engine/Debug/DebugDraw.cs index f6a8a19f4..5884fc4fa 100644 --- a/Source/Engine/Debug/DebugDraw.cs +++ b/Source/Engine/Debug/DebugDraw.cs @@ -40,7 +40,7 @@ namespace FlaxEngine /// The size of the axis. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - static void DrawAxisFromDirection(Vector3 origin, Vector3 direction, Color color ,float Size = 100.0f, float duration = 0.0f, bool depthTest = true); + static void DrawAxisFromDirection(Vector3 origin, Vector3 direction, Color color ,float Size = 100.0f, float duration = 0.0f, bool depthTest = true){} /// /// Draws the line in a direction. @@ -50,7 +50,7 @@ namespace FlaxEngine /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - static void DrawDirection(Vector3 origin, Vector3 direction, Color color, float duration = 0.0f, bool depthTest = true); + static void DrawDirection(Vector3 origin, Vector3 direction, Color color, float duration = 0.0f, bool depthTest = true){} /// /// Draws the line in a direction. @@ -61,7 +61,7 @@ namespace FlaxEngine /// The length of the ray. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - static void DrawRay(Vector3 origin, Vector3 direction, Color color, float length = 3.402823466e+38f, float duration = 0.0f, bool depthTest = true); + static void DrawRay(Vector3 origin, Vector3 direction, Color color, float length = 3.402823466e+38f, float duration = 0.0f, bool depthTest = true){} /// /// Draws the line in a direction. @@ -71,7 +71,9 @@ namespace FlaxEngine /// The length of the ray. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - static void DrawRay(Ray ray,Color color, float length = 3.402823466e+38f, float duration = 0.0f, bool depthTest = true); + static void DrawRay(Ray ray,Color color, float length = 3.402823466e+38f, float duration = 0.0f, bool depthTest = true) + { + } /// /// Draws the line. From 4451f5feb280165bd06caeeff37cdcd3925787a6 Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Thu, 21 Sep 2023 12:44:47 -0400 Subject: [PATCH 08/61] add spline snap --- Source/Editor/SceneGraph/Actors/SplineNode.cs | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs index 1b35cdc35..a6d07fc2a 100644 --- a/Source/Editor/SceneGraph/Actors/SplineNode.cs +++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs @@ -7,6 +7,7 @@ using Real = System.Single; #endif using System; +using System.Collections.Generic; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.Modules; using FlaxEngine; @@ -287,6 +288,10 @@ namespace FlaxEditor.SceneGraph.Actors private const Real PointNodeSize = 1.5f; private const Real TangentNodeSize = 1.0f; + private const Real SnapIndicatorSize = 1.7f; + private const Real SnapPointIndicatorSize = 2f; + + private static Spline _currentEditSpline; /// public SplineNode(Actor actor) @@ -298,6 +303,11 @@ namespace FlaxEditor.SceneGraph.Actors private unsafe void OnUpdate() { + if (Input.Keyboard.GetKey(KeyboardKeys.Shift)) + { + EditSplineWithSnap(); + } + // Sync spline points with gizmo handles var actor = (Spline)Actor; var dstCount = actor.SplinePointsCount; @@ -328,6 +338,56 @@ namespace FlaxEditor.SceneGraph.Actors } } + private void EditSplineWithSnap() + { + if (_currentEditSpline == null || _currentEditSpline != Actor) + return; + + var selectedNode = SelectedSplineNode(); + if (selectedNode == null) + return; + + var selectedNodeBounds = new BoundingSphere(selectedNode.Transform.Translation, 1f); + var allSplinesInView = GetSplinesOnView(); + allSplinesInView.Remove(_currentEditSpline); + + if (allSplinesInView.Count == 0 || selectedNode == null) + return; + + var snappedOnSplinePoint = false; + for (int i = 0; i < allSplinesInView.Count; i++) + { + for (int x = 0; x < allSplinesInView[i].SplineKeyframes.Length; x++) + { + var keyframePosition = allSplinesInView[i].GetSplinePoint(x); + var pointIndicatorSize = NodeSizeByDistance(keyframePosition, SnapPointIndicatorSize); + var keyframeBounds = new BoundingSphere(keyframePosition, pointIndicatorSize); + DebugDraw.DrawSphere(keyframeBounds, Color.Red, 0, false); + + if (keyframeBounds.Intersects(selectedNodeBounds)) + { + _currentEditSpline.SetSplinePoint(selectedNode.Index, keyframeBounds.Center); + snappedOnSplinePoint = true; + break; + } + } + } + + if (!snappedOnSplinePoint) + { + var nearSplineSnapPoint = GetNearSplineSnapPosition(selectedNode.Transform.Translation, allSplinesInView); + var snapIndicatorSize = NodeSizeByDistance(nearSplineSnapPoint, SnapIndicatorSize); + var snapBounds = new BoundingSphere(nearSplineSnapPoint, snapIndicatorSize); + + if (snapBounds.Intersects(selectedNodeBounds)) + { + _currentEditSpline.SetSplinePoint(selectedNode.Index, snapBounds.Center); + } + + DebugDraw.DrawSphere(snapBounds, Color.Yellow, 0, true); + } + } + /// public override void PostSpawn() { @@ -396,6 +456,7 @@ namespace FlaxEditor.SceneGraph.Actors internal static void OnSplineEdited(Spline spline) { + _currentEditSpline = spline; var collider = spline.GetChild(); if (collider && collider.Scene && collider.IsActiveInHierarchy && collider.HasStaticFlag(StaticFlags.Navigation) && !Editor.IsPlayMode) { @@ -407,6 +468,60 @@ namespace FlaxEditor.SceneGraph.Actors } } + private static SplinePointNode SelectedSplineNode() + { + var selection = Editor.Instance.SceneEditing.Selection; + if (selection.Count != 1) + return null; + if (selection[0] is not SplineNode.SplinePointNode) + return null; + + return (SplinePointNode)selection[0]; + } + + private static List GetSplinesOnView() + { + var splines = Level.GetActors(); + var splinesOnView = new List(); + + var viewTransform = Editor.Instance.Windows.EditWin.Viewport.ViewTransform; + var viewFov = Editor.Instance.Windows.EditWin.Viewport.FieldOfView; + var viewNear = Editor.Instance.Windows.EditWin.Viewport.NearPlane; + var viewFar = Editor.Instance.Windows.EditWin.Viewport.FarPlane; + var viewAspect = Editor.Instance.Windows.EditWin.Width / Editor.Instance.Windows.EditWin.Height; + var viewBounds = BoundingFrustum.FromCamera(viewTransform.Translation, viewTransform.Forward, viewTransform.Up, viewFov, viewNear, viewFar, viewAspect); + + foreach (var s in splines) + { + var contains = viewBounds.Contains(s.EditorBox); + if (contains == ContainmentType.Contains || contains == ContainmentType.Intersects) + { + splinesOnView.Add(s); + } + } + + return splinesOnView; + } + + private static Vector3 GetNearSplineSnapPosition(Vector3 position, List splines) + { + var nearPoint = splines[0].GetSplinePointClosestToPoint(position); + var nearDistance = Vector3.Distance(nearPoint, position); + + for (int i = 1; i < splines.Count; i++) + { + var point = splines[i].GetSplinePointClosestToPoint(position); + var distance = Vector3.Distance(point, position); + if (distance < nearDistance) + { + nearPoint = point; + nearDistance = distance; + } + } + + return nearPoint; + } + internal static Real NodeSizeByDistance(Vector3 nodePosition, Real nodeSize) { var cameraTransform = Editor.Instance.Windows.EditWin.Viewport.ViewportCamera.Viewport.ViewTransform; From 29e1d9855a6b52d3ffb00cec3f6062860d1a6d5a Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Fri, 22 Sep 2023 15:32:07 -0400 Subject: [PATCH 09/61] add hability to create spline points with mouse click --- Source/Editor/SceneGraph/Actors/SplineNode.cs | 101 +++++++++++++++++- 1 file changed, 96 insertions(+), 5 deletions(-) diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs index a6d07fc2a..d752073d1 100644 --- a/Source/Editor/SceneGraph/Actors/SplineNode.cs +++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs @@ -8,10 +8,12 @@ using Real = System.Single; using System; using System.Collections.Generic; -using FlaxEditor.GUI.ContextMenu; +using FlaxEditor.Actions; using FlaxEditor.Modules; +using FlaxEditor.GUI.ContextMenu; using FlaxEngine; using FlaxEngine.Json; +using FlaxEngine.Utilities; using Object = FlaxEngine.Object; namespace FlaxEditor.SceneGraph.Actors @@ -301,14 +303,24 @@ namespace FlaxEditor.SceneGraph.Actors FlaxEngine.Scripting.Update += OnUpdate; } - private unsafe void OnUpdate() + private void OnUpdate() { if (Input.Keyboard.GetKey(KeyboardKeys.Shift)) { EditSplineWithSnap(); } - // Sync spline points with gizmo handles + var canAddSplinePoint = Input.Mouse.PositionDelta == Float2.Zero && Input.Mouse.Position != Float2.Zero; + var requestAddSplinePoint = Input.Keyboard.GetKey(KeyboardKeys.Control) && Input.Mouse.GetButtonDown(MouseButton.Right); + + if (requestAddSplinePoint && canAddSplinePoint) + AddSplinePoint(); + + SyncSplineKeyframeWithNodes(); + } + + private unsafe void SyncSplineKeyframeWithNodes() + { var actor = (Spline)Actor; var dstCount = actor.SplinePointsCount; if (dstCount > 1 && actor.IsLoop) @@ -338,12 +350,91 @@ namespace FlaxEditor.SceneGraph.Actors } } + private unsafe void AddSplinePoint() + { + var selectedPoint = GetSelectedSplineNode(); + if (selectedPoint == null) + return; + + // checking mouse hit on scene + var spline = (Spline)Actor; + var viewport = Editor.Instance.Windows.EditWin.Viewport; + var mouseRay = viewport.MouseRay; + var viewRay = new Ray(viewport.ViewPosition, viewport.ViewDirection); + var flags = RayCastData.FlagTypes.SkipColliders | RayCastData.FlagTypes.SkipEditorPrimitives; + var hit = Editor.Instance.Scene.Root.RayCast(ref mouseRay, ref viewRay, out var closest, out var normal, flags); + + if (hit == null) + return; + + // Undo data + var oldSpline = spline.SplineKeyframes; + var editAction = new EditSplineAction(spline, oldSpline); + Root.Undo.AddAction(editAction); + + // Getting spline point to duplicate + var hitPoint = mouseRay.Position + mouseRay.Direction * closest; + var lastPointIndex = selectedPoint.Index; + var newPointIndex = lastPointIndex > 0 ? lastPointIndex + 1 : 0; + var lastKeyframe = spline.GetSplineKeyframe(lastPointIndex); + var isLastPoint = lastPointIndex == spline.SplinePointsCount - 1; + var isFirstPoint = lastPointIndex == 0; + + // Getting data to create new point + + var lastPointTime = spline.GetSplineTime(lastPointIndex); + var nextPointTime = isLastPoint ? lastPointTime : spline.GetSplineTime(newPointIndex); + var newTime = isLastPoint ? lastPointTime + 1.0f : (lastPointTime + nextPointTime) * 0.5f; + var distanceFromLastPoint = Vector3.Distance(hitPoint, spline.GetSplinePoint(lastPointIndex)); + var newPointDirection = spline.GetSplineTangent(lastPointIndex, false).Translation - hitPoint; + + // set correctly keyframe direction on spawn point + if (isFirstPoint) + newPointDirection = hitPoint - spline.GetSplineTangent(lastPointIndex, true).Translation; + else if (isLastPoint) + newPointDirection = spline.GetSplineTangent(lastPointIndex, false).Translation - hitPoint; + + var newPointLocalPosition = spline.Transform.WorldToLocal(hitPoint); + var newPointLocalOrientation = Quaternion.LookRotation(newPointDirection); + + // Adding new point + spline.InsertSplinePoint(newPointIndex, newTime, Transform.Identity, false); + var newKeyframe = lastKeyframe.DeepClone(); + var newKeyframeTransform = newKeyframe.Value; + newKeyframeTransform.Translation = newPointLocalPosition; + newKeyframeTransform.Orientation = newPointLocalOrientation; + newKeyframe.Value = newKeyframeTransform; + + // Setting new point keyframe + var newkeyframeTangentIn = Transform.Identity; + var newkeyframeTangentOut = Transform.Identity; + newkeyframeTangentIn.Translation = (Vector3.Forward * newPointLocalOrientation) * distanceFromLastPoint; + newkeyframeTangentOut.Translation = (Vector3.Backward * newPointLocalOrientation) * distanceFromLastPoint; + newKeyframe.TangentIn = newkeyframeTangentIn; + newKeyframe.TangentOut = newkeyframeTangentOut; + spline.SetSplineKeyframe(newPointIndex, newKeyframe); + + for (int i = 1; i < spline.SplinePointsCount; i++) + { + // check all elements to don't left keyframe has invalid time + // because points can be added on start or on middle of spline + // conflicting with time of another keyframes + spline.SetSplinePointTime(i, i, false); + } + + // Select new point node + SyncSplineKeyframeWithNodes(); + Editor.Instance.SceneEditing.Select(ChildNodes[newPointIndex]); + + spline.UpdateSpline(); + } + private void EditSplineWithSnap() { if (_currentEditSpline == null || _currentEditSpline != Actor) return; - var selectedNode = SelectedSplineNode(); + var selectedNode = GetSelectedSplineNode(); if (selectedNode == null) return; @@ -468,7 +559,7 @@ namespace FlaxEditor.SceneGraph.Actors } } - private static SplinePointNode SelectedSplineNode() + private static SplinePointNode GetSelectedSplineNode() { var selection = Editor.Instance.SceneEditing.Selection; if (selection.Count != 1) From 809b9e3a7ae5aef0d4a196a482cec3e641429836 Mon Sep 17 00:00:00 2001 From: NoriteSC <53096989+NoriteSC@users.noreply.github.com> Date: Sun, 1 Oct 2023 16:47:33 +0200 Subject: [PATCH 10/61] added safeguards --- Source/Engine/Debug/DebugDraw.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Source/Engine/Debug/DebugDraw.cpp b/Source/Engine/Debug/DebugDraw.cpp index 733c4a643..1e50a74be 100644 --- a/Source/Engine/Debug/DebugDraw.cpp +++ b/Source/Engine/Debug/DebugDraw.cpp @@ -934,15 +934,22 @@ void DebugDraw::DrawAxisFromDirection(const Vector3& origin, const Vector3& dire void DebugDraw::DrawDirection(const Vector3& origin, const Vector3& direction, const Color& color, float duration, bool depthTest) { + auto dir = origin + direction; + if (dir.IsNanOrInfinity()) + return; DrawLine(origin, origin + direction, color, duration, depthTest); } void DebugDraw::DrawRay(const Vector3& origin, const Vector3& direction,const Color& color, float length, float duration, bool depthTest) { + if (isnan(length) || isinf(length)) + return; DrawLine(origin, origin + (direction.GetNormalized() * length), color, duration, depthTest); } void DebugDraw::DrawRay(const Ray& ray,const Color& color, float length, float duration, bool depthTest) { + if (isnan(length) || isinf(length)) + return; DrawLine(ray.Position, ray.Position + (ray.Direction.GetNormalized() * length), color, duration, depthTest); } From 0a127249743917f5a15b86142cc0a4469fea8532 Mon Sep 17 00:00:00 2001 From: NoriteSC <53096989+NoriteSC@users.noreply.github.com> Date: Tue, 10 Oct 2023 22:04:30 +0000 Subject: [PATCH 11/61] changed the 3.402823466e+38f to MAX_float --- Source/Engine/Debug/DebugDraw.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Debug/DebugDraw.h b/Source/Engine/Debug/DebugDraw.h index c557fa57c..140a11013 100644 --- a/Source/Engine/Debug/DebugDraw.h +++ b/Source/Engine/Debug/DebugDraw.h @@ -99,7 +99,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The length of the ray. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawRay(const Vector3& origin, const Vector3& direction, const Color& color = Color::White, float length = 3.402823466e+38f, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawRay(const Vector3& origin, const Vector3& direction, const Color& color = Color::White, float length = MAX_float, float duration = 0.0f, bool depthTest = true); /// /// Draws the line in a direction. @@ -109,7 +109,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The length of the ray. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawRay(const Ray& ray,const Color& color = Color::White, float length = 3.402823466e+38f, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawRay(const Ray& ray,const Color& color = Color::White, float length = MAX_float, float duration = 0.0f, bool depthTest = true); /// /// Draws the line. From 4f67d142612410fc57a01e99835774bf93eb0c25 Mon Sep 17 00:00:00 2001 From: NoriteSC <53096989+NoriteSC@users.noreply.github.com> Date: Tue, 10 Oct 2023 22:15:31 +0000 Subject: [PATCH 12/61] forgotten the public keyword in engine/debug/debugdraw.cs --- Source/Engine/Debug/DebugDraw.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Debug/DebugDraw.cs b/Source/Engine/Debug/DebugDraw.cs index 5884fc4fa..a6fc7b3b2 100644 --- a/Source/Engine/Debug/DebugDraw.cs +++ b/Source/Engine/Debug/DebugDraw.cs @@ -40,7 +40,7 @@ namespace FlaxEngine /// The size of the axis. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - static void DrawAxisFromDirection(Vector3 origin, Vector3 direction, Color color ,float Size = 100.0f, float duration = 0.0f, bool depthTest = true){} + public static void DrawAxisFromDirection(Vector3 origin, Vector3 direction, Color color ,float Size = 100.0f, float duration = 0.0f, bool depthTest = true){} /// /// Draws the line in a direction. @@ -50,7 +50,7 @@ namespace FlaxEngine /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - static void DrawDirection(Vector3 origin, Vector3 direction, Color color, float duration = 0.0f, bool depthTest = true){} + public static void DrawDirection(Vector3 origin, Vector3 direction, Color color, float duration = 0.0f, bool depthTest = true){} /// /// Draws the line in a direction. @@ -61,7 +61,7 @@ namespace FlaxEngine /// The length of the ray. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - static void DrawRay(Vector3 origin, Vector3 direction, Color color, float length = 3.402823466e+38f, float duration = 0.0f, bool depthTest = true){} + public static void DrawRay(Vector3 origin, Vector3 direction, Color color, float length = 3.402823466e+38f, float duration = 0.0f, bool depthTest = true){} /// /// Draws the line in a direction. @@ -71,7 +71,7 @@ namespace FlaxEngine /// The length of the ray. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - static void DrawRay(Ray ray,Color color, float length = 3.402823466e+38f, float duration = 0.0f, bool depthTest = true) + public static void DrawRay(Ray ray,Color color, float length = 3.402823466e+38f, float duration = 0.0f, bool depthTest = true) { } From fff8a1e8a6b78de58c0e29287656eaaf3bb09b64 Mon Sep 17 00:00:00 2001 From: "Mr. Capybara" Date: Sun, 22 Oct 2023 13:26:07 -0400 Subject: [PATCH 13/61] Optimize actor search --- Source/Engine/Level/Actor.cpp | 12 ++++++++---- Source/Engine/Level/Actor.cs | 12 +++++++----- Source/Engine/Level/Actor.h | 6 ++++-- Source/Engine/Level/Level.cpp | 28 ++++++++++++++++------------ Source/Engine/Level/Level.h | 9 ++++++--- 5 files changed, 41 insertions(+), 26 deletions(-) diff --git a/Source/Engine/Level/Actor.cpp b/Source/Engine/Level/Actor.cpp index 75e3c12d4..06ddd8d8d 100644 --- a/Source/Engine/Level/Actor.cpp +++ b/Source/Engine/Level/Actor.cpp @@ -1359,14 +1359,16 @@ Actor* Actor::FindActor(const StringView& name) const return result; } -Actor* Actor::FindActor(const MClass* type) const +Actor* Actor::FindActor(const MClass* type, bool activeOnly) const { CHECK_RETURN(type, nullptr); + if (activeOnly && !_isActive) + return nullptr; if (GetClass()->IsSubClassOf(type)) return const_cast(this); for (auto child : Children) { - const auto actor = child->FindActor(type); + const auto actor = child->FindActor(type, activeOnly); if (actor) return actor; } @@ -1387,14 +1389,16 @@ Actor* Actor::FindActor(const MClass* type, const StringView& name) const return nullptr; } -Actor* Actor::FindActor(const MClass* type, const Tag& tag) const +Actor* Actor::FindActor(const MClass* type, const Tag& tag, bool activeOnly) const { CHECK_RETURN(type, nullptr); + if (activeOnly && !_isActive) + return nullptr; if (GetClass()->IsSubClassOf(type) && HasTag(tag)) return const_cast(this); for (auto child : Children) { - const auto actor = child->FindActor(type, tag); + const auto actor = child->FindActor(type, tag, activeOnly); if (actor) return actor; } diff --git a/Source/Engine/Level/Actor.cs b/Source/Engine/Level/Actor.cs index dbe8a89b5..f1fc09bfd 100644 --- a/Source/Engine/Level/Actor.cs +++ b/Source/Engine/Level/Actor.cs @@ -253,10 +253,11 @@ namespace FlaxEngine /// Tries to find the actor of the given type in this actor hierarchy (checks this actor and all children hierarchy). /// /// Type of the object. + /// Finds only a active actor. /// Actor instance if found, null otherwise. - public T FindActor() where T : Actor + public T FindActor(bool activeOnly = false) where T : Actor { - return FindActor(typeof(T)) as T; + return FindActor(typeof(T), activeOnly) as T; } /// @@ -269,16 +270,17 @@ namespace FlaxEngine { return FindActor(typeof(T), name) as T; } - + /// /// Tries to find actor of the given type and tag in this actor hierarchy (checks this actor and all children hierarchy). /// /// A tag on the object. /// Type of the object. + /// Finds only an active actor. /// Actor instance if found, null otherwise. - public T FindActor(Tag tag) where T : Actor + public T FindActor(Tag tag, bool activeOnly = false) where T : Actor { - return FindActor(typeof(T), tag) as T; + return FindActor(typeof(T), tag, activeOnly) as T; } /// diff --git a/Source/Engine/Level/Actor.h b/Source/Engine/Level/Actor.h index a1f2dc78f..ded07fce5 100644 --- a/Source/Engine/Level/Actor.h +++ b/Source/Engine/Level/Actor.h @@ -751,8 +751,9 @@ public: /// Tries to find the actor of the given type in this actor hierarchy (checks this actor and all children hierarchy). /// /// Type of the actor to search for. Includes any actors derived from the type. + /// Finds only a active actor. /// Actor instance if found, null otherwise. - API_FUNCTION() Actor* FindActor(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type) const; + API_FUNCTION() Actor* FindActor(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type, bool activeOnly = false) const; /// /// Tries to find the actor of the given type and name in this actor hierarchy (checks this actor and all children hierarchy). @@ -767,8 +768,9 @@ public: /// /// Type of the actor to search for. Includes any actors derived from the type. /// The tag of the actor to search for. + /// Finds only an active actor. /// Actor instance if found, null otherwise. - API_FUNCTION() Actor* FindActor(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type, const Tag& tag) const; + API_FUNCTION() Actor* FindActor(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type, const Tag& tag, bool activeOnly = false) const; /// /// Tries to find the actor of the given type in this actor hierarchy (checks this actor and all children hierarchy). diff --git a/Source/Engine/Level/Level.cpp b/Source/Engine/Level/Level.cpp index dfd5786c6..19d0313bb 100644 --- a/Source/Engine/Level/Level.cpp +++ b/Source/Engine/Level/Level.cpp @@ -1421,13 +1421,13 @@ Actor* Level::FindActor(const StringView& name) return result; } -Actor* Level::FindActor(const MClass* type) +Actor* Level::FindActor(const MClass* type, bool activeOnly) { CHECK_RETURN(type, nullptr); Actor* result = nullptr; ScopeLock lock(ScenesLock); for (int32 i = 0; result == nullptr && i < Scenes.Count(); i++) - result = Scenes[i]->FindActor(type); + result = Scenes[i]->FindActor(type, activeOnly); return result; } @@ -1441,29 +1441,33 @@ Actor* Level::FindActor(const MClass* type, const StringView& name) return result; } -Actor* FindActorRecursive(Actor* node, const Tag& tag) +Actor* FindActorRecursive(Actor* node, const Tag& tag, bool activeOnly) { + if (activeOnly && !node->GetIsActive()) + return nullptr; if (node->HasTag(tag)) return node; Actor* result = nullptr; for (Actor* child : node->Children) { - result = FindActorRecursive(child, tag); + result = FindActorRecursive(child, tag, activeOnly); if (result) break; } return result; } -Actor* FindActorRecursiveByType(Actor* node, const MClass* type, const Tag& tag) +Actor* FindActorRecursiveByType(Actor* node, const MClass* type, const Tag& tag, bool activeOnly) { CHECK_RETURN(type, nullptr); + if (activeOnly && !node->GetIsActive()) + return nullptr; if (node->HasTag(tag) && node->GetClass()->IsSubClassOf(type)) return node; Actor* result = nullptr; for (Actor* child : node->Children) { - result = FindActorRecursiveByType(child, type, tag); + result = FindActorRecursiveByType(child, type, tag, activeOnly); if (result) break; } @@ -1496,30 +1500,30 @@ void FindActorsRecursiveByParentTags(Actor* node, const Array& tags, const FindActorsRecursiveByParentTags(child, tags, activeOnly, result); } -Actor* Level::FindActor(const Tag& tag, Actor* root) +Actor* Level::FindActor(const Tag& tag, bool activeOnly, Actor* root) { PROFILE_CPU(); if (root) - return FindActorRecursive(root, tag); + return FindActorRecursive(root, tag, activeOnly); Actor* result = nullptr; for (Scene* scene : Scenes) { - result = FindActorRecursive(scene, tag); + result = FindActorRecursive(scene, tag, activeOnly); if (result) break; } return result; } -Actor* Level::FindActor(const MClass* type, const Tag& tag, Actor* root) +Actor* Level::FindActor(const MClass* type, const Tag& tag, bool activeOnly, Actor* root) { CHECK_RETURN(type, nullptr); if (root) - return FindActorRecursiveByType(root, type, tag); + return FindActorRecursiveByType(root, type, tag, activeOnly); Actor* result = nullptr; ScopeLock lock(ScenesLock); for (int32 i = 0; result == nullptr && i < Scenes.Count(); i++) - result = Scenes[i]->FindActor(type, tag); + result = Scenes[i]->FindActor(type, tag, activeOnly); return result; } diff --git a/Source/Engine/Level/Level.h b/Source/Engine/Level/Level.h index 1f0acda2d..bb97b8908 100644 --- a/Source/Engine/Level/Level.h +++ b/Source/Engine/Level/Level.h @@ -360,8 +360,9 @@ public: /// Tries to find the actor of the given type in all the loaded scenes. /// /// Type of the actor to search for. Includes any actors derived from the type. + /// Finds only an active actor. /// Found actor or null. - API_FUNCTION() static Actor* FindActor(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type); + API_FUNCTION() static Actor* FindActor(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type, bool activeOnly = false); /// /// Tries to find the actor of the given type and name in all the loaded scenes. @@ -375,18 +376,20 @@ public: /// Tries to find the actor with the given tag (returns the first one found). /// /// The tag of the actor to search for. + /// Finds only an active actor. /// The custom root actor to start searching from (hierarchical), otherwise null to search all loaded scenes. /// Found actor or null. - API_FUNCTION() static Actor* FindActor(const Tag& tag, Actor* root = nullptr); + API_FUNCTION() static Actor* FindActor(const Tag& tag, bool activeOnly = false, Actor* root = nullptr); /// /// Tries to find the actor of the given type and tag in all the loaded scenes. /// /// Type of the actor to search for. Includes any actors derived from the type. /// The tag of the actor to search for. + /// Finds only an active actor. /// The custom root actor to start searching from (hierarchical), otherwise null to search all loaded scenes. /// Actor instance if found, null otherwise. - API_FUNCTION() static Actor* FindActor(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type, const Tag& tag, Actor* root = nullptr); + API_FUNCTION() static Actor* FindActor(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type, const Tag& tag, bool activeOnly = false, Actor* root = nullptr); /// /// Tries to find the actors with the given tag (returns all found). From ff64fdb503e7d27a91da8d41628f83c2ca3f0621 Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Thu, 26 Oct 2023 20:33:07 +0200 Subject: [PATCH 14/61] - Test environment and first tests --- Source/Editor/Surface/Archetypes/Math.cs | 14 +++++++++++--- Source/Editor/Surface/ContextMenu/VisjectCMItem.cs | 4 +--- Source/Editor/Utilities/QueryFilterHelper.cs | 9 ++++----- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Math.cs b/Source/Editor/Surface/Archetypes/Math.cs index fe2f1e044..7f4afa6a6 100644 --- a/Source/Editor/Surface/Archetypes/Math.cs +++ b/Source/Editor/Surface/Archetypes/Math.cs @@ -13,6 +13,11 @@ namespace FlaxEditor.Surface.Archetypes public static class Math { private static NodeArchetype Op1(ushort id, string title, string desc, ConnectionsHint hints = ConnectionsHint.Numeric, Type type = null) + { + return Op1(id, title, desc, null, hints, type); + } + + private static NodeArchetype Op1(ushort id, string title, string desc, string[] altTitles, ConnectionsHint hints = ConnectionsHint.Numeric, Type type = null) { return new NodeArchetype { @@ -20,6 +25,7 @@ namespace FlaxEditor.Surface.Archetypes Title = title, Description = desc, Flags = NodeFlags.AllGraphs, + AlternativeTitles = altTitles, Size = new Float2(110, 20), DefaultType = new ScriptType(type), ConnectionsHints = hints, @@ -92,6 +98,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 11, Title = "Length", + AlternativeTitles = new[] { "Magnitude", "Mag" }, Description = "Returns the length of A vector", Flags = NodeFlags.AllGraphs, Size = new Float2(110, 20), @@ -107,10 +114,10 @@ namespace FlaxEditor.Surface.Archetypes Op1(13, "Round", "Rounds A to the nearest integer"), Op1(14, "Saturate", "Clamps A to the range [0, 1]"), Op1(15, "Sine", "Returns sine of A"), - Op1(16, "Sqrt", "Returns square root of A"), + Op1(16, "Sqrt", "Returns square root of A", new [] { "Square Root", "Square", "Root" }), Op1(17, "Tangent", "Returns tangent of A"), Op2(18, "Cross", "Returns the cross product of A and B", ConnectionsHint.None, typeof(Float3)), - Op2(19, "Distance", "Returns a distance scalar between A and B", ConnectionsHint.Vector, null, typeof(float), false), + Op2(19, "Distance", "Returns a distance scalar between A and B", new [] { "Magnitude", "Mag", "Length" }, ConnectionsHint.Vector, null, typeof(float), false), Op2(20, "Dot", "Returns the dot product of A and B", ConnectionsHint.Vector, null, typeof(float), false), Op2(21, "Max", "Selects the greater of A and B"), Op2(22, "Min", "Selects the lesser of A and B"), @@ -185,7 +192,7 @@ namespace FlaxEditor.Surface.Archetypes } }, // - Op1(27, "Negate", "Returns opposite value"), + Op1(27, "Negate", "Returns opposite value", new [] { "Invert" }), Op1(28, "One Minus", "Returns 1 - value"), // new NodeArchetype @@ -225,6 +232,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 31, Title = "Mad", + AlternativeTitles = new [] { "Multiply", "Add", "*+" }, Description = "Performs value multiplication and addition at once", Flags = NodeFlags.AllGraphs, Size = new Float2(160, 60), diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs index 37da7d74d..2694d7e53 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs @@ -72,9 +72,7 @@ namespace FlaxEditor.Surface.ContextMenu public void UpdateScore(Box selectedBox) { SortScore = 0; - - if (!(_highlights?.Count > 0)) - return; + if (!Visible) return; diff --git a/Source/Editor/Utilities/QueryFilterHelper.cs b/Source/Editor/Utilities/QueryFilterHelper.cs index e9e5e9ccb..f2db81009 100644 --- a/Source/Editor/Utilities/QueryFilterHelper.cs +++ b/Source/Editor/Utilities/QueryFilterHelper.cs @@ -140,8 +140,7 @@ namespace FlaxEditor.Utilities // Check if start the matching sequence if (matchStartPos == -1) { - if (ranges == null) - ranges = new List(); + ranges ??= new List(); matchStartPos = textPos; } } @@ -152,7 +151,7 @@ namespace FlaxEditor.Utilities { var length = textPos - matchStartPos; if (length >= MinLength) - ranges.Add(new Range(matchStartPos, length)); + ranges!.Add(new Range(matchStartPos, length)); textPos = matchStartPos + length; matchStartPos = -1; } @@ -165,13 +164,13 @@ namespace FlaxEditor.Utilities { var length = endPos - matchStartPos; if (length >= MinLength) - ranges.Add(new Range(matchStartPos, length)); + ranges!.Add(new Range(matchStartPos, length)); textPos = matchStartPos + length; } } // Check if has any range - if (ranges != null && ranges.Count > 0) + if (ranges is { Count: > 0 }) { matches = ranges.ToArray(); return true; From 0acf352d40ae4eb767359adcba5d506a4adebecb Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Thu, 26 Oct 2023 21:13:06 +0200 Subject: [PATCH 15/61] - Alternative titles now use QueryFilterHelper Match function as well - Cleaned up some code and using early outs now - Added some comments --- .../Surface/ContextMenu/VisjectCMItem.cs | 136 ++++++++++-------- 1 file changed, 76 insertions(+), 60 deletions(-) diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs index 2694d7e53..41821c2aa 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs @@ -166,6 +166,7 @@ namespace FlaxEditor.Surface.ContextMenu /// True if item's group header got a filter match and item should stay visible. public void UpdateFilter(string filterText, Box selectedBox, bool groupHeaderMatches = false) { + // When dragging connection out of a box, validate if the box is compatible with this item's type if (selectedBox != null) { Visible = CanConnectTo(selectedBox); @@ -182,72 +183,87 @@ namespace FlaxEditor.Surface.ContextMenu // Clear filter _highlights?.Clear(); Visible = true; + return; } - else + + GetTextRectangle(out var textRect); + + // Check archetype title + if (QueryFilterHelper.Match(filterText, _archetype.Title, out var ranges)) { - GetTextRectangle(out var textRect); - if (QueryFilterHelper.Match(filterText, _archetype.Title, out var ranges)) + // Update highlights + if (_highlights == null) + _highlights = new List(ranges.Length); + else + _highlights.Clear(); + var style = Style.Current; + var font = style.FontSmall; + for (int i = 0; i < ranges.Length; i++) { - // Update highlights - if (_highlights == null) - _highlights = new List(ranges.Length); - else - _highlights.Clear(); - var style = Style.Current; - var font = style.FontSmall; - for (int i = 0; i < ranges.Length; i++) + var start = font.GetCharPosition(_archetype.Title, ranges[i].StartIndex); + var end = font.GetCharPosition(_archetype.Title, ranges[i].EndIndex); + _highlights.Add(new Rectangle(start.X + textRect.X, 0, end.X - start.X, Height)); + + if (ranges[i].StartIndex <= 0) { - var start = font.GetCharPosition(_archetype.Title, ranges[i].StartIndex); - var end = font.GetCharPosition(_archetype.Title, ranges[i].EndIndex); - _highlights.Add(new Rectangle(start.X + textRect.X, 0, end.X - start.X, Height)); - - if (ranges[i].StartIndex <= 0) - { - _isStartsWithMatch = true; - if (ranges[i].Length == _archetype.Title.Length) - _isFullMatch = true; - } + _isStartsWithMatch = true; + if (ranges[i].Length == _archetype.Title.Length) + _isFullMatch = true; } - Visible = true; - } - else if (_archetype.AlternativeTitles?.Any(altTitle => string.Equals(filterText, altTitle, StringComparison.CurrentCultureIgnoreCase)) == true) - { - // Update highlights - if (_highlights == null) - _highlights = new List(1); - else - _highlights.Clear(); - var style = Style.Current; - var font = style.FontSmall; - var start = font.GetCharPosition(_archetype.Title, 0); - var end = font.GetCharPosition(_archetype.Title, _archetype.Title.Length - 1); - _highlights.Add(new Rectangle(start.X + textRect.X, 0, end.X - start.X, Height)); - _isFullMatch = true; - Visible = true; - } - else if (NodeArchetype.TryParseText != null && NodeArchetype.TryParseText(filterText, out var data)) - { - // Update highlights - if (_highlights == null) - _highlights = new List(1); - else - _highlights.Clear(); - var style = Style.Current; - var font = style.FontSmall; - var start = font.GetCharPosition(_archetype.Title, 0); - var end = font.GetCharPosition(_archetype.Title, _archetype.Title.Length - 1); - _highlights.Add(new Rectangle(start.X + textRect.X, 0, end.X - start.X, Height)); - Visible = true; - - Data = data; - } - else if (!groupHeaderMatches) - { - // Hide - _highlights?.Clear(); - Visible = false; } + Visible = true; + return; } + + // Check archetype synonyms + if (_archetype.AlternativeTitles!= null && _archetype.AlternativeTitles.Any(altTitle => QueryFilterHelper.Match(filterText, altTitle, out ranges))) + { + // Update highlights + if (_highlights == null) + _highlights = new List(1); + else + _highlights.Clear(); + var style = Style.Current; + var font = style.FontSmall; + var start = font.GetCharPosition(_archetype.Title, 0); + var end = font.GetCharPosition(_archetype.Title, _archetype.Title.Length - 1); + _highlights.Add(new Rectangle(start.X + textRect.X, 0, end.X - start.X, Height)); + for (int i = 0; i < ranges.Length; i++) + { + if (ranges[i].StartIndex <= 0) + { + _isStartsWithMatch = true; + } + } + Visible = true; + return; + } + + // Check archetype data (if it exists) + if (NodeArchetype.TryParseText != null && NodeArchetype.TryParseText(filterText, out var data)) + { + // Update highlights + if (_highlights == null) + _highlights = new List(1); + else + _highlights.Clear(); + var style = Style.Current; + var font = style.FontSmall; + var start = font.GetCharPosition(_archetype.Title, 0); + var end = font.GetCharPosition(_archetype.Title, _archetype.Title.Length - 1); + _highlights.Add(new Rectangle(start.X + textRect.X, 0, end.X - start.X, Height)); + Visible = true; + + Data = data; + return; + } + + if (groupHeaderMatches) + return; + + // Hide + _highlights?.Clear(); + Visible = false; } /// @@ -280,7 +296,7 @@ namespace FlaxEditor.Surface.ContextMenu } // Draw name - Render2D.DrawText(style.FontSmall, _archetype.Title, textRect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontSmall, _archetype.Title + "(" + SortScore + ")", textRect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); if (_archetype.SubTitle != null) { var titleLength = style.FontSmall.MeasureText(_archetype.Title).X; From 8cc7c5d3d905270308e58ce0da2bfa0db979a5ee Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Thu, 26 Oct 2023 21:47:32 +0200 Subject: [PATCH 16/61] - Highlights get cleared now when there is no match - Highlights now give points, since it means there is a substring match - Cleanup --- .../Surface/ContextMenu/VisjectCMItem.cs | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs index 41821c2aa..c6288f327 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs @@ -80,6 +80,8 @@ namespace FlaxEditor.Surface.ContextMenu SortScore += 1; if (Data != null) SortScore += 1; + if (_highlights is { Count: > 0 }) + SortScore += 1; if (_isStartsWithMatch) SortScore += 2; if (_isFullMatch) @@ -185,9 +187,9 @@ namespace FlaxEditor.Surface.ContextMenu Visible = true; return; } - + GetTextRectangle(out var textRect); - + // Check archetype title if (QueryFilterHelper.Match(filterText, _archetype.Title, out var ranges)) { @@ -214,7 +216,7 @@ namespace FlaxEditor.Surface.ContextMenu Visible = true; return; } - + // Check archetype synonyms if (_archetype.AlternativeTitles!= null && _archetype.AlternativeTitles.Any(altTitle => QueryFilterHelper.Match(filterText, altTitle, out ranges))) { @@ -238,7 +240,7 @@ namespace FlaxEditor.Surface.ContextMenu Visible = true; return; } - + // Check archetype data (if it exists) if (NodeArchetype.TryParseText != null && NodeArchetype.TryParseText(filterText, out var data)) { @@ -257,13 +259,12 @@ namespace FlaxEditor.Surface.ContextMenu Data = data; return; } - - if (groupHeaderMatches) - return; - - // Hide + _highlights?.Clear(); - Visible = false; + + // Hide + if (!groupHeaderMatches) + Visible = false; } /// @@ -296,7 +297,7 @@ namespace FlaxEditor.Surface.ContextMenu } // Draw name - Render2D.DrawText(style.FontSmall, _archetype.Title + "(" + SortScore + ")", textRect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontSmall, _archetype.Title, textRect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); if (_archetype.SubTitle != null) { var titleLength = style.FontSmall.MeasureText(_archetype.Title).X; From e55cdd91d5c11052c9e54e3c4867230e4979b4a8 Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Thu, 26 Oct 2023 22:23:55 +0200 Subject: [PATCH 17/61] - Reverted match class from more alternative titles PR --- Source/Editor/Surface/Archetypes/Math.cs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Math.cs b/Source/Editor/Surface/Archetypes/Math.cs index 7f4afa6a6..fe2f1e044 100644 --- a/Source/Editor/Surface/Archetypes/Math.cs +++ b/Source/Editor/Surface/Archetypes/Math.cs @@ -13,11 +13,6 @@ namespace FlaxEditor.Surface.Archetypes public static class Math { private static NodeArchetype Op1(ushort id, string title, string desc, ConnectionsHint hints = ConnectionsHint.Numeric, Type type = null) - { - return Op1(id, title, desc, null, hints, type); - } - - private static NodeArchetype Op1(ushort id, string title, string desc, string[] altTitles, ConnectionsHint hints = ConnectionsHint.Numeric, Type type = null) { return new NodeArchetype { @@ -25,7 +20,6 @@ namespace FlaxEditor.Surface.Archetypes Title = title, Description = desc, Flags = NodeFlags.AllGraphs, - AlternativeTitles = altTitles, Size = new Float2(110, 20), DefaultType = new ScriptType(type), ConnectionsHints = hints, @@ -98,7 +92,6 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 11, Title = "Length", - AlternativeTitles = new[] { "Magnitude", "Mag" }, Description = "Returns the length of A vector", Flags = NodeFlags.AllGraphs, Size = new Float2(110, 20), @@ -114,10 +107,10 @@ namespace FlaxEditor.Surface.Archetypes Op1(13, "Round", "Rounds A to the nearest integer"), Op1(14, "Saturate", "Clamps A to the range [0, 1]"), Op1(15, "Sine", "Returns sine of A"), - Op1(16, "Sqrt", "Returns square root of A", new [] { "Square Root", "Square", "Root" }), + Op1(16, "Sqrt", "Returns square root of A"), Op1(17, "Tangent", "Returns tangent of A"), Op2(18, "Cross", "Returns the cross product of A and B", ConnectionsHint.None, typeof(Float3)), - Op2(19, "Distance", "Returns a distance scalar between A and B", new [] { "Magnitude", "Mag", "Length" }, ConnectionsHint.Vector, null, typeof(float), false), + Op2(19, "Distance", "Returns a distance scalar between A and B", ConnectionsHint.Vector, null, typeof(float), false), Op2(20, "Dot", "Returns the dot product of A and B", ConnectionsHint.Vector, null, typeof(float), false), Op2(21, "Max", "Selects the greater of A and B"), Op2(22, "Min", "Selects the lesser of A and B"), @@ -192,7 +185,7 @@ namespace FlaxEditor.Surface.Archetypes } }, // - Op1(27, "Negate", "Returns opposite value", new [] { "Invert" }), + Op1(27, "Negate", "Returns opposite value"), Op1(28, "One Minus", "Returns 1 - value"), // new NodeArchetype @@ -232,7 +225,6 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 31, Title = "Mad", - AlternativeTitles = new [] { "Multiply", "Add", "*+" }, Description = "Performs value multiplication and addition at once", Flags = NodeFlags.AllGraphs, Size = new Float2(160, 60), From 95735035f3989a6a4c40b67ffe8586fa44735ff6 Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Sat, 28 Oct 2023 16:51:56 +0200 Subject: [PATCH 18/61] - Tab key now also spawns node --- Source/Editor/Surface/ContextMenu/VisjectCM.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Surface/ContextMenu/VisjectCM.cs b/Source/Editor/Surface/ContextMenu/VisjectCM.cs index 930741807..8e5e4defd 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCM.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCM.cs @@ -704,7 +704,7 @@ namespace FlaxEditor.Surface.ContextMenu Hide(); return true; } - else if (key == KeyboardKeys.Return) + else if (key == KeyboardKeys.Return || key == KeyboardKeys.Tab) { if (SelectedItem != null) OnClickItem(SelectedItem); From b88233a65d012903faff04bd776324d41cfc01c6 Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Sat, 28 Oct 2023 16:58:37 +0200 Subject: [PATCH 19/61] - Always select first valid item when updating filters --- Source/Editor/Surface/ContextMenu/VisjectCM.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Source/Editor/Surface/ContextMenu/VisjectCM.cs b/Source/Editor/Surface/ContextMenu/VisjectCM.cs index 8e5e4defd..ad4bf8372 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCM.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCM.cs @@ -492,8 +492,7 @@ namespace FlaxEditor.Surface.ContextMenu // If no item is selected (or it's not visible anymore), select the top one Profiler.BeginEvent("VisjectCM.Layout"); - if (SelectedItem == null || !SelectedItem.VisibleInHierarchy) - SelectedItem = _groups.Find(g => g.Visible)?.Children.Find(c => c.Visible && c is VisjectCMItem) as VisjectCMItem; + SelectedItem = _groups.Find(g => g.Visible)?.Children.Find(c => c.Visible && c is VisjectCMItem) as VisjectCMItem; PerformLayout(); if (SelectedItem != null) _panel1.ScrollViewTo(SelectedItem); From 0fea7aad9295755113f817c95e6556e208d49b88 Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Sat, 28 Oct 2023 17:11:03 +0200 Subject: [PATCH 20/61] - Minor cleanup --- Source/Editor/Surface/ContextMenu/VisjectCMItem.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs index c6288f327..d1bcec377 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs @@ -72,7 +72,7 @@ namespace FlaxEditor.Surface.ContextMenu public void UpdateScore(Box selectedBox) { SortScore = 0; - + if (!Visible) return; @@ -230,6 +230,7 @@ namespace FlaxEditor.Surface.ContextMenu var start = font.GetCharPosition(_archetype.Title, 0); var end = font.GetCharPosition(_archetype.Title, _archetype.Title.Length - 1); _highlights.Add(new Rectangle(start.X + textRect.X, 0, end.X - start.X, Height)); + for (int i = 0; i < ranges.Length; i++) { if (ranges[i].StartIndex <= 0) @@ -237,6 +238,7 @@ namespace FlaxEditor.Surface.ContextMenu _isStartsWithMatch = true; } } + Visible = true; return; } @@ -255,7 +257,6 @@ namespace FlaxEditor.Surface.ContextMenu var end = font.GetCharPosition(_archetype.Title, _archetype.Title.Length - 1); _highlights.Add(new Rectangle(start.X + textRect.X, 0, end.X - start.X, Height)); Visible = true; - Data = data; return; } From 5afb00e2c6444caa53c53b314e9c53eeab585304 Mon Sep 17 00:00:00 2001 From: MineBill Date: Sat, 28 Oct 2023 20:21:43 +0300 Subject: [PATCH 21/61] Allow quick creation of scripts. --- .../CustomEditors/Dedicated/ScriptsEditor.cs | 138 +++++++++++++++++- Source/Editor/GUI/ItemsListContextMenu.cs | 3 + 2 files changed, 139 insertions(+), 2 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs index d7bfbbad7..7e2b84fea 100644 --- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs @@ -3,6 +3,8 @@ using System; using System.Collections; using System.Collections.Generic; +using System.IO; +using System.Linq; using FlaxEditor.Actions; using FlaxEditor.Content; using FlaxEditor.GUI; @@ -16,6 +18,25 @@ using Object = FlaxEngine.Object; namespace FlaxEditor.CustomEditors.Dedicated { + internal class NewScriptItem : ItemsListContextMenu.Item + { + private string _scriptName; + public string ScriptName + { + get => _scriptName; + set + { + _scriptName = value; + Name = $"Create script '{value}'"; + } + } + + public NewScriptItem(string scriptName) + { + ScriptName = scriptName; + TooltipText = "Create a new script"; + } + } /// /// Drag and drop scripts area control. /// @@ -74,7 +95,48 @@ namespace FlaxEditor.CustomEditors.Dedicated { cm.AddItem(new TypeSearchPopup.TypeItemView(scripts[i])); } - cm.ItemClicked += item => AddScript((ScriptType)item.Tag); + cm.TextChanged += text => + { + if (!IsValidScriptName(text)) + return; + var items = cm.ItemsPanel.Children.Count(x => x.Visible && x is not NewScriptItem); + if (items == 0) + { + // If there are no visible items, that means the search failed so we can find the create script + // button or create one if it's the first time. + + var createScriptItem = cm.ItemsPanel.Children.FirstOrDefault(x => x is NewScriptItem); + if (createScriptItem != null) + { + var item = createScriptItem as NewScriptItem; + item.Visible = true; + item.ScriptName = text; + } + else + { + cm.AddItem(new NewScriptItem(text)); + } + } + else + { + // Make sure to hide the create script button if there + var createScriptButton = cm.ItemsPanel.Children.FirstOrDefault(x => x is NewScriptItem); + if (createScriptButton != null) + createScriptButton.Visible = false; + } + }; + cm.ItemClicked += item => + { + if (item.Tag is ScriptType script) + { + AddScript(script); + } + else if (item is NewScriptItem newScriptItem) + { + CreateScript(newScriptItem); + } + + }; cm.SortItems(); cm.Show(this, button.BottomLeft - new Float2((cm.Width - button.Width) / 2, 0)); } @@ -112,6 +174,17 @@ namespace FlaxEditor.CustomEditors.Dedicated return scriptItem.ScriptType != ScriptType.Null; return false; } + + private static bool IsValidScriptName(string text) + { + if (text.Contains(' ')) + return false; + if (char.IsDigit(text[0])) + return false; + if (text.Any(c => !char.IsLetterOrDigit(c) && c != '_')) + return false; + return true; + } /// public override DragDropEffect OnDragEnter(ref Float2 location, DragData data) @@ -177,7 +250,46 @@ namespace FlaxEditor.CustomEditors.Dedicated return result; } - private void AddScript(ScriptType item) + private void CreateScript(NewScriptItem item) + { + ScriptsEditor.NewScriptItem = item; + var paths = Directory.GetFiles(Globals.ProjectSourceFolder, "*.Build.cs"); + + string moduleName = null; + foreach (var p in paths) + { + var file = File.ReadAllText(p); + // Skip + if (!file.Contains("GameProjectTarget")) + continue; + + if (file.Contains("Modules.Add(\"Game\")")) + { + // Assume Game represents the main game module + moduleName = "Game"; + break; + } + } + + // Ensure the path slashes are correct for the OS + var correctedPath = Path.GetFullPath(Globals.ProjectSourceFolder); + + if (string.IsNullOrEmpty(moduleName)) + { + var error = FileSystem.ShowBrowseFolderDialog(Editor.Instance.Windows.MainWindow, correctedPath, "Select a module folder to put the new script in", out moduleName); + if (error) + return; + } + + var path = Path.Combine(Globals.ProjectSourceFolder, moduleName, item.ScriptName + ".cs"); + new CSharpScriptProxy().Create(path, null); + } + + /// + /// Attach a script to the actor. + /// + /// The script. + public void AddScript(ScriptType item) { var list = new List(1) { item }; AddScripts(list); @@ -439,6 +551,11 @@ namespace FlaxEditor.CustomEditors.Dedicated /// public override IEnumerable UndoObjects => _scripts; + // We need somewhere to store the newly created script name. + // The problem is that the ScriptsEditor gets destroyed after scripts compilation + // so we must make it static to store this information. + internal static NewScriptItem NewScriptItem { get; set; } + private void AddMissingScript(int index, LayoutElementsContainer layout) { var group = layout.Group("Missing script"); @@ -546,6 +663,23 @@ namespace FlaxEditor.CustomEditors.Dedicated // Area for drag&drop scripts var dragArea = layout.CustomContainer(); dragArea.CustomControl.ScriptsEditor = this; + + // If the initialization is triggered by an editor recompilation, check if it + // was due to script generation from DragAreaControl. + if (NewScriptItem != null) + { + var script = Editor.Instance.CodeEditing.Scripts.Get() + .FirstOrDefault(x => x.Name == NewScriptItem.ScriptName); + if (script != null) + { + dragArea.CustomControl.AddScript(script); + NewScriptItem = null; + } + else + { + Editor.LogWarning("Failed to find newly created script."); + } + } // No support for showing scripts from multiple actors that have different set of scripts var scripts = (Script[])Values[0]; diff --git a/Source/Editor/GUI/ItemsListContextMenu.cs b/Source/Editor/GUI/ItemsListContextMenu.cs index 42d236991..a0351bf0f 100644 --- a/Source/Editor/GUI/ItemsListContextMenu.cs +++ b/Source/Editor/GUI/ItemsListContextMenu.cs @@ -189,6 +189,8 @@ namespace FlaxEditor.GUI /// public event Action ItemClicked; + public event Action TextChanged; + /// /// The panel control where you should add your items. /// @@ -263,6 +265,7 @@ namespace FlaxEditor.GUI UnlockChildrenRecursive(); PerformLayout(true); _searchBox.Focus(); + TextChanged?.Invoke(_searchBox.Text); } /// From 1c23f0f5b49a11beee76032329b268cffd87986f Mon Sep 17 00:00:00 2001 From: "Mr. Capybara" Date: Fri, 3 Nov 2023 11:30:24 -0400 Subject: [PATCH 22/61] Add ActiveOnly parameter to Level::GetActors --- Source/Engine/Level/Level.cpp | 10 ++++++---- Source/Engine/Level/Level.cs | 5 +++-- Source/Engine/Level/Level.h | 3 ++- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Source/Engine/Level/Level.cpp b/Source/Engine/Level/Level.cpp index cfdf11a6f..6770961fb 100644 --- a/Source/Engine/Level/Level.cpp +++ b/Source/Engine/Level/Level.cpp @@ -1564,12 +1564,14 @@ Script* Level::FindScript(const MClass* type) namespace { - void GetActors(const MClass* type, Actor* actor, Array& result) + void GetActors(const MClass* type, Actor* actor, bool activeOnly, Array& result) { + if (activeOnly && !actor->GetIsActive()) + return; if (actor->GetClass()->IsSubClassOf(type)) result.Add(actor); for (auto child : actor->Children) - GetActors(type, child, result); + GetActors(type, child, activeOnly, result); } void GetScripts(const MClass* type, Actor* actor, Array& result) @@ -1582,13 +1584,13 @@ namespace } } -Array Level::GetActors(const MClass* type) +Array Level::GetActors(const MClass* type, bool activeOnly) { Array result; CHECK_RETURN(type, result); ScopeLock lock(ScenesLock); for (int32 i = 0; i < Scenes.Count(); i++) - ::GetActors(type, Scenes[i], result); + ::GetActors(type, Scenes[i], activeOnly, result); return result; } diff --git a/Source/Engine/Level/Level.cs b/Source/Engine/Level/Level.cs index 1e8524f2c..b43d9e91a 100644 --- a/Source/Engine/Level/Level.cs +++ b/Source/Engine/Level/Level.cs @@ -119,10 +119,11 @@ namespace FlaxEngine /// Finds all the actors of the given type in all the loaded scenes. /// /// Type of the object. + /// Finds only active actors. /// Found actors list. - public static T[] GetActors() where T : Actor + public static T[] GetActors(bool activeOnly = false) where T : Actor { - var actors = GetActors(typeof(T)); + var actors = GetActors(typeof(T), activeOnly); var result = new T[actors.Length]; for (int i = 0; i < actors.Length; i++) result[i] = actors[i] as T; diff --git a/Source/Engine/Level/Level.h b/Source/Engine/Level/Level.h index e09f8e376..0e7ff04c9 100644 --- a/Source/Engine/Level/Level.h +++ b/Source/Engine/Level/Level.h @@ -460,8 +460,9 @@ public: /// Finds all the actors of the given type in all the loaded scenes. /// /// Type of the actor to search for. Includes any actors derived from the type. + /// Finds only active actors in the scene. /// Found actors list. - API_FUNCTION() static Array GetActors(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type); + API_FUNCTION() static Array GetActors(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type, bool activeOnly = false); /// /// Finds all the scripts of the given type in all the loaded scenes. From de55ad90b75c23e5ed7c14d96335a3daa829c286 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 16 Nov 2023 20:57:53 -0600 Subject: [PATCH 23/61] Add require script attribute and functionality. --- .../CustomEditors/Dedicated/ScriptsEditor.cs | 70 ++++++++++++++++++- .../Editor/RequireScriptAttribute.cs | 25 +++++++ 2 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 Source/Engine/Scripting/Attributes/Editor/RequireScriptAttribute.cs diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs index d7bfbbad7..9da2ab180 100644 --- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs @@ -224,16 +224,44 @@ namespace FlaxEditor.CustomEditors.Dedicated private void AddScripts(List items) { - var actions = new List(4); + var actions = new List(); for (int i = 0; i < items.Count; i++) { var scriptType = items[i]; + List requiredScripts = new List(); + if (scriptType.HasAttribute(typeof(RequireScriptAttribute), false)) + { + var attributes = new List(); + foreach (var e in scriptType.GetAttributes(false)) + { + if (e is not RequireScriptAttribute requireScriptAttribute) + continue; + attributes.Add(requireScriptAttribute); + } + + if (attributes.Count > 0) + { + foreach (var attribute in attributes) + { + if (!attribute.RequiredType.IsSubclassOf(typeof(Script))) + continue; + requiredScripts.Add(new ScriptType(attribute.RequiredType)); + } + } + } var actors = ScriptsEditor.ParentEditor.Values; for (int j = 0; j < actors.Count; j++) { var actor = (Actor)actors[j]; actions.Add(AddRemoveScript.Add(actor, scriptType)); + // Check if actor has required scripts and add them if the actor does not. + foreach (var type in requiredScripts) + { + if (actor.GetScript(type.Type) != null) + continue; + actions.Add(AddRemoveScript.Add(actor, type)); + } } } @@ -584,10 +612,50 @@ namespace FlaxEditor.CustomEditors.Dedicated var values = new ScriptsContainer(elementType, i, Values); var scriptType = TypeUtils.GetObjectType(script); var editor = CustomEditorsUtil.CreateEditor(scriptType, false); + + // Check if actor has all the required scripts + bool hasAllRequiredScripts = true; + if (scriptType.HasAttribute(typeof(RequireScriptAttribute), false)) + { + var scriptTypesToCheck = new List(); + var attributes = new List(); + foreach (var e in scriptType.GetAttributes(false)) + { + if (e is not RequireScriptAttribute requireScriptAttribute) + continue; + attributes.Add(requireScriptAttribute); + } + + if (attributes.Count > 0) + { + foreach (var attribute in attributes) + { + if (!attribute.RequiredType.IsSubclassOf(typeof(Script))) + continue; + scriptTypesToCheck.Add(new ScriptType(attribute.RequiredType)); + } + } + + if (scriptTypesToCheck.Count > 0) + { + foreach (var type in scriptTypesToCheck) + { + var requiredScript = script.Actor.GetScript(type.Type); + if (requiredScript == null) + { + Editor.LogWarning($"{script} on {script.Actor} is missing required script of type {type}."); + hasAllRequiredScripts = false; + break; + } + } + } + } // Create group var title = Utilities.Utils.GetPropertyNameUI(scriptType.Name); var group = layout.Group(title, editor); + if (!hasAllRequiredScripts) + group.Panel.HeaderTextColor = FlaxEngine.GUI.Style.Current.Statusbar.Failed; if ((Presenter.Features & FeatureFlags.CacheExpandedGroups) != 0) { if (Editor.Instance.ProjectCache.IsCollapsedGroup(title)) diff --git a/Source/Engine/Scripting/Attributes/Editor/RequireScriptAttribute.cs b/Source/Engine/Scripting/Attributes/Editor/RequireScriptAttribute.cs new file mode 100644 index 000000000..eee529c5c --- /dev/null +++ b/Source/Engine/Scripting/Attributes/Editor/RequireScriptAttribute.cs @@ -0,0 +1,25 @@ +using System; + +namespace FlaxEngine; + +/// +/// This attribute is used to check for if a script requires another script type. +/// +[Serializable] +[AttributeUsage(AttributeTargets.Class)] +public class RequireScriptAttribute : Attribute +{ + /// + /// The required type. + /// + public Type RequiredType; + + /// + /// Initializes a new instance of the class. + /// + /// The required type. + public RequireScriptAttribute(Type type) + { + RequiredType = type; + } +} From c4c3a3a5e8f0743cbaca227deda933583d88c9f3 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 16 Nov 2023 21:01:19 -0600 Subject: [PATCH 24/61] Remove break to show all missing required scripts in logs. --- Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs index 9da2ab180..91a89bba7 100644 --- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs @@ -643,9 +643,8 @@ namespace FlaxEditor.CustomEditors.Dedicated var requiredScript = script.Actor.GetScript(type.Type); if (requiredScript == null) { - Editor.LogWarning($"{script} on {script.Actor} is missing required script of type {type}."); + Editor.LogWarning($"{Utilities.Utils.GetPropertyNameUI(scriptType.Name)} on {script.Actor} is missing a required script of type {type}."); hasAllRequiredScripts = false; - break; } } } From 44e55cc8b6a3c5fb3cce5f1de828d9489fed7bf3 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 16 Nov 2023 21:25:40 -0600 Subject: [PATCH 25/61] Add require Actor attribute --- .../CustomEditors/Dedicated/ScriptsEditor.cs | 62 ++++++++++++++++--- .../Editor/RequireActorAttribute.cs | 25 ++++++++ 2 files changed, 80 insertions(+), 7 deletions(-) create mode 100644 Source/Engine/Scripting/Attributes/Editor/RequireActorAttribute.cs diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs index 91a89bba7..93a8ec6be 100644 --- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs @@ -242,18 +242,43 @@ namespace FlaxEditor.CustomEditors.Dedicated if (attributes.Count > 0) { - foreach (var attribute in attributes) + foreach (var e in attributes) { - if (!attribute.RequiredType.IsSubclassOf(typeof(Script))) + if (!e.RequiredType.IsSubclassOf(typeof(Script))) continue; - requiredScripts.Add(new ScriptType(attribute.RequiredType)); + requiredScripts.Add(new ScriptType(e.RequiredType)); } } } + + // See if script requires a specific actor type + RequireActorAttribute actorAttribute = null; + if (scriptType.HasAttribute(typeof(RequireActorAttribute), false)) + { + foreach (var e in scriptType.GetAttributes(false)) + { + if (e is not RequireActorAttribute requireActorAttribute) + continue; + actorAttribute = requireActorAttribute; + break; + } + } + var actors = ScriptsEditor.ParentEditor.Values; for (int j = 0; j < actors.Count; j++) { var actor = (Actor)actors[j]; + + // If required actor exists but is not this actor type then skip adding to actor + if (actorAttribute != null) + { + if (actor.GetType() != actorAttribute.RequiredType && !actor.GetType().IsSubclassOf(actorAttribute.RequiredType)) + { + Editor.LogWarning($"{Utilities.Utils.GetPropertyNameUI(scriptType.Name)} not added to {actor} due to script requiring an Actor type of {actorAttribute.RequiredType}."); + continue; + } + } + actions.Add(AddRemoveScript.Add(actor, scriptType)); // Check if actor has required scripts and add them if the actor does not. foreach (var type in requiredScripts) @@ -614,7 +639,7 @@ namespace FlaxEditor.CustomEditors.Dedicated var editor = CustomEditorsUtil.CreateEditor(scriptType, false); // Check if actor has all the required scripts - bool hasAllRequiredScripts = true; + bool hasAllRequirements = true; if (scriptType.HasAttribute(typeof(RequireScriptAttribute), false)) { var scriptTypesToCheck = new List(); @@ -643,17 +668,40 @@ namespace FlaxEditor.CustomEditors.Dedicated var requiredScript = script.Actor.GetScript(type.Type); if (requiredScript == null) { - Editor.LogWarning($"{Utilities.Utils.GetPropertyNameUI(scriptType.Name)} on {script.Actor} is missing a required script of type {type}."); - hasAllRequiredScripts = false; + Editor.LogWarning($"{Utilities.Utils.GetPropertyNameUI(scriptType.Name)} on {script.Actor} is missing a required Script of type {type}."); + hasAllRequirements = false; } } } } + if (scriptType.HasAttribute(typeof(RequireActorAttribute), false)) + { + var scriptTypesToCheck = new List(); + RequireActorAttribute attribute = null; + foreach (var e in scriptType.GetAttributes(false)) + { + if (e is not RequireActorAttribute requireActorAttribute) + continue; + attribute = requireActorAttribute; + break; + } + + if (attribute != null) + { + var actor = script.Actor; + if (actor.GetType() != attribute.RequiredType && !actor.GetType().IsSubclassOf(attribute.RequiredType)) + { + Editor.LogWarning($"{Utilities.Utils.GetPropertyNameUI(scriptType.Name)} on {script.Actor} is missing a required Actor of type {attribute.RequiredType}."); + hasAllRequirements = false; + // Maybe call to remove script here? + } + } + } // Create group var title = Utilities.Utils.GetPropertyNameUI(scriptType.Name); var group = layout.Group(title, editor); - if (!hasAllRequiredScripts) + if (!hasAllRequirements) group.Panel.HeaderTextColor = FlaxEngine.GUI.Style.Current.Statusbar.Failed; if ((Presenter.Features & FeatureFlags.CacheExpandedGroups) != 0) { diff --git a/Source/Engine/Scripting/Attributes/Editor/RequireActorAttribute.cs b/Source/Engine/Scripting/Attributes/Editor/RequireActorAttribute.cs new file mode 100644 index 000000000..6e08217bf --- /dev/null +++ b/Source/Engine/Scripting/Attributes/Editor/RequireActorAttribute.cs @@ -0,0 +1,25 @@ +using System; + +namespace FlaxEngine; + +/// +/// This attribute is used to check for if a script requires an Actor type. +/// +[Serializable] +[AttributeUsage(AttributeTargets.Class)] +public class RequireActorAttribute : Attribute +{ + /// + /// The required type. + /// + public Type RequiredType; + + /// + /// Initializes a new instance of the class. + /// + /// The required type. + public RequireActorAttribute(Type type) + { + RequiredType = type; + } +} From 2ac8480df4c311177efeaced06bcfcd3873c8eb5 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 16 Nov 2023 21:45:33 -0600 Subject: [PATCH 26/61] Simplify RequireScriptAttribute code. --- .../CustomEditors/Dedicated/ScriptsEditor.cs | 60 ++++++++----------- .../Editor/RequireScriptAttribute.cs | 13 +++- 2 files changed, 35 insertions(+), 38 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs index 93a8ec6be..6692eb5f0 100644 --- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs @@ -229,25 +229,15 @@ namespace FlaxEditor.CustomEditors.Dedicated for (int i = 0; i < items.Count; i++) { var scriptType = items[i]; - List requiredScripts = new List(); + RequireScriptAttribute scriptAttribute = null; if (scriptType.HasAttribute(typeof(RequireScriptAttribute), false)) { - var attributes = new List(); foreach (var e in scriptType.GetAttributes(false)) { if (e is not RequireScriptAttribute requireScriptAttribute) continue; - attributes.Add(requireScriptAttribute); - } - - if (attributes.Count > 0) - { - foreach (var e in attributes) - { - if (!e.RequiredType.IsSubclassOf(typeof(Script))) - continue; - requiredScripts.Add(new ScriptType(e.RequiredType)); - } + scriptAttribute = requireScriptAttribute; + break; } } @@ -274,18 +264,26 @@ namespace FlaxEditor.CustomEditors.Dedicated { if (actor.GetType() != actorAttribute.RequiredType && !actor.GetType().IsSubclassOf(actorAttribute.RequiredType)) { - Editor.LogWarning($"{Utilities.Utils.GetPropertyNameUI(scriptType.Name)} not added to {actor} due to script requiring an Actor type of {actorAttribute.RequiredType}."); + Editor.LogWarning($"`{Utilities.Utils.GetPropertyNameUI(scriptType.Name)}` not added to `{actor}` due to script requiring an Actor type of `{actorAttribute.RequiredType}`."); continue; } } actions.Add(AddRemoveScript.Add(actor, scriptType)); // Check if actor has required scripts and add them if the actor does not. - foreach (var type in requiredScripts) + if (scriptAttribute != null) { - if (actor.GetScript(type.Type) != null) - continue; - actions.Add(AddRemoveScript.Add(actor, type)); + foreach (var type in scriptAttribute.RequiredTypes) + { + if (!type.IsSubclassOf(typeof(Script))) + { + Editor.LogWarning($"`{Utilities.Utils.GetPropertyNameUI(type.Name)}` not added to `{actor}` due to the class not being a subclass of Script."); + continue; + } + if (actor.GetScript(type) != null) + continue; + actions.Add(AddRemoveScript.Add(actor, new ScriptType(type))); + } } } } @@ -642,33 +640,24 @@ namespace FlaxEditor.CustomEditors.Dedicated bool hasAllRequirements = true; if (scriptType.HasAttribute(typeof(RequireScriptAttribute), false)) { - var scriptTypesToCheck = new List(); - var attributes = new List(); + RequireScriptAttribute scriptAttribute = null; foreach (var e in scriptType.GetAttributes(false)) { if (e is not RequireScriptAttribute requireScriptAttribute) continue; - attributes.Add(requireScriptAttribute); + scriptAttribute = requireScriptAttribute; } - if (attributes.Count > 0) + if (scriptAttribute != null) { - foreach (var attribute in attributes) + foreach (var type in scriptAttribute.RequiredTypes) { - if (!attribute.RequiredType.IsSubclassOf(typeof(Script))) + if (!type.IsSubclassOf(typeof(Script))) continue; - scriptTypesToCheck.Add(new ScriptType(attribute.RequiredType)); - } - } - - if (scriptTypesToCheck.Count > 0) - { - foreach (var type in scriptTypesToCheck) - { - var requiredScript = script.Actor.GetScript(type.Type); + var requiredScript = script.Actor.GetScript(type); if (requiredScript == null) { - Editor.LogWarning($"{Utilities.Utils.GetPropertyNameUI(scriptType.Name)} on {script.Actor} is missing a required Script of type {type}."); + Editor.LogWarning($"`{Utilities.Utils.GetPropertyNameUI(scriptType.Name)}` on `{script.Actor}` is missing a required Script of type `{type}`."); hasAllRequirements = false; } } @@ -676,7 +665,6 @@ namespace FlaxEditor.CustomEditors.Dedicated } if (scriptType.HasAttribute(typeof(RequireActorAttribute), false)) { - var scriptTypesToCheck = new List(); RequireActorAttribute attribute = null; foreach (var e in scriptType.GetAttributes(false)) { @@ -691,7 +679,7 @@ namespace FlaxEditor.CustomEditors.Dedicated var actor = script.Actor; if (actor.GetType() != attribute.RequiredType && !actor.GetType().IsSubclassOf(attribute.RequiredType)) { - Editor.LogWarning($"{Utilities.Utils.GetPropertyNameUI(scriptType.Name)} on {script.Actor} is missing a required Actor of type {attribute.RequiredType}."); + Editor.LogWarning($"`{Utilities.Utils.GetPropertyNameUI(scriptType.Name)}` on `{script.Actor}` is missing a required Actor of type `{attribute.RequiredType}`."); hasAllRequirements = false; // Maybe call to remove script here? } diff --git a/Source/Engine/Scripting/Attributes/Editor/RequireScriptAttribute.cs b/Source/Engine/Scripting/Attributes/Editor/RequireScriptAttribute.cs index eee529c5c..9b3c4ccd1 100644 --- a/Source/Engine/Scripting/Attributes/Editor/RequireScriptAttribute.cs +++ b/Source/Engine/Scripting/Attributes/Editor/RequireScriptAttribute.cs @@ -12,7 +12,7 @@ public class RequireScriptAttribute : Attribute /// /// The required type. /// - public Type RequiredType; + public Type[] RequiredTypes; /// /// Initializes a new instance of the class. @@ -20,6 +20,15 @@ public class RequireScriptAttribute : Attribute /// The required type. public RequireScriptAttribute(Type type) { - RequiredType = type; + RequiredTypes = new[] { type }; + } + + /// + /// Initializes a new instance of the <see cref="RequireScriptAttribute"/> class. + /// + /// The required types. + public RequireScriptAttribute(Type[] types) + { + RequiredTypes = types; } } From 9de408e4e886d4455dcff1c6f3ed25cb707d7dfd Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 16 Nov 2023 21:46:37 -0600 Subject: [PATCH 27/61] Fix comments --- .../Scripting/Attributes/Editor/RequireScriptAttribute.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Scripting/Attributes/Editor/RequireScriptAttribute.cs b/Source/Engine/Scripting/Attributes/Editor/RequireScriptAttribute.cs index 9b3c4ccd1..414a13aeb 100644 --- a/Source/Engine/Scripting/Attributes/Editor/RequireScriptAttribute.cs +++ b/Source/Engine/Scripting/Attributes/Editor/RequireScriptAttribute.cs @@ -3,19 +3,19 @@ using System; namespace FlaxEngine; /// -/// This attribute is used to check for if a script requires another script type. +/// This attribute is used to check for if a script requires other script types. /// [Serializable] [AttributeUsage(AttributeTargets.Class)] public class RequireScriptAttribute : Attribute { /// - /// The required type. + /// The required types. /// public Type[] RequiredTypes; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The required type. public RequireScriptAttribute(Type type) @@ -24,7 +24,7 @@ public class RequireScriptAttribute : Attribute } /// - /// Initializes a new instance of the <see cref="RequireScriptAttribute"/> class. + /// Initializes a new instance of the class. /// /// The required types. public RequireScriptAttribute(Type[] types) From 3824b19c8b4aab7ad4ba2e53fc8de8805d6f0e05 Mon Sep 17 00:00:00 2001 From: Menotdan <32620310+Menotdan@users.noreply.github.com> Date: Thu, 16 Nov 2023 23:46:44 -0500 Subject: [PATCH 28/61] Added Actor.GetPrefabRoot() --- Source/Engine/Level/Actor.cpp | 15 +++++++++++++++ Source/Engine/Level/Actor.h | 6 ++++++ 2 files changed, 21 insertions(+) diff --git a/Source/Engine/Level/Actor.cpp b/Source/Engine/Level/Actor.cpp index addb4861e..fa0b10af1 100644 --- a/Source/Engine/Level/Actor.cpp +++ b/Source/Engine/Level/Actor.cpp @@ -1339,6 +1339,21 @@ bool Actor::IsPrefabRoot() const return _isPrefabRoot != 0; } +Actor* Actor::GetPrefabRoot() +{ + if (!this->HasPrefabLink()) + { + return NULL; + } + + Actor* result = this; + while (!result == NULL && !result->IsPrefabRoot()) + { + result = result->GetParent(); + } + return result; +} + Actor* Actor::FindActor(const StringView& name) const { Actor* result = nullptr; diff --git a/Source/Engine/Level/Actor.h b/Source/Engine/Level/Actor.h index 0ce9a0dbc..6aad2f481 100644 --- a/Source/Engine/Level/Actor.h +++ b/Source/Engine/Level/Actor.h @@ -739,6 +739,12 @@ public: /// API_PROPERTY() bool IsPrefabRoot() const; + /// + /// Gets the root of the prefab this actor is attached to. + /// + /// The root prefab object, or null if this actor is not a prefab. + API_FUNCTION() Actor* GetPrefabRoot(); + public: /// /// Tries to find the actor with the given name in this actor hierarchy (checks this actor and all children hierarchy). From e4dc6d729597e4260a6617cbb1550eb5956441db Mon Sep 17 00:00:00 2001 From: "Mr. Capybara" Date: Sat, 18 Nov 2023 14:43:32 -0400 Subject: [PATCH 29/61] add checkbox to start game focused --- Source/Editor/Windows/GameWindow.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Source/Editor/Windows/GameWindow.cs b/Source/Editor/Windows/GameWindow.cs index 8c6f5dc06..0af541713 100644 --- a/Source/Editor/Windows/GameWindow.cs +++ b/Source/Editor/Windows/GameWindow.cs @@ -484,6 +484,16 @@ namespace FlaxEditor.Windows { base.OnShowContextMenu(menu); + // Focus on play + { + var focus = menu.AddButton("Start Focused"); + focus.CloseMenuOnClick = false; + var checkbox = new CheckBox(140, 2, FocusOnPlay) { Parent = focus }; + checkbox.StateChanged += state => FocusOnPlay = state.Checked; + } + + menu.AddSeparator(); + // Viewport Brightness { var brightness = menu.AddButton("Viewport Brightness"); From 03d629f0e1789ef7950219d7a264037b766f5af1 Mon Sep 17 00:00:00 2001 From: nothingTVatYT Date: Mon, 27 Nov 2023 04:19:45 +0100 Subject: [PATCH 30/61] calculate follow selection on FOV and far plane --- Source/Editor/Viewport/Cameras/FPSCamera.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Viewport/Cameras/FPSCamera.cs b/Source/Editor/Viewport/Cameras/FPSCamera.cs index bf2e840ea..0bbcfd0e8 100644 --- a/Source/Editor/Viewport/Cameras/FPSCamera.cs +++ b/Source/Editor/Viewport/Cameras/FPSCamera.cs @@ -174,7 +174,10 @@ namespace FlaxEditor.Viewport.Cameras } else { - position = sphere.Center - Vector3.Forward * orientation * (sphere.Radius * 2.5f); + // calculate the min. distance so that the sphere fits roughly 70% in FOV + // clip to far plane as a disappearing big object might be confusing + var distance = Mathf.Min(1.4f * sphere.Radius / Mathf.Tan(Mathf.DegreesToRadians * Viewport.FieldOfView / 2), Viewport.FarPlane); + position = sphere.Center - Vector3.Forward * orientation * distance; } TargetPoint = sphere.Center; MoveViewport(position, orientation); From 47a25c7828cb6351f660ca2a6d282957458b8b6d Mon Sep 17 00:00:00 2001 From: ExMatics HydrogenC <33123710+HydrogenC@users.noreply.github.com> Date: Tue, 28 Nov 2023 07:17:46 +0800 Subject: [PATCH 31/61] Add font fallback Note: All the `First()` in the code are temperary workarounds to make it work and require refractoring --- Source/Editor/Content/Tree/ContentTreeNode.cs | 5 +- .../CustomEditors/Dedicated/ScriptsEditor.cs | 3 +- .../Dedicated/UIControlEditor.cs | 4 +- .../Editors/ActorTransformEditor.cs | 3 +- .../Editor/CustomEditors/Editors/TagEditor.cs | 2 +- Source/Editor/EditorAssets.cs | 2 + Source/Editor/GUI/ComboBox.cs | 2 +- .../GUI/ContextMenu/ContextMenuButton.cs | 7 +- Source/Editor/GUI/CurveEditor.cs | 2 +- Source/Editor/GUI/Docking/DockWindow.cs | 3 +- Source/Editor/GUI/ItemsListContextMenu.cs | 5 +- Source/Editor/GUI/MainMenuButton.cs | 5 +- Source/Editor/GUI/NavigationButton.cs | 5 +- Source/Editor/GUI/Row.cs | 5 +- Source/Editor/GUI/Table.cs | 3 +- .../Editor/GUI/Timeline/Tracks/MemberTrack.cs | 2 +- Source/Editor/GUI/ToolStripButton.cs | 5 +- Source/Editor/GUI/Tree/TreeNode.cs | 3 +- Source/Editor/Options/InterfaceOptions.cs | 6 + Source/Editor/Options/OptionsModule.cs | 9 +- Source/Editor/SceneGraph/GUI/ActorTreeNode.cs | 4 +- .../Editor/Surface/Archetypes/BehaviorTree.cs | 3 +- .../Surface/ContextMenu/VisjectCMItem.cs | 14 +- Source/Editor/Surface/Elements/InputBox.cs | 4 +- Source/Editor/Surface/SurfaceNode.cs | 5 +- Source/Editor/Tools/Foliage/FoliageTab.cs | 3 +- Source/Editor/Tools/Terrain/CarveTab.cs | 3 +- Source/Editor/Viewport/EditorViewport.cs | 6 +- .../Viewport/Widgets/ViewportWidgetButton.cs | 5 +- Source/Editor/Windows/AboutDialog.cs | 3 +- Source/Editor/Windows/Profiler/Timeline.cs | 5 +- Source/Editor/Windows/ToolboxWindow.cs | 5 +- Source/Engine/Render2D/Font.cpp | 205 +++++++++++ Source/Engine/Render2D/Font.h | 35 ++ Source/Engine/Render2D/FontManager.cpp | 3 + Source/Engine/Render2D/Render2D.cpp | 332 +++++++++++++++++- Source/Engine/Render2D/Render2D.cs | 53 +++ Source/Engine/Render2D/Render2D.h | 45 ++- Source/Engine/Scripting/Scripting.cs | 5 +- Source/Engine/UI/GUI/Common/Button.cs | 3 +- Source/Engine/UI/GUI/Common/Dropdown.cs | 3 +- Source/Engine/UI/GUI/Common/Label.cs | 8 +- Source/Engine/UI/GUI/Common/RichTextBox.cs | 3 +- Source/Engine/UI/GUI/Common/TextBox.cs | 4 +- Source/Engine/UI/GUI/Panels/DropPanel.cs | 3 +- Source/Engine/UI/GUI/Style.cs | 26 +- Source/Engine/UI/GUI/Tooltip.cs | 5 +- 47 files changed, 787 insertions(+), 87 deletions(-) diff --git a/Source/Editor/Content/Tree/ContentTreeNode.cs b/Source/Editor/Content/Tree/ContentTreeNode.cs index 0c0fc6a51..47159c6c9 100644 --- a/Source/Editor/Content/Tree/ContentTreeNode.cs +++ b/Source/Editor/Content/Tree/ContentTreeNode.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System.Collections.Generic; +using System.Linq; using FlaxEditor.GUI; using FlaxEditor.GUI.Drag; using FlaxEditor.GUI.Tree; @@ -140,8 +141,8 @@ namespace FlaxEditor.Content var textRect = TextRect; for (int i = 0; i < ranges.Length; i++) { - var start = font.GetCharPosition(text, ranges[i].StartIndex); - var end = font.GetCharPosition(text, ranges[i].EndIndex); + var start = font.First().GetCharPosition(text, ranges[i].StartIndex); + var end = font.First().GetCharPosition(text, ranges[i].EndIndex); _highlights.Add(new Rectangle(start.X + textRect.X, textRect.Y, end.X - start.X, textRect.Height)); } isThisVisible = true; diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs index d7bfbbad7..1f69074ce 100644 --- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs @@ -3,6 +3,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Linq; using FlaxEditor.Actions; using FlaxEditor.Content; using FlaxEditor.GUI; @@ -42,7 +43,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Add script button var buttonText = "Add script"; - var textSize = Style.Current.FontMedium.MeasureText(buttonText); + var textSize = Style.Current.FontMedium.First().MeasureText(buttonText); float addScriptButtonWidth = (textSize.X < 60.0f) ? 60.0f : textSize.X + 4; var buttonHeight = (textSize.Y < 18) ? 18 : textSize.Y + 4; _addScriptsButton = new Button diff --git a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs index 296560507..5b5c2f90d 100644 --- a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs @@ -247,7 +247,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Info var info = new Label(0, title.Bottom, DialogWidth, InfoHeight) { - Font = new FontReference(style.FontSmall), + Font = new FontReference(style.FontSmall.First()), Text = "Shift: also set bounds\nControl: also set pivot", Parent = this }; @@ -423,7 +423,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Set control type button var space = layout.Space(20); var buttonText = "Set Type"; - var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(buttonText); + var textSize = FlaxEngine.GUI.Style.Current.FontMedium.First().MeasureText(buttonText); float setTypeButtonWidth = (textSize.X < 60.0f) ? 60.0f : textSize.X + 4; var setTypeButton = new Button { diff --git a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs index 4c153e759..d7059f712 100644 --- a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs @@ -2,6 +2,7 @@ using FlaxEngine; using FlaxEngine.GUI; +using System.Linq; namespace FlaxEditor.CustomEditors.Editors { @@ -100,7 +101,7 @@ namespace FlaxEditor.CustomEditors.Editors _linkButton.Clicked += ToggleLink; ToggleEnabled(); SetLinkStyle(); - var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(LinkedLabel.Text.Value); + var textSize = FlaxEngine.GUI.Style.Current.FontMedium.First().MeasureText(LinkedLabel.Text.Value); _linkButton.LocalX += textSize.X + 10; LinkedLabel.SetupContextMenu += (label, menu, editor) => { diff --git a/Source/Editor/CustomEditors/Editors/TagEditor.cs b/Source/Editor/CustomEditors/Editors/TagEditor.cs index dbd5d124c..a174d02e4 100644 --- a/Source/Editor/CustomEditors/Editors/TagEditor.cs +++ b/Source/Editor/CustomEditors/Editors/TagEditor.cs @@ -631,7 +631,7 @@ namespace FlaxEditor.CustomEditors.Editors TooltipText = "Edit...", Parent = _label, }; - var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(buttonText); + var textSize = FlaxEngine.GUI.Style.Current.FontMedium.First().MeasureText(buttonText); if (textSize.Y > button.Width) button.Width = textSize.Y + 2; diff --git a/Source/Editor/EditorAssets.cs b/Source/Editor/EditorAssets.cs index eb2f21356..237908a0c 100644 --- a/Source/Editor/EditorAssets.cs +++ b/Source/Editor/EditorAssets.cs @@ -54,6 +54,8 @@ namespace FlaxEditor /// public static string PrimaryFont = "Editor/Fonts/Roboto-Regular"; + public static string CJKFont = "Editor/Fonts/NotoSansSC-Medium"; + /// /// The Inconsolata Regular font. /// diff --git a/Source/Editor/GUI/ComboBox.cs b/Source/Editor/GUI/ComboBox.cs index 0417cc7e3..7dc407698 100644 --- a/Source/Editor/GUI/ComboBox.cs +++ b/Source/Editor/GUI/ComboBox.cs @@ -273,7 +273,7 @@ namespace FlaxEditor.GUI MaximumItemsInViewCount = 20; var style = Style.Current; - Font = new FontReference(style.FontMedium); + Font = new FontReference(style.FontMedium.First()); TextColor = style.Foreground; BackgroundColor = style.BackgroundNormal; BackgroundColorHighlighted = BackgroundColor; diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs b/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs index e371f7c4b..872700a9b 100644 --- a/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs +++ b/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; +using System.Linq; using FlaxEngine; using FlaxEngine.GUI; @@ -233,11 +234,11 @@ namespace FlaxEditor.GUI.ContextMenu { var style = Style.Current; float width = 20; - if (style.FontMedium) + if (style.FontMedium.First()) { - width += style.FontMedium.MeasureText(Text).X; + width += style.FontMedium.First().MeasureText(Text).X; if (!string.IsNullOrEmpty(ShortKeys)) - width += 40 + style.FontMedium.MeasureText(ShortKeys).X; + width += 40 + style.FontMedium.First().MeasureText(ShortKeys).X; } return Mathf.Max(width, base.MinimumWidth); diff --git a/Source/Editor/GUI/CurveEditor.cs b/Source/Editor/GUI/CurveEditor.cs index 22deec120..f71409b20 100644 --- a/Source/Editor/GUI/CurveEditor.cs +++ b/Source/Editor/GUI/CurveEditor.cs @@ -437,7 +437,7 @@ namespace FlaxEditor.GUI _contentsColor = style.Background.RGBMultiplied(0.7f); _linesColor = style.ForegroundDisabled.RGBMultiplied(0.7f); _labelsColor = style.ForegroundDisabled; - _labelsFont = style.FontSmall; + _labelsFont = style.FontSmall.First(); _mainPanel = new Panel(ScrollBars.Both) { diff --git a/Source/Editor/GUI/Docking/DockWindow.cs b/Source/Editor/GUI/Docking/DockWindow.cs index dd4e39ce2..8601b9c53 100644 --- a/Source/Editor/GUI/Docking/DockWindow.cs +++ b/Source/Editor/GUI/Docking/DockWindow.cs @@ -6,6 +6,7 @@ using FlaxEngine; using FlaxEngine.Assertions; using FlaxEngine.GUI; using FlaxEditor.Options; +using System.Linq; namespace FlaxEditor.GUI.Docking { @@ -488,7 +489,7 @@ namespace FlaxEditor.GUI.Docking { var style = Style.Current; if (style?.FontMedium != null) - _titleSize = style.FontMedium.MeasureText(_title); + _titleSize = style.FontMedium.First().MeasureText(_title); } base.PerformLayoutBeforeChildren(); diff --git a/Source/Editor/GUI/ItemsListContextMenu.cs b/Source/Editor/GUI/ItemsListContextMenu.cs index 42d236991..ce69f3544 100644 --- a/Source/Editor/GUI/ItemsListContextMenu.cs +++ b/Source/Editor/GUI/ItemsListContextMenu.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Linq; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.GUI.Input; using FlaxEditor.Utilities; @@ -86,8 +87,8 @@ namespace FlaxEditor.GUI var font = style.FontSmall; for (int i = 0; i < ranges.Length; i++) { - var start = font.GetCharPosition(Name, ranges[i].StartIndex); - var end = font.GetCharPosition(Name, ranges[i].EndIndex); + var start = font.First().GetCharPosition(Name, ranges[i].StartIndex); + var end = font.First().GetCharPosition(Name, ranges[i].EndIndex); _highlights.Add(new Rectangle(start.X + 2, 0, end.X - start.X, Height)); } Visible = true; diff --git a/Source/Editor/GUI/MainMenuButton.cs b/Source/Editor/GUI/MainMenuButton.cs index 117922361..76687f8b9 100644 --- a/Source/Editor/GUI/MainMenuButton.cs +++ b/Source/Editor/GUI/MainMenuButton.cs @@ -2,6 +2,7 @@ using FlaxEngine; using FlaxEngine.GUI; +using System.Linq; namespace FlaxEditor.GUI { @@ -101,8 +102,8 @@ namespace FlaxEditor.GUI var style = Style.Current; float width = 18; - if (style.FontMedium) - width += style.FontMedium.MeasureText(Text).X; + if (style.FontMedium.First()) + width += style.FontMedium.First().MeasureText(Text).X; Width = width; } diff --git a/Source/Editor/GUI/NavigationButton.cs b/Source/Editor/GUI/NavigationButton.cs index 18e862304..77f8a7656 100644 --- a/Source/Editor/GUI/NavigationButton.cs +++ b/Source/Editor/GUI/NavigationButton.cs @@ -2,6 +2,7 @@ using FlaxEngine; using FlaxEngine.GUI; +using System.Linq; namespace FlaxEditor.GUI { @@ -65,9 +66,9 @@ namespace FlaxEditor.GUI { var style = Style.Current; - if (style.FontMedium) + if (style.FontMedium.First()) { - Width = style.FontMedium.MeasureText(Text).X + 2 * DefaultMargin; + Width = style.FontMedium.First().MeasureText(Text).X + 2 * DefaultMargin; } } } diff --git a/Source/Editor/GUI/Row.cs b/Source/Editor/GUI/Row.cs index f6bd5b02a..458138aea 100644 --- a/Source/Editor/GUI/Row.cs +++ b/Source/Editor/GUI/Row.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; +using System.Linq; using FlaxEngine; using FlaxEngine.GUI; @@ -38,8 +39,8 @@ namespace FlaxEditor.GUI { Depth = -1; - if (Height < Style.Current.FontMedium.Height) - Height = Style.Current.FontMedium.Height + 4; + if (Height < Style.Current.FontMedium.First().Height) + Height = Style.Current.FontMedium.First().Height + 4; } /// diff --git a/Source/Editor/GUI/Table.cs b/Source/Editor/GUI/Table.cs index 1d22ddb15..fed33a336 100644 --- a/Source/Editor/GUI/Table.cs +++ b/Source/Editor/GUI/Table.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; +using System.Linq; using System.Runtime.CompilerServices; using FlaxEngine; using FlaxEngine.GUI; @@ -129,7 +130,7 @@ namespace FlaxEditor.GUI Render2D.FillRectangle(rect, column.TitleBackgroundColor); var style = Style.Current; - var font = column.TitleFont ?? style.FontMedium; + var font = column.TitleFont ?? style.FontMedium.First(); Render2D.DrawText(font, column.Title, rect, column.TitleColor, TextAlignment.Center, TextAlignment.Center); if (columnIndex < _columns.Length - 1) diff --git a/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs b/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs index 63787df2c..928129917 100644 --- a/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs @@ -345,7 +345,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks if (_previewValue != null) { // Based on Track.Draw for track text placement - var left = _xOffset + 16 + Style.Current.FontSmall.MeasureText(Title ?? Name).X; + var left = _xOffset + 16 + Style.Current.FontSmall.First().MeasureText(Title ?? Name).X; if (Icon.IsValid) left += 18; if (IsExpanded) diff --git a/Source/Editor/GUI/ToolStripButton.cs b/Source/Editor/GUI/ToolStripButton.cs index e839a7356..b74c7c19f 100644 --- a/Source/Editor/GUI/ToolStripButton.cs +++ b/Source/Editor/GUI/ToolStripButton.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; +using System.Linq; using FlaxEngine; using FlaxEngine.GUI; @@ -150,8 +151,8 @@ namespace FlaxEditor.GUI if (hasSprite) width += iconSize; - if (!string.IsNullOrEmpty(_text) && style.FontMedium) - width += style.FontMedium.MeasureText(_text).X + (hasSprite ? DefaultMargin : 0); + if (!string.IsNullOrEmpty(_text) && style.FontMedium.First()) + width += style.FontMedium.First().MeasureText(_text).X + (hasSprite ? DefaultMargin : 0); Width = width; } diff --git a/Source/Editor/GUI/Tree/TreeNode.cs b/Source/Editor/GUI/Tree/TreeNode.cs index 703079469..f26929993 100644 --- a/Source/Editor/GUI/Tree/TreeNode.cs +++ b/Source/Editor/GUI/Tree/TreeNode.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; +using System.Linq; using FlaxEngine; using FlaxEngine.GUI; @@ -317,7 +318,7 @@ namespace FlaxEditor.GUI.Tree BackgroundColorSelected = style.BackgroundSelected; BackgroundColorHighlighted = style.BackgroundHighlighted; BackgroundColorSelectedUnfocused = style.LightBackground; - TextFont = new FontReference(style.FontSmall); + TextFont = new FontReference(style.FontSmall.First()); } /// diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index 95a273f19..c96a52fb1 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -237,12 +237,18 @@ namespace FlaxEditor.Options public int NumberOfGameClientsToLaunch = 1; private static FontAsset DefaultFont => FlaxEngine.Content.LoadAsyncInternal(EditorAssets.PrimaryFont); + private static FontAsset _cjkFont => FlaxEngine.Content.LoadAsyncInternal(EditorAssets.CJKFont); private FontReference _titleFont = new FontReference(DefaultFont, 18); private FontReference _largeFont = new FontReference(DefaultFont, 14); private FontReference _mediumFont = new FontReference(DefaultFont, 9); private FontReference _smallFont = new FontReference(DefaultFont, 9); private FontReference _outputLogFont = new FontReference(FlaxEngine.Content.LoadAsyncInternal(EditorAssets.InconsolataRegularFont), 10); + public FontReference CJKFont + { + get => new FontReference(_cjkFont, 9); + } + /// /// Gets or sets the title font for editor UI. /// diff --git a/Source/Editor/Options/OptionsModule.cs b/Source/Editor/Options/OptionsModule.cs index 1137e4c37..15059bd56 100644 --- a/Source/Editor/Options/OptionsModule.cs +++ b/Source/Editor/Options/OptionsModule.cs @@ -261,8 +261,9 @@ namespace FlaxEditor.Options // Fonts FontTitle = options.Interface.TitleFont.GetFont(), FontLarge = options.Interface.LargeFont.GetFont(), - FontMedium = options.Interface.MediumFont.GetFont(), - FontSmall = options.Interface.SmallFont.GetFont(), + FontMedium = new Font[] { options.Interface.MediumFont.GetFont(), options.Interface.CJKFont.GetFont() }, + FontSmall = new Font[] { options.Interface.SmallFont.GetFont(), options.Interface.CJKFont.GetFont() }, + FontCJK = options.Interface.CJKFont.GetFont(), // Icons ArrowDown = Editor.Icons.ArrowDown12, @@ -314,8 +315,8 @@ namespace FlaxEditor.Options // Fonts FontTitle = options.Interface.TitleFont.GetFont(), FontLarge = options.Interface.LargeFont.GetFont(), - FontMedium = options.Interface.MediumFont.GetFont(), - FontSmall = options.Interface.SmallFont.GetFont(), + FontMedium = new Font[] { options.Interface.MediumFont.GetFont(), options.Interface.CJKFont.GetFont() }, + FontSmall = new Font[] { options.Interface.SmallFont.GetFont(), options.Interface.CJKFont.GetFont() }, // Icons ArrowDown = Editor.Icons.ArrowDown12, diff --git a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs index f64e46385..9a6fe4fd2 100644 --- a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs +++ b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs @@ -142,8 +142,8 @@ namespace FlaxEditor.SceneGraph.GUI var textRect = TextRect; for (int i = 0; i < ranges.Length; i++) { - var start = font.GetCharPosition(text, ranges[i].StartIndex); - var end = font.GetCharPosition(text, ranges[i].EndIndex); + var start = font.First().GetCharPosition(text, ranges[i].StartIndex); + var end = font.First().GetCharPosition(text, ranges[i].EndIndex); _highlights.Add(new Rectangle(start.X + textRect.X, textRect.Y, end.X - start.X, textRect.Height)); } isThisVisible = true; diff --git a/Source/Editor/Surface/Archetypes/BehaviorTree.cs b/Source/Editor/Surface/Archetypes/BehaviorTree.cs index f8cd7bb5a..38dd28e62 100644 --- a/Source/Editor/Surface/Archetypes/BehaviorTree.cs +++ b/Source/Editor/Surface/Archetypes/BehaviorTree.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Linq; using FlaxEditor.CustomEditors.Dedicated; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.GUI.Drag; @@ -100,7 +101,7 @@ namespace FlaxEditor.Surface.Archetypes _debugRelevant = Behavior.GetNodeDebugRelevancy(instance, behavior); _debugInfo = Behavior.GetNodeDebugInfo(instance, behavior); if (!string.IsNullOrEmpty(_debugInfo)) - _debugInfoSize = Style.Current.FontSmall.MeasureText(_debugInfo); + _debugInfoSize = Style.Current.FontSmall.First().MeasureText(_debugInfo); } } diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs index 207875a92..812bec5d2 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs @@ -200,8 +200,8 @@ namespace FlaxEditor.Surface.ContextMenu var font = style.FontSmall; for (int i = 0; i < ranges.Length; i++) { - var start = font.GetCharPosition(_archetype.Title, ranges[i].StartIndex); - var end = font.GetCharPosition(_archetype.Title, ranges[i].EndIndex); + var start = font.First().GetCharPosition(_archetype.Title, ranges[i].StartIndex); + var end = font.First().GetCharPosition(_archetype.Title, ranges[i].EndIndex); _highlights.Add(new Rectangle(start.X + textRect.X, 0, end.X - start.X, Height)); if (ranges[i].StartIndex <= 0) @@ -222,8 +222,8 @@ namespace FlaxEditor.Surface.ContextMenu _highlights.Clear(); var style = Style.Current; var font = style.FontSmall; - var start = font.GetCharPosition(_archetype.Title, 0); - var end = font.GetCharPosition(_archetype.Title, _archetype.Title.Length - 1); + var start = font.First().GetCharPosition(_archetype.Title, 0); + var end = font.First().GetCharPosition(_archetype.Title, _archetype.Title.Length - 1); _highlights.Add(new Rectangle(start.X + textRect.X, 0, end.X - start.X, Height)); _isFullMatch = true; Visible = true; @@ -237,8 +237,8 @@ namespace FlaxEditor.Surface.ContextMenu _highlights.Clear(); var style = Style.Current; var font = style.FontSmall; - var start = font.GetCharPosition(_archetype.Title, 0); - var end = font.GetCharPosition(_archetype.Title, _archetype.Title.Length - 1); + var start = font.First().GetCharPosition(_archetype.Title, 0); + var end = font.First().GetCharPosition(_archetype.Title, _archetype.Title.Length - 1); _highlights.Add(new Rectangle(start.X + textRect.X, 0, end.X - start.X, Height)); Visible = true; @@ -286,7 +286,7 @@ namespace FlaxEditor.Surface.ContextMenu Render2D.DrawText(style.FontSmall, _archetype.Title, textRect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); if (_archetype.SubTitle != null) { - var titleLength = style.FontSmall.MeasureText(_archetype.Title).X; + var titleLength = style.FontSmall.First().MeasureText(_archetype.Title).X; var subTitleRect = new Rectangle(textRect.X + titleLength, textRect.Y, textRect.Width - titleLength, textRect.Height); Render2D.DrawText(style.FontSmall, _archetype.SubTitle, subTitleRect, style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); } diff --git a/Source/Editor/Surface/Elements/InputBox.cs b/Source/Editor/Surface/Elements/InputBox.cs index 2047bcd1a..611160611 100644 --- a/Source/Editor/Surface/Elements/InputBox.cs +++ b/Source/Editor/Surface/Elements/InputBox.cs @@ -1428,7 +1428,7 @@ namespace FlaxEditor.Surface.Elements if (_defaultValueEditor != null) { - _defaultValueEditor.Location = new Float2(X + Width + 8 + Style.Current.FontSmall.MeasureText(Text).X, Y); + _defaultValueEditor.Location = new Float2(X + Width + 8 + Style.Current.FontSmall.First().MeasureText(Text).X, Y); } } @@ -1635,7 +1635,7 @@ namespace FlaxEditor.Surface.Elements { if (DefaultValueEditors[i].CanUse(this, ref _currentType)) { - var bounds = new Rectangle(X + Width + 8 + Style.Current.FontSmall.MeasureText(Text).X, Y, 90, Height); + var bounds = new Rectangle(X + Width + 8 + Style.Current.FontSmall.First().MeasureText(Text).X, Y, 90, Height); _editor = DefaultValueEditors[i]; try { diff --git a/Source/Editor/Surface/SurfaceNode.cs b/Source/Editor/Surface/SurfaceNode.cs index 780ef81f0..82a9ab2bd 100644 --- a/Source/Editor/Surface/SurfaceNode.cs +++ b/Source/Editor/Surface/SurfaceNode.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Linq; using FlaxEditor.Scripting; using FlaxEditor.Surface.Elements; using FlaxEditor.Surface.Undo; @@ -199,7 +200,7 @@ namespace FlaxEditor.Surface continue; if (child is InputBox inputBox) { - var boxWidth = boxLabelFont.MeasureText(inputBox.Text).X + 20; + var boxWidth = boxLabelFont.First().MeasureText(inputBox.Text).X + 20; if (inputBox.DefaultValueEditor != null) boxWidth += inputBox.DefaultValueEditor.Width + 4; leftWidth = Mathf.Max(leftWidth, boxWidth); @@ -207,7 +208,7 @@ namespace FlaxEditor.Surface } else if (child is OutputBox outputBox) { - rightWidth = Mathf.Max(rightWidth, boxLabelFont.MeasureText(outputBox.Text).X + 20); + rightWidth = Mathf.Max(rightWidth, boxLabelFont.First().MeasureText(outputBox.Text).X + 20); rightHeight = Mathf.Max(rightHeight, outputBox.Archetype.Position.Y - Constants.NodeMarginY - Constants.NodeHeaderSize + 20.0f); } else if (child is Control control) diff --git a/Source/Editor/Tools/Foliage/FoliageTab.cs b/Source/Editor/Tools/Foliage/FoliageTab.cs index 1b46a42be..e88972b5c 100644 --- a/Source/Editor/Tools/Foliage/FoliageTab.cs +++ b/Source/Editor/Tools/Foliage/FoliageTab.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Linq; using FlaxEditor.GUI.Tabs; using FlaxEditor.Modules; using FlaxEditor.SceneGraph.Actors; @@ -147,7 +148,7 @@ namespace FlaxEditor.Tools.Foliage Parent = _noFoliagePanel, Enabled = false }; - var textSize = Style.Current.FontMedium.MeasureText(buttonText); + var textSize = Style.Current.FontMedium.First().MeasureText(buttonText); if (_createNewFoliage.Width < textSize.X) { _createNewFoliage.LocalX -= (textSize.X - _createNewFoliage.Width) / 2; diff --git a/Source/Editor/Tools/Terrain/CarveTab.cs b/Source/Editor/Tools/Terrain/CarveTab.cs index d51915acf..0a43cd2d2 100644 --- a/Source/Editor/Tools/Terrain/CarveTab.cs +++ b/Source/Editor/Tools/Terrain/CarveTab.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; +using System.Linq; using FlaxEditor.GUI.Tabs; using FlaxEditor.Modules; using FlaxEditor.SceneGraph.Actors; @@ -105,7 +106,7 @@ namespace FlaxEditor.Tools.Terrain Parent = _noTerrainPanel, Enabled = false }; - var textSize = Style.Current.FontMedium.MeasureText(buttonText); + var textSize = Style.Current.FontMedium.First().MeasureText(buttonText); if (_createTerrainButton.Width < textSize.X) { _createTerrainButton.LocalX -= (textSize.X - _createTerrainButton.Width) / 2; diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index c49392d01..f4ebfb51b 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -548,9 +548,9 @@ namespace FlaxEditor.Viewport #region Camera settings widget var largestText = "Relative Panning"; - var textSize = Style.Current.FontMedium.MeasureText(largestText); + var textSize = Style.Current.FontMedium.First().MeasureText(largestText); var xLocationForExtras = textSize.X + 5; - var cameraSpeedTextWidth = Style.Current.FontMedium.MeasureText("0.00").X; + var cameraSpeedTextWidth = Style.Current.FontMedium.First().MeasureText("0.00").X; // Camera Settings Widget _cameraWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); @@ -801,7 +801,7 @@ namespace FlaxEditor.Viewport #region View mode widget largestText = "Brightness"; - textSize = Style.Current.FontMedium.MeasureText(largestText); + textSize = Style.Current.FontMedium.First().MeasureText(largestText); xLocationForExtras = textSize.X + 5; var viewMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperLeft); diff --git a/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs b/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs index 77e94ae53..a58350ded 100644 --- a/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs +++ b/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; +using System.Linq; using FlaxEditor.GUI.ContextMenu; using FlaxEngine; using FlaxEngine.GUI; @@ -162,8 +163,8 @@ namespace FlaxEditor.Viewport.Widgets { var style = Style.Current; - if (style != null && style.FontMedium) - Width = CalculateButtonWidth(_forcedTextWidth > 0.0f ? _forcedTextWidth : style.FontMedium.MeasureText(_text).X, Icon.IsValid); + if (style != null && style.FontMedium.First()) + Width = CalculateButtonWidth(_forcedTextWidth > 0.0f ? _forcedTextWidth : style.FontMedium.First().MeasureText(_text).X, Icon.IsValid); } } } diff --git a/Source/Editor/Windows/AboutDialog.cs b/Source/Editor/Windows/AboutDialog.cs index b059dadcb..63ad44f29 100644 --- a/Source/Editor/Windows/AboutDialog.cs +++ b/Source/Editor/Windows/AboutDialog.cs @@ -2,6 +2,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System.Collections.Generic; +using System.Linq; using FlaxEditor.GUI.Dialogs; using FlaxEngine; using FlaxEngine.GUI; @@ -53,7 +54,7 @@ namespace FlaxEditor.Windows Parent = this }; var buttonText = "Copy version info"; - var fontSize = Style.Current.FontMedium.MeasureText(buttonText); + var fontSize = Style.Current.FontMedium.First().MeasureText(buttonText); var copyVersionButton = new Button(Width - fontSize.X - 8, 6, fontSize.X + 4, 20) { Text = buttonText, diff --git a/Source/Editor/Windows/Profiler/Timeline.cs b/Source/Editor/Windows/Profiler/Timeline.cs index 59a7a0e26..88019b329 100644 --- a/Source/Editor/Windows/Profiler/Timeline.cs +++ b/Source/Editor/Windows/Profiler/Timeline.cs @@ -2,6 +2,7 @@ using FlaxEngine; using FlaxEngine.GUI; +using System.Linq; namespace FlaxEditor.Windows.Profiler { @@ -84,8 +85,8 @@ namespace FlaxEditor.Windows.Profiler Render2D.FillRectangle(bounds, color); Render2D.DrawRectangle(bounds, color * 0.5f); - if (_nameLength < 0 && style.FontMedium) - _nameLength = style.FontMedium.MeasureText(_name).X; + if (_nameLength < 0 && style.FontMedium.First()) + _nameLength = style.FontMedium.First().MeasureText(_name).X; if (_nameLength < bounds.Width + 4) { diff --git a/Source/Editor/Windows/ToolboxWindow.cs b/Source/Editor/Windows/ToolboxWindow.cs index e2b04669b..e8fc1d56c 100644 --- a/Source/Editor/Windows/ToolboxWindow.cs +++ b/Source/Editor/Windows/ToolboxWindow.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Linq; using FlaxEditor.GUI.Input; using FlaxEditor.GUI.Tabs; using FlaxEditor.GUI.Tree; @@ -272,8 +273,8 @@ namespace FlaxEditor.Windows var textRect = item.TextRect; for (int i = 0; i < ranges.Length; i++) { - var start = font.GetCharPosition(text, ranges[i].StartIndex); - var end = font.GetCharPosition(text, ranges[i].EndIndex); + var start = font.First().GetCharPosition(text, ranges[i].StartIndex); + var end = font.First().GetCharPosition(text, ranges[i].EndIndex); highlights.Add(new Rectangle(start.X + textRect.X, textRect.Y, end.X - start.X, textRect.Height)); } item.SetHighlights(highlights); diff --git a/Source/Engine/Render2D/Font.cpp b/Source/Engine/Render2D/Font.cpp index 90423f5ce..13b17197c 100644 --- a/Source/Engine/Render2D/Font.cpp +++ b/Source/Engine/Render2D/Font.cpp @@ -283,6 +283,206 @@ void Font::ProcessText(const StringView& text, Array& outputLines } } +void Font::ProcessText(const Array& fonts, const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout) +{ + float cursorX = 0; + int32 kerning; + FontLineCache tmpLine; + FontCharacterEntry entry; + FontCharacterEntry previous; + int32 textLength = text.Length(); + float scale = layout.Scale / FontManager::FontScale; + float boundsWidth = layout.Bounds.GetWidth(); + float baseLinesDistanceScale = layout.BaseLinesGapScale * scale; + tmpLine.Location = Float2::Zero; + tmpLine.Size = Float2::Zero; + tmpLine.FirstCharIndex = 0; + tmpLine.LastCharIndex = -1; + + int32 lastWrapCharIndex = INVALID_INDEX; + float lastWrapCharX = 0; + bool lastMoveLine = false; + + int32 previousFontIndex = -1; + // The maximum font height of the current line + float maxHeight = 0; + // Process each character to split text into single lines + for (int32 currentIndex = 0; currentIndex < textLength;) + { + bool moveLine = false; + float xAdvance = 0; + int32 nextCharIndex = currentIndex + 1; + + // Cache current character + const Char currentChar = text[currentIndex]; + const bool isWhitespace = StringUtils::IsWhitespace(currentChar); + + // Check if character can wrap words + const bool isWrapChar = !StringUtils::IsAlnum(currentChar) || isWhitespace || StringUtils::IsUpper(currentChar); + if (isWrapChar && currentIndex != 0) + { + lastWrapCharIndex = currentIndex; + lastWrapCharX = cursorX; + } + + // Check if it's a newline character + if (currentChar == '\n') + { + // Break line + moveLine = true; + currentIndex++; + tmpLine.LastCharIndex++; + } + else + { + // Get character entry + int32 fontIndex = 0; + while (fontIndex < fonts.Count() && !fonts[fontIndex]->ContainsChar(currentChar)) + { + fontIndex++; + } + + // If no font can match the char, then use the first font + if (fontIndex == fonts.Count()) { + fontIndex = 0; + } + // Get character entry + fonts[fontIndex]->GetCharacter(currentChar, entry); + maxHeight = Math::Max(maxHeight, static_cast(fonts[fontIndex]->GetHeight())); + + // Get kerning, only when the font hasn't changed + if (!isWhitespace && previous.IsValid && previousFontIndex == fontIndex) + { + kerning = fonts[fontIndex]->GetKerning(previous.Character, entry.Character); + } + else + { + kerning = 0; + } + previous = entry; + previousFontIndex = fontIndex; + xAdvance = (kerning + entry.AdvanceX) * scale; + + // Check if character fits the line or skip wrapping + if (cursorX + xAdvance <= boundsWidth || layout.TextWrapping == TextWrapping::NoWrap) + { + // Move character + cursorX += xAdvance; + tmpLine.LastCharIndex++; + } + else if (layout.TextWrapping == TextWrapping::WrapWords) + { + if (lastWrapCharIndex != INVALID_INDEX) + { + // Skip moving twice for the same character + int32 lastLineLasCharIndex = outputLines.HasItems() ? outputLines.Last().LastCharIndex : -10000; + if (lastLineLasCharIndex == lastWrapCharIndex || lastLineLasCharIndex == lastWrapCharIndex - 1 || lastLineLasCharIndex == lastWrapCharIndex - 2) + { + currentIndex = nextCharIndex; + lastMoveLine = moveLine; + continue; + } + + // Move line + const Char wrapChar = text[lastWrapCharIndex]; + moveLine = true; + cursorX = lastWrapCharX; + if (StringUtils::IsWhitespace(wrapChar)) + { + // Skip whitespaces + tmpLine.LastCharIndex = lastWrapCharIndex - 1; + nextCharIndex = currentIndex = lastWrapCharIndex + 1; + } + else + { + tmpLine.LastCharIndex = lastWrapCharIndex - 1; + nextCharIndex = currentIndex = lastWrapCharIndex; + } + } + } + else if (layout.TextWrapping == TextWrapping::WrapChars) + { + // Move line + moveLine = true; + nextCharIndex = currentIndex; + + // Skip moving twice for the same character + if (lastMoveLine) + break; + } + } + + // Check if move to another line + if (moveLine) + { + // Add line + tmpLine.Size.X = cursorX; + tmpLine.Size.Y = baseLinesDistanceScale * maxHeight; + tmpLine.LastCharIndex = Math::Max(tmpLine.LastCharIndex, tmpLine.FirstCharIndex); + outputLines.Add(tmpLine); + + // Reset line + tmpLine.Location.Y += baseLinesDistanceScale * maxHeight; + tmpLine.FirstCharIndex = currentIndex; + tmpLine.LastCharIndex = currentIndex - 1; + cursorX = 0; + lastWrapCharIndex = INVALID_INDEX; + lastWrapCharX = 0; + previous.IsValid = false; + + // Reset max font height + maxHeight = 0; + } + + currentIndex = nextCharIndex; + lastMoveLine = moveLine; + } + + if (textLength != 0 && (tmpLine.LastCharIndex >= tmpLine.FirstCharIndex || text[textLength - 1] == '\n')) + { + // Add line + tmpLine.Size.X = cursorX; + tmpLine.Size.Y = baseLinesDistanceScale * maxHeight; + tmpLine.LastCharIndex = textLength - 1; + outputLines.Add(tmpLine); + + tmpLine.Location.Y += baseLinesDistanceScale * maxHeight; + } + + // Check amount of lines + if (outputLines.IsEmpty()) + return; + + float totalHeight = tmpLine.Location.Y; + + Float2 offset = Float2::Zero; + if (layout.VerticalAlignment == TextAlignment::Center) + { + offset.Y += (layout.Bounds.GetHeight() - totalHeight) * 0.5f; + } + else if (layout.VerticalAlignment == TextAlignment::Far) + { + offset.Y += layout.Bounds.GetHeight() - totalHeight; + } + for (int32 i = 0; i < outputLines.Count(); i++) + { + FontLineCache& line = outputLines[i]; + Float2 rootPos = line.Location + offset; + + // Fix upper left line corner to match desire text alignment + if (layout.HorizontalAlignment == TextAlignment::Center) + { + rootPos.X += (layout.Bounds.GetWidth() - line.Size.X) * 0.5f; + } + else if (layout.HorizontalAlignment == TextAlignment::Far) + { + rootPos.X += layout.Bounds.GetWidth() - line.Size.X; + } + + line.Location = rootPos; + } +} + Float2 Font::MeasureText(const StringView& text, const TextLayoutOptions& layout) { // Check if there is no need to do anything @@ -432,6 +632,11 @@ Float2 Font::GetCharPosition(const StringView& text, int32 index, const TextLayo return rootOffset + Float2(lines.Last().Size.X, static_cast((lines.Count() - 1) * baseLinesDistance)); } +bool Font::ContainsChar(Char c) +{ + return FT_Get_Char_Index(GetAsset()->GetFTFace(), c) > 0; +} + void Font::FlushFaceSize() const { // Set the character size diff --git a/Source/Engine/Render2D/Font.h b/Source/Engine/Render2D/Font.h index 90f723cd8..d8ac1b708 100644 --- a/Source/Engine/Render2D/Font.h +++ b/Source/Engine/Render2D/Font.h @@ -339,6 +339,15 @@ public: /// The output lines list. void ProcessText(const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout); + /// + /// Processes text to get cached lines for rendering. + /// + /// The font list. + /// The input text. + /// The layout properties. + /// The output lines list. + static void ProcessText(const Array& fonts, const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout); + /// /// Processes text to get cached lines for rendering. /// @@ -395,6 +404,15 @@ public: /// The minimum size for that text and fot to render properly. API_FUNCTION() Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout); + /// + /// Measures minimum size of the rectangle that will be needed to draw given text. + /// + /// The fonts to render with. + /// The input text to test. + /// The layout properties. + /// The minimum size for that text and fot to render properly. + API_FUNCTION() static Float2 MeasureText(const Array& fonts, const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout); + /// /// Measures minimum size of the rectangle that will be needed to draw given text. /// @@ -482,6 +500,16 @@ public: /// The character position (upper left corner which can be used for a caret position). API_FUNCTION() Float2 GetCharPosition(const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout); + /// + /// Calculates character position for given text and character index. + /// + /// The fonts to use. + /// The input text to test. + /// The text position to get coordinates of. + /// The text layout properties. + /// The character position (upper left corner which can be used for a caret position). + API_FUNCTION() static Float2 GetCharPosition(const Array& fonts, const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout); + /// /// Calculates character position for given text and character index. /// @@ -518,6 +546,13 @@ public: return GetCharPosition(textRange.Substring(text), index, TextLayoutOptions()); } + /// + /// Check if the font contains the glyph of a char + /// + /// The char to test. + /// True if the font contains the glyph of the char, otherwise false. + API_FUNCTION() FORCE_INLINE bool ContainsChar(Char c); + /// /// Flushes the size of the face with the Free Type library backend. /// diff --git a/Source/Engine/Render2D/FontManager.cpp b/Source/Engine/Render2D/FontManager.cpp index bb7edf974..5b08c1d87 100644 --- a/Source/Engine/Render2D/FontManager.cpp +++ b/Source/Engine/Render2D/FontManager.cpp @@ -155,6 +155,9 @@ bool FontManager::AddNewEntry(Font* font, Char c, FontCharacterEntry& entry) // Get the index to the glyph in the font face const FT_UInt glyphIndex = FT_Get_Char_Index(face, c); + if (glyphIndex == 0) { + LOG(Warning, "Font `{}` doesn't contain character `\\u{:x}`, consider choosing another font. ", String(face->family_name), c); + } // Load the glyph const FT_Error error = FT_Load_Glyph(face, glyphIndex, glyphFlags); diff --git a/Source/Engine/Render2D/Render2D.cpp b/Source/Engine/Render2D/Render2D.cpp index b56c3e1a2..eedfbeffe 100644 --- a/Source/Engine/Render2D/Render2D.cpp +++ b/Source/Engine/Render2D/Render2D.cpp @@ -27,11 +27,11 @@ #if USE_EDITOR #define RENDER2D_CHECK_RENDERING_STATE \ - if (!Render2D::IsRendering()) \ - { \ - LOG(Error, "Calling Render2D is only valid during rendering."); \ - return; \ - } + if (!Render2D::IsRendering()) \ + { \ + LOG(Error, "Calling Render2D is only valid during rendering."); \ + return; \ + } #else #define RENDER2D_CHECK_RENDERING_STATE #endif @@ -54,7 +54,7 @@ const bool DownsampleForBlur = false; PACK_STRUCT(struct Data { Matrix ViewProjection; - }); +}); PACK_STRUCT(struct BlurData { Float2 InvBufferSize; @@ -62,7 +62,7 @@ PACK_STRUCT(struct BlurData { float Dummy0; Float4 Bounds; Float4 WeightAndOffsets[RENDER2D_BLUR_MAX_SAMPLES / 2]; - }); +}); enum class DrawCallType : byte { @@ -1174,7 +1174,7 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color, drawCall.AsChar.Mat = nullptr; } Float2 pointer = location; - for (int32 currentIndex = 0; currentIndex <= text.Length(); currentIndex++) + for (int32 currentIndex = 0; currentIndex < text.Length(); currentIndex++) { // Cache current character const Char currentChar = text[currentIndex]; @@ -1368,6 +1368,318 @@ void Render2D::DrawText(Font* font, const StringView& text, const TextRange& tex DrawText(font, textRange.Substring(text), color, layout, customMaterial); } +void Render2D::DrawText(const Array& fonts, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial) +{ + RENDER2D_CHECK_RENDERING_STATE; + + // Check if there is no need to do anything + if (fonts.IsEmpty() || text.Length() < 0) + return; + + // Temporary data + uint32 fontAtlasIndex = 0; + FontTextureAtlas* fontAtlas = nullptr; + Float2 invAtlasSize = Float2::One; + FontCharacterEntry previous; + int32 kerning; + float scale = 1.0f / FontManager::FontScale; + + // Render all characters + FontCharacterEntry entry; + Render2DDrawCall drawCall; + if (customMaterial) + { + drawCall.Type = DrawCallType::DrawCharMaterial; + drawCall.AsChar.Mat = customMaterial; + } + else + { + drawCall.Type = DrawCallType::DrawChar; + drawCall.AsChar.Mat = nullptr; + } + + // The following code cut the text into segments, according to the font used to render + Float2 pointer = location; + // The starting index of the current segment + int32 startIndex = 0; + // The index of the font used by the current segment + int32 segmentFontIndex = 0; + // The maximum font height of the current line + float maxHeight = 0; + for (int32 currentIndex = 0; currentIndex <= text.Length(); currentIndex++) + { + // Cache current character + const Char currentChar = currentIndex < text.Length() ? text[currentIndex] : 0; + + // Check if it isn't a newline character + if (currentChar != '\n') + { + int32 fontIndex = 0; + if (currentIndex < text.Length()) { + while (fontIndex < fonts.Count() && !fonts[fontIndex]->ContainsChar(currentChar)) + { + fontIndex++; + } + + // If no font can match the char, then use the segment font + if (fontIndex == fonts.Count()) { + fontIndex = segmentFontIndex; + } + + // Do nothing if the char still belongs to the current segment + if (fontIndex == segmentFontIndex) { + continue; + } + } + + // Render the pending segment before beginning the new segment + renderText:auto fontHeight = fonts[segmentFontIndex]->GetHeight(); + maxHeight = Math::Max(maxHeight, static_cast(fontHeight)); + auto fontDescender = fonts[segmentFontIndex]->GetDescender(); + for (int32 renderIndex = startIndex; renderIndex < currentIndex; renderIndex++) + { + // Get character entry + fonts[segmentFontIndex]->GetCharacter(text[renderIndex], entry); + + // Check if need to select/change font atlas (since characters even in the same font may be located in different atlases) + if (fontAtlas == nullptr || entry.TextureIndex != fontAtlasIndex) + { + // Get texture atlas that contains current character + fontAtlasIndex = entry.TextureIndex; + fontAtlas = FontManager::GetAtlas(fontAtlasIndex); + if (fontAtlas) + { + fontAtlas->EnsureTextureCreated(); + drawCall.AsChar.Tex = fontAtlas->GetTexture(); + invAtlasSize = 1.0f / fontAtlas->GetSize(); + } + else + { + drawCall.AsChar.Tex = nullptr; + invAtlasSize = 1.0f; + } + } + + // Check if character is a whitespace + const bool isWhitespace = StringUtils::IsWhitespace(text[renderIndex]); + + // Get kerning + if (!isWhitespace && previous.IsValid) + { + kerning = fonts[segmentFontIndex]->GetKerning(previous.Character, entry.Character); + } + else + { + kerning = 0; + } + pointer.X += kerning * scale; + previous = entry; + + // Omit whitespace characters + if (!isWhitespace) + { + // Calculate character size and atlas coordinates + const float x = pointer.X + entry.OffsetX * scale; + const float y = pointer.Y + (fontHeight + fontDescender - entry.OffsetY) * scale; + + Rectangle charRect(x, y, entry.UVSize.X * scale, entry.UVSize.Y * scale); + + Float2 upperLeftUV = entry.UV * invAtlasSize; + Float2 rightBottomUV = (entry.UV + entry.UVSize) * invAtlasSize; + + // Add draw call + drawCall.StartIB = IBIndex; + drawCall.CountIB = 6; + DrawCalls.Add(drawCall); + WriteRect(charRect, color, upperLeftUV, rightBottomUV); + } + + // Move + pointer.X += entry.AdvanceX * scale; + } + + // Start new segment + startIndex = currentIndex; + segmentFontIndex = fontIndex; + + if (currentIndex == text.Length() - 1) { + currentIndex++; + goto renderText; + } + } + else + { + // Move + pointer.X = location.X; + pointer.Y += maxHeight * scale; + // Clear max height + maxHeight = 0; + } + } +} + +void Render2D::DrawText(const Array& fonts, const StringView& text, const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial) +{ + DrawText(fonts, textRange.Substring(text), color, location, customMaterial); +} + +void Render2D::DrawText(const Array& fonts, const StringView& text, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) +{ + RENDER2D_CHECK_RENDERING_STATE; + + // Check if there is no need to do anything + if (fonts.IsEmpty() || text.IsEmpty() || layout.Scale <= ZeroTolerance) + return; + + // Temporary data + uint32 fontAtlasIndex = 0; + FontTextureAtlas* fontAtlas = nullptr; + Float2 invAtlasSize = Float2::One; + FontCharacterEntry previous; + int32 kerning; + float scale = layout.Scale / FontManager::FontScale; + + // Process text to get lines + Lines.Clear(); + Font::ProcessText(fonts, text, Lines, layout); + + // Render all lines + FontCharacterEntry entry; + Render2DDrawCall drawCall; + if (customMaterial) + { + drawCall.Type = DrawCallType::DrawCharMaterial; + drawCall.AsChar.Mat = customMaterial; + } + else + { + drawCall.Type = DrawCallType::DrawChar; + drawCall.AsChar.Mat = nullptr; + } + + for (int32 lineIndex = 0; lineIndex < Lines.Count(); lineIndex++) + { + const FontLineCache& line = Lines[lineIndex]; + + // The following code cut the text into segments, according to the font used to render + Float2 pointer = line.Location; + // The starting index of the current segment + int32 startIndex = line.FirstCharIndex; + // The index of the font used by the current segment + int32 segmentFontIndex = 0; + // The maximum font height of the current line + float maxHeight = 0; + + // Partition and render all characters from the line + for (int32 charIndex = line.FirstCharIndex; charIndex <= line.LastCharIndex + 1; charIndex++) + { + const Char c = charIndex <= line.LastCharIndex ? text[charIndex] : 0; + + if (c != '\n') + { + int32 fontIndex = 0; + if (charIndex <= line.LastCharIndex) { + while (fontIndex < fonts.Count() && !fonts[fontIndex]->ContainsChar(c)) + { + fontIndex++; + } + + // If no font can match the char, then use the segment font + if (fontIndex == fonts.Count()) { + fontIndex = segmentFontIndex; + } + + + // Do nothing if the char still belongs to the current segment + if (fontIndex == segmentFontIndex) { + continue; + } + } + + + // Render the pending segment before beginning the new segment + auto fontHeight = fonts[segmentFontIndex]->GetHeight(); + maxHeight = Math::Max(maxHeight, static_cast(fontHeight)); + auto fontDescender = fonts[segmentFontIndex]->GetDescender(); + + const Char* pred = L"Type"; + if (text.Substring(0, Math::Min(4, text.Length())) == pred) { + // __debugbreak(); + } + for (int32 renderIndex = startIndex; renderIndex < charIndex; renderIndex++) + { + // Get character entry + fonts[segmentFontIndex]->GetCharacter(text[renderIndex], entry); + + // Check if need to select/change font atlas (since characters even in the same font may be located in different atlases) + if (fontAtlas == nullptr || entry.TextureIndex != fontAtlasIndex) + { + // Get texture atlas that contains current character + fontAtlasIndex = entry.TextureIndex; + fontAtlas = FontManager::GetAtlas(fontAtlasIndex); + if (fontAtlas) + { + fontAtlas->EnsureTextureCreated(); + invAtlasSize = 1.0f / fontAtlas->GetSize(); + drawCall.AsChar.Tex = fontAtlas->GetTexture(); + } + else + { + invAtlasSize = 1.0f; + drawCall.AsChar.Tex = nullptr; + } + } + + // Get kerning + const bool isWhitespace = StringUtils::IsWhitespace(text[renderIndex]); + if (!isWhitespace && previous.IsValid) + { + kerning = fonts[segmentFontIndex]->GetKerning(previous.Character, entry.Character); + } + else + { + kerning = 0; + } + pointer.X += (float)kerning * scale; + previous = entry; + + // Omit whitespace characters + if (!isWhitespace) + { + // Calculate character size and atlas coordinates + const float x = pointer.X + entry.OffsetX * scale; + const float y = pointer.Y - entry.OffsetY * scale + Math::Ceil((fontHeight + fontDescender) * scale); + + Rectangle charRect(x, y, entry.UVSize.X * scale, entry.UVSize.Y * scale); + charRect.Offset(layout.Bounds.Location); + + Float2 upperLeftUV = entry.UV * invAtlasSize; + Float2 rightBottomUV = (entry.UV + entry.UVSize) * invAtlasSize; + + // Add draw call + drawCall.StartIB = IBIndex; + drawCall.CountIB = 6; + DrawCalls.Add(drawCall); + WriteRect(charRect, color, upperLeftUV, rightBottomUV); + } + + // Move + pointer.X += entry.AdvanceX * scale; + } + + // Start new segment + startIndex = charIndex; + segmentFontIndex = fontIndex; + } + } + } +} + +void Render2D::DrawText(const Array& fonts, const StringView& text, const TextRange& textRange, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) +{ + DrawText(fonts, textRange.Substring(text), color, layout, customMaterial); +} + FORCE_INLINE bool NeedAlphaWithTint(const Color& color) { return (color.A * TintLayersStack.Peek().A) < 1.0f; @@ -1931,7 +2243,7 @@ void Render2D::DrawBlur(const Rectangle& rect, float blurStrength) void Render2D::DrawTexturedTriangles(GPUTexture* t, const Span& vertices, const Span& uvs) { RENDER2D_CHECK_RENDERING_STATE; - CHECK(vertices.Length() == uvs.Length()) + CHECK(vertices.Length() == uvs.Length()); Render2DDrawCall& drawCall = DrawCalls.AddOne(); drawCall.Type = DrawCallType::FillTexture; @@ -1977,7 +2289,7 @@ void Render2D::DrawTexturedTriangles(GPUTexture* t, const Span& indices, drawCall.StartIB = IBIndex; drawCall.CountIB = indices.Length(); drawCall.AsTexture.Ptr = t; - + for (int32 i = 0; i < indices.Length();) { const uint16 i0 = indices.Get()[i++]; diff --git a/Source/Engine/Render2D/Render2D.cs b/Source/Engine/Render2D/Render2D.cs index c4d9e81b4..a73556f7b 100644 --- a/Source/Engine/Render2D/Render2D.cs +++ b/Source/Engine/Render2D/Render2D.cs @@ -152,6 +152,59 @@ namespace FlaxEngine DrawText(font, text, color, ref layout, customMaterial); } + /// + /// Draws a text. + /// + /// The fonts to use, ordered by priority. + /// The text to render. + /// The size and position of the area in which the text is drawn. + /// The text color. + /// The horizontal alignment of the text in a layout rectangle. + /// The vertical alignment of the text in a layout rectangle. + /// Describes how wrap text inside a layout rectangle. + /// The scale for distance one baseline from another. Default is 1. + /// The text drawing scale. Default is 1. + public static void DrawText(Font[] fonts, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) + { + var layout = new TextLayoutOptions + { + Bounds = layoutRect, + HorizontalAlignment = horizontalAlignment, + VerticalAlignment = verticalAlignment, + TextWrapping = textWrapping, + Scale = scale, + BaseLinesGapScale = baseLinesGapScale, + }; + DrawText(fonts, text, color, ref layout); + } + + /// + /// Draws a text using a custom material shader. Given material must have GUI domain and a public parameter named Font (texture parameter used for a font atlas sampling). + /// + /// The fonts to use, ordered by priority. + /// Custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. + /// The text to render. + /// The size and position of the area in which the text is drawn. + /// The text color. + /// The horizontal alignment of the text in a layout rectangle. + /// The vertical alignment of the text in a layout rectangle. + /// Describes how wrap text inside a layout rectangle. + /// The scale for distance one baseline from another. Default is 1. + /// The text drawing scale. Default is 1. + public static void DrawText(Font[] fonts, MaterialBase customMaterial, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) + { + var layout = new TextLayoutOptions + { + Bounds = layoutRect, + HorizontalAlignment = horizontalAlignment, + VerticalAlignment = verticalAlignment, + TextWrapping = textWrapping, + Scale = scale, + BaseLinesGapScale = baseLinesGapScale, + }; + DrawText(fonts, text, color, ref layout, customMaterial); + } + /// /// Calls drawing GUI to the texture. /// diff --git a/Source/Engine/Render2D/Render2D.h b/Source/Engine/Render2D/Render2D.h index 2b890ced9..0ec88edc0 100644 --- a/Source/Engine/Render2D/Render2D.h +++ b/Source/Engine/Render2D/Render2D.h @@ -33,7 +33,7 @@ API_CLASS(Static) class FLAXENGINE_API Render2D /// /// The rendering features and options flags. /// - API_ENUM(Attributes="Flags") enum class RenderingFeatures + API_ENUM(Attributes = "Flags") enum class RenderingFeatures { /// /// The none. @@ -215,6 +215,49 @@ public: /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. API_FUNCTION() static void DrawText(Font* font, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); + /// + /// Draws a text. + /// + /// The fonts to use, ordered by priority. + /// The text to render. + /// The input text range (substring range of the input text parameter). + /// The text color. + /// The text location. + /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. + API_FUNCTION() static void DrawText(const Array& fonts, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); + + /// + /// Draws a text with formatting. + /// + /// The fonts to use, ordered by priority. + /// The text to render. + /// The text color. + /// The text layout properties. + /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. + API_FUNCTION() static void DrawText(const Array& fonts, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); + + /// + /// Draws a text with formatting. + /// + /// The fonts to use, ordered by priority. + /// The text to render. + /// The input text range (substring range of the input text parameter). + /// The text color. + /// The text layout properties. + /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. + API_FUNCTION() static void DrawText(const Array& fonts, const StringView& text, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); + + /// + /// Draws a text with formatting. + /// + /// The fonts to use, ordered by priority. + /// The text to render. + /// The input text range (substring range of the input text parameter). + /// The text color. + /// The text layout properties. + /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. + API_FUNCTION() static void DrawText(const Array& fonts, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); + /// /// Fills a rectangle area. /// diff --git a/Source/Engine/Scripting/Scripting.cs b/Source/Engine/Scripting/Scripting.cs index 188333ff1..8e71c9f31 100644 --- a/Source/Engine/Scripting/Scripting.cs +++ b/Source/Engine/Scripting/Scripting.cs @@ -295,12 +295,13 @@ namespace FlaxEngine // Use optionally bundled default font (matches Editor) var defaultFont = Content.LoadAsyncInternal("Editor/Fonts/Roboto-Regular"); + var cjkFont = Content.LoadAsyncInternal("NotoSansSC-Medium"); if (defaultFont) { style.FontTitle = defaultFont.CreateFont(18); style.FontLarge = defaultFont.CreateFont(14); - style.FontMedium = defaultFont.CreateFont(9); - style.FontSmall = defaultFont.CreateFont(9); + style.FontMedium = new Font[] { defaultFont.CreateFont(9), cjkFont.CreateFont(9) }; + style.FontSmall = new Font[] { defaultFont.CreateFont(9), cjkFont.CreateFont(9) }; } Style.Current = style; diff --git a/Source/Engine/UI/GUI/Common/Button.cs b/Source/Engine/UI/GUI/Common/Button.cs index 02337411b..1b965b8d1 100644 --- a/Source/Engine/UI/GUI/Common/Button.cs +++ b/Source/Engine/UI/GUI/Common/Button.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; +using System.Linq; namespace FlaxEngine.GUI { @@ -155,7 +156,7 @@ namespace FlaxEngine.GUI var style = Style.Current; if (style != null) { - _font = new FontReference(style.FontMedium); + _font = new FontReference(style.FontMedium.First()); TextColor = style.Foreground; BackgroundColor = style.BackgroundNormal; BorderColor = style.BorderNormal; diff --git a/Source/Engine/UI/GUI/Common/Dropdown.cs b/Source/Engine/UI/GUI/Common/Dropdown.cs index ecca2978f..21ca9cbea 100644 --- a/Source/Engine/UI/GUI/Common/Dropdown.cs +++ b/Source/Engine/UI/GUI/Common/Dropdown.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Linq; namespace FlaxEngine.GUI { @@ -358,7 +359,7 @@ namespace FlaxEngine.GUI : base(0, 0, 120, 18.0f) { var style = Style.Current; - Font = new FontReference(style.FontMedium); + Font = new FontReference(style.FontMedium.First()); TextColor = style.Foreground; BackgroundColor = style.BackgroundNormal; BackgroundColorHighlighted = BackgroundColor; diff --git a/Source/Engine/UI/GUI/Common/Label.cs b/Source/Engine/UI/GUI/Common/Label.cs index 3c7c04fb2..b92e762e1 100644 --- a/Source/Engine/UI/GUI/Common/Label.cs +++ b/Source/Engine/UI/GUI/Common/Label.cs @@ -1,6 +1,8 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. +using FlaxEditor.Options; using System.ComponentModel; +using System.Linq; namespace FlaxEngine.GUI { @@ -190,7 +192,7 @@ namespace FlaxEngine.GUI { AutoFocus = false; var style = Style.Current; - Font = new FontReference(style.FontMedium); + Font = new FontReference(style.FontMedium.First()); TextColor = style.Foreground; TextColorHighlighted = style.Foreground; } @@ -201,7 +203,7 @@ namespace FlaxEngine.GUI { AutoFocus = false; var style = Style.Current; - Font = new FontReference(style.FontMedium); + Font = new FontReference(style.FontMedium.First()); TextColor = style.Foreground; TextColorHighlighted = style.Foreground; } @@ -233,7 +235,7 @@ namespace FlaxEngine.GUI } } - Render2D.DrawText(_font.GetFont(), Material, _text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale); + Render2D.DrawText(new Font[] { _font.GetFont(), Style.Current.FontCJK }, Material, _text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale); if (ClipText) Render2D.PopClip(); diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.cs b/Source/Engine/UI/GUI/Common/RichTextBox.cs index ab922d93d..e6a82c986 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBox.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBox.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System.Collections.Generic; +using System.Linq; namespace FlaxEngine.GUI { @@ -45,7 +46,7 @@ namespace FlaxEngine.GUI var style = Style.Current; _textStyle = new TextBlockStyle { - Font = new FontReference(style.FontMedium), + Font = new FontReference(style.FontMedium.First()), Color = style.Foreground, BackgroundSelectedBrush = new SolidColorBrush(style.BackgroundSelected), }; diff --git a/Source/Engine/UI/GUI/Common/TextBox.cs b/Source/Engine/UI/GUI/Common/TextBox.cs index ee4f744a6..967617649 100644 --- a/Source/Engine/UI/GUI/Common/TextBox.cs +++ b/Source/Engine/UI/GUI/Common/TextBox.cs @@ -1,5 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. +using System.Linq; + namespace FlaxEngine.GUI { /// @@ -88,7 +90,7 @@ namespace FlaxEngine.GUI _layout.Bounds = new Rectangle(DefaultMargin, 1, Width - 2 * DefaultMargin, Height - 2); var style = Style.Current; - Font = new FontReference(style.FontMedium); + Font = new FontReference(style.FontMedium.First()); TextColor = style.Foreground; WatermarkTextColor = style.ForegroundDisabled; SelectionColor = style.BackgroundSelected; diff --git a/Source/Engine/UI/GUI/Panels/DropPanel.cs b/Source/Engine/UI/GUI/Panels/DropPanel.cs index 66e7413eb..33c2e1605 100644 --- a/Source/Engine/UI/GUI/Panels/DropPanel.cs +++ b/Source/Engine/UI/GUI/Panels/DropPanel.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; +using System.Linq; namespace FlaxEngine.GUI { @@ -237,7 +238,7 @@ namespace FlaxEngine.GUI var style = Style.Current; HeaderColor = style.BackgroundNormal; HeaderColorMouseOver = style.BackgroundHighlighted; - HeaderTextFont = new FontReference(style.FontMedium); + HeaderTextFont = new FontReference(style.FontMedium.First()); HeaderTextColor = style.Foreground; ArrowImageOpened = new SpriteBrush(style.ArrowDown); ArrowImageClosed = new SpriteBrush(style.ArrowRight); diff --git a/Source/Engine/UI/GUI/Style.cs b/Source/Engine/UI/GUI/Style.cs index 22b8f52af..d3375a670 100644 --- a/Source/Engine/UI/GUI/Style.cs +++ b/Source/Engine/UI/GUI/Style.cs @@ -1,5 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. +using System.Linq; + namespace FlaxEngine.GUI { /// @@ -12,6 +14,14 @@ namespace FlaxEngine.GUI /// public static Style Current { get; set; } + public Font FontCJK + { + get => _fontCJK?.GetFont(); + set => _fontCJK = new FontReference(value); + } + + private FontReference _fontCJK; + [Serialize] private FontReference _fontTitle; @@ -41,31 +51,31 @@ namespace FlaxEngine.GUI } [Serialize] - private FontReference _fontMedium; + private FontReference[] _fontMedium; /// /// The font medium. /// [NoSerialize] [EditorOrder(30)] - public Font FontMedium + public Font[] FontMedium { - get => _fontMedium?.GetFont(); - set => _fontMedium = new FontReference(value); + get => _fontMedium?.Select((x)=>x.GetFont()).ToArray(); + set => _fontMedium = value.Select((x)=>new FontReference(x)).ToArray(); } [Serialize] - private FontReference _fontSmall; + private FontReference[] _fontSmall; /// /// The font small. /// [NoSerialize] [EditorOrder(40)] - public Font FontSmall + public Font[] FontSmall { - get => _fontSmall?.GetFont(); - set => _fontSmall = new FontReference(value); + get => _fontSmall?.Select((x) => x.GetFont()).ToArray(); + set => _fontSmall = value.Select((x) => new FontReference(x)).ToArray(); } /// diff --git a/Source/Engine/UI/GUI/Tooltip.cs b/Source/Engine/UI/GUI/Tooltip.cs index 734fb078f..e17b754c4 100644 --- a/Source/Engine/UI/GUI/Tooltip.cs +++ b/Source/Engine/UI/GUI/Tooltip.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; +using System.Linq; namespace FlaxEngine.GUI { @@ -255,14 +256,14 @@ namespace FlaxEngine.GUI // Calculate size of the tooltip var size = Float2.Zero; - if (style != null && style.FontMedium && !string.IsNullOrEmpty(_currentText)) + if (style != null && style.FontMedium.First() && !string.IsNullOrEmpty(_currentText)) { var layout = TextLayoutOptions.Default; layout.Bounds = new Rectangle(0, 0, MaxWidth, 10000000); layout.HorizontalAlignment = TextAlignment.Center; layout.VerticalAlignment = TextAlignment.Center; layout.TextWrapping = TextWrapping.WrapWords; - var items = style.FontMedium.ProcessText(_currentText, ref layout); + var items = style.FontMedium.First().ProcessText(_currentText, ref layout); for (int i = 0; i < items.Length; i++) { ref var item = ref items[i]; From a3bc394e4e0279c51b3acb133a6ad8ef5a2f27bf Mon Sep 17 00:00:00 2001 From: ExMatics HydrogenC <33123710+HydrogenC@users.noreply.github.com> Date: Wed, 29 Nov 2023 15:04:29 +0800 Subject: [PATCH 32/61] Fix a bunch of rendering bugs --- .gitignore | 1 + Source/Editor/EditorAssets.cs | 2 +- Source/Engine/Render2D/Font.cpp | 133 ++++++++++---- Source/Engine/Render2D/Font.h | 39 +++- Source/Engine/Render2D/MultiFont.cpp | 1 + Source/Engine/Render2D/MultiFont.h | 77 ++++++++ Source/Engine/Render2D/Render2D.cpp | 266 +++++++++++++-------------- Source/Engine/UI/GUI/Common/Label.cs | 2 +- 8 files changed, 332 insertions(+), 189 deletions(-) create mode 100644 Source/Engine/Render2D/MultiFont.cpp create mode 100644 Source/Engine/Render2D/MultiFont.h diff --git a/.gitignore b/.gitignore index b7e11e554..b653b7f77 100644 --- a/.gitignore +++ b/.gitignore @@ -157,3 +157,4 @@ obj/ .idea/ *.code-workspace omnisharp.json +Content/Editor/Fonts/NotoSansSC-Regular.flax diff --git a/Source/Editor/EditorAssets.cs b/Source/Editor/EditorAssets.cs index 237908a0c..0fe5ee47e 100644 --- a/Source/Editor/EditorAssets.cs +++ b/Source/Editor/EditorAssets.cs @@ -54,7 +54,7 @@ namespace FlaxEditor /// public static string PrimaryFont = "Editor/Fonts/Roboto-Regular"; - public static string CJKFont = "Editor/Fonts/NotoSansSC-Medium"; + public static string CJKFont = "Editor/Fonts/NotoSansSC-Regular"; /// /// The Inconsolata Regular font. diff --git a/Source/Engine/Render2D/Font.cpp b/Source/Engine/Render2D/Font.cpp index 13b17197c..277ad8ddd 100644 --- a/Source/Engine/Render2D/Font.cpp +++ b/Source/Engine/Render2D/Font.cpp @@ -6,6 +6,7 @@ #include "Engine/Core/Log.h" #include "Engine/Threading/Threading.h" #include "IncludeFreeType.h" +#include "MultiFont.h" Font::Font(FontAsset* parentAsset, float size) : ManagedScriptingObject(SpawnParams(Guid::New(), Font::TypeInitializer)) @@ -118,6 +119,10 @@ void Font::ProcessText(const StringView& text, Array& outputLines tmpLine.FirstCharIndex = 0; tmpLine.LastCharIndex = -1; + if (textLength == 0) { + return; + } + int32 lastWrapCharIndex = INVALID_INDEX; float lastWrapCharX = 0; bool lastMoveLine = false; @@ -129,6 +134,11 @@ void Font::ProcessText(const StringView& text, Array& outputLines float xAdvance = 0; int32 nextCharIndex = currentIndex + 1; + // Submit line if text ends + if (nextCharIndex == textLength) { + moveLine = true; + } + // Cache current character const Char currentChar = text[currentIndex]; const bool isWhitespace = StringUtils::IsWhitespace(currentChar); @@ -146,7 +156,6 @@ void Font::ProcessText(const StringView& text, Array& outputLines { // Break line moveLine = true; - currentIndex++; tmpLine.LastCharIndex++; } else @@ -178,8 +187,8 @@ void Font::ProcessText(const StringView& text, Array& outputLines if (lastWrapCharIndex != INVALID_INDEX) { // Skip moving twice for the same character - int32 lastLineLasCharIndex = outputLines.HasItems() ? outputLines.Last().LastCharIndex : -10000; - if (lastLineLasCharIndex == lastWrapCharIndex || lastLineLasCharIndex == lastWrapCharIndex - 1 || lastLineLasCharIndex == lastWrapCharIndex - 2) + int32 lastLineLastCharIndex = outputLines.HasItems() ? outputLines.Last().LastCharIndex : -10000; + if (lastLineLastCharIndex == lastWrapCharIndex || lastLineLastCharIndex == lastWrapCharIndex - 1 || lastLineLastCharIndex == lastWrapCharIndex - 2) { currentIndex = nextCharIndex; lastMoveLine = moveLine; @@ -226,8 +235,8 @@ void Font::ProcessText(const StringView& text, Array& outputLines // Reset line tmpLine.Location.Y += baseLinesDistance; - tmpLine.FirstCharIndex = currentIndex; - tmpLine.LastCharIndex = currentIndex - 1; + tmpLine.FirstCharIndex = nextCharIndex; + tmpLine.LastCharIndex = nextCharIndex - 1; cursorX = 0; lastWrapCharIndex = INVALID_INDEX; lastWrapCharX = 0; @@ -238,7 +247,8 @@ void Font::ProcessText(const StringView& text, Array& outputLines lastMoveLine = moveLine; } - if (textLength != 0 && (tmpLine.LastCharIndex >= tmpLine.FirstCharIndex || text[textLength - 1] == '\n')) + // Check if an additional line should be created + if (text[textLength - 1] == '\n') { // Add line tmpLine.Size.X = cursorX; @@ -283,84 +293,101 @@ void Font::ProcessText(const StringView& text, Array& outputLines } } -void Font::ProcessText(const Array& fonts, const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout) +void Font::ProcessText(const Array& fonts, const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout) { float cursorX = 0; int32 kerning; - FontLineCache tmpLine; + MultiFontLineCache tmpLine; + MultiFontSegmentCache tmpSegment; FontCharacterEntry entry; FontCharacterEntry previous; int32 textLength = text.Length(); float scale = layout.Scale / FontManager::FontScale; float boundsWidth = layout.Bounds.GetWidth(); float baseLinesDistanceScale = layout.BaseLinesGapScale * scale; + + tmpSegment.Location = Float2::Zero; + tmpSegment.Height = 0; + tmpSegment.FirstCharIndex = 0; + tmpSegment.LastCharIndex = -1; + tmpLine.Location = Float2::Zero; tmpLine.Size = Float2::Zero; - tmpLine.FirstCharIndex = 0; - tmpLine.LastCharIndex = -1; + tmpLine.Segments = Array(); + + if (textLength == 0) { + return; + } int32 lastWrapCharIndex = INVALID_INDEX; float lastWrapCharX = 0; bool lastMoveLine = false; - - int32 previousFontIndex = -1; + // The index of the font used by the current segment + int32 currentFontIndex = GetCharFontIndex(fonts, text[0], 0); // The maximum font height of the current line float maxHeight = 0; + float maxAscender = 0; + // Process each character to split text into single lines for (int32 currentIndex = 0; currentIndex < textLength;) { bool moveLine = false; + bool moveSegment = false; float xAdvance = 0; int32 nextCharIndex = currentIndex + 1; + // Submit line and segment if text ends + if (nextCharIndex == textLength) { + moveLine = moveSegment = true; + } + // Cache current character const Char currentChar = text[currentIndex]; const bool isWhitespace = StringUtils::IsWhitespace(currentChar); // Check if character can wrap words - const bool isWrapChar = !StringUtils::IsAlnum(currentChar) || isWhitespace || StringUtils::IsUpper(currentChar); + const bool isWrapChar = !StringUtils::IsAlnum(currentChar) || isWhitespace || StringUtils::IsUpper(currentChar) || (currentChar >= 0x3040 && currentChar <= 0x9FFF); if (isWrapChar && currentIndex != 0) { lastWrapCharIndex = currentIndex; lastWrapCharX = cursorX; } + int32 nextFontIndex = currentFontIndex; // Check if it's a newline character if (currentChar == '\n') { // Break line - moveLine = true; - currentIndex++; - tmpLine.LastCharIndex++; + moveLine = moveSegment = true; + tmpSegment.LastCharIndex++; } else { // Get character entry - int32 fontIndex = 0; - while (fontIndex < fonts.Count() && !fonts[fontIndex]->ContainsChar(currentChar)) - { - fontIndex++; + if (nextCharIndex < textLength) { + nextFontIndex = GetCharFontIndex(fonts, text[nextCharIndex], currentFontIndex); } - // If no font can match the char, then use the first font - if (fontIndex == fonts.Count()) { - fontIndex = 0; - } // Get character entry - fonts[fontIndex]->GetCharacter(currentChar, entry); - maxHeight = Math::Max(maxHeight, static_cast(fonts[fontIndex]->GetHeight())); + fonts[currentFontIndex]->GetCharacter(currentChar, entry); + maxHeight = Math::Max(maxHeight, static_cast(fonts[currentFontIndex]->GetHeight())); + maxAscender = Math::Max(maxAscender, static_cast(fonts[currentFontIndex]->GetAscender())); + + // Move segment if the font changes or text ends + if (nextFontIndex != currentFontIndex || nextCharIndex == textLength) { + moveSegment = true; + } // Get kerning, only when the font hasn't changed - if (!isWhitespace && previous.IsValid && previousFontIndex == fontIndex) + if (!isWhitespace && previous.IsValid && !moveSegment) { - kerning = fonts[fontIndex]->GetKerning(previous.Character, entry.Character); + kerning = fonts[currentFontIndex]->GetKerning(previous.Character, entry.Character); } else { kerning = 0; } previous = entry; - previousFontIndex = fontIndex; xAdvance = (kerning + entry.AdvanceX) * scale; // Check if character fits the line or skip wrapping @@ -368,15 +395,15 @@ void Font::ProcessText(const Array& fonts, const StringView& text, Array< { // Move character cursorX += xAdvance; - tmpLine.LastCharIndex++; + tmpSegment.LastCharIndex++; } else if (layout.TextWrapping == TextWrapping::WrapWords) { if (lastWrapCharIndex != INVALID_INDEX) { // Skip moving twice for the same character - int32 lastLineLasCharIndex = outputLines.HasItems() ? outputLines.Last().LastCharIndex : -10000; - if (lastLineLasCharIndex == lastWrapCharIndex || lastLineLasCharIndex == lastWrapCharIndex - 1 || lastLineLasCharIndex == lastWrapCharIndex - 2) + int32 lastLineLastCharIndex = outputLines.HasItems() && outputLines.Last().Segments.HasItems() ? outputLines.Last().Segments.Last().LastCharIndex : -10000; + if (lastLineLastCharIndex == lastWrapCharIndex || lastLineLastCharIndex == lastWrapCharIndex - 1 || lastLineLastCharIndex == lastWrapCharIndex - 2) { currentIndex = nextCharIndex; lastMoveLine = moveLine; @@ -386,16 +413,18 @@ void Font::ProcessText(const Array& fonts, const StringView& text, Array< // Move line const Char wrapChar = text[lastWrapCharIndex]; moveLine = true; + moveSegment = tmpSegment.FirstCharIndex < lastWrapCharIndex; + cursorX = lastWrapCharX; if (StringUtils::IsWhitespace(wrapChar)) { // Skip whitespaces - tmpLine.LastCharIndex = lastWrapCharIndex - 1; + tmpSegment.LastCharIndex = lastWrapCharIndex - 1; nextCharIndex = currentIndex = lastWrapCharIndex + 1; } else { - tmpLine.LastCharIndex = lastWrapCharIndex - 1; + tmpSegment.LastCharIndex = lastWrapCharIndex - 1; nextCharIndex = currentIndex = lastWrapCharIndex; } } @@ -404,6 +433,7 @@ void Font::ProcessText(const Array& fonts, const StringView& text, Array< { // Move line moveLine = true; + moveSegment = tmpSegment.FirstCharIndex < currentChar; nextCharIndex = currentIndex; // Skip moving twice for the same character @@ -412,38 +442,54 @@ void Font::ProcessText(const Array& fonts, const StringView& text, Array< } } + if (moveSegment) { + // Add segment + tmpSegment.Height = baseLinesDistanceScale * fonts[currentFontIndex]->GetHeight(); + tmpSegment.LastCharIndex = Math::Max(tmpSegment.LastCharIndex, tmpSegment.FirstCharIndex); + tmpSegment.FontIndex = currentFontIndex; + tmpLine.Segments.Add(tmpSegment); + + // Reset segment + tmpSegment.Location.X = cursorX; + tmpSegment.FirstCharIndex = nextCharIndex; + tmpSegment.LastCharIndex = nextCharIndex - 1; + + currentFontIndex = nextFontIndex; + } + // Check if move to another line if (moveLine) { // Add line tmpLine.Size.X = cursorX; tmpLine.Size.Y = baseLinesDistanceScale * maxHeight; - tmpLine.LastCharIndex = Math::Max(tmpLine.LastCharIndex, tmpLine.FirstCharIndex); + tmpLine.MaxAscender = maxAscender; outputLines.Add(tmpLine); // Reset line + tmpLine.Segments.Clear(); tmpLine.Location.Y += baseLinesDistanceScale * maxHeight; - tmpLine.FirstCharIndex = currentIndex; - tmpLine.LastCharIndex = currentIndex - 1; cursorX = 0; + tmpSegment.Location.X = cursorX; lastWrapCharIndex = INVALID_INDEX; lastWrapCharX = 0; previous.IsValid = false; // Reset max font height maxHeight = 0; + maxAscender = 0; } currentIndex = nextCharIndex; lastMoveLine = moveLine; } - if (textLength != 0 && (tmpLine.LastCharIndex >= tmpLine.FirstCharIndex || text[textLength - 1] == '\n')) + // Check if an additional line should be created + if (text[textLength - 1] == '\n') { // Add line tmpLine.Size.X = cursorX; tmpLine.Size.Y = baseLinesDistanceScale * maxHeight; - tmpLine.LastCharIndex = textLength - 1; outputLines.Add(tmpLine); tmpLine.Location.Y += baseLinesDistanceScale * maxHeight; @@ -466,7 +512,7 @@ void Font::ProcessText(const Array& fonts, const StringView& text, Array< } for (int32 i = 0; i < outputLines.Count(); i++) { - FontLineCache& line = outputLines[i]; + MultiFontLineCache& line = outputLines[i]; Float2 rootPos = line.Location + offset; // Fix upper left line corner to match desire text alignment @@ -480,6 +526,13 @@ void Font::ProcessText(const Array& fonts, const StringView& text, Array< } line.Location = rootPos; + + // Align all segments to center in case they have different heights + for (int32 j = 0; j < line.Segments.Count(); j++) + { + MultiFontSegmentCache& segment = line.Segments[j]; + segment.Location.Y += (line.MaxAscender - fonts[segment.FontIndex]->GetAscender()) / 2; + } } } diff --git a/Source/Engine/Render2D/Font.h b/Source/Engine/Render2D/Font.h index d8ac1b708..0425f2bc5 100644 --- a/Source/Engine/Render2D/Font.h +++ b/Source/Engine/Render2D/Font.h @@ -8,9 +8,11 @@ #include "Engine/Content/AssetReference.h" #include "Engine/Scripting/ScriptingObject.h" #include "TextLayoutOptions.h" +#include "MultiFont.h" class FontAsset; struct FontTextureAtlasSlot; +struct MultiFontLineCache; // The default DPI that engine is using #define DefaultDPI 96 @@ -20,7 +22,7 @@ struct FontTextureAtlasSlot; /// API_STRUCT(NoDefault) struct TextRange { -DECLARE_SCRIPTING_TYPE_MINIMAL(TextRange); + DECLARE_SCRIPTING_TYPE_MINIMAL(TextRange); /// /// The start index (inclusive). @@ -90,7 +92,7 @@ struct TIsPODType /// API_STRUCT(NoDefault) struct FontLineCache { -DECLARE_SCRIPTING_TYPE_MINIMAL(FontLineCache); + DECLARE_SCRIPTING_TYPE_MINIMAL(FontLineCache); /// /// The root position of the line (upper left corner). @@ -108,7 +110,7 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(FontLineCache); API_FIELD() int32 FirstCharIndex; /// - /// The last character index (from the input text). + /// The last character index (from the input text), inclusive. /// API_FIELD() int32 LastCharIndex; }; @@ -154,7 +156,7 @@ struct TIsPODType /// API_STRUCT(NoDefault) struct FontCharacterEntry { -DECLARE_SCRIPTING_TYPE_MINIMAL(FontCharacterEntry); + DECLARE_SCRIPTING_TYPE_MINIMAL(FontCharacterEntry); /// /// The character represented by this entry. @@ -223,7 +225,7 @@ struct TIsPODType /// API_CLASS(Sealed, NoSpawn) class FLAXENGINE_API Font : public ManagedScriptingObject { -DECLARE_SCRIPTING_TYPE_NO_SPAWN(Font); + DECLARE_SCRIPTING_TYPE_NO_SPAWN(Font); friend FontAsset; private: @@ -346,7 +348,7 @@ public: /// The input text. /// The layout properties. /// The output lines list. - static void ProcessText(const Array& fonts, const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout); + static void ProcessText(const Array& fonts, const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout); /// /// Processes text to get cached lines for rendering. @@ -404,6 +406,7 @@ public: /// The minimum size for that text and fot to render properly. API_FUNCTION() Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout); + /* /// /// Measures minimum size of the rectangle that will be needed to draw given text. /// @@ -412,6 +415,7 @@ public: /// The layout properties. /// The minimum size for that text and fot to render properly. API_FUNCTION() static Float2 MeasureText(const Array& fonts, const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout); + */ /// /// Measures minimum size of the rectangle that will be needed to draw given text. @@ -500,6 +504,7 @@ public: /// The character position (upper left corner which can be used for a caret position). API_FUNCTION() Float2 GetCharPosition(const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout); + /* /// /// Calculates character position for given text and character index. /// @@ -509,6 +514,7 @@ public: /// The text layout properties. /// The character position (upper left corner which can be used for a caret position). API_FUNCTION() static Float2 GetCharPosition(const Array& fonts, const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout); + */ /// /// Calculates character position for given text and character index. @@ -553,6 +559,27 @@ public: /// True if the font contains the glyph of the char, otherwise false. API_FUNCTION() FORCE_INLINE bool ContainsChar(Char c); + /// + /// Gets the index of the font that should be used to render the char + /// + /// The font list. + /// The char. + /// Number to return if char cannot be found. + /// + API_FUNCTION() FORCE_INLINE static int32 GetCharFontIndex(const Array& fonts, Char c, int32 missing = -1) { + int32 fontIndex = 0; + while (fontIndex < fonts.Count() && !fonts[fontIndex]->ContainsChar(c)) + { + fontIndex++; + } + + if (fontIndex == fonts.Count()) { + return missing; + } + + return fontIndex; + } + /// /// Flushes the size of the face with the Free Type library backend. /// diff --git a/Source/Engine/Render2D/MultiFont.cpp b/Source/Engine/Render2D/MultiFont.cpp new file mode 100644 index 000000000..9844c12e0 --- /dev/null +++ b/Source/Engine/Render2D/MultiFont.cpp @@ -0,0 +1 @@ +#include "MultiFont.h" diff --git a/Source/Engine/Render2D/MultiFont.h b/Source/Engine/Render2D/MultiFont.h new file mode 100644 index 000000000..b1da832d5 --- /dev/null +++ b/Source/Engine/Render2D/MultiFont.h @@ -0,0 +1,77 @@ +#pragma once + +#include "Engine/Core/Collections/Array.h" +#include "Engine/Core/Collections/Dictionary.h" +#include "Font.h" + +/// +/// The font segment info generated during text processing. +/// +API_STRUCT(NoDefault) struct MultiFontSegmentCache +{ + DECLARE_SCRIPTING_TYPE_MINIMAL(MultiFontSegmentCache); + + /// + /// The root position of the segment (upper left corner), relative to line. + /// + API_FIELD() Float2 Location; + + /// + /// The height of the current segment + /// + API_FIELD() float Height; + + /// + /// The first character index (from the input text). + /// + API_FIELD() int32 FirstCharIndex; + + /// + /// The last character index (from the input text), inclusive. + /// + API_FIELD() int32 LastCharIndex; + + /// + /// The index of the font to render with + /// + API_FIELD() int32 FontIndex; +}; + +template<> +struct TIsPODType +{ + enum { Value = true }; +}; + +/// +/// Line of font segments info generated during text processing. +/// +API_STRUCT(NoDefault) struct MultiFontLineCache +{ + DECLARE_SCRIPTING_TYPE_MINIMAL(MultiFontLineCache); + + /// + /// The root position of the line (upper left corner). + /// + API_FIELD() Float2 Location; + + /// + /// The line bounds (width and height). + /// + API_FIELD() Float2 Size; + + /// + /// The maximum ascendent of the line. + /// + API_FIELD() float MaxAscender; + + /// + /// The index of the font to render with + /// + API_FIELD() Array Segments; +}; + +API_CLASS(Sealed, NoSpawn) class FLAXENGINE_API MultiFont : public ManagedScriptingObject +{ + DECLARE_SCRIPTING_TYPE_NO_SPAWN(MultiFont); +}; diff --git a/Source/Engine/Render2D/Render2D.cpp b/Source/Engine/Render2D/Render2D.cpp index eedfbeffe..8a39d29be 100644 --- a/Source/Engine/Render2D/Render2D.cpp +++ b/Source/Engine/Render2D/Render2D.cpp @@ -3,6 +3,7 @@ #include "Render2D.h" #include "Font.h" #include "FontManager.h" +#include "MultiFont.h" #include "FontTextureAtlas.h" #include "RotatedRectangle.h" #include "SpriteAtlas.h" @@ -194,6 +195,7 @@ namespace // Drawing Array DrawCalls; Array Lines; + Array MultiFontLines; Array Lines2; bool IsScissorsRectEmpty; bool IsScissorsRectEnabled; @@ -1384,6 +1386,9 @@ void Render2D::DrawText(const Array& fonts, const StringView& text, const int32 kerning; float scale = 1.0f / FontManager::FontScale; + // Process text to get lines + Array maxAscenders; + // Render all characters FontCharacterEntry entry; Render2DDrawCall drawCall; @@ -1398,48 +1403,70 @@ void Render2D::DrawText(const Array& fonts, const StringView& text, const drawCall.AsChar.Mat = nullptr; } + int32 lineIndex = 0; + maxAscenders.Add(0); + for (int32 currentIndex = 0; currentIndex < text.Length(); currentIndex++) + { + if (text[currentIndex] != '\n') { + int32 fontIndex = Font::GetCharFontIndex(fonts, text[currentIndex], 0); + maxAscenders[lineIndex] = Math::Max(maxAscenders[lineIndex], static_cast(fonts[fontIndex]->GetAscender())); + } + else { + lineIndex++; + maxAscenders.Add(0); + } + } + + lineIndex = 0; // The following code cut the text into segments, according to the font used to render Float2 pointer = location; // The starting index of the current segment int32 startIndex = 0; // The index of the font used by the current segment - int32 segmentFontIndex = 0; + int32 currentFontIndex = Font::GetCharFontIndex(fonts, text[0], 0); // The maximum font height of the current line float maxHeight = 0; - for (int32 currentIndex = 0; currentIndex <= text.Length(); currentIndex++) + for (int32 currentIndex = 0; currentIndex < text.Length(); currentIndex++) { // Cache current character - const Char currentChar = currentIndex < text.Length() ? text[currentIndex] : 0; + const Char currentChar = text[currentIndex]; + int32 nextCharIndex = currentIndex + 1; + bool moveSegment = false; + bool moveLine = false; + int32 nextFontIndex = currentFontIndex; + + // Submit segment if text ends + if (nextCharIndex == text.Length()) { + moveSegment = true; + } // Check if it isn't a newline character if (currentChar != '\n') { - int32 fontIndex = 0; - if (currentIndex < text.Length()) { - while (fontIndex < fonts.Count() && !fonts[fontIndex]->ContainsChar(currentChar)) - { - fontIndex++; - } - - // If no font can match the char, then use the segment font - if (fontIndex == fonts.Count()) { - fontIndex = segmentFontIndex; - } - - // Do nothing if the char still belongs to the current segment - if (fontIndex == segmentFontIndex) { - continue; - } + // Get character entry + if (nextCharIndex < text.Length()) { + nextFontIndex = Font::GetCharFontIndex(fonts, text[nextCharIndex], currentFontIndex); } + if (nextFontIndex != currentFontIndex) { + moveSegment = true; + } + } + else + { + // Move + moveLine = moveSegment = true; + } + + if (moveSegment) { // Render the pending segment before beginning the new segment - renderText:auto fontHeight = fonts[segmentFontIndex]->GetHeight(); + auto fontHeight = fonts[currentFontIndex]->GetHeight(); maxHeight = Math::Max(maxHeight, static_cast(fontHeight)); - auto fontDescender = fonts[segmentFontIndex]->GetDescender(); - for (int32 renderIndex = startIndex; renderIndex < currentIndex; renderIndex++) + auto fontDescender = fonts[currentFontIndex]->GetDescender(); + for (int32 renderIndex = startIndex; renderIndex <= currentIndex; renderIndex++) { // Get character entry - fonts[segmentFontIndex]->GetCharacter(text[renderIndex], entry); + fonts[currentFontIndex]->GetCharacter(text[renderIndex], entry); // Check if need to select/change font atlas (since characters even in the same font may be located in different atlases) if (fontAtlas == nullptr || entry.TextureIndex != fontAtlasIndex) @@ -1466,7 +1493,7 @@ void Render2D::DrawText(const Array& fonts, const StringView& text, const // Get kerning if (!isWhitespace && previous.IsValid) { - kerning = fonts[segmentFontIndex]->GetKerning(previous.Character, entry.Character); + kerning = fonts[currentFontIndex]->GetKerning(previous.Character, entry.Character); } else { @@ -1482,7 +1509,7 @@ void Render2D::DrawText(const Array& fonts, const StringView& text, const const float x = pointer.X + entry.OffsetX * scale; const float y = pointer.Y + (fontHeight + fontDescender - entry.OffsetY) * scale; - Rectangle charRect(x, y, entry.UVSize.X * scale, entry.UVSize.Y * scale); + Rectangle charRect(x, y + (maxAscenders[lineIndex] - fonts[currentFontIndex]->GetAscender()) / 2, entry.UVSize.X * scale, entry.UVSize.Y * scale); Float2 upperLeftUV = entry.UV * invAtlasSize; Float2 rightBottomUV = (entry.UV + entry.UVSize) * invAtlasSize; @@ -1498,22 +1525,17 @@ void Render2D::DrawText(const Array& fonts, const StringView& text, const pointer.X += entry.AdvanceX * scale; } - // Start new segment - startIndex = currentIndex; - segmentFontIndex = fontIndex; - - if (currentIndex == text.Length() - 1) { - currentIndex++; - goto renderText; + if (moveLine) { + pointer.X = location.X; + pointer.Y += maxHeight * scale; + // Clear max height + maxHeight = 0; + lineIndex++; } - } - else - { - // Move - pointer.X = location.X; - pointer.Y += maxHeight * scale; - // Clear max height - maxHeight = 0; + + // Start new segment + startIndex = nextCharIndex; + currentFontIndex = nextFontIndex; } } } @@ -1540,8 +1562,8 @@ void Render2D::DrawText(const Array& fonts, const StringView& text, const float scale = layout.Scale / FontManager::FontScale; // Process text to get lines - Lines.Clear(); - Font::ProcessText(fonts, text, Lines, layout); + MultiFontLines.Clear(); + Font::ProcessText(fonts, text, MultiFontLines, layout); // Render all lines FontCharacterEntry entry; @@ -1557,119 +1579,81 @@ void Render2D::DrawText(const Array& fonts, const StringView& text, const drawCall.AsChar.Mat = nullptr; } - for (int32 lineIndex = 0; lineIndex < Lines.Count(); lineIndex++) + for (int32 lineIndex = 0; lineIndex < MultiFontLines.Count(); lineIndex++) { - const FontLineCache& line = Lines[lineIndex]; - - // The following code cut the text into segments, according to the font used to render - Float2 pointer = line.Location; - // The starting index of the current segment - int32 startIndex = line.FirstCharIndex; - // The index of the font used by the current segment - int32 segmentFontIndex = 0; - // The maximum font height of the current line - float maxHeight = 0; - - // Partition and render all characters from the line - for (int32 charIndex = line.FirstCharIndex; charIndex <= line.LastCharIndex + 1; charIndex++) + const MultiFontLineCache& line = MultiFontLines[lineIndex]; + for (int32 segmentIndex = 0; segmentIndex < line.Segments.Count(); segmentIndex++) { - const Char c = charIndex <= line.LastCharIndex ? text[charIndex] : 0; + const MultiFontSegmentCache& segment = MultiFontLines[lineIndex].Segments[segmentIndex]; + auto fontHeight = fonts[segment.FontIndex]->GetHeight(); + auto fontDescender = fonts[segment.FontIndex]->GetDescender(); + Float2 pointer = line.Location + segment.Location; - if (c != '\n') + for (int32 charIndex = segment.FirstCharIndex; charIndex <= segment.LastCharIndex; charIndex++) { - int32 fontIndex = 0; - if (charIndex <= line.LastCharIndex) { - while (fontIndex < fonts.Count() && !fonts[fontIndex]->ContainsChar(c)) - { - fontIndex++; - } - - // If no font can match the char, then use the segment font - if (fontIndex == fonts.Count()) { - fontIndex = segmentFontIndex; - } - - - // Do nothing if the char still belongs to the current segment - if (fontIndex == segmentFontIndex) { - continue; - } - } - - - // Render the pending segment before beginning the new segment - auto fontHeight = fonts[segmentFontIndex]->GetHeight(); - maxHeight = Math::Max(maxHeight, static_cast(fontHeight)); - auto fontDescender = fonts[segmentFontIndex]->GetDescender(); - - const Char* pred = L"Type"; - if (text.Substring(0, Math::Min(4, text.Length())) == pred) { - // __debugbreak(); - } - for (int32 renderIndex = startIndex; renderIndex < charIndex; renderIndex++) + Char c = text[charIndex]; + if (c == '\n') { - // Get character entry - fonts[segmentFontIndex]->GetCharacter(text[renderIndex], entry); + continue; + } - // Check if need to select/change font atlas (since characters even in the same font may be located in different atlases) - if (fontAtlas == nullptr || entry.TextureIndex != fontAtlasIndex) - { - // Get texture atlas that contains current character - fontAtlasIndex = entry.TextureIndex; - fontAtlas = FontManager::GetAtlas(fontAtlasIndex); - if (fontAtlas) - { - fontAtlas->EnsureTextureCreated(); - invAtlasSize = 1.0f / fontAtlas->GetSize(); - drawCall.AsChar.Tex = fontAtlas->GetTexture(); - } - else - { - invAtlasSize = 1.0f; - drawCall.AsChar.Tex = nullptr; - } - } + // Get character entry + fonts[segment.FontIndex]->GetCharacter(c, entry); - // Get kerning - const bool isWhitespace = StringUtils::IsWhitespace(text[renderIndex]); - if (!isWhitespace && previous.IsValid) + // Check if need to select/change font atlas (since characters even in the same font may be located in different atlases) + if (fontAtlas == nullptr || entry.TextureIndex != fontAtlasIndex) + { + // Get texture atlas that contains current character + fontAtlasIndex = entry.TextureIndex; + fontAtlas = FontManager::GetAtlas(fontAtlasIndex); + if (fontAtlas) { - kerning = fonts[segmentFontIndex]->GetKerning(previous.Character, entry.Character); + fontAtlas->EnsureTextureCreated(); + invAtlasSize = 1.0f / fontAtlas->GetSize(); + drawCall.AsChar.Tex = fontAtlas->GetTexture(); } else { - kerning = 0; + invAtlasSize = 1.0f; + drawCall.AsChar.Tex = nullptr; } - pointer.X += (float)kerning * scale; - previous = entry; - - // Omit whitespace characters - if (!isWhitespace) - { - // Calculate character size and atlas coordinates - const float x = pointer.X + entry.OffsetX * scale; - const float y = pointer.Y - entry.OffsetY * scale + Math::Ceil((fontHeight + fontDescender) * scale); - - Rectangle charRect(x, y, entry.UVSize.X * scale, entry.UVSize.Y * scale); - charRect.Offset(layout.Bounds.Location); - - Float2 upperLeftUV = entry.UV * invAtlasSize; - Float2 rightBottomUV = (entry.UV + entry.UVSize) * invAtlasSize; - - // Add draw call - drawCall.StartIB = IBIndex; - drawCall.CountIB = 6; - DrawCalls.Add(drawCall); - WriteRect(charRect, color, upperLeftUV, rightBottomUV); - } - - // Move - pointer.X += entry.AdvanceX * scale; } - // Start new segment - startIndex = charIndex; - segmentFontIndex = fontIndex; + // Get kerning + const bool isWhitespace = StringUtils::IsWhitespace(c); + if (!isWhitespace && previous.IsValid) + { + kerning = fonts[segment.FontIndex]->GetKerning(previous.Character, entry.Character); + } + else + { + kerning = 0; + } + pointer.X += (float)kerning * scale; + previous = entry; + + // Omit whitespace characters + if (!isWhitespace) + { + // Calculate character size and atlas coordinates + const float x = pointer.X + entry.OffsetX * scale; + const float y = pointer.Y - entry.OffsetY * scale + Math::Ceil((fontHeight + fontDescender) * scale); + + Rectangle charRect(x, y, entry.UVSize.X * scale, entry.UVSize.Y * scale); + charRect.Offset(layout.Bounds.Location); + + Float2 upperLeftUV = entry.UV * invAtlasSize; + Float2 rightBottomUV = (entry.UV + entry.UVSize) * invAtlasSize; + + // Add draw call + drawCall.StartIB = IBIndex; + drawCall.CountIB = 6; + DrawCalls.Add(drawCall); + WriteRect(charRect, color, upperLeftUV, rightBottomUV); + } + + // Move + pointer.X += entry.AdvanceX * scale; } } } diff --git a/Source/Engine/UI/GUI/Common/Label.cs b/Source/Engine/UI/GUI/Common/Label.cs index b92e762e1..5fa8ad21d 100644 --- a/Source/Engine/UI/GUI/Common/Label.cs +++ b/Source/Engine/UI/GUI/Common/Label.cs @@ -235,7 +235,7 @@ namespace FlaxEngine.GUI } } - Render2D.DrawText(new Font[] { _font.GetFont(), Style.Current.FontCJK }, Material, _text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale); + Render2D.DrawText([_font.GetFont(), Style.Current.FontCJK], Material, _text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale); if (ClipText) Render2D.PopClip(); From 41bbce56f6935d9066579a6b1d1e5d1b03c7fcc6 Mon Sep 17 00:00:00 2001 From: ExMatics HydrogenC <33123710+HydrogenC@users.noreply.github.com> Date: Thu, 30 Nov 2023 13:41:45 +0800 Subject: [PATCH 33/61] Add multifont rendering to editor --- .../Content/Create/CreateFilesDialog.cs | 2 +- .../Content/Import/ImportFilesDialog.cs | 2 +- Source/Editor/Content/Tree/ContentTreeNode.cs | 4 +- .../CustomEditors/Dedicated/RagdollEditor.cs | 2 +- .../CustomEditors/Dedicated/ScriptsEditor.cs | 2 +- .../Dedicated/UIControlEditor.cs | 6 +- .../Editors/ActorTransformEditor.cs | 2 +- .../Editor/CustomEditors/Editors/TagEditor.cs | 2 +- .../CustomEditors/LayoutElementsContainer.cs | 4 +- Source/Editor/EditorAssets.cs | 2 +- Source/Editor/GUI/ColumnDefinition.cs | 2 +- Source/Editor/GUI/ComboBox.cs | 6 +- .../GUI/ContextMenu/ContextMenuButton.cs | 6 +- Source/Editor/GUI/CurveEditor.cs | 4 +- Source/Editor/GUI/Docking/DockWindow.cs | 2 +- Source/Editor/GUI/ItemsListContextMenu.cs | 4 +- Source/Editor/GUI/MainMenu.cs | 8 +- Source/Editor/GUI/MainMenuButton.cs | 4 +- Source/Editor/GUI/NavigationButton.cs | 4 +- Source/Editor/GUI/Row.cs | 4 +- Source/Editor/GUI/Table.cs | 2 +- .../Editor/GUI/Timeline/GUI/PositionHandle.cs | 21 +- .../Editor/GUI/Timeline/Tracks/MemberTrack.cs | 2 +- Source/Editor/GUI/ToolStripButton.cs | 4 +- Source/Editor/GUI/Tree/TreeNode.cs | 8 +- Source/Editor/Options/InterfaceOptions.cs | 63 ++- Source/Editor/Options/OptionsModule.cs | 17 +- Source/Editor/SceneGraph/GUI/ActorTreeNode.cs | 4 +- .../Archetypes/Animation.TransitionEditor.cs | 2 +- .../Editor/Surface/Archetypes/BehaviorTree.cs | 2 +- Source/Editor/Surface/Archetypes/Function.cs | 2 +- Source/Editor/Surface/AttributesEditor.cs | 2 +- .../Editor/Surface/ContextMenu/VisjectCM.cs | 2 +- .../Surface/ContextMenu/VisjectCMItem.cs | 14 +- Source/Editor/Surface/Elements/InputBox.cs | 4 +- Source/Editor/Surface/SurfaceNode.cs | 4 +- Source/Editor/Tools/Foliage/FoliageTab.cs | 2 +- Source/Editor/Tools/Terrain/CarveTab.cs | 2 +- .../Tools/Terrain/CreateTerrainDialog.cs | 2 +- Source/Editor/Viewport/EditorViewport.cs | 6 +- .../Viewport/Widgets/ViewportWidgetButton.cs | 4 +- Source/Editor/Windows/AboutDialog.cs | 4 +- Source/Editor/Windows/Assets/FontWindow.cs | 2 +- Source/Editor/Windows/ContentWindow.Search.cs | 2 +- Source/Editor/Windows/OutputLogWindow.cs | 74 +-- Source/Editor/Windows/PluginsWindow.cs | 9 +- Source/Editor/Windows/Profiler/Timeline.cs | 4 +- Source/Editor/Windows/ToolboxWindow.cs | 4 +- Source/Engine/Render2D/Font.cpp | 249 +--------- Source/Engine/Render2D/Font.h | 56 +-- Source/Engine/Render2D/MultiFont.cpp | 430 ++++++++++++++++++ Source/Engine/Render2D/MultiFont.h | 296 +++++++++++- Source/Engine/Render2D/MultiFontReference.cs | 74 +++ Source/Engine/Render2D/Render2D.cpp | 72 +-- Source/Engine/Render2D/Render2D.cs | 7 +- Source/Engine/Render2D/Render2D.h | 9 +- Source/Engine/Scripting/Scripting.cs | 15 +- Source/Engine/UI/GUI/Common/Button.cs | 8 +- Source/Engine/UI/GUI/Common/Dropdown.cs | 6 +- Source/Engine/UI/GUI/Common/Label.cs | 12 +- .../UI/GUI/Common/RichTextBox.Parsing.cs | 34 +- .../Engine/UI/GUI/Common/RichTextBox.Tags.cs | 72 +-- Source/Engine/UI/GUI/Common/RichTextBox.cs | 2 +- .../Engine/UI/GUI/Common/RichTextBoxBase.cs | 28 +- Source/Engine/UI/GUI/Common/TextBox.cs | 16 +- Source/Engine/UI/GUI/Panels/DropPanel.cs | 6 +- Source/Engine/UI/GUI/Style.cs | 40 +- Source/Engine/UI/GUI/TextBlockStyle.cs | 2 +- Source/Engine/UI/GUI/Tooltip.cs | 5 +- 69 files changed, 1132 insertions(+), 647 deletions(-) create mode 100644 Source/Engine/Render2D/MultiFontReference.cs diff --git a/Source/Editor/Content/Create/CreateFilesDialog.cs b/Source/Editor/Content/Create/CreateFilesDialog.cs index d48e878bc..48e4920bb 100644 --- a/Source/Editor/Content/Create/CreateFilesDialog.cs +++ b/Source/Editor/Content/Create/CreateFilesDialog.cs @@ -39,7 +39,7 @@ namespace FlaxEditor.Content.Create AnchorPreset = AnchorPresets.HorizontalStretchTop, Offsets = new Margin(0, 0, 0, 40), Parent = this, - Font = new FontReference(Style.Current.FontTitle) + Font = new MultiFontReference(Style.Current.FontTitle) }; var infoLabel = new Label { diff --git a/Source/Editor/Content/Import/ImportFilesDialog.cs b/Source/Editor/Content/Import/ImportFilesDialog.cs index 967583cf6..5a142d0f6 100644 --- a/Source/Editor/Content/Import/ImportFilesDialog.cs +++ b/Source/Editor/Content/Import/ImportFilesDialog.cs @@ -60,7 +60,7 @@ namespace FlaxEditor.Content.Import AnchorPreset = AnchorPresets.HorizontalStretchTop, Offsets = new Margin(0, 0, 0, 40), Parent = this, - Font = new FontReference(Style.Current.FontTitle) + Font = new MultiFontReference(Style.Current.FontTitle) }; var infoLabel = new Label { diff --git a/Source/Editor/Content/Tree/ContentTreeNode.cs b/Source/Editor/Content/Tree/ContentTreeNode.cs index 37c4e8dbb..ee9b463f7 100644 --- a/Source/Editor/Content/Tree/ContentTreeNode.cs +++ b/Source/Editor/Content/Tree/ContentTreeNode.cs @@ -151,8 +151,8 @@ namespace FlaxEditor.Content var textRect = TextRect; for (int i = 0; i < ranges.Length; i++) { - var start = font.First().GetCharPosition(text, ranges[i].StartIndex); - var end = font.First().GetCharPosition(text, ranges[i].EndIndex); + var start = font.GetCharPosition(text, ranges[i].StartIndex); + var end = font.GetCharPosition(text, ranges[i].EndIndex); _highlights.Add(new Rectangle(start.X + textRect.X, textRect.Y, end.X - start.X, textRect.Height)); } isThisVisible = true; diff --git a/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs b/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs index c4b334b3a..b6d14d81e 100644 --- a/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs @@ -81,7 +81,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Title var title = new Label(2, 2, width - 4, 23.0f) { - Font = new FontReference(FlaxEngine.GUI.Style.Current.FontLarge), + Font = new MultiFontReference(FlaxEngine.GUI.Style.Current.FontLarge), Text = "Ragdoll Options", Parent = this }; diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs index 1f69074ce..fd56422cb 100644 --- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs @@ -43,7 +43,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Add script button var buttonText = "Add script"; - var textSize = Style.Current.FontMedium.First().MeasureText(buttonText); + var textSize = Style.Current.FontMedium.MeasureText(buttonText); float addScriptButtonWidth = (textSize.X < 60.0f) ? 60.0f : textSize.X + 4; var buttonHeight = (textSize.Y < 18) ? 18 : textSize.Y + 4; _addScriptsButton = new Button diff --git a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs index 5b5c2f90d..9627edfd8 100644 --- a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs @@ -239,7 +239,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Title var title = new Label(2, 2, DialogWidth - 4, TitleHeight) { - Font = new FontReference(style.FontLarge), + Font = new MultiFontReference(style.FontLarge), Text = "Anchor Presets", Parent = this }; @@ -247,7 +247,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Info var info = new Label(0, title.Bottom, DialogWidth, InfoHeight) { - Font = new FontReference(style.FontSmall.First()), + Font = new MultiFontReference(style.FontSmall), Text = "Shift: also set bounds\nControl: also set pivot", Parent = this }; @@ -423,7 +423,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Set control type button var space = layout.Space(20); var buttonText = "Set Type"; - var textSize = FlaxEngine.GUI.Style.Current.FontMedium.First().MeasureText(buttonText); + var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(buttonText); float setTypeButtonWidth = (textSize.X < 60.0f) ? 60.0f : textSize.X + 4; var setTypeButton = new Button { diff --git a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs index d7059f712..0cad200fd 100644 --- a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs @@ -101,7 +101,7 @@ namespace FlaxEditor.CustomEditors.Editors _linkButton.Clicked += ToggleLink; ToggleEnabled(); SetLinkStyle(); - var textSize = FlaxEngine.GUI.Style.Current.FontMedium.First().MeasureText(LinkedLabel.Text.Value); + var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(LinkedLabel.Text.Value); _linkButton.LocalX += textSize.X + 10; LinkedLabel.SetupContextMenu += (label, menu, editor) => { diff --git a/Source/Editor/CustomEditors/Editors/TagEditor.cs b/Source/Editor/CustomEditors/Editors/TagEditor.cs index a174d02e4..dbd5d124c 100644 --- a/Source/Editor/CustomEditors/Editors/TagEditor.cs +++ b/Source/Editor/CustomEditors/Editors/TagEditor.cs @@ -631,7 +631,7 @@ namespace FlaxEditor.CustomEditors.Editors TooltipText = "Edit...", Parent = _label, }; - var textSize = FlaxEngine.GUI.Style.Current.FontMedium.First().MeasureText(buttonText); + var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(buttonText); if (textSize.Y > button.Width) button.Width = textSize.Y + 2; diff --git a/Source/Editor/CustomEditors/LayoutElementsContainer.cs b/Source/Editor/CustomEditors/LayoutElementsContainer.cs index 936851b15..1584e88de 100644 --- a/Source/Editor/CustomEditors/LayoutElementsContainer.cs +++ b/Source/Editor/CustomEditors/LayoutElementsContainer.cs @@ -276,7 +276,7 @@ namespace FlaxEditor.CustomEditors public LabelElement Header(string text) { var element = Label(text); - element.Label.Font = new FontReference(Style.Current.FontLarge); + element.Label.Font = new MultiFontReference(Style.Current.FontLarge); return element; } @@ -284,7 +284,7 @@ namespace FlaxEditor.CustomEditors { var element = Header(header.Text); if (header.FontSize != -1) - element.Label.Font = new FontReference(element.Label.Font.Font, header.FontSize); + element.Label.Font = new MultiFontReference(element.Label.Font, header.FontSize); if (header.Color != 0) element.Label.TextColor = Color.FromRGBA(header.Color); return element; diff --git a/Source/Editor/EditorAssets.cs b/Source/Editor/EditorAssets.cs index 0fe5ee47e..c894abe6b 100644 --- a/Source/Editor/EditorAssets.cs +++ b/Source/Editor/EditorAssets.cs @@ -54,7 +54,7 @@ namespace FlaxEditor /// public static string PrimaryFont = "Editor/Fonts/Roboto-Regular"; - public static string CJKFont = "Editor/Fonts/NotoSansSC-Regular"; + public static string CjkFont = "Editor/Fonts/NotoSansSC-Regular"; /// /// The Inconsolata Regular font. diff --git a/Source/Editor/GUI/ColumnDefinition.cs b/Source/Editor/GUI/ColumnDefinition.cs index aff1817c3..c6e8f2889 100644 --- a/Source/Editor/GUI/ColumnDefinition.cs +++ b/Source/Editor/GUI/ColumnDefinition.cs @@ -35,7 +35,7 @@ namespace FlaxEditor.GUI /// /// The title font. /// - public Font TitleFont; + public MultiFont TitleFont; /// /// The column title text color. diff --git a/Source/Editor/GUI/ComboBox.cs b/Source/Editor/GUI/ComboBox.cs index 7dc407698..8e6cf39a0 100644 --- a/Source/Editor/GUI/ComboBox.cs +++ b/Source/Editor/GUI/ComboBox.cs @@ -191,7 +191,7 @@ namespace FlaxEditor.GUI /// Gets or sets the font used to draw text. /// [EditorDisplay("Style"), EditorOrder(2000)] - public FontReference Font { get; set; } + public MultiFontReference Font { get; set; } /// /// Gets or sets the color of the text. @@ -273,7 +273,7 @@ namespace FlaxEditor.GUI MaximumItemsInViewCount = 20; var style = Style.Current; - Font = new FontReference(style.FontMedium.First()); + Font = new MultiFontReference(style.FontMedium); TextColor = style.Foreground; BackgroundColor = style.BackgroundNormal; BackgroundColorHighlighted = BackgroundColor; @@ -554,7 +554,7 @@ namespace FlaxEditor.GUI var textRect = new Rectangle(margin, 0, clientRect.Width - boxSize - 2.0f * margin, clientRect.Height); Render2D.PushClip(textRect); var textColor = TextColor; - Render2D.DrawText(Font.GetFont(), text, textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, textScale); + Render2D.DrawText(Font.GetMultiFont(), text, textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, textScale); Render2D.PopClip(); } diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs b/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs index 872700a9b..ba3326412 100644 --- a/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs +++ b/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs @@ -234,11 +234,11 @@ namespace FlaxEditor.GUI.ContextMenu { var style = Style.Current; float width = 20; - if (style.FontMedium.First()) + if (style.FontMedium) { - width += style.FontMedium.First().MeasureText(Text).X; + width += style.FontMedium.MeasureText(Text).X; if (!string.IsNullOrEmpty(ShortKeys)) - width += 40 + style.FontMedium.First().MeasureText(ShortKeys).X; + width += 40 + style.FontMedium.MeasureText(ShortKeys).X; } return Mathf.Max(width, base.MinimumWidth); diff --git a/Source/Editor/GUI/CurveEditor.cs b/Source/Editor/GUI/CurveEditor.cs index f71409b20..ff14cdb24 100644 --- a/Source/Editor/GUI/CurveEditor.cs +++ b/Source/Editor/GUI/CurveEditor.cs @@ -317,7 +317,7 @@ namespace FlaxEditor.GUI private Color _contentsColor; private Color _linesColor; private Color _labelsColor; - private Font _labelsFont; + private MultiFont _labelsFont; /// /// The keyframe UI points. @@ -437,7 +437,7 @@ namespace FlaxEditor.GUI _contentsColor = style.Background.RGBMultiplied(0.7f); _linesColor = style.ForegroundDisabled.RGBMultiplied(0.7f); _labelsColor = style.ForegroundDisabled; - _labelsFont = style.FontSmall.First(); + _labelsFont = style.FontSmall; _mainPanel = new Panel(ScrollBars.Both) { diff --git a/Source/Editor/GUI/Docking/DockWindow.cs b/Source/Editor/GUI/Docking/DockWindow.cs index 8601b9c53..374885f01 100644 --- a/Source/Editor/GUI/Docking/DockWindow.cs +++ b/Source/Editor/GUI/Docking/DockWindow.cs @@ -489,7 +489,7 @@ namespace FlaxEditor.GUI.Docking { var style = Style.Current; if (style?.FontMedium != null) - _titleSize = style.FontMedium.First().MeasureText(_title); + _titleSize = style.FontMedium.MeasureText(_title); } base.PerformLayoutBeforeChildren(); diff --git a/Source/Editor/GUI/ItemsListContextMenu.cs b/Source/Editor/GUI/ItemsListContextMenu.cs index ce69f3544..b823cc907 100644 --- a/Source/Editor/GUI/ItemsListContextMenu.cs +++ b/Source/Editor/GUI/ItemsListContextMenu.cs @@ -87,8 +87,8 @@ namespace FlaxEditor.GUI var font = style.FontSmall; for (int i = 0; i < ranges.Length; i++) { - var start = font.First().GetCharPosition(Name, ranges[i].StartIndex); - var end = font.First().GetCharPosition(Name, ranges[i].EndIndex); + var start = font.GetCharPosition(Name, ranges[i].StartIndex); + var end = font.GetCharPosition(Name, ranges[i].EndIndex); _highlights.Add(new Rectangle(start.X + 2, 0, end.X - start.X, Height)); } Visible = true; diff --git a/Source/Editor/GUI/MainMenu.cs b/Source/Editor/GUI/MainMenu.cs index b313fed9f..aadfbf0a7 100644 --- a/Source/Editor/GUI/MainMenu.cs +++ b/Source/Editor/GUI/MainMenu.cs @@ -76,7 +76,7 @@ namespace FlaxEditor.GUI var windowIcon = FlaxEngine.Content.LoadAsyncInternal(EditorAssets.WindowIcon); FontAsset windowIconsFont = FlaxEngine.Content.LoadAsyncInternal(EditorAssets.WindowIconsFont); - Font iconFont = windowIconsFont?.CreateFont(9); + MultiFont iconFont = new MultiFontReference([windowIconsFont], 9).GetMultiFont(); _window = mainWindow.RootWindow.Window; _window.HitTest += OnHitTest; @@ -108,7 +108,7 @@ namespace FlaxEditor.GUI _closeButton = new Button { Text = ((char)EditorAssets.SegMDL2Icons.ChromeClose).ToString(), - Font = new FontReference(iconFont), + Font = new MultiFontReference(iconFont), BackgroundColor = Color.Transparent, BorderColor = Color.Transparent, BorderColorHighlighted = Color.Transparent, @@ -124,7 +124,7 @@ namespace FlaxEditor.GUI _minimizeButton = new Button { Text = ((char)EditorAssets.SegMDL2Icons.ChromeMinimize).ToString(), - Font = new FontReference(iconFont), + Font = new MultiFontReference(iconFont), BackgroundColor = Color.Transparent, BorderColor = Color.Transparent, BorderColorHighlighted = Color.Transparent, @@ -139,7 +139,7 @@ namespace FlaxEditor.GUI _maximizeButton = new Button { Text = ((char)(_window.IsMaximized ? EditorAssets.SegMDL2Icons.ChromeRestore : EditorAssets.SegMDL2Icons.ChromeMaximize)).ToString(), - Font = new FontReference(iconFont), + Font = new MultiFontReference(iconFont), BackgroundColor = Color.Transparent, BorderColor = Color.Transparent, BorderColorHighlighted = Color.Transparent, diff --git a/Source/Editor/GUI/MainMenuButton.cs b/Source/Editor/GUI/MainMenuButton.cs index 76687f8b9..3bc57479c 100644 --- a/Source/Editor/GUI/MainMenuButton.cs +++ b/Source/Editor/GUI/MainMenuButton.cs @@ -102,8 +102,8 @@ namespace FlaxEditor.GUI var style = Style.Current; float width = 18; - if (style.FontMedium.First()) - width += style.FontMedium.First().MeasureText(Text).X; + if (style.FontMedium) + width += style.FontMedium.MeasureText(Text).X; Width = width; } diff --git a/Source/Editor/GUI/NavigationButton.cs b/Source/Editor/GUI/NavigationButton.cs index 77f8a7656..6fd17332c 100644 --- a/Source/Editor/GUI/NavigationButton.cs +++ b/Source/Editor/GUI/NavigationButton.cs @@ -66,9 +66,9 @@ namespace FlaxEditor.GUI { var style = Style.Current; - if (style.FontMedium.First()) + if (style.FontMedium) { - Width = style.FontMedium.First().MeasureText(Text).X + 2 * DefaultMargin; + Width = style.FontMedium.MeasureText(Text).X + 2 * DefaultMargin; } } } diff --git a/Source/Editor/GUI/Row.cs b/Source/Editor/GUI/Row.cs index 458138aea..7533dfb17 100644 --- a/Source/Editor/GUI/Row.cs +++ b/Source/Editor/GUI/Row.cs @@ -39,8 +39,8 @@ namespace FlaxEditor.GUI { Depth = -1; - if (Height < Style.Current.FontMedium.First().Height) - Height = Style.Current.FontMedium.First().Height + 4; + if (Height < Style.Current.FontMedium.MaxHeight) + Height = Style.Current.FontMedium.MaxHeight + 4; } /// diff --git a/Source/Editor/GUI/Table.cs b/Source/Editor/GUI/Table.cs index fed33a336..54656a6c2 100644 --- a/Source/Editor/GUI/Table.cs +++ b/Source/Editor/GUI/Table.cs @@ -130,7 +130,7 @@ namespace FlaxEditor.GUI Render2D.FillRectangle(rect, column.TitleBackgroundColor); var style = Style.Current; - var font = column.TitleFont ?? style.FontMedium.First(); + var font = column.TitleFont ?? style.FontMedium; Render2D.DrawText(font, column.Title, rect, column.TitleColor, TextAlignment.Center, TextAlignment.Center); if (columnIndex < _columns.Length - 1) diff --git a/Source/Editor/GUI/Timeline/GUI/PositionHandle.cs b/Source/Editor/GUI/Timeline/GUI/PositionHandle.cs index bedb61a5e..791fb7133 100644 --- a/Source/Editor/GUI/Timeline/GUI/PositionHandle.cs +++ b/Source/Editor/GUI/Timeline/GUI/PositionHandle.cs @@ -2,6 +2,7 @@ using System; using System.Globalization; +using System.Linq; using FlaxEngine; using FlaxEngine.GUI; @@ -36,16 +37,16 @@ namespace FlaxEditor.GUI.Timeline.GUI string labelText; switch (_timeline.TimeShowMode) { - case Timeline.TimeShowModes.Frames: - labelText = _timeline.CurrentFrame.ToString("###0", CultureInfo.InvariantCulture); - break; - case Timeline.TimeShowModes.Seconds: - labelText = _timeline.CurrentTime.ToString("###0.##'s'", CultureInfo.InvariantCulture); - break; - case Timeline.TimeShowModes.Time: - labelText = TimeSpan.FromSeconds(_timeline.CurrentTime).ToString("g"); - break; - default: throw new ArgumentOutOfRangeException(); + case Timeline.TimeShowModes.Frames: + labelText = _timeline.CurrentFrame.ToString("###0", CultureInfo.InvariantCulture); + break; + case Timeline.TimeShowModes.Seconds: + labelText = _timeline.CurrentTime.ToString("###0.##'s'", CultureInfo.InvariantCulture); + break; + case Timeline.TimeShowModes.Time: + labelText = TimeSpan.FromSeconds(_timeline.CurrentTime).ToString("g"); + break; + default: throw new ArgumentOutOfRangeException(); } var color = (_timeline.IsMovingPositionHandle ? style.ProgressNormal : style.Foreground).AlphaMultiplied(0.6f); Matrix3x3.RotationZ(Mathf.PiOverTwo, out var m1); diff --git a/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs b/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs index 928129917..63787df2c 100644 --- a/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs @@ -345,7 +345,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks if (_previewValue != null) { // Based on Track.Draw for track text placement - var left = _xOffset + 16 + Style.Current.FontSmall.First().MeasureText(Title ?? Name).X; + var left = _xOffset + 16 + Style.Current.FontSmall.MeasureText(Title ?? Name).X; if (Icon.IsValid) left += 18; if (IsExpanded) diff --git a/Source/Editor/GUI/ToolStripButton.cs b/Source/Editor/GUI/ToolStripButton.cs index b74c7c19f..d21fd5689 100644 --- a/Source/Editor/GUI/ToolStripButton.cs +++ b/Source/Editor/GUI/ToolStripButton.cs @@ -151,8 +151,8 @@ namespace FlaxEditor.GUI if (hasSprite) width += iconSize; - if (!string.IsNullOrEmpty(_text) && style.FontMedium.First()) - width += style.FontMedium.First().MeasureText(_text).X + (hasSprite ? DefaultMargin : 0); + if (!string.IsNullOrEmpty(_text) && style.FontMedium) + width += style.FontMedium.MeasureText(_text).X + (hasSprite ? DefaultMargin : 0); Width = width; } diff --git a/Source/Editor/GUI/Tree/TreeNode.cs b/Source/Editor/GUI/Tree/TreeNode.cs index f26929993..acb67ea8f 100644 --- a/Source/Editor/GUI/Tree/TreeNode.cs +++ b/Source/Editor/GUI/Tree/TreeNode.cs @@ -115,7 +115,7 @@ namespace FlaxEditor.GUI.Tree /// Gets or sets the font used to render text. /// [EditorDisplay("Style"), EditorOrder(2000)] - public FontReference TextFont { get; set; } + public MultiFontReference TextFont { get; set; } /// /// Gets or sets the color of the background when tree node is selected. @@ -318,7 +318,7 @@ namespace FlaxEditor.GUI.Tree BackgroundColorSelected = style.BackgroundSelected; BackgroundColorHighlighted = style.BackgroundHighlighted; BackgroundColorSelectedUnfocused = style.LightBackground; - TextFont = new FontReference(style.FontSmall.First()); + TextFont = new MultiFontReference(style.FontSmall); } /// @@ -573,7 +573,7 @@ namespace FlaxEditor.GUI.Tree { if (_textChanged) { - var font = TextFont.GetFont(); + var font = TextFont.GetMultiFont(); if (font) { _textWidth = font.MeasureText(_text).X; @@ -657,7 +657,7 @@ namespace FlaxEditor.GUI.Tree } // Draw text - Render2D.DrawText(TextFont.GetFont(), _text, textRect, _cachedTextColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(TextFont.GetMultiFont(), _text, textRect, _cachedTextColor, TextAlignment.Near, TextAlignment.Center); // Draw drag and drop effect if (IsDragOver && _tree.DraggedOverNode == this) diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index c96a52fb1..e43751fd1 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -166,15 +166,13 @@ namespace FlaxEditor.Options /// Gets or sets the output log text font. /// [EditorDisplay("Output Log", "Text Font"), EditorOrder(320), Tooltip("The output log text font.")] - public FontReference OutputLogTextFont + public MultiFontReference OutputLogTextFont { get => _outputLogFont; set { - if (value == null) - _outputLogFont = new FontReference(FlaxEngine.Content.LoadAsyncInternal(EditorAssets.InconsolataRegularFont), 10); - else if (!value.Font) - _outputLogFont.Font = FlaxEngine.Content.LoadAsyncInternal(EditorAssets.InconsolataRegularFont); + if (value == null || !value.Verify()) + _outputLogFont = new MultiFontReference(ConsoleFonts, 10); else _outputLogFont = value; } @@ -236,32 +234,31 @@ namespace FlaxEditor.Options [EditorDisplay("Cook & Run"), EditorOrder(500)] public int NumberOfGameClientsToLaunch = 1; - private static FontAsset DefaultFont => FlaxEngine.Content.LoadAsyncInternal(EditorAssets.PrimaryFont); - private static FontAsset _cjkFont => FlaxEngine.Content.LoadAsyncInternal(EditorAssets.CJKFont); - private FontReference _titleFont = new FontReference(DefaultFont, 18); - private FontReference _largeFont = new FontReference(DefaultFont, 14); - private FontReference _mediumFont = new FontReference(DefaultFont, 9); - private FontReference _smallFont = new FontReference(DefaultFont, 9); - private FontReference _outputLogFont = new FontReference(FlaxEngine.Content.LoadAsyncInternal(EditorAssets.InconsolataRegularFont), 10); + private static FontAsset[] DefaultFonts => + [FlaxEngine.Content.LoadAsyncInternal(EditorAssets.PrimaryFont), + FlaxEngine.Content.LoadAsyncInternal(EditorAssets.CjkFont)]; + + private static FontAsset[] ConsoleFonts => [FlaxEngine.Content.LoadAsyncInternal(EditorAssets.PrimaryFont), + FlaxEngine.Content.LoadAsyncInternal(EditorAssets.CjkFont)]; + + private MultiFontReference _titleFont = new MultiFontReference(DefaultFonts, 18); + private MultiFontReference _largeFont = new MultiFontReference(DefaultFonts, 14); + private MultiFontReference _mediumFont = new MultiFontReference(DefaultFonts, 9); + private MultiFontReference _smallFont = new MultiFontReference(DefaultFonts, 9); + private MultiFontReference _outputLogFont = new MultiFontReference(ConsoleFonts, 10); - public FontReference CJKFont - { - get => new FontReference(_cjkFont, 9); - } /// /// Gets or sets the title font for editor UI. /// [EditorDisplay("Fonts"), EditorOrder(600), Tooltip("The title font for editor UI.")] - public FontReference TitleFont + public MultiFontReference TitleFont { get => _titleFont; set { - if (value == null) - _titleFont = new FontReference(DefaultFont, 18); - else if (!value.Font) - _titleFont.Font = DefaultFont; + if (value == null || !value.Verify()) + _titleFont = new MultiFontReference(DefaultFonts, 18); else _titleFont = value; } @@ -271,15 +268,13 @@ namespace FlaxEditor.Options /// Gets or sets the large font for editor UI. /// [EditorDisplay("Fonts"), EditorOrder(610), Tooltip("The large font for editor UI.")] - public FontReference LargeFont + public MultiFontReference LargeFont { get => _largeFont; set { - if (value == null) - _largeFont = new FontReference(DefaultFont, 14); - else if (!value.Font) - _largeFont.Font = DefaultFont; + if (value == null || !value.Verify()) + _largeFont = new MultiFontReference(DefaultFonts, 14); else _largeFont = value; } @@ -289,15 +284,13 @@ namespace FlaxEditor.Options /// Gets or sets the medium font for editor UI. /// [EditorDisplay("Fonts"), EditorOrder(620), Tooltip("The medium font for editor UI.")] - public FontReference MediumFont + public MultiFontReference MediumFont { get => _mediumFont; set { - if (value == null) - _mediumFont = new FontReference(DefaultFont, 9); - else if (!value.Font) - _mediumFont.Font = DefaultFont; + if (value == null || !value.Verify()) + _mediumFont = new MultiFontReference(DefaultFonts, 9); else _mediumFont = value; } @@ -307,15 +300,13 @@ namespace FlaxEditor.Options /// Gets or sets the small font for editor UI. /// [EditorDisplay("Fonts"), EditorOrder(630), Tooltip("The small font for editor UI.")] - public FontReference SmallFont + public MultiFontReference SmallFont { get => _smallFont; set { - if (value == null) - _smallFont = new FontReference(DefaultFont, 9); - else if (!value.Font) - _smallFont.Font = DefaultFont; + if (value == null || !value.Verify()) + _smallFont = new MultiFontReference(DefaultFonts, 9); else _smallFont = value; } diff --git a/Source/Editor/Options/OptionsModule.cs b/Source/Editor/Options/OptionsModule.cs index 15059bd56..d138d7d0d 100644 --- a/Source/Editor/Options/OptionsModule.cs +++ b/Source/Editor/Options/OptionsModule.cs @@ -259,11 +259,10 @@ namespace FlaxEditor.Options }, // Fonts - FontTitle = options.Interface.TitleFont.GetFont(), - FontLarge = options.Interface.LargeFont.GetFont(), - FontMedium = new Font[] { options.Interface.MediumFont.GetFont(), options.Interface.CJKFont.GetFont() }, - FontSmall = new Font[] { options.Interface.SmallFont.GetFont(), options.Interface.CJKFont.GetFont() }, - FontCJK = options.Interface.CJKFont.GetFont(), + FontTitle = options.Interface.TitleFont.GetMultiFont(), + FontLarge = options.Interface.LargeFont.GetMultiFont(), + FontMedium = options.Interface.MediumFont.GetMultiFont(), + FontSmall = options.Interface.SmallFont.GetMultiFont(), // Icons ArrowDown = Editor.Icons.ArrowDown12, @@ -313,10 +312,10 @@ namespace FlaxEditor.Options ProgressNormal = new Color(0.03f, 0.65f, 0.12f, 1f), // Fonts - FontTitle = options.Interface.TitleFont.GetFont(), - FontLarge = options.Interface.LargeFont.GetFont(), - FontMedium = new Font[] { options.Interface.MediumFont.GetFont(), options.Interface.CJKFont.GetFont() }, - FontSmall = new Font[] { options.Interface.SmallFont.GetFont(), options.Interface.CJKFont.GetFont() }, + FontTitle = options.Interface.TitleFont.GetMultiFont(), + FontLarge = options.Interface.LargeFont.GetMultiFont(), + FontMedium = options.Interface.MediumFont.GetMultiFont(), + FontSmall = options.Interface.SmallFont.GetMultiFont(), // Icons ArrowDown = Editor.Icons.ArrowDown12, diff --git a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs index 9a6fe4fd2..f64e46385 100644 --- a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs +++ b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs @@ -142,8 +142,8 @@ namespace FlaxEditor.SceneGraph.GUI var textRect = TextRect; for (int i = 0; i < ranges.Length; i++) { - var start = font.First().GetCharPosition(text, ranges[i].StartIndex); - var end = font.First().GetCharPosition(text, ranges[i].EndIndex); + var start = font.GetCharPosition(text, ranges[i].StartIndex); + var end = font.GetCharPosition(text, ranges[i].EndIndex); _highlights.Add(new Rectangle(start.X + textRect.X, textRect.Y, end.X - start.X, textRect.Height)); } isThisVisible = true; diff --git a/Source/Editor/Surface/Archetypes/Animation.TransitionEditor.cs b/Source/Editor/Surface/Archetypes/Animation.TransitionEditor.cs index 84c2144b2..e1fdb0a42 100644 --- a/Source/Editor/Surface/Archetypes/Animation.TransitionEditor.cs +++ b/Source/Editor/Surface/Archetypes/Animation.TransitionEditor.cs @@ -39,7 +39,7 @@ namespace FlaxEditor.Surface.Archetypes // Title var title = new Label(2, 2, width - 4, 23.0f) { - Font = new FontReference(Style.Current.FontLarge), + Font = new MultiFontReference(Style.Current.FontLarge), Text = transition.SurfaceName, Parent = this }; diff --git a/Source/Editor/Surface/Archetypes/BehaviorTree.cs b/Source/Editor/Surface/Archetypes/BehaviorTree.cs index 38dd28e62..cca6856ae 100644 --- a/Source/Editor/Surface/Archetypes/BehaviorTree.cs +++ b/Source/Editor/Surface/Archetypes/BehaviorTree.cs @@ -101,7 +101,7 @@ namespace FlaxEditor.Surface.Archetypes _debugRelevant = Behavior.GetNodeDebugRelevancy(instance, behavior); _debugInfo = Behavior.GetNodeDebugInfo(instance, behavior); if (!string.IsNullOrEmpty(_debugInfo)) - _debugInfoSize = Style.Current.FontSmall.First().MeasureText(_debugInfo); + _debugInfoSize = Style.Current.FontSmall.MeasureText(_debugInfo); } } diff --git a/Source/Editor/Surface/Archetypes/Function.cs b/Source/Editor/Surface/Archetypes/Function.cs index 53950dad2..bc982a510 100644 --- a/Source/Editor/Surface/Archetypes/Function.cs +++ b/Source/Editor/Surface/Archetypes/Function.cs @@ -1407,7 +1407,7 @@ namespace FlaxEditor.Surface.Archetypes // Title var title = new Label(2, 2, width - 4, 23.0f) { - Font = new FontReference(Style.Current.FontLarge), + Font = new MultiFontReference(Style.Current.FontLarge), Text = "Edit function signature", Parent = this }; diff --git a/Source/Editor/Surface/AttributesEditor.cs b/Source/Editor/Surface/AttributesEditor.cs index 81b11bb68..c9e32e23a 100644 --- a/Source/Editor/Surface/AttributesEditor.cs +++ b/Source/Editor/Surface/AttributesEditor.cs @@ -83,7 +83,7 @@ namespace FlaxEditor.Surface // Title var title = new Label(2, 2, width - 4, 23.0f) { - Font = new FontReference(Style.Current.FontLarge), + Font = new MultiFontReference(Style.Current.FontLarge), Text = "Edit attributes", Parent = this }; diff --git a/Source/Editor/Surface/ContextMenu/VisjectCM.cs b/Source/Editor/Surface/ContextMenu/VisjectCM.cs index 930741807..0624d44b3 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCM.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCM.cs @@ -141,7 +141,7 @@ namespace FlaxEditor.Surface.ContextMenu }; // Title bar - var titleFontReference = new FontReference(Style.Current.FontLarge.Asset, 10); + var titleFontReference = new MultiFontReference(Style.Current.FontLarge); var titleLabel = new Label { Width = Width * 0.5f - 8f, diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs index 812bec5d2..207875a92 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs @@ -200,8 +200,8 @@ namespace FlaxEditor.Surface.ContextMenu var font = style.FontSmall; for (int i = 0; i < ranges.Length; i++) { - var start = font.First().GetCharPosition(_archetype.Title, ranges[i].StartIndex); - var end = font.First().GetCharPosition(_archetype.Title, ranges[i].EndIndex); + var start = font.GetCharPosition(_archetype.Title, ranges[i].StartIndex); + var end = font.GetCharPosition(_archetype.Title, ranges[i].EndIndex); _highlights.Add(new Rectangle(start.X + textRect.X, 0, end.X - start.X, Height)); if (ranges[i].StartIndex <= 0) @@ -222,8 +222,8 @@ namespace FlaxEditor.Surface.ContextMenu _highlights.Clear(); var style = Style.Current; var font = style.FontSmall; - var start = font.First().GetCharPosition(_archetype.Title, 0); - var end = font.First().GetCharPosition(_archetype.Title, _archetype.Title.Length - 1); + var start = font.GetCharPosition(_archetype.Title, 0); + var end = font.GetCharPosition(_archetype.Title, _archetype.Title.Length - 1); _highlights.Add(new Rectangle(start.X + textRect.X, 0, end.X - start.X, Height)); _isFullMatch = true; Visible = true; @@ -237,8 +237,8 @@ namespace FlaxEditor.Surface.ContextMenu _highlights.Clear(); var style = Style.Current; var font = style.FontSmall; - var start = font.First().GetCharPosition(_archetype.Title, 0); - var end = font.First().GetCharPosition(_archetype.Title, _archetype.Title.Length - 1); + var start = font.GetCharPosition(_archetype.Title, 0); + var end = font.GetCharPosition(_archetype.Title, _archetype.Title.Length - 1); _highlights.Add(new Rectangle(start.X + textRect.X, 0, end.X - start.X, Height)); Visible = true; @@ -286,7 +286,7 @@ namespace FlaxEditor.Surface.ContextMenu Render2D.DrawText(style.FontSmall, _archetype.Title, textRect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); if (_archetype.SubTitle != null) { - var titleLength = style.FontSmall.First().MeasureText(_archetype.Title).X; + var titleLength = style.FontSmall.MeasureText(_archetype.Title).X; var subTitleRect = new Rectangle(textRect.X + titleLength, textRect.Y, textRect.Width - titleLength, textRect.Height); Render2D.DrawText(style.FontSmall, _archetype.SubTitle, subTitleRect, style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); } diff --git a/Source/Editor/Surface/Elements/InputBox.cs b/Source/Editor/Surface/Elements/InputBox.cs index 611160611..2047bcd1a 100644 --- a/Source/Editor/Surface/Elements/InputBox.cs +++ b/Source/Editor/Surface/Elements/InputBox.cs @@ -1428,7 +1428,7 @@ namespace FlaxEditor.Surface.Elements if (_defaultValueEditor != null) { - _defaultValueEditor.Location = new Float2(X + Width + 8 + Style.Current.FontSmall.First().MeasureText(Text).X, Y); + _defaultValueEditor.Location = new Float2(X + Width + 8 + Style.Current.FontSmall.MeasureText(Text).X, Y); } } @@ -1635,7 +1635,7 @@ namespace FlaxEditor.Surface.Elements { if (DefaultValueEditors[i].CanUse(this, ref _currentType)) { - var bounds = new Rectangle(X + Width + 8 + Style.Current.FontSmall.First().MeasureText(Text).X, Y, 90, Height); + var bounds = new Rectangle(X + Width + 8 + Style.Current.FontSmall.MeasureText(Text).X, Y, 90, Height); _editor = DefaultValueEditors[i]; try { diff --git a/Source/Editor/Surface/SurfaceNode.cs b/Source/Editor/Surface/SurfaceNode.cs index 82a9ab2bd..b6436e542 100644 --- a/Source/Editor/Surface/SurfaceNode.cs +++ b/Source/Editor/Surface/SurfaceNode.cs @@ -200,7 +200,7 @@ namespace FlaxEditor.Surface continue; if (child is InputBox inputBox) { - var boxWidth = boxLabelFont.First().MeasureText(inputBox.Text).X + 20; + var boxWidth = boxLabelFont.MeasureText(inputBox.Text).X + 20; if (inputBox.DefaultValueEditor != null) boxWidth += inputBox.DefaultValueEditor.Width + 4; leftWidth = Mathf.Max(leftWidth, boxWidth); @@ -208,7 +208,7 @@ namespace FlaxEditor.Surface } else if (child is OutputBox outputBox) { - rightWidth = Mathf.Max(rightWidth, boxLabelFont.First().MeasureText(outputBox.Text).X + 20); + rightWidth = Mathf.Max(rightWidth, boxLabelFont.MeasureText(outputBox.Text).X + 20); rightHeight = Mathf.Max(rightHeight, outputBox.Archetype.Position.Y - Constants.NodeMarginY - Constants.NodeHeaderSize + 20.0f); } else if (child is Control control) diff --git a/Source/Editor/Tools/Foliage/FoliageTab.cs b/Source/Editor/Tools/Foliage/FoliageTab.cs index e88972b5c..1badb8c0c 100644 --- a/Source/Editor/Tools/Foliage/FoliageTab.cs +++ b/Source/Editor/Tools/Foliage/FoliageTab.cs @@ -148,7 +148,7 @@ namespace FlaxEditor.Tools.Foliage Parent = _noFoliagePanel, Enabled = false }; - var textSize = Style.Current.FontMedium.First().MeasureText(buttonText); + var textSize = Style.Current.FontMedium.MeasureText(buttonText); if (_createNewFoliage.Width < textSize.X) { _createNewFoliage.LocalX -= (textSize.X - _createNewFoliage.Width) / 2; diff --git a/Source/Editor/Tools/Terrain/CarveTab.cs b/Source/Editor/Tools/Terrain/CarveTab.cs index 0a43cd2d2..4ff85ca23 100644 --- a/Source/Editor/Tools/Terrain/CarveTab.cs +++ b/Source/Editor/Tools/Terrain/CarveTab.cs @@ -106,7 +106,7 @@ namespace FlaxEditor.Tools.Terrain Parent = _noTerrainPanel, Enabled = false }; - var textSize = Style.Current.FontMedium.First().MeasureText(buttonText); + var textSize = Style.Current.FontMedium.MeasureText(buttonText); if (_createTerrainButton.Width < textSize.X) { _createTerrainButton.LocalX -= (textSize.X - _createTerrainButton.Width) / 2; diff --git a/Source/Editor/Tools/Terrain/CreateTerrainDialog.cs b/Source/Editor/Tools/Terrain/CreateTerrainDialog.cs index 252891d44..cba52283d 100644 --- a/Source/Editor/Tools/Terrain/CreateTerrainDialog.cs +++ b/Source/Editor/Tools/Terrain/CreateTerrainDialog.cs @@ -96,7 +96,7 @@ namespace FlaxEditor.Tools.Terrain AnchorPreset = AnchorPresets.HorizontalStretchTop, Offsets = new Margin(0, 0, 0, 40), Parent = this, - Font = new FontReference(Style.Current.FontTitle) + Font = new MultiFontReference(Style.Current.FontTitle) }; var infoLabel = new Label { diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index f4ebfb51b..c49392d01 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -548,9 +548,9 @@ namespace FlaxEditor.Viewport #region Camera settings widget var largestText = "Relative Panning"; - var textSize = Style.Current.FontMedium.First().MeasureText(largestText); + var textSize = Style.Current.FontMedium.MeasureText(largestText); var xLocationForExtras = textSize.X + 5; - var cameraSpeedTextWidth = Style.Current.FontMedium.First().MeasureText("0.00").X; + var cameraSpeedTextWidth = Style.Current.FontMedium.MeasureText("0.00").X; // Camera Settings Widget _cameraWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); @@ -801,7 +801,7 @@ namespace FlaxEditor.Viewport #region View mode widget largestText = "Brightness"; - textSize = Style.Current.FontMedium.First().MeasureText(largestText); + textSize = Style.Current.FontMedium.MeasureText(largestText); xLocationForExtras = textSize.X + 5; var viewMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperLeft); diff --git a/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs b/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs index a58350ded..fe73048fd 100644 --- a/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs +++ b/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs @@ -163,8 +163,8 @@ namespace FlaxEditor.Viewport.Widgets { var style = Style.Current; - if (style != null && style.FontMedium.First()) - Width = CalculateButtonWidth(_forcedTextWidth > 0.0f ? _forcedTextWidth : style.FontMedium.First().MeasureText(_text).X, Icon.IsValid); + if (style != null && style.FontMedium) + Width = CalculateButtonWidth(_forcedTextWidth > 0.0f ? _forcedTextWidth : style.FontMedium.MeasureText(_text).X, Icon.IsValid); } } } diff --git a/Source/Editor/Windows/AboutDialog.cs b/Source/Editor/Windows/AboutDialog.cs index 63ad44f29..1a81a9421 100644 --- a/Source/Editor/Windows/AboutDialog.cs +++ b/Source/Editor/Windows/AboutDialog.cs @@ -41,7 +41,7 @@ namespace FlaxEditor.Windows var nameLabel = new Label(icon.Right + 10, icon.Top, 200, 34) { Text = "Flax Engine", - Font = new FontReference(Style.Current.FontTitle), + Font = new MultiFontReference(Style.Current.FontTitle), HorizontalAlignment = TextAlignment.Near, VerticalAlignment = TextAlignment.Center, Parent = this @@ -54,7 +54,7 @@ namespace FlaxEditor.Windows Parent = this }; var buttonText = "Copy version info"; - var fontSize = Style.Current.FontMedium.First().MeasureText(buttonText); + var fontSize = Style.Current.FontMedium.MeasureText(buttonText); var copyVersionButton = new Button(Width - fontSize.X - 8, 6, fontSize.X + 4, 20) { Text = buttonText, diff --git a/Source/Editor/Windows/Assets/FontWindow.cs b/Source/Editor/Windows/Assets/FontWindow.cs index ff4135165..2e2e14d99 100644 --- a/Source/Editor/Windows/Assets/FontWindow.cs +++ b/Source/Editor/Windows/Assets/FontWindow.cs @@ -144,7 +144,7 @@ namespace FlaxEditor.Windows.Assets protected override void OnAssetLinked() { Asset.WaitForLoaded(); - _textPreview.Font = new FontReference(Asset.CreateFont(30)); + _textPreview.Font = new MultiFontReference([Asset], 30); _inputText.Text = string.Format("This is a sample text using font {0}.", Asset.FamilyName); var options = Asset.Options; _proxy.Set(ref options); diff --git a/Source/Editor/Windows/ContentWindow.Search.cs b/Source/Editor/Windows/ContentWindow.Search.cs index a1072d158..f29dc0bd6 100644 --- a/Source/Editor/Windows/ContentWindow.Search.cs +++ b/Source/Editor/Windows/ContentWindow.Search.cs @@ -57,7 +57,7 @@ namespace FlaxEditor.Windows var textRect = new Rectangle(margin, 0, clientRect.Width - boxSize - 2.0f * margin, clientRect.Height); Render2D.PushClip(textRect); var textColor = TextColor; - Render2D.DrawText(Font.GetFont(), "View", textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, textScale); + Render2D.DrawText(Font.GetMultiFont(), "View", textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, textScale); Render2D.PopClip(); // Arrow diff --git a/Source/Editor/Windows/OutputLogWindow.cs b/Source/Editor/Windows/OutputLogWindow.cs index 6526d7c8a..ac4fea5ba 100644 --- a/Source/Editor/Windows/OutputLogWindow.cs +++ b/Source/Editor/Windows/OutputLogWindow.cs @@ -470,9 +470,9 @@ namespace FlaxEditor.Windows var wasEmpty = _output.TextLength == 0; // Cache fonts - _output.DefaultStyle.Font.GetFont(); - _output.WarningStyle.Font.GetFont(); - _output.ErrorStyle.Font.GetFont(); + _output.DefaultStyle.Font.GetMultiFont(); + _output.WarningStyle.Font.GetMultiFont(); + _output.ErrorStyle.Font.GetMultiFont(); // Generate the output log Span entries = CollectionsMarshal.AsSpan(_entries); @@ -536,7 +536,7 @@ namespace FlaxEditor.Windows } var prevBlockBottom = _textBlocks.Count == 0 ? 0.0f : _textBlocks[_textBlocks.Count - 1].Bounds.Bottom; var entryText = _textBuffer.ToString(startIndex, endIndex - startIndex); - var font = textBlock.Style.Font.GetFont(); + var font = textBlock.Style.Font.GetMultiFont(); if (!font) continue; var style = textBlock.Style; @@ -544,46 +544,52 @@ namespace FlaxEditor.Windows for (int j = 0; j < lines.Length; j++) { ref var line = ref lines[j]; - textBlock.Range.StartIndex = startIndex + line.FirstCharIndex; - textBlock.Range.EndIndex = startIndex + line.LastCharIndex + 1; - textBlock.Bounds = new Rectangle(new Float2(0.0f, prevBlockBottom), line.Size); - - if (textBlock.Range.Length > 0) + for (int k = 0; k < line.Blocks.Length; k++) { - // Parse compilation error/warning - var regexStart = line.FirstCharIndex; - if (j == 0) - regexStart += prefixLength; - var regexLength = line.LastCharIndex + 1 - regexStart; - if (regexLength > 0) + ref var block = ref line.Blocks[k]; + + textBlock.Range.StartIndex = startIndex + block.FirstCharIndex; + textBlock.Range.EndIndex = startIndex + block.LastCharIndex + 1; + textBlock.Bounds = new Rectangle(new Float2(block.Location.X, prevBlockBottom), block.Size); + + if (textBlock.Range.Length > 0) { - var match = _compileRegex.Match(entryText, regexStart, regexLength); - if (match.Success) + // Parse compilation error/warning + var regexStart = block.FirstCharIndex; + if (j == 0) + regexStart += prefixLength; + var regexLength = block.LastCharIndex + 1 - regexStart; + if (regexLength > 0) { - switch (match.Groups["level"].Value) + var match = _compileRegex.Match(entryText, regexStart, regexLength); + if (match.Success) { - case "error": - textBlock.Style = _output.ErrorStyle; - break; - case "warning": - textBlock.Style = _output.WarningStyle; - break; + switch (match.Groups["level"].Value) + { + case "error": + textBlock.Style = _output.ErrorStyle; + break; + case "warning": + textBlock.Style = _output.WarningStyle; + break; + } + textBlock.Tag = new TextBlockTag + { + Type = TextBlockTag.Types.CodeLocation, + Url = match.Groups["path"].Value, + Line = int.Parse(match.Groups["line"].Value), + }; } - textBlock.Tag = new TextBlockTag - { - Type = TextBlockTag.Types.CodeLocation, - Url = match.Groups["path"].Value, - Line = int.Parse(match.Groups["line"].Value), - }; + // TODO: parsing hyperlinks with link + // TODO: parsing file paths with link } - // TODO: parsing hyperlinks with link - // TODO: parsing file paths with link } + + _textBlocks.Add(textBlock); + textBlock.Style = style; } prevBlockBottom += line.Size.Y; - _textBlocks.Add(textBlock); - textBlock.Style = style; } } diff --git a/Source/Editor/Windows/PluginsWindow.cs b/Source/Editor/Windows/PluginsWindow.cs index 372505795..d6c14f4e4 100644 --- a/Source/Editor/Windows/PluginsWindow.cs +++ b/Source/Editor/Windows/PluginsWindow.cs @@ -14,6 +14,7 @@ using FlaxEditor.GUI.Tabs; using FlaxEngine; using FlaxEngine.GUI; using FlaxEngine.Json; +using FlaxEngine.Utilities; namespace FlaxEditor.Windows { @@ -78,7 +79,7 @@ namespace FlaxEditor.Windows HorizontalAlignment = TextAlignment.Near, AnchorPreset = AnchorPresets.HorizontalStretchTop, Text = desc.Name, - Font = new FontReference(Style.Current.FontLarge), + Font = new MultiFontReference(Style.Current.FontLarge), Parent = this, Bounds = new Rectangle(tmp1, margin, Width - tmp1 - margin, 28), }; @@ -119,8 +120,8 @@ namespace FlaxEditor.Windows url = desc.HomepageUrl; else if (!string.IsNullOrEmpty(desc.RepositoryUrl)) url = desc.RepositoryUrl; - versionLabel.Font.Font.WaitForLoaded(); - var font = versionLabel.Font.GetFont(); + versionLabel.Font.ForEach(x => x.Font.WaitForLoaded()); + var font = versionLabel.Font.GetMultiFont(); var authorWidth = font.MeasureText(desc.Author).X + 8; var authorLabel = new ClickableLabel { @@ -391,7 +392,7 @@ namespace FlaxEditor.Windows } Editor.Log("Plugin project has been cloned."); - + try { // Start git submodule clone diff --git a/Source/Editor/Windows/Profiler/Timeline.cs b/Source/Editor/Windows/Profiler/Timeline.cs index 88019b329..917647f23 100644 --- a/Source/Editor/Windows/Profiler/Timeline.cs +++ b/Source/Editor/Windows/Profiler/Timeline.cs @@ -85,8 +85,8 @@ namespace FlaxEditor.Windows.Profiler Render2D.FillRectangle(bounds, color); Render2D.DrawRectangle(bounds, color * 0.5f); - if (_nameLength < 0 && style.FontMedium.First()) - _nameLength = style.FontMedium.First().MeasureText(_name).X; + if (_nameLength < 0 && style.FontMedium) + _nameLength = style.FontMedium.MeasureText(_name).X; if (_nameLength < bounds.Width + 4) { diff --git a/Source/Editor/Windows/ToolboxWindow.cs b/Source/Editor/Windows/ToolboxWindow.cs index e8fc1d56c..16b9165e1 100644 --- a/Source/Editor/Windows/ToolboxWindow.cs +++ b/Source/Editor/Windows/ToolboxWindow.cs @@ -273,8 +273,8 @@ namespace FlaxEditor.Windows var textRect = item.TextRect; for (int i = 0; i < ranges.Length; i++) { - var start = font.First().GetCharPosition(text, ranges[i].StartIndex); - var end = font.First().GetCharPosition(text, ranges[i].EndIndex); + var start = font.GetCharPosition(text, ranges[i].StartIndex); + var end = font.GetCharPosition(text, ranges[i].EndIndex); highlights.Add(new Rectangle(start.X + textRect.X, textRect.Y, end.X - start.X, textRect.Height)); } item.SetHighlights(highlights); diff --git a/Source/Engine/Render2D/Font.cpp b/Source/Engine/Render2D/Font.cpp index 277ad8ddd..b33b10899 100644 --- a/Source/Engine/Render2D/Font.cpp +++ b/Source/Engine/Render2D/Font.cpp @@ -293,249 +293,6 @@ void Font::ProcessText(const StringView& text, Array& outputLines } } -void Font::ProcessText(const Array& fonts, const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout) -{ - float cursorX = 0; - int32 kerning; - MultiFontLineCache tmpLine; - MultiFontSegmentCache tmpSegment; - FontCharacterEntry entry; - FontCharacterEntry previous; - int32 textLength = text.Length(); - float scale = layout.Scale / FontManager::FontScale; - float boundsWidth = layout.Bounds.GetWidth(); - float baseLinesDistanceScale = layout.BaseLinesGapScale * scale; - - tmpSegment.Location = Float2::Zero; - tmpSegment.Height = 0; - tmpSegment.FirstCharIndex = 0; - tmpSegment.LastCharIndex = -1; - - tmpLine.Location = Float2::Zero; - tmpLine.Size = Float2::Zero; - tmpLine.Segments = Array(); - - if (textLength == 0) { - return; - } - - int32 lastWrapCharIndex = INVALID_INDEX; - float lastWrapCharX = 0; - bool lastMoveLine = false; - // The index of the font used by the current segment - int32 currentFontIndex = GetCharFontIndex(fonts, text[0], 0); - // The maximum font height of the current line - float maxHeight = 0; - float maxAscender = 0; - - // Process each character to split text into single lines - for (int32 currentIndex = 0; currentIndex < textLength;) - { - bool moveLine = false; - bool moveSegment = false; - float xAdvance = 0; - int32 nextCharIndex = currentIndex + 1; - - // Submit line and segment if text ends - if (nextCharIndex == textLength) { - moveLine = moveSegment = true; - } - - // Cache current character - const Char currentChar = text[currentIndex]; - const bool isWhitespace = StringUtils::IsWhitespace(currentChar); - - // Check if character can wrap words - const bool isWrapChar = !StringUtils::IsAlnum(currentChar) || isWhitespace || StringUtils::IsUpper(currentChar) || (currentChar >= 0x3040 && currentChar <= 0x9FFF); - if (isWrapChar && currentIndex != 0) - { - lastWrapCharIndex = currentIndex; - lastWrapCharX = cursorX; - } - - int32 nextFontIndex = currentFontIndex; - // Check if it's a newline character - if (currentChar == '\n') - { - // Break line - moveLine = moveSegment = true; - tmpSegment.LastCharIndex++; - } - else - { - // Get character entry - if (nextCharIndex < textLength) { - nextFontIndex = GetCharFontIndex(fonts, text[nextCharIndex], currentFontIndex); - } - - // Get character entry - fonts[currentFontIndex]->GetCharacter(currentChar, entry); - maxHeight = Math::Max(maxHeight, static_cast(fonts[currentFontIndex]->GetHeight())); - maxAscender = Math::Max(maxAscender, static_cast(fonts[currentFontIndex]->GetAscender())); - - // Move segment if the font changes or text ends - if (nextFontIndex != currentFontIndex || nextCharIndex == textLength) { - moveSegment = true; - } - - // Get kerning, only when the font hasn't changed - if (!isWhitespace && previous.IsValid && !moveSegment) - { - kerning = fonts[currentFontIndex]->GetKerning(previous.Character, entry.Character); - } - else - { - kerning = 0; - } - previous = entry; - xAdvance = (kerning + entry.AdvanceX) * scale; - - // Check if character fits the line or skip wrapping - if (cursorX + xAdvance <= boundsWidth || layout.TextWrapping == TextWrapping::NoWrap) - { - // Move character - cursorX += xAdvance; - tmpSegment.LastCharIndex++; - } - else if (layout.TextWrapping == TextWrapping::WrapWords) - { - if (lastWrapCharIndex != INVALID_INDEX) - { - // Skip moving twice for the same character - int32 lastLineLastCharIndex = outputLines.HasItems() && outputLines.Last().Segments.HasItems() ? outputLines.Last().Segments.Last().LastCharIndex : -10000; - if (lastLineLastCharIndex == lastWrapCharIndex || lastLineLastCharIndex == lastWrapCharIndex - 1 || lastLineLastCharIndex == lastWrapCharIndex - 2) - { - currentIndex = nextCharIndex; - lastMoveLine = moveLine; - continue; - } - - // Move line - const Char wrapChar = text[lastWrapCharIndex]; - moveLine = true; - moveSegment = tmpSegment.FirstCharIndex < lastWrapCharIndex; - - cursorX = lastWrapCharX; - if (StringUtils::IsWhitespace(wrapChar)) - { - // Skip whitespaces - tmpSegment.LastCharIndex = lastWrapCharIndex - 1; - nextCharIndex = currentIndex = lastWrapCharIndex + 1; - } - else - { - tmpSegment.LastCharIndex = lastWrapCharIndex - 1; - nextCharIndex = currentIndex = lastWrapCharIndex; - } - } - } - else if (layout.TextWrapping == TextWrapping::WrapChars) - { - // Move line - moveLine = true; - moveSegment = tmpSegment.FirstCharIndex < currentChar; - nextCharIndex = currentIndex; - - // Skip moving twice for the same character - if (lastMoveLine) - break; - } - } - - if (moveSegment) { - // Add segment - tmpSegment.Height = baseLinesDistanceScale * fonts[currentFontIndex]->GetHeight(); - tmpSegment.LastCharIndex = Math::Max(tmpSegment.LastCharIndex, tmpSegment.FirstCharIndex); - tmpSegment.FontIndex = currentFontIndex; - tmpLine.Segments.Add(tmpSegment); - - // Reset segment - tmpSegment.Location.X = cursorX; - tmpSegment.FirstCharIndex = nextCharIndex; - tmpSegment.LastCharIndex = nextCharIndex - 1; - - currentFontIndex = nextFontIndex; - } - - // Check if move to another line - if (moveLine) - { - // Add line - tmpLine.Size.X = cursorX; - tmpLine.Size.Y = baseLinesDistanceScale * maxHeight; - tmpLine.MaxAscender = maxAscender; - outputLines.Add(tmpLine); - - // Reset line - tmpLine.Segments.Clear(); - tmpLine.Location.Y += baseLinesDistanceScale * maxHeight; - cursorX = 0; - tmpSegment.Location.X = cursorX; - lastWrapCharIndex = INVALID_INDEX; - lastWrapCharX = 0; - previous.IsValid = false; - - // Reset max font height - maxHeight = 0; - maxAscender = 0; - } - - currentIndex = nextCharIndex; - lastMoveLine = moveLine; - } - - // Check if an additional line should be created - if (text[textLength - 1] == '\n') - { - // Add line - tmpLine.Size.X = cursorX; - tmpLine.Size.Y = baseLinesDistanceScale * maxHeight; - outputLines.Add(tmpLine); - - tmpLine.Location.Y += baseLinesDistanceScale * maxHeight; - } - - // Check amount of lines - if (outputLines.IsEmpty()) - return; - - float totalHeight = tmpLine.Location.Y; - - Float2 offset = Float2::Zero; - if (layout.VerticalAlignment == TextAlignment::Center) - { - offset.Y += (layout.Bounds.GetHeight() - totalHeight) * 0.5f; - } - else if (layout.VerticalAlignment == TextAlignment::Far) - { - offset.Y += layout.Bounds.GetHeight() - totalHeight; - } - for (int32 i = 0; i < outputLines.Count(); i++) - { - MultiFontLineCache& line = outputLines[i]; - Float2 rootPos = line.Location + offset; - - // Fix upper left line corner to match desire text alignment - if (layout.HorizontalAlignment == TextAlignment::Center) - { - rootPos.X += (layout.Bounds.GetWidth() - line.Size.X) * 0.5f; - } - else if (layout.HorizontalAlignment == TextAlignment::Far) - { - rootPos.X += layout.Bounds.GetWidth() - line.Size.X; - } - - line.Location = rootPos; - - // Align all segments to center in case they have different heights - for (int32 j = 0; j < line.Segments.Count(); j++) - { - MultiFontSegmentCache& segment = line.Segments[j]; - segment.Location.Y += (line.MaxAscender - fonts[segment.FontIndex]->GetAscender()) / 2; - } - } -} - Float2 Font::MeasureText(const StringView& text, const TextLayoutOptions& layout) { // Check if there is no need to do anything @@ -643,7 +400,7 @@ Float2 Font::GetCharPosition(const StringView& text, int32 index, const TextLayo ASSERT(lines.HasItems()); float scale = layout.Scale / FontManager::FontScale; float baseLinesDistance = static_cast(_height) * layout.BaseLinesGapScale * scale; - Float2 rootOffset = layout.Bounds.Location + lines.First().Location; + Float2 rootOffset = layout.Bounds.Location; // Find line with that position FontCharacterEntry previous; @@ -682,10 +439,10 @@ Float2 Font::GetCharPosition(const StringView& text, int32 index, const TextLayo } // Position after last character in the last line - return rootOffset + Float2(lines.Last().Size.X, static_cast((lines.Count() - 1) * baseLinesDistance)); + return rootOffset + Float2(lines.Last().Location.X + lines.Last().Size.X, static_cast((lines.Count() - 1) * baseLinesDistance)); } -bool Font::ContainsChar(Char c) +bool Font::ContainsChar(Char c) const { return FT_Get_Char_Index(GetAsset()->GetFTFace(), c) > 0; } diff --git a/Source/Engine/Render2D/Font.h b/Source/Engine/Render2D/Font.h index 0425f2bc5..453872582 100644 --- a/Source/Engine/Render2D/Font.h +++ b/Source/Engine/Render2D/Font.h @@ -8,7 +8,6 @@ #include "Engine/Content/AssetReference.h" #include "Engine/Scripting/ScriptingObject.h" #include "TextLayoutOptions.h" -#include "MultiFont.h" class FontAsset; struct FontTextureAtlasSlot; @@ -341,15 +340,6 @@ public: /// The output lines list. void ProcessText(const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout); - /// - /// Processes text to get cached lines for rendering. - /// - /// The font list. - /// The input text. - /// The layout properties. - /// The output lines list. - static void ProcessText(const Array& fonts, const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout); - /// /// Processes text to get cached lines for rendering. /// @@ -406,17 +396,6 @@ public: /// The minimum size for that text and fot to render properly. API_FUNCTION() Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout); - /* - /// - /// Measures minimum size of the rectangle that will be needed to draw given text. - /// - /// The fonts to render with. - /// The input text to test. - /// The layout properties. - /// The minimum size for that text and fot to render properly. - API_FUNCTION() static Float2 MeasureText(const Array& fonts, const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout); - */ - /// /// Measures minimum size of the rectangle that will be needed to draw given text. /// @@ -504,18 +483,6 @@ public: /// The character position (upper left corner which can be used for a caret position). API_FUNCTION() Float2 GetCharPosition(const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout); - /* - /// - /// Calculates character position for given text and character index. - /// - /// The fonts to use. - /// The input text to test. - /// The text position to get coordinates of. - /// The text layout properties. - /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() static Float2 GetCharPosition(const Array& fonts, const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout); - */ - /// /// Calculates character position for given text and character index. /// @@ -557,28 +524,7 @@ public: /// /// The char to test. /// True if the font contains the glyph of the char, otherwise false. - API_FUNCTION() FORCE_INLINE bool ContainsChar(Char c); - - /// - /// Gets the index of the font that should be used to render the char - /// - /// The font list. - /// The char. - /// Number to return if char cannot be found. - /// - API_FUNCTION() FORCE_INLINE static int32 GetCharFontIndex(const Array& fonts, Char c, int32 missing = -1) { - int32 fontIndex = 0; - while (fontIndex < fonts.Count() && !fonts[fontIndex]->ContainsChar(c)) - { - fontIndex++; - } - - if (fontIndex == fonts.Count()) { - return missing; - } - - return fontIndex; - } + API_FUNCTION() FORCE_INLINE bool ContainsChar(Char c) const; /// /// Flushes the size of the face with the Free Type library backend. diff --git a/Source/Engine/Render2D/MultiFont.cpp b/Source/Engine/Render2D/MultiFont.cpp index 9844c12e0..4a510a9f8 100644 --- a/Source/Engine/Render2D/MultiFont.cpp +++ b/Source/Engine/Render2D/MultiFont.cpp @@ -1 +1,431 @@ #include "MultiFont.h" +#include "FontManager.h" +#include "Engine/Core/Math/Math.h" + +MultiFont::MultiFont(const Array& fonts) + : ManagedScriptingObject(SpawnParams(Guid::New(), Font::TypeInitializer)), + _fonts(fonts) +{ + +} + +void MultiFont::ProcessText(const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout) +{ + float cursorX = 0; + int32 kerning; + MultiFontLineCache tmpLine; + MultiFontBlockCache tmpBlock; + FontCharacterEntry entry; + FontCharacterEntry previous; + int32 textLength = text.Length(); + float scale = layout.Scale / FontManager::FontScale; + float boundsWidth = layout.Bounds.GetWidth(); + float baseLinesDistanceScale = layout.BaseLinesGapScale * scale; + + tmpBlock.Location = Float2::Zero; + tmpBlock.Size = Float2::Zero; + tmpBlock.FirstCharIndex = 0; + tmpBlock.LastCharIndex = -1; + + tmpLine.Location = Float2::Zero; + tmpLine.Size = Float2::Zero; + tmpLine.Blocks = Array(); + + if (textLength == 0) { + return; + } + + int32 lastWrapCharIndex = INVALID_INDEX; + float lastWrapCharX = 0; + bool lastMoveLine = false; + // The index of the font used by the current block + int32 currentFontIndex = GetCharFontIndex(text[0], 0); + // The maximum font height of the current line + float maxHeight = 0; + float maxAscender = 0; + float lastCursorX = 0; + + // Process each character to split text into single lines + for (int32 currentIndex = 0; currentIndex < textLength;) + { + bool moveLine = false; + bool moveBlock = false; + float xAdvance = 0; + int32 nextCharIndex = currentIndex + 1; + + // Submit line and block if text ends + if (nextCharIndex == textLength) { + moveLine = moveBlock = true; + } + + // Cache current character + const Char currentChar = text[currentIndex]; + const bool isWhitespace = StringUtils::IsWhitespace(currentChar); + + // Check if character can wrap words + const bool isWrapChar = !StringUtils::IsAlnum(currentChar) || isWhitespace || StringUtils::IsUpper(currentChar) || (currentChar >= 0x3040 && currentChar <= 0x9FFF); + if (isWrapChar && currentIndex != 0) + { + lastWrapCharIndex = currentIndex; + lastWrapCharX = cursorX; + } + + int32 nextFontIndex = currentFontIndex; + // Check if it's a newline character + if (currentChar == '\n') + { + // Break line + moveLine = moveBlock = true; + tmpBlock.LastCharIndex++; + } + else + { + // Get character entry + if (nextCharIndex < textLength) { + nextFontIndex = GetCharFontIndex(text[nextCharIndex], currentFontIndex); + } + + // Get character entry + _fonts[currentFontIndex]->GetCharacter(currentChar, entry); + maxHeight = Math::Max(maxHeight, static_cast(_fonts[currentFontIndex]->GetHeight())); + maxAscender = Math::Max(maxAscender, static_cast(_fonts[currentFontIndex]->GetAscender())); + + // Move block if the font changes or text ends + if (nextFontIndex != currentFontIndex || nextCharIndex == textLength) { + moveBlock = true; + } + + // Get kerning, only when the font hasn't changed + if (!isWhitespace && previous.IsValid && !moveBlock) + { + kerning = _fonts[currentFontIndex]->GetKerning(previous.Character, entry.Character); + } + else + { + kerning = 0; + } + previous = entry; + xAdvance = (kerning + entry.AdvanceX) * scale; + + // Check if character fits the line or skip wrapping + if (cursorX + xAdvance <= boundsWidth || layout.TextWrapping == TextWrapping::NoWrap) + { + // Move character + cursorX += xAdvance; + tmpBlock.LastCharIndex++; + } + else if (layout.TextWrapping == TextWrapping::WrapWords) + { + if (lastWrapCharIndex != INVALID_INDEX) + { + // Skip moving twice for the same character + int32 lastLineLastCharIndex = outputLines.HasItems() && outputLines.Last().Blocks.HasItems() ? outputLines.Last().Blocks.Last().LastCharIndex : -10000; + if (lastLineLastCharIndex == lastWrapCharIndex || lastLineLastCharIndex == lastWrapCharIndex - 1 || lastLineLastCharIndex == lastWrapCharIndex - 2) + { + currentIndex = nextCharIndex; + lastMoveLine = moveLine; + continue; + } + + // Move line + const Char wrapChar = text[lastWrapCharIndex]; + moveLine = true; + moveBlock = tmpBlock.FirstCharIndex < lastWrapCharIndex; + + cursorX = lastWrapCharX; + if (StringUtils::IsWhitespace(wrapChar)) + { + // Skip whitespaces + tmpBlock.LastCharIndex = lastWrapCharIndex - 1; + nextCharIndex = currentIndex = lastWrapCharIndex + 1; + } + else + { + tmpBlock.LastCharIndex = lastWrapCharIndex - 1; + nextCharIndex = currentIndex = lastWrapCharIndex; + } + } + } + else if (layout.TextWrapping == TextWrapping::WrapChars) + { + // Move line + moveLine = true; + moveBlock = tmpBlock.FirstCharIndex < currentChar; + nextCharIndex = currentIndex; + + // Skip moving twice for the same character + if (lastMoveLine) + break; + } + } + + if (moveBlock) { + // Add block + tmpBlock.Size.X = lastCursorX - cursorX; + tmpBlock.Size.Y = baseLinesDistanceScale * _fonts[currentFontIndex]->GetHeight(); + tmpBlock.LastCharIndex = Math::Max(tmpBlock.LastCharIndex, tmpBlock.FirstCharIndex); + tmpBlock.FontIndex = currentFontIndex; + tmpLine.Blocks.Add(tmpBlock); + + // Reset block + tmpBlock.Location.X = cursorX; + tmpBlock.FirstCharIndex = nextCharIndex; + tmpBlock.LastCharIndex = nextCharIndex - 1; + + currentFontIndex = nextFontIndex; + lastCursorX = cursorX; + } + + // Check if move to another line + if (moveLine) + { + // Add line + tmpLine.Size.X = cursorX; + tmpLine.Size.Y = baseLinesDistanceScale * maxHeight; + tmpLine.MaxAscender = maxAscender; + outputLines.Add(tmpLine); + + // Reset line + tmpLine.Blocks.Clear(); + tmpLine.Location.Y += baseLinesDistanceScale * maxHeight; + cursorX = 0; + tmpBlock.Location.X = cursorX; + lastWrapCharIndex = INVALID_INDEX; + lastWrapCharX = 0; + previous.IsValid = false; + + // Reset max font height + maxHeight = 0; + maxAscender = 0; + lastCursorX = 0; + } + + currentIndex = nextCharIndex; + lastMoveLine = moveLine; + } + + // Check if an additional line should be created + if (text[textLength - 1] == '\n') + { + // Add line + tmpLine.Size.X = cursorX; + tmpLine.Size.Y = baseLinesDistanceScale * maxHeight; + outputLines.Add(tmpLine); + + tmpLine.Location.Y += baseLinesDistanceScale * maxHeight; + } + + // Check amount of lines + if (outputLines.IsEmpty()) + return; + + float totalHeight = tmpLine.Location.Y; + + Float2 offset = Float2::Zero; + if (layout.VerticalAlignment == TextAlignment::Center) + { + offset.Y += (layout.Bounds.GetHeight() - totalHeight) * 0.5f; + } + else if (layout.VerticalAlignment == TextAlignment::Far) + { + offset.Y += layout.Bounds.GetHeight() - totalHeight; + } + for (int32 i = 0; i < outputLines.Count(); i++) + { + MultiFontLineCache& line = outputLines[i]; + Float2 rootPos = line.Location + offset; + + // Fix upper left line corner to match desire text alignment + if (layout.HorizontalAlignment == TextAlignment::Center) + { + rootPos.X += (layout.Bounds.GetWidth() - line.Size.X) * 0.5f; + } + else if (layout.HorizontalAlignment == TextAlignment::Far) + { + rootPos.X += layout.Bounds.GetWidth() - line.Size.X; + } + + line.Location = rootPos; + + // Align all blocks to center in case they have different heights + for (int32 j = 0; j < line.Blocks.Count(); j++) + { + MultiFontBlockCache& block = line.Blocks[j]; + block.Location.Y += (line.MaxAscender - _fonts[block.FontIndex]->GetAscender()) / 2; + } + } +} + +Float2 MultiFont::GetCharPosition(const StringView& text, int32 index, const TextLayoutOptions& layout) +{ + // Check if there is no need to do anything + if (text.IsEmpty()) + return layout.Bounds.Location; + + // Process text + Array lines; + ProcessText(text, lines, layout); + ASSERT(lines.HasItems()); + float scale = layout.Scale / FontManager::FontScale; + float baseLinesDistance = layout.BaseLinesGapScale * scale; + Float2 rootOffset = layout.Bounds.Location; + + // Find line with that position + FontCharacterEntry previous; + FontCharacterEntry entry; + for (int32 lineIndex = 0; lineIndex < lines.Count(); lineIndex++) + { + const MultiFontLineCache& line = lines[lineIndex]; + for (int32 blockIndex = 0; blockIndex < line.Blocks.Count(); blockIndex++) + { + const MultiFontBlockCache& block = line.Blocks[blockIndex]; + // Check if desire position is somewhere inside characters in line range + if (Math::IsInRange(index, block.FirstCharIndex, block.LastCharIndex)) + { + float x = line.Location.X + block.Location.X; + float y = line.Location.Y + block.Location.Y; + + // Check all characters in the line + for (int32 currentIndex = block.FirstCharIndex; currentIndex < index; currentIndex++) + { + // Cache current character + const Char currentChar = text[currentIndex]; + _fonts[block.FontIndex]->GetCharacter(currentChar, entry); + const bool isWhitespace = StringUtils::IsWhitespace(currentChar); + + // Apply kerning + if (!isWhitespace && previous.IsValid) + { + x += _fonts[block.FontIndex]->GetKerning(previous.Character, entry.Character); + } + previous = entry; + + // Move + x += entry.AdvanceX * scale; + } + + // Upper left corner of the character + return rootOffset + Float2(x, y); + } + } + } + + // Position after last character in the last line + return rootOffset + Float2(lines.Last().Location.X + lines.Last().Size.X, lines.Last().Location.Y); +} + +int32 MultiFont::HitTestText(const StringView& text, const Float2& location, const TextLayoutOptions& layout) +{ + // Check if there is no need to do anything + if (text.Length() <= 0) + return 0; + + // Process text + Array lines; + ProcessText(text, lines, layout); + ASSERT(lines.HasItems()); + float scale = layout.Scale / FontManager::FontScale; + + // Offset position to match lines origin space + Float2 rootOffset = layout.Bounds.Location + lines.First().Location; + Float2 testPoint = location - rootOffset; + + // Get block which may intersect with the position (it's possible because lines have fixed height) + int32 lineIndex = 0; + while (lineIndex < lines.Count()) + { + if (lines[lineIndex].Location.Y + lines[lineIndex].Size.Y >= location.Y) { + break; + } + + lineIndex++; + } + lineIndex = Math::Clamp(lineIndex, 0, lines.Count() - 1); + const MultiFontLineCache& line = lines[lineIndex]; + + int32 blockIndex = 0; + while (blockIndex < line.Blocks.Count() - 1) + { + if (line.Location.X + line.Blocks[blockIndex + 1].Location.X >= location.X) { + break; + } + + blockIndex++; + } + const MultiFontBlockCache& block = line.Blocks[blockIndex]; + float x = line.Location.X; + + // Check all characters in the line to find hit point + FontCharacterEntry previous; + FontCharacterEntry entry; + int32 smallestIndex = INVALID_INDEX; + float dst, smallestDst = MAX_float; + for (int32 currentIndex = block.FirstCharIndex; currentIndex <= block.LastCharIndex; currentIndex++) + { + // Cache current character + const Char currentChar = text[currentIndex]; + + _fonts[block.FontIndex]->GetCharacter(currentChar, entry); + const bool isWhitespace = StringUtils::IsWhitespace(currentChar); + + // Apply kerning + if (!isWhitespace && previous.IsValid) + { + x += _fonts[block.FontIndex]->GetKerning(previous.Character, entry.Character); + } + previous = entry; + + // Test + dst = Math::Abs(testPoint.X - x); + if (dst < smallestDst) + { + // Found closer character + smallestIndex = currentIndex; + smallestDst = dst; + } + else if (dst > smallestDst) + { + // Current char is worse so return the best result + return smallestIndex; + } + + // Move + x += entry.AdvanceX * scale; + } + + // Test line end edge + dst = Math::Abs(testPoint.X - x); + if (dst < smallestDst) + { + // Pointer is behind the last character in the line + smallestIndex = block.LastCharIndex; + + // Fix for last line + if (lineIndex == lines.Count() - 1) + smallestIndex++; + } + + return smallestIndex; +} + +Float2 MultiFont::MeasureText(const StringView& text, const TextLayoutOptions& layout) +{ + // Check if there is no need to do anything + if (text.IsEmpty()) + return Float2::Zero; + + // Process text + Array lines; + ProcessText(text, lines, layout); + + // Calculate bounds + Float2 max = Float2::Zero; + for (int32 i = 0; i < lines.Count(); i++) + { + const MultiFontLineCache& line = lines[i]; + max = Float2::Max(max, line.Location + line.Size); + } + + return max; +} + diff --git a/Source/Engine/Render2D/MultiFont.h b/Source/Engine/Render2D/MultiFont.h index b1da832d5..870b8e668 100644 --- a/Source/Engine/Render2D/MultiFont.h +++ b/Source/Engine/Render2D/MultiFont.h @@ -3,23 +3,28 @@ #include "Engine/Core/Collections/Array.h" #include "Engine/Core/Collections/Dictionary.h" #include "Font.h" +#include "FontAsset.h" + +struct TextRange; +class Font; +class FontAsset; /// -/// The font segment info generated during text processing. +/// The font block info generated during text processing. /// -API_STRUCT(NoDefault) struct MultiFontSegmentCache +API_STRUCT(NoDefault) struct MultiFontBlockCache { - DECLARE_SCRIPTING_TYPE_MINIMAL(MultiFontSegmentCache); + DECLARE_SCRIPTING_TYPE_MINIMAL(MultiFontBlockCache); /// - /// The root position of the segment (upper left corner), relative to line. + /// The root position of the block (upper left corner), relative to line. /// API_FIELD() Float2 Location; /// - /// The height of the current segment + /// The height of the current block /// - API_FIELD() float Height; + API_FIELD() Float2 Size; /// /// The first character index (from the input text). @@ -38,13 +43,13 @@ API_STRUCT(NoDefault) struct MultiFontSegmentCache }; template<> -struct TIsPODType +struct TIsPODType { enum { Value = true }; }; /// -/// Line of font segments info generated during text processing. +/// Line of font blocks info generated during text processing. /// API_STRUCT(NoDefault) struct MultiFontLineCache { @@ -61,17 +66,288 @@ API_STRUCT(NoDefault) struct MultiFontLineCache API_FIELD() Float2 Size; /// - /// The maximum ascendent of the line. + /// The maximum ascender of the line. /// API_FIELD() float MaxAscender; /// /// The index of the font to render with /// - API_FIELD() Array Segments; + API_FIELD() Array Blocks; }; API_CLASS(Sealed, NoSpawn) class FLAXENGINE_API MultiFont : public ManagedScriptingObject { DECLARE_SCRIPTING_TYPE_NO_SPAWN(MultiFont); +private: + Array _fonts; + +public: + MultiFont(const Array& fonts); + + API_FUNCTION() FORCE_INLINE static MultiFont* Create(const Array& fonts) { + return New(fonts); + } + + API_FUNCTION() FORCE_INLINE static MultiFont* Create(const Array& fontAssets, float size) { + Array fonts; + fonts.Resize(fontAssets.Count()); + for (int32 i = 0; i < fontAssets.Count(); i++) + { + fonts[i] = fontAssets[i]->CreateFont(size); + } + + return New(fonts); + } + + API_PROPERTY() FORCE_INLINE Array& GetFonts() { + return _fonts; + } + + API_PROPERTY() FORCE_INLINE void SetFonts(const Array& val) { + _fonts = val; + } + + API_PROPERTY() FORCE_INLINE int32 GetMaxHeight() { + int32 maxHeight = 0; + for (int32 i = 0; i < _fonts.Count(); i++) + { + if (_fonts[i]) { + maxHeight = Math::Max(maxHeight, _fonts[i]->GetHeight()); + } + } + + return maxHeight; + } + + API_PROPERTY() FORCE_INLINE int32 GetMaxAscender() { + int32 maxAsc = 0; + for (int32 i = 0; i < _fonts.Count(); i++) + { + if (_fonts[i]) { + maxAsc = Math::Max(maxAsc, _fonts[i]->GetAscender()); + } + } + + return maxAsc; + } + + /// + /// Processes text to get cached lines for rendering. + /// + /// The input text. + /// The layout properties. + /// The output lines list. + void ProcessText(const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout); + + /// + /// Processes text to get cached lines for rendering. + /// + /// The input text. + /// The layout properties. + /// The output lines list. + API_FUNCTION() Array ProcessText(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout) + { + Array lines; + ProcessText(text, lines, layout); + return lines; + } + + /// + /// Processes text to get cached lines for rendering. + /// + /// The input text. + /// The input text range (substring range of the input text parameter). + /// The layout properties. + /// The output lines list. + API_FUNCTION() Array ProcessText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) + { + Array lines; + ProcessText(textRange.Substring(text), lines, layout); + return lines; + } + + /// + /// Processes text to get cached lines for rendering. + /// + /// The input text. + /// The output lines list. + API_FUNCTION() FORCE_INLINE Array ProcessText(const StringView& text) + { + return ProcessText(text, TextLayoutOptions()); + } + + /// + /// Processes text to get cached lines for rendering. + /// + /// The input text. + /// The input text range (substring range of the input text parameter). + /// The output lines list. + API_FUNCTION() FORCE_INLINE Array ProcessText(const StringView& text, API_PARAM(Ref) const TextRange& textRange) + { + return ProcessText(textRange.Substring(text), TextLayoutOptions()); + } + + /// + /// Measures minimum size of the rectangle that will be needed to draw given text. + /// + /// The input text to test. + /// The layout properties. + /// The minimum size for that text and fot to render properly. + API_FUNCTION() Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout); + + /// + /// Measures minimum size of the rectangle that will be needed to draw given text. + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The layout properties. + /// The minimum size for that text and fot to render properly. + API_FUNCTION() Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) + { + return MeasureText(textRange.Substring(text), layout); + } + + /// + /// Measures minimum size of the rectangle that will be needed to draw given text + /// . + /// The input text to test. + /// The minimum size for that text and fot to render properly. + API_FUNCTION() FORCE_INLINE Float2 MeasureText(const StringView& text) + { + return MeasureText(text, TextLayoutOptions()); + } + + /// + /// Measures minimum size of the rectangle that will be needed to draw given text + /// . + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The minimum size for that text and fot to render properly. + API_FUNCTION() FORCE_INLINE Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextRange& textRange) + { + return MeasureText(textRange.Substring(text), TextLayoutOptions()); + } + + /// + /// Calculates hit character index at given location. + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The input location to test. + /// The text layout properties. + /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). + API_FUNCTION() int32 HitTestText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout) + { + return HitTestText(textRange.Substring(text), location, layout); + } + + /// + /// Calculates hit character index at given location. + /// + /// The input text to test. + /// The input location to test. + /// The text layout properties. + /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). + API_FUNCTION() int32 HitTestText(const StringView& text, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout); + + /// + /// Calculates hit character index at given location. + /// + /// The input text to test. + /// The input location to test. + /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). + API_FUNCTION() FORCE_INLINE int32 HitTestText(const StringView& text, const Float2& location) + { + return HitTestText(text, location, TextLayoutOptions()); + } + + /// + /// Calculates hit character index at given location. + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The input location to test. + /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). + API_FUNCTION() FORCE_INLINE int32 HitTestText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location) + { + return HitTestText(textRange.Substring(text), location, TextLayoutOptions()); + } + + /// + /// Calculates character position for given text and character index. + /// + /// The input text to test. + /// The text position to get coordinates of. + /// The text layout properties. + /// The character position (upper left corner which can be used for a caret position). + API_FUNCTION() Float2 GetCharPosition(const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout); + + /// + /// Calculates character position for given text and character index. + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The text position to get coordinates of. + /// The text layout properties. + /// The character position (upper left corner which can be used for a caret position). + API_FUNCTION() Float2 GetCharPosition(const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout) + { + return GetCharPosition(textRange.Substring(text), index, layout); + } + + /// + /// Calculates character position for given text and character index + /// + /// The input text to test. + /// The text position to get coordinates of. + /// The character position (upper left corner which can be used for a caret position). + API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(const StringView& text, int32 index) + { + return GetCharPosition(text, index, TextLayoutOptions()); + } + + /// + /// Calculates character position for given text and character index + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The text position to get coordinates of. + /// The character position (upper left corner which can be used for a caret position). + API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index) + { + return GetCharPosition(textRange.Substring(text), index, TextLayoutOptions()); + } + + /// + /// Gets the index of the font that should be used to render the char + /// + /// The font list. + /// The char. + /// Number to return if char cannot be found. + /// + API_FUNCTION() FORCE_INLINE int32 GetCharFontIndex(Char c, int32 missing = -1) { + int32 fontIndex = 0; + while (fontIndex < _fonts.Count() && _fonts[fontIndex] && !_fonts[fontIndex]->ContainsChar(c)) + { + fontIndex++; + } + + if (fontIndex == _fonts.Count()) { + return missing; + } + + return fontIndex; + } + + API_FUNCTION() FORCE_INLINE bool Verify() { + for (int32 i = 0; i < _fonts.Count(); i++) + { + if (!_fonts[i]) { + return false; + } + } + + return true; + } }; diff --git a/Source/Engine/Render2D/MultiFontReference.cs b/Source/Engine/Render2D/MultiFontReference.cs new file mode 100644 index 000000000..dd3f4d179 --- /dev/null +++ b/Source/Engine/Render2D/MultiFontReference.cs @@ -0,0 +1,74 @@ + + +using System.Collections.Generic; +using System.Linq; + +namespace FlaxEngine +{ + /// + /// Reference to multiple font references + /// + public class MultiFontReference : List + { + public MultiFontReference() + { + _cachedFont = null; + } + + public MultiFontReference(IEnumerable other) + { + AddRange(other); + _cachedFont = null; + } + + public MultiFontReference(MultiFontReference other) + { + AddRange(other); + _cachedFont = other._cachedFont; + } + + public MultiFontReference(MultiFontReference other, float size) + { + AddRange(other.Select(x => new FontReference(x) { Size = size })); + _cachedFont = null; + } + + public MultiFontReference(MultiFont other) + { + AddRange(other.Fonts.Select(x => new FontReference(x))); + _cachedFont = other; + } + + public MultiFontReference(FontAsset[] assets, float size) + { + AddRange(assets.Select(x => new FontReference(x, size))); + _cachedFont = null; + } + + [EditorOrder(0), Tooltip("The font asset to use as characters source.")] + public MultiFont GetMultiFont() + { + if (_cachedFont) + return _cachedFont; + var fontList = this.Where(x => x.Font).Select(x => x.GetFont()).ToArray(); + _cachedFont = MultiFont.Create(fontList); + return _cachedFont; + } + + public bool Verify() + { + foreach (var i in this) + { + if (!i.Font) + { + return false; + } + } + + return true; + } + + [NoSerialize] + private MultiFont _cachedFont; + } +} diff --git a/Source/Engine/Render2D/Render2D.cpp b/Source/Engine/Render2D/Render2D.cpp index 8a39d29be..c68c2445d 100644 --- a/Source/Engine/Render2D/Render2D.cpp +++ b/Source/Engine/Render2D/Render2D.cpp @@ -1370,10 +1370,11 @@ void Render2D::DrawText(Font* font, const StringView& text, const TextRange& tex DrawText(font, textRange.Substring(text), color, layout, customMaterial); } -void Render2D::DrawText(const Array& fonts, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial) +void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial) { RENDER2D_CHECK_RENDERING_STATE; + const Array& fonts = multiFont->GetFonts(); // Check if there is no need to do anything if (fonts.IsEmpty() || text.Length() < 0) return; @@ -1408,7 +1409,7 @@ void Render2D::DrawText(const Array& fonts, const StringView& text, const for (int32 currentIndex = 0; currentIndex < text.Length(); currentIndex++) { if (text[currentIndex] != '\n') { - int32 fontIndex = Font::GetCharFontIndex(fonts, text[currentIndex], 0); + int32 fontIndex = multiFont->GetCharFontIndex(text[currentIndex], 0); maxAscenders[lineIndex] = Math::Max(maxAscenders[lineIndex], static_cast(fonts[fontIndex]->GetAscender())); } else { @@ -1418,12 +1419,12 @@ void Render2D::DrawText(const Array& fonts, const StringView& text, const } lineIndex = 0; - // The following code cut the text into segments, according to the font used to render + // The following code cut the text into blocks, according to the font used to render Float2 pointer = location; - // The starting index of the current segment + // The starting index of the current block int32 startIndex = 0; - // The index of the font used by the current segment - int32 currentFontIndex = Font::GetCharFontIndex(fonts, text[0], 0); + // The index of the font used by the current block + int32 currentFontIndex = multiFont->GetCharFontIndex(text[0], 0); // The maximum font height of the current line float maxHeight = 0; for (int32 currentIndex = 0; currentIndex < text.Length(); currentIndex++) @@ -1431,13 +1432,13 @@ void Render2D::DrawText(const Array& fonts, const StringView& text, const // Cache current character const Char currentChar = text[currentIndex]; int32 nextCharIndex = currentIndex + 1; - bool moveSegment = false; + bool moveBlock = false; bool moveLine = false; int32 nextFontIndex = currentFontIndex; - // Submit segment if text ends + // Submit block if text ends if (nextCharIndex == text.Length()) { - moveSegment = true; + moveBlock = true; } // Check if it isn't a newline character @@ -1445,21 +1446,21 @@ void Render2D::DrawText(const Array& fonts, const StringView& text, const { // Get character entry if (nextCharIndex < text.Length()) { - nextFontIndex = Font::GetCharFontIndex(fonts, text[nextCharIndex], currentFontIndex); + nextFontIndex = multiFont->GetCharFontIndex(text[nextCharIndex], currentFontIndex); } if (nextFontIndex != currentFontIndex) { - moveSegment = true; + moveBlock = true; } } else { // Move - moveLine = moveSegment = true; + moveLine = moveBlock = true; } - if (moveSegment) { - // Render the pending segment before beginning the new segment + if (moveBlock) { + // Render the pending block before beginning the new block auto fontHeight = fonts[currentFontIndex]->GetHeight(); maxHeight = Math::Max(maxHeight, static_cast(fontHeight)); auto fontDescender = fonts[currentFontIndex]->GetDescender(); @@ -1533,22 +1534,23 @@ void Render2D::DrawText(const Array& fonts, const StringView& text, const lineIndex++; } - // Start new segment + // Start new block startIndex = nextCharIndex; currentFontIndex = nextFontIndex; } } } -void Render2D::DrawText(const Array& fonts, const StringView& text, const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial) +void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial) { - DrawText(fonts, textRange.Substring(text), color, location, customMaterial); + DrawText(multiFont, textRange.Substring(text), color, location, customMaterial); } -void Render2D::DrawText(const Array& fonts, const StringView& text, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) +void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) { RENDER2D_CHECK_RENDERING_STATE; + const Array& fonts = multiFont->GetFonts(); // Check if there is no need to do anything if (fonts.IsEmpty() || text.IsEmpty() || layout.Scale <= ZeroTolerance) return; @@ -1563,7 +1565,7 @@ void Render2D::DrawText(const Array& fonts, const StringView& text, const // Process text to get lines MultiFontLines.Clear(); - Font::ProcessText(fonts, text, MultiFontLines, layout); + multiFont->ProcessText(text, MultiFontLines, layout); // Render all lines FontCharacterEntry entry; @@ -1582,14 +1584,14 @@ void Render2D::DrawText(const Array& fonts, const StringView& text, const for (int32 lineIndex = 0; lineIndex < MultiFontLines.Count(); lineIndex++) { const MultiFontLineCache& line = MultiFontLines[lineIndex]; - for (int32 segmentIndex = 0; segmentIndex < line.Segments.Count(); segmentIndex++) + for (int32 blockIndex = 0; blockIndex < line.Blocks.Count(); blockIndex++) { - const MultiFontSegmentCache& segment = MultiFontLines[lineIndex].Segments[segmentIndex]; - auto fontHeight = fonts[segment.FontIndex]->GetHeight(); - auto fontDescender = fonts[segment.FontIndex]->GetDescender(); - Float2 pointer = line.Location + segment.Location; + const MultiFontBlockCache& block = MultiFontLines[lineIndex].Blocks[blockIndex]; + auto fontHeight = fonts[block.FontIndex]->GetHeight(); + auto fontDescender = fonts[block.FontIndex]->GetDescender(); + Float2 pointer = line.Location + block.Location; - for (int32 charIndex = segment.FirstCharIndex; charIndex <= segment.LastCharIndex; charIndex++) + for (int32 charIndex = block.FirstCharIndex; charIndex <= block.LastCharIndex; charIndex++) { Char c = text[charIndex]; if (c == '\n') @@ -1598,7 +1600,7 @@ void Render2D::DrawText(const Array& fonts, const StringView& text, const } // Get character entry - fonts[segment.FontIndex]->GetCharacter(c, entry); + fonts[block.FontIndex]->GetCharacter(c, entry); // Check if need to select/change font atlas (since characters even in the same font may be located in different atlases) if (fontAtlas == nullptr || entry.TextureIndex != fontAtlasIndex) @@ -1623,7 +1625,7 @@ void Render2D::DrawText(const Array& fonts, const StringView& text, const const bool isWhitespace = StringUtils::IsWhitespace(c); if (!isWhitespace && previous.IsValid) { - kerning = fonts[segment.FontIndex]->GetKerning(previous.Character, entry.Character); + kerning = fonts[block.FontIndex]->GetKerning(previous.Character, entry.Character); } else { @@ -1659,9 +1661,9 @@ void Render2D::DrawText(const Array& fonts, const StringView& text, const } } -void Render2D::DrawText(const Array& fonts, const StringView& text, const TextRange& textRange, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) +void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const TextRange& textRange, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) { - DrawText(fonts, textRange.Substring(text), color, layout, customMaterial); + DrawText(multiFont, textRange.Substring(text), color, layout, customMaterial); } FORCE_INLINE bool NeedAlphaWithTint(const Color& color) @@ -2165,22 +2167,22 @@ void Render2D::DrawBezier(const Float2& p1, const Float2& p2, const Float2& p3, { RENDER2D_CHECK_RENDERING_STATE; - // Find amount of segments to use + // Find amount of blocks to use const Float2 d1 = p2 - p1; const Float2 d2 = p3 - p2; const Float2 d3 = p4 - p3; const float len = d1.Length() + d2.Length() + d3.Length(); - const int32 segmentCount = Math::Clamp(Math::CeilToInt(len * 0.05f), 1, 100); - const float segmentCountInv = 1.0f / segmentCount; + const int32 blockCount = Math::Clamp(Math::CeilToInt(len * 0.05f), 1, 100); + const float blockCountInv = 1.0f / blockCount; - // Draw segmented curve + // Draw blocked curve Float2 p; AnimationUtils::Bezier(p1, p2, p3, p4, 0, p); Lines2.Clear(); Lines2.Add(p); - for (int32 i = 1; i <= segmentCount; i++) + for (int32 i = 1; i <= blockCount; i++) { - const float t = i * segmentCountInv; + const float t = i * blockCountInv; AnimationUtils::Bezier(p1, p2, p3, p4, t, p); Lines2.Add(p); } diff --git a/Source/Engine/Render2D/Render2D.cs b/Source/Engine/Render2D/Render2D.cs index a73556f7b..cad8380ab 100644 --- a/Source/Engine/Render2D/Render2D.cs +++ b/Source/Engine/Render2D/Render2D.cs @@ -164,7 +164,7 @@ namespace FlaxEngine /// Describes how wrap text inside a layout rectangle. /// The scale for distance one baseline from another. Default is 1. /// The text drawing scale. Default is 1. - public static void DrawText(Font[] fonts, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) + public static void DrawText(MultiFont fonts, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) { var layout = new TextLayoutOptions { @@ -175,6 +175,8 @@ namespace FlaxEngine Scale = scale, BaseLinesGapScale = baseLinesGapScale, }; + + DrawText(fonts, text, color, ref layout); } @@ -191,7 +193,7 @@ namespace FlaxEngine /// Describes how wrap text inside a layout rectangle. /// The scale for distance one baseline from another. Default is 1. /// The text drawing scale. Default is 1. - public static void DrawText(Font[] fonts, MaterialBase customMaterial, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) + public static void DrawText(MultiFont fonts, MaterialBase customMaterial, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) { var layout = new TextLayoutOptions { @@ -202,6 +204,7 @@ namespace FlaxEngine Scale = scale, BaseLinesGapScale = baseLinesGapScale, }; + DrawText(fonts, text, color, ref layout, customMaterial); } diff --git a/Source/Engine/Render2D/Render2D.h b/Source/Engine/Render2D/Render2D.h index 0ec88edc0..5050171b2 100644 --- a/Source/Engine/Render2D/Render2D.h +++ b/Source/Engine/Render2D/Render2D.h @@ -15,6 +15,7 @@ struct Matrix3x3; struct Viewport; struct TextRange; class Font; +class MultiFont; class GPUPipelineState; class GPUTexture; class GPUTextureView; @@ -224,7 +225,7 @@ public: /// The text color. /// The text location. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawText(const Array& fonts, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawText(MultiFont* multiFont, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); /// /// Draws a text with formatting. @@ -234,7 +235,7 @@ public: /// The text color. /// The text layout properties. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawText(const Array& fonts, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawText(MultiFont* multiFont, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); /// /// Draws a text with formatting. @@ -245,7 +246,7 @@ public: /// The text color. /// The text layout properties. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawText(const Array& fonts, const StringView& text, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawText(MultiFont* multiFont, const StringView& text, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); /// /// Draws a text with formatting. @@ -256,7 +257,7 @@ public: /// The text color. /// The text layout properties. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawText(const Array& fonts, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawText(MultiFont* multiFont, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); /// /// Fills a rectangle area. diff --git a/Source/Engine/Scripting/Scripting.cs b/Source/Engine/Scripting/Scripting.cs index 8e71c9f31..bc800f88a 100644 --- a/Source/Engine/Scripting/Scripting.cs +++ b/Source/Engine/Scripting/Scripting.cs @@ -294,15 +294,12 @@ namespace FlaxEngine style.DragWindow = style.BackgroundSelected * 0.7f; // Use optionally bundled default font (matches Editor) - var defaultFont = Content.LoadAsyncInternal("Editor/Fonts/Roboto-Regular"); - var cjkFont = Content.LoadAsyncInternal("NotoSansSC-Medium"); - if (defaultFont) - { - style.FontTitle = defaultFont.CreateFont(18); - style.FontLarge = defaultFont.CreateFont(14); - style.FontMedium = new Font[] { defaultFont.CreateFont(9), cjkFont.CreateFont(9) }; - style.FontSmall = new Font[] { defaultFont.CreateFont(9), cjkFont.CreateFont(9) }; - } + FontAsset[] defaultFont = [Content.LoadAsyncInternal("Editor/Fonts/Roboto-Regular"), Content.LoadAsyncInternal("Editor/Fonts/NotoSansSC-Regular")]; + + style.FontTitle = new MultiFontReference(defaultFont, 18).GetMultiFont(); + style.FontLarge = new MultiFontReference(defaultFont, 14).GetMultiFont(); + style.FontMedium = new MultiFontReference(defaultFont, 9).GetMultiFont(); + style.FontSmall = new MultiFontReference(defaultFont, 9).GetMultiFont(); Style.Current = style; } diff --git a/Source/Engine/UI/GUI/Common/Button.cs b/Source/Engine/UI/GUI/Common/Button.cs index 1b965b8d1..f21a29191 100644 --- a/Source/Engine/UI/GUI/Common/Button.cs +++ b/Source/Engine/UI/GUI/Common/Button.cs @@ -23,7 +23,7 @@ namespace FlaxEngine.GUI /// /// The font. /// - protected FontReference _font; + protected MultiFontReference _font; /// /// The text. @@ -44,7 +44,7 @@ namespace FlaxEngine.GUI /// Gets or sets the font used to draw button text. /// [EditorDisplay("Text Style"), EditorOrder(2022), ExpandGroups] - public FontReference Font + public MultiFontReference Font { get => _font; set => _font = value; @@ -156,7 +156,7 @@ namespace FlaxEngine.GUI var style = Style.Current; if (style != null) { - _font = new FontReference(style.FontMedium.First()); + _font = new MultiFontReference(style.FontMedium); TextColor = style.Foreground; BackgroundColor = style.BackgroundNormal; BorderColor = style.BorderNormal; @@ -262,7 +262,7 @@ namespace FlaxEngine.GUI Render2D.DrawRectangle(clientRect, borderColor, BorderThickness); // Draw text - Render2D.DrawText(_font?.GetFont(), TextMaterial, _text, clientRect, textColor, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(_font?.GetMultiFont(), TextMaterial, _text, clientRect, textColor, TextAlignment.Center, TextAlignment.Center); } /// diff --git a/Source/Engine/UI/GUI/Common/Dropdown.cs b/Source/Engine/UI/GUI/Common/Dropdown.cs index 21ca9cbea..2681aac1b 100644 --- a/Source/Engine/UI/GUI/Common/Dropdown.cs +++ b/Source/Engine/UI/GUI/Common/Dropdown.cs @@ -278,7 +278,7 @@ namespace FlaxEngine.GUI /// Gets or sets the font used to draw text. /// [EditorDisplay("Text Style"), EditorOrder(2021)] - public FontReference Font { get; set; } + public MultiFontReference Font { get; set; } /// /// Gets or sets the custom material used to render the text. It must has domain set to GUI and have a public texture parameter named Font used to sample font atlas texture with font characters data. @@ -359,7 +359,7 @@ namespace FlaxEngine.GUI : base(0, 0, 120, 18.0f) { var style = Style.Current; - Font = new FontReference(style.FontMedium.First()); + Font = new MultiFontReference(style.FontMedium); TextColor = style.Foreground; BackgroundColor = style.BackgroundNormal; BackgroundColorHighlighted = BackgroundColor; @@ -674,7 +674,7 @@ namespace FlaxEngine.GUI var textRect = new Rectangle(margin, 0, clientRect.Width - boxSize - 2.0f * margin, clientRect.Height); Render2D.PushClip(textRect); var textColor = TextColor; - Render2D.DrawText(Font.GetFont(), FontMaterial, _items[_selectedIndex], textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(Font.GetMultiFont(), FontMaterial, _items[_selectedIndex], textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center); Render2D.PopClip(); } diff --git a/Source/Engine/UI/GUI/Common/Label.cs b/Source/Engine/UI/GUI/Common/Label.cs index 5fa8ad21d..ccc1cc190 100644 --- a/Source/Engine/UI/GUI/Common/Label.cs +++ b/Source/Engine/UI/GUI/Common/Label.cs @@ -26,7 +26,7 @@ namespace FlaxEngine.GUI /// /// The font. /// - protected FontReference _font; + protected MultiFontReference _font; /// /// Gets or sets the text. @@ -86,7 +86,7 @@ namespace FlaxEngine.GUI /// Gets or sets the font. /// [EditorDisplay("Text Style"), EditorOrder(2024)] - public FontReference Font + public MultiFontReference Font { get => _font; set @@ -192,7 +192,7 @@ namespace FlaxEngine.GUI { AutoFocus = false; var style = Style.Current; - Font = new FontReference(style.FontMedium.First()); + Font = new MultiFontReference(style.FontMedium); TextColor = style.Foreground; TextColorHighlighted = style.Foreground; } @@ -203,7 +203,7 @@ namespace FlaxEngine.GUI { AutoFocus = false; var style = Style.Current; - Font = new FontReference(style.FontMedium.First()); + Font = new MultiFontReference(style.FontMedium); TextColor = style.Foreground; TextColorHighlighted = style.Foreground; } @@ -235,7 +235,7 @@ namespace FlaxEngine.GUI } } - Render2D.DrawText([_font.GetFont(), Style.Current.FontCJK], Material, _text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale); + Render2D.DrawText(_font.GetMultiFont(), Material, _text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale); if (ClipText) Render2D.PopClip(); @@ -246,7 +246,7 @@ namespace FlaxEngine.GUI { if (_autoWidth || _autoHeight || _autoFitText) { - var font = _font.GetFont(); + var font = _font.GetMultiFont(); if (font) { // Calculate text size diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs b/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs index 2270136ae..9523ad30a 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs @@ -1,5 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. +using System; using System.Collections.Generic; using System.Runtime.InteropServices; using FlaxEngine.Utilities; @@ -185,7 +186,7 @@ namespace FlaxEngine.GUI }; // Process text into text blocks (handle newlines etc.) - var font = textBlock.Style.Font.GetFont(); + var font = textBlock.Style.Font.GetMultiFont(); if (!font) return; var lines = font.ProcessText(_text, ref textBlock.Range); @@ -194,20 +195,27 @@ namespace FlaxEngine.GUI for (int i = 0; i < lines.Length; i++) { ref var line = ref lines[i]; - textBlock.Range = new TextRange - { - StartIndex = start + line.FirstCharIndex, - EndIndex = start + line.LastCharIndex, - }; + if (i != 0) { context.Caret.X = 0; OnLineAdded(ref context, textBlock.Range.StartIndex - 1); } - textBlock.Bounds = new Rectangle(context.Caret, line.Size); - textBlock.Bounds.X += line.Location.X; + for (int k = 0; k < line.Blocks.Length; k++) + { + ref var block = ref line.Blocks[k]; - context.AddTextBlock(ref textBlock); + textBlock.Range = new TextRange + { + StartIndex = start + block.FirstCharIndex, + EndIndex = start + block.LastCharIndex, + }; + + textBlock.Bounds = new Rectangle(context.Caret, block.Size); + textBlock.Bounds.X += block.Location.X; + + context.AddTextBlock(ref textBlock); + } } // Update the caret location @@ -236,9 +244,9 @@ namespace FlaxEngine.GUI var ascender = textBlock.Ascender; //if (ascender <= 0) { - var textBlockFont = textBlock.Style.Font.GetFont(); + var textBlockFont = textBlock.Style.Font.GetMultiFont(); if (textBlockFont) - ascender = textBlockFont.Ascender; + ascender = textBlockFont.MaxAscender; } lineAscender = Mathf.Max(lineAscender, ascender); lineSize = Float2.Max(lineSize, textBlockSize); @@ -259,9 +267,9 @@ namespace FlaxEngine.GUI var ascender = textBlock.Ascender; if (ascender <= 0) { - var textBlockFont = textBlock.Style.Font.GetFont(); + var textBlockFont = textBlock.Style.Font.GetMultiFont(); if (textBlockFont) - ascender = textBlockFont.Ascender; + ascender = textBlockFont.MaxAscender; } vOffset = lineAscender - ascender; textBlock.Bounds.Location.Y += vOffset; diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs b/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs index 044c65044..08637918d 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using FlaxEngine.Utilities; +using System.Linq; namespace FlaxEngine.GUI { @@ -10,9 +11,9 @@ namespace FlaxEngine.GUI { context.Caret.X = 0; var style = context.StyleStack.Peek(); - var font = style.Font.GetFont(); + var font = style.Font.GetMultiFont(); if (font) - context.Caret.Y += font.Height; + context.Caret.Y += font.MaxHeight; } private static void ProcessColor(ref ParsingContext context, ref HtmlTag tag) @@ -86,15 +87,14 @@ namespace FlaxEngine.GUI else { var style = context.StyleStack.Peek(); - style.Font = new FontReference(style.Font); - if (tag.Attributes.TryGetValue(string.Empty, out var fontName)) + style.Font = new MultiFontReference(style.Font); + if (tag.Attributes.TryGetValue("size", out var sizeText) && int.TryParse(sizeText, out var size) && tag.Attributes.TryGetValue(string.Empty, out var fontName)) { var font = (FontAsset)FindAsset(fontName, typeof(FontAsset)); if (font) - style.Font.Font = font; + style.Font = new MultiFontReference([font], size); } - if (tag.Attributes.TryGetValue("size", out var sizeText) && int.TryParse(sizeText, out var size)) - style.Font.Size = size; + context.StyleStack.Push(style); } } @@ -108,7 +108,7 @@ namespace FlaxEngine.GUI else { var style = context.StyleStack.Peek(); - style.Font = style.Font.GetBold(); + // style.Font = style.Font.GetBold(); context.StyleStack.Push(style); } } @@ -122,7 +122,7 @@ namespace FlaxEngine.GUI else { var style = context.StyleStack.Peek(); - style.Font = style.Font.GetItalic(); + // style.Font = style.Font.GetItalic(); context.StyleStack.Push(style); } } @@ -136,9 +136,9 @@ namespace FlaxEngine.GUI else { var style = context.StyleStack.Peek(); - style.Font = new FontReference(style.Font); - TryParseNumberTag(ref tag, string.Empty, style.Font.Size, out var size); - style.Font.Size = (int)size; + style.Font = new MultiFontReference(style.Font); + TryParseNumberTag(ref tag, string.Empty, style.Font.First().Size, out var size); + style.Font = new MultiFontReference(style.Font, (int)size); context.StyleStack.Push(style); } } @@ -173,9 +173,9 @@ namespace FlaxEngine.GUI imageBlock.Style.BackgroundBrush = image; // Setup size - var font = imageBlock.Style.Font.GetFont(); + var font = imageBlock.Style.Font.GetMultiFont(); if (font) - imageBlock.Bounds.Size = new Float2(font.Height); + imageBlock.Bounds.Size = new Float2(font.MaxHeight); imageBlock.Bounds.Size.X *= image.Size.X / image.Size.Y; // Keep original aspect ratio bool hasWidth = TryParseNumberTag(ref tag, "width", imageBlock.Bounds.Width, out var width); imageBlock.Bounds.Width = width; @@ -213,18 +213,18 @@ namespace FlaxEngine.GUI style.Alignment &= ~TextBlockStyle.Alignments.VerticalMask; switch (valign) { - case "top": - style.Alignment = TextBlockStyle.Alignments.Top; - break; - case "bottom": - style.Alignment = TextBlockStyle.Alignments.Bottom; - break; - case "middle": - style.Alignment = TextBlockStyle.Alignments.Middle; - break; - case "baseline": - style.Alignment = TextBlockStyle.Alignments.Baseline; - break; + case "top": + style.Alignment = TextBlockStyle.Alignments.Top; + break; + case "bottom": + style.Alignment = TextBlockStyle.Alignments.Bottom; + break; + case "middle": + style.Alignment = TextBlockStyle.Alignments.Middle; + break; + case "baseline": + style.Alignment = TextBlockStyle.Alignments.Baseline; + break; } } context.StyleStack.Push(style); @@ -245,15 +245,15 @@ namespace FlaxEngine.GUI style.Alignment &= ~TextBlockStyle.Alignments.VerticalMask; switch (valign) { - case "left": - style.Alignment = TextBlockStyle.Alignments.Left; - break; - case "right": - style.Alignment = TextBlockStyle.Alignments.Right; - break; - case "center": - style.Alignment = TextBlockStyle.Alignments.Center; - break; + case "left": + style.Alignment = TextBlockStyle.Alignments.Left; + break; + case "right": + style.Alignment = TextBlockStyle.Alignments.Right; + break; + case "center": + style.Alignment = TextBlockStyle.Alignments.Center; + break; } } context.StyleStack.Push(style); @@ -280,7 +280,7 @@ namespace FlaxEngine.GUI foreach (var id in ids) { var path = Content.GetEditorAssetPath(id); - if (!string.IsNullOrEmpty(path) && + if (!string.IsNullOrEmpty(path) && string.Equals(name, System.IO.Path.GetFileNameWithoutExtension(path), System.StringComparison.OrdinalIgnoreCase)) { return Content.LoadAsync(id, type); diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.cs b/Source/Engine/UI/GUI/Common/RichTextBox.cs index e6a82c986..78da9621c 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBox.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBox.cs @@ -46,7 +46,7 @@ namespace FlaxEngine.GUI var style = Style.Current; _textStyle = new TextBlockStyle { - Font = new FontReference(style.FontMedium.First()), + Font = new MultiFontReference(style.FontMedium), Color = style.Foreground, BackgroundSelectedBrush = new SolidColorBrush(style.BackgroundSelected), }; diff --git a/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs b/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs index 438a7e3d8..6247cb885 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs @@ -123,10 +123,10 @@ namespace FlaxEngine.GUI if (index <= 0) { ref TextBlock textBlock = ref textBlocks[0]; - var font = textBlock.Style.Font.GetFont(); + var font = textBlock.Style.Font.GetMultiFont(); if (font) { - height = font.Height / DpiScale; + height = font.MaxHeight / DpiScale; return textBlock.Bounds.UpperLeft; } } @@ -135,10 +135,10 @@ namespace FlaxEngine.GUI if (index >= _text.Length) { ref TextBlock textBlock = ref textBlocks[count - 1]; - var font = textBlock.Style.Font.GetFont(); + var font = textBlock.Style.Font.GetMultiFont(); if (font) { - height = font.Height / DpiScale; + height = font.MaxHeight / DpiScale; return textBlock.Bounds.UpperRight; } } @@ -150,10 +150,10 @@ namespace FlaxEngine.GUI if (textBlock.Range.Contains(index)) { - var font = textBlock.Style.Font.GetFont(); + var font = textBlock.Style.Font.GetMultiFont(); if (!font) break; - height = font.Height / DpiScale; + height = font.MaxHeight / DpiScale; return textBlock.Bounds.Location + font.GetCharPosition(_text, ref textBlock.Range, index - textBlock.Range.StartIndex); } } @@ -165,10 +165,10 @@ namespace FlaxEngine.GUI if (index >= textBlock.Range.EndIndex) { - var font = textBlock.Style.Font.GetFont(); + var font = textBlock.Style.Font.GetMultiFont(); if (!font) break; - height = font.Height / DpiScale; + height = font.MaxHeight / DpiScale; return textBlock.Bounds.UpperRight; } } @@ -193,7 +193,7 @@ namespace FlaxEngine.GUI if (containsY && (containsX || (i + 1 < count && textBlocks[i + 1].Bounds.Location.Y > textBlock.Bounds.Location.Y + 1.0f))) { - var font = textBlock.Style.Font.GetFont(); + var font = textBlock.Style.Font.GetMultiFont(); if (!font && textBlock.Range.Length > 0) break; return font.HitTestText(_text, ref textBlock.Range, location - textBlock.Bounds.Location) + textBlock.Range.StartIndex; @@ -281,7 +281,7 @@ namespace FlaxEngine.GUI } // Pick font - var font = textBlock.Style.Font.GetFont(); + var font = textBlock.Style.Font.GetMultiFont(); if (!font) continue; @@ -290,7 +290,7 @@ namespace FlaxEngine.GUI { var leftEdge = selection.StartIndex <= textBlock.Range.StartIndex ? textBlock.Bounds.UpperLeft : font.GetCharPosition(_text, selection.StartIndex); var rightEdge = selection.EndIndex >= textBlock.Range.EndIndex ? textBlock.Bounds.UpperRight : font.GetCharPosition(_text, selection.EndIndex); - float height = font.Height / DpiScale; + float height = font.MaxHeight / DpiScale; float alpha = Mathf.Min(1.0f, Mathf.Cos(_animateTime * BackgroundSelectedFlashSpeed) * 0.5f + 1.3f); alpha *= alpha; Color selectionColor = Color.White * alpha; @@ -305,7 +305,7 @@ namespace FlaxEngine.GUI ref TextBlock textBlock = ref textBlocks[i]; // Pick font - var font = textBlock.Style.Font.GetFont(); + var font = textBlock.Style.Font.GetMultiFont(); if (!font) continue; @@ -332,7 +332,7 @@ namespace FlaxEngine.GUI ref TextBlock textBlock = ref textBlocks[i]; // Pick font - var font = textBlock.Style.Font.GetFont(); + var font = textBlock.Style.Font.GetMultiFont(); if (!font) continue; @@ -340,7 +340,7 @@ namespace FlaxEngine.GUI if (textBlock.Style.UnderlineBrush != null) { var underLineHeight = 2.0f; - var height = font.Height / DpiScale; + var height = font.MaxHeight / DpiScale; var underlineRect = new Rectangle(textBlock.Bounds.Location.X, textBlock.Bounds.Location.Y + height - underLineHeight * 0.5f, textBlock.Bounds.Width, underLineHeight); textBlock.Style.UnderlineBrush.Draw(underlineRect, textBlock.Style.Color); } diff --git a/Source/Engine/UI/GUI/Common/TextBox.cs b/Source/Engine/UI/GUI/Common/TextBox.cs index 967617649..d9c7c1a80 100644 --- a/Source/Engine/UI/GUI/Common/TextBox.cs +++ b/Source/Engine/UI/GUI/Common/TextBox.cs @@ -40,7 +40,7 @@ namespace FlaxEngine.GUI /// Gets or sets the font. /// [EditorDisplay("Text Style"), EditorOrder(2024)] - public FontReference Font { get; set; } + public MultiFontReference Font { get; set; } /// /// Gets or sets the custom material used to render the text. It must has domain set to GUI and have a public texture parameter named Font used to sample font atlas texture with font characters data. @@ -90,7 +90,7 @@ namespace FlaxEngine.GUI _layout.Bounds = new Rectangle(DefaultMargin, 1, Width - 2 * DefaultMargin, Height - 2); var style = Style.Current; - Font = new FontReference(style.FontMedium.First()); + Font = new MultiFontReference(style.FontMedium); TextColor = style.Foreground; WatermarkTextColor = style.ForegroundDisabled; SelectionColor = style.BackgroundSelected; @@ -99,7 +99,7 @@ namespace FlaxEngine.GUI /// public override Float2 GetTextSize() { - var font = Font.GetFont(); + var font = Font.GetMultiFont(); if (font == null) { return Float2.Zero; @@ -111,21 +111,21 @@ namespace FlaxEngine.GUI /// public override Float2 GetCharPosition(int index, out float height) { - var font = Font.GetFont(); + var font = Font.GetMultiFont(); if (font == null) { height = Height; return Float2.Zero; } - height = font.Height / DpiScale; + height = font.MaxHeight / DpiScale; return font.GetCharPosition(_text, index, ref _layout); } /// public override int HitTestText(Float2 location) { - var font = Font.GetFont(); + var font = Font.GetMultiFont(); if (font == null) { return 0; @@ -148,7 +148,7 @@ namespace FlaxEngine.GUI // Cache data var rect = new Rectangle(Float2.Zero, Size); bool enabled = EnabledInHierarchy; - var font = Font.GetFont(); + var font = Font.GetMultiFont(); if (!font) return; @@ -172,7 +172,7 @@ namespace FlaxEngine.GUI { var leftEdge = font.GetCharPosition(_text, SelectionLeft, ref _layout); var rightEdge = font.GetCharPosition(_text, SelectionRight, ref _layout); - float fontHeight = font.Height / DpiScale; + float fontHeight = font.MaxHeight / DpiScale; // Draw selection background float alpha = Mathf.Min(1.0f, Mathf.Cos(_animateTime * BackgroundSelectedFlashSpeed) * 0.5f + 1.3f); diff --git a/Source/Engine/UI/GUI/Panels/DropPanel.cs b/Source/Engine/UI/GUI/Panels/DropPanel.cs index 33c2e1605..06256c9f8 100644 --- a/Source/Engine/UI/GUI/Panels/DropPanel.cs +++ b/Source/Engine/UI/GUI/Panels/DropPanel.cs @@ -130,7 +130,7 @@ namespace FlaxEngine.GUI /// Gets or sets the font used to render panel header text. /// [EditorDisplay("Header Text Style"), EditorOrder(2020), ExpandGroups] - public FontReference HeaderTextFont { get; set; } + public MultiFontReference HeaderTextFont { get; set; } /// /// Gets or sets the custom material used to render the text. It must has domain set to GUI and have a public texture parameter named Font used to sample font atlas texture with font characters data. @@ -238,7 +238,7 @@ namespace FlaxEngine.GUI var style = Style.Current; HeaderColor = style.BackgroundNormal; HeaderColorMouseOver = style.BackgroundHighlighted; - HeaderTextFont = new FontReference(style.FontMedium.First()); + HeaderTextFont = new MultiFontReference(style.FontMedium); HeaderTextColor = style.Foreground; ArrowImageOpened = new SpriteBrush(style.ArrowDown); ArrowImageClosed = new SpriteBrush(style.ArrowRight); @@ -375,7 +375,7 @@ namespace FlaxEngine.GUI textColor *= 0.6f; } - Render2D.DrawText(HeaderTextFont.GetFont(), HeaderTextMaterial, HeaderText, textRect, textColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(HeaderTextFont.GetMultiFont(), HeaderTextMaterial, HeaderText, textRect, textColor, TextAlignment.Near, TextAlignment.Center); if (!_isClosed && EnableContainmentLines) { diff --git a/Source/Engine/UI/GUI/Style.cs b/Source/Engine/UI/GUI/Style.cs index d3375a670..a8b87fb3a 100644 --- a/Source/Engine/UI/GUI/Style.cs +++ b/Source/Engine/UI/GUI/Style.cs @@ -14,68 +14,60 @@ namespace FlaxEngine.GUI /// public static Style Current { get; set; } - public Font FontCJK - { - get => _fontCJK?.GetFont(); - set => _fontCJK = new FontReference(value); - } - - private FontReference _fontCJK; - [Serialize] - private FontReference _fontTitle; + private MultiFontReference _fontTitle; /// /// The font title. /// [NoSerialize] [EditorOrder(10)] - public Font FontTitle + public MultiFont FontTitle { - get => _fontTitle?.GetFont(); - set => _fontTitle = new FontReference(value); + get => _fontTitle?.GetMultiFont(); + set => _fontTitle = new MultiFontReference(value); } [Serialize] - private FontReference _fontLarge; + private MultiFontReference _fontLarge; /// /// The font large. /// [NoSerialize] [EditorOrder(20)] - public Font FontLarge + public MultiFont FontLarge { - get => _fontLarge?.GetFont(); - set => _fontLarge = new FontReference(value); + get => _fontLarge?.GetMultiFont(); + set => _fontLarge = new MultiFontReference(value); } [Serialize] - private FontReference[] _fontMedium; + private MultiFontReference _fontMedium; /// /// The font medium. /// [NoSerialize] [EditorOrder(30)] - public Font[] FontMedium + public MultiFont FontMedium { - get => _fontMedium?.Select((x)=>x.GetFont()).ToArray(); - set => _fontMedium = value.Select((x)=>new FontReference(x)).ToArray(); + get => _fontMedium?.GetMultiFont(); + set => _fontMedium = new MultiFontReference(value); } [Serialize] - private FontReference[] _fontSmall; + private MultiFontReference _fontSmall; /// /// The font small. /// [NoSerialize] [EditorOrder(40)] - public Font[] FontSmall + public MultiFont FontSmall { - get => _fontSmall?.Select((x) => x.GetFont()).ToArray(); - set => _fontSmall = value.Select((x) => new FontReference(x)).ToArray(); + get => _fontSmall?.GetMultiFont(); + set => _fontSmall = new MultiFontReference(value); } /// diff --git a/Source/Engine/UI/GUI/TextBlockStyle.cs b/Source/Engine/UI/GUI/TextBlockStyle.cs index c4c87715f..08f74e7c8 100644 --- a/Source/Engine/UI/GUI/TextBlockStyle.cs +++ b/Source/Engine/UI/GUI/TextBlockStyle.cs @@ -64,7 +64,7 @@ namespace FlaxEngine.GUI /// The text font. /// [EditorOrder(0)] - public FontReference Font; + public MultiFontReference Font; /// /// The custom material for the text rendering (must be GUI domain). diff --git a/Source/Engine/UI/GUI/Tooltip.cs b/Source/Engine/UI/GUI/Tooltip.cs index e17b754c4..7a1026e55 100644 --- a/Source/Engine/UI/GUI/Tooltip.cs +++ b/Source/Engine/UI/GUI/Tooltip.cs @@ -244,6 +244,7 @@ namespace FlaxEngine.GUI TextAlignment.Center, TextWrapping.WrapWords ); + } /// @@ -256,14 +257,14 @@ namespace FlaxEngine.GUI // Calculate size of the tooltip var size = Float2.Zero; - if (style != null && style.FontMedium.First() && !string.IsNullOrEmpty(_currentText)) + if (style != null && style.FontMedium && !string.IsNullOrEmpty(_currentText)) { var layout = TextLayoutOptions.Default; layout.Bounds = new Rectangle(0, 0, MaxWidth, 10000000); layout.HorizontalAlignment = TextAlignment.Center; layout.VerticalAlignment = TextAlignment.Center; layout.TextWrapping = TextWrapping.WrapWords; - var items = style.FontMedium.First().ProcessText(_currentText, ref layout); + var items = style.FontMedium.ProcessText(_currentText, ref layout); for (int i = 0; i < items.Length; i++) { ref var item = ref items[i]; From 3365fb5afc6f7600acaa5e730a257bb378f2776f Mon Sep 17 00:00:00 2001 From: ExMatics HydrogenC <33123710+HydrogenC@users.noreply.github.com> Date: Thu, 30 Nov 2023 14:59:43 +0800 Subject: [PATCH 34/61] Fix console rendering bug --- Source/Editor/Options/InterfaceOptions.cs | 11 ++- Source/Editor/Windows/OutputLogWindow.cs | 74 +++++++++---------- .../UI/GUI/Common/RichTextBox.Parsing.cs | 33 ++++----- .../Engine/UI/GUI/Common/RichTextBox.Tags.cs | 27 +++---- Source/Engine/UI/GUI/Common/RichTextBox.cs | 2 +- .../Engine/UI/GUI/Common/RichTextBoxBase.cs | 28 +++---- Source/Engine/UI/GUI/TextBlockStyle.cs | 2 +- 7 files changed, 82 insertions(+), 95 deletions(-) diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index e43751fd1..91eebe4eb 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -166,13 +166,13 @@ namespace FlaxEditor.Options /// Gets or sets the output log text font. /// [EditorDisplay("Output Log", "Text Font"), EditorOrder(320), Tooltip("The output log text font.")] - public MultiFontReference OutputLogTextFont + public FontReference OutputLogTextFont { get => _outputLogFont; set { - if (value == null || !value.Verify()) - _outputLogFont = new MultiFontReference(ConsoleFonts, 10); + if (value == null || !value.Font) + _outputLogFont = new FontReference(ConsoleFont, 10); else _outputLogFont = value; } @@ -238,14 +238,13 @@ namespace FlaxEditor.Options [FlaxEngine.Content.LoadAsyncInternal(EditorAssets.PrimaryFont), FlaxEngine.Content.LoadAsyncInternal(EditorAssets.CjkFont)]; - private static FontAsset[] ConsoleFonts => [FlaxEngine.Content.LoadAsyncInternal(EditorAssets.PrimaryFont), - FlaxEngine.Content.LoadAsyncInternal(EditorAssets.CjkFont)]; + private static FontAsset ConsoleFont => FlaxEngine.Content.LoadAsyncInternal(EditorAssets.PrimaryFont); private MultiFontReference _titleFont = new MultiFontReference(DefaultFonts, 18); private MultiFontReference _largeFont = new MultiFontReference(DefaultFonts, 14); private MultiFontReference _mediumFont = new MultiFontReference(DefaultFonts, 9); private MultiFontReference _smallFont = new MultiFontReference(DefaultFonts, 9); - private MultiFontReference _outputLogFont = new MultiFontReference(ConsoleFonts, 10); + private FontReference _outputLogFont = new FontReference(ConsoleFont, 10); /// diff --git a/Source/Editor/Windows/OutputLogWindow.cs b/Source/Editor/Windows/OutputLogWindow.cs index ac4fea5ba..f168d8d45 100644 --- a/Source/Editor/Windows/OutputLogWindow.cs +++ b/Source/Editor/Windows/OutputLogWindow.cs @@ -470,9 +470,9 @@ namespace FlaxEditor.Windows var wasEmpty = _output.TextLength == 0; // Cache fonts - _output.DefaultStyle.Font.GetMultiFont(); - _output.WarningStyle.Font.GetMultiFont(); - _output.ErrorStyle.Font.GetMultiFont(); + _output.DefaultStyle.Font.GetFont(); + _output.WarningStyle.Font.GetFont(); + _output.ErrorStyle.Font.GetFont(); // Generate the output log Span entries = CollectionsMarshal.AsSpan(_entries); @@ -536,7 +536,7 @@ namespace FlaxEditor.Windows } var prevBlockBottom = _textBlocks.Count == 0 ? 0.0f : _textBlocks[_textBlocks.Count - 1].Bounds.Bottom; var entryText = _textBuffer.ToString(startIndex, endIndex - startIndex); - var font = textBlock.Style.Font.GetMultiFont(); + var font = textBlock.Style.Font.GetFont(); if (!font) continue; var style = textBlock.Style; @@ -544,52 +544,46 @@ namespace FlaxEditor.Windows for (int j = 0; j < lines.Length; j++) { ref var line = ref lines[j]; - for (int k = 0; k < line.Blocks.Length; k++) + textBlock.Range.StartIndex = startIndex + line.FirstCharIndex; + textBlock.Range.EndIndex = startIndex + line.LastCharIndex + 1; + textBlock.Bounds = new Rectangle(new Float2(0.0f, prevBlockBottom), line.Size); + + if (textBlock.Range.Length > 0) { - ref var block = ref line.Blocks[k]; - - textBlock.Range.StartIndex = startIndex + block.FirstCharIndex; - textBlock.Range.EndIndex = startIndex + block.LastCharIndex + 1; - textBlock.Bounds = new Rectangle(new Float2(block.Location.X, prevBlockBottom), block.Size); - - if (textBlock.Range.Length > 0) + // Parse compilation error/warning + var regexStart = line.FirstCharIndex; + if (j == 0) + regexStart += prefixLength; + var regexLength = line.LastCharIndex + 1 - regexStart; + if (regexLength > 0) { - // Parse compilation error/warning - var regexStart = block.FirstCharIndex; - if (j == 0) - regexStart += prefixLength; - var regexLength = block.LastCharIndex + 1 - regexStart; - if (regexLength > 0) + var match = _compileRegex.Match(entryText, regexStart, regexLength); + if (match.Success) { - var match = _compileRegex.Match(entryText, regexStart, regexLength); - if (match.Success) + switch (match.Groups["level"].Value) { - switch (match.Groups["level"].Value) - { - case "error": - textBlock.Style = _output.ErrorStyle; - break; - case "warning": - textBlock.Style = _output.WarningStyle; - break; - } - textBlock.Tag = new TextBlockTag - { - Type = TextBlockTag.Types.CodeLocation, - Url = match.Groups["path"].Value, - Line = int.Parse(match.Groups["line"].Value), - }; + case "error": + textBlock.Style = _output.ErrorStyle; + break; + case "warning": + textBlock.Style = _output.WarningStyle; + break; } - // TODO: parsing hyperlinks with link - // TODO: parsing file paths with link + textBlock.Tag = new TextBlockTag + { + Type = TextBlockTag.Types.CodeLocation, + Url = match.Groups["path"].Value, + Line = int.Parse(match.Groups["line"].Value), + }; } + // TODO: parsing hyperlinks with link + // TODO: parsing file paths with link } - - _textBlocks.Add(textBlock); - textBlock.Style = style; } prevBlockBottom += line.Size.Y; + _textBlocks.Add(textBlock); + textBlock.Style = style; } } diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs b/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs index 9523ad30a..ab01df12b 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs @@ -186,7 +186,7 @@ namespace FlaxEngine.GUI }; // Process text into text blocks (handle newlines etc.) - var font = textBlock.Style.Font.GetMultiFont(); + var font = textBlock.Style.Font.GetFont(); if (!font) return; var lines = font.ProcessText(_text, ref textBlock.Range); @@ -195,27 +195,20 @@ namespace FlaxEngine.GUI for (int i = 0; i < lines.Length; i++) { ref var line = ref lines[i]; - + textBlock.Range = new TextRange + { + StartIndex = start + line.FirstCharIndex, + EndIndex = start + line.LastCharIndex, + }; if (i != 0) { context.Caret.X = 0; OnLineAdded(ref context, textBlock.Range.StartIndex - 1); } - for (int k = 0; k < line.Blocks.Length; k++) - { - ref var block = ref line.Blocks[k]; + textBlock.Bounds = new Rectangle(context.Caret, line.Size); + textBlock.Bounds.X += line.Location.X; - textBlock.Range = new TextRange - { - StartIndex = start + block.FirstCharIndex, - EndIndex = start + block.LastCharIndex, - }; - - textBlock.Bounds = new Rectangle(context.Caret, block.Size); - textBlock.Bounds.X += block.Location.X; - - context.AddTextBlock(ref textBlock); - } + context.AddTextBlock(ref textBlock); } // Update the caret location @@ -244,9 +237,9 @@ namespace FlaxEngine.GUI var ascender = textBlock.Ascender; //if (ascender <= 0) { - var textBlockFont = textBlock.Style.Font.GetMultiFont(); + var textBlockFont = textBlock.Style.Font.GetFont(); if (textBlockFont) - ascender = textBlockFont.MaxAscender; + ascender = textBlockFont.Ascender; } lineAscender = Mathf.Max(lineAscender, ascender); lineSize = Float2.Max(lineSize, textBlockSize); @@ -267,9 +260,9 @@ namespace FlaxEngine.GUI var ascender = textBlock.Ascender; if (ascender <= 0) { - var textBlockFont = textBlock.Style.Font.GetMultiFont(); + var textBlockFont = textBlock.Style.Font.GetFont(); if (textBlockFont) - ascender = textBlockFont.MaxAscender; + ascender = textBlockFont.Ascender; } vOffset = lineAscender - ascender; textBlock.Bounds.Location.Y += vOffset; diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs b/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs index 08637918d..d3c2feefe 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs @@ -11,9 +11,9 @@ namespace FlaxEngine.GUI { context.Caret.X = 0; var style = context.StyleStack.Peek(); - var font = style.Font.GetMultiFont(); + var font = style.Font.GetFont(); if (font) - context.Caret.Y += font.MaxHeight; + context.Caret.Y += font.Height; } private static void ProcessColor(ref ParsingContext context, ref HtmlTag tag) @@ -87,14 +87,15 @@ namespace FlaxEngine.GUI else { var style = context.StyleStack.Peek(); - style.Font = new MultiFontReference(style.Font); - if (tag.Attributes.TryGetValue("size", out var sizeText) && int.TryParse(sizeText, out var size) && tag.Attributes.TryGetValue(string.Empty, out var fontName)) + style.Font = new FontReference(style.Font); + if (tag.Attributes.TryGetValue(string.Empty, out var fontName)) { var font = (FontAsset)FindAsset(fontName, typeof(FontAsset)); if (font) - style.Font = new MultiFontReference([font], size); + style.Font.Font = font; } - + if (tag.Attributes.TryGetValue("size", out var sizeText) && int.TryParse(sizeText, out var size)) + style.Font.Size = size; context.StyleStack.Push(style); } } @@ -108,7 +109,7 @@ namespace FlaxEngine.GUI else { var style = context.StyleStack.Peek(); - // style.Font = style.Font.GetBold(); + style.Font = style.Font.GetBold(); context.StyleStack.Push(style); } } @@ -122,7 +123,7 @@ namespace FlaxEngine.GUI else { var style = context.StyleStack.Peek(); - // style.Font = style.Font.GetItalic(); + style.Font = style.Font.GetItalic(); context.StyleStack.Push(style); } } @@ -136,9 +137,9 @@ namespace FlaxEngine.GUI else { var style = context.StyleStack.Peek(); - style.Font = new MultiFontReference(style.Font); - TryParseNumberTag(ref tag, string.Empty, style.Font.First().Size, out var size); - style.Font = new MultiFontReference(style.Font, (int)size); + style.Font = new FontReference(style.Font); + TryParseNumberTag(ref tag, string.Empty, style.Font.Size, out var size); + style.Font.Size = (int)size; context.StyleStack.Push(style); } } @@ -173,9 +174,9 @@ namespace FlaxEngine.GUI imageBlock.Style.BackgroundBrush = image; // Setup size - var font = imageBlock.Style.Font.GetMultiFont(); + var font = imageBlock.Style.Font.GetFont(); if (font) - imageBlock.Bounds.Size = new Float2(font.MaxHeight); + imageBlock.Bounds.Size = new Float2(font.Height); imageBlock.Bounds.Size.X *= image.Size.X / image.Size.Y; // Keep original aspect ratio bool hasWidth = TryParseNumberTag(ref tag, "width", imageBlock.Bounds.Width, out var width); imageBlock.Bounds.Width = width; diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.cs b/Source/Engine/UI/GUI/Common/RichTextBox.cs index 78da9621c..f8ac7d353 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBox.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBox.cs @@ -46,7 +46,7 @@ namespace FlaxEngine.GUI var style = Style.Current; _textStyle = new TextBlockStyle { - Font = new MultiFontReference(style.FontMedium), + Font = new FontReference(style.FontMedium.Fonts.First()), Color = style.Foreground, BackgroundSelectedBrush = new SolidColorBrush(style.BackgroundSelected), }; diff --git a/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs b/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs index 6247cb885..438a7e3d8 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs @@ -123,10 +123,10 @@ namespace FlaxEngine.GUI if (index <= 0) { ref TextBlock textBlock = ref textBlocks[0]; - var font = textBlock.Style.Font.GetMultiFont(); + var font = textBlock.Style.Font.GetFont(); if (font) { - height = font.MaxHeight / DpiScale; + height = font.Height / DpiScale; return textBlock.Bounds.UpperLeft; } } @@ -135,10 +135,10 @@ namespace FlaxEngine.GUI if (index >= _text.Length) { ref TextBlock textBlock = ref textBlocks[count - 1]; - var font = textBlock.Style.Font.GetMultiFont(); + var font = textBlock.Style.Font.GetFont(); if (font) { - height = font.MaxHeight / DpiScale; + height = font.Height / DpiScale; return textBlock.Bounds.UpperRight; } } @@ -150,10 +150,10 @@ namespace FlaxEngine.GUI if (textBlock.Range.Contains(index)) { - var font = textBlock.Style.Font.GetMultiFont(); + var font = textBlock.Style.Font.GetFont(); if (!font) break; - height = font.MaxHeight / DpiScale; + height = font.Height / DpiScale; return textBlock.Bounds.Location + font.GetCharPosition(_text, ref textBlock.Range, index - textBlock.Range.StartIndex); } } @@ -165,10 +165,10 @@ namespace FlaxEngine.GUI if (index >= textBlock.Range.EndIndex) { - var font = textBlock.Style.Font.GetMultiFont(); + var font = textBlock.Style.Font.GetFont(); if (!font) break; - height = font.MaxHeight / DpiScale; + height = font.Height / DpiScale; return textBlock.Bounds.UpperRight; } } @@ -193,7 +193,7 @@ namespace FlaxEngine.GUI if (containsY && (containsX || (i + 1 < count && textBlocks[i + 1].Bounds.Location.Y > textBlock.Bounds.Location.Y + 1.0f))) { - var font = textBlock.Style.Font.GetMultiFont(); + var font = textBlock.Style.Font.GetFont(); if (!font && textBlock.Range.Length > 0) break; return font.HitTestText(_text, ref textBlock.Range, location - textBlock.Bounds.Location) + textBlock.Range.StartIndex; @@ -281,7 +281,7 @@ namespace FlaxEngine.GUI } // Pick font - var font = textBlock.Style.Font.GetMultiFont(); + var font = textBlock.Style.Font.GetFont(); if (!font) continue; @@ -290,7 +290,7 @@ namespace FlaxEngine.GUI { var leftEdge = selection.StartIndex <= textBlock.Range.StartIndex ? textBlock.Bounds.UpperLeft : font.GetCharPosition(_text, selection.StartIndex); var rightEdge = selection.EndIndex >= textBlock.Range.EndIndex ? textBlock.Bounds.UpperRight : font.GetCharPosition(_text, selection.EndIndex); - float height = font.MaxHeight / DpiScale; + float height = font.Height / DpiScale; float alpha = Mathf.Min(1.0f, Mathf.Cos(_animateTime * BackgroundSelectedFlashSpeed) * 0.5f + 1.3f); alpha *= alpha; Color selectionColor = Color.White * alpha; @@ -305,7 +305,7 @@ namespace FlaxEngine.GUI ref TextBlock textBlock = ref textBlocks[i]; // Pick font - var font = textBlock.Style.Font.GetMultiFont(); + var font = textBlock.Style.Font.GetFont(); if (!font) continue; @@ -332,7 +332,7 @@ namespace FlaxEngine.GUI ref TextBlock textBlock = ref textBlocks[i]; // Pick font - var font = textBlock.Style.Font.GetMultiFont(); + var font = textBlock.Style.Font.GetFont(); if (!font) continue; @@ -340,7 +340,7 @@ namespace FlaxEngine.GUI if (textBlock.Style.UnderlineBrush != null) { var underLineHeight = 2.0f; - var height = font.MaxHeight / DpiScale; + var height = font.Height / DpiScale; var underlineRect = new Rectangle(textBlock.Bounds.Location.X, textBlock.Bounds.Location.Y + height - underLineHeight * 0.5f, textBlock.Bounds.Width, underLineHeight); textBlock.Style.UnderlineBrush.Draw(underlineRect, textBlock.Style.Color); } diff --git a/Source/Engine/UI/GUI/TextBlockStyle.cs b/Source/Engine/UI/GUI/TextBlockStyle.cs index 08f74e7c8..c4c87715f 100644 --- a/Source/Engine/UI/GUI/TextBlockStyle.cs +++ b/Source/Engine/UI/GUI/TextBlockStyle.cs @@ -64,7 +64,7 @@ namespace FlaxEngine.GUI /// The text font. /// [EditorOrder(0)] - public MultiFontReference Font; + public FontReference Font; /// /// The custom material for the text rendering (must be GUI domain). From d3840bb1f36fafb2ca6d5154d9922b3c16b500fb Mon Sep 17 00:00:00 2001 From: ExMatics HydrogenC <33123710+HydrogenC@users.noreply.github.com> Date: Thu, 30 Nov 2023 16:25:32 +0800 Subject: [PATCH 35/61] Refractor C++ code structure --- Source/Engine/Render2D/FallbackFonts.cpp | 11 + Source/Engine/Render2D/FallbackFonts.h | 94 +++++ Source/Engine/Render2D/Font.cpp | 441 ++++++++++++++++++++++- Source/Engine/Render2D/Font.h | 276 +++++++++++++- Source/Engine/Render2D/FontAsset.cpp | 4 + Source/Engine/Render2D/FontAsset.h | 7 + Source/Engine/Render2D/MultiFont.cpp | 431 ---------------------- Source/Engine/Render2D/MultiFont.h | 353 ------------------ Source/Engine/Render2D/Render2D.cpp | 74 ++-- Source/Engine/Render2D/Render2D.h | 10 +- 10 files changed, 864 insertions(+), 837 deletions(-) create mode 100644 Source/Engine/Render2D/FallbackFonts.cpp create mode 100644 Source/Engine/Render2D/FallbackFonts.h delete mode 100644 Source/Engine/Render2D/MultiFont.cpp delete mode 100644 Source/Engine/Render2D/MultiFont.h diff --git a/Source/Engine/Render2D/FallbackFonts.cpp b/Source/Engine/Render2D/FallbackFonts.cpp new file mode 100644 index 000000000..b79a0db4e --- /dev/null +++ b/Source/Engine/Render2D/FallbackFonts.cpp @@ -0,0 +1,11 @@ +#include "FallbackFonts.h" +#include "FontManager.h" +#include "Engine/Core/Math/Math.h" + +FallbackFonts::FallbackFonts(const Array& fonts) + : ManagedScriptingObject(SpawnParams(Guid::New(), Font::TypeInitializer)), + _fontAssets(fonts) +{ + +} + diff --git a/Source/Engine/Render2D/FallbackFonts.h b/Source/Engine/Render2D/FallbackFonts.h new file mode 100644 index 000000000..4b97e1c11 --- /dev/null +++ b/Source/Engine/Render2D/FallbackFonts.h @@ -0,0 +1,94 @@ +#pragma once + +#include "Engine/Core/Collections/Array.h" +#include "Engine/Core/Collections/Dictionary.h" +#include "Font.h" +#include "FontAsset.h" + +struct TextRange; +class Font; +class FontAsset; + +API_CLASS(Sealed, NoSpawn) class FLAXENGINE_API FallbackFonts : public ManagedScriptingObject +{ + DECLARE_SCRIPTING_TYPE_NO_SPAWN(FallbackFonts); +private: + /// + /// The list of fallback fonts, ordered by priority. + /// The first element is reserved for the primary font, fallback fonts starts from the second element. + /// + Array _fontAssets; + + Dictionary*> _cache; + +public: + FallbackFonts(const Array& fonts); + + API_FUNCTION() FORCE_INLINE static FallbackFonts* Create(const Array& fonts) { + return New(fonts); + } + + API_PROPERTY() FORCE_INLINE Array& GetFonts() { + return _fontAssets; + } + + API_PROPERTY() FORCE_INLINE void SetFonts(const Array& val) { + _fontAssets = val; + } + + /// + /// Combine the primary fonts with the fallback fonts to get a font list + /// + API_PROPERTY() FORCE_INLINE Array& GetFontList(float size) { + Array* result; + if (_cache.TryGet(size, result)) { + return *result; + } + + result = New>(_fontAssets.Count()); + auto& arr = *result; + for (int32 i = 0; i < _fontAssets.Count(); i++) + { + arr.Add(_fontAssets[i]->CreateFont(size)); + } + + _cache[size] = result; + return *result; + } + + + /// + /// Gets the index of the fallback font that should be used to render the char + /// + /// The char. + /// -1 if char can be rendered with primary font, index if it matches a fallback font. + API_FUNCTION() FORCE_INLINE int32 GetCharFallbackIndex(Char c, Font* primaryFont = nullptr, int32 missing = -1) { + if (primaryFont && primaryFont->GetAsset()->ContainsChar(c)) { + return -1; + } + + int32 fontIndex = 0; + while (fontIndex < _fontAssets.Count() && _fontAssets[fontIndex] && !_fontAssets[fontIndex]->ContainsChar(c)) + { + fontIndex++; + } + + if (fontIndex < _fontAssets.Count()) { + return fontIndex; + + } + + return missing; + } + + API_FUNCTION() FORCE_INLINE bool Verify() { + for (int32 i = 0; i < _fontAssets.Count(); i++) + { + if (!_fontAssets[i]) { + return false; + } + } + + return true; + } +}; diff --git a/Source/Engine/Render2D/Font.cpp b/Source/Engine/Render2D/Font.cpp index b33b10899..4cf1f73a2 100644 --- a/Source/Engine/Render2D/Font.cpp +++ b/Source/Engine/Render2D/Font.cpp @@ -6,7 +6,7 @@ #include "Engine/Core/Log.h" #include "Engine/Threading/Threading.h" #include "IncludeFreeType.h" -#include "MultiFont.h" +#include "FallbackFonts.h" Font::Font(FontAsset* parentAsset, float size) : ManagedScriptingObject(SpawnParams(Guid::New(), Font::TypeInitializer)) @@ -293,6 +293,261 @@ void Font::ProcessText(const StringView& text, Array& outputLines } } +void Font::ProcessText(FallbackFonts* fallbacks, const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout) +{ + const Array& fallbackFonts = fallbacks->GetFontList(GetSize()); + float cursorX = 0; + int32 kerning; + BlockedTextLineCache tmpLine; + FontBlockCache tmpBlock; + FontCharacterEntry entry; + FontCharacterEntry previous; + int32 textLength = text.Length(); + float scale = layout.Scale / FontManager::FontScale; + float boundsWidth = layout.Bounds.GetWidth(); + float baseLinesDistanceScale = layout.BaseLinesGapScale * scale; + + tmpBlock.Location = Float2::Zero; + tmpBlock.Size = Float2::Zero; + tmpBlock.FirstCharIndex = 0; + tmpBlock.LastCharIndex = -1; + + tmpLine.Location = Float2::Zero; + tmpLine.Size = Float2::Zero; + tmpLine.Blocks = Array(); + + if (textLength == 0) { + return; + } + + int32 lastWrapCharIndex = INVALID_INDEX; + float lastWrapCharX = 0; + bool lastMoveLine = false; + // The index of the font used by the current block + int32 currentFontIndex = fallbacks->GetCharFallbackIndex(text[0], this); + // The maximum font height of the current line + float maxHeight = 0; + float maxAscender = 0; + float lastCursorX = 0; + + auto getFont = [&](int32 index)->Font* { + return index >= 0 ? fallbackFonts[index] : this; + }; + + // Process each character to split text into single blocks + for (int32 currentIndex = 0; currentIndex < textLength;) + { + bool moveLine = false; + bool moveBlock = false; + float xAdvance = 0; + int32 nextCharIndex = currentIndex + 1; + + // Submit line and block if text ends + if (nextCharIndex == textLength) { + moveLine = moveBlock = true; + } + + // Cache current character + const Char currentChar = text[currentIndex]; + const bool isWhitespace = StringUtils::IsWhitespace(currentChar); + + // Check if character can wrap words + const bool isWrapChar = !StringUtils::IsAlnum(currentChar) || isWhitespace || StringUtils::IsUpper(currentChar) || (currentChar >= 0x3040 && currentChar <= 0x9FFF); + if (isWrapChar && currentIndex != 0) + { + lastWrapCharIndex = currentIndex; + lastWrapCharX = cursorX; + } + + int32 nextFontIndex = currentFontIndex; + // Check if it's a newline character + if (currentChar == '\n') + { + // Break line + moveLine = moveBlock = true; + tmpBlock.LastCharIndex++; + } + else + { + // Get character entry + if (nextCharIndex < textLength) { + nextFontIndex = fallbacks->GetCharFallbackIndex(text[nextCharIndex], this, currentFontIndex); + } + + // Get character entry + getFont(currentFontIndex)->GetCharacter(currentChar, entry); + + maxHeight = Math::Max(maxHeight, + static_cast(getFont(currentFontIndex)->GetHeight())); + maxAscender = Math::Max(maxAscender, + static_cast(getFont(currentFontIndex)->GetAscender())); + + // Move block if the font changes or text ends + if (nextFontIndex != currentFontIndex || nextCharIndex == textLength) { + moveBlock = true; + } + + // Get kerning, only when the font hasn't changed + if (!isWhitespace && previous.IsValid && !moveBlock) + { + kerning = getFont(currentFontIndex)->GetKerning(previous.Character, entry.Character); + } + else + { + kerning = 0; + } + previous = entry; + xAdvance = (kerning + entry.AdvanceX) * scale; + + // Check if character fits the line or skip wrapping + if (cursorX + xAdvance <= boundsWidth || layout.TextWrapping == TextWrapping::NoWrap) + { + // Move character + cursorX += xAdvance; + tmpBlock.LastCharIndex++; + } + else if (layout.TextWrapping == TextWrapping::WrapWords) + { + if (lastWrapCharIndex != INVALID_INDEX) + { + // Skip moving twice for the same character + int32 lastLineLastCharIndex = outputLines.HasItems() && outputLines.Last().Blocks.HasItems() ? outputLines.Last().Blocks.Last().LastCharIndex : -10000; + if (lastLineLastCharIndex == lastWrapCharIndex || lastLineLastCharIndex == lastWrapCharIndex - 1 || lastLineLastCharIndex == lastWrapCharIndex - 2) + { + currentIndex = nextCharIndex; + lastMoveLine = moveLine; + continue; + } + + // Move line + const Char wrapChar = text[lastWrapCharIndex]; + moveLine = true; + moveBlock = tmpBlock.FirstCharIndex < lastWrapCharIndex; + + cursorX = lastWrapCharX; + if (StringUtils::IsWhitespace(wrapChar)) + { + // Skip whitespaces + tmpBlock.LastCharIndex = lastWrapCharIndex - 1; + nextCharIndex = currentIndex = lastWrapCharIndex + 1; + } + else + { + tmpBlock.LastCharIndex = lastWrapCharIndex - 1; + nextCharIndex = currentIndex = lastWrapCharIndex; + } + } + } + else if (layout.TextWrapping == TextWrapping::WrapChars) + { + // Move line + moveLine = true; + moveBlock = tmpBlock.FirstCharIndex < currentChar; + nextCharIndex = currentIndex; + + // Skip moving twice for the same character + if (lastMoveLine) + break; + } + } + + if (moveBlock) { + // Add block + tmpBlock.Size.X = lastCursorX - cursorX; + tmpBlock.Size.Y = baseLinesDistanceScale * getFont(currentFontIndex)->GetHeight(); + tmpBlock.LastCharIndex = Math::Max(tmpBlock.LastCharIndex, tmpBlock.FirstCharIndex); + tmpBlock.FallbackFontIndex = currentFontIndex; + tmpLine.Blocks.Add(tmpBlock); + + // Reset block + tmpBlock.Location.X = cursorX; + tmpBlock.FirstCharIndex = nextCharIndex; + tmpBlock.LastCharIndex = nextCharIndex - 1; + + currentFontIndex = nextFontIndex; + lastCursorX = cursorX; + } + + // Check if move to another line + if (moveLine) + { + // Add line + tmpLine.Size.X = cursorX; + tmpLine.Size.Y = baseLinesDistanceScale * maxHeight; + tmpLine.MaxAscender = maxAscender; + outputLines.Add(tmpLine); + + // Reset line + tmpLine.Blocks.Clear(); + tmpLine.Location.Y += baseLinesDistanceScale * maxHeight; + cursorX = 0; + tmpBlock.Location.X = cursorX; + lastWrapCharIndex = INVALID_INDEX; + lastWrapCharX = 0; + previous.IsValid = false; + + // Reset max font height + maxHeight = 0; + maxAscender = 0; + lastCursorX = 0; + } + + currentIndex = nextCharIndex; + lastMoveLine = moveLine; + } + + // Check if an additional line should be created + if (text[textLength - 1] == '\n') + { + // Add line + tmpLine.Size.X = cursorX; + tmpLine.Size.Y = baseLinesDistanceScale * maxHeight; + outputLines.Add(tmpLine); + + tmpLine.Location.Y += baseLinesDistanceScale * maxHeight; + } + + // Check amount of lines + if (outputLines.IsEmpty()) + return; + + float totalHeight = tmpLine.Location.Y; + + Float2 offset = Float2::Zero; + if (layout.VerticalAlignment == TextAlignment::Center) + { + offset.Y += (layout.Bounds.GetHeight() - totalHeight) * 0.5f; + } + else if (layout.VerticalAlignment == TextAlignment::Far) + { + offset.Y += layout.Bounds.GetHeight() - totalHeight; + } + for (int32 i = 0; i < outputLines.Count(); i++) + { + BlockedTextLineCache& line = outputLines[i]; + Float2 rootPos = line.Location + offset; + + // Fix upper left line corner to match desire text alignment + if (layout.HorizontalAlignment == TextAlignment::Center) + { + rootPos.X += (layout.Bounds.GetWidth() - line.Size.X) * 0.5f; + } + else if (layout.HorizontalAlignment == TextAlignment::Far) + { + rootPos.X += layout.Bounds.GetWidth() - line.Size.X; + } + + line.Location = rootPos; + + // Align all blocks to center in case they have different heights + for (int32 j = 0; j < line.Blocks.Count(); j++) + { + FontBlockCache& block = line.Blocks[j]; + block.Location.Y += (line.MaxAscender - getFont(block.FallbackFontIndex)->GetAscender()) / 2; + } + } +} + Float2 Font::MeasureText(const StringView& text, const TextLayoutOptions& layout) { // Check if there is no need to do anything @@ -314,6 +569,27 @@ Float2 Font::MeasureText(const StringView& text, const TextLayoutOptions& layout return max; } +Float2 Font::MeasureText(FallbackFonts* fallbacks, const StringView& text, const TextLayoutOptions& layout) +{ + // Check if there is no need to do anything + if (text.IsEmpty()) + return Float2::Zero; + + // Process text + Array lines; + ProcessText(fallbacks, text, lines, layout); + + // Calculate bounds + Float2 max = Float2::Zero; + for (int32 i = 0; i < lines.Count(); i++) + { + const BlockedTextLineCache& line = lines[i]; + max = Float2::Max(max, line.Location + line.Size); + } + + return max; +} + int32 Font::HitTestText(const StringView& text, const Float2& location, const TextLayoutOptions& layout) { // Check if there is no need to do anything @@ -388,6 +664,106 @@ int32 Font::HitTestText(const StringView& text, const Float2& location, const Te return smallestIndex; } +int32 Font::HitTestText(FallbackFonts* fallbacks, const StringView& text, const Float2& location, const TextLayoutOptions& layout) +{ + // Check if there is no need to do anything + if (text.Length() <= 0) + return 0; + + // Process text + const Array& fallbackFonts = fallbacks->GetFontList(GetSize()); + Array lines; + ProcessText(fallbacks, text, lines, layout); + ASSERT(lines.HasItems()); + float scale = layout.Scale / FontManager::FontScale; + + // Offset position to match lines origin space + Float2 rootOffset = layout.Bounds.Location + lines.First().Location; + Float2 testPoint = location - rootOffset; + + // Get block which may intersect with the position (it's possible because lines have fixed height) + int32 lineIndex = 0; + while (lineIndex < lines.Count()) + { + if (lines[lineIndex].Location.Y + lines[lineIndex].Size.Y >= location.Y) { + break; + } + + lineIndex++; + } + lineIndex = Math::Clamp(lineIndex, 0, lines.Count() - 1); + const BlockedTextLineCache& line = lines[lineIndex]; + + int32 blockIndex = 0; + while (blockIndex < line.Blocks.Count() - 1) + { + if (line.Location.X + line.Blocks[blockIndex + 1].Location.X >= location.X) { + break; + } + + blockIndex++; + } + const FontBlockCache& block = line.Blocks[blockIndex]; + float x = line.Location.X; + + // Check all characters in the line to find hit point + FontCharacterEntry previous; + FontCharacterEntry entry; + int32 smallestIndex = INVALID_INDEX; + float dst, smallestDst = MAX_float; + + auto getFont = [&](int32 index)->Font* { + return index >= 0 ? fallbackFonts[index] : this; + }; + + for (int32 currentIndex = block.FirstCharIndex; currentIndex <= block.LastCharIndex; currentIndex++) + { + // Cache current character + const Char currentChar = text[currentIndex]; + + getFont(block.FallbackFontIndex)->GetCharacter(currentChar, entry); + const bool isWhitespace = StringUtils::IsWhitespace(currentChar); + + // Apply kerning + if (!isWhitespace && previous.IsValid) + { + x += getFont(block.FallbackFontIndex)->GetKerning(previous.Character, entry.Character); + } + previous = entry; + + // Test + dst = Math::Abs(testPoint.X - x); + if (dst < smallestDst) + { + // Found closer character + smallestIndex = currentIndex; + smallestDst = dst; + } + else if (dst > smallestDst) + { + // Current char is worse so return the best result + return smallestIndex; + } + + // Move + x += entry.AdvanceX * scale; + } + + // Test line end edge + dst = Math::Abs(testPoint.X - x); + if (dst < smallestDst) + { + // Pointer is behind the last character in the line + smallestIndex = block.LastCharIndex; + + // Fix for last line + if (lineIndex == lines.Count() - 1) + smallestIndex++; + } + + return smallestIndex; +} + Float2 Font::GetCharPosition(const StringView& text, int32 index, const TextLayoutOptions& layout) { // Check if there is no need to do anything @@ -442,9 +818,68 @@ Float2 Font::GetCharPosition(const StringView& text, int32 index, const TextLayo return rootOffset + Float2(lines.Last().Location.X + lines.Last().Size.X, static_cast((lines.Count() - 1) * baseLinesDistance)); } -bool Font::ContainsChar(Char c) const +Float2 Font::GetCharPosition(FallbackFonts* fallbacks, const StringView& text, int32 index, const TextLayoutOptions& layout) { - return FT_Get_Char_Index(GetAsset()->GetFTFace(), c) > 0; + // Check if there is no need to do anything + if (text.IsEmpty()) + return layout.Bounds.Location; + + // Process text + const Array& fallbackFonts = fallbacks->GetFontList(GetSize()); + Array lines; + ProcessText(fallbacks, text, lines, layout); + ASSERT(lines.HasItems()); + float scale = layout.Scale / FontManager::FontScale; + float baseLinesDistance = layout.BaseLinesGapScale * scale; + Float2 rootOffset = layout.Bounds.Location; + + // Find line with that position + FontCharacterEntry previous; + FontCharacterEntry entry; + + auto getFont = [&](int32 index)->Font* { + return index >= 0 ? fallbackFonts[index] : this; + }; + + for (int32 lineIndex = 0; lineIndex < lines.Count(); lineIndex++) + { + const BlockedTextLineCache& line = lines[lineIndex]; + for (int32 blockIndex = 0; blockIndex < line.Blocks.Count(); blockIndex++) + { + const FontBlockCache& block = line.Blocks[blockIndex]; + // Check if desire position is somewhere inside characters in line range + if (Math::IsInRange(index, block.FirstCharIndex, block.LastCharIndex)) + { + float x = line.Location.X + block.Location.X; + float y = line.Location.Y + block.Location.Y; + + // Check all characters in the line + for (int32 currentIndex = block.FirstCharIndex; currentIndex < index; currentIndex++) + { + // Cache current character + const Char currentChar = text[currentIndex]; + getFont(block.FallbackFontIndex)->GetCharacter(currentChar, entry); + const bool isWhitespace = StringUtils::IsWhitespace(currentChar); + + // Apply kerning + if (!isWhitespace && previous.IsValid) + { + x += getFont(block.FallbackFontIndex)->GetKerning(previous.Character, entry.Character); + } + previous = entry; + + // Move + x += entry.AdvanceX * scale; + } + + // Upper left corner of the character + return rootOffset + Float2(x, y); + } + } + } + + // Position after last character in the last line + return rootOffset + Float2(lines.Last().Location.X + lines.Last().Size.X, lines.Last().Location.Y); } void Font::FlushFaceSize() const diff --git a/Source/Engine/Render2D/Font.h b/Source/Engine/Render2D/Font.h index 453872582..d932888fb 100644 --- a/Source/Engine/Render2D/Font.h +++ b/Source/Engine/Render2D/Font.h @@ -10,8 +10,9 @@ #include "TextLayoutOptions.h" class FontAsset; +class FallbackFonts; struct FontTextureAtlasSlot; -struct MultiFontLineCache; +struct BlockedTextLineCache; // The default DPI that engine is using #define DefaultDPI 96 @@ -120,6 +121,73 @@ struct TIsPODType enum { Value = true }; }; +/// +/// The font block info generated during text processing. +/// +API_STRUCT(NoDefault) struct FontBlockCache +{ + DECLARE_SCRIPTING_TYPE_MINIMAL(FontBlockCache); + + /// + /// The root position of the block (upper left corner), relative to line. + /// + API_FIELD() Float2 Location; + + /// + /// The height of the current block + /// + API_FIELD() Float2 Size; + + /// + /// The first character index (from the input text). + /// + API_FIELD() int32 FirstCharIndex; + + /// + /// The last character index (from the input text), inclusive. + /// + API_FIELD() int32 LastCharIndex; + + /// + /// Indicates the fallback font to render this block with, -1 if doesn't require fallback. + /// + API_FIELD() int32 FallbackFontIndex; +}; + +template<> +struct TIsPODType +{ + enum { Value = true }; +}; + +/// +/// Line of font blocks info generated during text processing. +/// +API_STRUCT(NoDefault) struct BlockedTextLineCache +{ + DECLARE_SCRIPTING_TYPE_MINIMAL(BlockedTextLineCache); + + /// + /// The root position of the line (upper left corner). + /// + API_FIELD() Float2 Location; + + /// + /// The line bounds (width and height). + /// + API_FIELD() Float2 Size; + + /// + /// The maximum ascender of the line. + /// + API_FIELD() float MaxAscender; + + /// + /// The index of the font to render with + /// + API_FIELD() Array Blocks; +}; + // Font glyph metrics: // // xmin xmax @@ -388,6 +456,62 @@ public: return ProcessText(textRange.Substring(text), TextLayoutOptions()); } + /// + /// Processes text to get cached lines for rendering. + /// + /// The input text. + /// The layout properties. + /// The output lines list. + void ProcessText(FallbackFonts* fallbacks, const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout); + + /// + /// Processes text to get cached lines for rendering. + /// + /// The input text. + /// The layout properties. + /// The output lines list. + API_FUNCTION() Array ProcessText(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout) + { + Array lines; + ProcessText(fallbacks, text, lines, layout); + return lines; + } + + /// + /// Processes text to get cached lines for rendering. + /// + /// The input text. + /// The input text range (substring range of the input text parameter). + /// The layout properties. + /// The output lines list. + API_FUNCTION() Array ProcessText(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) + { + Array lines; + ProcessText(fallbacks, textRange.Substring(text), lines, layout); + return lines; + } + + /// + /// Processes text to get cached lines for rendering. + /// + /// The input text. + /// The output lines list. + API_FUNCTION() FORCE_INLINE Array ProcessText(FallbackFonts* fallbacks, const StringView& text) + { + return ProcessText(fallbacks, text, TextLayoutOptions()); + } + + /// + /// Processes text to get cached lines for rendering. + /// + /// The input text. + /// The input text range (substring range of the input text parameter). + /// The output lines list. + API_FUNCTION() FORCE_INLINE Array ProcessText(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange) + { + return ProcessText(fallbacks, textRange.Substring(text), TextLayoutOptions()); + } + /// /// Measures minimum size of the rectangle that will be needed to draw given text. /// @@ -429,6 +553,56 @@ public: return MeasureText(textRange.Substring(text), TextLayoutOptions()); } + /// + /// Measures minimum size of the rectangle that will be needed to draw given text. + /// + /// The input text to test. + /// The layout properties. + /// The minimum size for that text and fot to render properly. + API_FUNCTION() Float2 MeasureText(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout); + + /// + /// Measures minimum size of the rectangle that will be needed to draw given text. + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The layout properties. + /// The minimum size for that text and fot to render properly. + API_FUNCTION() Float2 MeasureText(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) + { + return MeasureText(fallbacks, textRange.Substring(text), layout); + } + + /// + /// Measures minimum size of the rectangle that will be needed to draw given text + /// . + /// The input text to test. + /// The minimum size for that text and fot to render properly. + API_FUNCTION() FORCE_INLINE Float2 MeasureText(FallbackFonts* fallbacks, const StringView& text) + { + return MeasureText(fallbacks, text, TextLayoutOptions()); + } + + /// + /// Measures minimum size of the rectangle that will be needed to draw given text + /// . + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The minimum size for that text and fot to render properly. + API_FUNCTION() FORCE_INLINE Float2 MeasureText(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange) + { + return MeasureText(fallbacks, textRange.Substring(text), TextLayoutOptions()); + } + + /// + /// Calculates hit character index at given location. + /// + /// The input text to test. + /// The input location to test. + /// The text layout properties. + /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). + API_FUNCTION() int32 HitTestText(const StringView& text, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout); + /// /// Calculates hit character index at given location. /// @@ -442,15 +616,6 @@ public: return HitTestText(textRange.Substring(text), location, layout); } - /// - /// Calculates hit character index at given location. - /// - /// The input text to test. - /// The input location to test. - /// The text layout properties. - /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() int32 HitTestText(const StringView& text, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout); - /// /// Calculates hit character index at given location. /// @@ -474,6 +639,51 @@ public: return HitTestText(textRange.Substring(text), location, TextLayoutOptions()); } + /// + /// Calculates hit character index at given location. + /// + /// The input text to test. + /// The input location to test. + /// The text layout properties. + /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). + API_FUNCTION() int32 HitTestText(FallbackFonts* fallbacks, const StringView& text, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout); + + /// + /// Calculates hit character index at given location. + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The input location to test. + /// The text layout properties. + /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). + API_FUNCTION() int32 HitTestText(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout) + { + return HitTestText(fallbacks, textRange.Substring(text), location, layout); + } + + /// + /// Calculates hit character index at given location. + /// + /// The input text to test. + /// The input location to test. + /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). + API_FUNCTION() FORCE_INLINE int32 HitTestText(FallbackFonts* fallbacks, const StringView& text, const Float2& location) + { + return HitTestText(fallbacks, text, location, TextLayoutOptions()); + } + + /// + /// Calculates hit character index at given location. + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The input location to test. + /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). + API_FUNCTION() FORCE_INLINE int32 HitTestText(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location) + { + return HitTestText(fallbacks, textRange.Substring(text), location, TextLayoutOptions()); + } + /// /// Calculates character position for given text and character index. /// @@ -520,11 +730,49 @@ public: } /// - /// Check if the font contains the glyph of a char + /// Calculates character position for given text and character index. /// - /// The char to test. - /// True if the font contains the glyph of the char, otherwise false. - API_FUNCTION() FORCE_INLINE bool ContainsChar(Char c) const; + /// The input text to test. + /// The text position to get coordinates of. + /// The text layout properties. + /// The character position (upper left corner which can be used for a caret position). + API_FUNCTION() Float2 GetCharPosition(FallbackFonts* fallbacks, const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout); + + /// + /// Calculates character position for given text and character index. + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The text position to get coordinates of. + /// The text layout properties. + /// The character position (upper left corner which can be used for a caret position). + API_FUNCTION() Float2 GetCharPosition(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout) + { + return GetCharPosition(fallbacks, textRange.Substring(text), index, layout); + } + + /// + /// Calculates character position for given text and character index + /// + /// The input text to test. + /// The text position to get coordinates of. + /// The character position (upper left corner which can be used for a caret position). + API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(FallbackFonts* fallbacks, const StringView& text, int32 index) + { + return GetCharPosition(fallbacks, text, index, TextLayoutOptions()); + } + + /// + /// Calculates character position for given text and character index + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The text position to get coordinates of. + /// The character position (upper left corner which can be used for a caret position). + API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index) + { + return GetCharPosition(fallbacks, textRange.Substring(text), index, TextLayoutOptions()); + } /// /// Flushes the size of the face with the Free Type library backend. diff --git a/Source/Engine/Render2D/FontAsset.cpp b/Source/Engine/Render2D/FontAsset.cpp index a477e5f4d..1e94b19b1 100644 --- a/Source/Engine/Render2D/FontAsset.cpp +++ b/Source/Engine/Render2D/FontAsset.cpp @@ -199,6 +199,10 @@ bool FontAsset::Save(const StringView& path) #endif +bool FontAsset::ContainsChar(Char c) const { + return FT_Get_Char_Index(GetFTFace(), c) > 0; +} + void FontAsset::Invalidate() { ScopeLock lock(Locker); diff --git a/Source/Engine/Render2D/FontAsset.h b/Source/Engine/Render2D/FontAsset.h index 4dea84a5b..a773a8ad6 100644 --- a/Source/Engine/Render2D/FontAsset.h +++ b/Source/Engine/Render2D/FontAsset.h @@ -174,6 +174,13 @@ public: API_FUNCTION() bool Save(const StringView& path = StringView::Empty); #endif + /// + /// Check if the font contains the glyph of a char + /// + /// The char to test. + /// True if the font contains the glyph of the char, otherwise false. + API_FUNCTION() FORCE_INLINE bool ContainsChar(Char c) const; + /// /// Invalidates all cached dynamic font atlases using this font. Can be used to reload font characters after changing font asset options. /// diff --git a/Source/Engine/Render2D/MultiFont.cpp b/Source/Engine/Render2D/MultiFont.cpp deleted file mode 100644 index 4a510a9f8..000000000 --- a/Source/Engine/Render2D/MultiFont.cpp +++ /dev/null @@ -1,431 +0,0 @@ -#include "MultiFont.h" -#include "FontManager.h" -#include "Engine/Core/Math/Math.h" - -MultiFont::MultiFont(const Array& fonts) - : ManagedScriptingObject(SpawnParams(Guid::New(), Font::TypeInitializer)), - _fonts(fonts) -{ - -} - -void MultiFont::ProcessText(const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout) -{ - float cursorX = 0; - int32 kerning; - MultiFontLineCache tmpLine; - MultiFontBlockCache tmpBlock; - FontCharacterEntry entry; - FontCharacterEntry previous; - int32 textLength = text.Length(); - float scale = layout.Scale / FontManager::FontScale; - float boundsWidth = layout.Bounds.GetWidth(); - float baseLinesDistanceScale = layout.BaseLinesGapScale * scale; - - tmpBlock.Location = Float2::Zero; - tmpBlock.Size = Float2::Zero; - tmpBlock.FirstCharIndex = 0; - tmpBlock.LastCharIndex = -1; - - tmpLine.Location = Float2::Zero; - tmpLine.Size = Float2::Zero; - tmpLine.Blocks = Array(); - - if (textLength == 0) { - return; - } - - int32 lastWrapCharIndex = INVALID_INDEX; - float lastWrapCharX = 0; - bool lastMoveLine = false; - // The index of the font used by the current block - int32 currentFontIndex = GetCharFontIndex(text[0], 0); - // The maximum font height of the current line - float maxHeight = 0; - float maxAscender = 0; - float lastCursorX = 0; - - // Process each character to split text into single lines - for (int32 currentIndex = 0; currentIndex < textLength;) - { - bool moveLine = false; - bool moveBlock = false; - float xAdvance = 0; - int32 nextCharIndex = currentIndex + 1; - - // Submit line and block if text ends - if (nextCharIndex == textLength) { - moveLine = moveBlock = true; - } - - // Cache current character - const Char currentChar = text[currentIndex]; - const bool isWhitespace = StringUtils::IsWhitespace(currentChar); - - // Check if character can wrap words - const bool isWrapChar = !StringUtils::IsAlnum(currentChar) || isWhitespace || StringUtils::IsUpper(currentChar) || (currentChar >= 0x3040 && currentChar <= 0x9FFF); - if (isWrapChar && currentIndex != 0) - { - lastWrapCharIndex = currentIndex; - lastWrapCharX = cursorX; - } - - int32 nextFontIndex = currentFontIndex; - // Check if it's a newline character - if (currentChar == '\n') - { - // Break line - moveLine = moveBlock = true; - tmpBlock.LastCharIndex++; - } - else - { - // Get character entry - if (nextCharIndex < textLength) { - nextFontIndex = GetCharFontIndex(text[nextCharIndex], currentFontIndex); - } - - // Get character entry - _fonts[currentFontIndex]->GetCharacter(currentChar, entry); - maxHeight = Math::Max(maxHeight, static_cast(_fonts[currentFontIndex]->GetHeight())); - maxAscender = Math::Max(maxAscender, static_cast(_fonts[currentFontIndex]->GetAscender())); - - // Move block if the font changes or text ends - if (nextFontIndex != currentFontIndex || nextCharIndex == textLength) { - moveBlock = true; - } - - // Get kerning, only when the font hasn't changed - if (!isWhitespace && previous.IsValid && !moveBlock) - { - kerning = _fonts[currentFontIndex]->GetKerning(previous.Character, entry.Character); - } - else - { - kerning = 0; - } - previous = entry; - xAdvance = (kerning + entry.AdvanceX) * scale; - - // Check if character fits the line or skip wrapping - if (cursorX + xAdvance <= boundsWidth || layout.TextWrapping == TextWrapping::NoWrap) - { - // Move character - cursorX += xAdvance; - tmpBlock.LastCharIndex++; - } - else if (layout.TextWrapping == TextWrapping::WrapWords) - { - if (lastWrapCharIndex != INVALID_INDEX) - { - // Skip moving twice for the same character - int32 lastLineLastCharIndex = outputLines.HasItems() && outputLines.Last().Blocks.HasItems() ? outputLines.Last().Blocks.Last().LastCharIndex : -10000; - if (lastLineLastCharIndex == lastWrapCharIndex || lastLineLastCharIndex == lastWrapCharIndex - 1 || lastLineLastCharIndex == lastWrapCharIndex - 2) - { - currentIndex = nextCharIndex; - lastMoveLine = moveLine; - continue; - } - - // Move line - const Char wrapChar = text[lastWrapCharIndex]; - moveLine = true; - moveBlock = tmpBlock.FirstCharIndex < lastWrapCharIndex; - - cursorX = lastWrapCharX; - if (StringUtils::IsWhitespace(wrapChar)) - { - // Skip whitespaces - tmpBlock.LastCharIndex = lastWrapCharIndex - 1; - nextCharIndex = currentIndex = lastWrapCharIndex + 1; - } - else - { - tmpBlock.LastCharIndex = lastWrapCharIndex - 1; - nextCharIndex = currentIndex = lastWrapCharIndex; - } - } - } - else if (layout.TextWrapping == TextWrapping::WrapChars) - { - // Move line - moveLine = true; - moveBlock = tmpBlock.FirstCharIndex < currentChar; - nextCharIndex = currentIndex; - - // Skip moving twice for the same character - if (lastMoveLine) - break; - } - } - - if (moveBlock) { - // Add block - tmpBlock.Size.X = lastCursorX - cursorX; - tmpBlock.Size.Y = baseLinesDistanceScale * _fonts[currentFontIndex]->GetHeight(); - tmpBlock.LastCharIndex = Math::Max(tmpBlock.LastCharIndex, tmpBlock.FirstCharIndex); - tmpBlock.FontIndex = currentFontIndex; - tmpLine.Blocks.Add(tmpBlock); - - // Reset block - tmpBlock.Location.X = cursorX; - tmpBlock.FirstCharIndex = nextCharIndex; - tmpBlock.LastCharIndex = nextCharIndex - 1; - - currentFontIndex = nextFontIndex; - lastCursorX = cursorX; - } - - // Check if move to another line - if (moveLine) - { - // Add line - tmpLine.Size.X = cursorX; - tmpLine.Size.Y = baseLinesDistanceScale * maxHeight; - tmpLine.MaxAscender = maxAscender; - outputLines.Add(tmpLine); - - // Reset line - tmpLine.Blocks.Clear(); - tmpLine.Location.Y += baseLinesDistanceScale * maxHeight; - cursorX = 0; - tmpBlock.Location.X = cursorX; - lastWrapCharIndex = INVALID_INDEX; - lastWrapCharX = 0; - previous.IsValid = false; - - // Reset max font height - maxHeight = 0; - maxAscender = 0; - lastCursorX = 0; - } - - currentIndex = nextCharIndex; - lastMoveLine = moveLine; - } - - // Check if an additional line should be created - if (text[textLength - 1] == '\n') - { - // Add line - tmpLine.Size.X = cursorX; - tmpLine.Size.Y = baseLinesDistanceScale * maxHeight; - outputLines.Add(tmpLine); - - tmpLine.Location.Y += baseLinesDistanceScale * maxHeight; - } - - // Check amount of lines - if (outputLines.IsEmpty()) - return; - - float totalHeight = tmpLine.Location.Y; - - Float2 offset = Float2::Zero; - if (layout.VerticalAlignment == TextAlignment::Center) - { - offset.Y += (layout.Bounds.GetHeight() - totalHeight) * 0.5f; - } - else if (layout.VerticalAlignment == TextAlignment::Far) - { - offset.Y += layout.Bounds.GetHeight() - totalHeight; - } - for (int32 i = 0; i < outputLines.Count(); i++) - { - MultiFontLineCache& line = outputLines[i]; - Float2 rootPos = line.Location + offset; - - // Fix upper left line corner to match desire text alignment - if (layout.HorizontalAlignment == TextAlignment::Center) - { - rootPos.X += (layout.Bounds.GetWidth() - line.Size.X) * 0.5f; - } - else if (layout.HorizontalAlignment == TextAlignment::Far) - { - rootPos.X += layout.Bounds.GetWidth() - line.Size.X; - } - - line.Location = rootPos; - - // Align all blocks to center in case they have different heights - for (int32 j = 0; j < line.Blocks.Count(); j++) - { - MultiFontBlockCache& block = line.Blocks[j]; - block.Location.Y += (line.MaxAscender - _fonts[block.FontIndex]->GetAscender()) / 2; - } - } -} - -Float2 MultiFont::GetCharPosition(const StringView& text, int32 index, const TextLayoutOptions& layout) -{ - // Check if there is no need to do anything - if (text.IsEmpty()) - return layout.Bounds.Location; - - // Process text - Array lines; - ProcessText(text, lines, layout); - ASSERT(lines.HasItems()); - float scale = layout.Scale / FontManager::FontScale; - float baseLinesDistance = layout.BaseLinesGapScale * scale; - Float2 rootOffset = layout.Bounds.Location; - - // Find line with that position - FontCharacterEntry previous; - FontCharacterEntry entry; - for (int32 lineIndex = 0; lineIndex < lines.Count(); lineIndex++) - { - const MultiFontLineCache& line = lines[lineIndex]; - for (int32 blockIndex = 0; blockIndex < line.Blocks.Count(); blockIndex++) - { - const MultiFontBlockCache& block = line.Blocks[blockIndex]; - // Check if desire position is somewhere inside characters in line range - if (Math::IsInRange(index, block.FirstCharIndex, block.LastCharIndex)) - { - float x = line.Location.X + block.Location.X; - float y = line.Location.Y + block.Location.Y; - - // Check all characters in the line - for (int32 currentIndex = block.FirstCharIndex; currentIndex < index; currentIndex++) - { - // Cache current character - const Char currentChar = text[currentIndex]; - _fonts[block.FontIndex]->GetCharacter(currentChar, entry); - const bool isWhitespace = StringUtils::IsWhitespace(currentChar); - - // Apply kerning - if (!isWhitespace && previous.IsValid) - { - x += _fonts[block.FontIndex]->GetKerning(previous.Character, entry.Character); - } - previous = entry; - - // Move - x += entry.AdvanceX * scale; - } - - // Upper left corner of the character - return rootOffset + Float2(x, y); - } - } - } - - // Position after last character in the last line - return rootOffset + Float2(lines.Last().Location.X + lines.Last().Size.X, lines.Last().Location.Y); -} - -int32 MultiFont::HitTestText(const StringView& text, const Float2& location, const TextLayoutOptions& layout) -{ - // Check if there is no need to do anything - if (text.Length() <= 0) - return 0; - - // Process text - Array lines; - ProcessText(text, lines, layout); - ASSERT(lines.HasItems()); - float scale = layout.Scale / FontManager::FontScale; - - // Offset position to match lines origin space - Float2 rootOffset = layout.Bounds.Location + lines.First().Location; - Float2 testPoint = location - rootOffset; - - // Get block which may intersect with the position (it's possible because lines have fixed height) - int32 lineIndex = 0; - while (lineIndex < lines.Count()) - { - if (lines[lineIndex].Location.Y + lines[lineIndex].Size.Y >= location.Y) { - break; - } - - lineIndex++; - } - lineIndex = Math::Clamp(lineIndex, 0, lines.Count() - 1); - const MultiFontLineCache& line = lines[lineIndex]; - - int32 blockIndex = 0; - while (blockIndex < line.Blocks.Count() - 1) - { - if (line.Location.X + line.Blocks[blockIndex + 1].Location.X >= location.X) { - break; - } - - blockIndex++; - } - const MultiFontBlockCache& block = line.Blocks[blockIndex]; - float x = line.Location.X; - - // Check all characters in the line to find hit point - FontCharacterEntry previous; - FontCharacterEntry entry; - int32 smallestIndex = INVALID_INDEX; - float dst, smallestDst = MAX_float; - for (int32 currentIndex = block.FirstCharIndex; currentIndex <= block.LastCharIndex; currentIndex++) - { - // Cache current character - const Char currentChar = text[currentIndex]; - - _fonts[block.FontIndex]->GetCharacter(currentChar, entry); - const bool isWhitespace = StringUtils::IsWhitespace(currentChar); - - // Apply kerning - if (!isWhitespace && previous.IsValid) - { - x += _fonts[block.FontIndex]->GetKerning(previous.Character, entry.Character); - } - previous = entry; - - // Test - dst = Math::Abs(testPoint.X - x); - if (dst < smallestDst) - { - // Found closer character - smallestIndex = currentIndex; - smallestDst = dst; - } - else if (dst > smallestDst) - { - // Current char is worse so return the best result - return smallestIndex; - } - - // Move - x += entry.AdvanceX * scale; - } - - // Test line end edge - dst = Math::Abs(testPoint.X - x); - if (dst < smallestDst) - { - // Pointer is behind the last character in the line - smallestIndex = block.LastCharIndex; - - // Fix for last line - if (lineIndex == lines.Count() - 1) - smallestIndex++; - } - - return smallestIndex; -} - -Float2 MultiFont::MeasureText(const StringView& text, const TextLayoutOptions& layout) -{ - // Check if there is no need to do anything - if (text.IsEmpty()) - return Float2::Zero; - - // Process text - Array lines; - ProcessText(text, lines, layout); - - // Calculate bounds - Float2 max = Float2::Zero; - for (int32 i = 0; i < lines.Count(); i++) - { - const MultiFontLineCache& line = lines[i]; - max = Float2::Max(max, line.Location + line.Size); - } - - return max; -} - diff --git a/Source/Engine/Render2D/MultiFont.h b/Source/Engine/Render2D/MultiFont.h deleted file mode 100644 index 870b8e668..000000000 --- a/Source/Engine/Render2D/MultiFont.h +++ /dev/null @@ -1,353 +0,0 @@ -#pragma once - -#include "Engine/Core/Collections/Array.h" -#include "Engine/Core/Collections/Dictionary.h" -#include "Font.h" -#include "FontAsset.h" - -struct TextRange; -class Font; -class FontAsset; - -/// -/// The font block info generated during text processing. -/// -API_STRUCT(NoDefault) struct MultiFontBlockCache -{ - DECLARE_SCRIPTING_TYPE_MINIMAL(MultiFontBlockCache); - - /// - /// The root position of the block (upper left corner), relative to line. - /// - API_FIELD() Float2 Location; - - /// - /// The height of the current block - /// - API_FIELD() Float2 Size; - - /// - /// The first character index (from the input text). - /// - API_FIELD() int32 FirstCharIndex; - - /// - /// The last character index (from the input text), inclusive. - /// - API_FIELD() int32 LastCharIndex; - - /// - /// The index of the font to render with - /// - API_FIELD() int32 FontIndex; -}; - -template<> -struct TIsPODType -{ - enum { Value = true }; -}; - -/// -/// Line of font blocks info generated during text processing. -/// -API_STRUCT(NoDefault) struct MultiFontLineCache -{ - DECLARE_SCRIPTING_TYPE_MINIMAL(MultiFontLineCache); - - /// - /// The root position of the line (upper left corner). - /// - API_FIELD() Float2 Location; - - /// - /// The line bounds (width and height). - /// - API_FIELD() Float2 Size; - - /// - /// The maximum ascender of the line. - /// - API_FIELD() float MaxAscender; - - /// - /// The index of the font to render with - /// - API_FIELD() Array Blocks; -}; - -API_CLASS(Sealed, NoSpawn) class FLAXENGINE_API MultiFont : public ManagedScriptingObject -{ - DECLARE_SCRIPTING_TYPE_NO_SPAWN(MultiFont); -private: - Array _fonts; - -public: - MultiFont(const Array& fonts); - - API_FUNCTION() FORCE_INLINE static MultiFont* Create(const Array& fonts) { - return New(fonts); - } - - API_FUNCTION() FORCE_INLINE static MultiFont* Create(const Array& fontAssets, float size) { - Array fonts; - fonts.Resize(fontAssets.Count()); - for (int32 i = 0; i < fontAssets.Count(); i++) - { - fonts[i] = fontAssets[i]->CreateFont(size); - } - - return New(fonts); - } - - API_PROPERTY() FORCE_INLINE Array& GetFonts() { - return _fonts; - } - - API_PROPERTY() FORCE_INLINE void SetFonts(const Array& val) { - _fonts = val; - } - - API_PROPERTY() FORCE_INLINE int32 GetMaxHeight() { - int32 maxHeight = 0; - for (int32 i = 0; i < _fonts.Count(); i++) - { - if (_fonts[i]) { - maxHeight = Math::Max(maxHeight, _fonts[i]->GetHeight()); - } - } - - return maxHeight; - } - - API_PROPERTY() FORCE_INLINE int32 GetMaxAscender() { - int32 maxAsc = 0; - for (int32 i = 0; i < _fonts.Count(); i++) - { - if (_fonts[i]) { - maxAsc = Math::Max(maxAsc, _fonts[i]->GetAscender()); - } - } - - return maxAsc; - } - - /// - /// Processes text to get cached lines for rendering. - /// - /// The input text. - /// The layout properties. - /// The output lines list. - void ProcessText(const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout); - - /// - /// Processes text to get cached lines for rendering. - /// - /// The input text. - /// The layout properties. - /// The output lines list. - API_FUNCTION() Array ProcessText(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout) - { - Array lines; - ProcessText(text, lines, layout); - return lines; - } - - /// - /// Processes text to get cached lines for rendering. - /// - /// The input text. - /// The input text range (substring range of the input text parameter). - /// The layout properties. - /// The output lines list. - API_FUNCTION() Array ProcessText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) - { - Array lines; - ProcessText(textRange.Substring(text), lines, layout); - return lines; - } - - /// - /// Processes text to get cached lines for rendering. - /// - /// The input text. - /// The output lines list. - API_FUNCTION() FORCE_INLINE Array ProcessText(const StringView& text) - { - return ProcessText(text, TextLayoutOptions()); - } - - /// - /// Processes text to get cached lines for rendering. - /// - /// The input text. - /// The input text range (substring range of the input text parameter). - /// The output lines list. - API_FUNCTION() FORCE_INLINE Array ProcessText(const StringView& text, API_PARAM(Ref) const TextRange& textRange) - { - return ProcessText(textRange.Substring(text), TextLayoutOptions()); - } - - /// - /// Measures minimum size of the rectangle that will be needed to draw given text. - /// - /// The input text to test. - /// The layout properties. - /// The minimum size for that text and fot to render properly. - API_FUNCTION() Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout); - - /// - /// Measures minimum size of the rectangle that will be needed to draw given text. - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The layout properties. - /// The minimum size for that text and fot to render properly. - API_FUNCTION() Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) - { - return MeasureText(textRange.Substring(text), layout); - } - - /// - /// Measures minimum size of the rectangle that will be needed to draw given text - /// . - /// The input text to test. - /// The minimum size for that text and fot to render properly. - API_FUNCTION() FORCE_INLINE Float2 MeasureText(const StringView& text) - { - return MeasureText(text, TextLayoutOptions()); - } - - /// - /// Measures minimum size of the rectangle that will be needed to draw given text - /// . - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The minimum size for that text and fot to render properly. - API_FUNCTION() FORCE_INLINE Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextRange& textRange) - { - return MeasureText(textRange.Substring(text), TextLayoutOptions()); - } - - /// - /// Calculates hit character index at given location. - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The input location to test. - /// The text layout properties. - /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() int32 HitTestText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout) - { - return HitTestText(textRange.Substring(text), location, layout); - } - - /// - /// Calculates hit character index at given location. - /// - /// The input text to test. - /// The input location to test. - /// The text layout properties. - /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() int32 HitTestText(const StringView& text, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout); - - /// - /// Calculates hit character index at given location. - /// - /// The input text to test. - /// The input location to test. - /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() FORCE_INLINE int32 HitTestText(const StringView& text, const Float2& location) - { - return HitTestText(text, location, TextLayoutOptions()); - } - - /// - /// Calculates hit character index at given location. - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The input location to test. - /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() FORCE_INLINE int32 HitTestText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location) - { - return HitTestText(textRange.Substring(text), location, TextLayoutOptions()); - } - - /// - /// Calculates character position for given text and character index. - /// - /// The input text to test. - /// The text position to get coordinates of. - /// The text layout properties. - /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() Float2 GetCharPosition(const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout); - - /// - /// Calculates character position for given text and character index. - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The text position to get coordinates of. - /// The text layout properties. - /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() Float2 GetCharPosition(const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout) - { - return GetCharPosition(textRange.Substring(text), index, layout); - } - - /// - /// Calculates character position for given text and character index - /// - /// The input text to test. - /// The text position to get coordinates of. - /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(const StringView& text, int32 index) - { - return GetCharPosition(text, index, TextLayoutOptions()); - } - - /// - /// Calculates character position for given text and character index - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The text position to get coordinates of. - /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index) - { - return GetCharPosition(textRange.Substring(text), index, TextLayoutOptions()); - } - - /// - /// Gets the index of the font that should be used to render the char - /// - /// The font list. - /// The char. - /// Number to return if char cannot be found. - /// - API_FUNCTION() FORCE_INLINE int32 GetCharFontIndex(Char c, int32 missing = -1) { - int32 fontIndex = 0; - while (fontIndex < _fonts.Count() && _fonts[fontIndex] && !_fonts[fontIndex]->ContainsChar(c)) - { - fontIndex++; - } - - if (fontIndex == _fonts.Count()) { - return missing; - } - - return fontIndex; - } - - API_FUNCTION() FORCE_INLINE bool Verify() { - for (int32 i = 0; i < _fonts.Count(); i++) - { - if (!_fonts[i]) { - return false; - } - } - - return true; - } -}; diff --git a/Source/Engine/Render2D/Render2D.cpp b/Source/Engine/Render2D/Render2D.cpp index c68c2445d..4e0cacb44 100644 --- a/Source/Engine/Render2D/Render2D.cpp +++ b/Source/Engine/Render2D/Render2D.cpp @@ -3,7 +3,7 @@ #include "Render2D.h" #include "Font.h" #include "FontManager.h" -#include "MultiFont.h" +#include "FallbackFonts.h" #include "FontTextureAtlas.h" #include "RotatedRectangle.h" #include "SpriteAtlas.h" @@ -195,7 +195,7 @@ namespace // Drawing Array DrawCalls; Array Lines; - Array MultiFontLines; + Array BlockedTextLines; Array Lines2; bool IsScissorsRectEmpty; bool IsScissorsRectEnabled; @@ -1370,16 +1370,16 @@ void Render2D::DrawText(Font* font, const StringView& text, const TextRange& tex DrawText(font, textRange.Substring(text), color, layout, customMaterial); } -void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial) +void Render2D::DrawText(Font* font, FallbackFonts* fallbacks, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial) { RENDER2D_CHECK_RENDERING_STATE; - const Array& fonts = multiFont->GetFonts(); // Check if there is no need to do anything - if (fonts.IsEmpty() || text.Length() < 0) + if (font == nullptr || text.Length() < 0) return; // Temporary data + const Array& fallbackFonts = fallbacks->GetFontList(font->GetSize()); uint32 fontAtlasIndex = 0; FontTextureAtlas* fontAtlas = nullptr; Float2 invAtlasSize = Float2::One; @@ -1406,11 +1406,19 @@ void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const Colo int32 lineIndex = 0; maxAscenders.Add(0); + + auto getFont = [&](int32 index)->Font* { + return index >= 0 ? fallbackFonts[index] : font; + }; + + // Preprocess the text to determine vertical offset of blocks for (int32 currentIndex = 0; currentIndex < text.Length(); currentIndex++) { - if (text[currentIndex] != '\n') { - int32 fontIndex = multiFont->GetCharFontIndex(text[currentIndex], 0); - maxAscenders[lineIndex] = Math::Max(maxAscenders[lineIndex], static_cast(fonts[fontIndex]->GetAscender())); + const Char c = text[currentIndex]; + if (c != '\n') { + int32 fontIndex = fallbacks->GetCharFallbackIndex(c, font); + maxAscenders[lineIndex] = Math::Max(maxAscenders[lineIndex], + static_cast(getFont(fontIndex)->GetAscender())); } else { lineIndex++; @@ -1424,7 +1432,7 @@ void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const Colo // The starting index of the current block int32 startIndex = 0; // The index of the font used by the current block - int32 currentFontIndex = multiFont->GetCharFontIndex(text[0], 0); + int32 currentFontIndex = fallbacks->GetCharFallbackIndex(text[0], font); // The maximum font height of the current line float maxHeight = 0; for (int32 currentIndex = 0; currentIndex < text.Length(); currentIndex++) @@ -1446,7 +1454,7 @@ void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const Colo { // Get character entry if (nextCharIndex < text.Length()) { - nextFontIndex = multiFont->GetCharFontIndex(text[nextCharIndex], currentFontIndex); + nextFontIndex = fallbacks->GetCharFallbackIndex(text[nextCharIndex], font); } if (nextFontIndex != currentFontIndex) { @@ -1461,13 +1469,13 @@ void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const Colo if (moveBlock) { // Render the pending block before beginning the new block - auto fontHeight = fonts[currentFontIndex]->GetHeight(); + auto fontHeight = getFont(currentFontIndex)->GetHeight(); maxHeight = Math::Max(maxHeight, static_cast(fontHeight)); - auto fontDescender = fonts[currentFontIndex]->GetDescender(); + auto fontDescender = getFont(currentFontIndex)->GetDescender(); for (int32 renderIndex = startIndex; renderIndex <= currentIndex; renderIndex++) { // Get character entry - fonts[currentFontIndex]->GetCharacter(text[renderIndex], entry); + getFont(currentFontIndex)->GetCharacter(text[renderIndex], entry); // Check if need to select/change font atlas (since characters even in the same font may be located in different atlases) if (fontAtlas == nullptr || entry.TextureIndex != fontAtlasIndex) @@ -1494,7 +1502,7 @@ void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const Colo // Get kerning if (!isWhitespace && previous.IsValid) { - kerning = fonts[currentFontIndex]->GetKerning(previous.Character, entry.Character); + kerning = getFont(currentFontIndex)->GetKerning(previous.Character, entry.Character); } else { @@ -1510,7 +1518,7 @@ void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const Colo const float x = pointer.X + entry.OffsetX * scale; const float y = pointer.Y + (fontHeight + fontDescender - entry.OffsetY) * scale; - Rectangle charRect(x, y + (maxAscenders[lineIndex] - fonts[currentFontIndex]->GetAscender()) / 2, entry.UVSize.X * scale, entry.UVSize.Y * scale); + Rectangle charRect(x, y + (maxAscenders[lineIndex] - getFont(currentFontIndex)->GetAscender()) / 2, entry.UVSize.X * scale, entry.UVSize.Y * scale); Float2 upperLeftUV = entry.UV * invAtlasSize; Float2 rightBottomUV = (entry.UV + entry.UVSize) * invAtlasSize; @@ -1541,21 +1549,21 @@ void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const Colo } } -void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial) +void Render2D::DrawText(Font* font, FallbackFonts* fallbacks, const StringView& text, const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial) { - DrawText(multiFont, textRange.Substring(text), color, location, customMaterial); + DrawText(font, fallbacks, textRange.Substring(text), color, location, customMaterial); } -void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) +void Render2D::DrawText(Font* font, FallbackFonts* fallbacks, const StringView& text, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) { RENDER2D_CHECK_RENDERING_STATE; - const Array& fonts = multiFont->GetFonts(); // Check if there is no need to do anything - if (fonts.IsEmpty() || text.IsEmpty() || layout.Scale <= ZeroTolerance) + if (font == nullptr || text.IsEmpty() || layout.Scale <= ZeroTolerance) return; // Temporary data + const Array& fallbackFonts = fallbacks->GetFontList(font->GetSize()); uint32 fontAtlasIndex = 0; FontTextureAtlas* fontAtlas = nullptr; Float2 invAtlasSize = Float2::One; @@ -1564,8 +1572,8 @@ void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const Colo float scale = layout.Scale / FontManager::FontScale; // Process text to get lines - MultiFontLines.Clear(); - multiFont->ProcessText(text, MultiFontLines, layout); + BlockedTextLines.Clear(); + font->ProcessText(fallbacks, text, BlockedTextLines, layout); // Render all lines FontCharacterEntry entry; @@ -1581,14 +1589,18 @@ void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const Colo drawCall.AsChar.Mat = nullptr; } - for (int32 lineIndex = 0; lineIndex < MultiFontLines.Count(); lineIndex++) + auto getFont = [&](int32 index)->Font* { + return index >= 0 ? fallbackFonts[index] : font; + }; + + for (int32 lineIndex = 0; lineIndex < BlockedTextLines.Count(); lineIndex++) { - const MultiFontLineCache& line = MultiFontLines[lineIndex]; + const BlockedTextLineCache& line = BlockedTextLines[lineIndex]; for (int32 blockIndex = 0; blockIndex < line.Blocks.Count(); blockIndex++) { - const MultiFontBlockCache& block = MultiFontLines[lineIndex].Blocks[blockIndex]; - auto fontHeight = fonts[block.FontIndex]->GetHeight(); - auto fontDescender = fonts[block.FontIndex]->GetDescender(); + const FontBlockCache& block = BlockedTextLines[lineIndex].Blocks[blockIndex]; + auto fontHeight = getFont(block.FallbackFontIndex)->GetHeight(); + auto fontDescender = getFont(block.FallbackFontIndex)->GetDescender(); Float2 pointer = line.Location + block.Location; for (int32 charIndex = block.FirstCharIndex; charIndex <= block.LastCharIndex; charIndex++) @@ -1600,7 +1612,7 @@ void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const Colo } // Get character entry - fonts[block.FontIndex]->GetCharacter(c, entry); + getFont(block.FallbackFontIndex)->GetCharacter(c, entry); // Check if need to select/change font atlas (since characters even in the same font may be located in different atlases) if (fontAtlas == nullptr || entry.TextureIndex != fontAtlasIndex) @@ -1625,7 +1637,7 @@ void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const Colo const bool isWhitespace = StringUtils::IsWhitespace(c); if (!isWhitespace && previous.IsValid) { - kerning = fonts[block.FontIndex]->GetKerning(previous.Character, entry.Character); + kerning = getFont(block.FallbackFontIndex)->GetKerning(previous.Character, entry.Character); } else { @@ -1661,9 +1673,9 @@ void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const Colo } } -void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const TextRange& textRange, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) +void Render2D::DrawText(Font* font, FallbackFonts* fallbacks, const StringView& text, const TextRange& textRange, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) { - DrawText(multiFont, textRange.Substring(text), color, layout, customMaterial); + DrawText(font, fallbacks, textRange.Substring(text), color, layout, customMaterial); } FORCE_INLINE bool NeedAlphaWithTint(const Color& color) diff --git a/Source/Engine/Render2D/Render2D.h b/Source/Engine/Render2D/Render2D.h index 5050171b2..c99d38104 100644 --- a/Source/Engine/Render2D/Render2D.h +++ b/Source/Engine/Render2D/Render2D.h @@ -15,7 +15,7 @@ struct Matrix3x3; struct Viewport; struct TextRange; class Font; -class MultiFont; +class FallbackFonts; class GPUPipelineState; class GPUTexture; class GPUTextureView; @@ -225,7 +225,7 @@ public: /// The text color. /// The text location. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawText(MultiFont* multiFont, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawText(Font* font, FallbackFonts* fallbacks, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); /// /// Draws a text with formatting. @@ -235,7 +235,7 @@ public: /// The text color. /// The text layout properties. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawText(MultiFont* multiFont, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawText(Font* font, FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); /// /// Draws a text with formatting. @@ -246,7 +246,7 @@ public: /// The text color. /// The text layout properties. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawText(MultiFont* multiFont, const StringView& text, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawText(Font* font, FallbackFonts* fallbacks, const StringView& text, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); /// /// Draws a text with formatting. @@ -257,7 +257,7 @@ public: /// The text color. /// The text layout properties. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawText(MultiFont* multiFont, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawText(Font* font, FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); /// /// Fills a rectangle area. From cdbe59a3fbe4988660d9684b2fc9122ad2be4907 Mon Sep 17 00:00:00 2001 From: ExMatics HydrogenC <33123710+HydrogenC@users.noreply.github.com> Date: Thu, 30 Nov 2023 23:12:56 +0800 Subject: [PATCH 36/61] Add fallback settings to CSharp --- .../Content/Create/CreateFilesDialog.cs | 2 +- .../Content/Import/ImportFilesDialog.cs | 2 +- .../CustomEditors/Dedicated/RagdollEditor.cs | 2 +- .../CustomEditors/Dedicated/ScriptsEditor.cs | 2 +- .../Dedicated/UIControlEditor.cs | 6 +- .../Editors/ActorTransformEditor.cs | 2 +- .../Editor/CustomEditors/Editors/TagEditor.cs | 2 +- .../CustomEditors/LayoutElementsContainer.cs | 4 +- Source/Editor/GUI/ColumnDefinition.cs | 2 +- Source/Editor/GUI/ComboBox.cs | 6 +- .../GUI/ContextMenu/ContextMenuButton.cs | 4 +- Source/Editor/GUI/CurveEditor.cs | 2 +- Source/Editor/GUI/Docking/DockWindow.cs | 2 +- Source/Editor/GUI/MainMenu.cs | 8 +- Source/Editor/GUI/MainMenuButton.cs | 2 +- Source/Editor/GUI/NavigationButton.cs | 2 +- Source/Editor/GUI/Row.cs | 4 +- .../Editor/GUI/Timeline/Tracks/MemberTrack.cs | 2 +- Source/Editor/GUI/ToolStripButton.cs | 2 +- Source/Editor/GUI/Tree/TreeNode.cs | 10 +- Source/Editor/Options/InterfaceOptions.cs | 54 ++++---- Source/Editor/Options/OptionsModule.cs | 20 +-- .../Archetypes/Animation.StateMachine.cs | 4 +- .../Archetypes/Animation.TransitionEditor.cs | 2 +- Source/Editor/Surface/Archetypes/Animation.cs | 2 +- .../Editor/Surface/Archetypes/BehaviorTree.cs | 4 +- Source/Editor/Surface/Archetypes/Function.cs | 2 +- Source/Editor/Surface/AttributesEditor.cs | 2 +- .../Editor/Surface/ContextMenu/VisjectCM.cs | 2 +- .../Surface/ContextMenu/VisjectCMItem.cs | 2 +- Source/Editor/Surface/Elements/InputBox.cs | 4 +- Source/Editor/Surface/SurfaceNode.cs | 6 +- Source/Editor/Tools/Foliage/FoliageTab.cs | 2 +- Source/Editor/Tools/Terrain/CarveTab.cs | 2 +- .../Tools/Terrain/CreateTerrainDialog.cs | 2 +- Source/Editor/Utilities/TextRenderUtils.cs | 108 ++++++++++++++++ Source/Editor/Viewport/EditorViewport.cs | 6 +- .../Viewport/Widgets/ViewportWidgetButton.cs | 2 +- Source/Editor/Windows/AboutDialog.cs | 4 +- Source/Editor/Windows/Assets/FontWindow.cs | 2 +- Source/Editor/Windows/ContentWindow.Search.cs | 2 +- Source/Editor/Windows/PluginsWindow.cs | 9 +- Source/Editor/Windows/Profiler/Timeline.cs | 2 +- Source/Engine/Render2D/FallbackFonts.h | 2 +- Source/Engine/Render2D/MultiFontReference.cs | 74 ----------- Source/Engine/Render2D/Render2D.cs | 118 ++++++++++-------- Source/Engine/Scripting/Scripting.cs | 10 +- Source/Engine/UI/GUI/Common/Button.cs | 8 +- Source/Engine/UI/GUI/Common/Dropdown.cs | 8 +- Source/Engine/UI/GUI/Common/Label.cs | 14 +-- Source/Engine/UI/GUI/Common/RichTextBox.cs | 2 +- Source/Engine/UI/GUI/Common/TextBox.cs | 37 ++++-- Source/Engine/UI/GUI/Panels/DropPanel.cs | 6 +- Source/Engine/UI/GUI/Style.cs | 38 +++--- 54 files changed, 357 insertions(+), 273 deletions(-) create mode 100644 Source/Editor/Utilities/TextRenderUtils.cs delete mode 100644 Source/Engine/Render2D/MultiFontReference.cs diff --git a/Source/Editor/Content/Create/CreateFilesDialog.cs b/Source/Editor/Content/Create/CreateFilesDialog.cs index 48e4920bb..d48e878bc 100644 --- a/Source/Editor/Content/Create/CreateFilesDialog.cs +++ b/Source/Editor/Content/Create/CreateFilesDialog.cs @@ -39,7 +39,7 @@ namespace FlaxEditor.Content.Create AnchorPreset = AnchorPresets.HorizontalStretchTop, Offsets = new Margin(0, 0, 0, 40), Parent = this, - Font = new MultiFontReference(Style.Current.FontTitle) + Font = new FontReference(Style.Current.FontTitle) }; var infoLabel = new Label { diff --git a/Source/Editor/Content/Import/ImportFilesDialog.cs b/Source/Editor/Content/Import/ImportFilesDialog.cs index 5a142d0f6..967583cf6 100644 --- a/Source/Editor/Content/Import/ImportFilesDialog.cs +++ b/Source/Editor/Content/Import/ImportFilesDialog.cs @@ -60,7 +60,7 @@ namespace FlaxEditor.Content.Import AnchorPreset = AnchorPresets.HorizontalStretchTop, Offsets = new Margin(0, 0, 0, 40), Parent = this, - Font = new MultiFontReference(Style.Current.FontTitle) + Font = new FontReference(Style.Current.FontTitle) }; var infoLabel = new Label { diff --git a/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs b/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs index b6d14d81e..c4b334b3a 100644 --- a/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs @@ -81,7 +81,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Title var title = new Label(2, 2, width - 4, 23.0f) { - Font = new MultiFontReference(FlaxEngine.GUI.Style.Current.FontLarge), + Font = new FontReference(FlaxEngine.GUI.Style.Current.FontLarge), Text = "Ragdoll Options", Parent = this }; diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs index fd56422cb..90ae9ae54 100644 --- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs @@ -43,7 +43,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Add script button var buttonText = "Add script"; - var textSize = Style.Current.FontMedium.MeasureText(buttonText); + var textSize = Render2D.MeasureText(Style.Current.FontMedium, buttonText); float addScriptButtonWidth = (textSize.X < 60.0f) ? 60.0f : textSize.X + 4; var buttonHeight = (textSize.Y < 18) ? 18 : textSize.Y + 4; _addScriptsButton = new Button diff --git a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs index 9627edfd8..b8250918f 100644 --- a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs @@ -239,7 +239,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Title var title = new Label(2, 2, DialogWidth - 4, TitleHeight) { - Font = new MultiFontReference(style.FontLarge), + Font = new FontReference(style.FontLarge), Text = "Anchor Presets", Parent = this }; @@ -247,7 +247,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Info var info = new Label(0, title.Bottom, DialogWidth, InfoHeight) { - Font = new MultiFontReference(style.FontSmall), + Font = new FontReference(style.FontSmall), Text = "Shift: also set bounds\nControl: also set pivot", Parent = this }; @@ -423,7 +423,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Set control type button var space = layout.Space(20); var buttonText = "Set Type"; - var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(buttonText); + var textSize = Render2D.MeasureText(FlaxEngine.GUI.Style.Current.FontMedium, buttonText); float setTypeButtonWidth = (textSize.X < 60.0f) ? 60.0f : textSize.X + 4; var setTypeButton = new Button { diff --git a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs index 0cad200fd..e84bf5914 100644 --- a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs @@ -101,7 +101,7 @@ namespace FlaxEditor.CustomEditors.Editors _linkButton.Clicked += ToggleLink; ToggleEnabled(); SetLinkStyle(); - var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(LinkedLabel.Text.Value); + var textSize = Render2D.MeasureText(FlaxEngine.GUI.Style.Current.FontMedium, LinkedLabel.Text.Value); _linkButton.LocalX += textSize.X + 10; LinkedLabel.SetupContextMenu += (label, menu, editor) => { diff --git a/Source/Editor/CustomEditors/Editors/TagEditor.cs b/Source/Editor/CustomEditors/Editors/TagEditor.cs index dbd5d124c..49ac9d937 100644 --- a/Source/Editor/CustomEditors/Editors/TagEditor.cs +++ b/Source/Editor/CustomEditors/Editors/TagEditor.cs @@ -631,7 +631,7 @@ namespace FlaxEditor.CustomEditors.Editors TooltipText = "Edit...", Parent = _label, }; - var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(buttonText); + var textSize = Render2D.MeasureText(FlaxEngine.GUI.Style.Current.FontMedium, buttonText); if (textSize.Y > button.Width) button.Width = textSize.Y + 2; diff --git a/Source/Editor/CustomEditors/LayoutElementsContainer.cs b/Source/Editor/CustomEditors/LayoutElementsContainer.cs index 1584e88de..936851b15 100644 --- a/Source/Editor/CustomEditors/LayoutElementsContainer.cs +++ b/Source/Editor/CustomEditors/LayoutElementsContainer.cs @@ -276,7 +276,7 @@ namespace FlaxEditor.CustomEditors public LabelElement Header(string text) { var element = Label(text); - element.Label.Font = new MultiFontReference(Style.Current.FontLarge); + element.Label.Font = new FontReference(Style.Current.FontLarge); return element; } @@ -284,7 +284,7 @@ namespace FlaxEditor.CustomEditors { var element = Header(header.Text); if (header.FontSize != -1) - element.Label.Font = new MultiFontReference(element.Label.Font, header.FontSize); + element.Label.Font = new FontReference(element.Label.Font.Font, header.FontSize); if (header.Color != 0) element.Label.TextColor = Color.FromRGBA(header.Color); return element; diff --git a/Source/Editor/GUI/ColumnDefinition.cs b/Source/Editor/GUI/ColumnDefinition.cs index c6e8f2889..aff1817c3 100644 --- a/Source/Editor/GUI/ColumnDefinition.cs +++ b/Source/Editor/GUI/ColumnDefinition.cs @@ -35,7 +35,7 @@ namespace FlaxEditor.GUI /// /// The title font. /// - public MultiFont TitleFont; + public Font TitleFont; /// /// The column title text color. diff --git a/Source/Editor/GUI/ComboBox.cs b/Source/Editor/GUI/ComboBox.cs index 8e6cf39a0..0417cc7e3 100644 --- a/Source/Editor/GUI/ComboBox.cs +++ b/Source/Editor/GUI/ComboBox.cs @@ -191,7 +191,7 @@ namespace FlaxEditor.GUI /// Gets or sets the font used to draw text. /// [EditorDisplay("Style"), EditorOrder(2000)] - public MultiFontReference Font { get; set; } + public FontReference Font { get; set; } /// /// Gets or sets the color of the text. @@ -273,7 +273,7 @@ namespace FlaxEditor.GUI MaximumItemsInViewCount = 20; var style = Style.Current; - Font = new MultiFontReference(style.FontMedium); + Font = new FontReference(style.FontMedium); TextColor = style.Foreground; BackgroundColor = style.BackgroundNormal; BackgroundColorHighlighted = BackgroundColor; @@ -554,7 +554,7 @@ namespace FlaxEditor.GUI var textRect = new Rectangle(margin, 0, clientRect.Width - boxSize - 2.0f * margin, clientRect.Height); Render2D.PushClip(textRect); var textColor = TextColor; - Render2D.DrawText(Font.GetMultiFont(), text, textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, textScale); + Render2D.DrawText(Font.GetFont(), text, textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, textScale); Render2D.PopClip(); } diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs b/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs index ba3326412..3137de240 100644 --- a/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs +++ b/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs @@ -236,9 +236,9 @@ namespace FlaxEditor.GUI.ContextMenu float width = 20; if (style.FontMedium) { - width += style.FontMedium.MeasureText(Text).X; + width += Render2D.MeasureText(style.FontMedium, Text).X; if (!string.IsNullOrEmpty(ShortKeys)) - width += 40 + style.FontMedium.MeasureText(ShortKeys).X; + width += 40 + Render2D.MeasureText(style.FontMedium, ShortKeys).X; } return Mathf.Max(width, base.MinimumWidth); diff --git a/Source/Editor/GUI/CurveEditor.cs b/Source/Editor/GUI/CurveEditor.cs index ff14cdb24..22deec120 100644 --- a/Source/Editor/GUI/CurveEditor.cs +++ b/Source/Editor/GUI/CurveEditor.cs @@ -317,7 +317,7 @@ namespace FlaxEditor.GUI private Color _contentsColor; private Color _linesColor; private Color _labelsColor; - private MultiFont _labelsFont; + private Font _labelsFont; /// /// The keyframe UI points. diff --git a/Source/Editor/GUI/Docking/DockWindow.cs b/Source/Editor/GUI/Docking/DockWindow.cs index 374885f01..36a4c112e 100644 --- a/Source/Editor/GUI/Docking/DockWindow.cs +++ b/Source/Editor/GUI/Docking/DockWindow.cs @@ -489,7 +489,7 @@ namespace FlaxEditor.GUI.Docking { var style = Style.Current; if (style?.FontMedium != null) - _titleSize = style.FontMedium.MeasureText(_title); + _titleSize = Render2D.MeasureText(style.FontMedium, _title); } base.PerformLayoutBeforeChildren(); diff --git a/Source/Editor/GUI/MainMenu.cs b/Source/Editor/GUI/MainMenu.cs index aadfbf0a7..b313fed9f 100644 --- a/Source/Editor/GUI/MainMenu.cs +++ b/Source/Editor/GUI/MainMenu.cs @@ -76,7 +76,7 @@ namespace FlaxEditor.GUI var windowIcon = FlaxEngine.Content.LoadAsyncInternal(EditorAssets.WindowIcon); FontAsset windowIconsFont = FlaxEngine.Content.LoadAsyncInternal(EditorAssets.WindowIconsFont); - MultiFont iconFont = new MultiFontReference([windowIconsFont], 9).GetMultiFont(); + Font iconFont = windowIconsFont?.CreateFont(9); _window = mainWindow.RootWindow.Window; _window.HitTest += OnHitTest; @@ -108,7 +108,7 @@ namespace FlaxEditor.GUI _closeButton = new Button { Text = ((char)EditorAssets.SegMDL2Icons.ChromeClose).ToString(), - Font = new MultiFontReference(iconFont), + Font = new FontReference(iconFont), BackgroundColor = Color.Transparent, BorderColor = Color.Transparent, BorderColorHighlighted = Color.Transparent, @@ -124,7 +124,7 @@ namespace FlaxEditor.GUI _minimizeButton = new Button { Text = ((char)EditorAssets.SegMDL2Icons.ChromeMinimize).ToString(), - Font = new MultiFontReference(iconFont), + Font = new FontReference(iconFont), BackgroundColor = Color.Transparent, BorderColor = Color.Transparent, BorderColorHighlighted = Color.Transparent, @@ -139,7 +139,7 @@ namespace FlaxEditor.GUI _maximizeButton = new Button { Text = ((char)(_window.IsMaximized ? EditorAssets.SegMDL2Icons.ChromeRestore : EditorAssets.SegMDL2Icons.ChromeMaximize)).ToString(), - Font = new MultiFontReference(iconFont), + Font = new FontReference(iconFont), BackgroundColor = Color.Transparent, BorderColor = Color.Transparent, BorderColorHighlighted = Color.Transparent, diff --git a/Source/Editor/GUI/MainMenuButton.cs b/Source/Editor/GUI/MainMenuButton.cs index 3bc57479c..3440996aa 100644 --- a/Source/Editor/GUI/MainMenuButton.cs +++ b/Source/Editor/GUI/MainMenuButton.cs @@ -103,7 +103,7 @@ namespace FlaxEditor.GUI float width = 18; if (style.FontMedium) - width += style.FontMedium.MeasureText(Text).X; + width += Render2D.MeasureText(style.FontMedium, Text).X; Width = width; } diff --git a/Source/Editor/GUI/NavigationButton.cs b/Source/Editor/GUI/NavigationButton.cs index 6fd17332c..6face21ef 100644 --- a/Source/Editor/GUI/NavigationButton.cs +++ b/Source/Editor/GUI/NavigationButton.cs @@ -68,7 +68,7 @@ namespace FlaxEditor.GUI if (style.FontMedium) { - Width = style.FontMedium.MeasureText(Text).X + 2 * DefaultMargin; + Width = Render2D.MeasureText(style.FontMedium, Text).X + 2 * DefaultMargin; } } } diff --git a/Source/Editor/GUI/Row.cs b/Source/Editor/GUI/Row.cs index 7533dfb17..ab74cbe96 100644 --- a/Source/Editor/GUI/Row.cs +++ b/Source/Editor/GUI/Row.cs @@ -39,8 +39,8 @@ namespace FlaxEditor.GUI { Depth = -1; - if (Height < Style.Current.FontMedium.MaxHeight) - Height = Style.Current.FontMedium.MaxHeight + 4; + if (Height < Style.Current.FontMedium.Height) + Height = Style.Current.FontMedium.Height + 4; } /// diff --git a/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs b/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs index 63787df2c..b4433622e 100644 --- a/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs @@ -345,7 +345,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks if (_previewValue != null) { // Based on Track.Draw for track text placement - var left = _xOffset + 16 + Style.Current.FontSmall.MeasureText(Title ?? Name).X; + var left = _xOffset + 16 + Render2D.MeasureText(Style.Current.FontSmall, Title ?? Name).X; if (Icon.IsValid) left += 18; if (IsExpanded) diff --git a/Source/Editor/GUI/ToolStripButton.cs b/Source/Editor/GUI/ToolStripButton.cs index d21fd5689..cf34fd36b 100644 --- a/Source/Editor/GUI/ToolStripButton.cs +++ b/Source/Editor/GUI/ToolStripButton.cs @@ -152,7 +152,7 @@ namespace FlaxEditor.GUI if (hasSprite) width += iconSize; if (!string.IsNullOrEmpty(_text) && style.FontMedium) - width += style.FontMedium.MeasureText(_text).X + (hasSprite ? DefaultMargin : 0); + width += Render2D.MeasureText(style.FontMedium, _text).X + (hasSprite ? DefaultMargin : 0); Width = width; } diff --git a/Source/Editor/GUI/Tree/TreeNode.cs b/Source/Editor/GUI/Tree/TreeNode.cs index acb67ea8f..7b47e5eb6 100644 --- a/Source/Editor/GUI/Tree/TreeNode.cs +++ b/Source/Editor/GUI/Tree/TreeNode.cs @@ -115,7 +115,7 @@ namespace FlaxEditor.GUI.Tree /// Gets or sets the font used to render text. /// [EditorDisplay("Style"), EditorOrder(2000)] - public MultiFontReference TextFont { get; set; } + public FontReference TextFont { get; set; } /// /// Gets or sets the color of the background when tree node is selected. @@ -318,7 +318,7 @@ namespace FlaxEditor.GUI.Tree BackgroundColorSelected = style.BackgroundSelected; BackgroundColorHighlighted = style.BackgroundHighlighted; BackgroundColorSelectedUnfocused = style.LightBackground; - TextFont = new MultiFontReference(style.FontSmall); + TextFont = new FontReference(style.FontSmall); } /// @@ -573,10 +573,10 @@ namespace FlaxEditor.GUI.Tree { if (_textChanged) { - var font = TextFont.GetMultiFont(); + var font = TextFont.GetFont(); if (font) { - _textWidth = font.MeasureText(_text).X; + _textWidth = Render2D.MeasureText(font, _text).X; _textChanged = false; } } @@ -657,7 +657,7 @@ namespace FlaxEditor.GUI.Tree } // Draw text - Render2D.DrawText(TextFont.GetMultiFont(), _text, textRect, _cachedTextColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(TextFont.GetFont(), _text, textRect, _cachedTextColor, TextAlignment.Near, TextAlignment.Center); // Draw drag and drop effect if (IsDragOver && _tree.DraggedOverNode == this) diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index 91eebe4eb..af432baa7 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -171,8 +171,10 @@ namespace FlaxEditor.Options get => _outputLogFont; set { - if (value == null || !value.Font) - _outputLogFont = new FontReference(ConsoleFont, 10); + if (value == null) + _outputLogFont = new FontReference(FlaxEngine.Content.LoadAsyncInternal(EditorAssets.InconsolataRegularFont), 10); + else if (!value.Font) + _outputLogFont.Font = FlaxEngine.Content.LoadAsyncInternal(EditorAssets.InconsolataRegularFont); else _outputLogFont = value; } @@ -234,30 +236,34 @@ namespace FlaxEditor.Options [EditorDisplay("Cook & Run"), EditorOrder(500)] public int NumberOfGameClientsToLaunch = 1; - private static FontAsset[] DefaultFonts => - [FlaxEngine.Content.LoadAsyncInternal(EditorAssets.PrimaryFont), - FlaxEngine.Content.LoadAsyncInternal(EditorAssets.CjkFont)]; + private static FontAsset DefaultFont => FlaxEngine.Content.LoadAsyncInternal(EditorAssets.PrimaryFont); private static FontAsset ConsoleFont => FlaxEngine.Content.LoadAsyncInternal(EditorAssets.PrimaryFont); - private MultiFontReference _titleFont = new MultiFontReference(DefaultFonts, 18); - private MultiFontReference _largeFont = new MultiFontReference(DefaultFonts, 14); - private MultiFontReference _mediumFont = new MultiFontReference(DefaultFonts, 9); - private MultiFontReference _smallFont = new MultiFontReference(DefaultFonts, 9); + private FontReference _titleFont = new FontReference(DefaultFont, 18); + private FontReference _largeFont = new FontReference(DefaultFont, 14); + private FontReference _mediumFont = new FontReference(DefaultFont, 9); + private FontReference _smallFont = new FontReference(DefaultFont, 9); private FontReference _outputLogFont = new FontReference(ConsoleFont, 10); + /// + /// The fallback fonts. + /// + public FontAsset[] Fallbacks = [FlaxEngine.Content.LoadAsyncInternal(EditorAssets.CjkFont)]; /// /// Gets or sets the title font for editor UI. /// [EditorDisplay("Fonts"), EditorOrder(600), Tooltip("The title font for editor UI.")] - public MultiFontReference TitleFont + public FontReference TitleFont { get => _titleFont; set { - if (value == null || !value.Verify()) - _titleFont = new MultiFontReference(DefaultFonts, 18); + if (value == null) + _titleFont = new FontReference(DefaultFont, 18); + else if (!value.Font) + _titleFont.Font = DefaultFont; else _titleFont = value; } @@ -267,13 +273,15 @@ namespace FlaxEditor.Options /// Gets or sets the large font for editor UI. /// [EditorDisplay("Fonts"), EditorOrder(610), Tooltip("The large font for editor UI.")] - public MultiFontReference LargeFont + public FontReference LargeFont { get => _largeFont; set { - if (value == null || !value.Verify()) - _largeFont = new MultiFontReference(DefaultFonts, 14); + if (value == null) + _largeFont = new FontReference(DefaultFont, 14); + else if (!value.Font) + _largeFont.Font = DefaultFont; else _largeFont = value; } @@ -283,13 +291,15 @@ namespace FlaxEditor.Options /// Gets or sets the medium font for editor UI. /// [EditorDisplay("Fonts"), EditorOrder(620), Tooltip("The medium font for editor UI.")] - public MultiFontReference MediumFont + public FontReference MediumFont { get => _mediumFont; set { - if (value == null || !value.Verify()) - _mediumFont = new MultiFontReference(DefaultFonts, 9); + if (value == null) + _mediumFont = new FontReference(DefaultFont, 9); + else if (!value.Font) + _mediumFont.Font = DefaultFont; else _mediumFont = value; } @@ -299,13 +309,15 @@ namespace FlaxEditor.Options /// Gets or sets the small font for editor UI. /// [EditorDisplay("Fonts"), EditorOrder(630), Tooltip("The small font for editor UI.")] - public MultiFontReference SmallFont + public FontReference SmallFont { get => _smallFont; set { - if (value == null || !value.Verify()) - _smallFont = new MultiFontReference(DefaultFonts, 9); + if (value == null) + _smallFont = new FontReference(DefaultFont, 9); + else if (!value.Font) + _smallFont.Font = DefaultFont; else _smallFont = value; } diff --git a/Source/Editor/Options/OptionsModule.cs b/Source/Editor/Options/OptionsModule.cs index d138d7d0d..b13548016 100644 --- a/Source/Editor/Options/OptionsModule.cs +++ b/Source/Editor/Options/OptionsModule.cs @@ -217,12 +217,14 @@ namespace FlaxEditor.Options if (styleName == ThemeOptions.LightDefault) { Style.Current = CreateLightStyle(); - } + } else { Style.Current = CreateDefaultStyle(); } } + + Render2D.Fallbacks = FallbackFonts.Create(Options.Interface.Fallbacks); } /// @@ -259,10 +261,10 @@ namespace FlaxEditor.Options }, // Fonts - FontTitle = options.Interface.TitleFont.GetMultiFont(), - FontLarge = options.Interface.LargeFont.GetMultiFont(), - FontMedium = options.Interface.MediumFont.GetMultiFont(), - FontSmall = options.Interface.SmallFont.GetMultiFont(), + FontTitle = options.Interface.TitleFont.GetFont(), + FontLarge = options.Interface.LargeFont.GetFont(), + FontMedium = options.Interface.MediumFont.GetFont(), + FontSmall = options.Interface.SmallFont.GetFont(), // Icons ArrowDown = Editor.Icons.ArrowDown12, @@ -312,10 +314,10 @@ namespace FlaxEditor.Options ProgressNormal = new Color(0.03f, 0.65f, 0.12f, 1f), // Fonts - FontTitle = options.Interface.TitleFont.GetMultiFont(), - FontLarge = options.Interface.LargeFont.GetMultiFont(), - FontMedium = options.Interface.MediumFont.GetMultiFont(), - FontSmall = options.Interface.SmallFont.GetMultiFont(), + FontTitle = options.Interface.TitleFont.GetFont(), + FontLarge = options.Interface.LargeFont.GetFont(), + FontMedium = options.Interface.MediumFont.GetFont(), + FontSmall = options.Interface.SmallFont.GetFont(), // Icons ArrowDown = Editor.Icons.ArrowDown12, diff --git a/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs b/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs index 621c7c25e..19a995b1e 100644 --- a/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs +++ b/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs @@ -337,7 +337,7 @@ namespace FlaxEditor.Surface.Archetypes _textRect = new Rectangle(Float2.Zero, Size); var style = Style.Current; - var titleSize = style.FontLarge.MeasureText(Title); + var titleSize = Render2D.MeasureText(style.FontLarge, Title); var width = Mathf.Max(100, titleSize.X + 50); Resize(width, 0); titleSize.X += 8.0f; @@ -1402,7 +1402,7 @@ namespace FlaxEditor.Surface.Archetypes { Title = StateTitle; var style = Style.Current; - var titleSize = style.FontLarge.MeasureText(Title); + var titleSize = Render2D.MeasureText(style.FontLarge, Title); var width = Mathf.Max(100, titleSize.X + 50); Resize(width, 0); titleSize.X += 8.0f; diff --git a/Source/Editor/Surface/Archetypes/Animation.TransitionEditor.cs b/Source/Editor/Surface/Archetypes/Animation.TransitionEditor.cs index e1fdb0a42..84c2144b2 100644 --- a/Source/Editor/Surface/Archetypes/Animation.TransitionEditor.cs +++ b/Source/Editor/Surface/Archetypes/Animation.TransitionEditor.cs @@ -39,7 +39,7 @@ namespace FlaxEditor.Surface.Archetypes // Title var title = new Label(2, 2, width - 4, 23.0f) { - Font = new MultiFontReference(Style.Current.FontLarge), + Font = new FontReference(Style.Current.FontLarge), Text = transition.SurfaceName, Parent = this }; diff --git a/Source/Editor/Surface/Archetypes/Animation.cs b/Source/Editor/Surface/Archetypes/Animation.cs index 40a3d2a63..dcf4a9689 100644 --- a/Source/Editor/Surface/Archetypes/Animation.cs +++ b/Source/Editor/Surface/Archetypes/Animation.cs @@ -77,7 +77,7 @@ namespace FlaxEditor.Surface.Archetypes Title = asset?.ShortName ?? "Animation"; var style = Style.Current; - Resize(Mathf.Max(230, style.FontLarge.MeasureText(Title).X + 30), 160); + Resize(Mathf.Max(230, Render2D.MeasureText(style.FontLarge, Title).X + 30), 160); } /// diff --git a/Source/Editor/Surface/Archetypes/BehaviorTree.cs b/Source/Editor/Surface/Archetypes/BehaviorTree.cs index cca6856ae..364dfa1ef 100644 --- a/Source/Editor/Surface/Archetypes/BehaviorTree.cs +++ b/Source/Editor/Surface/Archetypes/BehaviorTree.cs @@ -101,7 +101,7 @@ namespace FlaxEditor.Surface.Archetypes _debugRelevant = Behavior.GetNodeDebugRelevancy(instance, behavior); _debugInfo = Behavior.GetNodeDebugInfo(instance, behavior); if (!string.IsNullOrEmpty(_debugInfo)) - _debugInfoSize = Style.Current.FontSmall.MeasureText(_debugInfo); + _debugInfoSize = Render2D.MeasureText(Style.Current.FontSmall, _debugInfo); } } @@ -488,7 +488,7 @@ namespace FlaxEditor.Surface.Archetypes var height = 0.0f; var titleLabelFont = Style.Current.FontLarge; width = Mathf.Max(width, 100.0f); - width = Mathf.Max(width, titleLabelFont.MeasureText(Title).X + 30); + width = Mathf.Max(width, Render2D.MeasureText(titleLabelFont, Title).X + 30); if (_debugInfoSize.X > 0) { width = Mathf.Max(width, _debugInfoSize.X + 8.0f); diff --git a/Source/Editor/Surface/Archetypes/Function.cs b/Source/Editor/Surface/Archetypes/Function.cs index bc982a510..53950dad2 100644 --- a/Source/Editor/Surface/Archetypes/Function.cs +++ b/Source/Editor/Surface/Archetypes/Function.cs @@ -1407,7 +1407,7 @@ namespace FlaxEditor.Surface.Archetypes // Title var title = new Label(2, 2, width - 4, 23.0f) { - Font = new MultiFontReference(Style.Current.FontLarge), + Font = new FontReference(Style.Current.FontLarge), Text = "Edit function signature", Parent = this }; diff --git a/Source/Editor/Surface/AttributesEditor.cs b/Source/Editor/Surface/AttributesEditor.cs index c9e32e23a..81b11bb68 100644 --- a/Source/Editor/Surface/AttributesEditor.cs +++ b/Source/Editor/Surface/AttributesEditor.cs @@ -83,7 +83,7 @@ namespace FlaxEditor.Surface // Title var title = new Label(2, 2, width - 4, 23.0f) { - Font = new MultiFontReference(Style.Current.FontLarge), + Font = new FontReference(Style.Current.FontLarge), Text = "Edit attributes", Parent = this }; diff --git a/Source/Editor/Surface/ContextMenu/VisjectCM.cs b/Source/Editor/Surface/ContextMenu/VisjectCM.cs index 0624d44b3..5256b3cb7 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCM.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCM.cs @@ -141,7 +141,7 @@ namespace FlaxEditor.Surface.ContextMenu }; // Title bar - var titleFontReference = new MultiFontReference(Style.Current.FontLarge); + var titleFontReference = new FontReference(Style.Current.FontLarge); var titleLabel = new Label { Width = Width * 0.5f - 8f, diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs index 207875a92..fd9f0c63b 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs @@ -286,7 +286,7 @@ namespace FlaxEditor.Surface.ContextMenu Render2D.DrawText(style.FontSmall, _archetype.Title, textRect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); if (_archetype.SubTitle != null) { - var titleLength = style.FontSmall.MeasureText(_archetype.Title).X; + var titleLength = Render2D.MeasureText(style.FontSmall, _archetype.Title).X; var subTitleRect = new Rectangle(textRect.X + titleLength, textRect.Y, textRect.Width - titleLength, textRect.Height); Render2D.DrawText(style.FontSmall, _archetype.SubTitle, subTitleRect, style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); } diff --git a/Source/Editor/Surface/Elements/InputBox.cs b/Source/Editor/Surface/Elements/InputBox.cs index 2047bcd1a..2ab286bd9 100644 --- a/Source/Editor/Surface/Elements/InputBox.cs +++ b/Source/Editor/Surface/Elements/InputBox.cs @@ -1428,7 +1428,7 @@ namespace FlaxEditor.Surface.Elements if (_defaultValueEditor != null) { - _defaultValueEditor.Location = new Float2(X + Width + 8 + Style.Current.FontSmall.MeasureText(Text).X, Y); + _defaultValueEditor.Location = new Float2(X + Width + 8 + Render2D.MeasureText(Style.Current.FontSmall, Text).X, Y); } } @@ -1635,7 +1635,7 @@ namespace FlaxEditor.Surface.Elements { if (DefaultValueEditors[i].CanUse(this, ref _currentType)) { - var bounds = new Rectangle(X + Width + 8 + Style.Current.FontSmall.MeasureText(Text).X, Y, 90, Height); + var bounds = new Rectangle(X + Width + 8 + Render2D.MeasureText(Style.Current.FontSmall, Text).X, Y, 90, Height); _editor = DefaultValueEditors[i]; try { diff --git a/Source/Editor/Surface/SurfaceNode.cs b/Source/Editor/Surface/SurfaceNode.cs index b6436e542..60d776102 100644 --- a/Source/Editor/Surface/SurfaceNode.cs +++ b/Source/Editor/Surface/SurfaceNode.cs @@ -200,7 +200,7 @@ namespace FlaxEditor.Surface continue; if (child is InputBox inputBox) { - var boxWidth = boxLabelFont.MeasureText(inputBox.Text).X + 20; + var boxWidth = Render2D.MeasureText(boxLabelFont, inputBox.Text).X + 20; if (inputBox.DefaultValueEditor != null) boxWidth += inputBox.DefaultValueEditor.Width + 4; leftWidth = Mathf.Max(leftWidth, boxWidth); @@ -208,7 +208,7 @@ namespace FlaxEditor.Surface } else if (child is OutputBox outputBox) { - rightWidth = Mathf.Max(rightWidth, boxLabelFont.MeasureText(outputBox.Text).X + 20); + rightWidth = Mathf.Max(rightWidth, Render2D.MeasureText(boxLabelFont, outputBox.Text).X + 20); rightHeight = Mathf.Max(rightHeight, outputBox.Archetype.Position.Y - Constants.NodeMarginY - Constants.NodeHeaderSize + 20.0f); } else if (child is Control control) @@ -226,7 +226,7 @@ namespace FlaxEditor.Surface } } width = Mathf.Max(width, leftWidth + rightWidth + 10); - width = Mathf.Max(width, titleLabelFont.MeasureText(Title).X + 30); + width = Mathf.Max(width, Render2D.MeasureText(titleLabelFont, Title).X + 30); height = Mathf.Max(height, Mathf.Max(leftHeight, rightHeight)); Resize(width, height); } diff --git a/Source/Editor/Tools/Foliage/FoliageTab.cs b/Source/Editor/Tools/Foliage/FoliageTab.cs index 1badb8c0c..58cf6f204 100644 --- a/Source/Editor/Tools/Foliage/FoliageTab.cs +++ b/Source/Editor/Tools/Foliage/FoliageTab.cs @@ -148,7 +148,7 @@ namespace FlaxEditor.Tools.Foliage Parent = _noFoliagePanel, Enabled = false }; - var textSize = Style.Current.FontMedium.MeasureText(buttonText); + var textSize = Render2D.MeasureText(Style.Current.FontMedium, buttonText); if (_createNewFoliage.Width < textSize.X) { _createNewFoliage.LocalX -= (textSize.X - _createNewFoliage.Width) / 2; diff --git a/Source/Editor/Tools/Terrain/CarveTab.cs b/Source/Editor/Tools/Terrain/CarveTab.cs index 4ff85ca23..12c9fd148 100644 --- a/Source/Editor/Tools/Terrain/CarveTab.cs +++ b/Source/Editor/Tools/Terrain/CarveTab.cs @@ -106,7 +106,7 @@ namespace FlaxEditor.Tools.Terrain Parent = _noTerrainPanel, Enabled = false }; - var textSize = Style.Current.FontMedium.MeasureText(buttonText); + var textSize = Render2D.MeasureText(Style.Current.FontMedium, buttonText); if (_createTerrainButton.Width < textSize.X) { _createTerrainButton.LocalX -= (textSize.X - _createTerrainButton.Width) / 2; diff --git a/Source/Editor/Tools/Terrain/CreateTerrainDialog.cs b/Source/Editor/Tools/Terrain/CreateTerrainDialog.cs index cba52283d..252891d44 100644 --- a/Source/Editor/Tools/Terrain/CreateTerrainDialog.cs +++ b/Source/Editor/Tools/Terrain/CreateTerrainDialog.cs @@ -96,7 +96,7 @@ namespace FlaxEditor.Tools.Terrain AnchorPreset = AnchorPresets.HorizontalStretchTop, Offsets = new Margin(0, 0, 0, 40), Parent = this, - Font = new MultiFontReference(Style.Current.FontTitle) + Font = new FontReference(Style.Current.FontTitle) }; var infoLabel = new Label { diff --git a/Source/Editor/Utilities/TextRenderUtils.cs b/Source/Editor/Utilities/TextRenderUtils.cs new file mode 100644 index 000000000..d7b79ae81 --- /dev/null +++ b/Source/Editor/Utilities/TextRenderUtils.cs @@ -0,0 +1,108 @@ +using FlaxEngine.GUI; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FlaxEngine +{ + public static class TextRenderUtils + { + /// + /// Draws a text using the global fallback defined in styles. + /// + /// The font to use. + /// The text to render. + /// The size and position of the area in which the text is drawn. + /// The text color. + /// The horizontal alignment of the text in a layout rectangle. + /// The vertical alignment of the text in a layout rectangle. + /// Describes how wrap text inside a layout rectangle. + /// The scale for distance one baseline from another. Default is 1. + /// The text drawing scale. Default is 1. + public static void DrawTextWithFallback(Font font, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) + { + var layout = new TextLayoutOptions + { + Bounds = layoutRect, + HorizontalAlignment = horizontalAlignment, + VerticalAlignment = verticalAlignment, + TextWrapping = textWrapping, + Scale = scale, + BaseLinesGapScale = baseLinesGapScale, + }; + + + Render2D.DrawText(font, Style.Current.Fallbacks, text, color, ref layout); + } + + /// + /// Draws a text using the global fallback defined in styles. Given material must have GUI domain and a public parameter named Font (texture parameter used for a font atlas sampling). + /// + /// The font to use. + /// Custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. + /// The text to render. + /// The size and position of the area in which the text is drawn. + /// The text color. + /// The horizontal alignment of the text in a layout rectangle. + /// The vertical alignment of the text in a layout rectangle. + /// Describes how wrap text inside a layout rectangle. + /// The scale for distance one baseline from another. Default is 1. + /// The text drawing scale. Default is 1. + public static void DrawTextWithFallback(Font font, MaterialBase customMaterial, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) + { + var layout = new TextLayoutOptions + { + Bounds = layoutRect, + HorizontalAlignment = horizontalAlignment, + VerticalAlignment = verticalAlignment, + TextWrapping = textWrapping, + Scale = scale, + BaseLinesGapScale = baseLinesGapScale, + }; + + Render2D.DrawText(font, Style.Current.Fallbacks, text, color, ref layout, customMaterial); + } + + public static Float2 MeasureTextWithFallback(Font font, string text) + { + return font.MeasureText(Style.Current.Fallbacks, text); + } + + public static Float2 MeasureTextWithFallback(Font font, string text, ref TextRange textRange) + { + return font.MeasureText(Style.Current.Fallbacks, text, ref textRange); + } + + public static Float2 MeasureTextWithFallback(Font font, string text, ref TextLayoutOptions layout) + { + return font.MeasureText(Style.Current.Fallbacks, text, ref layout); + } + + public static Float2 MeasureTextWithFallback(Font font, string text, ref TextRange textRange, ref TextLayoutOptions layout) + { + return font.MeasureText(Style.Current.Fallbacks, text, ref textRange, ref layout); + } + + public static Float2 GetCharPositionWithFallback(Font font, string text, int index) + { + return font.GetCharPosition(Style.Current.Fallbacks, text, index); + } + + public static Float2 GetCharPositionWithFallback(Font font, string text, ref TextRange textRange, int index) + { + return font.GetCharPosition(Style.Current.Fallbacks, text, ref textRange, index); + } + + public static Float2 GetCharPositionWithFallback(Font font, string text, int index, ref TextLayoutOptions layout) + { + return font.GetCharPosition(Style.Current.Fallbacks, text, index, ref layout); + } + + public static Float2 GetCharPositionWithFallback(Font font, string text, ref TextRange textRange, int index, ref TextLayoutOptions layout) + { + return font.GetCharPosition(Style.Current.Fallbacks, text, ref textRange, index, ref layout); + } + } +} diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index c49392d01..77a677c0c 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -548,9 +548,9 @@ namespace FlaxEditor.Viewport #region Camera settings widget var largestText = "Relative Panning"; - var textSize = Style.Current.FontMedium.MeasureText(largestText); + var textSize = Render2D.MeasureText(Style.Current.FontMedium, largestText); var xLocationForExtras = textSize.X + 5; - var cameraSpeedTextWidth = Style.Current.FontMedium.MeasureText("0.00").X; + var cameraSpeedTextWidth = Render2D.MeasureText(Style.Current.FontMedium, "0.00").X; // Camera Settings Widget _cameraWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); @@ -801,7 +801,7 @@ namespace FlaxEditor.Viewport #region View mode widget largestText = "Brightness"; - textSize = Style.Current.FontMedium.MeasureText(largestText); + textSize = Render2D.MeasureText(Style.Current.FontMedium, largestText); xLocationForExtras = textSize.X + 5; var viewMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperLeft); diff --git a/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs b/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs index fe73048fd..250790621 100644 --- a/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs +++ b/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs @@ -164,7 +164,7 @@ namespace FlaxEditor.Viewport.Widgets var style = Style.Current; if (style != null && style.FontMedium) - Width = CalculateButtonWidth(_forcedTextWidth > 0.0f ? _forcedTextWidth : style.FontMedium.MeasureText(_text).X, Icon.IsValid); + Width = CalculateButtonWidth(_forcedTextWidth > 0.0f ? _forcedTextWidth : Render2D.MeasureText(style.FontMedium, _text).X, Icon.IsValid); } } } diff --git a/Source/Editor/Windows/AboutDialog.cs b/Source/Editor/Windows/AboutDialog.cs index 1a81a9421..ac887dadf 100644 --- a/Source/Editor/Windows/AboutDialog.cs +++ b/Source/Editor/Windows/AboutDialog.cs @@ -41,7 +41,7 @@ namespace FlaxEditor.Windows var nameLabel = new Label(icon.Right + 10, icon.Top, 200, 34) { Text = "Flax Engine", - Font = new MultiFontReference(Style.Current.FontTitle), + Font = new FontReference(Style.Current.FontTitle), HorizontalAlignment = TextAlignment.Near, VerticalAlignment = TextAlignment.Center, Parent = this @@ -54,7 +54,7 @@ namespace FlaxEditor.Windows Parent = this }; var buttonText = "Copy version info"; - var fontSize = Style.Current.FontMedium.MeasureText(buttonText); + var fontSize = Render2D.MeasureText(Style.Current.FontMedium, buttonText); var copyVersionButton = new Button(Width - fontSize.X - 8, 6, fontSize.X + 4, 20) { Text = buttonText, diff --git a/Source/Editor/Windows/Assets/FontWindow.cs b/Source/Editor/Windows/Assets/FontWindow.cs index 2e2e14d99..bc91ca5e2 100644 --- a/Source/Editor/Windows/Assets/FontWindow.cs +++ b/Source/Editor/Windows/Assets/FontWindow.cs @@ -144,7 +144,7 @@ namespace FlaxEditor.Windows.Assets protected override void OnAssetLinked() { Asset.WaitForLoaded(); - _textPreview.Font = new MultiFontReference([Asset], 30); + _textPreview.Font = new FontReference(Asset, 30); _inputText.Text = string.Format("This is a sample text using font {0}.", Asset.FamilyName); var options = Asset.Options; _proxy.Set(ref options); diff --git a/Source/Editor/Windows/ContentWindow.Search.cs b/Source/Editor/Windows/ContentWindow.Search.cs index f29dc0bd6..a1072d158 100644 --- a/Source/Editor/Windows/ContentWindow.Search.cs +++ b/Source/Editor/Windows/ContentWindow.Search.cs @@ -57,7 +57,7 @@ namespace FlaxEditor.Windows var textRect = new Rectangle(margin, 0, clientRect.Width - boxSize - 2.0f * margin, clientRect.Height); Render2D.PushClip(textRect); var textColor = TextColor; - Render2D.DrawText(Font.GetMultiFont(), "View", textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, textScale); + Render2D.DrawText(Font.GetFont(), "View", textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, textScale); Render2D.PopClip(); // Arrow diff --git a/Source/Editor/Windows/PluginsWindow.cs b/Source/Editor/Windows/PluginsWindow.cs index d6c14f4e4..4dfa0ed08 100644 --- a/Source/Editor/Windows/PluginsWindow.cs +++ b/Source/Editor/Windows/PluginsWindow.cs @@ -79,7 +79,7 @@ namespace FlaxEditor.Windows HorizontalAlignment = TextAlignment.Near, AnchorPreset = AnchorPresets.HorizontalStretchTop, Text = desc.Name, - Font = new MultiFontReference(Style.Current.FontLarge), + Font = new FontReference(Style.Current.FontLarge), Parent = this, Bounds = new Rectangle(tmp1, margin, Width - tmp1 - margin, 28), }; @@ -120,9 +120,9 @@ namespace FlaxEditor.Windows url = desc.HomepageUrl; else if (!string.IsNullOrEmpty(desc.RepositoryUrl)) url = desc.RepositoryUrl; - versionLabel.Font.ForEach(x => x.Font.WaitForLoaded()); - var font = versionLabel.Font.GetMultiFont(); - var authorWidth = font.MeasureText(desc.Author).X + 8; + versionLabel.Font.Font.WaitForLoaded(); + var font = versionLabel.Font.GetFont(); + var authorWidth = Render2D.MeasureText(font, desc.Author).X + 8; var authorLabel = new ClickableLabel { HorizontalAlignment = TextAlignment.Far, @@ -392,7 +392,6 @@ namespace FlaxEditor.Windows } Editor.Log("Plugin project has been cloned."); - try { // Start git submodule clone diff --git a/Source/Editor/Windows/Profiler/Timeline.cs b/Source/Editor/Windows/Profiler/Timeline.cs index 917647f23..00365f74a 100644 --- a/Source/Editor/Windows/Profiler/Timeline.cs +++ b/Source/Editor/Windows/Profiler/Timeline.cs @@ -86,7 +86,7 @@ namespace FlaxEditor.Windows.Profiler Render2D.DrawRectangle(bounds, color * 0.5f); if (_nameLength < 0 && style.FontMedium) - _nameLength = style.FontMedium.MeasureText(_name).X; + _nameLength = Render2D.MeasureText(style.FontMedium, _name).X; if (_nameLength < bounds.Width + 4) { diff --git a/Source/Engine/Render2D/FallbackFonts.h b/Source/Engine/Render2D/FallbackFonts.h index 4b97e1c11..5861354fc 100644 --- a/Source/Engine/Render2D/FallbackFonts.h +++ b/Source/Engine/Render2D/FallbackFonts.h @@ -39,7 +39,7 @@ public: /// /// Combine the primary fonts with the fallback fonts to get a font list /// - API_PROPERTY() FORCE_INLINE Array& GetFontList(float size) { + API_FUNCTION() FORCE_INLINE Array& GetFontList(float size) { Array* result; if (_cache.TryGet(size, result)) { return *result; diff --git a/Source/Engine/Render2D/MultiFontReference.cs b/Source/Engine/Render2D/MultiFontReference.cs deleted file mode 100644 index dd3f4d179..000000000 --- a/Source/Engine/Render2D/MultiFontReference.cs +++ /dev/null @@ -1,74 +0,0 @@ - - -using System.Collections.Generic; -using System.Linq; - -namespace FlaxEngine -{ - /// - /// Reference to multiple font references - /// - public class MultiFontReference : List - { - public MultiFontReference() - { - _cachedFont = null; - } - - public MultiFontReference(IEnumerable other) - { - AddRange(other); - _cachedFont = null; - } - - public MultiFontReference(MultiFontReference other) - { - AddRange(other); - _cachedFont = other._cachedFont; - } - - public MultiFontReference(MultiFontReference other, float size) - { - AddRange(other.Select(x => new FontReference(x) { Size = size })); - _cachedFont = null; - } - - public MultiFontReference(MultiFont other) - { - AddRange(other.Fonts.Select(x => new FontReference(x))); - _cachedFont = other; - } - - public MultiFontReference(FontAsset[] assets, float size) - { - AddRange(assets.Select(x => new FontReference(x, size))); - _cachedFont = null; - } - - [EditorOrder(0), Tooltip("The font asset to use as characters source.")] - public MultiFont GetMultiFont() - { - if (_cachedFont) - return _cachedFont; - var fontList = this.Where(x => x.Font).Select(x => x.GetFont()).ToArray(); - _cachedFont = MultiFont.Create(fontList); - return _cachedFont; - } - - public bool Verify() - { - foreach (var i in this) - { - if (!i.Font) - { - return false; - } - } - - return true; - } - - [NoSerialize] - private MultiFont _cachedFont; - } -} diff --git a/Source/Engine/Render2D/Render2D.cs b/Source/Engine/Render2D/Render2D.cs index cad8380ab..b7158ccc7 100644 --- a/Source/Engine/Render2D/Render2D.cs +++ b/Source/Engine/Render2D/Render2D.cs @@ -1,11 +1,17 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. +using FlaxEngine.GUI; using System; namespace FlaxEngine { partial class Render2D { + public static FallbackFonts Fallbacks + { + get; set; + } = null; + /// /// Pushes transformation layer. /// @@ -111,7 +117,8 @@ namespace FlaxEngine /// Describes how wrap text inside a layout rectangle. /// The scale for distance one baseline from another. Default is 1. /// The text drawing scale. Default is 1. - public static void DrawText(Font font, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) + /// Whether to use fallback fonts for chars not renderable by the font. + public static void DrawText(Font font, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f, bool useFallback = true) { var layout = new TextLayoutOptions { @@ -122,7 +129,15 @@ namespace FlaxEngine Scale = scale, BaseLinesGapScale = baseLinesGapScale, }; - DrawText(font, text, color, ref layout); + + if (useFallback && Fallbacks != null) + { + DrawText(font, Fallbacks, text, color, ref layout); + } + else + { + DrawText(font, text, color, ref layout); + } } /// @@ -138,7 +153,8 @@ namespace FlaxEngine /// Describes how wrap text inside a layout rectangle. /// The scale for distance one baseline from another. Default is 1. /// The text drawing scale. Default is 1. - public static void DrawText(Font font, MaterialBase customMaterial, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) + /// Whether to use fallback fonts for chars not renderable by the font. + public static void DrawText(Font font, MaterialBase customMaterial, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f, bool useFallback = true) { var layout = new TextLayoutOptions { @@ -149,63 +165,63 @@ namespace FlaxEngine Scale = scale, BaseLinesGapScale = baseLinesGapScale, }; - DrawText(font, text, color, ref layout, customMaterial); + + if (useFallback && Fallbacks != null) + { + DrawText(font, Fallbacks, text, color, ref layout, customMaterial); + } + else + { + DrawText(font, text, color, ref layout, customMaterial); + } } - /// - /// Draws a text. - /// - /// The fonts to use, ordered by priority. - /// The text to render. - /// The size and position of the area in which the text is drawn. - /// The text color. - /// The horizontal alignment of the text in a layout rectangle. - /// The vertical alignment of the text in a layout rectangle. - /// Describes how wrap text inside a layout rectangle. - /// The scale for distance one baseline from another. Default is 1. - /// The text drawing scale. Default is 1. - public static void DrawText(MultiFont fonts, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) + public static Float2 MeasureText(Font font, string text, bool useFallback = true) { - var layout = new TextLayoutOptions + if (useFallback && Fallbacks != null) { - Bounds = layoutRect, - HorizontalAlignment = horizontalAlignment, - VerticalAlignment = verticalAlignment, - TextWrapping = textWrapping, - Scale = scale, - BaseLinesGapScale = baseLinesGapScale, - }; - - - DrawText(fonts, text, color, ref layout); + return font.MeasureText(Style.Current.Fallbacks, text); + } + else + { + return font.MeasureText(text); + } } - /// - /// Draws a text using a custom material shader. Given material must have GUI domain and a public parameter named Font (texture parameter used for a font atlas sampling). - /// - /// The fonts to use, ordered by priority. - /// Custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - /// The text to render. - /// The size and position of the area in which the text is drawn. - /// The text color. - /// The horizontal alignment of the text in a layout rectangle. - /// The vertical alignment of the text in a layout rectangle. - /// Describes how wrap text inside a layout rectangle. - /// The scale for distance one baseline from another. Default is 1. - /// The text drawing scale. Default is 1. - public static void DrawText(MultiFont fonts, MaterialBase customMaterial, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) + public static Float2 MeasureText(Font font, string text, ref TextRange textRange, bool useFallback = true) { - var layout = new TextLayoutOptions + if (useFallback && Fallbacks != null) { - Bounds = layoutRect, - HorizontalAlignment = horizontalAlignment, - VerticalAlignment = verticalAlignment, - TextWrapping = textWrapping, - Scale = scale, - BaseLinesGapScale = baseLinesGapScale, - }; + return font.MeasureText(Style.Current.Fallbacks, text, ref textRange); + } + else + { + return font.MeasureText(text, ref textRange); + } + } - DrawText(fonts, text, color, ref layout, customMaterial); + public static Float2 MeasureText(Font font, string text, ref TextLayoutOptions layout, bool useFallback = true) + { + if (useFallback && Fallbacks != null) + { + return font.MeasureText(Style.Current.Fallbacks, text, ref layout); + } + else + { + return font.MeasureText(text, ref layout); + } + } + + public static Float2 MeasureText(Font font, string text, ref TextRange textRange, ref TextLayoutOptions layout, bool useFallback = true) + { + if (useFallback && Fallbacks != null) + { + return font.MeasureText(Style.Current.Fallbacks, text, ref textRange, ref layout); + } + else + { + return font.MeasureText(text, ref textRange, ref layout); + } } /// diff --git a/Source/Engine/Scripting/Scripting.cs b/Source/Engine/Scripting/Scripting.cs index bc800f88a..a4cfd76b1 100644 --- a/Source/Engine/Scripting/Scripting.cs +++ b/Source/Engine/Scripting/Scripting.cs @@ -294,12 +294,12 @@ namespace FlaxEngine style.DragWindow = style.BackgroundSelected * 0.7f; // Use optionally bundled default font (matches Editor) - FontAsset[] defaultFont = [Content.LoadAsyncInternal("Editor/Fonts/Roboto-Regular"), Content.LoadAsyncInternal("Editor/Fonts/NotoSansSC-Regular")]; + FontAsset defaultFont = Content.LoadAsyncInternal("Editor/Fonts/Roboto-Regular"); - style.FontTitle = new MultiFontReference(defaultFont, 18).GetMultiFont(); - style.FontLarge = new MultiFontReference(defaultFont, 14).GetMultiFont(); - style.FontMedium = new MultiFontReference(defaultFont, 9).GetMultiFont(); - style.FontSmall = new MultiFontReference(defaultFont, 9).GetMultiFont(); + style.FontTitle = new FontReference(defaultFont, 18).GetFont(); + style.FontLarge = new FontReference(defaultFont, 14).GetFont(); + style.FontMedium = new FontReference(defaultFont, 9).GetFont(); + style.FontSmall = new FontReference(defaultFont, 9).GetFont(); Style.Current = style; } diff --git a/Source/Engine/UI/GUI/Common/Button.cs b/Source/Engine/UI/GUI/Common/Button.cs index f21a29191..756501c5e 100644 --- a/Source/Engine/UI/GUI/Common/Button.cs +++ b/Source/Engine/UI/GUI/Common/Button.cs @@ -23,7 +23,7 @@ namespace FlaxEngine.GUI /// /// The font. /// - protected MultiFontReference _font; + protected FontReference _font; /// /// The text. @@ -44,7 +44,7 @@ namespace FlaxEngine.GUI /// Gets or sets the font used to draw button text. /// [EditorDisplay("Text Style"), EditorOrder(2022), ExpandGroups] - public MultiFontReference Font + public FontReference Font { get => _font; set => _font = value; @@ -156,7 +156,7 @@ namespace FlaxEngine.GUI var style = Style.Current; if (style != null) { - _font = new MultiFontReference(style.FontMedium); + _font = new FontReference(style.FontMedium); TextColor = style.Foreground; BackgroundColor = style.BackgroundNormal; BorderColor = style.BorderNormal; @@ -262,7 +262,7 @@ namespace FlaxEngine.GUI Render2D.DrawRectangle(clientRect, borderColor, BorderThickness); // Draw text - Render2D.DrawText(_font?.GetMultiFont(), TextMaterial, _text, clientRect, textColor, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(_font?.GetFont(), TextMaterial, _text, clientRect, textColor, TextAlignment.Center, TextAlignment.Center); } /// diff --git a/Source/Engine/UI/GUI/Common/Dropdown.cs b/Source/Engine/UI/GUI/Common/Dropdown.cs index 2681aac1b..c9fef131c 100644 --- a/Source/Engine/UI/GUI/Common/Dropdown.cs +++ b/Source/Engine/UI/GUI/Common/Dropdown.cs @@ -278,7 +278,7 @@ namespace FlaxEngine.GUI /// Gets or sets the font used to draw text. /// [EditorDisplay("Text Style"), EditorOrder(2021)] - public MultiFontReference Font { get; set; } + public FontReference Font { get; set; } /// /// Gets or sets the custom material used to render the text. It must has domain set to GUI and have a public texture parameter named Font used to sample font atlas texture with font characters data. @@ -359,7 +359,7 @@ namespace FlaxEngine.GUI : base(0, 0, 120, 18.0f) { var style = Style.Current; - Font = new MultiFontReference(style.FontMedium); + Font = new FontReference(style.FontMedium); TextColor = style.Foreground; BackgroundColor = style.BackgroundNormal; BackgroundColorHighlighted = BackgroundColor; @@ -476,7 +476,7 @@ namespace FlaxEngine.GUI var font = Font.GetFont(); for (int i = 0; i < _items.Count; i++) { - itemsWidth = Mathf.Max(itemsWidth, itemsMargin + 4 + font.MeasureText(_items[i]).X); + itemsWidth = Mathf.Max(itemsWidth, itemsMargin + 4 + Render2D.MeasureText(font, _items[i]).X); } */ var itemsWidth = Width; @@ -674,7 +674,7 @@ namespace FlaxEngine.GUI var textRect = new Rectangle(margin, 0, clientRect.Width - boxSize - 2.0f * margin, clientRect.Height); Render2D.PushClip(textRect); var textColor = TextColor; - Render2D.DrawText(Font.GetMultiFont(), FontMaterial, _items[_selectedIndex], textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(Font.GetFont(), FontMaterial, _items[_selectedIndex], textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center); Render2D.PopClip(); } diff --git a/Source/Engine/UI/GUI/Common/Label.cs b/Source/Engine/UI/GUI/Common/Label.cs index ccc1cc190..d7147d3ab 100644 --- a/Source/Engine/UI/GUI/Common/Label.cs +++ b/Source/Engine/UI/GUI/Common/Label.cs @@ -26,7 +26,7 @@ namespace FlaxEngine.GUI /// /// The font. /// - protected MultiFontReference _font; + protected FontReference _font; /// /// Gets or sets the text. @@ -86,7 +86,7 @@ namespace FlaxEngine.GUI /// Gets or sets the font. /// [EditorDisplay("Text Style"), EditorOrder(2024)] - public MultiFontReference Font + public FontReference Font { get => _font; set @@ -192,7 +192,7 @@ namespace FlaxEngine.GUI { AutoFocus = false; var style = Style.Current; - Font = new MultiFontReference(style.FontMedium); + Font = new FontReference(style.FontMedium); TextColor = style.Foreground; TextColorHighlighted = style.Foreground; } @@ -203,7 +203,7 @@ namespace FlaxEngine.GUI { AutoFocus = false; var style = Style.Current; - Font = new MultiFontReference(style.FontMedium); + Font = new FontReference(style.FontMedium); TextColor = style.Foreground; TextColorHighlighted = style.Foreground; } @@ -235,7 +235,7 @@ namespace FlaxEngine.GUI } } - Render2D.DrawText(_font.GetMultiFont(), Material, _text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale); + Render2D.DrawText(_font.GetFont(), Material, _text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale); if (ClipText) Render2D.PopClip(); @@ -246,7 +246,7 @@ namespace FlaxEngine.GUI { if (_autoWidth || _autoHeight || _autoFitText) { - var font = _font.GetMultiFont(); + var font = _font.GetFont(); if (font) { // Calculate text size @@ -256,7 +256,7 @@ namespace FlaxEngine.GUI layout.Bounds.Size.X = Width - Margin.Width; else if (_autoWidth && !_autoHeight) layout.Bounds.Size.Y = Height - Margin.Height; - _textSize = font.MeasureText(_text, ref layout); + _textSize = Render2D.MeasureText(font, _text, ref layout); _textSize.Y *= BaseLinesGapScale; // Check if size is controlled via text diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.cs b/Source/Engine/UI/GUI/Common/RichTextBox.cs index f8ac7d353..514878b37 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBox.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBox.cs @@ -46,7 +46,7 @@ namespace FlaxEngine.GUI var style = Style.Current; _textStyle = new TextBlockStyle { - Font = new FontReference(style.FontMedium.Fonts.First()), + Font = new FontReference(style.FontMedium), Color = style.Foreground, BackgroundSelectedBrush = new SolidColorBrush(style.BackgroundSelected), }; diff --git a/Source/Engine/UI/GUI/Common/TextBox.cs b/Source/Engine/UI/GUI/Common/TextBox.cs index d9c7c1a80..a32f3a76c 100644 --- a/Source/Engine/UI/GUI/Common/TextBox.cs +++ b/Source/Engine/UI/GUI/Common/TextBox.cs @@ -1,5 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. +using System.Drawing; using System.Linq; namespace FlaxEngine.GUI @@ -40,7 +41,7 @@ namespace FlaxEngine.GUI /// Gets or sets the font. /// [EditorDisplay("Text Style"), EditorOrder(2024)] - public MultiFontReference Font { get; set; } + public FontReference Font { get; set; } /// /// Gets or sets the custom material used to render the text. It must has domain set to GUI and have a public texture parameter named Font used to sample font atlas texture with font characters data. @@ -90,7 +91,7 @@ namespace FlaxEngine.GUI _layout.Bounds = new Rectangle(DefaultMargin, 1, Width - 2 * DefaultMargin, Height - 2); var style = Style.Current; - Font = new MultiFontReference(style.FontMedium); + Font = new FontReference(style.FontMedium); TextColor = style.Foreground; WatermarkTextColor = style.ForegroundDisabled; SelectionColor = style.BackgroundSelected; @@ -99,33 +100,33 @@ namespace FlaxEngine.GUI /// public override Float2 GetTextSize() { - var font = Font.GetMultiFont(); + var font = Font.GetFont(); if (font == null) { return Float2.Zero; } - return font.MeasureText(_text, ref _layout); + return Render2D.MeasureText(font, _text, ref _layout); } /// public override Float2 GetCharPosition(int index, out float height) { - var font = Font.GetMultiFont(); + var font = Font.GetFont(); if (font == null) { height = Height; return Float2.Zero; } - height = font.MaxHeight / DpiScale; + height = font.Height / DpiScale; return font.GetCharPosition(_text, index, ref _layout); } /// public override int HitTestText(Float2 location) { - var font = Font.GetMultiFont(); + var font = Font.GetFont(); if (font == null) { return 0; @@ -148,7 +149,7 @@ namespace FlaxEngine.GUI // Cache data var rect = new Rectangle(Float2.Zero, Size); bool enabled = EnabledInHierarchy; - var font = Font.GetMultiFont(); + var font = Font.GetFont(); if (!font) return; @@ -172,7 +173,7 @@ namespace FlaxEngine.GUI { var leftEdge = font.GetCharPosition(_text, SelectionLeft, ref _layout); var rightEdge = font.GetCharPosition(_text, SelectionRight, ref _layout); - float fontHeight = font.MaxHeight / DpiScale; + float fontHeight = font.Height / DpiScale; // Draw selection background float alpha = Mathf.Min(1.0f, Mathf.Cos(_animateTime * BackgroundSelectedFlashSpeed) * 0.5f + 1.3f); @@ -212,11 +213,25 @@ namespace FlaxEngine.GUI var color = TextColor; if (!enabled) color *= 0.6f; - Render2D.DrawText(font, _text, color, ref _layout, TextMaterial); + if (Render2D.Fallbacks != null) + { + Render2D.DrawText(font, Render2D.Fallbacks, _text, color, ref _layout, TextMaterial); + } + else + { + Render2D.DrawText(font, _text, color, ref _layout, TextMaterial); + } } else if (!string.IsNullOrEmpty(_watermarkText) && !IsFocused) { - Render2D.DrawText(font, _watermarkText, WatermarkTextColor, ref _layout, TextMaterial); + if (Render2D.Fallbacks != null) + { + Render2D.DrawText(font, Render2D.Fallbacks, _watermarkText, WatermarkTextColor, ref _layout, TextMaterial); + } + else + { + Render2D.DrawText(font, _watermarkText, WatermarkTextColor, ref _layout, TextMaterial); + } } // Caret diff --git a/Source/Engine/UI/GUI/Panels/DropPanel.cs b/Source/Engine/UI/GUI/Panels/DropPanel.cs index 06256c9f8..ed620ab98 100644 --- a/Source/Engine/UI/GUI/Panels/DropPanel.cs +++ b/Source/Engine/UI/GUI/Panels/DropPanel.cs @@ -130,7 +130,7 @@ namespace FlaxEngine.GUI /// Gets or sets the font used to render panel header text. /// [EditorDisplay("Header Text Style"), EditorOrder(2020), ExpandGroups] - public MultiFontReference HeaderTextFont { get; set; } + public FontReference HeaderTextFont { get; set; } /// /// Gets or sets the custom material used to render the text. It must has domain set to GUI and have a public texture parameter named Font used to sample font atlas texture with font characters data. @@ -238,7 +238,7 @@ namespace FlaxEngine.GUI var style = Style.Current; HeaderColor = style.BackgroundNormal; HeaderColorMouseOver = style.BackgroundHighlighted; - HeaderTextFont = new MultiFontReference(style.FontMedium); + HeaderTextFont = new FontReference(style.FontMedium); HeaderTextColor = style.Foreground; ArrowImageOpened = new SpriteBrush(style.ArrowDown); ArrowImageClosed = new SpriteBrush(style.ArrowRight); @@ -375,7 +375,7 @@ namespace FlaxEngine.GUI textColor *= 0.6f; } - Render2D.DrawText(HeaderTextFont.GetMultiFont(), HeaderTextMaterial, HeaderText, textRect, textColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(HeaderTextFont.GetFont(), HeaderTextMaterial, HeaderText, textRect, textColor, TextAlignment.Near, TextAlignment.Center); if (!_isClosed && EnableContainmentLines) { diff --git a/Source/Engine/UI/GUI/Style.cs b/Source/Engine/UI/GUI/Style.cs index a8b87fb3a..c4b51b085 100644 --- a/Source/Engine/UI/GUI/Style.cs +++ b/Source/Engine/UI/GUI/Style.cs @@ -15,61 +15,67 @@ namespace FlaxEngine.GUI public static Style Current { get; set; } [Serialize] - private MultiFontReference _fontTitle; + private FontReference _fontTitle; /// /// The font title. /// [NoSerialize] [EditorOrder(10)] - public MultiFont FontTitle + public Font FontTitle { - get => _fontTitle?.GetMultiFont(); - set => _fontTitle = new MultiFontReference(value); + get => _fontTitle?.GetFont(); + set => _fontTitle = new FontReference(value); } [Serialize] - private MultiFontReference _fontLarge; + private FontReference _fontLarge; /// /// The font large. /// [NoSerialize] [EditorOrder(20)] - public MultiFont FontLarge + public Font FontLarge { - get => _fontLarge?.GetMultiFont(); - set => _fontLarge = new MultiFontReference(value); + get => _fontLarge?.GetFont(); + set => _fontLarge = new FontReference(value); } [Serialize] - private MultiFontReference _fontMedium; + private FontReference _fontMedium; /// /// The font medium. /// [NoSerialize] [EditorOrder(30)] - public MultiFont FontMedium + public Font FontMedium { - get => _fontMedium?.GetMultiFont(); - set => _fontMedium = new MultiFontReference(value); + get => _fontMedium?.GetFont(); + set => _fontMedium = new FontReference(value); } [Serialize] - private MultiFontReference _fontSmall; + private FontReference _fontSmall; /// /// The font small. /// [NoSerialize] [EditorOrder(40)] - public MultiFont FontSmall + public Font FontSmall { - get => _fontSmall?.GetMultiFont(); - set => _fontSmall = new MultiFontReference(value); + get => _fontSmall?.GetFont(); + set => _fontSmall = new FontReference(value); } + /// + /// The fallback fonts to use if the primary font can't render the char. + /// + [EditorOrder(50)] + public FallbackFonts Fallbacks; + /// /// The background color. /// From 623f478b4466bb248db1f6f7448124a1b338668b Mon Sep 17 00:00:00 2001 From: ExMatics HydrogenC <33123710+HydrogenC@users.noreply.github.com> Date: Thu, 30 Nov 2023 23:22:11 +0800 Subject: [PATCH 37/61] Move fallback rendering to new class --- .../CustomEditors/Dedicated/ScriptsEditor.cs | 2 +- .../Dedicated/UIControlEditor.cs | 2 +- .../Editors/ActorTransformEditor.cs | 2 +- .../Editor/CustomEditors/Editors/TagEditor.cs | 2 +- .../GUI/ContextMenu/ContextMenuButton.cs | 4 +- Source/Editor/GUI/Docking/DockWindow.cs | 2 +- Source/Editor/GUI/MainMenuButton.cs | 2 +- Source/Editor/GUI/NavigationButton.cs | 2 +- .../Editor/GUI/Timeline/Tracks/MemberTrack.cs | 2 +- Source/Editor/GUI/ToolStripButton.cs | 2 +- Source/Editor/GUI/Tree/TreeNode.cs | 2 +- .../Archetypes/Animation.StateMachine.cs | 4 +- Source/Editor/Surface/Archetypes/Animation.cs | 2 +- .../Editor/Surface/Archetypes/BehaviorTree.cs | 4 +- .../Surface/ContextMenu/VisjectCMItem.cs | 2 +- Source/Editor/Surface/Elements/InputBox.cs | 4 +- Source/Editor/Surface/SurfaceNode.cs | 6 +- Source/Editor/Tools/Foliage/FoliageTab.cs | 2 +- Source/Editor/Tools/Terrain/CarveTab.cs | 2 +- Source/Editor/Utilities/TextRenderUtils.cs | 108 --------------- Source/Editor/Viewport/EditorViewport.cs | 6 +- .../Viewport/Widgets/ViewportWidgetButton.cs | 2 +- Source/Editor/Windows/AboutDialog.cs | 2 +- Source/Editor/Windows/PluginsWindow.cs | 2 +- Source/Editor/Windows/Profiler/Timeline.cs | 2 +- Source/Engine/Render2D/FallbackTextUtils.cs | 126 ++++++++++++++++++ Source/Engine/Render2D/Render2D.cs | 78 +---------- Source/Engine/UI/GUI/Common/Dropdown.cs | 2 +- Source/Engine/UI/GUI/Common/Label.cs | 2 +- Source/Engine/UI/GUI/Common/TextBox.cs | 2 +- 30 files changed, 165 insertions(+), 217 deletions(-) delete mode 100644 Source/Editor/Utilities/TextRenderUtils.cs create mode 100644 Source/Engine/Render2D/FallbackTextUtils.cs diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs index 90ae9ae54..ab949508e 100644 --- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs @@ -43,7 +43,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Add script button var buttonText = "Add script"; - var textSize = Render2D.MeasureText(Style.Current.FontMedium, buttonText); + var textSize = FallbackTextUtils.MeasureText(Style.Current.FontMedium, buttonText); float addScriptButtonWidth = (textSize.X < 60.0f) ? 60.0f : textSize.X + 4; var buttonHeight = (textSize.Y < 18) ? 18 : textSize.Y + 4; _addScriptsButton = new Button diff --git a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs index b8250918f..4efbee259 100644 --- a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs @@ -423,7 +423,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Set control type button var space = layout.Space(20); var buttonText = "Set Type"; - var textSize = Render2D.MeasureText(FlaxEngine.GUI.Style.Current.FontMedium, buttonText); + var textSize = FallbackTextUtils.MeasureText(FlaxEngine.GUI.Style.Current.FontMedium, buttonText); float setTypeButtonWidth = (textSize.X < 60.0f) ? 60.0f : textSize.X + 4; var setTypeButton = new Button { diff --git a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs index e84bf5914..8889a765a 100644 --- a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs @@ -101,7 +101,7 @@ namespace FlaxEditor.CustomEditors.Editors _linkButton.Clicked += ToggleLink; ToggleEnabled(); SetLinkStyle(); - var textSize = Render2D.MeasureText(FlaxEngine.GUI.Style.Current.FontMedium, LinkedLabel.Text.Value); + var textSize = FallbackTextUtils.MeasureText(FlaxEngine.GUI.Style.Current.FontMedium, LinkedLabel.Text.Value); _linkButton.LocalX += textSize.X + 10; LinkedLabel.SetupContextMenu += (label, menu, editor) => { diff --git a/Source/Editor/CustomEditors/Editors/TagEditor.cs b/Source/Editor/CustomEditors/Editors/TagEditor.cs index 49ac9d937..bbece7e04 100644 --- a/Source/Editor/CustomEditors/Editors/TagEditor.cs +++ b/Source/Editor/CustomEditors/Editors/TagEditor.cs @@ -631,7 +631,7 @@ namespace FlaxEditor.CustomEditors.Editors TooltipText = "Edit...", Parent = _label, }; - var textSize = Render2D.MeasureText(FlaxEngine.GUI.Style.Current.FontMedium, buttonText); + var textSize = FallbackTextUtils.MeasureText(FlaxEngine.GUI.Style.Current.FontMedium, buttonText); if (textSize.Y > button.Width) button.Width = textSize.Y + 2; diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs b/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs index 3137de240..d8c492e46 100644 --- a/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs +++ b/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs @@ -236,9 +236,9 @@ namespace FlaxEditor.GUI.ContextMenu float width = 20; if (style.FontMedium) { - width += Render2D.MeasureText(style.FontMedium, Text).X; + width += FallbackTextUtils.MeasureText(style.FontMedium, Text).X; if (!string.IsNullOrEmpty(ShortKeys)) - width += 40 + Render2D.MeasureText(style.FontMedium, ShortKeys).X; + width += 40 + FallbackTextUtils.MeasureText(style.FontMedium, ShortKeys).X; } return Mathf.Max(width, base.MinimumWidth); diff --git a/Source/Editor/GUI/Docking/DockWindow.cs b/Source/Editor/GUI/Docking/DockWindow.cs index 36a4c112e..b7c12287f 100644 --- a/Source/Editor/GUI/Docking/DockWindow.cs +++ b/Source/Editor/GUI/Docking/DockWindow.cs @@ -489,7 +489,7 @@ namespace FlaxEditor.GUI.Docking { var style = Style.Current; if (style?.FontMedium != null) - _titleSize = Render2D.MeasureText(style.FontMedium, _title); + _titleSize = FallbackTextUtils.MeasureText(style.FontMedium, _title); } base.PerformLayoutBeforeChildren(); diff --git a/Source/Editor/GUI/MainMenuButton.cs b/Source/Editor/GUI/MainMenuButton.cs index 3440996aa..6fe8b98d3 100644 --- a/Source/Editor/GUI/MainMenuButton.cs +++ b/Source/Editor/GUI/MainMenuButton.cs @@ -103,7 +103,7 @@ namespace FlaxEditor.GUI float width = 18; if (style.FontMedium) - width += Render2D.MeasureText(style.FontMedium, Text).X; + width += FallbackTextUtils.MeasureText(style.FontMedium, Text).X; Width = width; } diff --git a/Source/Editor/GUI/NavigationButton.cs b/Source/Editor/GUI/NavigationButton.cs index 6face21ef..5d399da02 100644 --- a/Source/Editor/GUI/NavigationButton.cs +++ b/Source/Editor/GUI/NavigationButton.cs @@ -68,7 +68,7 @@ namespace FlaxEditor.GUI if (style.FontMedium) { - Width = Render2D.MeasureText(style.FontMedium, Text).X + 2 * DefaultMargin; + Width = FallbackTextUtils.MeasureText(style.FontMedium, Text).X + 2 * DefaultMargin; } } } diff --git a/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs b/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs index b4433622e..dd79922ac 100644 --- a/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs @@ -345,7 +345,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks if (_previewValue != null) { // Based on Track.Draw for track text placement - var left = _xOffset + 16 + Render2D.MeasureText(Style.Current.FontSmall, Title ?? Name).X; + var left = _xOffset + 16 + FallbackTextUtils.MeasureText(Style.Current.FontSmall, Title ?? Name).X; if (Icon.IsValid) left += 18; if (IsExpanded) diff --git a/Source/Editor/GUI/ToolStripButton.cs b/Source/Editor/GUI/ToolStripButton.cs index cf34fd36b..50dce78b4 100644 --- a/Source/Editor/GUI/ToolStripButton.cs +++ b/Source/Editor/GUI/ToolStripButton.cs @@ -152,7 +152,7 @@ namespace FlaxEditor.GUI if (hasSprite) width += iconSize; if (!string.IsNullOrEmpty(_text) && style.FontMedium) - width += Render2D.MeasureText(style.FontMedium, _text).X + (hasSprite ? DefaultMargin : 0); + width += FallbackTextUtils.MeasureText(style.FontMedium, _text).X + (hasSprite ? DefaultMargin : 0); Width = width; } diff --git a/Source/Editor/GUI/Tree/TreeNode.cs b/Source/Editor/GUI/Tree/TreeNode.cs index 7b47e5eb6..700ba6da1 100644 --- a/Source/Editor/GUI/Tree/TreeNode.cs +++ b/Source/Editor/GUI/Tree/TreeNode.cs @@ -576,7 +576,7 @@ namespace FlaxEditor.GUI.Tree var font = TextFont.GetFont(); if (font) { - _textWidth = Render2D.MeasureText(font, _text).X; + _textWidth = FallbackTextUtils.MeasureText(font, _text).X; _textChanged = false; } } diff --git a/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs b/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs index 19a995b1e..078b89c56 100644 --- a/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs +++ b/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs @@ -337,7 +337,7 @@ namespace FlaxEditor.Surface.Archetypes _textRect = new Rectangle(Float2.Zero, Size); var style = Style.Current; - var titleSize = Render2D.MeasureText(style.FontLarge, Title); + var titleSize = FallbackTextUtils.MeasureText(style.FontLarge, Title); var width = Mathf.Max(100, titleSize.X + 50); Resize(width, 0); titleSize.X += 8.0f; @@ -1402,7 +1402,7 @@ namespace FlaxEditor.Surface.Archetypes { Title = StateTitle; var style = Style.Current; - var titleSize = Render2D.MeasureText(style.FontLarge, Title); + var titleSize = FallbackTextUtils.MeasureText(style.FontLarge, Title); var width = Mathf.Max(100, titleSize.X + 50); Resize(width, 0); titleSize.X += 8.0f; diff --git a/Source/Editor/Surface/Archetypes/Animation.cs b/Source/Editor/Surface/Archetypes/Animation.cs index dcf4a9689..dae16b425 100644 --- a/Source/Editor/Surface/Archetypes/Animation.cs +++ b/Source/Editor/Surface/Archetypes/Animation.cs @@ -77,7 +77,7 @@ namespace FlaxEditor.Surface.Archetypes Title = asset?.ShortName ?? "Animation"; var style = Style.Current; - Resize(Mathf.Max(230, Render2D.MeasureText(style.FontLarge, Title).X + 30), 160); + Resize(Mathf.Max(230, FallbackTextUtils.MeasureText(style.FontLarge, Title).X + 30), 160); } /// diff --git a/Source/Editor/Surface/Archetypes/BehaviorTree.cs b/Source/Editor/Surface/Archetypes/BehaviorTree.cs index 364dfa1ef..5f3c0b0a2 100644 --- a/Source/Editor/Surface/Archetypes/BehaviorTree.cs +++ b/Source/Editor/Surface/Archetypes/BehaviorTree.cs @@ -101,7 +101,7 @@ namespace FlaxEditor.Surface.Archetypes _debugRelevant = Behavior.GetNodeDebugRelevancy(instance, behavior); _debugInfo = Behavior.GetNodeDebugInfo(instance, behavior); if (!string.IsNullOrEmpty(_debugInfo)) - _debugInfoSize = Render2D.MeasureText(Style.Current.FontSmall, _debugInfo); + _debugInfoSize = FallbackTextUtils.MeasureText(Style.Current.FontSmall, _debugInfo); } } @@ -488,7 +488,7 @@ namespace FlaxEditor.Surface.Archetypes var height = 0.0f; var titleLabelFont = Style.Current.FontLarge; width = Mathf.Max(width, 100.0f); - width = Mathf.Max(width, Render2D.MeasureText(titleLabelFont, Title).X + 30); + width = Mathf.Max(width, FallbackTextUtils.MeasureText(titleLabelFont, Title).X + 30); if (_debugInfoSize.X > 0) { width = Mathf.Max(width, _debugInfoSize.X + 8.0f); diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs index fd9f0c63b..019be78b6 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs @@ -286,7 +286,7 @@ namespace FlaxEditor.Surface.ContextMenu Render2D.DrawText(style.FontSmall, _archetype.Title, textRect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); if (_archetype.SubTitle != null) { - var titleLength = Render2D.MeasureText(style.FontSmall, _archetype.Title).X; + var titleLength = FallbackTextUtils.MeasureText(style.FontSmall, _archetype.Title).X; var subTitleRect = new Rectangle(textRect.X + titleLength, textRect.Y, textRect.Width - titleLength, textRect.Height); Render2D.DrawText(style.FontSmall, _archetype.SubTitle, subTitleRect, style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); } diff --git a/Source/Editor/Surface/Elements/InputBox.cs b/Source/Editor/Surface/Elements/InputBox.cs index 2ab286bd9..526d85d15 100644 --- a/Source/Editor/Surface/Elements/InputBox.cs +++ b/Source/Editor/Surface/Elements/InputBox.cs @@ -1428,7 +1428,7 @@ namespace FlaxEditor.Surface.Elements if (_defaultValueEditor != null) { - _defaultValueEditor.Location = new Float2(X + Width + 8 + Render2D.MeasureText(Style.Current.FontSmall, Text).X, Y); + _defaultValueEditor.Location = new Float2(X + Width + 8 + FallbackTextUtils.MeasureText(Style.Current.FontSmall, Text).X, Y); } } @@ -1635,7 +1635,7 @@ namespace FlaxEditor.Surface.Elements { if (DefaultValueEditors[i].CanUse(this, ref _currentType)) { - var bounds = new Rectangle(X + Width + 8 + Render2D.MeasureText(Style.Current.FontSmall, Text).X, Y, 90, Height); + var bounds = new Rectangle(X + Width + 8 + FallbackTextUtils.MeasureText(Style.Current.FontSmall, Text).X, Y, 90, Height); _editor = DefaultValueEditors[i]; try { diff --git a/Source/Editor/Surface/SurfaceNode.cs b/Source/Editor/Surface/SurfaceNode.cs index 60d776102..3c2381192 100644 --- a/Source/Editor/Surface/SurfaceNode.cs +++ b/Source/Editor/Surface/SurfaceNode.cs @@ -200,7 +200,7 @@ namespace FlaxEditor.Surface continue; if (child is InputBox inputBox) { - var boxWidth = Render2D.MeasureText(boxLabelFont, inputBox.Text).X + 20; + var boxWidth = FallbackTextUtils.MeasureText(boxLabelFont, inputBox.Text).X + 20; if (inputBox.DefaultValueEditor != null) boxWidth += inputBox.DefaultValueEditor.Width + 4; leftWidth = Mathf.Max(leftWidth, boxWidth); @@ -208,7 +208,7 @@ namespace FlaxEditor.Surface } else if (child is OutputBox outputBox) { - rightWidth = Mathf.Max(rightWidth, Render2D.MeasureText(boxLabelFont, outputBox.Text).X + 20); + rightWidth = Mathf.Max(rightWidth, FallbackTextUtils.MeasureText(boxLabelFont, outputBox.Text).X + 20); rightHeight = Mathf.Max(rightHeight, outputBox.Archetype.Position.Y - Constants.NodeMarginY - Constants.NodeHeaderSize + 20.0f); } else if (child is Control control) @@ -226,7 +226,7 @@ namespace FlaxEditor.Surface } } width = Mathf.Max(width, leftWidth + rightWidth + 10); - width = Mathf.Max(width, Render2D.MeasureText(titleLabelFont, Title).X + 30); + width = Mathf.Max(width, FallbackTextUtils.MeasureText(titleLabelFont, Title).X + 30); height = Mathf.Max(height, Mathf.Max(leftHeight, rightHeight)); Resize(width, height); } diff --git a/Source/Editor/Tools/Foliage/FoliageTab.cs b/Source/Editor/Tools/Foliage/FoliageTab.cs index 58cf6f204..c2b1fbe68 100644 --- a/Source/Editor/Tools/Foliage/FoliageTab.cs +++ b/Source/Editor/Tools/Foliage/FoliageTab.cs @@ -148,7 +148,7 @@ namespace FlaxEditor.Tools.Foliage Parent = _noFoliagePanel, Enabled = false }; - var textSize = Render2D.MeasureText(Style.Current.FontMedium, buttonText); + var textSize = FallbackTextUtils.MeasureText(Style.Current.FontMedium, buttonText); if (_createNewFoliage.Width < textSize.X) { _createNewFoliage.LocalX -= (textSize.X - _createNewFoliage.Width) / 2; diff --git a/Source/Editor/Tools/Terrain/CarveTab.cs b/Source/Editor/Tools/Terrain/CarveTab.cs index 12c9fd148..0a86aab6f 100644 --- a/Source/Editor/Tools/Terrain/CarveTab.cs +++ b/Source/Editor/Tools/Terrain/CarveTab.cs @@ -106,7 +106,7 @@ namespace FlaxEditor.Tools.Terrain Parent = _noTerrainPanel, Enabled = false }; - var textSize = Render2D.MeasureText(Style.Current.FontMedium, buttonText); + var textSize = FallbackTextUtils.MeasureText(Style.Current.FontMedium, buttonText); if (_createTerrainButton.Width < textSize.X) { _createTerrainButton.LocalX -= (textSize.X - _createTerrainButton.Width) / 2; diff --git a/Source/Editor/Utilities/TextRenderUtils.cs b/Source/Editor/Utilities/TextRenderUtils.cs deleted file mode 100644 index d7b79ae81..000000000 --- a/Source/Editor/Utilities/TextRenderUtils.cs +++ /dev/null @@ -1,108 +0,0 @@ -using FlaxEngine.GUI; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace FlaxEngine -{ - public static class TextRenderUtils - { - /// - /// Draws a text using the global fallback defined in styles. - /// - /// The font to use. - /// The text to render. - /// The size and position of the area in which the text is drawn. - /// The text color. - /// The horizontal alignment of the text in a layout rectangle. - /// The vertical alignment of the text in a layout rectangle. - /// Describes how wrap text inside a layout rectangle. - /// The scale for distance one baseline from another. Default is 1. - /// The text drawing scale. Default is 1. - public static void DrawTextWithFallback(Font font, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) - { - var layout = new TextLayoutOptions - { - Bounds = layoutRect, - HorizontalAlignment = horizontalAlignment, - VerticalAlignment = verticalAlignment, - TextWrapping = textWrapping, - Scale = scale, - BaseLinesGapScale = baseLinesGapScale, - }; - - - Render2D.DrawText(font, Style.Current.Fallbacks, text, color, ref layout); - } - - /// - /// Draws a text using the global fallback defined in styles. Given material must have GUI domain and a public parameter named Font (texture parameter used for a font atlas sampling). - /// - /// The font to use. - /// Custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - /// The text to render. - /// The size and position of the area in which the text is drawn. - /// The text color. - /// The horizontal alignment of the text in a layout rectangle. - /// The vertical alignment of the text in a layout rectangle. - /// Describes how wrap text inside a layout rectangle. - /// The scale for distance one baseline from another. Default is 1. - /// The text drawing scale. Default is 1. - public static void DrawTextWithFallback(Font font, MaterialBase customMaterial, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) - { - var layout = new TextLayoutOptions - { - Bounds = layoutRect, - HorizontalAlignment = horizontalAlignment, - VerticalAlignment = verticalAlignment, - TextWrapping = textWrapping, - Scale = scale, - BaseLinesGapScale = baseLinesGapScale, - }; - - Render2D.DrawText(font, Style.Current.Fallbacks, text, color, ref layout, customMaterial); - } - - public static Float2 MeasureTextWithFallback(Font font, string text) - { - return font.MeasureText(Style.Current.Fallbacks, text); - } - - public static Float2 MeasureTextWithFallback(Font font, string text, ref TextRange textRange) - { - return font.MeasureText(Style.Current.Fallbacks, text, ref textRange); - } - - public static Float2 MeasureTextWithFallback(Font font, string text, ref TextLayoutOptions layout) - { - return font.MeasureText(Style.Current.Fallbacks, text, ref layout); - } - - public static Float2 MeasureTextWithFallback(Font font, string text, ref TextRange textRange, ref TextLayoutOptions layout) - { - return font.MeasureText(Style.Current.Fallbacks, text, ref textRange, ref layout); - } - - public static Float2 GetCharPositionWithFallback(Font font, string text, int index) - { - return font.GetCharPosition(Style.Current.Fallbacks, text, index); - } - - public static Float2 GetCharPositionWithFallback(Font font, string text, ref TextRange textRange, int index) - { - return font.GetCharPosition(Style.Current.Fallbacks, text, ref textRange, index); - } - - public static Float2 GetCharPositionWithFallback(Font font, string text, int index, ref TextLayoutOptions layout) - { - return font.GetCharPosition(Style.Current.Fallbacks, text, index, ref layout); - } - - public static Float2 GetCharPositionWithFallback(Font font, string text, ref TextRange textRange, int index, ref TextLayoutOptions layout) - { - return font.GetCharPosition(Style.Current.Fallbacks, text, ref textRange, index, ref layout); - } - } -} diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index 77a677c0c..0633d0ba9 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -548,9 +548,9 @@ namespace FlaxEditor.Viewport #region Camera settings widget var largestText = "Relative Panning"; - var textSize = Render2D.MeasureText(Style.Current.FontMedium, largestText); + var textSize = FallbackTextUtils.MeasureText(Style.Current.FontMedium, largestText); var xLocationForExtras = textSize.X + 5; - var cameraSpeedTextWidth = Render2D.MeasureText(Style.Current.FontMedium, "0.00").X; + var cameraSpeedTextWidth = FallbackTextUtils.MeasureText(Style.Current.FontMedium, "0.00").X; // Camera Settings Widget _cameraWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); @@ -801,7 +801,7 @@ namespace FlaxEditor.Viewport #region View mode widget largestText = "Brightness"; - textSize = Render2D.MeasureText(Style.Current.FontMedium, largestText); + textSize = FallbackTextUtils.MeasureText(Style.Current.FontMedium, largestText); xLocationForExtras = textSize.X + 5; var viewMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperLeft); diff --git a/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs b/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs index 250790621..598f4ee32 100644 --- a/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs +++ b/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs @@ -164,7 +164,7 @@ namespace FlaxEditor.Viewport.Widgets var style = Style.Current; if (style != null && style.FontMedium) - Width = CalculateButtonWidth(_forcedTextWidth > 0.0f ? _forcedTextWidth : Render2D.MeasureText(style.FontMedium, _text).X, Icon.IsValid); + Width = CalculateButtonWidth(_forcedTextWidth > 0.0f ? _forcedTextWidth : FallbackTextUtils.MeasureText(style.FontMedium, _text).X, Icon.IsValid); } } } diff --git a/Source/Editor/Windows/AboutDialog.cs b/Source/Editor/Windows/AboutDialog.cs index ac887dadf..02aad16ec 100644 --- a/Source/Editor/Windows/AboutDialog.cs +++ b/Source/Editor/Windows/AboutDialog.cs @@ -54,7 +54,7 @@ namespace FlaxEditor.Windows Parent = this }; var buttonText = "Copy version info"; - var fontSize = Render2D.MeasureText(Style.Current.FontMedium, buttonText); + var fontSize = FallbackTextUtils.MeasureText(Style.Current.FontMedium, buttonText); var copyVersionButton = new Button(Width - fontSize.X - 8, 6, fontSize.X + 4, 20) { Text = buttonText, diff --git a/Source/Editor/Windows/PluginsWindow.cs b/Source/Editor/Windows/PluginsWindow.cs index 4dfa0ed08..03beab2fa 100644 --- a/Source/Editor/Windows/PluginsWindow.cs +++ b/Source/Editor/Windows/PluginsWindow.cs @@ -122,7 +122,7 @@ namespace FlaxEditor.Windows url = desc.RepositoryUrl; versionLabel.Font.Font.WaitForLoaded(); var font = versionLabel.Font.GetFont(); - var authorWidth = Render2D.MeasureText(font, desc.Author).X + 8; + var authorWidth = FallbackTextUtils.MeasureText(font, desc.Author).X + 8; var authorLabel = new ClickableLabel { HorizontalAlignment = TextAlignment.Far, diff --git a/Source/Editor/Windows/Profiler/Timeline.cs b/Source/Editor/Windows/Profiler/Timeline.cs index 00365f74a..780b31a3b 100644 --- a/Source/Editor/Windows/Profiler/Timeline.cs +++ b/Source/Editor/Windows/Profiler/Timeline.cs @@ -86,7 +86,7 @@ namespace FlaxEditor.Windows.Profiler Render2D.DrawRectangle(bounds, color * 0.5f); if (_nameLength < 0 && style.FontMedium) - _nameLength = Render2D.MeasureText(style.FontMedium, _name).X; + _nameLength = FallbackTextUtils.MeasureText(style.FontMedium, _name).X; if (_nameLength < bounds.Width + 4) { diff --git a/Source/Engine/Render2D/FallbackTextUtils.cs b/Source/Engine/Render2D/FallbackTextUtils.cs new file mode 100644 index 000000000..eb676f0a9 --- /dev/null +++ b/Source/Engine/Render2D/FallbackTextUtils.cs @@ -0,0 +1,126 @@ + +namespace FlaxEngine +{ + /// + /// A collection of functions to handle text rendering with fallback font + /// + public static class FallbackTextUtils + { + public static FallbackFonts Fallbacks + { + get; set; + } = null; + + public static void DrawText(Font font, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) + { + var layout = new TextLayoutOptions + { + Bounds = layoutRect, + HorizontalAlignment = horizontalAlignment, + VerticalAlignment = verticalAlignment, + TextWrapping = textWrapping, + Scale = scale, + BaseLinesGapScale = baseLinesGapScale, + }; + + if (Fallbacks != null) + { + Render2D.DrawText(font, Fallbacks, text, color, ref layout); + } + else + { + Render2D.DrawText(font, text, color, ref layout); + } + } + + public static void DrawText(Font font, MaterialBase customMaterial, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) + { + var layout = new TextLayoutOptions + { + Bounds = layoutRect, + HorizontalAlignment = horizontalAlignment, + VerticalAlignment = verticalAlignment, + TextWrapping = textWrapping, + Scale = scale, + BaseLinesGapScale = baseLinesGapScale, + }; + + if (Fallbacks != null) + { + Render2D.DrawText(font, Fallbacks, text, color, ref layout, customMaterial); + } + else + { + Render2D.DrawText(font, text, color, ref layout, customMaterial); + } + } + + public static Float2 MeasureText(Font font, string text) + { + if (Fallbacks != null) + { + return font.MeasureText(Fallbacks, text); + } + else + { + return font.MeasureText(text); + } + } + + public static Float2 MeasureText(Font font, string text, ref TextRange textRange) + { + if (Fallbacks != null) + { + return font.MeasureText(Fallbacks, text, ref textRange); + } + else + { + return font.MeasureText(text, ref textRange); + } + } + + public static Float2 MeasureText(Font font, string text, ref TextLayoutOptions layout) + { + if (Fallbacks != null) + { + return font.MeasureText(Fallbacks, text, ref layout); + } + else + { + return font.MeasureText(text, ref layout); + } + } + + public static Float2 MeasureText(Font font, string text, ref TextRange textRange, ref TextLayoutOptions layout) + { + if (Fallbacks != null) + { + return font.MeasureText(Fallbacks, text, ref textRange, ref layout); + } + else + { + return font.MeasureText(text, ref textRange, ref layout); + } + } + + public static Float2 GetCharPosition(Font font, string text, int index) + { + return font.GetCharPosition(Style.Current.Fallbacks, text, index); + } + + public static Float2 GetCharPosition(Font font, string text, ref TextRange textRange, int index) + { + return font.GetCharPosition(Style.Current.Fallbacks, text, ref textRange, index); + } + + public static Float2 GetCharPosition(Font font, string text, int index, ref TextLayoutOptions layout) + { + return font.GetCharPosition(Style.Current.Fallbacks, text, index, ref layout); + } + + public static Float2 GetCharPosition(Font font, string text, ref TextRange textRange, int index, ref TextLayoutOptions layout) + { + return font.GetCharPosition(Style.Current.Fallbacks, text, ref textRange, index, ref layout); + } + } +} diff --git a/Source/Engine/Render2D/Render2D.cs b/Source/Engine/Render2D/Render2D.cs index b7158ccc7..6e4f1dc1d 100644 --- a/Source/Engine/Render2D/Render2D.cs +++ b/Source/Engine/Render2D/Render2D.cs @@ -7,10 +7,6 @@ namespace FlaxEngine { partial class Render2D { - public static FallbackFonts Fallbacks - { - get; set; - } = null; /// /// Pushes transformation layer. @@ -117,8 +113,7 @@ namespace FlaxEngine /// Describes how wrap text inside a layout rectangle. /// The scale for distance one baseline from another. Default is 1. /// The text drawing scale. Default is 1. - /// Whether to use fallback fonts for chars not renderable by the font. - public static void DrawText(Font font, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f, bool useFallback = true) + public static void DrawText(Font font, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) { var layout = new TextLayoutOptions { @@ -129,15 +124,7 @@ namespace FlaxEngine Scale = scale, BaseLinesGapScale = baseLinesGapScale, }; - - if (useFallback && Fallbacks != null) - { - DrawText(font, Fallbacks, text, color, ref layout); - } - else - { - DrawText(font, text, color, ref layout); - } + DrawText(font, text, color, ref layout); } /// @@ -153,8 +140,7 @@ namespace FlaxEngine /// Describes how wrap text inside a layout rectangle. /// The scale for distance one baseline from another. Default is 1. /// The text drawing scale. Default is 1. - /// Whether to use fallback fonts for chars not renderable by the font. - public static void DrawText(Font font, MaterialBase customMaterial, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f, bool useFallback = true) + public static void DrawText(Font font, MaterialBase customMaterial, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) { var layout = new TextLayoutOptions { @@ -165,63 +151,7 @@ namespace FlaxEngine Scale = scale, BaseLinesGapScale = baseLinesGapScale, }; - - if (useFallback && Fallbacks != null) - { - DrawText(font, Fallbacks, text, color, ref layout, customMaterial); - } - else - { - DrawText(font, text, color, ref layout, customMaterial); - } - } - - public static Float2 MeasureText(Font font, string text, bool useFallback = true) - { - if (useFallback && Fallbacks != null) - { - return font.MeasureText(Style.Current.Fallbacks, text); - } - else - { - return font.MeasureText(text); - } - } - - public static Float2 MeasureText(Font font, string text, ref TextRange textRange, bool useFallback = true) - { - if (useFallback && Fallbacks != null) - { - return font.MeasureText(Style.Current.Fallbacks, text, ref textRange); - } - else - { - return font.MeasureText(text, ref textRange); - } - } - - public static Float2 MeasureText(Font font, string text, ref TextLayoutOptions layout, bool useFallback = true) - { - if (useFallback && Fallbacks != null) - { - return font.MeasureText(Style.Current.Fallbacks, text, ref layout); - } - else - { - return font.MeasureText(text, ref layout); - } - } - - public static Float2 MeasureText(Font font, string text, ref TextRange textRange, ref TextLayoutOptions layout, bool useFallback = true) - { - if (useFallback && Fallbacks != null) - { - return font.MeasureText(Style.Current.Fallbacks, text, ref textRange, ref layout); - } - else - { - return font.MeasureText(text, ref textRange, ref layout); - } + DrawText(font, text, color, ref layout, customMaterial); } /// diff --git a/Source/Engine/UI/GUI/Common/Dropdown.cs b/Source/Engine/UI/GUI/Common/Dropdown.cs index c9fef131c..46f05f516 100644 --- a/Source/Engine/UI/GUI/Common/Dropdown.cs +++ b/Source/Engine/UI/GUI/Common/Dropdown.cs @@ -476,7 +476,7 @@ namespace FlaxEngine.GUI var font = Font.GetFont(); for (int i = 0; i < _items.Count; i++) { - itemsWidth = Mathf.Max(itemsWidth, itemsMargin + 4 + Render2D.MeasureText(font, _items[i]).X); + itemsWidth = Mathf.Max(itemsWidth, itemsMargin + 4 + FallbackTextUtils.MeasureText(font, _items[i]).X); } */ var itemsWidth = Width; diff --git a/Source/Engine/UI/GUI/Common/Label.cs b/Source/Engine/UI/GUI/Common/Label.cs index d7147d3ab..4e26c1934 100644 --- a/Source/Engine/UI/GUI/Common/Label.cs +++ b/Source/Engine/UI/GUI/Common/Label.cs @@ -256,7 +256,7 @@ namespace FlaxEngine.GUI layout.Bounds.Size.X = Width - Margin.Width; else if (_autoWidth && !_autoHeight) layout.Bounds.Size.Y = Height - Margin.Height; - _textSize = Render2D.MeasureText(font, _text, ref layout); + _textSize = FallbackTextUtils.MeasureText(font, _text, ref layout); _textSize.Y *= BaseLinesGapScale; // Check if size is controlled via text diff --git a/Source/Engine/UI/GUI/Common/TextBox.cs b/Source/Engine/UI/GUI/Common/TextBox.cs index a32f3a76c..ea7523f48 100644 --- a/Source/Engine/UI/GUI/Common/TextBox.cs +++ b/Source/Engine/UI/GUI/Common/TextBox.cs @@ -106,7 +106,7 @@ namespace FlaxEngine.GUI return Float2.Zero; } - return Render2D.MeasureText(font, _text, ref _layout); + return FallbackTextUtils.MeasureText(font, _text, ref _layout); } /// From 3d139a241ab4720e64ea03aae082de284bd5d26b Mon Sep 17 00:00:00 2001 From: ExMatics HydrogenC <33123710+HydrogenC@users.noreply.github.com> Date: Fri, 1 Dec 2023 09:28:29 +0800 Subject: [PATCH 38/61] Fix C# editor --- Source/Editor/Content/GUI/ContentView.cs | 2 +- Source/Editor/Content/Items/ContentItem.cs | 2 +- Source/Editor/Content/Tree/ContentTreeNode.cs | 5 +- .../Dedicated/MeshReferenceEditor.cs | 4 +- .../CustomEditors/Dedicated/ScriptsEditor.cs | 3 +- .../CustomEditors/Dedicated/SplineEditor.cs | 2 +- .../Editors/ActorTransformEditor.cs | 1 - .../Editors/FlaxObjectRefEditor.cs | 4 +- .../CustomEditors/Editors/TypeEditor.cs | 4 +- Source/Editor/GUI/AssetPicker.cs | 6 +- Source/Editor/GUI/ComboBox.cs | 2 +- .../GUI/ContextMenu/ContextMenuButton.cs | 5 +- Source/Editor/GUI/CurveEditor.cs | 2 +- .../Editor/GUI/Dialogs/ColorPickerDialog.cs | 22 +- Source/Editor/GUI/Docking/DockPanelProxy.cs | 4 +- Source/Editor/GUI/Docking/DockWindow.cs | 1 - Source/Editor/GUI/ItemsListContextMenu.cs | 7 +- Source/Editor/GUI/MainMenuButton.cs | 3 +- Source/Editor/GUI/NavigationButton.cs | 3 +- Source/Editor/GUI/Row.cs | 3 +- Source/Editor/GUI/StatusBar.cs | 2 +- Source/Editor/GUI/StyleValueEditor.cs | 2 +- Source/Editor/GUI/Table.cs | 3 +- Source/Editor/GUI/Tabs/Tabs.cs | 2 +- Source/Editor/GUI/Timeline/GUI/Background.cs | 2 +- .../Editor/GUI/Timeline/GUI/PositionHandle.cs | 1 - Source/Editor/GUI/Timeline/Track.cs | 2 +- Source/Editor/GUI/ToolStripButton.cs | 3 +- Source/Editor/GUI/Tree/TreeNode.cs | 3 +- Source/Editor/Options/OptionsModule.cs | 2 +- Source/Editor/SceneGraph/GUI/ActorTreeNode.cs | 4 +- .../Archetypes/Animation.StateMachine.cs | 4 +- .../Editor/Surface/Archetypes/BehaviorTree.cs | 3 +- .../Surface/Archetypes/ParticleModules.cs | 2 +- Source/Editor/Surface/Archetypes/Particles.cs | 4 +- .../Editor/Surface/ContextMenu/VisjectCM.cs | 2 +- .../Surface/ContextMenu/VisjectCMItem.cs | 18 +- Source/Editor/Surface/Elements/InputBox.cs | 2 +- Source/Editor/Surface/Elements/OutputBox.cs | 2 +- Source/Editor/Surface/Elements/TextView.cs | 2 +- Source/Editor/Surface/SurfaceComment.cs | 2 +- Source/Editor/Surface/SurfaceNode.cs | 3 +- Source/Editor/Tools/Foliage/FoliageTab.cs | 1 - Source/Editor/Tools/Terrain/CarveTab.cs | 1 - Source/Editor/Viewport/EditorViewport.cs | 6 +- .../Viewport/Previews/AnimationPreview.cs | 4 +- .../Editor/Viewport/Previews/ModelPreview.cs | 4 +- .../Previews/ParticleSystemPreview.cs | 2 +- .../Viewport/Previews/SkinnedModelPreview.cs | 4 +- .../Viewport/Previews/TexturePreview.cs | 2 +- .../Viewport/Widgets/ViewportWidgetButton.cs | 3 +- Source/Editor/Windows/AboutDialog.cs | 1 - .../Windows/Assets/AnimationGraphWindow.cs | 2 +- .../Editor/Windows/Assets/AnimationWindow.cs | 2 +- Source/Editor/Windows/Assets/ModelWindow.cs | 4 +- .../Windows/Assets/SkinnedModelWindow.cs | 4 +- Source/Editor/Windows/ContentWindow.Search.cs | 2 +- Source/Editor/Windows/DebugLogWindow.cs | 4 +- Source/Editor/Windows/GameWindow.cs | 8 +- Source/Editor/Windows/Profiler/SingleChart.cs | 4 +- Source/Editor/Windows/Profiler/Timeline.cs | 5 +- Source/Editor/Windows/SceneTreeWindow.cs | 2 +- Source/Editor/Windows/ToolboxWindow.cs | 5 +- Source/Engine/Render2D/FallbackTextUtils.cs | 205 +++++++++++++----- Source/Engine/UI/GUI/Common/Button.cs | 3 +- Source/Engine/UI/GUI/Common/Dropdown.cs | 3 +- Source/Engine/UI/GUI/Common/Label.cs | 4 +- .../Engine/UI/GUI/Common/RichTextBox.Tags.cs | 1 - Source/Engine/UI/GUI/Common/RichTextBox.cs | 1 - .../Engine/UI/GUI/Common/RichTextBoxBase.cs | 8 +- Source/Engine/UI/GUI/Common/TextBox.cs | 28 +-- Source/Engine/UI/GUI/Panels/DropPanel.cs | 3 +- Source/Engine/UI/GUI/Style.cs | 1 - Source/Engine/UI/GUI/Tooltip.cs | 3 +- 74 files changed, 272 insertions(+), 213 deletions(-) diff --git a/Source/Editor/Content/GUI/ContentView.cs b/Source/Editor/Content/GUI/ContentView.cs index 259be104b..1091c9cef 100644 --- a/Source/Editor/Content/GUI/ContentView.cs +++ b/Source/Editor/Content/GUI/ContentView.cs @@ -598,7 +598,7 @@ namespace FlaxEditor.Content.GUI // Check if it's an empty thing if (_items.Count == 0) { - Render2D.DrawText(style.FontSmall, IsSearching ? "No results" : "Empty", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontSmall, IsSearching ? "No results" : "Empty", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); } } diff --git a/Source/Editor/Content/Items/ContentItem.cs b/Source/Editor/Content/Items/ContentItem.cs index 604caa704..0842dfff7 100644 --- a/Source/Editor/Content/Items/ContentItem.cs +++ b/Source/Editor/Content/Items/ContentItem.cs @@ -745,7 +745,7 @@ namespace FlaxEditor.Content // Draw short name Render2D.PushClip(ref textRect); - Render2D.DrawText(style.FontMedium, ShowFileExtension || view.ShowFileExtensions ? FileName : ShortName, textRect, style.Foreground, nameAlignment, TextAlignment.Center, TextWrapping.WrapWords, 1f, 0.95f); + FallbackTextUtils.DrawText(style.FontMedium, ShowFileExtension || view.ShowFileExtensions ? FileName : ShortName, textRect, style.Foreground, nameAlignment, TextAlignment.Center, TextWrapping.WrapWords, 1f, 0.95f); Render2D.PopClip(); } diff --git a/Source/Editor/Content/Tree/ContentTreeNode.cs b/Source/Editor/Content/Tree/ContentTreeNode.cs index ee9b463f7..2f378e7f5 100644 --- a/Source/Editor/Content/Tree/ContentTreeNode.cs +++ b/Source/Editor/Content/Tree/ContentTreeNode.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System.Collections.Generic; -using System.Linq; using FlaxEditor.GUI; using FlaxEditor.GUI.Drag; using FlaxEditor.GUI.Tree; @@ -151,8 +150,8 @@ namespace FlaxEditor.Content var textRect = TextRect; for (int i = 0; i < ranges.Length; i++) { - var start = font.GetCharPosition(text, ranges[i].StartIndex); - var end = font.GetCharPosition(text, ranges[i].EndIndex); + var start = FallbackTextUtils.GetCharPosition(font, text, ranges[i].StartIndex); + var end = FallbackTextUtils.GetCharPosition(font, text, ranges[i].EndIndex); _highlights.Add(new Rectangle(start.X + textRect.X, textRect.Y, end.X - start.X, textRect.Height)); } isThisVisible = true; diff --git a/Source/Editor/CustomEditors/Dedicated/MeshReferenceEditor.cs b/Source/Editor/CustomEditors/Dedicated/MeshReferenceEditor.cs index 4099e5aee..66c1c791f 100644 --- a/Source/Editor/CustomEditors/Dedicated/MeshReferenceEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/MeshReferenceEditor.cs @@ -109,7 +109,7 @@ namespace FlaxEditor.CustomEditors.Dedicated { // Draw name Render2D.PushClip(nameRect); - Render2D.DrawText(style.FontMedium, _valueName, nameRect, isEnabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, _valueName, nameRect, isEnabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); Render2D.PopClip(); // Draw deselect button @@ -118,7 +118,7 @@ namespace FlaxEditor.CustomEditors.Dedicated else { // Draw info - Render2D.DrawText(style.FontMedium, "-", nameRect, isEnabled ? Color.OrangeRed : Color.DarkOrange, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, "-", nameRect, isEnabled ? Color.OrangeRed : Color.DarkOrange, TextAlignment.Near, TextAlignment.Center); } // Draw picker button diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs index ab949508e..017fd7655 100644 --- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs @@ -3,7 +3,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Linq; using FlaxEditor.Actions; using FlaxEditor.Content; using FlaxEditor.GUI; @@ -87,7 +86,7 @@ namespace FlaxEditor.CustomEditors.Dedicated var size = Size; // Info - Render2D.DrawText(style.FontSmall, "Drag scripts here", new Rectangle(2, _addScriptsButton.Height + 4, size.X - 4, size.Y - 4 - 20), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontSmall, "Drag scripts here", new Rectangle(2, _addScriptsButton.Height + 4, size.X - 4, size.Y - 4 - 20), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); // Check if drag is over if (IsDragOver && _dragHandlers != null && _dragHandlers.HasValidDrag) diff --git a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs index 7b9b65c5c..fa4379db1 100644 --- a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs @@ -226,7 +226,7 @@ namespace FlaxEditor.CustomEditors.Dedicated if (!enabled) color *= 0.6f; Render2D.DrawSprite(tab._customIcon, iconRect, color); - Render2D.DrawText(style.FontMedium, tab._customText, new Rectangle(0, iconSize, size.X, textHeight), color, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, tab._customText, new Rectangle(0, iconSize, size.X, textHeight), color, TextAlignment.Center, TextAlignment.Center); } } diff --git a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs index 8889a765a..d8d16027b 100644 --- a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs @@ -2,7 +2,6 @@ using FlaxEngine; using FlaxEngine.GUI; -using System.Linq; namespace FlaxEditor.CustomEditors.Editors { diff --git a/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs b/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs index 731da3817..2f64ea159 100644 --- a/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs +++ b/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs @@ -199,7 +199,7 @@ namespace FlaxEditor.CustomEditors.Editors { // Draw name Render2D.PushClip(nameRect); - Render2D.DrawText(style.FontMedium, _valueName, nameRect, isEnabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, _valueName, nameRect, isEnabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); Render2D.PopClip(); // Draw deselect button @@ -208,7 +208,7 @@ namespace FlaxEditor.CustomEditors.Editors else { // Draw info - Render2D.DrawText(style.FontMedium, "-", nameRect, isEnabled ? Color.OrangeRed : Color.DarkOrange, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, "-", nameRect, isEnabled ? Color.OrangeRed : Color.DarkOrange, TextAlignment.Near, TextAlignment.Center); } // Draw picker button diff --git a/Source/Editor/CustomEditors/Editors/TypeEditor.cs b/Source/Editor/CustomEditors/Editors/TypeEditor.cs index 38800a738..4d0b90383 100644 --- a/Source/Editor/CustomEditors/Editors/TypeEditor.cs +++ b/Source/Editor/CustomEditors/Editors/TypeEditor.cs @@ -160,13 +160,13 @@ namespace FlaxEditor.CustomEditors.Editors // Draw name Render2D.PushClip(nameRect); - Render2D.DrawText(style.FontMedium, _valueName, nameRect, style.Foreground, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, _valueName, nameRect, style.Foreground, TextAlignment.Near, TextAlignment.Center); Render2D.PopClip(); } else { // Draw info - Render2D.DrawText(style.FontMedium, "-", nameRect, Color.OrangeRed, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, "-", nameRect, Color.OrangeRed, TextAlignment.Near, TextAlignment.Center); } // Draw picker button diff --git a/Source/Editor/GUI/AssetPicker.cs b/Source/Editor/GUI/AssetPicker.cs index 84f58daf1..2abe5db38 100644 --- a/Source/Editor/GUI/AssetPicker.cs +++ b/Source/Editor/GUI/AssetPicker.cs @@ -139,7 +139,7 @@ namespace FlaxEditor.GUI float sizeForTextLeft = Width - button1Rect.Right; if (sizeForTextLeft > 30) { - Render2D.DrawText( + FallbackTextUtils.DrawText( style.FontSmall, Validator.SelectedItem.ShortName, new Rectangle(button1Rect.Right + 2, 0, sizeForTextLeft, ButtonsSize), @@ -161,7 +161,7 @@ namespace FlaxEditor.GUI var name = Validator.SelectedAsset.GetType().Name; if (Validator.SelectedAsset.IsVirtual) name += " (virtual)"; - Render2D.DrawText( + FallbackTextUtils.DrawText( style.FontSmall, name, new Rectangle(button1Rect.Right + 2, 0, sizeForTextLeft, ButtonsSize), @@ -174,7 +174,7 @@ namespace FlaxEditor.GUI { // No element selected Render2D.FillRectangle(iconRect, style.BackgroundNormal); - Render2D.DrawText(style.FontMedium, "No asset\nselected", iconRect, Color.Orange, TextAlignment.Center, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, Height / DefaultIconSize); + FallbackTextUtils.DrawText(style.FontMedium, "No asset\nselected", iconRect, Color.Orange, TextAlignment.Center, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, Height / DefaultIconSize); } // Check if drag is over diff --git a/Source/Editor/GUI/ComboBox.cs b/Source/Editor/GUI/ComboBox.cs index 0417cc7e3..8f3a7cb4b 100644 --- a/Source/Editor/GUI/ComboBox.cs +++ b/Source/Editor/GUI/ComboBox.cs @@ -554,7 +554,7 @@ namespace FlaxEditor.GUI var textRect = new Rectangle(margin, 0, clientRect.Width - boxSize - 2.0f * margin, clientRect.Height); Render2D.PushClip(textRect); var textColor = TextColor; - Render2D.DrawText(Font.GetFont(), text, textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, textScale); + FallbackTextUtils.DrawText(Font.GetFont(), text, textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, textScale); Render2D.PopClip(); } diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs b/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs index d8c492e46..035e997e9 100644 --- a/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs +++ b/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; -using System.Linq; using FlaxEngine; using FlaxEngine.GUI; @@ -129,12 +128,12 @@ namespace FlaxEditor.GUI.ContextMenu base.Draw(); // Draw text - Render2D.DrawText(style.FontMedium, Text, textRect, textColor, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, Text, textRect, textColor, TextAlignment.Near, TextAlignment.Center); if (!string.IsNullOrEmpty(ShortKeys)) { // Draw short keys - Render2D.DrawText(style.FontMedium, ShortKeys, textRect, textColor, TextAlignment.Far, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, ShortKeys, textRect, textColor, TextAlignment.Far, TextAlignment.Center); } // Draw icon diff --git a/Source/Editor/GUI/CurveEditor.cs b/Source/Editor/GUI/CurveEditor.cs index 22deec120..f4839acd5 100644 --- a/Source/Editor/GUI/CurveEditor.cs +++ b/Source/Editor/GUI/CurveEditor.cs @@ -832,7 +832,7 @@ namespace FlaxEditor.GUI 50, LabelsSize ); - Render2D.DrawText(_labelsFont, label, labelRect, _labelsColor.AlphaMultiplied(strength), TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.7f); + FallbackTextUtils.DrawText(_labelsFont, label, labelRect, _labelsColor.AlphaMultiplied(strength), TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.7f); } } } diff --git a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs index 27878a763..8b1881faf 100644 --- a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs +++ b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs @@ -281,33 +281,33 @@ namespace FlaxEditor.GUI.Dialogs // RGBA var rgbaR = new Rectangle(_cRed.Left - ChannelTextWidth, _cRed.Y, 10000, _cRed.Height); - Render2D.DrawText(style.FontMedium, "R", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, "R", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center); rgbaR.Location.Y = _cGreen.Y; - Render2D.DrawText(style.FontMedium, "G", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, "G", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center); rgbaR.Location.Y = _cBlue.Y; - Render2D.DrawText(style.FontMedium, "B", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, "B", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center); rgbaR.Location.Y = _cAlpha.Y; - Render2D.DrawText(style.FontMedium, "A", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, "A", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center); // HSV left var hsvHl = new Rectangle(_cHue.Left - ChannelTextWidth, _cHue.Y, 10000, _cHue.Height); - Render2D.DrawText(style.FontMedium, "H", hsvHl, textColor, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, "H", hsvHl, textColor, TextAlignment.Near, TextAlignment.Center); hsvHl.Location.Y = _cSaturation.Y; - Render2D.DrawText(style.FontMedium, "S", hsvHl, textColor, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, "S", hsvHl, textColor, TextAlignment.Near, TextAlignment.Center); hsvHl.Location.Y = _cValue.Y; - Render2D.DrawText(style.FontMedium, "V", hsvHl, textColor, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, "V", hsvHl, textColor, TextAlignment.Near, TextAlignment.Center); // HSV right var hsvHr = new Rectangle(_cHue.Right + 2, _cHue.Y, 10000, _cHue.Height); - Render2D.DrawText(style.FontMedium, "°", hsvHr, textColor, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, "°", hsvHr, textColor, TextAlignment.Near, TextAlignment.Center); hsvHr.Location.Y = _cSaturation.Y; - Render2D.DrawText(style.FontMedium, "%", hsvHr, textColor, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, "%", hsvHr, textColor, TextAlignment.Near, TextAlignment.Center); hsvHr.Location.Y = _cValue.Y; - Render2D.DrawText(style.FontMedium, "%", hsvHr, textColor, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, "%", hsvHr, textColor, TextAlignment.Near, TextAlignment.Center); // Hex var hex = new Rectangle(_cHex.Left - 26, _cHex.Y, 10000, _cHex.Height); - Render2D.DrawText(style.FontMedium, "Hex", hex, textColor, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, "Hex", hex, textColor, TextAlignment.Near, TextAlignment.Center); // Color difference var newRect = new Rectangle(_cOK.X, _cHex.Bottom + PickerMargin, _cCancel.Right - _cOK.Left, 0); diff --git a/Source/Editor/GUI/Docking/DockPanelProxy.cs b/Source/Editor/GUI/Docking/DockPanelProxy.cs index e6e57de8e..0b36b85fa 100644 --- a/Source/Editor/GUI/Docking/DockPanelProxy.cs +++ b/Source/Editor/GUI/Docking/DockPanelProxy.cs @@ -208,7 +208,7 @@ namespace FlaxEditor.GUI.Docking } // Draw text - Render2D.DrawText( + FallbackTextUtils.DrawText( style.FontMedium, tab.Title, new Rectangle(DockPanel.DefaultLeftTextMargin + iconWidth, 0, Width - DockPanel.DefaultLeftTextMargin - DockPanel.DefaultButtonsSize - 2 * DockPanel.DefaultButtonsMargin, DockPanel.DefaultHeaderHeight), @@ -271,7 +271,7 @@ namespace FlaxEditor.GUI.Docking } // Draw text - Render2D.DrawText( + FallbackTextUtils.DrawText( style.FontMedium, tab.Title, new Rectangle(x + DockPanel.DefaultLeftTextMargin + iconWidth, 0, 10000, DockPanel.DefaultHeaderHeight), diff --git a/Source/Editor/GUI/Docking/DockWindow.cs b/Source/Editor/GUI/Docking/DockWindow.cs index b7c12287f..561c898a0 100644 --- a/Source/Editor/GUI/Docking/DockWindow.cs +++ b/Source/Editor/GUI/Docking/DockWindow.cs @@ -6,7 +6,6 @@ using FlaxEngine; using FlaxEngine.Assertions; using FlaxEngine.GUI; using FlaxEditor.Options; -using System.Linq; namespace FlaxEditor.GUI.Docking { diff --git a/Source/Editor/GUI/ItemsListContextMenu.cs b/Source/Editor/GUI/ItemsListContextMenu.cs index b823cc907..d3c433a47 100644 --- a/Source/Editor/GUI/ItemsListContextMenu.cs +++ b/Source/Editor/GUI/ItemsListContextMenu.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; -using System.Linq; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.GUI.Input; using FlaxEditor.Utilities; @@ -87,8 +86,8 @@ namespace FlaxEditor.GUI var font = style.FontSmall; for (int i = 0; i < ranges.Length; i++) { - var start = font.GetCharPosition(Name, ranges[i].StartIndex); - var end = font.GetCharPosition(Name, ranges[i].EndIndex); + var start = FallbackTextUtils.GetCharPosition(font, Name, ranges[i].StartIndex); + var end = FallbackTextUtils.GetCharPosition(font, Name, ranges[i].EndIndex); _highlights.Add(new Rectangle(start.X + 2, 0, end.X - start.X, Height)); } Visible = true; @@ -137,7 +136,7 @@ namespace FlaxEditor.GUI } // Draw name - Render2D.DrawText(style.FontSmall, Name, textRect, TintColor * (Enabled ? style.Foreground : style.ForegroundDisabled), TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontSmall, Name, textRect, TintColor * (Enabled ? style.Foreground : style.ForegroundDisabled), TextAlignment.Near, TextAlignment.Center); } /// diff --git a/Source/Editor/GUI/MainMenuButton.cs b/Source/Editor/GUI/MainMenuButton.cs index 6fe8b98d3..43849f4ab 100644 --- a/Source/Editor/GUI/MainMenuButton.cs +++ b/Source/Editor/GUI/MainMenuButton.cs @@ -2,7 +2,6 @@ using FlaxEngine; using FlaxEngine.GUI; -using System.Linq; namespace FlaxEditor.GUI { @@ -73,7 +72,7 @@ namespace FlaxEditor.GUI } // Draw text - Render2D.DrawText(style.FontMedium, Text, clientRect, enabled && hasChildItems ? style.Foreground : style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, Text, clientRect, enabled && hasChildItems ? style.Foreground : style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); } /// diff --git a/Source/Editor/GUI/NavigationButton.cs b/Source/Editor/GUI/NavigationButton.cs index 5d399da02..8dedf72fd 100644 --- a/Source/Editor/GUI/NavigationButton.cs +++ b/Source/Editor/GUI/NavigationButton.cs @@ -2,7 +2,6 @@ using FlaxEngine; using FlaxEngine.GUI; -using System.Linq; namespace FlaxEditor.GUI { @@ -58,7 +57,7 @@ namespace FlaxEditor.GUI } // Draw text - Render2D.DrawText(style.FontMedium, Text, textRect, EnabledInHierarchy ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, Text, textRect, EnabledInHierarchy ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); } /// diff --git a/Source/Editor/GUI/Row.cs b/Source/Editor/GUI/Row.cs index ab74cbe96..c7be48626 100644 --- a/Source/Editor/GUI/Row.cs +++ b/Source/Editor/GUI/Row.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; -using System.Linq; using FlaxEngine; using FlaxEngine.GUI; @@ -99,7 +98,7 @@ namespace FlaxEditor.GUI rect.Width -= leftDepthMargin; Render2D.PushClip(rect); - Render2D.DrawText(style.FontMedium, text, rect, style.Foreground, column.CellAlignment, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, text, rect, style.Foreground, column.CellAlignment, TextAlignment.Center); Render2D.PopClip(); x += width; diff --git a/Source/Editor/GUI/StatusBar.cs b/Source/Editor/GUI/StatusBar.cs index f8f7ae839..7770af9a1 100644 --- a/Source/Editor/GUI/StatusBar.cs +++ b/Source/Editor/GUI/StatusBar.cs @@ -56,7 +56,7 @@ namespace FlaxEditor.GUI Render2D.DrawSprite(style.StatusBarSizeGrip, new Rectangle(Width - 12, 10, 12, 12), style.Foreground); // Draw status text - Render2D.DrawText(style.FontSmall, Text, new Rectangle(4, 0, Width - 20, Height), TextColor, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontSmall, Text, new Rectangle(4, 0, Width - 20, Height), TextColor, TextAlignment.Near, TextAlignment.Center); } } } diff --git a/Source/Editor/GUI/StyleValueEditor.cs b/Source/Editor/GUI/StyleValueEditor.cs index a89902177..db6696152 100644 --- a/Source/Editor/GUI/StyleValueEditor.cs +++ b/Source/Editor/GUI/StyleValueEditor.cs @@ -157,7 +157,7 @@ namespace FlaxEditor.GUI { Rectangle textRectangle = r; textRectangle.X = 4; - Render2D.DrawText(style.FontMedium, "No Style", textRectangle, style.Foreground); + FallbackTextUtils.DrawText(style.FontMedium, "No Style", textRectangle, style.Foreground); } } diff --git a/Source/Editor/GUI/Table.cs b/Source/Editor/GUI/Table.cs index 54656a6c2..2358557c3 100644 --- a/Source/Editor/GUI/Table.cs +++ b/Source/Editor/GUI/Table.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; -using System.Linq; using System.Runtime.CompilerServices; using FlaxEngine; using FlaxEngine.GUI; @@ -131,7 +130,7 @@ namespace FlaxEditor.GUI var style = Style.Current; var font = column.TitleFont ?? style.FontMedium; - Render2D.DrawText(font, column.Title, rect, column.TitleColor, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(font, column.Title, rect, column.TitleColor, TextAlignment.Center, TextAlignment.Center); if (columnIndex < _columns.Length - 1) { diff --git a/Source/Editor/GUI/Tabs/Tabs.cs b/Source/Editor/GUI/Tabs/Tabs.cs index 3c70363e7..5995c7ef5 100644 --- a/Source/Editor/GUI/Tabs/Tabs.cs +++ b/Source/Editor/GUI/Tabs/Tabs.cs @@ -98,7 +98,7 @@ namespace FlaxEditor.GUI.Tabs // Draw text if (!string.IsNullOrEmpty(Tab.Text)) { - Render2D.DrawText(style.FontMedium, Tab.Text, new Rectangle(tabRect.X + textOffset, tabRect.Y, tabRect.Width - textOffset, tabRect.Height), style.Foreground, Tabs.TabsTextHorizontalAlignment, Tabs.TabsTextVerticalAlignment); + FallbackTextUtils.DrawText(style.FontMedium, Tab.Text, new Rectangle(tabRect.X + textOffset, tabRect.Y, tabRect.Width - textOffset, tabRect.Height), style.Foreground, Tabs.TabsTextHorizontalAlignment, Tabs.TabsTextVerticalAlignment); } } } diff --git a/Source/Editor/GUI/Timeline/GUI/Background.cs b/Source/Editor/GUI/Timeline/GUI/Background.cs index b9eff562a..05c1121c0 100644 --- a/Source/Editor/GUI/Timeline/GUI/Background.cs +++ b/Source/Editor/GUI/Timeline/GUI/Background.cs @@ -301,7 +301,7 @@ namespace FlaxEditor.GUI.Timeline.GUI default: throw new ArgumentOutOfRangeException(); } var labelRect = new Rectangle(x + 2, -verticalLinesHeaderExtend * 0.8f + timeAxisHeaderOffset, 50, verticalLinesHeaderExtend); - Render2D.DrawText(style.FontSmall, labelText, labelRect, labelColor, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.8f); + FallbackTextUtils.DrawText(style.FontSmall, labelText, labelRect, labelColor, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.8f); } } } diff --git a/Source/Editor/GUI/Timeline/GUI/PositionHandle.cs b/Source/Editor/GUI/Timeline/GUI/PositionHandle.cs index 791fb7133..3f835a35f 100644 --- a/Source/Editor/GUI/Timeline/GUI/PositionHandle.cs +++ b/Source/Editor/GUI/Timeline/GUI/PositionHandle.cs @@ -2,7 +2,6 @@ using System; using System.Globalization; -using System.Linq; using FlaxEngine; using FlaxEngine.GUI; diff --git a/Source/Editor/GUI/Timeline/Track.cs b/Source/Editor/GUI/Timeline/Track.cs index f38917aa6..20be8f0c2 100644 --- a/Source/Editor/GUI/Timeline/Track.cs +++ b/Source/Editor/GUI/Timeline/Track.cs @@ -965,7 +965,7 @@ namespace FlaxEditor.GUI.Timeline } // Draw text - Render2D.DrawText(style.FontSmall, Title ?? Name, textRect, textColor, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontSmall, Title ?? Name, textRect, textColor, TextAlignment.Near, TextAlignment.Center); // Disabled overlay DrawDisabled = Mute || (ParentTrack != null && ParentTrack.DrawDisabled); diff --git a/Source/Editor/GUI/ToolStripButton.cs b/Source/Editor/GUI/ToolStripButton.cs index 50dce78b4..c4f4603e2 100644 --- a/Source/Editor/GUI/ToolStripButton.cs +++ b/Source/Editor/GUI/ToolStripButton.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; -using System.Linq; using FlaxEngine; using FlaxEngine.GUI; @@ -137,7 +136,7 @@ namespace FlaxEditor.GUI if (!string.IsNullOrEmpty(_text)) { textRect.Size.X = Width - DefaultMargin - textRect.Left; - Render2D.DrawText(style.FontMedium, _text, textRect, enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, _text, textRect, enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); } } diff --git a/Source/Editor/GUI/Tree/TreeNode.cs b/Source/Editor/GUI/Tree/TreeNode.cs index 700ba6da1..6680e3608 100644 --- a/Source/Editor/GUI/Tree/TreeNode.cs +++ b/Source/Editor/GUI/Tree/TreeNode.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; -using System.Linq; using FlaxEngine; using FlaxEngine.GUI; @@ -657,7 +656,7 @@ namespace FlaxEditor.GUI.Tree } // Draw text - Render2D.DrawText(TextFont.GetFont(), _text, textRect, _cachedTextColor, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(TextFont.GetFont(), _text, textRect, _cachedTextColor, TextAlignment.Near, TextAlignment.Center); // Draw drag and drop effect if (IsDragOver && _tree.DraggedOverNode == this) diff --git a/Source/Editor/Options/OptionsModule.cs b/Source/Editor/Options/OptionsModule.cs index b13548016..dc376a0a2 100644 --- a/Source/Editor/Options/OptionsModule.cs +++ b/Source/Editor/Options/OptionsModule.cs @@ -224,7 +224,7 @@ namespace FlaxEditor.Options } } - Render2D.Fallbacks = FallbackFonts.Create(Options.Interface.Fallbacks); + FallbackTextUtils.Fallbacks = FallbackFonts.Create(Options.Interface.Fallbacks); } /// diff --git a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs index f64e46385..eefd7f4dd 100644 --- a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs +++ b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs @@ -142,8 +142,8 @@ namespace FlaxEditor.SceneGraph.GUI var textRect = TextRect; for (int i = 0; i < ranges.Length; i++) { - var start = font.GetCharPosition(text, ranges[i].StartIndex); - var end = font.GetCharPosition(text, ranges[i].EndIndex); + var start = FallbackTextUtils.GetCharPosition(font, text, ranges[i].StartIndex); + var end = FallbackTextUtils.GetCharPosition(font, text, ranges[i].EndIndex); _highlights.Add(new Rectangle(start.X + textRect.X, textRect.Y, end.X - start.X, textRect.Height)); } isThisVisible = true; diff --git a/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs b/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs index 078b89c56..be9a96a7d 100644 --- a/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs +++ b/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs @@ -379,7 +379,7 @@ namespace FlaxEditor.Surface.Archetypes } // Name - Render2D.DrawText(style.FontLarge, Title, _textRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontLarge, Title, _textRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); } /// @@ -1128,7 +1128,7 @@ namespace FlaxEditor.Surface.Archetypes } // Name - Render2D.DrawText(style.FontLarge, Title, _textRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontLarge, Title, _textRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); // Close button Render2D.DrawSprite(style.Cross, _closeButtonRect, _closeButtonRect.Contains(_mousePosition) ? style.Foreground : style.ForegroundGrey); diff --git a/Source/Editor/Surface/Archetypes/BehaviorTree.cs b/Source/Editor/Surface/Archetypes/BehaviorTree.cs index 5f3c0b0a2..b240685cc 100644 --- a/Source/Editor/Surface/Archetypes/BehaviorTree.cs +++ b/Source/Editor/Surface/Archetypes/BehaviorTree.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; -using System.Linq; using FlaxEditor.CustomEditors.Dedicated; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.GUI.Drag; @@ -185,7 +184,7 @@ namespace FlaxEditor.Surface.Archetypes if (!string.IsNullOrEmpty(_debugInfo)) { var style = Style.Current; - Render2D.DrawText(style.FontSmall, _debugInfo, new Rectangle(4, _headerRect.Bottom + 4, _debugInfoSize), style.Foreground); + FallbackTextUtils.DrawText(style.FontSmall, _debugInfo, new Rectangle(4, _headerRect.Bottom + 4, _debugInfoSize), style.Foreground); } // Debug relevancy outline diff --git a/Source/Editor/Surface/Archetypes/ParticleModules.cs b/Source/Editor/Surface/Archetypes/ParticleModules.cs index e5be3d35d..6082e212e 100644 --- a/Source/Editor/Surface/Archetypes/ParticleModules.cs +++ b/Source/Editor/Surface/Archetypes/ParticleModules.cs @@ -113,7 +113,7 @@ namespace FlaxEditor.Surface.Archetypes var idx = (int)ModuleType; var headerRect = new Rectangle(0, 0, Width, 16.0f); //Render2D.FillRectangle(headerRect, Color.Red); - Render2D.DrawText(style.FontMedium, Title, headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, Title, headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); DrawChildren(); diff --git a/Source/Editor/Surface/Archetypes/Particles.cs b/Source/Editor/Surface/Archetypes/Particles.cs index 40b38f7ce..bf7828960 100644 --- a/Source/Editor/Surface/Archetypes/Particles.cs +++ b/Source/Editor/Surface/Archetypes/Particles.cs @@ -154,7 +154,7 @@ namespace FlaxEditor.Surface.Archetypes if (headerRect.Contains(mousePosition)) headerColor *= 1.07f; Render2D.FillRectangle(headerRect, headerColor); - Render2D.DrawText(style.FontLarge, Names[idx], headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontLarge, Names[idx], headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); DrawChildren(); } @@ -194,7 +194,7 @@ namespace FlaxEditor.Surface.Archetypes if (_headerRect.Contains(ref _mousePosition)) headerColor *= 1.07f; Render2D.FillRectangle(_headerRect, headerColor); - Render2D.DrawText(style.FontLarge, Title, _headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontLarge, Title, _headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); DrawChildren(); diff --git a/Source/Editor/Surface/ContextMenu/VisjectCM.cs b/Source/Editor/Surface/ContextMenu/VisjectCM.cs index 5256b3cb7..930741807 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCM.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCM.cs @@ -141,7 +141,7 @@ namespace FlaxEditor.Surface.ContextMenu }; // Title bar - var titleFontReference = new FontReference(Style.Current.FontLarge); + var titleFontReference = new FontReference(Style.Current.FontLarge.Asset, 10); var titleLabel = new Label { Width = Width * 0.5f - 8f, diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs index 019be78b6..f4976be67 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs @@ -200,8 +200,8 @@ namespace FlaxEditor.Surface.ContextMenu var font = style.FontSmall; for (int i = 0; i < ranges.Length; i++) { - var start = font.GetCharPosition(_archetype.Title, ranges[i].StartIndex); - var end = font.GetCharPosition(_archetype.Title, ranges[i].EndIndex); + var start = FallbackTextUtils.GetCharPosition(font, _archetype.Title, ranges[i].StartIndex); + var end = FallbackTextUtils.GetCharPosition(font, _archetype.Title, ranges[i].EndIndex); _highlights.Add(new Rectangle(start.X + textRect.X, 0, end.X - start.X, Height)); if (ranges[i].StartIndex <= 0) @@ -222,8 +222,8 @@ namespace FlaxEditor.Surface.ContextMenu _highlights.Clear(); var style = Style.Current; var font = style.FontSmall; - var start = font.GetCharPosition(_archetype.Title, 0); - var end = font.GetCharPosition(_archetype.Title, _archetype.Title.Length - 1); + var start = FallbackTextUtils.GetCharPosition(font, _archetype.Title, 0); + var end = FallbackTextUtils.GetCharPosition(font, _archetype.Title, _archetype.Title.Length - 1); _highlights.Add(new Rectangle(start.X + textRect.X, 0, end.X - start.X, Height)); _isFullMatch = true; Visible = true; @@ -237,8 +237,8 @@ namespace FlaxEditor.Surface.ContextMenu _highlights.Clear(); var style = Style.Current; var font = style.FontSmall; - var start = font.GetCharPosition(_archetype.Title, 0); - var end = font.GetCharPosition(_archetype.Title, _archetype.Title.Length - 1); + var start = FallbackTextUtils.GetCharPosition(font, _archetype.Title, 0); + var end = FallbackTextUtils.GetCharPosition(font, _archetype.Title, _archetype.Title.Length - 1); _highlights.Add(new Rectangle(start.X + textRect.X, 0, end.X - start.X, Height)); Visible = true; @@ -283,19 +283,19 @@ namespace FlaxEditor.Surface.ContextMenu } // Draw name - Render2D.DrawText(style.FontSmall, _archetype.Title, textRect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontSmall, _archetype.Title, textRect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); if (_archetype.SubTitle != null) { var titleLength = FallbackTextUtils.MeasureText(style.FontSmall, _archetype.Title).X; var subTitleRect = new Rectangle(textRect.X + titleLength, textRect.Y, textRect.Width - titleLength, textRect.Height); - Render2D.DrawText(style.FontSmall, _archetype.SubTitle, subTitleRect, style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontSmall, _archetype.SubTitle, subTitleRect, style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); } // Reset transform and draw score mark if (showScoreHit) { Render2D.PopTransform(); - Render2D.DrawText(style.FontSmall, "> ", textRect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontSmall, "> ", textRect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); } } diff --git a/Source/Editor/Surface/Elements/InputBox.cs b/Source/Editor/Surface/Elements/InputBox.cs index 526d85d15..a5a99b932 100644 --- a/Source/Editor/Surface/Elements/InputBox.cs +++ b/Source/Editor/Surface/Elements/InputBox.cs @@ -1443,7 +1443,7 @@ namespace FlaxEditor.Surface.Elements // Draw text var style = Style.Current; var rect = new Rectangle(Width + 4, 0, 1410, Height); - Render2D.DrawText(style.FontSmall, Text, rect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontSmall, Text, rect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); } /// diff --git a/Source/Editor/Surface/Elements/OutputBox.cs b/Source/Editor/Surface/Elements/OutputBox.cs index 497e2001a..5fb123700 100644 --- a/Source/Editor/Surface/Elements/OutputBox.cs +++ b/Source/Editor/Surface/Elements/OutputBox.cs @@ -189,7 +189,7 @@ namespace FlaxEditor.Surface.Elements // Draw text var style = Style.Current; var rect = new Rectangle(-100, 0, 100 - 2, Height); - Render2D.DrawText(style.FontSmall, Text, rect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Far, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontSmall, Text, rect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Far, TextAlignment.Center); } } } diff --git a/Source/Editor/Surface/Elements/TextView.cs b/Source/Editor/Surface/Elements/TextView.cs index 284497396..c52667fe3 100644 --- a/Source/Editor/Surface/Elements/TextView.cs +++ b/Source/Editor/Surface/Elements/TextView.cs @@ -25,7 +25,7 @@ namespace FlaxEditor.Surface.Elements var style = Style.Current; var color = Enabled ? style.Foreground : style.ForegroundDisabled; - Render2D.DrawText(style.FontSmall, Archetype.Text, new Rectangle(Float2.Zero, Size), color, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontSmall, Archetype.Text, new Rectangle(Float2.Zero, Size), color, TextAlignment.Near, TextAlignment.Center); } } } diff --git a/Source/Editor/Surface/SurfaceComment.cs b/Source/Editor/Surface/SurfaceComment.cs index aad45190e..26cc912ab 100644 --- a/Source/Editor/Surface/SurfaceComment.cs +++ b/Source/Editor/Surface/SurfaceComment.cs @@ -169,7 +169,7 @@ namespace FlaxEditor.Surface // Header Render2D.FillRectangle(_headerRect, headerColor); - Render2D.DrawText(style.FontLarge, Title, _headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontLarge, Title, _headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); // Close button Render2D.DrawSprite(style.Cross, _closeButtonRect, _closeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey); diff --git a/Source/Editor/Surface/SurfaceNode.cs b/Source/Editor/Surface/SurfaceNode.cs index 3c2381192..df706faa6 100644 --- a/Source/Editor/Surface/SurfaceNode.cs +++ b/Source/Editor/Surface/SurfaceNode.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; -using System.Linq; using FlaxEditor.Scripting; using FlaxEditor.Surface.Elements; using FlaxEditor.Surface.Undo; @@ -1028,7 +1027,7 @@ namespace FlaxEditor.Surface if (_headerRect.Contains(ref _mousePosition)) headerColor *= 1.07f; Render2D.FillRectangle(_headerRect, headerColor); - Render2D.DrawText(style.FontLarge, Title, _headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontLarge, Title, _headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); // Close button if ((Archetype.Flags & NodeFlags.NoCloseButton) == 0 && Surface.CanEdit) diff --git a/Source/Editor/Tools/Foliage/FoliageTab.cs b/Source/Editor/Tools/Foliage/FoliageTab.cs index c2b1fbe68..d36c3b6ed 100644 --- a/Source/Editor/Tools/Foliage/FoliageTab.cs +++ b/Source/Editor/Tools/Foliage/FoliageTab.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; -using System.Linq; using FlaxEditor.GUI.Tabs; using FlaxEditor.Modules; using FlaxEditor.SceneGraph.Actors; diff --git a/Source/Editor/Tools/Terrain/CarveTab.cs b/Source/Editor/Tools/Terrain/CarveTab.cs index 0a86aab6f..e64a364f0 100644 --- a/Source/Editor/Tools/Terrain/CarveTab.cs +++ b/Source/Editor/Tools/Terrain/CarveTab.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; -using System.Linq; using FlaxEditor.GUI.Tabs; using FlaxEditor.Modules; using FlaxEditor.SceneGraph.Actors; diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index 0633d0ba9..515514131 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -1233,8 +1233,8 @@ namespace FlaxEditor.Viewport color = Color.Yellow; var text = string.Format("FPS: {0}", fps); var font = Style.Current.FontMedium; - Render2D.DrawText(font, text, new Rectangle(Float2.One, Size), Color.Black); - Render2D.DrawText(font, text, new Rectangle(Float2.Zero, Size), color); + FallbackTextUtils.DrawText(font, text, new Rectangle(Float2.One, Size), Color.Black); + FallbackTextUtils.DrawText(font, text, new Rectangle(Float2.Zero, Size), color); } } @@ -1814,7 +1814,7 @@ namespace FlaxEditor.Viewport { var bounds = new Rectangle(Float2.Zero, Size); Render2D.FillRectangle(bounds, new Color(0.0f, 0.0f, 0.0f, 0.2f)); - Render2D.DrawText(Style.Current.FontLarge, "Debugger breakpoint hit...", bounds, Color.White, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(Style.Current.FontLarge, "Debugger breakpoint hit...", bounds, Color.White, TextAlignment.Center, TextAlignment.Center); } } diff --git a/Source/Editor/Viewport/Previews/AnimationPreview.cs b/Source/Editor/Viewport/Previews/AnimationPreview.cs index 1679a36bc..c4ada83c4 100644 --- a/Source/Editor/Viewport/Previews/AnimationPreview.cs +++ b/Source/Editor/Viewport/Previews/AnimationPreview.cs @@ -96,11 +96,11 @@ namespace FlaxEditor.Viewport.Previews var skinnedModel = SkinnedModel; if (skinnedModel == null) { - Render2D.DrawText(style.FontLarge, "Missing Base Model", new Rectangle(Float2.Zero, Size), Color.Red, TextAlignment.Center, TextAlignment.Center, TextWrapping.WrapWords); + FallbackTextUtils.DrawText(style.FontLarge, "Missing Base Model", new Rectangle(Float2.Zero, Size), Color.Red, TextAlignment.Center, TextAlignment.Center, TextWrapping.WrapWords); } else if (!skinnedModel.IsLoaded) { - Render2D.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); } } diff --git a/Source/Editor/Viewport/Previews/ModelPreview.cs b/Source/Editor/Viewport/Previews/ModelPreview.cs index a6496aafe..b31776bf6 100644 --- a/Source/Editor/Viewport/Previews/ModelPreview.cs +++ b/Source/Editor/Viewport/Previews/ModelPreview.cs @@ -409,8 +409,8 @@ namespace FlaxEditor.Viewport.Previews } var font = Style.Current.FontMedium; var pos = new Float2(10, 50); - Render2D.DrawText(font, text, new Rectangle(pos + Float2.One, Size), Color.Black); - Render2D.DrawText(font, text, new Rectangle(pos, Size), Color.White); + FallbackTextUtils.DrawText(font, text, new Rectangle(pos + Float2.One, Size), Color.Black); + FallbackTextUtils.DrawText(font, text, new Rectangle(pos, Size), Color.White); } } diff --git a/Source/Editor/Viewport/Previews/ParticleSystemPreview.cs b/Source/Editor/Viewport/Previews/ParticleSystemPreview.cs index c6ce5a7b5..a6eb721ce 100644 --- a/Source/Editor/Viewport/Previews/ParticleSystemPreview.cs +++ b/Source/Editor/Viewport/Previews/ParticleSystemPreview.cs @@ -254,7 +254,7 @@ namespace FlaxEditor.Viewport.Previews if (_showParticlesCounter) { var count = _previewEffect.ParticlesCount; - Render2D.DrawText( + FallbackTextUtils.DrawText( Style.Current.FontSmall, "Particles: " + count, new Rectangle(Float2.Zero, Size), diff --git a/Source/Editor/Viewport/Previews/SkinnedModelPreview.cs b/Source/Editor/Viewport/Previews/SkinnedModelPreview.cs index 8bcc506d9..6c02c019a 100644 --- a/Source/Editor/Viewport/Previews/SkinnedModelPreview.cs +++ b/Source/Editor/Viewport/Previews/SkinnedModelPreview.cs @@ -161,8 +161,8 @@ namespace FlaxEditor.Viewport.Previews } var font = Style.Current.FontMedium; var pos = new Float2(10, 50); - Render2D.DrawText(font, text, new Rectangle(pos + Float2.One, Size), Color.Black); - Render2D.DrawText(font, text, new Rectangle(pos, Size), Color.White); + FallbackTextUtils.DrawText(font, text, new Rectangle(pos + Float2.One, Size), Color.Black); + FallbackTextUtils.DrawText(font, text, new Rectangle(pos, Size), Color.White); } } diff --git a/Source/Editor/Viewport/Previews/TexturePreview.cs b/Source/Editor/Viewport/Previews/TexturePreview.cs index ec10bb019..5e7594e9c 100644 --- a/Source/Editor/Viewport/Previews/TexturePreview.cs +++ b/Source/Editor/Viewport/Previews/TexturePreview.cs @@ -103,7 +103,7 @@ namespace FlaxEditor.Viewport.Previews { var bounds = new Rectangle(Float2.Zero, Size); Render2D.FillRectangle(bounds, new Color(0.0f, 0.0f, 0.0f, 0.2f)); - Render2D.DrawText(Style.Current.FontLarge, "Debugger breakpoint hit...", bounds, Color.White, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(Style.Current.FontLarge, "Debugger breakpoint hit...", bounds, Color.White, TextAlignment.Center, TextAlignment.Center); } Render2D.PopClip(); diff --git a/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs b/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs index 598f4ee32..43f990afb 100644 --- a/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs +++ b/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; -using System.Linq; using FlaxEditor.GUI.ContextMenu; using FlaxEngine; using FlaxEngine.GUI; @@ -124,7 +123,7 @@ namespace FlaxEditor.Viewport.Widgets } // Draw text - Render2D.DrawText(style.FontMedium, _text, textRect, style.ForegroundViewport * (IsMouseOver ? 1.0f : 0.9f), TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, _text, textRect, style.ForegroundViewport * (IsMouseOver ? 1.0f : 0.9f), TextAlignment.Center, TextAlignment.Center); } /// diff --git a/Source/Editor/Windows/AboutDialog.cs b/Source/Editor/Windows/AboutDialog.cs index 02aad16ec..8c38ed2a9 100644 --- a/Source/Editor/Windows/AboutDialog.cs +++ b/Source/Editor/Windows/AboutDialog.cs @@ -2,7 +2,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System.Collections.Generic; -using System.Linq; using FlaxEditor.GUI.Dialogs; using FlaxEngine; using FlaxEngine.GUI; diff --git a/Source/Editor/Windows/Assets/AnimationGraphWindow.cs b/Source/Editor/Windows/Assets/AnimationGraphWindow.cs index 12eac22b5..eb4198fd8 100644 --- a/Source/Editor/Windows/Assets/AnimationGraphWindow.cs +++ b/Source/Editor/Windows/Assets/AnimationGraphWindow.cs @@ -51,7 +51,7 @@ namespace FlaxEditor.Windows.Assets var style = Style.Current; if (_window.Asset == null || !_window.Asset.IsLoaded) { - Render2D.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); } } } diff --git a/Source/Editor/Windows/Assets/AnimationWindow.cs b/Source/Editor/Windows/Assets/AnimationWindow.cs index 8f88d93f6..042cf2b37 100644 --- a/Source/Editor/Windows/Assets/AnimationWindow.cs +++ b/Source/Editor/Windows/Assets/AnimationWindow.cs @@ -61,7 +61,7 @@ namespace FlaxEditor.Windows.Assets var animation = _window.Asset; if (animation == null || !animation.IsLoaded) { - Render2D.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); } } diff --git a/Source/Editor/Windows/Assets/ModelWindow.cs b/Source/Editor/Windows/Assets/ModelWindow.cs index c2764a5a6..d48966043 100644 --- a/Source/Editor/Windows/Assets/ModelWindow.cs +++ b/Source/Editor/Windows/Assets/ModelWindow.cs @@ -48,7 +48,7 @@ namespace FlaxEditor.Windows.Assets var asset = _window.Asset; if (asset == null || !asset.IsLoaded) { - Render2D.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); } } } @@ -645,7 +645,7 @@ namespace FlaxEditor.Windows.Assets if (!Proxy.Window._meshData.RequestMeshData(Proxy.Window._asset)) { Invalidate(); - Render2D.DrawText(Style.Current.FontMedium, "Loading...", new Rectangle(Float2.Zero, size), Color.White, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(Style.Current.FontMedium, "Loading...", new Rectangle(Float2.Zero, size), Color.White, TextAlignment.Center, TextAlignment.Center); return; } diff --git a/Source/Editor/Windows/Assets/SkinnedModelWindow.cs b/Source/Editor/Windows/Assets/SkinnedModelWindow.cs index 95827240c..1e7192d9f 100644 --- a/Source/Editor/Windows/Assets/SkinnedModelWindow.cs +++ b/Source/Editor/Windows/Assets/SkinnedModelWindow.cs @@ -50,7 +50,7 @@ namespace FlaxEditor.Windows.Assets var asset = _window.Asset; if (asset == null || !asset.IsLoaded) { - Render2D.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); } } } @@ -715,7 +715,7 @@ namespace FlaxEditor.Windows.Assets if (!Proxy.Window.RequestMeshData()) { Invalidate(); - Render2D.DrawText(Style.Current.FontMedium, "Loading...", new Rectangle(Float2.Zero, size), Color.White, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(Style.Current.FontMedium, "Loading...", new Rectangle(Float2.Zero, size), Color.White, TextAlignment.Center, TextAlignment.Center); return; } diff --git a/Source/Editor/Windows/ContentWindow.Search.cs b/Source/Editor/Windows/ContentWindow.Search.cs index a1072d158..67b7deddd 100644 --- a/Source/Editor/Windows/ContentWindow.Search.cs +++ b/Source/Editor/Windows/ContentWindow.Search.cs @@ -57,7 +57,7 @@ namespace FlaxEditor.Windows var textRect = new Rectangle(margin, 0, clientRect.Width - boxSize - 2.0f * margin, clientRect.Height); Render2D.PushClip(textRect); var textColor = TextColor; - Render2D.DrawText(Font.GetFont(), "View", textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, textScale); + FallbackTextUtils.DrawText(Font.GetFont(), "View", textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, textScale); Render2D.PopClip(); // Arrow diff --git a/Source/Editor/Windows/DebugLogWindow.cs b/Source/Editor/Windows/DebugLogWindow.cs index ab0c07ec5..91d4f9d8c 100644 --- a/Source/Editor/Windows/DebugLogWindow.cs +++ b/Source/Editor/Windows/DebugLogWindow.cs @@ -140,11 +140,11 @@ namespace FlaxEditor.Windows Render2D.PushClip(ref clientRect); if (LogCount == 1) { - Render2D.DrawText(style.FontMedium, Desc.Title, textRect, style.Foreground); + FallbackTextUtils.DrawText(style.FontMedium, Desc.Title, textRect, style.Foreground); } else if (LogCount > 1) { - Render2D.DrawText(style.FontMedium, $"{Desc.Title} ({LogCount})", textRect, style.Foreground); + FallbackTextUtils.DrawText(style.FontMedium, $"{Desc.Title} ({LogCount})", textRect, style.Foreground); } Render2D.PopClip(); } diff --git a/Source/Editor/Windows/GameWindow.cs b/Source/Editor/Windows/GameWindow.cs index 4a8c11176..2c0364995 100644 --- a/Source/Editor/Windows/GameWindow.cs +++ b/Source/Editor/Windows/GameWindow.cs @@ -827,7 +827,7 @@ namespace FlaxEditor.Windows if (Camera.MainCamera == null) { var style = Style.Current; - Render2D.DrawText(style.FontLarge, "No camera", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontLarge, "No camera", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); } // Selected UI controls outline @@ -866,8 +866,8 @@ namespace FlaxEditor.Windows var alpha = Mathf.Saturate(-animTime / fadeOutTime); var rect = new Rectangle(new Float2(6), Size - 12); var text = "Press Shift+F11 to unlock the mouse"; - Render2D.DrawText(style.FontSmall, text, rect + new Float2(1.0f), style.Background * alpha, TextAlignment.Near, TextAlignment.Far); - Render2D.DrawText(style.FontSmall, text, rect, style.Foreground * alpha, TextAlignment.Near, TextAlignment.Far); + FallbackTextUtils.DrawText(style.FontSmall, text, rect + new Float2(1.0f), style.Background * alpha, TextAlignment.Near, TextAlignment.Far); + FallbackTextUtils.DrawText(style.FontSmall, text, rect, style.Foreground * alpha, TextAlignment.Near, TextAlignment.Far); } timeout = 1.0f; @@ -884,7 +884,7 @@ namespace FlaxEditor.Windows { var bounds = new Rectangle(Float2.Zero, Size); Render2D.FillRectangle(bounds, new Color(0.0f, 0.0f, 0.0f, 0.2f)); - Render2D.DrawText(Style.Current.FontLarge, "Debugger breakpoint hit...", bounds, Color.White, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(Style.Current.FontLarge, "Debugger breakpoint hit...", bounds, Color.White, TextAlignment.Center, TextAlignment.Center); } } } diff --git a/Source/Editor/Windows/Profiler/SingleChart.cs b/Source/Editor/Windows/Profiler/SingleChart.cs index 4f36692e5..25241b4c6 100644 --- a/Source/Editor/Windows/Profiler/SingleChart.cs +++ b/Source/Editor/Windows/Profiler/SingleChart.cs @@ -138,8 +138,8 @@ namespace FlaxEditor.Windows.Profiler var headerRect = new Rectangle(0, chartHeight, Width, TitleHeight); var headerTextRect = new Rectangle(2, chartHeight, Width - 4, TitleHeight); Render2D.FillRectangle(headerRect, style.BackgroundNormal); - Render2D.DrawText(style.FontMedium, Title, headerTextRect, style.ForegroundGrey, TextAlignment.Near, TextAlignment.Center); - Render2D.DrawText(style.FontMedium, _sample, headerTextRect, style.Foreground, TextAlignment.Far, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, Title, headerTextRect, style.ForegroundGrey, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, _sample, headerTextRect, style.Foreground, TextAlignment.Far, TextAlignment.Center); } private void OnClick(ref Float2 location) diff --git a/Source/Editor/Windows/Profiler/Timeline.cs b/Source/Editor/Windows/Profiler/Timeline.cs index 780b31a3b..f87efdace 100644 --- a/Source/Editor/Windows/Profiler/Timeline.cs +++ b/Source/Editor/Windows/Profiler/Timeline.cs @@ -2,7 +2,6 @@ using FlaxEngine; using FlaxEngine.GUI; -using System.Linq; namespace FlaxEditor.Windows.Profiler { @@ -91,7 +90,7 @@ namespace FlaxEditor.Windows.Profiler if (_nameLength < bounds.Width + 4) { Render2D.PushClip(bounds); - Render2D.DrawText(style.FontMedium, _name, bounds, Style.Current.Foreground, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, _name, bounds, Style.Current.Foreground, TextAlignment.Center, TextAlignment.Center); Render2D.PopClip(); } } @@ -116,7 +115,7 @@ namespace FlaxEditor.Windows.Profiler var style = Style.Current; var rect = new Rectangle(Float2.Zero, Size); Render2D.PushClip(rect); - Render2D.DrawText(style.FontMedium, Name, rect, Style.Current.Foreground, TextAlignment.Center, TextAlignment.Center, TextWrapping.WrapChars); + FallbackTextUtils.DrawText(style.FontMedium, Name, rect, Style.Current.Foreground, TextAlignment.Center, TextAlignment.Center, TextWrapping.WrapChars); Render2D.PopClip(); } } diff --git a/Source/Editor/Windows/SceneTreeWindow.cs b/Source/Editor/Windows/SceneTreeWindow.cs index 63ba7b960..2fd990861 100644 --- a/Source/Editor/Windows/SceneTreeWindow.cs +++ b/Source/Editor/Windows/SceneTreeWindow.cs @@ -297,7 +297,7 @@ namespace FlaxEditor.Windows } if (overlayText != null) { - Render2D.DrawText(style.FontLarge, overlayText, GetClientArea(), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center, textWrap); + FallbackTextUtils.DrawText(style.FontLarge, overlayText, GetClientArea(), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center, textWrap); } base.Draw(); diff --git a/Source/Editor/Windows/ToolboxWindow.cs b/Source/Editor/Windows/ToolboxWindow.cs index 16b9165e1..f86341df3 100644 --- a/Source/Editor/Windows/ToolboxWindow.cs +++ b/Source/Editor/Windows/ToolboxWindow.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; -using System.Linq; using FlaxEditor.GUI.Input; using FlaxEditor.GUI.Tabs; using FlaxEditor.GUI.Tree; @@ -273,8 +272,8 @@ namespace FlaxEditor.Windows var textRect = item.TextRect; for (int i = 0; i < ranges.Length; i++) { - var start = font.GetCharPosition(text, ranges[i].StartIndex); - var end = font.GetCharPosition(text, ranges[i].EndIndex); + var start = FallbackTextUtils.GetCharPosition(font, text, ranges[i].StartIndex); + var end = FallbackTextUtils.GetCharPosition(font, text, ranges[i].EndIndex); highlights.Add(new Rectangle(start.X + textRect.X, textRect.Y, end.X - start.X, textRect.Height)); } item.SetHighlights(highlights); diff --git a/Source/Engine/Render2D/FallbackTextUtils.cs b/Source/Engine/Render2D/FallbackTextUtils.cs index eb676f0a9..b6d8770e2 100644 --- a/Source/Engine/Render2D/FallbackTextUtils.cs +++ b/Source/Engine/Render2D/FallbackTextUtils.cs @@ -1,4 +1,6 @@ +using System.Runtime.CompilerServices; + namespace FlaxEngine { /// @@ -11,41 +13,10 @@ namespace FlaxEngine get; set; } = null; - public static void DrawText(Font font, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void DrawText(Font font, string text, Color color, ref TextLayoutOptions layout, MaterialBase customMaterial = null, bool useFallback = true) { - var layout = new TextLayoutOptions - { - Bounds = layoutRect, - HorizontalAlignment = horizontalAlignment, - VerticalAlignment = verticalAlignment, - TextWrapping = textWrapping, - Scale = scale, - BaseLinesGapScale = baseLinesGapScale, - }; - - if (Fallbacks != null) - { - Render2D.DrawText(font, Fallbacks, text, color, ref layout); - } - else - { - Render2D.DrawText(font, text, color, ref layout); - } - } - - public static void DrawText(Font font, MaterialBase customMaterial, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) - { - var layout = new TextLayoutOptions - { - Bounds = layoutRect, - HorizontalAlignment = horizontalAlignment, - VerticalAlignment = verticalAlignment, - TextWrapping = textWrapping, - Scale = scale, - BaseLinesGapScale = baseLinesGapScale, - }; - - if (Fallbacks != null) + if (Fallbacks != null && useFallback) { Render2D.DrawText(font, Fallbacks, text, color, ref layout, customMaterial); } @@ -55,9 +26,56 @@ namespace FlaxEngine } } - public static Float2 MeasureText(Font font, string text) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void DrawText(Font font, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f, bool useFallback = true) { - if (Fallbacks != null) + var layout = new TextLayoutOptions + { + Bounds = layoutRect, + HorizontalAlignment = horizontalAlignment, + VerticalAlignment = verticalAlignment, + TextWrapping = textWrapping, + Scale = scale, + BaseLinesGapScale = baseLinesGapScale, + }; + + if (Fallbacks != null && useFallback) + { + Render2D.DrawText(font, Fallbacks, text, color, ref layout); + } + else + { + Render2D.DrawText(font, text, color, ref layout); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void DrawText(Font font, MaterialBase customMaterial, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f, bool useFallback = true) + { + var layout = new TextLayoutOptions + { + Bounds = layoutRect, + HorizontalAlignment = horizontalAlignment, + VerticalAlignment = verticalAlignment, + TextWrapping = textWrapping, + Scale = scale, + BaseLinesGapScale = baseLinesGapScale, + }; + + if (Fallbacks != null && useFallback) + { + Render2D.DrawText(font, Fallbacks, text, color, ref layout, customMaterial); + } + else + { + Render2D.DrawText(font, text, color, ref layout, customMaterial); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Float2 MeasureText(Font font, string text, bool useFallback = true) + { + if (Fallbacks != null && useFallback) { return font.MeasureText(Fallbacks, text); } @@ -67,9 +85,10 @@ namespace FlaxEngine } } - public static Float2 MeasureText(Font font, string text, ref TextRange textRange) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Float2 MeasureText(Font font, string text, ref TextRange textRange, bool useFallback = true) { - if (Fallbacks != null) + if (Fallbacks != null && useFallback) { return font.MeasureText(Fallbacks, text, ref textRange); } @@ -79,9 +98,10 @@ namespace FlaxEngine } } - public static Float2 MeasureText(Font font, string text, ref TextLayoutOptions layout) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Float2 MeasureText(Font font, string text, ref TextLayoutOptions layout, bool useFallback = true) { - if (Fallbacks != null) + if (Fallbacks != null && useFallback) { return font.MeasureText(Fallbacks, text, ref layout); } @@ -91,9 +111,10 @@ namespace FlaxEngine } } - public static Float2 MeasureText(Font font, string text, ref TextRange textRange, ref TextLayoutOptions layout) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Float2 MeasureText(Font font, string text, ref TextRange textRange, ref TextLayoutOptions layout, bool useFallback = true) { - if (Fallbacks != null) + if (Fallbacks != null && useFallback) { return font.MeasureText(Fallbacks, text, ref textRange, ref layout); } @@ -103,24 +124,108 @@ namespace FlaxEngine } } - public static Float2 GetCharPosition(Font font, string text, int index) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int HitTestText(Font font, string text, Float2 location, bool useFallback = true) { - return font.GetCharPosition(Style.Current.Fallbacks, text, index); + if (Fallbacks != null && useFallback) + { + return font.HitTestText(Fallbacks, text, location); + } + else + { + return font.HitTestText(text, location); + } } - public static Float2 GetCharPosition(Font font, string text, ref TextRange textRange, int index) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int HitTestText(Font font, string text, ref TextRange textRange, Float2 location, bool useFallback = true) { - return font.GetCharPosition(Style.Current.Fallbacks, text, ref textRange, index); + if (Fallbacks != null && useFallback) + { + return font.HitTestText(Fallbacks, text, ref textRange, location); + } + else + { + return font.HitTestText(text, ref textRange, location); + } } - public static Float2 GetCharPosition(Font font, string text, int index, ref TextLayoutOptions layout) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int HitTestText(Font font, string text, Float2 location, ref TextLayoutOptions layout, bool useFallback = true) { - return font.GetCharPosition(Style.Current.Fallbacks, text, index, ref layout); + if (Fallbacks != null && useFallback) + { + return font.HitTestText(Fallbacks, text, location, ref layout); + } + else + { + return font.HitTestText(text, location, ref layout); + } } - public static Float2 GetCharPosition(Font font, string text, ref TextRange textRange, int index, ref TextLayoutOptions layout) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int HitTestText(Font font, string text, ref TextRange textRange, Float2 location, ref TextLayoutOptions layout, bool useFallback = true) { - return font.GetCharPosition(Style.Current.Fallbacks, text, ref textRange, index, ref layout); + if (Fallbacks != null && useFallback) + { + return font.HitTestText(Fallbacks, text, ref textRange, location, ref layout); + } + else + { + return font.HitTestText(text, ref textRange, location, ref layout); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Float2 GetCharPosition(Font font, string text, int index, bool useFallback = true) + { + if (Fallbacks != null && useFallback) + { + return font.GetCharPosition(Fallbacks, text, index); + } + else + { + return font.GetCharPosition(text, index); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Float2 GetCharPosition(Font font, string text, ref TextRange textRange, int index, bool useFallback = true) + { + if (Fallbacks != null && useFallback) + { + return font.GetCharPosition(Fallbacks, text, ref textRange, index); + } + else + { + return font.GetCharPosition(text, ref textRange, index); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Float2 GetCharPosition(Font font, string text, int index, ref TextLayoutOptions layout, bool useFallback = true) + { + if (Fallbacks != null && useFallback) + { + return font.GetCharPosition(Fallbacks, text, index, ref layout); + } + else + { + return font.GetCharPosition(text, index, ref layout); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Float2 GetCharPosition(Font font, string text, ref TextRange textRange, int index, ref TextLayoutOptions layout, bool useFallback = true) + { + if (Fallbacks != null && useFallback) + { + return font.GetCharPosition(Fallbacks, text, ref textRange, index, ref layout); + } + else + { + return font.GetCharPosition(text, ref textRange, index, ref layout); + } } } } diff --git a/Source/Engine/UI/GUI/Common/Button.cs b/Source/Engine/UI/GUI/Common/Button.cs index 756501c5e..feb34418b 100644 --- a/Source/Engine/UI/GUI/Common/Button.cs +++ b/Source/Engine/UI/GUI/Common/Button.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; -using System.Linq; namespace FlaxEngine.GUI { @@ -262,7 +261,7 @@ namespace FlaxEngine.GUI Render2D.DrawRectangle(clientRect, borderColor, BorderThickness); // Draw text - Render2D.DrawText(_font?.GetFont(), TextMaterial, _text, clientRect, textColor, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(_font?.GetFont(), TextMaterial, _text, clientRect, textColor, TextAlignment.Center, TextAlignment.Center); } /// diff --git a/Source/Engine/UI/GUI/Common/Dropdown.cs b/Source/Engine/UI/GUI/Common/Dropdown.cs index 46f05f516..3b298b136 100644 --- a/Source/Engine/UI/GUI/Common/Dropdown.cs +++ b/Source/Engine/UI/GUI/Common/Dropdown.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; -using System.Linq; namespace FlaxEngine.GUI { @@ -674,7 +673,7 @@ namespace FlaxEngine.GUI var textRect = new Rectangle(margin, 0, clientRect.Width - boxSize - 2.0f * margin, clientRect.Height); Render2D.PushClip(textRect); var textColor = TextColor; - Render2D.DrawText(Font.GetFont(), FontMaterial, _items[_selectedIndex], textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(Font.GetFont(), FontMaterial, _items[_selectedIndex], textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center); Render2D.PopClip(); } diff --git a/Source/Engine/UI/GUI/Common/Label.cs b/Source/Engine/UI/GUI/Common/Label.cs index 4e26c1934..3d1caad28 100644 --- a/Source/Engine/UI/GUI/Common/Label.cs +++ b/Source/Engine/UI/GUI/Common/Label.cs @@ -1,8 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. -using FlaxEditor.Options; using System.ComponentModel; -using System.Linq; namespace FlaxEngine.GUI { @@ -235,7 +233,7 @@ namespace FlaxEngine.GUI } } - Render2D.DrawText(_font.GetFont(), Material, _text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale); + FallbackTextUtils.DrawText(_font.GetFont(), Material, _text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale); if (ClipText) Render2D.PopClip(); diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs b/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs index d3c2feefe..df8e0be7c 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using FlaxEngine.Utilities; -using System.Linq; namespace FlaxEngine.GUI { diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.cs b/Source/Engine/UI/GUI/Common/RichTextBox.cs index 514878b37..ab922d93d 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBox.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBox.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System.Collections.Generic; -using System.Linq; namespace FlaxEngine.GUI { diff --git a/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs b/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs index 438a7e3d8..a30d8e603 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs @@ -154,7 +154,7 @@ namespace FlaxEngine.GUI if (!font) break; height = font.Height / DpiScale; - return textBlock.Bounds.Location + font.GetCharPosition(_text, ref textBlock.Range, index - textBlock.Range.StartIndex); + return textBlock.Bounds.Location + FallbackTextUtils.GetCharPosition(font, _text, ref textBlock.Range, index - textBlock.Range.StartIndex); } } @@ -196,7 +196,7 @@ namespace FlaxEngine.GUI var font = textBlock.Style.Font.GetFont(); if (!font && textBlock.Range.Length > 0) break; - return font.HitTestText(_text, ref textBlock.Range, location - textBlock.Bounds.Location) + textBlock.Range.StartIndex; + return FallbackTextUtils.HitTestText(font, _text, ref textBlock.Range, location - textBlock.Bounds.Location) + textBlock.Range.StartIndex; } } @@ -288,8 +288,8 @@ namespace FlaxEngine.GUI // Selection if (hasSelection && textBlock.Style.BackgroundSelectedBrush != null && textBlock.Range.Intersect(ref selection)) { - var leftEdge = selection.StartIndex <= textBlock.Range.StartIndex ? textBlock.Bounds.UpperLeft : font.GetCharPosition(_text, selection.StartIndex); - var rightEdge = selection.EndIndex >= textBlock.Range.EndIndex ? textBlock.Bounds.UpperRight : font.GetCharPosition(_text, selection.EndIndex); + var leftEdge = selection.StartIndex <= textBlock.Range.StartIndex ? textBlock.Bounds.UpperLeft : FallbackTextUtils.GetCharPosition(font, _text, selection.StartIndex); + var rightEdge = selection.EndIndex >= textBlock.Range.EndIndex ? textBlock.Bounds.UpperRight : FallbackTextUtils.GetCharPosition(font, _text, selection.EndIndex); float height = font.Height / DpiScale; float alpha = Mathf.Min(1.0f, Mathf.Cos(_animateTime * BackgroundSelectedFlashSpeed) * 0.5f + 1.3f); alpha *= alpha; diff --git a/Source/Engine/UI/GUI/Common/TextBox.cs b/Source/Engine/UI/GUI/Common/TextBox.cs index ea7523f48..70a153faf 100644 --- a/Source/Engine/UI/GUI/Common/TextBox.cs +++ b/Source/Engine/UI/GUI/Common/TextBox.cs @@ -1,7 +1,5 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. -using System.Drawing; -using System.Linq; namespace FlaxEngine.GUI { @@ -120,7 +118,7 @@ namespace FlaxEngine.GUI } height = font.Height / DpiScale; - return font.GetCharPosition(_text, index, ref _layout); + return FallbackTextUtils.GetCharPosition(font, _text, index, ref _layout); } /// @@ -132,7 +130,7 @@ namespace FlaxEngine.GUI return 0; } - return font.HitTestText(_text, location, ref _layout); + return FallbackTextUtils.HitTestText(font, _text, location, ref _layout); } /// @@ -171,8 +169,8 @@ namespace FlaxEngine.GUI // Check if sth is selected to draw selection if (HasSelection) { - var leftEdge = font.GetCharPosition(_text, SelectionLeft, ref _layout); - var rightEdge = font.GetCharPosition(_text, SelectionRight, ref _layout); + var leftEdge = FallbackTextUtils.GetCharPosition(font, _text, SelectionLeft, ref _layout); + var rightEdge = FallbackTextUtils.GetCharPosition(font, _text, SelectionRight, ref _layout); float fontHeight = font.Height / DpiScale; // Draw selection background @@ -213,25 +211,11 @@ namespace FlaxEngine.GUI var color = TextColor; if (!enabled) color *= 0.6f; - if (Render2D.Fallbacks != null) - { - Render2D.DrawText(font, Render2D.Fallbacks, _text, color, ref _layout, TextMaterial); - } - else - { - Render2D.DrawText(font, _text, color, ref _layout, TextMaterial); - } + FallbackTextUtils.DrawText(font, _text, color, ref _layout, TextMaterial); } else if (!string.IsNullOrEmpty(_watermarkText) && !IsFocused) { - if (Render2D.Fallbacks != null) - { - Render2D.DrawText(font, Render2D.Fallbacks, _watermarkText, WatermarkTextColor, ref _layout, TextMaterial); - } - else - { - Render2D.DrawText(font, _watermarkText, WatermarkTextColor, ref _layout, TextMaterial); - } + FallbackTextUtils.DrawText(font, _watermarkText, WatermarkTextColor, ref _layout, TextMaterial); } // Caret diff --git a/Source/Engine/UI/GUI/Panels/DropPanel.cs b/Source/Engine/UI/GUI/Panels/DropPanel.cs index ed620ab98..123e0f034 100644 --- a/Source/Engine/UI/GUI/Panels/DropPanel.cs +++ b/Source/Engine/UI/GUI/Panels/DropPanel.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; -using System.Linq; namespace FlaxEngine.GUI { @@ -375,7 +374,7 @@ namespace FlaxEngine.GUI textColor *= 0.6f; } - Render2D.DrawText(HeaderTextFont.GetFont(), HeaderTextMaterial, HeaderText, textRect, textColor, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(HeaderTextFont.GetFont(), HeaderTextMaterial, HeaderText, textRect, textColor, TextAlignment.Near, TextAlignment.Center); if (!_isClosed && EnableContainmentLines) { diff --git a/Source/Engine/UI/GUI/Style.cs b/Source/Engine/UI/GUI/Style.cs index c4b51b085..f4fca13eb 100644 --- a/Source/Engine/UI/GUI/Style.cs +++ b/Source/Engine/UI/GUI/Style.cs @@ -1,6 +1,5 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. -using System.Linq; namespace FlaxEngine.GUI { diff --git a/Source/Engine/UI/GUI/Tooltip.cs b/Source/Engine/UI/GUI/Tooltip.cs index 7a1026e55..41d06b017 100644 --- a/Source/Engine/UI/GUI/Tooltip.cs +++ b/Source/Engine/UI/GUI/Tooltip.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; -using System.Linq; namespace FlaxEngine.GUI { @@ -235,7 +234,7 @@ namespace FlaxEngine.GUI Render2D.FillRectangle(new Rectangle(1.1f, 1.1f, Width - 2, Height - 2), style.Background); // Tooltip text - Render2D.DrawText( + FallbackTextUtils.DrawText( style.FontMedium, _currentText, GetClientArea(), From 3c5035d3e93540fa718ca97accd5ce94e7afa7a4 Mon Sep 17 00:00:00 2001 From: ExMatics HydrogenC <33123710+HydrogenC@users.noreply.github.com> Date: Fri, 1 Dec 2023 18:06:25 +0800 Subject: [PATCH 39/61] Fix merge problem --- Source/Editor/Windows/PluginsWindow.cs | 55 +++++++++++++++++++++----- 1 file changed, 45 insertions(+), 10 deletions(-) diff --git a/Source/Editor/Windows/PluginsWindow.cs b/Source/Editor/Windows/PluginsWindow.cs index 03beab2fa..f43fb0342 100644 --- a/Source/Editor/Windows/PluginsWindow.cs +++ b/Source/Editor/Windows/PluginsWindow.cs @@ -14,7 +14,6 @@ using FlaxEditor.GUI.Tabs; using FlaxEngine; using FlaxEngine.GUI; using FlaxEngine.Json; -using FlaxEngine.Utilities; namespace FlaxEditor.Windows { @@ -122,7 +121,7 @@ namespace FlaxEditor.Windows url = desc.RepositoryUrl; versionLabel.Font.Font.WaitForLoaded(); var font = versionLabel.Font.GetFont(); - var authorWidth = FallbackTextUtils.MeasureText(font, desc.Author).X + 8; + var authorWidth = font.MeasureText(desc.Author).X + 8; var authorLabel = new ClickableLabel { HorizontalAlignment = TextAlignment.Far, @@ -392,6 +391,7 @@ namespace FlaxEditor.Windows } Editor.Log("Plugin project has been cloned."); + try { // Start git submodule clone @@ -412,24 +412,28 @@ namespace FlaxEditor.Windows } // Find project config file. Could be different then what the user named the folder. - var files = Directory.GetFiles(clonePath); string pluginProjectName = ""; - foreach (var file in files) + foreach (var file in Directory.GetFiles(clonePath)) { if (file.Contains(".flaxproj", StringComparison.OrdinalIgnoreCase)) { pluginProjectName = Path.GetFileNameWithoutExtension(file); - Debug.Log(pluginProjectName); + break; } } - if (string.IsNullOrEmpty(pluginProjectName)) - Editor.LogError("Failed to find plugin project file to add to Project config. Please add manually."); - else { - await AddReferenceToProject(pluginName, pluginProjectName); - MessageBox.Show($"{pluginName} has been successfully cloned. Restart editor for changes to take effect.", "Plugin Project Created", MessageBoxButtons.OK); + Editor.LogError("Failed to find plugin project file to add to Project config. Please add manually."); + return; } + + await AddModuleReferencesInGameModule(clonePath); + await AddReferenceToProject(pluginName, pluginProjectName); + + if (Editor.Options.Options.SourceCode.AutoGenerateScriptsProjectFiles) + Editor.ProgressReporting.GenerateScriptsProjectFiles.RunAsync(); + + MessageBox.Show($"{pluginName} has been successfully cloned. Restart editor for changes to take effect.", "Plugin Project Created", MessageBoxButtons.OK); } private void OnAddButtonClicked() @@ -749,6 +753,37 @@ namespace FlaxEditor.Windows MessageBox.Show($"{pluginName} has been successfully created. Restart editor for changes to take effect.", "Plugin Project Created", MessageBoxButtons.OK); } + private async Task AddModuleReferencesInGameModule(string pluginFolderPath) + { + // Common game build script location + var gameScript = Path.Combine(Globals.ProjectFolder, "Source/Game/Game.Build.cs"); + if (File.Exists(gameScript)) + { + var gameScriptContents = await File.ReadAllTextAsync(gameScript); + var insertLocation = gameScriptContents.IndexOf("base.Setup(options);", StringComparison.Ordinal); + if (insertLocation != -1) + { + insertLocation += 20; + var modifiedAny = false; + + // Find all code modules in a plugin to auto-reference them in game build script + foreach (var subDir in Directory.GetDirectories(Path.Combine(pluginFolderPath, "Source"))) + { + var pluginModuleName = Path.GetFileName(subDir); + var pluginModuleScriptPath = Path.Combine(subDir, pluginModuleName + ".Build.cs"); + if (File.Exists(pluginModuleScriptPath)) + { + gameScriptContents = gameScriptContents.Insert(insertLocation, $"\n options.PublicDependencies.Add(\"{pluginModuleName}\");"); + modifiedAny = true; + } + } + + if (modifiedAny) + await File.WriteAllTextAsync(gameScript, gameScriptContents, Encoding.UTF8); + } + } + } + private async Task AddReferenceToProject(string pluginFolderName, string pluginName) { // Project flax config file From 95f5e31e4852ab6e2a25177f3f587ba0840b03a4 Mon Sep 17 00:00:00 2001 From: ExMatics HydrogenC <33123710+HydrogenC@users.noreply.github.com> Date: Fri, 1 Dec 2023 19:38:15 +0800 Subject: [PATCH 40/61] Fix textbox height Fix build error under non-windows platforms --- Source/Editor/GUI/Row.cs | 5 ++- Source/Engine/Render2D/FallbackFonts.h | 35 +++++++++++++++--- Source/Engine/Render2D/FallbackTextUtils.cs | 37 +++++++++++++++++++ Source/Engine/Render2D/Font.h | 7 ++-- Source/Engine/Render2D/FontAsset.cpp | 7 ++++ Source/Engine/Render2D/FontAsset.h | 2 +- .../Engine/UI/GUI/Common/RichTextBoxBase.cs | 1 + Source/Engine/UI/GUI/Common/TextBox.cs | 4 +- 8 files changed, 85 insertions(+), 13 deletions(-) diff --git a/Source/Editor/GUI/Row.cs b/Source/Editor/GUI/Row.cs index c7be48626..9868ab456 100644 --- a/Source/Editor/GUI/Row.cs +++ b/Source/Editor/GUI/Row.cs @@ -38,8 +38,9 @@ namespace FlaxEditor.GUI { Depth = -1; - if (Height < Style.Current.FontMedium.Height) - Height = Style.Current.FontMedium.Height + 4; + var mediumHeight = FallbackTextUtils.GetMaxHeight(Style.Current.FontMedium); + if (Height < mediumHeight) + Height = mediumHeight + 4; } /// diff --git a/Source/Engine/Render2D/FallbackFonts.h b/Source/Engine/Render2D/FallbackFonts.h index 5861354fc..040ca8087 100644 --- a/Source/Engine/Render2D/FallbackFonts.h +++ b/Source/Engine/Render2D/FallbackFonts.h @@ -9,36 +9,55 @@ struct TextRange; class Font; class FontAsset; +/// +/// Defines a list of fonts that can be used as a fallback, ordered by priority. +/// API_CLASS(Sealed, NoSpawn) class FLAXENGINE_API FallbackFonts : public ManagedScriptingObject { DECLARE_SCRIPTING_TYPE_NO_SPAWN(FallbackFonts); private: - /// - /// The list of fallback fonts, ordered by priority. - /// The first element is reserved for the primary font, fallback fonts starts from the second element. - /// Array _fontAssets; + // Cache fallback fonts of various sizes Dictionary*> _cache; public: + /// + /// Initializes a new instance of the class. + /// + /// The fallback font assets. FallbackFonts(const Array& fonts); + /// + /// Initializes a new instance of the class, exposed for C#. + /// + /// The fallback font assets. + /// The new instance. API_FUNCTION() FORCE_INLINE static FallbackFonts* Create(const Array& fonts) { return New(fonts); } + /// + /// Get the parent assets of fallback fonts. + /// + /// The font assets. API_PROPERTY() FORCE_INLINE Array& GetFonts() { return _fontAssets; } + /// + /// Set the fallback fonts. + /// + /// The parent assets of the new fonts. API_PROPERTY() FORCE_INLINE void SetFonts(const Array& val) { _fontAssets = val; } /// - /// Combine the primary fonts with the fallback fonts to get a font list + /// Gets the fallback fonts with the given size. /// + /// The size. + /// The generated fonts. API_FUNCTION() FORCE_INLINE Array& GetFontList(float size) { Array* result; if (_cache.TryGet(size, result)) { @@ -61,6 +80,8 @@ public: /// Gets the index of the fallback font that should be used to render the char /// /// The char. + /// The primary font. + /// The number to return if none of the fonts can render. /// -1 if char can be rendered with primary font, index if it matches a fallback font. API_FUNCTION() FORCE_INLINE int32 GetCharFallbackIndex(Char c, Font* primaryFont = nullptr, int32 missing = -1) { if (primaryFont && primaryFont->GetAsset()->ContainsChar(c)) { @@ -81,6 +102,10 @@ public: return missing; } + /// + /// Checks if every font is properly loaded. + /// + /// True if every font asset is non-null, otherwise false. API_FUNCTION() FORCE_INLINE bool Verify() { for (int32 i = 0; i < _fontAssets.Count(); i++) { diff --git a/Source/Engine/Render2D/FallbackTextUtils.cs b/Source/Engine/Render2D/FallbackTextUtils.cs index b6d8770e2..486a354bf 100644 --- a/Source/Engine/Render2D/FallbackTextUtils.cs +++ b/Source/Engine/Render2D/FallbackTextUtils.cs @@ -227,5 +227,42 @@ namespace FlaxEngine return font.GetCharPosition(text, ref textRange, index, ref layout); } } + + /// + /// Gets the max font height among the font and all fallback fonts of the same size. + /// + /// The primary font to use. + /// The fallback fonts. + /// The max height. + public static float GetMaxHeight(Font font, FallbackFonts fallbacks) + { + float height = font.Height; + + var fallbackFonts = fallbacks.GetFontList(font.Size); + foreach (var item in fallbackFonts) + { + height = Mathf.Max(height, item.Height); + } + + return height; + } + + /// + /// Gets the max font height among the font and all fallback fonts of the same size. + /// + /// The primary font to use. + /// Whether to enable fallback fonts, uses if true. + /// The max height. + public static float GetMaxHeight(Font font, bool useFallback = true) + { + if(Fallbacks != null && useFallback) + { + return GetMaxHeight(font, Fallbacks); + } + else + { + return font.Height; + } + } } } diff --git a/Source/Engine/Render2D/Font.h b/Source/Engine/Render2D/Font.h index d932888fb..110a8fe70 100644 --- a/Source/Engine/Render2D/Font.h +++ b/Source/Engine/Render2D/Font.h @@ -122,7 +122,8 @@ struct TIsPODType }; /// -/// The font block info generated during text processing. +/// The font block info generated during text processing. +/// A block means a range of text that belongs to the same line and can be rendered with the same font. /// API_STRUCT(NoDefault) struct FontBlockCache { @@ -134,7 +135,7 @@ API_STRUCT(NoDefault) struct FontBlockCache API_FIELD() Float2 Location; /// - /// The height of the current block + /// The size of the current block /// API_FIELD() Float2 Size; @@ -183,7 +184,7 @@ API_STRUCT(NoDefault) struct BlockedTextLineCache API_FIELD() float MaxAscender; /// - /// The index of the font to render with + /// The blocks that belongs to this line /// API_FIELD() Array Blocks; }; diff --git a/Source/Engine/Render2D/FontAsset.cpp b/Source/Engine/Render2D/FontAsset.cpp index 1e94b19b1..ea629367f 100644 --- a/Source/Engine/Render2D/FontAsset.cpp +++ b/Source/Engine/Render2D/FontAsset.cpp @@ -199,6 +199,13 @@ bool FontAsset::Save(const StringView& path) #endif + +/// +/// Check if the font contains the glyph of a char +/// +/// The char to test. +/// True if the font contains the glyph of the char, otherwise false. + bool FontAsset::ContainsChar(Char c) const { return FT_Get_Char_Index(GetFTFace(), c) > 0; } diff --git a/Source/Engine/Render2D/FontAsset.h b/Source/Engine/Render2D/FontAsset.h index a773a8ad6..f3c909911 100644 --- a/Source/Engine/Render2D/FontAsset.h +++ b/Source/Engine/Render2D/FontAsset.h @@ -179,7 +179,7 @@ public: /// /// The char to test. /// True if the font contains the glyph of the char, otherwise false. - API_FUNCTION() FORCE_INLINE bool ContainsChar(Char c) const; + API_FUNCTION() bool ContainsChar(Char c) const; /// /// Invalidates all cached dynamic font atlases using this font. Can be used to reload font characters after changing font asset options. diff --git a/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs b/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs index a30d8e603..8d7858af0 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs @@ -316,6 +316,7 @@ namespace FlaxEngine.GUI color = textBlock.Style.ShadowColor; if (!enabled) color *= 0.6f; + // We don't need font fallbacks for rich text since the font is user-selected Render2D.DrawText(font, _text, ref textBlock.Range, color, textBlock.Bounds.Location + textBlock.Style.ShadowOffset, textBlock.Style.CustomMaterial); } diff --git a/Source/Engine/UI/GUI/Common/TextBox.cs b/Source/Engine/UI/GUI/Common/TextBox.cs index 70a153faf..b1861df56 100644 --- a/Source/Engine/UI/GUI/Common/TextBox.cs +++ b/Source/Engine/UI/GUI/Common/TextBox.cs @@ -117,7 +117,7 @@ namespace FlaxEngine.GUI return Float2.Zero; } - height = font.Height / DpiScale; + height = FallbackTextUtils.GetMaxHeight(font) / DpiScale; return FallbackTextUtils.GetCharPosition(font, _text, index, ref _layout); } @@ -171,7 +171,7 @@ namespace FlaxEngine.GUI { var leftEdge = FallbackTextUtils.GetCharPosition(font, _text, SelectionLeft, ref _layout); var rightEdge = FallbackTextUtils.GetCharPosition(font, _text, SelectionRight, ref _layout); - float fontHeight = font.Height / DpiScale; + float fontHeight = FallbackTextUtils.GetMaxHeight(font) / DpiScale; // Draw selection background float alpha = Mathf.Min(1.0f, Mathf.Cos(_animateTime * BackgroundSelectedFlashSpeed) * 0.5f + 1.3f); From 29eb3954c5bc0da8c7ce0b2e997cc797c2b1754c Mon Sep 17 00:00:00 2001 From: ExMatics HydrogenC <33123710+HydrogenC@users.noreply.github.com> Date: Sat, 2 Dec 2023 17:48:51 +0800 Subject: [PATCH 41/61] Create global settings for font fallback --- Source/Editor/Windows/SplashScreen.cpp | 6 +- Source/Engine/Core/Config/GameSettings.cs | 2 +- Source/Engine/Core/Config/GameSettings.h | 11 +++ .../Platform/Win32/IncludeWindowsHeaders.h | 2 +- Source/Engine/Render2D/FallbackFonts.cpp | 2 +- Source/Engine/Render2D/FallbackFonts.h | 14 ++-- Source/Engine/Render2D/Font.cpp | 8 +- Source/Engine/Render2D/Font.h | 36 ++++----- Source/Engine/Render2D/Render2D.cpp | 24 +++--- Source/Engine/Render2D/Render2D.h | 81 +++++++++++++++---- 10 files changed, 122 insertions(+), 64 deletions(-) diff --git a/Source/Editor/Windows/SplashScreen.cpp b/Source/Editor/Windows/SplashScreen.cpp index 92894b537..cbb51ae3c 100644 --- a/Source/Editor/Windows/SplashScreen.cpp +++ b/Source/Editor/Windows/SplashScreen.cpp @@ -264,7 +264,7 @@ void SplashScreen::OnDraw() layout.HorizontalAlignment = TextAlignment::Near; layout.VerticalAlignment = TextAlignment::Near; layout.Scale = Math::Min((width - 20 * s) / titleLength.X, 1.0f); - Render2D::DrawText(_titleFont, GetTitle(), Color::White, layout); + Render2D::DrawTextInternal(_titleFont, GetTitle(), Color::White, layout); // Subtitle String subtitle(_quote); @@ -279,14 +279,14 @@ void SplashScreen::OnDraw() layout.Scale = 1.0f; layout.HorizontalAlignment = TextAlignment::Far; layout.VerticalAlignment = TextAlignment::Far; - Render2D::DrawText(_subtitleFont, subtitle, Color::FromRGB(0x8C8C8C), layout); + Render2D::DrawTextInternal(_subtitleFont, subtitle, Color::FromRGB(0x8C8C8C), layout); // Additional info const float infoMargin = 6 * s; layout.Bounds = Rectangle(infoMargin, lightBarHeight + infoMargin, width - (2 * infoMargin), height - lightBarHeight - (2 * infoMargin)); layout.HorizontalAlignment = TextAlignment::Near; layout.VerticalAlignment = TextAlignment::Center; - Render2D::DrawText(_subtitleFont, _infoText, Color::FromRGB(0xFFFFFF) * 0.9f, layout); + Render2D::DrawTextInternal(_subtitleFont, _infoText, Color::FromRGB(0xFFFFFF) * 0.9f, layout); } bool SplashScreen::HasLoadedFonts() const diff --git a/Source/Engine/Core/Config/GameSettings.cs b/Source/Engine/Core/Config/GameSettings.cs index 40d9f5dbc..76c968968 100644 --- a/Source/Engine/Core/Config/GameSettings.cs +++ b/Source/Engine/Core/Config/GameSettings.cs @@ -119,7 +119,7 @@ namespace FlaxEditor.Content.Settings /// /// The custom settings to use with a game. Can be specified by the user to define game-specific options and be used by the external plugins (used as key-value pair). /// - [EditorOrder(1100), EditorDisplay("Other Settings"), Tooltip("The custom settings to use with a game. Can be specified by the user to define game-specific options and be used by the external plugins (used as key-value pair).")] + [EditorOrder(1500), EditorDisplay("Other Settings"), Tooltip("The custom settings to use with a game. Can be specified by the user to define game-specific options and be used by the external plugins (used as key-value pair).")] public Dictionary CustomSettings; #if FLAX_EDITOR || PLATFORM_WINDOWS diff --git a/Source/Engine/Core/Config/GameSettings.h b/Source/Engine/Core/Config/GameSettings.h index 54ad29a7b..6813f3ee2 100644 --- a/Source/Engine/Core/Config/GameSettings.h +++ b/Source/Engine/Core/Config/GameSettings.h @@ -7,6 +7,8 @@ #include "Engine/Core/Types/String.h" #include "Engine/Core/Collections/Dictionary.h" +class FontFallbackList; + /// /// The main game engine configuration service. Loads and applies game configuration. /// @@ -33,6 +35,15 @@ public: API_FIELD(Attributes="EditorOrder(15), EditorDisplay(\"General\")") String CopyrightNotice; + /// + /// The copyright note used for content signing (eg. source code header). + /// + API_FIELD(Attributes = "EditorOrder(1200), EditorDisplay(\"Other Settings\")") + bool EnableFontFallback; + + API_FIELD(Attributes = "EditorOrder(1205), EditorDisplay(\"Other Settings\")") + FontFallbackList* FontFallbacks; + /// /// The default application icon. /// diff --git a/Source/Engine/Platform/Win32/IncludeWindowsHeaders.h b/Source/Engine/Platform/Win32/IncludeWindowsHeaders.h index 3f2b8d36e..66ed97a20 100644 --- a/Source/Engine/Platform/Win32/IncludeWindowsHeaders.h +++ b/Source/Engine/Platform/Win32/IncludeWindowsHeaders.h @@ -57,7 +57,7 @@ #undef CreateWindow #undef CreateProcess #undef SetWindowText -#undef DrawText +#undef DrawTextInternal #undef CreateFont #undef IsMinimized #undef IsMaximized diff --git a/Source/Engine/Render2D/FallbackFonts.cpp b/Source/Engine/Render2D/FallbackFonts.cpp index b79a0db4e..7f81a42bb 100644 --- a/Source/Engine/Render2D/FallbackFonts.cpp +++ b/Source/Engine/Render2D/FallbackFonts.cpp @@ -2,7 +2,7 @@ #include "FontManager.h" #include "Engine/Core/Math/Math.h" -FallbackFonts::FallbackFonts(const Array& fonts) +FontFallbackList::FontFallbackList(const Array& fonts) : ManagedScriptingObject(SpawnParams(Guid::New(), Font::TypeInitializer)), _fontAssets(fonts) { diff --git a/Source/Engine/Render2D/FallbackFonts.h b/Source/Engine/Render2D/FallbackFonts.h index 040ca8087..fb247e4e2 100644 --- a/Source/Engine/Render2D/FallbackFonts.h +++ b/Source/Engine/Render2D/FallbackFonts.h @@ -12,9 +12,9 @@ class FontAsset; /// /// Defines a list of fonts that can be used as a fallback, ordered by priority. /// -API_CLASS(Sealed, NoSpawn) class FLAXENGINE_API FallbackFonts : public ManagedScriptingObject +API_CLASS(Sealed, NoSpawn) class FLAXENGINE_API FontFallbackList : public ManagedScriptingObject { - DECLARE_SCRIPTING_TYPE_NO_SPAWN(FallbackFonts); + DECLARE_SCRIPTING_TYPE_NO_SPAWN(FontFallbackList); private: Array _fontAssets; @@ -23,18 +23,18 @@ private: public: /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The fallback font assets. - FallbackFonts(const Array& fonts); + FontFallbackList(const Array& fonts); /// - /// Initializes a new instance of the class, exposed for C#. + /// Initializes a new instance of the class, exposed for C#. /// /// The fallback font assets. /// The new instance. - API_FUNCTION() FORCE_INLINE static FallbackFonts* Create(const Array& fonts) { - return New(fonts); + API_FUNCTION() FORCE_INLINE static FontFallbackList* Create(const Array& fonts) { + return New(fonts); } /// diff --git a/Source/Engine/Render2D/Font.cpp b/Source/Engine/Render2D/Font.cpp index 4cf1f73a2..5d029ca5e 100644 --- a/Source/Engine/Render2D/Font.cpp +++ b/Source/Engine/Render2D/Font.cpp @@ -293,7 +293,7 @@ void Font::ProcessText(const StringView& text, Array& outputLines } } -void Font::ProcessText(FallbackFonts* fallbacks, const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout) +void Font::ProcessText(FontFallbackList* fallbacks, const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout) { const Array& fallbackFonts = fallbacks->GetFontList(GetSize()); float cursorX = 0; @@ -569,7 +569,7 @@ Float2 Font::MeasureText(const StringView& text, const TextLayoutOptions& layout return max; } -Float2 Font::MeasureText(FallbackFonts* fallbacks, const StringView& text, const TextLayoutOptions& layout) +Float2 Font::MeasureText(FontFallbackList* fallbacks, const StringView& text, const TextLayoutOptions& layout) { // Check if there is no need to do anything if (text.IsEmpty()) @@ -664,7 +664,7 @@ int32 Font::HitTestText(const StringView& text, const Float2& location, const Te return smallestIndex; } -int32 Font::HitTestText(FallbackFonts* fallbacks, const StringView& text, const Float2& location, const TextLayoutOptions& layout) +int32 Font::HitTestText(FontFallbackList* fallbacks, const StringView& text, const Float2& location, const TextLayoutOptions& layout) { // Check if there is no need to do anything if (text.Length() <= 0) @@ -818,7 +818,7 @@ Float2 Font::GetCharPosition(const StringView& text, int32 index, const TextLayo return rootOffset + Float2(lines.Last().Location.X + lines.Last().Size.X, static_cast((lines.Count() - 1) * baseLinesDistance)); } -Float2 Font::GetCharPosition(FallbackFonts* fallbacks, const StringView& text, int32 index, const TextLayoutOptions& layout) +Float2 Font::GetCharPosition(FontFallbackList* fallbacks, const StringView& text, int32 index, const TextLayoutOptions& layout) { // Check if there is no need to do anything if (text.IsEmpty()) diff --git a/Source/Engine/Render2D/Font.h b/Source/Engine/Render2D/Font.h index 110a8fe70..6bb2849e2 100644 --- a/Source/Engine/Render2D/Font.h +++ b/Source/Engine/Render2D/Font.h @@ -10,7 +10,7 @@ #include "TextLayoutOptions.h" class FontAsset; -class FallbackFonts; +class FontFallbackList; struct FontTextureAtlasSlot; struct BlockedTextLineCache; @@ -463,7 +463,7 @@ public: /// The input text. /// The layout properties. /// The output lines list. - void ProcessText(FallbackFonts* fallbacks, const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout); + void ProcessText(FontFallbackList* fallbacks, const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout); /// /// Processes text to get cached lines for rendering. @@ -471,7 +471,7 @@ public: /// The input text. /// The layout properties. /// The output lines list. - API_FUNCTION() Array ProcessText(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout) + API_FUNCTION() Array ProcessText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout) { Array lines; ProcessText(fallbacks, text, lines, layout); @@ -485,7 +485,7 @@ public: /// The input text range (substring range of the input text parameter). /// The layout properties. /// The output lines list. - API_FUNCTION() Array ProcessText(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) + API_FUNCTION() Array ProcessText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) { Array lines; ProcessText(fallbacks, textRange.Substring(text), lines, layout); @@ -497,7 +497,7 @@ public: /// /// The input text. /// The output lines list. - API_FUNCTION() FORCE_INLINE Array ProcessText(FallbackFonts* fallbacks, const StringView& text) + API_FUNCTION() FORCE_INLINE Array ProcessText(FontFallbackList* fallbacks, const StringView& text) { return ProcessText(fallbacks, text, TextLayoutOptions()); } @@ -508,7 +508,7 @@ public: /// The input text. /// The input text range (substring range of the input text parameter). /// The output lines list. - API_FUNCTION() FORCE_INLINE Array ProcessText(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange) + API_FUNCTION() FORCE_INLINE Array ProcessText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange) { return ProcessText(fallbacks, textRange.Substring(text), TextLayoutOptions()); } @@ -560,7 +560,7 @@ public: /// The input text to test. /// The layout properties. /// The minimum size for that text and fot to render properly. - API_FUNCTION() Float2 MeasureText(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout); + API_FUNCTION() Float2 MeasureText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout); /// /// Measures minimum size of the rectangle that will be needed to draw given text. @@ -569,7 +569,7 @@ public: /// The input text range (substring range of the input text parameter). /// The layout properties. /// The minimum size for that text and fot to render properly. - API_FUNCTION() Float2 MeasureText(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) + API_FUNCTION() Float2 MeasureText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) { return MeasureText(fallbacks, textRange.Substring(text), layout); } @@ -579,7 +579,7 @@ public: /// . /// The input text to test. /// The minimum size for that text and fot to render properly. - API_FUNCTION() FORCE_INLINE Float2 MeasureText(FallbackFonts* fallbacks, const StringView& text) + API_FUNCTION() FORCE_INLINE Float2 MeasureText(FontFallbackList* fallbacks, const StringView& text) { return MeasureText(fallbacks, text, TextLayoutOptions()); } @@ -590,7 +590,7 @@ public: /// The input text to test. /// The input text range (substring range of the input text parameter). /// The minimum size for that text and fot to render properly. - API_FUNCTION() FORCE_INLINE Float2 MeasureText(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange) + API_FUNCTION() FORCE_INLINE Float2 MeasureText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange) { return MeasureText(fallbacks, textRange.Substring(text), TextLayoutOptions()); } @@ -647,7 +647,7 @@ public: /// The input location to test. /// The text layout properties. /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() int32 HitTestText(FallbackFonts* fallbacks, const StringView& text, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout); + API_FUNCTION() int32 HitTestText(FontFallbackList* fallbacks, const StringView& text, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout); /// /// Calculates hit character index at given location. @@ -657,7 +657,7 @@ public: /// The input location to test. /// The text layout properties. /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() int32 HitTestText(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout) + API_FUNCTION() int32 HitTestText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout) { return HitTestText(fallbacks, textRange.Substring(text), location, layout); } @@ -668,7 +668,7 @@ public: /// The input text to test. /// The input location to test. /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() FORCE_INLINE int32 HitTestText(FallbackFonts* fallbacks, const StringView& text, const Float2& location) + API_FUNCTION() FORCE_INLINE int32 HitTestText(FontFallbackList* fallbacks, const StringView& text, const Float2& location) { return HitTestText(fallbacks, text, location, TextLayoutOptions()); } @@ -680,7 +680,7 @@ public: /// The input text range (substring range of the input text parameter). /// The input location to test. /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() FORCE_INLINE int32 HitTestText(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location) + API_FUNCTION() FORCE_INLINE int32 HitTestText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location) { return HitTestText(fallbacks, textRange.Substring(text), location, TextLayoutOptions()); } @@ -737,7 +737,7 @@ public: /// The text position to get coordinates of. /// The text layout properties. /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() Float2 GetCharPosition(FallbackFonts* fallbacks, const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout); + API_FUNCTION() Float2 GetCharPosition(FontFallbackList* fallbacks, const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout); /// /// Calculates character position for given text and character index. @@ -747,7 +747,7 @@ public: /// The text position to get coordinates of. /// The text layout properties. /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() Float2 GetCharPosition(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout) + API_FUNCTION() Float2 GetCharPosition(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout) { return GetCharPosition(fallbacks, textRange.Substring(text), index, layout); } @@ -758,7 +758,7 @@ public: /// The input text to test. /// The text position to get coordinates of. /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(FallbackFonts* fallbacks, const StringView& text, int32 index) + API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(FontFallbackList* fallbacks, const StringView& text, int32 index) { return GetCharPosition(fallbacks, text, index, TextLayoutOptions()); } @@ -770,7 +770,7 @@ public: /// The input text range (substring range of the input text parameter). /// The text position to get coordinates of. /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index) + API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index) { return GetCharPosition(fallbacks, textRange.Substring(text), index, TextLayoutOptions()); } diff --git a/Source/Engine/Render2D/Render2D.cpp b/Source/Engine/Render2D/Render2D.cpp index 4e0cacb44..351bb3700 100644 --- a/Source/Engine/Render2D/Render2D.cpp +++ b/Source/Engine/Render2D/Render2D.cpp @@ -1144,7 +1144,7 @@ void DrawBatch(int32 startIndex, int32 count) Context->DrawIndexed(countIb, 0, d.StartIB); } -void Render2D::DrawText(Font* font, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial) +void Render2D::DrawTextInternal(Font* font, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial) { RENDER2D_CHECK_RENDERING_STATE; @@ -1252,12 +1252,12 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color, } } -void Render2D::DrawText(Font* font, const StringView& text, const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial) +void Render2D::DrawTextInternal(Font* font, const StringView& text, const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial) { - DrawText(font, textRange.Substring(text), color, location, customMaterial); + DrawTextInternal(font, textRange.Substring(text), color, location, customMaterial); } -void Render2D::DrawText(Font* font, const StringView& text, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) +void Render2D::DrawTextInternal(Font* font, const StringView& text, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) { RENDER2D_CHECK_RENDERING_STATE; @@ -1365,12 +1365,12 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color, } } -void Render2D::DrawText(Font* font, const StringView& text, const TextRange& textRange, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) +void Render2D::DrawTextInternal(Font* font, const StringView& text, const TextRange& textRange, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) { - DrawText(font, textRange.Substring(text), color, layout, customMaterial); + DrawTextInternal(font, textRange.Substring(text), color, layout, customMaterial); } -void Render2D::DrawText(Font* font, FallbackFonts* fallbacks, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial) +void Render2D::DrawTextInternal(Font* font, FontFallbackList* fallbacks, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial) { RENDER2D_CHECK_RENDERING_STATE; @@ -1549,12 +1549,12 @@ void Render2D::DrawText(Font* font, FallbackFonts* fallbacks, const StringView& } } -void Render2D::DrawText(Font* font, FallbackFonts* fallbacks, const StringView& text, const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial) +void Render2D::DrawTextInternal(Font* font, FontFallbackList* fallbacks, const StringView& text, const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial) { - DrawText(font, fallbacks, textRange.Substring(text), color, location, customMaterial); + DrawTextInternal(font, fallbacks, textRange.Substring(text), color, location, customMaterial); } -void Render2D::DrawText(Font* font, FallbackFonts* fallbacks, const StringView& text, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) +void Render2D::DrawTextInternal(Font* font, FontFallbackList* fallbacks, const StringView& text, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) { RENDER2D_CHECK_RENDERING_STATE; @@ -1673,9 +1673,9 @@ void Render2D::DrawText(Font* font, FallbackFonts* fallbacks, const StringView& } } -void Render2D::DrawText(Font* font, FallbackFonts* fallbacks, const StringView& text, const TextRange& textRange, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) +void Render2D::DrawTextInternal(Font* font, FontFallbackList* fallbacks, const StringView& text, const TextRange& textRange, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) { - DrawText(font, fallbacks, textRange.Substring(text), color, layout, customMaterial); + DrawTextInternal(font, fallbacks, textRange.Substring(text), color, layout, customMaterial); } FORCE_INLINE bool NeedAlphaWithTint(const Color& color) diff --git a/Source/Engine/Render2D/Render2D.h b/Source/Engine/Render2D/Render2D.h index c99d38104..fa59b1d79 100644 --- a/Source/Engine/Render2D/Render2D.h +++ b/Source/Engine/Render2D/Render2D.h @@ -15,7 +15,7 @@ struct Matrix3x3; struct Viewport; struct TextRange; class Font; -class FallbackFonts; +class FontFallbackList; class GPUPipelineState; class GPUTexture; class GPUTextureView; @@ -54,6 +54,9 @@ API_CLASS(Static) class FLAXENGINE_API Render2D }; public: + API_FIELD() static bool EnableFontFallback; + API_FIELD() static FontFallbackList* FallbackFonts; + /// /// Checks if interface is during rendering phrase (Draw calls may be performed without failing). /// @@ -175,17 +178,17 @@ public: public: /// - /// Draws a text. + /// Draws a text, with font fallbacking disabled. /// /// The font to use. /// The text to render. /// The text color. /// The text location. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawText(Font* font, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawTextInternal(Font* font, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); /// - /// Draws a text. + /// Draws a text, with font fallbacking disabled. /// /// The font to use. /// The text to render. @@ -193,20 +196,20 @@ public: /// The text color. /// The text location. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawText(Font* font, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawTextInternal(Font* font, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); /// - /// Draws a text with formatting. + /// Draws a text with formatting, with font fallbacking disabled. /// /// The font to use. /// The text to render. /// The text color. /// The text layout properties. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawText(Font* font, const StringView& text, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawTextInternal(Font* font, const StringView& text, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); /// - /// Draws a text with formatting. + /// Draws a text with formatting, with font fallbacking disabled. /// /// The font to use. /// The text to render. @@ -214,10 +217,10 @@ public: /// The text color. /// The text layout properties. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawText(Font* font, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawTextInternal(Font* font, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); /// - /// Draws a text. + /// Draws a text, using custom fallback options. /// /// The fonts to use, ordered by priority. /// The text to render. @@ -225,20 +228,20 @@ public: /// The text color. /// The text location. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawText(Font* font, FallbackFonts* fallbacks, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawTextInternal(Font* font, FontFallbackList* fallbacks, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); /// - /// Draws a text with formatting. + /// Draws a text with formatting, using custom fallback options. /// /// The fonts to use, ordered by priority. /// The text to render. /// The text color. /// The text layout properties. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawText(Font* font, FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawTextInternal(Font* font, FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); /// - /// Draws a text with formatting. + /// Draws a text with formatting, using custom fallback options. /// /// The fonts to use, ordered by priority. /// The text to render. @@ -246,10 +249,10 @@ public: /// The text color. /// The text layout properties. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawText(Font* font, FallbackFonts* fallbacks, const StringView& text, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawTextInternal(Font* font, FontFallbackList* fallbacks, const StringView& text, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); /// - /// Draws a text with formatting. + /// Draws a text with formatting, using custom fallback options. /// /// The fonts to use, ordered by priority. /// The text to render. @@ -257,7 +260,51 @@ public: /// The text color. /// The text layout properties. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawText(Font* font, FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawTextInternal(Font* font, FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); + + /// + /// Draws a text, follows the fallback settings defined in . + /// + /// The font to use. + /// The text to render. + /// The text color. + /// The text location. + /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. + API_FUNCTION() FORCE_INLINE static void DrawText(Font* font, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr) { + if (EnableFontFallback && FallbackFonts) { + DrawTextInternal(font, FallbackFonts, text, color, location, customMaterial); + } + else { + DrawTextInternal(font, text, color, location, customMaterial); + } + } + + API_FUNCTION() FORCE_INLINE static void DrawText(Font* font, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr) { + if (EnableFontFallback && FallbackFonts) { + DrawTextInternal(font, FallbackFonts, text, textRange, color, location, customMaterial); + } + else { + DrawTextInternal(font, text, textRange, color, location, customMaterial); + } + } + + API_FUNCTION() FORCE_INLINE static void DrawText(Font* font, const StringView& text, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr) { + if (EnableFontFallback && FallbackFonts) { + DrawTextInternal(font, FallbackFonts, text, color, layout, customMaterial); + } + else { + DrawTextInternal(font, text, color, layout, customMaterial); + } + } + + API_FUNCTION() FORCE_INLINE static void DrawText(Font* font, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr) { + if (EnableFontFallback && FallbackFonts) { + DrawTextInternal(font, FallbackFonts, text, textRange, color, layout, customMaterial); + } + else { + DrawTextInternal(font, text, textRange, color, layout, customMaterial); + } + } /// /// Fills a rectangle area. From 2f019d4264fc7dfd57777297a8b592ab4064bf86 Mon Sep 17 00:00:00 2001 From: ExMatics HydrogenC <33123710+HydrogenC@users.noreply.github.com> Date: Sat, 2 Dec 2023 17:51:32 +0800 Subject: [PATCH 42/61] Fix unintended unname --- Source/Engine/Platform/Win32/IncludeWindowsHeaders.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Platform/Win32/IncludeWindowsHeaders.h b/Source/Engine/Platform/Win32/IncludeWindowsHeaders.h index 66ed97a20..3f2b8d36e 100644 --- a/Source/Engine/Platform/Win32/IncludeWindowsHeaders.h +++ b/Source/Engine/Platform/Win32/IncludeWindowsHeaders.h @@ -57,7 +57,7 @@ #undef CreateWindow #undef CreateProcess #undef SetWindowText -#undef DrawTextInternal +#undef DrawText #undef CreateFont #undef IsMinimized #undef IsMaximized From 23b71e7d3e825a3cb7d44300aa4e65c4fb684405 Mon Sep 17 00:00:00 2001 From: ExMatics HydrogenC <33123710+HydrogenC@users.noreply.github.com> Date: Sun, 3 Dec 2023 15:11:47 +0800 Subject: [PATCH 43/61] Make font fallbacking the default option --- Source/Editor/Content/GUI/ContentView.cs | 2 +- Source/Editor/Content/Items/ContentItem.cs | 2 +- Source/Editor/Content/Tree/ContentTreeNode.cs | 4 +- .../Dedicated/MeshReferenceEditor.cs | 4 +- .../CustomEditors/Dedicated/ScriptsEditor.cs | 4 +- .../CustomEditors/Dedicated/SplineEditor.cs | 2 +- .../Dedicated/UIControlEditor.cs | 2 +- .../Editors/ActorTransformEditor.cs | 2 +- .../Editors/FlaxObjectRefEditor.cs | 4 +- .../Editor/CustomEditors/Editors/TagEditor.cs | 2 +- .../CustomEditors/Editors/TypeEditor.cs | 4 +- Source/Editor/GUI/AssetPicker.cs | 6 +- Source/Editor/GUI/ComboBox.cs | 2 +- .../GUI/ContextMenu/ContextMenuButton.cs | 8 +- Source/Editor/GUI/CurveEditor.cs | 2 +- .../Editor/GUI/Dialogs/ColorPickerDialog.cs | 22 +- Source/Editor/GUI/Docking/DockPanelProxy.cs | 4 +- Source/Editor/GUI/Docking/DockWindow.cs | 2 +- Source/Editor/GUI/ItemsListContextMenu.cs | 6 +- Source/Editor/GUI/MainMenuButton.cs | 4 +- Source/Editor/GUI/NavigationButton.cs | 4 +- Source/Editor/GUI/Row.cs | 4 +- Source/Editor/GUI/StatusBar.cs | 2 +- Source/Editor/GUI/StyleValueEditor.cs | 2 +- Source/Editor/GUI/Table.cs | 2 +- Source/Editor/GUI/Tabs/Tabs.cs | 2 +- Source/Editor/GUI/Timeline/GUI/Background.cs | 2 +- Source/Editor/GUI/Timeline/Track.cs | 2 +- .../Editor/GUI/Timeline/Tracks/MemberTrack.cs | 2 +- Source/Editor/GUI/ToolStripButton.cs | 4 +- Source/Editor/GUI/Tree/TreeNode.cs | 4 +- Source/Editor/Options/OptionsModule.cs | 7 +- Source/Editor/SceneGraph/GUI/ActorTreeNode.cs | 4 +- .../Archetypes/Animation.StateMachine.cs | 8 +- Source/Editor/Surface/Archetypes/Animation.cs | 2 +- .../Editor/Surface/Archetypes/BehaviorTree.cs | 6 +- .../Surface/Archetypes/ParticleModules.cs | 2 +- Source/Editor/Surface/Archetypes/Particles.cs | 4 +- .../Surface/ContextMenu/VisjectCMItem.cs | 20 +- Source/Editor/Surface/Elements/InputBox.cs | 6 +- Source/Editor/Surface/Elements/OutputBox.cs | 2 +- Source/Editor/Surface/Elements/TextView.cs | 2 +- Source/Editor/Surface/SurfaceComment.cs | 2 +- Source/Editor/Surface/SurfaceNode.cs | 8 +- Source/Editor/Tools/Foliage/FoliageTab.cs | 2 +- Source/Editor/Tools/Terrain/CarveTab.cs | 2 +- Source/Editor/Viewport/EditorViewport.cs | 12 +- .../Viewport/Previews/AnimationPreview.cs | 4 +- .../Editor/Viewport/Previews/ModelPreview.cs | 4 +- .../Previews/ParticleSystemPreview.cs | 2 +- .../Viewport/Previews/SkinnedModelPreview.cs | 4 +- .../Viewport/Previews/TexturePreview.cs | 2 +- .../Viewport/Widgets/ViewportWidgetButton.cs | 4 +- Source/Editor/Windows/AboutDialog.cs | 2 +- .../Windows/Assets/AnimationGraphWindow.cs | 2 +- .../Editor/Windows/Assets/AnimationWindow.cs | 2 +- Source/Editor/Windows/Assets/ModelWindow.cs | 4 +- .../Windows/Assets/SkinnedModelWindow.cs | 4 +- Source/Editor/Windows/ContentWindow.Search.cs | 2 +- Source/Editor/Windows/DebugLogWindow.cs | 4 +- Source/Editor/Windows/GameWindow.cs | 8 +- Source/Editor/Windows/Profiler/SingleChart.cs | 4 +- Source/Editor/Windows/Profiler/Timeline.cs | 6 +- Source/Editor/Windows/SceneTreeWindow.cs | 2 +- Source/Editor/Windows/SplashScreen.cpp | 2 +- Source/Editor/Windows/ToolboxWindow.cs | 4 +- Source/Engine/Core/Config/GameSettings.h | 9 - Source/Engine/Core/Config/GraphicsSettings.h | 14 + Source/Engine/Graphics/Graphics.cpp | 4 + Source/Engine/Render2D/FallbackTextUtils.cs | 268 --------- Source/Engine/Render2D/Font.cpp | 24 +- Source/Engine/Render2D/Font.h | 567 ++++++++++++------ Source/Engine/Render2D/Render2D.cpp | 2 + Source/Engine/Render2D/Render2D.cs | 4 +- Source/Engine/Render2D/Render2D.h | 6 +- Source/Engine/UI/GUI/Common/Button.cs | 2 +- Source/Engine/UI/GUI/Common/Dropdown.cs | 4 +- Source/Engine/UI/GUI/Common/Label.cs | 4 +- .../Engine/UI/GUI/Common/RichTextBoxBase.cs | 8 +- Source/Engine/UI/GUI/Common/TextBox.cs | 18 +- Source/Engine/UI/GUI/Panels/DropPanel.cs | 2 +- Source/Engine/UI/GUI/Style.cs | 6 - Source/Engine/UI/GUI/Tooltip.cs | 2 +- 83 files changed, 598 insertions(+), 619 deletions(-) delete mode 100644 Source/Engine/Render2D/FallbackTextUtils.cs diff --git a/Source/Editor/Content/GUI/ContentView.cs b/Source/Editor/Content/GUI/ContentView.cs index 1091c9cef..259be104b 100644 --- a/Source/Editor/Content/GUI/ContentView.cs +++ b/Source/Editor/Content/GUI/ContentView.cs @@ -598,7 +598,7 @@ namespace FlaxEditor.Content.GUI // Check if it's an empty thing if (_items.Count == 0) { - FallbackTextUtils.DrawText(style.FontSmall, IsSearching ? "No results" : "Empty", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontSmall, IsSearching ? "No results" : "Empty", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); } } diff --git a/Source/Editor/Content/Items/ContentItem.cs b/Source/Editor/Content/Items/ContentItem.cs index 0842dfff7..604caa704 100644 --- a/Source/Editor/Content/Items/ContentItem.cs +++ b/Source/Editor/Content/Items/ContentItem.cs @@ -745,7 +745,7 @@ namespace FlaxEditor.Content // Draw short name Render2D.PushClip(ref textRect); - FallbackTextUtils.DrawText(style.FontMedium, ShowFileExtension || view.ShowFileExtensions ? FileName : ShortName, textRect, style.Foreground, nameAlignment, TextAlignment.Center, TextWrapping.WrapWords, 1f, 0.95f); + Render2D.DrawText(style.FontMedium, ShowFileExtension || view.ShowFileExtensions ? FileName : ShortName, textRect, style.Foreground, nameAlignment, TextAlignment.Center, TextWrapping.WrapWords, 1f, 0.95f); Render2D.PopClip(); } diff --git a/Source/Editor/Content/Tree/ContentTreeNode.cs b/Source/Editor/Content/Tree/ContentTreeNode.cs index 2f378e7f5..e2cd1e771 100644 --- a/Source/Editor/Content/Tree/ContentTreeNode.cs +++ b/Source/Editor/Content/Tree/ContentTreeNode.cs @@ -150,8 +150,8 @@ namespace FlaxEditor.Content var textRect = TextRect; for (int i = 0; i < ranges.Length; i++) { - var start = FallbackTextUtils.GetCharPosition(font, text, ranges[i].StartIndex); - var end = FallbackTextUtils.GetCharPosition(font, text, ranges[i].EndIndex); + var start = font.GetCharPosition(text, ranges[i].StartIndex); + var end = font.GetCharPosition(text, ranges[i].EndIndex); _highlights.Add(new Rectangle(start.X + textRect.X, textRect.Y, end.X - start.X, textRect.Height)); } isThisVisible = true; diff --git a/Source/Editor/CustomEditors/Dedicated/MeshReferenceEditor.cs b/Source/Editor/CustomEditors/Dedicated/MeshReferenceEditor.cs index 66c1c791f..4099e5aee 100644 --- a/Source/Editor/CustomEditors/Dedicated/MeshReferenceEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/MeshReferenceEditor.cs @@ -109,7 +109,7 @@ namespace FlaxEditor.CustomEditors.Dedicated { // Draw name Render2D.PushClip(nameRect); - FallbackTextUtils.DrawText(style.FontMedium, _valueName, nameRect, isEnabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, _valueName, nameRect, isEnabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); Render2D.PopClip(); // Draw deselect button @@ -118,7 +118,7 @@ namespace FlaxEditor.CustomEditors.Dedicated else { // Draw info - FallbackTextUtils.DrawText(style.FontMedium, "-", nameRect, isEnabled ? Color.OrangeRed : Color.DarkOrange, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, "-", nameRect, isEnabled ? Color.OrangeRed : Color.DarkOrange, TextAlignment.Near, TextAlignment.Center); } // Draw picker button diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs index 017fd7655..d7bfbbad7 100644 --- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs @@ -42,7 +42,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Add script button var buttonText = "Add script"; - var textSize = FallbackTextUtils.MeasureText(Style.Current.FontMedium, buttonText); + var textSize = Style.Current.FontMedium.MeasureText(buttonText); float addScriptButtonWidth = (textSize.X < 60.0f) ? 60.0f : textSize.X + 4; var buttonHeight = (textSize.Y < 18) ? 18 : textSize.Y + 4; _addScriptsButton = new Button @@ -86,7 +86,7 @@ namespace FlaxEditor.CustomEditors.Dedicated var size = Size; // Info - FallbackTextUtils.DrawText(style.FontSmall, "Drag scripts here", new Rectangle(2, _addScriptsButton.Height + 4, size.X - 4, size.Y - 4 - 20), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontSmall, "Drag scripts here", new Rectangle(2, _addScriptsButton.Height + 4, size.X - 4, size.Y - 4 - 20), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); // Check if drag is over if (IsDragOver && _dragHandlers != null && _dragHandlers.HasValidDrag) diff --git a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs index fa4379db1..7b9b65c5c 100644 --- a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs @@ -226,7 +226,7 @@ namespace FlaxEditor.CustomEditors.Dedicated if (!enabled) color *= 0.6f; Render2D.DrawSprite(tab._customIcon, iconRect, color); - FallbackTextUtils.DrawText(style.FontMedium, tab._customText, new Rectangle(0, iconSize, size.X, textHeight), color, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, tab._customText, new Rectangle(0, iconSize, size.X, textHeight), color, TextAlignment.Center, TextAlignment.Center); } } diff --git a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs index 4efbee259..296560507 100644 --- a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs @@ -423,7 +423,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Set control type button var space = layout.Space(20); var buttonText = "Set Type"; - var textSize = FallbackTextUtils.MeasureText(FlaxEngine.GUI.Style.Current.FontMedium, buttonText); + var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(buttonText); float setTypeButtonWidth = (textSize.X < 60.0f) ? 60.0f : textSize.X + 4; var setTypeButton = new Button { diff --git a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs index d8d16027b..4c153e759 100644 --- a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs @@ -100,7 +100,7 @@ namespace FlaxEditor.CustomEditors.Editors _linkButton.Clicked += ToggleLink; ToggleEnabled(); SetLinkStyle(); - var textSize = FallbackTextUtils.MeasureText(FlaxEngine.GUI.Style.Current.FontMedium, LinkedLabel.Text.Value); + var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(LinkedLabel.Text.Value); _linkButton.LocalX += textSize.X + 10; LinkedLabel.SetupContextMenu += (label, menu, editor) => { diff --git a/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs b/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs index 2f64ea159..731da3817 100644 --- a/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs +++ b/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs @@ -199,7 +199,7 @@ namespace FlaxEditor.CustomEditors.Editors { // Draw name Render2D.PushClip(nameRect); - FallbackTextUtils.DrawText(style.FontMedium, _valueName, nameRect, isEnabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, _valueName, nameRect, isEnabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); Render2D.PopClip(); // Draw deselect button @@ -208,7 +208,7 @@ namespace FlaxEditor.CustomEditors.Editors else { // Draw info - FallbackTextUtils.DrawText(style.FontMedium, "-", nameRect, isEnabled ? Color.OrangeRed : Color.DarkOrange, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, "-", nameRect, isEnabled ? Color.OrangeRed : Color.DarkOrange, TextAlignment.Near, TextAlignment.Center); } // Draw picker button diff --git a/Source/Editor/CustomEditors/Editors/TagEditor.cs b/Source/Editor/CustomEditors/Editors/TagEditor.cs index bbece7e04..dbd5d124c 100644 --- a/Source/Editor/CustomEditors/Editors/TagEditor.cs +++ b/Source/Editor/CustomEditors/Editors/TagEditor.cs @@ -631,7 +631,7 @@ namespace FlaxEditor.CustomEditors.Editors TooltipText = "Edit...", Parent = _label, }; - var textSize = FallbackTextUtils.MeasureText(FlaxEngine.GUI.Style.Current.FontMedium, buttonText); + var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(buttonText); if (textSize.Y > button.Width) button.Width = textSize.Y + 2; diff --git a/Source/Editor/CustomEditors/Editors/TypeEditor.cs b/Source/Editor/CustomEditors/Editors/TypeEditor.cs index 4d0b90383..38800a738 100644 --- a/Source/Editor/CustomEditors/Editors/TypeEditor.cs +++ b/Source/Editor/CustomEditors/Editors/TypeEditor.cs @@ -160,13 +160,13 @@ namespace FlaxEditor.CustomEditors.Editors // Draw name Render2D.PushClip(nameRect); - FallbackTextUtils.DrawText(style.FontMedium, _valueName, nameRect, style.Foreground, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, _valueName, nameRect, style.Foreground, TextAlignment.Near, TextAlignment.Center); Render2D.PopClip(); } else { // Draw info - FallbackTextUtils.DrawText(style.FontMedium, "-", nameRect, Color.OrangeRed, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, "-", nameRect, Color.OrangeRed, TextAlignment.Near, TextAlignment.Center); } // Draw picker button diff --git a/Source/Editor/GUI/AssetPicker.cs b/Source/Editor/GUI/AssetPicker.cs index 2abe5db38..84f58daf1 100644 --- a/Source/Editor/GUI/AssetPicker.cs +++ b/Source/Editor/GUI/AssetPicker.cs @@ -139,7 +139,7 @@ namespace FlaxEditor.GUI float sizeForTextLeft = Width - button1Rect.Right; if (sizeForTextLeft > 30) { - FallbackTextUtils.DrawText( + Render2D.DrawText( style.FontSmall, Validator.SelectedItem.ShortName, new Rectangle(button1Rect.Right + 2, 0, sizeForTextLeft, ButtonsSize), @@ -161,7 +161,7 @@ namespace FlaxEditor.GUI var name = Validator.SelectedAsset.GetType().Name; if (Validator.SelectedAsset.IsVirtual) name += " (virtual)"; - FallbackTextUtils.DrawText( + Render2D.DrawText( style.FontSmall, name, new Rectangle(button1Rect.Right + 2, 0, sizeForTextLeft, ButtonsSize), @@ -174,7 +174,7 @@ namespace FlaxEditor.GUI { // No element selected Render2D.FillRectangle(iconRect, style.BackgroundNormal); - FallbackTextUtils.DrawText(style.FontMedium, "No asset\nselected", iconRect, Color.Orange, TextAlignment.Center, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, Height / DefaultIconSize); + Render2D.DrawText(style.FontMedium, "No asset\nselected", iconRect, Color.Orange, TextAlignment.Center, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, Height / DefaultIconSize); } // Check if drag is over diff --git a/Source/Editor/GUI/ComboBox.cs b/Source/Editor/GUI/ComboBox.cs index 8f3a7cb4b..0417cc7e3 100644 --- a/Source/Editor/GUI/ComboBox.cs +++ b/Source/Editor/GUI/ComboBox.cs @@ -554,7 +554,7 @@ namespace FlaxEditor.GUI var textRect = new Rectangle(margin, 0, clientRect.Width - boxSize - 2.0f * margin, clientRect.Height); Render2D.PushClip(textRect); var textColor = TextColor; - FallbackTextUtils.DrawText(Font.GetFont(), text, textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, textScale); + Render2D.DrawText(Font.GetFont(), text, textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, textScale); Render2D.PopClip(); } diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs b/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs index 035e997e9..e371f7c4b 100644 --- a/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs +++ b/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs @@ -128,12 +128,12 @@ namespace FlaxEditor.GUI.ContextMenu base.Draw(); // Draw text - FallbackTextUtils.DrawText(style.FontMedium, Text, textRect, textColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, Text, textRect, textColor, TextAlignment.Near, TextAlignment.Center); if (!string.IsNullOrEmpty(ShortKeys)) { // Draw short keys - FallbackTextUtils.DrawText(style.FontMedium, ShortKeys, textRect, textColor, TextAlignment.Far, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, ShortKeys, textRect, textColor, TextAlignment.Far, TextAlignment.Center); } // Draw icon @@ -235,9 +235,9 @@ namespace FlaxEditor.GUI.ContextMenu float width = 20; if (style.FontMedium) { - width += FallbackTextUtils.MeasureText(style.FontMedium, Text).X; + width += style.FontMedium.MeasureText(Text).X; if (!string.IsNullOrEmpty(ShortKeys)) - width += 40 + FallbackTextUtils.MeasureText(style.FontMedium, ShortKeys).X; + width += 40 + style.FontMedium.MeasureText(ShortKeys).X; } return Mathf.Max(width, base.MinimumWidth); diff --git a/Source/Editor/GUI/CurveEditor.cs b/Source/Editor/GUI/CurveEditor.cs index f4839acd5..22deec120 100644 --- a/Source/Editor/GUI/CurveEditor.cs +++ b/Source/Editor/GUI/CurveEditor.cs @@ -832,7 +832,7 @@ namespace FlaxEditor.GUI 50, LabelsSize ); - FallbackTextUtils.DrawText(_labelsFont, label, labelRect, _labelsColor.AlphaMultiplied(strength), TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.7f); + Render2D.DrawText(_labelsFont, label, labelRect, _labelsColor.AlphaMultiplied(strength), TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.7f); } } } diff --git a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs index 8b1881faf..27878a763 100644 --- a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs +++ b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs @@ -281,33 +281,33 @@ namespace FlaxEditor.GUI.Dialogs // RGBA var rgbaR = new Rectangle(_cRed.Left - ChannelTextWidth, _cRed.Y, 10000, _cRed.Height); - FallbackTextUtils.DrawText(style.FontMedium, "R", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, "R", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center); rgbaR.Location.Y = _cGreen.Y; - FallbackTextUtils.DrawText(style.FontMedium, "G", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, "G", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center); rgbaR.Location.Y = _cBlue.Y; - FallbackTextUtils.DrawText(style.FontMedium, "B", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, "B", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center); rgbaR.Location.Y = _cAlpha.Y; - FallbackTextUtils.DrawText(style.FontMedium, "A", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, "A", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center); // HSV left var hsvHl = new Rectangle(_cHue.Left - ChannelTextWidth, _cHue.Y, 10000, _cHue.Height); - FallbackTextUtils.DrawText(style.FontMedium, "H", hsvHl, textColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, "H", hsvHl, textColor, TextAlignment.Near, TextAlignment.Center); hsvHl.Location.Y = _cSaturation.Y; - FallbackTextUtils.DrawText(style.FontMedium, "S", hsvHl, textColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, "S", hsvHl, textColor, TextAlignment.Near, TextAlignment.Center); hsvHl.Location.Y = _cValue.Y; - FallbackTextUtils.DrawText(style.FontMedium, "V", hsvHl, textColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, "V", hsvHl, textColor, TextAlignment.Near, TextAlignment.Center); // HSV right var hsvHr = new Rectangle(_cHue.Right + 2, _cHue.Y, 10000, _cHue.Height); - FallbackTextUtils.DrawText(style.FontMedium, "°", hsvHr, textColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, "°", hsvHr, textColor, TextAlignment.Near, TextAlignment.Center); hsvHr.Location.Y = _cSaturation.Y; - FallbackTextUtils.DrawText(style.FontMedium, "%", hsvHr, textColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, "%", hsvHr, textColor, TextAlignment.Near, TextAlignment.Center); hsvHr.Location.Y = _cValue.Y; - FallbackTextUtils.DrawText(style.FontMedium, "%", hsvHr, textColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, "%", hsvHr, textColor, TextAlignment.Near, TextAlignment.Center); // Hex var hex = new Rectangle(_cHex.Left - 26, _cHex.Y, 10000, _cHex.Height); - FallbackTextUtils.DrawText(style.FontMedium, "Hex", hex, textColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, "Hex", hex, textColor, TextAlignment.Near, TextAlignment.Center); // Color difference var newRect = new Rectangle(_cOK.X, _cHex.Bottom + PickerMargin, _cCancel.Right - _cOK.Left, 0); diff --git a/Source/Editor/GUI/Docking/DockPanelProxy.cs b/Source/Editor/GUI/Docking/DockPanelProxy.cs index 0b36b85fa..e6e57de8e 100644 --- a/Source/Editor/GUI/Docking/DockPanelProxy.cs +++ b/Source/Editor/GUI/Docking/DockPanelProxy.cs @@ -208,7 +208,7 @@ namespace FlaxEditor.GUI.Docking } // Draw text - FallbackTextUtils.DrawText( + Render2D.DrawText( style.FontMedium, tab.Title, new Rectangle(DockPanel.DefaultLeftTextMargin + iconWidth, 0, Width - DockPanel.DefaultLeftTextMargin - DockPanel.DefaultButtonsSize - 2 * DockPanel.DefaultButtonsMargin, DockPanel.DefaultHeaderHeight), @@ -271,7 +271,7 @@ namespace FlaxEditor.GUI.Docking } // Draw text - FallbackTextUtils.DrawText( + Render2D.DrawText( style.FontMedium, tab.Title, new Rectangle(x + DockPanel.DefaultLeftTextMargin + iconWidth, 0, 10000, DockPanel.DefaultHeaderHeight), diff --git a/Source/Editor/GUI/Docking/DockWindow.cs b/Source/Editor/GUI/Docking/DockWindow.cs index 561c898a0..dd4e39ce2 100644 --- a/Source/Editor/GUI/Docking/DockWindow.cs +++ b/Source/Editor/GUI/Docking/DockWindow.cs @@ -488,7 +488,7 @@ namespace FlaxEditor.GUI.Docking { var style = Style.Current; if (style?.FontMedium != null) - _titleSize = FallbackTextUtils.MeasureText(style.FontMedium, _title); + _titleSize = style.FontMedium.MeasureText(_title); } base.PerformLayoutBeforeChildren(); diff --git a/Source/Editor/GUI/ItemsListContextMenu.cs b/Source/Editor/GUI/ItemsListContextMenu.cs index d3c433a47..42d236991 100644 --- a/Source/Editor/GUI/ItemsListContextMenu.cs +++ b/Source/Editor/GUI/ItemsListContextMenu.cs @@ -86,8 +86,8 @@ namespace FlaxEditor.GUI var font = style.FontSmall; for (int i = 0; i < ranges.Length; i++) { - var start = FallbackTextUtils.GetCharPosition(font, Name, ranges[i].StartIndex); - var end = FallbackTextUtils.GetCharPosition(font, Name, ranges[i].EndIndex); + var start = font.GetCharPosition(Name, ranges[i].StartIndex); + var end = font.GetCharPosition(Name, ranges[i].EndIndex); _highlights.Add(new Rectangle(start.X + 2, 0, end.X - start.X, Height)); } Visible = true; @@ -136,7 +136,7 @@ namespace FlaxEditor.GUI } // Draw name - FallbackTextUtils.DrawText(style.FontSmall, Name, textRect, TintColor * (Enabled ? style.Foreground : style.ForegroundDisabled), TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontSmall, Name, textRect, TintColor * (Enabled ? style.Foreground : style.ForegroundDisabled), TextAlignment.Near, TextAlignment.Center); } /// diff --git a/Source/Editor/GUI/MainMenuButton.cs b/Source/Editor/GUI/MainMenuButton.cs index 43849f4ab..117922361 100644 --- a/Source/Editor/GUI/MainMenuButton.cs +++ b/Source/Editor/GUI/MainMenuButton.cs @@ -72,7 +72,7 @@ namespace FlaxEditor.GUI } // Draw text - FallbackTextUtils.DrawText(style.FontMedium, Text, clientRect, enabled && hasChildItems ? style.Foreground : style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, Text, clientRect, enabled && hasChildItems ? style.Foreground : style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); } /// @@ -102,7 +102,7 @@ namespace FlaxEditor.GUI float width = 18; if (style.FontMedium) - width += FallbackTextUtils.MeasureText(style.FontMedium, Text).X; + width += style.FontMedium.MeasureText(Text).X; Width = width; } diff --git a/Source/Editor/GUI/NavigationButton.cs b/Source/Editor/GUI/NavigationButton.cs index 8dedf72fd..18e862304 100644 --- a/Source/Editor/GUI/NavigationButton.cs +++ b/Source/Editor/GUI/NavigationButton.cs @@ -57,7 +57,7 @@ namespace FlaxEditor.GUI } // Draw text - FallbackTextUtils.DrawText(style.FontMedium, Text, textRect, EnabledInHierarchy ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, Text, textRect, EnabledInHierarchy ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); } /// @@ -67,7 +67,7 @@ namespace FlaxEditor.GUI if (style.FontMedium) { - Width = FallbackTextUtils.MeasureText(style.FontMedium, Text).X + 2 * DefaultMargin; + Width = style.FontMedium.MeasureText(Text).X + 2 * DefaultMargin; } } } diff --git a/Source/Editor/GUI/Row.cs b/Source/Editor/GUI/Row.cs index 9868ab456..43785c735 100644 --- a/Source/Editor/GUI/Row.cs +++ b/Source/Editor/GUI/Row.cs @@ -38,7 +38,7 @@ namespace FlaxEditor.GUI { Depth = -1; - var mediumHeight = FallbackTextUtils.GetMaxHeight(Style.Current.FontMedium); + var mediumHeight = Style.Current.FontMedium.GetMaxHeight(); if (Height < mediumHeight) Height = mediumHeight + 4; } @@ -99,7 +99,7 @@ namespace FlaxEditor.GUI rect.Width -= leftDepthMargin; Render2D.PushClip(rect); - FallbackTextUtils.DrawText(style.FontMedium, text, rect, style.Foreground, column.CellAlignment, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, text, rect, style.Foreground, column.CellAlignment, TextAlignment.Center); Render2D.PopClip(); x += width; diff --git a/Source/Editor/GUI/StatusBar.cs b/Source/Editor/GUI/StatusBar.cs index 7770af9a1..f8f7ae839 100644 --- a/Source/Editor/GUI/StatusBar.cs +++ b/Source/Editor/GUI/StatusBar.cs @@ -56,7 +56,7 @@ namespace FlaxEditor.GUI Render2D.DrawSprite(style.StatusBarSizeGrip, new Rectangle(Width - 12, 10, 12, 12), style.Foreground); // Draw status text - FallbackTextUtils.DrawText(style.FontSmall, Text, new Rectangle(4, 0, Width - 20, Height), TextColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontSmall, Text, new Rectangle(4, 0, Width - 20, Height), TextColor, TextAlignment.Near, TextAlignment.Center); } } } diff --git a/Source/Editor/GUI/StyleValueEditor.cs b/Source/Editor/GUI/StyleValueEditor.cs index db6696152..a89902177 100644 --- a/Source/Editor/GUI/StyleValueEditor.cs +++ b/Source/Editor/GUI/StyleValueEditor.cs @@ -157,7 +157,7 @@ namespace FlaxEditor.GUI { Rectangle textRectangle = r; textRectangle.X = 4; - FallbackTextUtils.DrawText(style.FontMedium, "No Style", textRectangle, style.Foreground); + Render2D.DrawText(style.FontMedium, "No Style", textRectangle, style.Foreground); } } diff --git a/Source/Editor/GUI/Table.cs b/Source/Editor/GUI/Table.cs index 2358557c3..1d22ddb15 100644 --- a/Source/Editor/GUI/Table.cs +++ b/Source/Editor/GUI/Table.cs @@ -130,7 +130,7 @@ namespace FlaxEditor.GUI var style = Style.Current; var font = column.TitleFont ?? style.FontMedium; - FallbackTextUtils.DrawText(font, column.Title, rect, column.TitleColor, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(font, column.Title, rect, column.TitleColor, TextAlignment.Center, TextAlignment.Center); if (columnIndex < _columns.Length - 1) { diff --git a/Source/Editor/GUI/Tabs/Tabs.cs b/Source/Editor/GUI/Tabs/Tabs.cs index 5995c7ef5..3c70363e7 100644 --- a/Source/Editor/GUI/Tabs/Tabs.cs +++ b/Source/Editor/GUI/Tabs/Tabs.cs @@ -98,7 +98,7 @@ namespace FlaxEditor.GUI.Tabs // Draw text if (!string.IsNullOrEmpty(Tab.Text)) { - FallbackTextUtils.DrawText(style.FontMedium, Tab.Text, new Rectangle(tabRect.X + textOffset, tabRect.Y, tabRect.Width - textOffset, tabRect.Height), style.Foreground, Tabs.TabsTextHorizontalAlignment, Tabs.TabsTextVerticalAlignment); + Render2D.DrawText(style.FontMedium, Tab.Text, new Rectangle(tabRect.X + textOffset, tabRect.Y, tabRect.Width - textOffset, tabRect.Height), style.Foreground, Tabs.TabsTextHorizontalAlignment, Tabs.TabsTextVerticalAlignment); } } } diff --git a/Source/Editor/GUI/Timeline/GUI/Background.cs b/Source/Editor/GUI/Timeline/GUI/Background.cs index 05c1121c0..b9eff562a 100644 --- a/Source/Editor/GUI/Timeline/GUI/Background.cs +++ b/Source/Editor/GUI/Timeline/GUI/Background.cs @@ -301,7 +301,7 @@ namespace FlaxEditor.GUI.Timeline.GUI default: throw new ArgumentOutOfRangeException(); } var labelRect = new Rectangle(x + 2, -verticalLinesHeaderExtend * 0.8f + timeAxisHeaderOffset, 50, verticalLinesHeaderExtend); - FallbackTextUtils.DrawText(style.FontSmall, labelText, labelRect, labelColor, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.8f); + Render2D.DrawText(style.FontSmall, labelText, labelRect, labelColor, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.8f); } } } diff --git a/Source/Editor/GUI/Timeline/Track.cs b/Source/Editor/GUI/Timeline/Track.cs index 20be8f0c2..f38917aa6 100644 --- a/Source/Editor/GUI/Timeline/Track.cs +++ b/Source/Editor/GUI/Timeline/Track.cs @@ -965,7 +965,7 @@ namespace FlaxEditor.GUI.Timeline } // Draw text - FallbackTextUtils.DrawText(style.FontSmall, Title ?? Name, textRect, textColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontSmall, Title ?? Name, textRect, textColor, TextAlignment.Near, TextAlignment.Center); // Disabled overlay DrawDisabled = Mute || (ParentTrack != null && ParentTrack.DrawDisabled); diff --git a/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs b/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs index dd79922ac..63787df2c 100644 --- a/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs @@ -345,7 +345,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks if (_previewValue != null) { // Based on Track.Draw for track text placement - var left = _xOffset + 16 + FallbackTextUtils.MeasureText(Style.Current.FontSmall, Title ?? Name).X; + var left = _xOffset + 16 + Style.Current.FontSmall.MeasureText(Title ?? Name).X; if (Icon.IsValid) left += 18; if (IsExpanded) diff --git a/Source/Editor/GUI/ToolStripButton.cs b/Source/Editor/GUI/ToolStripButton.cs index c4f4603e2..e839a7356 100644 --- a/Source/Editor/GUI/ToolStripButton.cs +++ b/Source/Editor/GUI/ToolStripButton.cs @@ -136,7 +136,7 @@ namespace FlaxEditor.GUI if (!string.IsNullOrEmpty(_text)) { textRect.Size.X = Width - DefaultMargin - textRect.Left; - FallbackTextUtils.DrawText(style.FontMedium, _text, textRect, enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, _text, textRect, enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); } } @@ -151,7 +151,7 @@ namespace FlaxEditor.GUI if (hasSprite) width += iconSize; if (!string.IsNullOrEmpty(_text) && style.FontMedium) - width += FallbackTextUtils.MeasureText(style.FontMedium, _text).X + (hasSprite ? DefaultMargin : 0); + width += style.FontMedium.MeasureText(_text).X + (hasSprite ? DefaultMargin : 0); Width = width; } diff --git a/Source/Editor/GUI/Tree/TreeNode.cs b/Source/Editor/GUI/Tree/TreeNode.cs index 6680e3608..703079469 100644 --- a/Source/Editor/GUI/Tree/TreeNode.cs +++ b/Source/Editor/GUI/Tree/TreeNode.cs @@ -575,7 +575,7 @@ namespace FlaxEditor.GUI.Tree var font = TextFont.GetFont(); if (font) { - _textWidth = FallbackTextUtils.MeasureText(font, _text).X; + _textWidth = font.MeasureText(_text).X; _textChanged = false; } } @@ -656,7 +656,7 @@ namespace FlaxEditor.GUI.Tree } // Draw text - FallbackTextUtils.DrawText(TextFont.GetFont(), _text, textRect, _cachedTextColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(TextFont.GetFont(), _text, textRect, _cachedTextColor, TextAlignment.Near, TextAlignment.Center); // Draw drag and drop effect if (IsDragOver && _tree.DraggedOverNode == this) diff --git a/Source/Editor/Options/OptionsModule.cs b/Source/Editor/Options/OptionsModule.cs index dc376a0a2..bf35105a5 100644 --- a/Source/Editor/Options/OptionsModule.cs +++ b/Source/Editor/Options/OptionsModule.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.IO; +using FlaxEditor.Content.Settings; using FlaxEditor.Modules; using FlaxEngine; using FlaxEngine.GUI; @@ -224,7 +225,11 @@ namespace FlaxEditor.Options } } - FallbackTextUtils.Fallbacks = FallbackFonts.Create(Options.Interface.Fallbacks); + var graphicsSetttings = GameSettings.Load(); + if (graphicsSetttings.EnableFontFallback && graphicsSetttings.FallbackFonts == null) + { + Render2D.FallbackFonts = graphicsSetttings.FallbackFonts = FontFallbackList.Create(Options.Interface.Fallbacks); + } } /// diff --git a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs index eefd7f4dd..f64e46385 100644 --- a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs +++ b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs @@ -142,8 +142,8 @@ namespace FlaxEditor.SceneGraph.GUI var textRect = TextRect; for (int i = 0; i < ranges.Length; i++) { - var start = FallbackTextUtils.GetCharPosition(font, text, ranges[i].StartIndex); - var end = FallbackTextUtils.GetCharPosition(font, text, ranges[i].EndIndex); + var start = font.GetCharPosition(text, ranges[i].StartIndex); + var end = font.GetCharPosition(text, ranges[i].EndIndex); _highlights.Add(new Rectangle(start.X + textRect.X, textRect.Y, end.X - start.X, textRect.Height)); } isThisVisible = true; diff --git a/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs b/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs index be9a96a7d..621c7c25e 100644 --- a/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs +++ b/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs @@ -337,7 +337,7 @@ namespace FlaxEditor.Surface.Archetypes _textRect = new Rectangle(Float2.Zero, Size); var style = Style.Current; - var titleSize = FallbackTextUtils.MeasureText(style.FontLarge, Title); + var titleSize = style.FontLarge.MeasureText(Title); var width = Mathf.Max(100, titleSize.X + 50); Resize(width, 0); titleSize.X += 8.0f; @@ -379,7 +379,7 @@ namespace FlaxEditor.Surface.Archetypes } // Name - FallbackTextUtils.DrawText(style.FontLarge, Title, _textRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontLarge, Title, _textRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); } /// @@ -1128,7 +1128,7 @@ namespace FlaxEditor.Surface.Archetypes } // Name - FallbackTextUtils.DrawText(style.FontLarge, Title, _textRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontLarge, Title, _textRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); // Close button Render2D.DrawSprite(style.Cross, _closeButtonRect, _closeButtonRect.Contains(_mousePosition) ? style.Foreground : style.ForegroundGrey); @@ -1402,7 +1402,7 @@ namespace FlaxEditor.Surface.Archetypes { Title = StateTitle; var style = Style.Current; - var titleSize = FallbackTextUtils.MeasureText(style.FontLarge, Title); + var titleSize = style.FontLarge.MeasureText(Title); var width = Mathf.Max(100, titleSize.X + 50); Resize(width, 0); titleSize.X += 8.0f; diff --git a/Source/Editor/Surface/Archetypes/Animation.cs b/Source/Editor/Surface/Archetypes/Animation.cs index dae16b425..40a3d2a63 100644 --- a/Source/Editor/Surface/Archetypes/Animation.cs +++ b/Source/Editor/Surface/Archetypes/Animation.cs @@ -77,7 +77,7 @@ namespace FlaxEditor.Surface.Archetypes Title = asset?.ShortName ?? "Animation"; var style = Style.Current; - Resize(Mathf.Max(230, FallbackTextUtils.MeasureText(style.FontLarge, Title).X + 30), 160); + Resize(Mathf.Max(230, style.FontLarge.MeasureText(Title).X + 30), 160); } /// diff --git a/Source/Editor/Surface/Archetypes/BehaviorTree.cs b/Source/Editor/Surface/Archetypes/BehaviorTree.cs index b240685cc..f8cd7bb5a 100644 --- a/Source/Editor/Surface/Archetypes/BehaviorTree.cs +++ b/Source/Editor/Surface/Archetypes/BehaviorTree.cs @@ -100,7 +100,7 @@ namespace FlaxEditor.Surface.Archetypes _debugRelevant = Behavior.GetNodeDebugRelevancy(instance, behavior); _debugInfo = Behavior.GetNodeDebugInfo(instance, behavior); if (!string.IsNullOrEmpty(_debugInfo)) - _debugInfoSize = FallbackTextUtils.MeasureText(Style.Current.FontSmall, _debugInfo); + _debugInfoSize = Style.Current.FontSmall.MeasureText(_debugInfo); } } @@ -184,7 +184,7 @@ namespace FlaxEditor.Surface.Archetypes if (!string.IsNullOrEmpty(_debugInfo)) { var style = Style.Current; - FallbackTextUtils.DrawText(style.FontSmall, _debugInfo, new Rectangle(4, _headerRect.Bottom + 4, _debugInfoSize), style.Foreground); + Render2D.DrawText(style.FontSmall, _debugInfo, new Rectangle(4, _headerRect.Bottom + 4, _debugInfoSize), style.Foreground); } // Debug relevancy outline @@ -487,7 +487,7 @@ namespace FlaxEditor.Surface.Archetypes var height = 0.0f; var titleLabelFont = Style.Current.FontLarge; width = Mathf.Max(width, 100.0f); - width = Mathf.Max(width, FallbackTextUtils.MeasureText(titleLabelFont, Title).X + 30); + width = Mathf.Max(width, titleLabelFont.MeasureText(Title).X + 30); if (_debugInfoSize.X > 0) { width = Mathf.Max(width, _debugInfoSize.X + 8.0f); diff --git a/Source/Editor/Surface/Archetypes/ParticleModules.cs b/Source/Editor/Surface/Archetypes/ParticleModules.cs index 6082e212e..e5be3d35d 100644 --- a/Source/Editor/Surface/Archetypes/ParticleModules.cs +++ b/Source/Editor/Surface/Archetypes/ParticleModules.cs @@ -113,7 +113,7 @@ namespace FlaxEditor.Surface.Archetypes var idx = (int)ModuleType; var headerRect = new Rectangle(0, 0, Width, 16.0f); //Render2D.FillRectangle(headerRect, Color.Red); - FallbackTextUtils.DrawText(style.FontMedium, Title, headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, Title, headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); DrawChildren(); diff --git a/Source/Editor/Surface/Archetypes/Particles.cs b/Source/Editor/Surface/Archetypes/Particles.cs index bf7828960..40b38f7ce 100644 --- a/Source/Editor/Surface/Archetypes/Particles.cs +++ b/Source/Editor/Surface/Archetypes/Particles.cs @@ -154,7 +154,7 @@ namespace FlaxEditor.Surface.Archetypes if (headerRect.Contains(mousePosition)) headerColor *= 1.07f; Render2D.FillRectangle(headerRect, headerColor); - FallbackTextUtils.DrawText(style.FontLarge, Names[idx], headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontLarge, Names[idx], headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); DrawChildren(); } @@ -194,7 +194,7 @@ namespace FlaxEditor.Surface.Archetypes if (_headerRect.Contains(ref _mousePosition)) headerColor *= 1.07f; Render2D.FillRectangle(_headerRect, headerColor); - FallbackTextUtils.DrawText(style.FontLarge, Title, _headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontLarge, Title, _headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); DrawChildren(); diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs index f4976be67..207875a92 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs @@ -200,8 +200,8 @@ namespace FlaxEditor.Surface.ContextMenu var font = style.FontSmall; for (int i = 0; i < ranges.Length; i++) { - var start = FallbackTextUtils.GetCharPosition(font, _archetype.Title, ranges[i].StartIndex); - var end = FallbackTextUtils.GetCharPosition(font, _archetype.Title, ranges[i].EndIndex); + var start = font.GetCharPosition(_archetype.Title, ranges[i].StartIndex); + var end = font.GetCharPosition(_archetype.Title, ranges[i].EndIndex); _highlights.Add(new Rectangle(start.X + textRect.X, 0, end.X - start.X, Height)); if (ranges[i].StartIndex <= 0) @@ -222,8 +222,8 @@ namespace FlaxEditor.Surface.ContextMenu _highlights.Clear(); var style = Style.Current; var font = style.FontSmall; - var start = FallbackTextUtils.GetCharPosition(font, _archetype.Title, 0); - var end = FallbackTextUtils.GetCharPosition(font, _archetype.Title, _archetype.Title.Length - 1); + var start = font.GetCharPosition(_archetype.Title, 0); + var end = font.GetCharPosition(_archetype.Title, _archetype.Title.Length - 1); _highlights.Add(new Rectangle(start.X + textRect.X, 0, end.X - start.X, Height)); _isFullMatch = true; Visible = true; @@ -237,8 +237,8 @@ namespace FlaxEditor.Surface.ContextMenu _highlights.Clear(); var style = Style.Current; var font = style.FontSmall; - var start = FallbackTextUtils.GetCharPosition(font, _archetype.Title, 0); - var end = FallbackTextUtils.GetCharPosition(font, _archetype.Title, _archetype.Title.Length - 1); + var start = font.GetCharPosition(_archetype.Title, 0); + var end = font.GetCharPosition(_archetype.Title, _archetype.Title.Length - 1); _highlights.Add(new Rectangle(start.X + textRect.X, 0, end.X - start.X, Height)); Visible = true; @@ -283,19 +283,19 @@ namespace FlaxEditor.Surface.ContextMenu } // Draw name - FallbackTextUtils.DrawText(style.FontSmall, _archetype.Title, textRect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontSmall, _archetype.Title, textRect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); if (_archetype.SubTitle != null) { - var titleLength = FallbackTextUtils.MeasureText(style.FontSmall, _archetype.Title).X; + var titleLength = style.FontSmall.MeasureText(_archetype.Title).X; var subTitleRect = new Rectangle(textRect.X + titleLength, textRect.Y, textRect.Width - titleLength, textRect.Height); - FallbackTextUtils.DrawText(style.FontSmall, _archetype.SubTitle, subTitleRect, style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontSmall, _archetype.SubTitle, subTitleRect, style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); } // Reset transform and draw score mark if (showScoreHit) { Render2D.PopTransform(); - FallbackTextUtils.DrawText(style.FontSmall, "> ", textRect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontSmall, "> ", textRect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); } } diff --git a/Source/Editor/Surface/Elements/InputBox.cs b/Source/Editor/Surface/Elements/InputBox.cs index a5a99b932..2047bcd1a 100644 --- a/Source/Editor/Surface/Elements/InputBox.cs +++ b/Source/Editor/Surface/Elements/InputBox.cs @@ -1428,7 +1428,7 @@ namespace FlaxEditor.Surface.Elements if (_defaultValueEditor != null) { - _defaultValueEditor.Location = new Float2(X + Width + 8 + FallbackTextUtils.MeasureText(Style.Current.FontSmall, Text).X, Y); + _defaultValueEditor.Location = new Float2(X + Width + 8 + Style.Current.FontSmall.MeasureText(Text).X, Y); } } @@ -1443,7 +1443,7 @@ namespace FlaxEditor.Surface.Elements // Draw text var style = Style.Current; var rect = new Rectangle(Width + 4, 0, 1410, Height); - FallbackTextUtils.DrawText(style.FontSmall, Text, rect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontSmall, Text, rect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); } /// @@ -1635,7 +1635,7 @@ namespace FlaxEditor.Surface.Elements { if (DefaultValueEditors[i].CanUse(this, ref _currentType)) { - var bounds = new Rectangle(X + Width + 8 + FallbackTextUtils.MeasureText(Style.Current.FontSmall, Text).X, Y, 90, Height); + var bounds = new Rectangle(X + Width + 8 + Style.Current.FontSmall.MeasureText(Text).X, Y, 90, Height); _editor = DefaultValueEditors[i]; try { diff --git a/Source/Editor/Surface/Elements/OutputBox.cs b/Source/Editor/Surface/Elements/OutputBox.cs index 5fb123700..497e2001a 100644 --- a/Source/Editor/Surface/Elements/OutputBox.cs +++ b/Source/Editor/Surface/Elements/OutputBox.cs @@ -189,7 +189,7 @@ namespace FlaxEditor.Surface.Elements // Draw text var style = Style.Current; var rect = new Rectangle(-100, 0, 100 - 2, Height); - FallbackTextUtils.DrawText(style.FontSmall, Text, rect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Far, TextAlignment.Center); + Render2D.DrawText(style.FontSmall, Text, rect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Far, TextAlignment.Center); } } } diff --git a/Source/Editor/Surface/Elements/TextView.cs b/Source/Editor/Surface/Elements/TextView.cs index c52667fe3..284497396 100644 --- a/Source/Editor/Surface/Elements/TextView.cs +++ b/Source/Editor/Surface/Elements/TextView.cs @@ -25,7 +25,7 @@ namespace FlaxEditor.Surface.Elements var style = Style.Current; var color = Enabled ? style.Foreground : style.ForegroundDisabled; - FallbackTextUtils.DrawText(style.FontSmall, Archetype.Text, new Rectangle(Float2.Zero, Size), color, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontSmall, Archetype.Text, new Rectangle(Float2.Zero, Size), color, TextAlignment.Near, TextAlignment.Center); } } } diff --git a/Source/Editor/Surface/SurfaceComment.cs b/Source/Editor/Surface/SurfaceComment.cs index 26cc912ab..aad45190e 100644 --- a/Source/Editor/Surface/SurfaceComment.cs +++ b/Source/Editor/Surface/SurfaceComment.cs @@ -169,7 +169,7 @@ namespace FlaxEditor.Surface // Header Render2D.FillRectangle(_headerRect, headerColor); - FallbackTextUtils.DrawText(style.FontLarge, Title, _headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontLarge, Title, _headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); // Close button Render2D.DrawSprite(style.Cross, _closeButtonRect, _closeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey); diff --git a/Source/Editor/Surface/SurfaceNode.cs b/Source/Editor/Surface/SurfaceNode.cs index df706faa6..780ef81f0 100644 --- a/Source/Editor/Surface/SurfaceNode.cs +++ b/Source/Editor/Surface/SurfaceNode.cs @@ -199,7 +199,7 @@ namespace FlaxEditor.Surface continue; if (child is InputBox inputBox) { - var boxWidth = FallbackTextUtils.MeasureText(boxLabelFont, inputBox.Text).X + 20; + var boxWidth = boxLabelFont.MeasureText(inputBox.Text).X + 20; if (inputBox.DefaultValueEditor != null) boxWidth += inputBox.DefaultValueEditor.Width + 4; leftWidth = Mathf.Max(leftWidth, boxWidth); @@ -207,7 +207,7 @@ namespace FlaxEditor.Surface } else if (child is OutputBox outputBox) { - rightWidth = Mathf.Max(rightWidth, FallbackTextUtils.MeasureText(boxLabelFont, outputBox.Text).X + 20); + rightWidth = Mathf.Max(rightWidth, boxLabelFont.MeasureText(outputBox.Text).X + 20); rightHeight = Mathf.Max(rightHeight, outputBox.Archetype.Position.Y - Constants.NodeMarginY - Constants.NodeHeaderSize + 20.0f); } else if (child is Control control) @@ -225,7 +225,7 @@ namespace FlaxEditor.Surface } } width = Mathf.Max(width, leftWidth + rightWidth + 10); - width = Mathf.Max(width, FallbackTextUtils.MeasureText(titleLabelFont, Title).X + 30); + width = Mathf.Max(width, titleLabelFont.MeasureText(Title).X + 30); height = Mathf.Max(height, Mathf.Max(leftHeight, rightHeight)); Resize(width, height); } @@ -1027,7 +1027,7 @@ namespace FlaxEditor.Surface if (_headerRect.Contains(ref _mousePosition)) headerColor *= 1.07f; Render2D.FillRectangle(_headerRect, headerColor); - FallbackTextUtils.DrawText(style.FontLarge, Title, _headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontLarge, Title, _headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); // Close button if ((Archetype.Flags & NodeFlags.NoCloseButton) == 0 && Surface.CanEdit) diff --git a/Source/Editor/Tools/Foliage/FoliageTab.cs b/Source/Editor/Tools/Foliage/FoliageTab.cs index d36c3b6ed..1b46a42be 100644 --- a/Source/Editor/Tools/Foliage/FoliageTab.cs +++ b/Source/Editor/Tools/Foliage/FoliageTab.cs @@ -147,7 +147,7 @@ namespace FlaxEditor.Tools.Foliage Parent = _noFoliagePanel, Enabled = false }; - var textSize = FallbackTextUtils.MeasureText(Style.Current.FontMedium, buttonText); + var textSize = Style.Current.FontMedium.MeasureText(buttonText); if (_createNewFoliage.Width < textSize.X) { _createNewFoliage.LocalX -= (textSize.X - _createNewFoliage.Width) / 2; diff --git a/Source/Editor/Tools/Terrain/CarveTab.cs b/Source/Editor/Tools/Terrain/CarveTab.cs index e64a364f0..d51915acf 100644 --- a/Source/Editor/Tools/Terrain/CarveTab.cs +++ b/Source/Editor/Tools/Terrain/CarveTab.cs @@ -105,7 +105,7 @@ namespace FlaxEditor.Tools.Terrain Parent = _noTerrainPanel, Enabled = false }; - var textSize = FallbackTextUtils.MeasureText(Style.Current.FontMedium, buttonText); + var textSize = Style.Current.FontMedium.MeasureText(buttonText); if (_createTerrainButton.Width < textSize.X) { _createTerrainButton.LocalX -= (textSize.X - _createTerrainButton.Width) / 2; diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index 515514131..c49392d01 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -548,9 +548,9 @@ namespace FlaxEditor.Viewport #region Camera settings widget var largestText = "Relative Panning"; - var textSize = FallbackTextUtils.MeasureText(Style.Current.FontMedium, largestText); + var textSize = Style.Current.FontMedium.MeasureText(largestText); var xLocationForExtras = textSize.X + 5; - var cameraSpeedTextWidth = FallbackTextUtils.MeasureText(Style.Current.FontMedium, "0.00").X; + var cameraSpeedTextWidth = Style.Current.FontMedium.MeasureText("0.00").X; // Camera Settings Widget _cameraWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); @@ -801,7 +801,7 @@ namespace FlaxEditor.Viewport #region View mode widget largestText = "Brightness"; - textSize = FallbackTextUtils.MeasureText(Style.Current.FontMedium, largestText); + textSize = Style.Current.FontMedium.MeasureText(largestText); xLocationForExtras = textSize.X + 5; var viewMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperLeft); @@ -1233,8 +1233,8 @@ namespace FlaxEditor.Viewport color = Color.Yellow; var text = string.Format("FPS: {0}", fps); var font = Style.Current.FontMedium; - FallbackTextUtils.DrawText(font, text, new Rectangle(Float2.One, Size), Color.Black); - FallbackTextUtils.DrawText(font, text, new Rectangle(Float2.Zero, Size), color); + Render2D.DrawText(font, text, new Rectangle(Float2.One, Size), Color.Black); + Render2D.DrawText(font, text, new Rectangle(Float2.Zero, Size), color); } } @@ -1814,7 +1814,7 @@ namespace FlaxEditor.Viewport { var bounds = new Rectangle(Float2.Zero, Size); Render2D.FillRectangle(bounds, new Color(0.0f, 0.0f, 0.0f, 0.2f)); - FallbackTextUtils.DrawText(Style.Current.FontLarge, "Debugger breakpoint hit...", bounds, Color.White, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(Style.Current.FontLarge, "Debugger breakpoint hit...", bounds, Color.White, TextAlignment.Center, TextAlignment.Center); } } diff --git a/Source/Editor/Viewport/Previews/AnimationPreview.cs b/Source/Editor/Viewport/Previews/AnimationPreview.cs index c4ada83c4..1679a36bc 100644 --- a/Source/Editor/Viewport/Previews/AnimationPreview.cs +++ b/Source/Editor/Viewport/Previews/AnimationPreview.cs @@ -96,11 +96,11 @@ namespace FlaxEditor.Viewport.Previews var skinnedModel = SkinnedModel; if (skinnedModel == null) { - FallbackTextUtils.DrawText(style.FontLarge, "Missing Base Model", new Rectangle(Float2.Zero, Size), Color.Red, TextAlignment.Center, TextAlignment.Center, TextWrapping.WrapWords); + Render2D.DrawText(style.FontLarge, "Missing Base Model", new Rectangle(Float2.Zero, Size), Color.Red, TextAlignment.Center, TextAlignment.Center, TextWrapping.WrapWords); } else if (!skinnedModel.IsLoaded) { - FallbackTextUtils.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); } } diff --git a/Source/Editor/Viewport/Previews/ModelPreview.cs b/Source/Editor/Viewport/Previews/ModelPreview.cs index b31776bf6..a6496aafe 100644 --- a/Source/Editor/Viewport/Previews/ModelPreview.cs +++ b/Source/Editor/Viewport/Previews/ModelPreview.cs @@ -409,8 +409,8 @@ namespace FlaxEditor.Viewport.Previews } var font = Style.Current.FontMedium; var pos = new Float2(10, 50); - FallbackTextUtils.DrawText(font, text, new Rectangle(pos + Float2.One, Size), Color.Black); - FallbackTextUtils.DrawText(font, text, new Rectangle(pos, Size), Color.White); + Render2D.DrawText(font, text, new Rectangle(pos + Float2.One, Size), Color.Black); + Render2D.DrawText(font, text, new Rectangle(pos, Size), Color.White); } } diff --git a/Source/Editor/Viewport/Previews/ParticleSystemPreview.cs b/Source/Editor/Viewport/Previews/ParticleSystemPreview.cs index a6eb721ce..c6ce5a7b5 100644 --- a/Source/Editor/Viewport/Previews/ParticleSystemPreview.cs +++ b/Source/Editor/Viewport/Previews/ParticleSystemPreview.cs @@ -254,7 +254,7 @@ namespace FlaxEditor.Viewport.Previews if (_showParticlesCounter) { var count = _previewEffect.ParticlesCount; - FallbackTextUtils.DrawText( + Render2D.DrawText( Style.Current.FontSmall, "Particles: " + count, new Rectangle(Float2.Zero, Size), diff --git a/Source/Editor/Viewport/Previews/SkinnedModelPreview.cs b/Source/Editor/Viewport/Previews/SkinnedModelPreview.cs index 6c02c019a..8bcc506d9 100644 --- a/Source/Editor/Viewport/Previews/SkinnedModelPreview.cs +++ b/Source/Editor/Viewport/Previews/SkinnedModelPreview.cs @@ -161,8 +161,8 @@ namespace FlaxEditor.Viewport.Previews } var font = Style.Current.FontMedium; var pos = new Float2(10, 50); - FallbackTextUtils.DrawText(font, text, new Rectangle(pos + Float2.One, Size), Color.Black); - FallbackTextUtils.DrawText(font, text, new Rectangle(pos, Size), Color.White); + Render2D.DrawText(font, text, new Rectangle(pos + Float2.One, Size), Color.Black); + Render2D.DrawText(font, text, new Rectangle(pos, Size), Color.White); } } diff --git a/Source/Editor/Viewport/Previews/TexturePreview.cs b/Source/Editor/Viewport/Previews/TexturePreview.cs index 5e7594e9c..ec10bb019 100644 --- a/Source/Editor/Viewport/Previews/TexturePreview.cs +++ b/Source/Editor/Viewport/Previews/TexturePreview.cs @@ -103,7 +103,7 @@ namespace FlaxEditor.Viewport.Previews { var bounds = new Rectangle(Float2.Zero, Size); Render2D.FillRectangle(bounds, new Color(0.0f, 0.0f, 0.0f, 0.2f)); - FallbackTextUtils.DrawText(Style.Current.FontLarge, "Debugger breakpoint hit...", bounds, Color.White, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(Style.Current.FontLarge, "Debugger breakpoint hit...", bounds, Color.White, TextAlignment.Center, TextAlignment.Center); } Render2D.PopClip(); diff --git a/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs b/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs index 43f990afb..77e94ae53 100644 --- a/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs +++ b/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs @@ -123,7 +123,7 @@ namespace FlaxEditor.Viewport.Widgets } // Draw text - FallbackTextUtils.DrawText(style.FontMedium, _text, textRect, style.ForegroundViewport * (IsMouseOver ? 1.0f : 0.9f), TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, _text, textRect, style.ForegroundViewport * (IsMouseOver ? 1.0f : 0.9f), TextAlignment.Center, TextAlignment.Center); } /// @@ -163,7 +163,7 @@ namespace FlaxEditor.Viewport.Widgets var style = Style.Current; if (style != null && style.FontMedium) - Width = CalculateButtonWidth(_forcedTextWidth > 0.0f ? _forcedTextWidth : FallbackTextUtils.MeasureText(style.FontMedium, _text).X, Icon.IsValid); + Width = CalculateButtonWidth(_forcedTextWidth > 0.0f ? _forcedTextWidth : style.FontMedium.MeasureText(_text).X, Icon.IsValid); } } } diff --git a/Source/Editor/Windows/AboutDialog.cs b/Source/Editor/Windows/AboutDialog.cs index 8c38ed2a9..b059dadcb 100644 --- a/Source/Editor/Windows/AboutDialog.cs +++ b/Source/Editor/Windows/AboutDialog.cs @@ -53,7 +53,7 @@ namespace FlaxEditor.Windows Parent = this }; var buttonText = "Copy version info"; - var fontSize = FallbackTextUtils.MeasureText(Style.Current.FontMedium, buttonText); + var fontSize = Style.Current.FontMedium.MeasureText(buttonText); var copyVersionButton = new Button(Width - fontSize.X - 8, 6, fontSize.X + 4, 20) { Text = buttonText, diff --git a/Source/Editor/Windows/Assets/AnimationGraphWindow.cs b/Source/Editor/Windows/Assets/AnimationGraphWindow.cs index eb4198fd8..12eac22b5 100644 --- a/Source/Editor/Windows/Assets/AnimationGraphWindow.cs +++ b/Source/Editor/Windows/Assets/AnimationGraphWindow.cs @@ -51,7 +51,7 @@ namespace FlaxEditor.Windows.Assets var style = Style.Current; if (_window.Asset == null || !_window.Asset.IsLoaded) { - FallbackTextUtils.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); } } } diff --git a/Source/Editor/Windows/Assets/AnimationWindow.cs b/Source/Editor/Windows/Assets/AnimationWindow.cs index 042cf2b37..8f88d93f6 100644 --- a/Source/Editor/Windows/Assets/AnimationWindow.cs +++ b/Source/Editor/Windows/Assets/AnimationWindow.cs @@ -61,7 +61,7 @@ namespace FlaxEditor.Windows.Assets var animation = _window.Asset; if (animation == null || !animation.IsLoaded) { - FallbackTextUtils.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); } } diff --git a/Source/Editor/Windows/Assets/ModelWindow.cs b/Source/Editor/Windows/Assets/ModelWindow.cs index d48966043..c2764a5a6 100644 --- a/Source/Editor/Windows/Assets/ModelWindow.cs +++ b/Source/Editor/Windows/Assets/ModelWindow.cs @@ -48,7 +48,7 @@ namespace FlaxEditor.Windows.Assets var asset = _window.Asset; if (asset == null || !asset.IsLoaded) { - FallbackTextUtils.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); } } } @@ -645,7 +645,7 @@ namespace FlaxEditor.Windows.Assets if (!Proxy.Window._meshData.RequestMeshData(Proxy.Window._asset)) { Invalidate(); - FallbackTextUtils.DrawText(Style.Current.FontMedium, "Loading...", new Rectangle(Float2.Zero, size), Color.White, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(Style.Current.FontMedium, "Loading...", new Rectangle(Float2.Zero, size), Color.White, TextAlignment.Center, TextAlignment.Center); return; } diff --git a/Source/Editor/Windows/Assets/SkinnedModelWindow.cs b/Source/Editor/Windows/Assets/SkinnedModelWindow.cs index 1e7192d9f..95827240c 100644 --- a/Source/Editor/Windows/Assets/SkinnedModelWindow.cs +++ b/Source/Editor/Windows/Assets/SkinnedModelWindow.cs @@ -50,7 +50,7 @@ namespace FlaxEditor.Windows.Assets var asset = _window.Asset; if (asset == null || !asset.IsLoaded) { - FallbackTextUtils.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); } } } @@ -715,7 +715,7 @@ namespace FlaxEditor.Windows.Assets if (!Proxy.Window.RequestMeshData()) { Invalidate(); - FallbackTextUtils.DrawText(Style.Current.FontMedium, "Loading...", new Rectangle(Float2.Zero, size), Color.White, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(Style.Current.FontMedium, "Loading...", new Rectangle(Float2.Zero, size), Color.White, TextAlignment.Center, TextAlignment.Center); return; } diff --git a/Source/Editor/Windows/ContentWindow.Search.cs b/Source/Editor/Windows/ContentWindow.Search.cs index 67b7deddd..a1072d158 100644 --- a/Source/Editor/Windows/ContentWindow.Search.cs +++ b/Source/Editor/Windows/ContentWindow.Search.cs @@ -57,7 +57,7 @@ namespace FlaxEditor.Windows var textRect = new Rectangle(margin, 0, clientRect.Width - boxSize - 2.0f * margin, clientRect.Height); Render2D.PushClip(textRect); var textColor = TextColor; - FallbackTextUtils.DrawText(Font.GetFont(), "View", textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, textScale); + Render2D.DrawText(Font.GetFont(), "View", textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, textScale); Render2D.PopClip(); // Arrow diff --git a/Source/Editor/Windows/DebugLogWindow.cs b/Source/Editor/Windows/DebugLogWindow.cs index 91d4f9d8c..ab0c07ec5 100644 --- a/Source/Editor/Windows/DebugLogWindow.cs +++ b/Source/Editor/Windows/DebugLogWindow.cs @@ -140,11 +140,11 @@ namespace FlaxEditor.Windows Render2D.PushClip(ref clientRect); if (LogCount == 1) { - FallbackTextUtils.DrawText(style.FontMedium, Desc.Title, textRect, style.Foreground); + Render2D.DrawText(style.FontMedium, Desc.Title, textRect, style.Foreground); } else if (LogCount > 1) { - FallbackTextUtils.DrawText(style.FontMedium, $"{Desc.Title} ({LogCount})", textRect, style.Foreground); + Render2D.DrawText(style.FontMedium, $"{Desc.Title} ({LogCount})", textRect, style.Foreground); } Render2D.PopClip(); } diff --git a/Source/Editor/Windows/GameWindow.cs b/Source/Editor/Windows/GameWindow.cs index 2c0364995..4a8c11176 100644 --- a/Source/Editor/Windows/GameWindow.cs +++ b/Source/Editor/Windows/GameWindow.cs @@ -827,7 +827,7 @@ namespace FlaxEditor.Windows if (Camera.MainCamera == null) { var style = Style.Current; - FallbackTextUtils.DrawText(style.FontLarge, "No camera", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontLarge, "No camera", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); } // Selected UI controls outline @@ -866,8 +866,8 @@ namespace FlaxEditor.Windows var alpha = Mathf.Saturate(-animTime / fadeOutTime); var rect = new Rectangle(new Float2(6), Size - 12); var text = "Press Shift+F11 to unlock the mouse"; - FallbackTextUtils.DrawText(style.FontSmall, text, rect + new Float2(1.0f), style.Background * alpha, TextAlignment.Near, TextAlignment.Far); - FallbackTextUtils.DrawText(style.FontSmall, text, rect, style.Foreground * alpha, TextAlignment.Near, TextAlignment.Far); + Render2D.DrawText(style.FontSmall, text, rect + new Float2(1.0f), style.Background * alpha, TextAlignment.Near, TextAlignment.Far); + Render2D.DrawText(style.FontSmall, text, rect, style.Foreground * alpha, TextAlignment.Near, TextAlignment.Far); } timeout = 1.0f; @@ -884,7 +884,7 @@ namespace FlaxEditor.Windows { var bounds = new Rectangle(Float2.Zero, Size); Render2D.FillRectangle(bounds, new Color(0.0f, 0.0f, 0.0f, 0.2f)); - FallbackTextUtils.DrawText(Style.Current.FontLarge, "Debugger breakpoint hit...", bounds, Color.White, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(Style.Current.FontLarge, "Debugger breakpoint hit...", bounds, Color.White, TextAlignment.Center, TextAlignment.Center); } } } diff --git a/Source/Editor/Windows/Profiler/SingleChart.cs b/Source/Editor/Windows/Profiler/SingleChart.cs index 25241b4c6..4f36692e5 100644 --- a/Source/Editor/Windows/Profiler/SingleChart.cs +++ b/Source/Editor/Windows/Profiler/SingleChart.cs @@ -138,8 +138,8 @@ namespace FlaxEditor.Windows.Profiler var headerRect = new Rectangle(0, chartHeight, Width, TitleHeight); var headerTextRect = new Rectangle(2, chartHeight, Width - 4, TitleHeight); Render2D.FillRectangle(headerRect, style.BackgroundNormal); - FallbackTextUtils.DrawText(style.FontMedium, Title, headerTextRect, style.ForegroundGrey, TextAlignment.Near, TextAlignment.Center); - FallbackTextUtils.DrawText(style.FontMedium, _sample, headerTextRect, style.Foreground, TextAlignment.Far, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, Title, headerTextRect, style.ForegroundGrey, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, _sample, headerTextRect, style.Foreground, TextAlignment.Far, TextAlignment.Center); } private void OnClick(ref Float2 location) diff --git a/Source/Editor/Windows/Profiler/Timeline.cs b/Source/Editor/Windows/Profiler/Timeline.cs index f87efdace..59a7a0e26 100644 --- a/Source/Editor/Windows/Profiler/Timeline.cs +++ b/Source/Editor/Windows/Profiler/Timeline.cs @@ -85,12 +85,12 @@ namespace FlaxEditor.Windows.Profiler Render2D.DrawRectangle(bounds, color * 0.5f); if (_nameLength < 0 && style.FontMedium) - _nameLength = FallbackTextUtils.MeasureText(style.FontMedium, _name).X; + _nameLength = style.FontMedium.MeasureText(_name).X; if (_nameLength < bounds.Width + 4) { Render2D.PushClip(bounds); - FallbackTextUtils.DrawText(style.FontMedium, _name, bounds, Style.Current.Foreground, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, _name, bounds, Style.Current.Foreground, TextAlignment.Center, TextAlignment.Center); Render2D.PopClip(); } } @@ -115,7 +115,7 @@ namespace FlaxEditor.Windows.Profiler var style = Style.Current; var rect = new Rectangle(Float2.Zero, Size); Render2D.PushClip(rect); - FallbackTextUtils.DrawText(style.FontMedium, Name, rect, Style.Current.Foreground, TextAlignment.Center, TextAlignment.Center, TextWrapping.WrapChars); + Render2D.DrawText(style.FontMedium, Name, rect, Style.Current.Foreground, TextAlignment.Center, TextAlignment.Center, TextWrapping.WrapChars); Render2D.PopClip(); } } diff --git a/Source/Editor/Windows/SceneTreeWindow.cs b/Source/Editor/Windows/SceneTreeWindow.cs index 2fd990861..63ba7b960 100644 --- a/Source/Editor/Windows/SceneTreeWindow.cs +++ b/Source/Editor/Windows/SceneTreeWindow.cs @@ -297,7 +297,7 @@ namespace FlaxEditor.Windows } if (overlayText != null) { - FallbackTextUtils.DrawText(style.FontLarge, overlayText, GetClientArea(), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center, textWrap); + Render2D.DrawText(style.FontLarge, overlayText, GetClientArea(), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center, textWrap); } base.Draw(); diff --git a/Source/Editor/Windows/SplashScreen.cpp b/Source/Editor/Windows/SplashScreen.cpp index cbb51ae3c..cba59191e 100644 --- a/Source/Editor/Windows/SplashScreen.cpp +++ b/Source/Editor/Windows/SplashScreen.cpp @@ -258,7 +258,7 @@ void SplashScreen::OnDraw() return; // Title - const auto titleLength = _titleFont->MeasureText(GetTitle()); + const auto titleLength = _titleFont->MeasureTextInternal(GetTitle()); TextLayoutOptions layout; layout.Bounds = Rectangle(10 * s, 10 * s, width - 10 * s, 50 * s); layout.HorizontalAlignment = TextAlignment::Near; diff --git a/Source/Editor/Windows/ToolboxWindow.cs b/Source/Editor/Windows/ToolboxWindow.cs index f86341df3..e2b04669b 100644 --- a/Source/Editor/Windows/ToolboxWindow.cs +++ b/Source/Editor/Windows/ToolboxWindow.cs @@ -272,8 +272,8 @@ namespace FlaxEditor.Windows var textRect = item.TextRect; for (int i = 0; i < ranges.Length; i++) { - var start = FallbackTextUtils.GetCharPosition(font, text, ranges[i].StartIndex); - var end = FallbackTextUtils.GetCharPosition(font, text, ranges[i].EndIndex); + var start = font.GetCharPosition(text, ranges[i].StartIndex); + var end = font.GetCharPosition(text, ranges[i].EndIndex); highlights.Add(new Rectangle(start.X + textRect.X, textRect.Y, end.X - start.X, textRect.Height)); } item.SetHighlights(highlights); diff --git a/Source/Engine/Core/Config/GameSettings.h b/Source/Engine/Core/Config/GameSettings.h index 6813f3ee2..675c26bd1 100644 --- a/Source/Engine/Core/Config/GameSettings.h +++ b/Source/Engine/Core/Config/GameSettings.h @@ -35,15 +35,6 @@ public: API_FIELD(Attributes="EditorOrder(15), EditorDisplay(\"General\")") String CopyrightNotice; - /// - /// The copyright note used for content signing (eg. source code header). - /// - API_FIELD(Attributes = "EditorOrder(1200), EditorDisplay(\"Other Settings\")") - bool EnableFontFallback; - - API_FIELD(Attributes = "EditorOrder(1205), EditorDisplay(\"Other Settings\")") - FontFallbackList* FontFallbacks; - /// /// The default application icon. /// diff --git a/Source/Engine/Core/Config/GraphicsSettings.h b/Source/Engine/Core/Config/GraphicsSettings.h index 81d80cb35..7abfbb048 100644 --- a/Source/Engine/Core/Config/GraphicsSettings.h +++ b/Source/Engine/Core/Config/GraphicsSettings.h @@ -6,6 +6,8 @@ #include "Engine/Graphics/Enums.h" #include "Engine/Graphics/PostProcessSettings.h" +class FontFallbackList; + /// /// Graphics rendering settings. /// @@ -118,6 +120,18 @@ public: API_FIELD(Attributes="EditorOrder(10000), EditorDisplay(\"Post Process Settings\", EditorDisplayAttribute.InlineStyle)") PostProcessSettings PostProcessSettings; + /// + /// + /// + API_FIELD(Attributes = "EditorOrder(12000), EditorDisplay(\"Text Render Settings\", EditorDisplayAttribute.InlineStyle)") + bool EnableFontFallback = true; + + /// + /// + /// + API_FIELD(Attributes = "EditorOrder(12005), EditorDisplay(\"Text Render Settings\", EditorDisplayAttribute.InlineStyle)") + FontFallbackList* FallbackFonts; + private: /// /// Renamed UeeHDRProbes into UseHDRProbes diff --git a/Source/Engine/Graphics/Graphics.cpp b/Source/Engine/Graphics/Graphics.cpp index f91c58cea..20f29288b 100644 --- a/Source/Engine/Graphics/Graphics.cpp +++ b/Source/Engine/Graphics/Graphics.cpp @@ -8,6 +8,7 @@ #include "Engine/Core/Config/GraphicsSettings.h" #include "Engine/Engine/CommandLine.h" #include "Engine/Engine/EngineService.h" +#include "Engine/Render2D/Render2D.h" bool Graphics::UseVSync = false; Quality Graphics::AAQuality = Quality::Medium; @@ -69,6 +70,9 @@ void GraphicsSettings::Apply() Graphics::GIQuality = GIQuality; Graphics::PostProcessSettings = ::PostProcessSettings(); Graphics::PostProcessSettings.BlendWith(PostProcessSettings, 1.0f); + + Render2D::EnableFontFallback = EnableFontFallback; + Render2D::FallbackFonts = FallbackFonts; } void Graphics::DisposeDevice() diff --git a/Source/Engine/Render2D/FallbackTextUtils.cs b/Source/Engine/Render2D/FallbackTextUtils.cs deleted file mode 100644 index 486a354bf..000000000 --- a/Source/Engine/Render2D/FallbackTextUtils.cs +++ /dev/null @@ -1,268 +0,0 @@ - -using System.Runtime.CompilerServices; - -namespace FlaxEngine -{ - /// - /// A collection of functions to handle text rendering with fallback font - /// - public static class FallbackTextUtils - { - public static FallbackFonts Fallbacks - { - get; set; - } = null; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void DrawText(Font font, string text, Color color, ref TextLayoutOptions layout, MaterialBase customMaterial = null, bool useFallback = true) - { - if (Fallbacks != null && useFallback) - { - Render2D.DrawText(font, Fallbacks, text, color, ref layout, customMaterial); - } - else - { - Render2D.DrawText(font, text, color, ref layout, customMaterial); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void DrawText(Font font, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f, bool useFallback = true) - { - var layout = new TextLayoutOptions - { - Bounds = layoutRect, - HorizontalAlignment = horizontalAlignment, - VerticalAlignment = verticalAlignment, - TextWrapping = textWrapping, - Scale = scale, - BaseLinesGapScale = baseLinesGapScale, - }; - - if (Fallbacks != null && useFallback) - { - Render2D.DrawText(font, Fallbacks, text, color, ref layout); - } - else - { - Render2D.DrawText(font, text, color, ref layout); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void DrawText(Font font, MaterialBase customMaterial, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f, bool useFallback = true) - { - var layout = new TextLayoutOptions - { - Bounds = layoutRect, - HorizontalAlignment = horizontalAlignment, - VerticalAlignment = verticalAlignment, - TextWrapping = textWrapping, - Scale = scale, - BaseLinesGapScale = baseLinesGapScale, - }; - - if (Fallbacks != null && useFallback) - { - Render2D.DrawText(font, Fallbacks, text, color, ref layout, customMaterial); - } - else - { - Render2D.DrawText(font, text, color, ref layout, customMaterial); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Float2 MeasureText(Font font, string text, bool useFallback = true) - { - if (Fallbacks != null && useFallback) - { - return font.MeasureText(Fallbacks, text); - } - else - { - return font.MeasureText(text); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Float2 MeasureText(Font font, string text, ref TextRange textRange, bool useFallback = true) - { - if (Fallbacks != null && useFallback) - { - return font.MeasureText(Fallbacks, text, ref textRange); - } - else - { - return font.MeasureText(text, ref textRange); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Float2 MeasureText(Font font, string text, ref TextLayoutOptions layout, bool useFallback = true) - { - if (Fallbacks != null && useFallback) - { - return font.MeasureText(Fallbacks, text, ref layout); - } - else - { - return font.MeasureText(text, ref layout); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Float2 MeasureText(Font font, string text, ref TextRange textRange, ref TextLayoutOptions layout, bool useFallback = true) - { - if (Fallbacks != null && useFallback) - { - return font.MeasureText(Fallbacks, text, ref textRange, ref layout); - } - else - { - return font.MeasureText(text, ref textRange, ref layout); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int HitTestText(Font font, string text, Float2 location, bool useFallback = true) - { - if (Fallbacks != null && useFallback) - { - return font.HitTestText(Fallbacks, text, location); - } - else - { - return font.HitTestText(text, location); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int HitTestText(Font font, string text, ref TextRange textRange, Float2 location, bool useFallback = true) - { - if (Fallbacks != null && useFallback) - { - return font.HitTestText(Fallbacks, text, ref textRange, location); - } - else - { - return font.HitTestText(text, ref textRange, location); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int HitTestText(Font font, string text, Float2 location, ref TextLayoutOptions layout, bool useFallback = true) - { - if (Fallbacks != null && useFallback) - { - return font.HitTestText(Fallbacks, text, location, ref layout); - } - else - { - return font.HitTestText(text, location, ref layout); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int HitTestText(Font font, string text, ref TextRange textRange, Float2 location, ref TextLayoutOptions layout, bool useFallback = true) - { - if (Fallbacks != null && useFallback) - { - return font.HitTestText(Fallbacks, text, ref textRange, location, ref layout); - } - else - { - return font.HitTestText(text, ref textRange, location, ref layout); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Float2 GetCharPosition(Font font, string text, int index, bool useFallback = true) - { - if (Fallbacks != null && useFallback) - { - return font.GetCharPosition(Fallbacks, text, index); - } - else - { - return font.GetCharPosition(text, index); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Float2 GetCharPosition(Font font, string text, ref TextRange textRange, int index, bool useFallback = true) - { - if (Fallbacks != null && useFallback) - { - return font.GetCharPosition(Fallbacks, text, ref textRange, index); - } - else - { - return font.GetCharPosition(text, ref textRange, index); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Float2 GetCharPosition(Font font, string text, int index, ref TextLayoutOptions layout, bool useFallback = true) - { - if (Fallbacks != null && useFallback) - { - return font.GetCharPosition(Fallbacks, text, index, ref layout); - } - else - { - return font.GetCharPosition(text, index, ref layout); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Float2 GetCharPosition(Font font, string text, ref TextRange textRange, int index, ref TextLayoutOptions layout, bool useFallback = true) - { - if (Fallbacks != null && useFallback) - { - return font.GetCharPosition(Fallbacks, text, ref textRange, index, ref layout); - } - else - { - return font.GetCharPosition(text, ref textRange, index, ref layout); - } - } - - /// - /// Gets the max font height among the font and all fallback fonts of the same size. - /// - /// The primary font to use. - /// The fallback fonts. - /// The max height. - public static float GetMaxHeight(Font font, FallbackFonts fallbacks) - { - float height = font.Height; - - var fallbackFonts = fallbacks.GetFontList(font.Size); - foreach (var item in fallbackFonts) - { - height = Mathf.Max(height, item.Height); - } - - return height; - } - - /// - /// Gets the max font height among the font and all fallback fonts of the same size. - /// - /// The primary font to use. - /// Whether to enable fallback fonts, uses if true. - /// The max height. - public static float GetMaxHeight(Font font, bool useFallback = true) - { - if(Fallbacks != null && useFallback) - { - return GetMaxHeight(font, Fallbacks); - } - else - { - return font.Height; - } - } - } -} diff --git a/Source/Engine/Render2D/Font.cpp b/Source/Engine/Render2D/Font.cpp index 5d029ca5e..9f28aac01 100644 --- a/Source/Engine/Render2D/Font.cpp +++ b/Source/Engine/Render2D/Font.cpp @@ -103,6 +103,18 @@ void Font::Invalidate() _characters.Clear(); } +inline API_FUNCTION() float Font::GetMaxHeight(FontFallbackList* fallbacks) const +{ + float height = GetHeight(); + auto& fallbackFonts = fallbacks->GetFontList(GetSize()); + for (int32 i = 0; i < fallbackFonts.Count(); i++) + { + height = Math::Max(height, static_cast(fallbackFonts[i]->GetHeight())); + } + + return height; +} + void Font::ProcessText(const StringView& text, Array& outputLines, const TextLayoutOptions& layout) { float cursorX = 0; @@ -548,7 +560,7 @@ void Font::ProcessText(FontFallbackList* fallbacks, const StringView& text, Arra } } -Float2 Font::MeasureText(const StringView& text, const TextLayoutOptions& layout) +Float2 Font::MeasureTextInternal(const StringView& text, const TextLayoutOptions& layout) { // Check if there is no need to do anything if (text.IsEmpty()) @@ -569,7 +581,7 @@ Float2 Font::MeasureText(const StringView& text, const TextLayoutOptions& layout return max; } -Float2 Font::MeasureText(FontFallbackList* fallbacks, const StringView& text, const TextLayoutOptions& layout) +Float2 Font::MeasureTextInternal(FontFallbackList* fallbacks, const StringView& text, const TextLayoutOptions& layout) { // Check if there is no need to do anything if (text.IsEmpty()) @@ -590,7 +602,7 @@ Float2 Font::MeasureText(FontFallbackList* fallbacks, const StringView& text, co return max; } -int32 Font::HitTestText(const StringView& text, const Float2& location, const TextLayoutOptions& layout) +int32 Font::HitTestTextInternal(const StringView& text, const Float2& location, const TextLayoutOptions& layout) { // Check if there is no need to do anything if (text.Length() <= 0) @@ -664,7 +676,7 @@ int32 Font::HitTestText(const StringView& text, const Float2& location, const Te return smallestIndex; } -int32 Font::HitTestText(FontFallbackList* fallbacks, const StringView& text, const Float2& location, const TextLayoutOptions& layout) +int32 Font::HitTestTextInternal(FontFallbackList* fallbacks, const StringView& text, const Float2& location, const TextLayoutOptions& layout) { // Check if there is no need to do anything if (text.Length() <= 0) @@ -764,7 +776,7 @@ int32 Font::HitTestText(FontFallbackList* fallbacks, const StringView& text, con return smallestIndex; } -Float2 Font::GetCharPosition(const StringView& text, int32 index, const TextLayoutOptions& layout) +Float2 Font::GetCharPositionInternal(const StringView& text, int32 index, const TextLayoutOptions& layout) { // Check if there is no need to do anything if (text.IsEmpty()) @@ -818,7 +830,7 @@ Float2 Font::GetCharPosition(const StringView& text, int32 index, const TextLayo return rootOffset + Float2(lines.Last().Location.X + lines.Last().Size.X, static_cast((lines.Count() - 1) * baseLinesDistance)); } -Float2 Font::GetCharPosition(FontFallbackList* fallbacks, const StringView& text, int32 index, const TextLayoutOptions& layout) +Float2 Font::GetCharPositionInternal(FontFallbackList* fallbacks, const StringView& text, int32 index, const TextLayoutOptions& layout) { // Check if there is no need to do anything if (text.IsEmpty()) diff --git a/Source/Engine/Render2D/Font.h b/Source/Engine/Render2D/Font.h index 6bb2849e2..1fb41d032 100644 --- a/Source/Engine/Render2D/Font.h +++ b/Source/Engine/Render2D/Font.h @@ -8,6 +8,7 @@ #include "Engine/Content/AssetReference.h" #include "Engine/Scripting/ScriptingObject.h" #include "TextLayoutOptions.h" +#include "Render2D.h" class FontAsset; class FontFallbackList; @@ -402,7 +403,31 @@ public: public: /// - /// Processes text to get cached lines for rendering. + /// Gets the maximum height among the font and the fallback fonts. + /// + /// The fallback fonts. + /// The maximum height. + API_FUNCTION() float GetMaxHeight(FontFallbackList* fallbacks) const; + + /// + /// Gets the maximum height among the font and the fallback fonts, uses the default font defined in . + /// + /// The fallback fonts. + /// The maximum height. + API_FUNCTION() FORCE_INLINE float GetMaxHeight() const + { + if (Render2D::EnableFontFallback && Render2D::FallbackFonts) + { + return GetMaxHeight(Render2D::FallbackFonts); + } + else + { + return GetHeight(); + } + } + + /// + /// Processes text to get cached lines for rendering, with font fallbacking disabled. /// /// The input text. /// The layout properties. @@ -410,12 +435,12 @@ public: void ProcessText(const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout); /// - /// Processes text to get cached lines for rendering. + /// Processes text to get cached lines for rendering, with font fallbacking disabled. /// /// The input text. /// The layout properties. /// The output lines list. - API_FUNCTION() Array ProcessText(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout) + API_FUNCTION() FORCE_INLINE Array ProcessText(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout) { Array lines; ProcessText(text, lines, layout); @@ -423,13 +448,13 @@ public: } /// - /// Processes text to get cached lines for rendering. + /// Processes text to get cached lines for rendering, with font fallbacking disabled. /// /// The input text. /// The input text range (substring range of the input text parameter). /// The layout properties. /// The output lines list. - API_FUNCTION() Array ProcessText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) + API_FUNCTION() FORCE_INLINE Array ProcessText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) { Array lines; ProcessText(textRange.Substring(text), lines, layout); @@ -437,7 +462,7 @@ public: } /// - /// Processes text to get cached lines for rendering. + /// Processes text to get cached lines for rendering, with font fallbacking disabled. /// /// The input text. /// The output lines list. @@ -447,7 +472,7 @@ public: } /// - /// Processes text to get cached lines for rendering. + /// Processes text to get cached lines for rendering, with font fallbacking disabled. /// /// The input text. /// The input text range (substring range of the input text parameter). @@ -458,7 +483,7 @@ public: } /// - /// Processes text to get cached lines for rendering. + /// Processes text to get cached lines for rendering, using custom fallback options. /// /// The input text. /// The layout properties. @@ -466,12 +491,12 @@ public: void ProcessText(FontFallbackList* fallbacks, const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout); /// - /// Processes text to get cached lines for rendering. + /// Processes text to get cached lines for rendering, using custom fallback options. /// /// The input text. /// The layout properties. /// The output lines list. - API_FUNCTION() Array ProcessText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout) + API_FUNCTION() FORCE_INLINE Array ProcessText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout) { Array lines; ProcessText(fallbacks, text, lines, layout); @@ -479,13 +504,13 @@ public: } /// - /// Processes text to get cached lines for rendering. + /// Processes text to get cached lines for rendering, using custom fallback options. /// /// The input text. /// The input text range (substring range of the input text parameter). /// The layout properties. /// The output lines list. - API_FUNCTION() Array ProcessText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) + API_FUNCTION() FORCE_INLINE Array ProcessText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) { Array lines; ProcessText(fallbacks, textRange.Substring(text), lines, layout); @@ -493,7 +518,7 @@ public: } /// - /// Processes text to get cached lines for rendering. + /// Processes text to get cached lines for rendering, using custom fallback options. /// /// The input text. /// The output lines list. @@ -503,7 +528,7 @@ public: } /// - /// Processes text to get cached lines for rendering. + /// Processes text to get cached lines for rendering, using custom fallback options. /// /// The input text. /// The input text range (substring range of the input text parameter). @@ -514,122 +539,293 @@ public: } /// - /// Measures minimum size of the rectangle that will be needed to draw given text. + /// Measures minimum size of the rectangle that will be needed to draw given text, with font fallbacking disabled. /// /// The input text to test. /// The layout properties. /// The minimum size for that text and fot to render properly. - API_FUNCTION() Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout); + API_FUNCTION() Float2 MeasureTextInternal(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout); /// - /// Measures minimum size of the rectangle that will be needed to draw given text. + /// Measures minimum size of the rectangle that will be needed to draw given text, with font fallbacking disabled. /// /// The input text to test. /// The input text range (substring range of the input text parameter). /// The layout properties. /// The minimum size for that text and fot to render properly. - API_FUNCTION() Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) + API_FUNCTION() FORCE_INLINE Float2 MeasureTextInternal(const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) { - return MeasureText(textRange.Substring(text), layout); + return MeasureTextInternal(textRange.Substring(text), layout); } /// - /// Measures minimum size of the rectangle that will be needed to draw given text + /// Measures minimum size of the rectangle that will be needed to draw given text, with font fallbacking disabled. + /// . + /// The input text to test. + /// The minimum size for that text and fot to render properly. + API_FUNCTION() FORCE_INLINE Float2 MeasureTextInternal(const StringView& text) + { + return MeasureTextInternal(text, TextLayoutOptions()); + } + + /// + /// Measures minimum size of the rectangle that will be needed to draw given text, with font fallbacking disabled. + /// . + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The minimum size for that text and fot to render properly. + API_FUNCTION() FORCE_INLINE Float2 MeasureTextInternal(const StringView& text, API_PARAM(Ref) const TextRange& textRange) + { + return MeasureTextInternal(textRange.Substring(text), TextLayoutOptions()); + } + + /// + /// Measures minimum size of the rectangle that will be needed to draw given text, using custom fallback options. + /// + /// The input text to test. + /// The layout properties. + /// The minimum size for that text and fot to render properly. + API_FUNCTION() Float2 MeasureTextInternal(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout); + + /// + /// Measures minimum size of the rectangle that will be needed to draw given text, using custom fallback options. + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The layout properties. + /// The minimum size for that text and fot to render properly. + API_FUNCTION() FORCE_INLINE Float2 MeasureTextInternal(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) + { + return MeasureTextInternal(fallbacks, textRange.Substring(text), layout); + } + + /// + /// Measures minimum size of the rectangle that will be needed to draw given text, using custom fallback options. + /// . + /// The input text to test. + /// The minimum size for that text and fot to render properly. + API_FUNCTION() FORCE_INLINE Float2 MeasureTextInternal(FontFallbackList* fallbacks, const StringView& text) + { + return MeasureTextInternal(fallbacks, text, TextLayoutOptions()); + } + + /// + /// Measures minimum size of the rectangle that will be needed to draw given text, using custom fallback options. + /// . + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The minimum size for that text and fot to render properly. + API_FUNCTION() FORCE_INLINE Float2 MeasureTextInternal(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange) + { + return MeasureTextInternal(fallbacks, textRange.Substring(text), TextLayoutOptions()); + } + + /// + /// Measures minimum size of the rectangle that will be needed to draw given text, follows the fallback settings defined in . + /// + /// The input text to test. + /// The layout properties. + /// The minimum size for that text and fot to render properly. + API_FUNCTION() FORCE_INLINE Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout) { + if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { + return MeasureTextInternal(Render2D::FallbackFonts, text, layout); + } + else { + return MeasureTextInternal(text, layout); + } + } + + /// + /// Measures minimum size of the rectangle that will be needed to draw given text, follows the fallback settings defined in . + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The layout properties. + /// The minimum size for that text and fot to render properly. + API_FUNCTION() FORCE_INLINE Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) + { + if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { + return MeasureTextInternal(Render2D::FallbackFonts, textRange.Substring(text), layout); + } + else { + return MeasureTextInternal(textRange.Substring(text), layout); + } + } + + /// + /// Measures minimum size of the rectangle that will be needed to draw given text, follows the fallback settings defined in . /// . /// The input text to test. /// The minimum size for that text and fot to render properly. API_FUNCTION() FORCE_INLINE Float2 MeasureText(const StringView& text) { - return MeasureText(text, TextLayoutOptions()); + if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { + return MeasureTextInternal(Render2D::FallbackFonts, text, TextLayoutOptions()); + } + else { + return MeasureTextInternal(text, TextLayoutOptions()); + } } /// - /// Measures minimum size of the rectangle that will be needed to draw given text + /// Measures minimum size of the rectangle that will be needed to draw given text, follows the fallback settings defined in . /// . /// The input text to test. /// The input text range (substring range of the input text parameter). /// The minimum size for that text and fot to render properly. API_FUNCTION() FORCE_INLINE Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextRange& textRange) { - return MeasureText(textRange.Substring(text), TextLayoutOptions()); + if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { + return MeasureTextInternal(Render2D::FallbackFonts, textRange.Substring(text), TextLayoutOptions()); + } + else { + return MeasureTextInternal(textRange.Substring(text), TextLayoutOptions()); + } } /// - /// Measures minimum size of the rectangle that will be needed to draw given text. - /// - /// The input text to test. - /// The layout properties. - /// The minimum size for that text and fot to render properly. - API_FUNCTION() Float2 MeasureText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout); - - /// - /// Measures minimum size of the rectangle that will be needed to draw given text. - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The layout properties. - /// The minimum size for that text and fot to render properly. - API_FUNCTION() Float2 MeasureText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) - { - return MeasureText(fallbacks, textRange.Substring(text), layout); - } - - /// - /// Measures minimum size of the rectangle that will be needed to draw given text - /// . - /// The input text to test. - /// The minimum size for that text and fot to render properly. - API_FUNCTION() FORCE_INLINE Float2 MeasureText(FontFallbackList* fallbacks, const StringView& text) - { - return MeasureText(fallbacks, text, TextLayoutOptions()); - } - - /// - /// Measures minimum size of the rectangle that will be needed to draw given text - /// . - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The minimum size for that text and fot to render properly. - API_FUNCTION() FORCE_INLINE Float2 MeasureText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange) - { - return MeasureText(fallbacks, textRange.Substring(text), TextLayoutOptions()); - } - - /// - /// Calculates hit character index at given location. + /// Calculates hit character index at given location, with font fallbacking disabled. /// /// The input text to test. /// The input location to test. /// The text layout properties. /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() int32 HitTestText(const StringView& text, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout); + API_FUNCTION() int32 HitTestTextInternal(const StringView& text, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout); /// - /// Calculates hit character index at given location. + /// Calculates hit character index at given location, with font fallbacking disabled. /// /// The input text to test. /// The input text range (substring range of the input text parameter). /// The input location to test. /// The text layout properties. /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() int32 HitTestText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout) + API_FUNCTION() FORCE_INLINE int32 HitTestTextInternal(const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout) { - return HitTestText(textRange.Substring(text), location, layout); + return HitTestTextInternal(textRange.Substring(text), location, layout); } /// - /// Calculates hit character index at given location. + /// Calculates hit character index at given location, with font fallbacking disabled. + /// + /// The input text to test. + /// The input location to test. + /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). + API_FUNCTION() FORCE_INLINE int32 HitTestTextInternal(const StringView& text, const Float2& location) + { + return HitTestTextInternal(text, location, TextLayoutOptions()); + } + + /// + /// Calculates hit character index at given location, with font fallbacking disabled. + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The input location to test. + /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). + API_FUNCTION() FORCE_INLINE int32 HitTestTextInternal(const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location) + { + return HitTestTextInternal(textRange.Substring(text), location, TextLayoutOptions()); + } + + /// + /// Calculates hit character index at given location, using custom fallback options. + /// + /// The input text to test. + /// The input location to test. + /// The text layout properties. + /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). + API_FUNCTION() int32 HitTestTextInternal(FontFallbackList* fallbacks, const StringView& text, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout); + + /// + /// Calculates hit character index at given location, using custom fallback options. + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The input location to test. + /// The text layout properties. + /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). + API_FUNCTION() FORCE_INLINE int32 HitTestTextInternal(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout) + { + return HitTestTextInternal(fallbacks, textRange.Substring(text), location, layout); + } + + /// + /// Calculates hit character index at given location, using custom fallback options. + /// + /// The input text to test. + /// The input location to test. + /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). + API_FUNCTION() FORCE_INLINE int32 HitTestTextInternal(FontFallbackList* fallbacks, const StringView& text, const Float2& location) + { + return HitTestTextInternal(fallbacks, text, location, TextLayoutOptions()); + } + + /// + /// Calculates hit character index at given location, using custom fallback options. + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The input location to test. + /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). + API_FUNCTION() FORCE_INLINE int32 HitTestTextInternal(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location) + { + return HitTestTextInternal(fallbacks, textRange.Substring(text), location, TextLayoutOptions()); + } + + /// + /// Calculates hit character index at given location, follows the fallback settings defined in . + /// + /// The input text to test. + /// The input location to test. + /// The text layout properties. + /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). + API_FUNCTION() FORCE_INLINE int32 HitTestText(const StringView& text, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout) { + if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { + return HitTestTextInternal(Render2D::FallbackFonts, text, location, layout); + } + else { + return HitTestTextInternal(text, location, layout); + } + } + + /// + /// Calculates hit character index at given location, follows the fallback settings defined in . + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The input location to test. + /// The text layout properties. + /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). + API_FUNCTION() FORCE_INLINE int32 HitTestText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout) + { + if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { + return HitTestTextInternal(Render2D::FallbackFonts, textRange.Substring(text), location, layout); + } + else { + return HitTestTextInternal(textRange.Substring(text), location, layout); + } + + } + + /// + /// Calculates hit character index at given location, follows the fallback settings defined in . /// /// The input text to test. /// The input location to test. /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). API_FUNCTION() FORCE_INLINE int32 HitTestText(const StringView& text, const Float2& location) { - return HitTestText(text, location, TextLayoutOptions()); + if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { + return HitTestTextInternal(Render2D::FallbackFonts, text, location, TextLayoutOptions()); + } + else { + return HitTestTextInternal(text, location, TextLayoutOptions()); + } } /// - /// Calculates hit character index at given location. + /// Calculates hit character index at given location, follows the fallback settings defined in . /// /// The input text to test. /// The input text range (substring range of the input text parameter). @@ -637,89 +833,156 @@ public: /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). API_FUNCTION() FORCE_INLINE int32 HitTestText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location) { - return HitTestText(textRange.Substring(text), location, TextLayoutOptions()); + if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { + return HitTestTextInternal(Render2D::FallbackFonts, textRange.Substring(text), location, TextLayoutOptions()); + } + else { + return HitTestTextInternal(textRange.Substring(text), location, TextLayoutOptions()); + } } /// - /// Calculates hit character index at given location. - /// - /// The input text to test. - /// The input location to test. - /// The text layout properties. - /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() int32 HitTestText(FontFallbackList* fallbacks, const StringView& text, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout); - - /// - /// Calculates hit character index at given location. - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The input location to test. - /// The text layout properties. - /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() int32 HitTestText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout) - { - return HitTestText(fallbacks, textRange.Substring(text), location, layout); - } - - /// - /// Calculates hit character index at given location. - /// - /// The input text to test. - /// The input location to test. - /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() FORCE_INLINE int32 HitTestText(FontFallbackList* fallbacks, const StringView& text, const Float2& location) - { - return HitTestText(fallbacks, text, location, TextLayoutOptions()); - } - - /// - /// Calculates hit character index at given location. - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The input location to test. - /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() FORCE_INLINE int32 HitTestText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location) - { - return HitTestText(fallbacks, textRange.Substring(text), location, TextLayoutOptions()); - } - - /// - /// Calculates character position for given text and character index. + /// Calculates character position for given text and character index, with font fallbacking disabled. /// /// The input text to test. /// The text position to get coordinates of. /// The text layout properties. /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() Float2 GetCharPosition(const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout); + API_FUNCTION() Float2 GetCharPositionInternal(const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout); /// - /// Calculates character position for given text and character index. + /// Calculates character position for given text and character index, with font fallbacking disabled. /// /// The input text to test. /// The input text range (substring range of the input text parameter). /// The text position to get coordinates of. /// The text layout properties. /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() Float2 GetCharPosition(const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout) + API_FUNCTION() FORCE_INLINE Float2 GetCharPositionInternal(const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout) { - return GetCharPosition(textRange.Substring(text), index, layout); + return GetCharPositionInternal(textRange.Substring(text), index, layout); } /// - /// Calculates character position for given text and character index + /// Calculates character position for given text and character index, with font fallbacking disabled. + /// + /// The input text to test. + /// The text position to get coordinates of. + /// The character position (upper left corner which can be used for a caret position). + API_FUNCTION() FORCE_INLINE Float2 GetCharPositionInternal(const StringView& text, int32 index) + { + return GetCharPositionInternal(text, index, TextLayoutOptions()); + } + + /// + /// Calculates character position for given text and character index, with font fallbacking disabled. + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The text position to get coordinates of. + /// The character position (upper left corner which can be used for a caret position). + API_FUNCTION() FORCE_INLINE Float2 GetCharPositionInternal(const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index) + { + return GetCharPositionInternal(textRange.Substring(text), index, TextLayoutOptions()); + } + + /// + /// Calculates character position for given text and character index, using custom fallback options. + /// + /// The input text to test. + /// The text position to get coordinates of. + /// The text layout properties. + /// The character position (upper left corner which can be used for a caret position). + API_FUNCTION() Float2 GetCharPositionInternal(FontFallbackList* fallbacks, const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout); + + /// + /// Calculates character position for given text and character index, using custom fallback options. + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The text position to get coordinates of. + /// The text layout properties. + /// The character position (upper left corner which can be used for a caret position). + API_FUNCTION() FORCE_INLINE Float2 GetCharPositionInternal(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout) + { + return GetCharPositionInternal(fallbacks, textRange.Substring(text), index, layout); + } + + /// + /// Calculates character position for given text and character index, using custom fallback options. + /// + /// The input text to test. + /// The text position to get coordinates of. + /// The character position (upper left corner which can be used for a caret position). + API_FUNCTION() FORCE_INLINE Float2 GetCharPositionInternal(FontFallbackList* fallbacks, const StringView& text, int32 index) + { + return GetCharPositionInternal(fallbacks, text, index, TextLayoutOptions()); + } + + /// + /// Calculates character position for given text and character index, using custom fallback options. + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The text position to get coordinates of. + /// The character position (upper left corner which can be used for a caret position). + API_FUNCTION() FORCE_INLINE Float2 GetCharPositionInternal(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index) + { + return GetCharPositionInternal(fallbacks, textRange.Substring(text), index, TextLayoutOptions()); + } + + /// + /// Calculates character position for given text and character index, follows the fallback settings defined in . + /// + /// The input text to test. + /// The text position to get coordinates of. + /// The text layout properties. + /// The character position (upper left corner which can be used for a caret position). + API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout) { + if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { + return GetCharPositionInternal(Render2D::FallbackFonts, text, index, layout); + } + else { + return GetCharPositionInternal(text, index, layout); + } + } + + /// + /// Calculates character position for given text and character index, follows the fallback settings defined in . + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The text position to get coordinates of. + /// The text layout properties. + /// The character position (upper left corner which can be used for a caret position). + API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout) + { + if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { + return GetCharPositionInternal(Render2D::FallbackFonts, textRange.Substring(text), index, layout); + } + else { + return GetCharPositionInternal(textRange.Substring(text), index, layout); + } + } + + /// + /// Calculates character position for given text and character index, follows the fallback settings defined in . /// /// The input text to test. /// The text position to get coordinates of. /// The character position (upper left corner which can be used for a caret position). API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(const StringView& text, int32 index) { - return GetCharPosition(text, index, TextLayoutOptions()); + if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { + return GetCharPositionInternal(Render2D::FallbackFonts, text, index, TextLayoutOptions()); + } + else { + return GetCharPositionInternal(text, index, TextLayoutOptions()); + } } /// - /// Calculates character position for given text and character index + /// Calculates character position for given text and character index, follows the fallback settings defined in . /// /// The input text to test. /// The input text range (substring range of the input text parameter). @@ -727,52 +990,12 @@ public: /// The character position (upper left corner which can be used for a caret position). API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index) { - return GetCharPosition(textRange.Substring(text), index, TextLayoutOptions()); - } - - /// - /// Calculates character position for given text and character index. - /// - /// The input text to test. - /// The text position to get coordinates of. - /// The text layout properties. - /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() Float2 GetCharPosition(FontFallbackList* fallbacks, const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout); - - /// - /// Calculates character position for given text and character index. - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The text position to get coordinates of. - /// The text layout properties. - /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() Float2 GetCharPosition(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout) - { - return GetCharPosition(fallbacks, textRange.Substring(text), index, layout); - } - - /// - /// Calculates character position for given text and character index - /// - /// The input text to test. - /// The text position to get coordinates of. - /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(FontFallbackList* fallbacks, const StringView& text, int32 index) - { - return GetCharPosition(fallbacks, text, index, TextLayoutOptions()); - } - - /// - /// Calculates character position for given text and character index - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The text position to get coordinates of. - /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index) - { - return GetCharPosition(fallbacks, textRange.Substring(text), index, TextLayoutOptions()); + if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { + return GetCharPositionInternal(Render2D::FallbackFonts, textRange.Substring(text), index, TextLayoutOptions()); + } + else { + return GetCharPositionInternal(textRange.Substring(text), index, TextLayoutOptions()); + } } /// diff --git a/Source/Engine/Render2D/Render2D.cpp b/Source/Engine/Render2D/Render2D.cpp index 351bb3700..fa52baff0 100644 --- a/Source/Engine/Render2D/Render2D.cpp +++ b/Source/Engine/Render2D/Render2D.cpp @@ -182,6 +182,8 @@ struct ClipMask }; Render2D::RenderingFeatures Render2D::Features = RenderingFeatures::VertexSnapping; +bool Render2D::EnableFontFallback = true; +FontFallbackList* Render2D::FallbackFonts = nullptr; namespace { diff --git a/Source/Engine/Render2D/Render2D.cs b/Source/Engine/Render2D/Render2D.cs index 6e4f1dc1d..b36f155df 100644 --- a/Source/Engine/Render2D/Render2D.cs +++ b/Source/Engine/Render2D/Render2D.cs @@ -102,7 +102,7 @@ namespace FlaxEngine } /// - /// Draws a text. + /// Draws a text, follows the font fallback settings defined in . /// /// The font to use. /// The text to render. @@ -128,7 +128,7 @@ namespace FlaxEngine } /// - /// Draws a text using a custom material shader. Given material must have GUI domain and a public parameter named Font (texture parameter used for a font atlas sampling). + /// Draws a text using a custom material shader. Given material must have GUI domain and a public parameter named Font (texture parameter used for a font atlas sampling). Follows the font fallback settings defined in . /// /// The font to use. /// Custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. diff --git a/Source/Engine/Render2D/Render2D.h b/Source/Engine/Render2D/Render2D.h index fa59b1d79..5813d6f87 100644 --- a/Source/Engine/Render2D/Render2D.h +++ b/Source/Engine/Render2D/Render2D.h @@ -54,8 +54,6 @@ API_CLASS(Static) class FLAXENGINE_API Render2D }; public: - API_FIELD() static bool EnableFontFallback; - API_FIELD() static FontFallbackList* FallbackFonts; /// /// Checks if interface is during rendering phrase (Draw calls may be performed without failing). @@ -72,6 +70,10 @@ public: /// API_FIELD() static RenderingFeatures Features; + API_FIELD() static bool EnableFontFallback; + + API_FIELD() static FontFallbackList* FallbackFonts; + /// /// Called when frame rendering begins by the graphics device. /// diff --git a/Source/Engine/UI/GUI/Common/Button.cs b/Source/Engine/UI/GUI/Common/Button.cs index feb34418b..02337411b 100644 --- a/Source/Engine/UI/GUI/Common/Button.cs +++ b/Source/Engine/UI/GUI/Common/Button.cs @@ -261,7 +261,7 @@ namespace FlaxEngine.GUI Render2D.DrawRectangle(clientRect, borderColor, BorderThickness); // Draw text - FallbackTextUtils.DrawText(_font?.GetFont(), TextMaterial, _text, clientRect, textColor, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(_font?.GetFont(), TextMaterial, _text, clientRect, textColor, TextAlignment.Center, TextAlignment.Center); } /// diff --git a/Source/Engine/UI/GUI/Common/Dropdown.cs b/Source/Engine/UI/GUI/Common/Dropdown.cs index 3b298b136..ecca2978f 100644 --- a/Source/Engine/UI/GUI/Common/Dropdown.cs +++ b/Source/Engine/UI/GUI/Common/Dropdown.cs @@ -475,7 +475,7 @@ namespace FlaxEngine.GUI var font = Font.GetFont(); for (int i = 0; i < _items.Count; i++) { - itemsWidth = Mathf.Max(itemsWidth, itemsMargin + 4 + FallbackTextUtils.MeasureText(font, _items[i]).X); + itemsWidth = Mathf.Max(itemsWidth, itemsMargin + 4 + font.MeasureText(_items[i]).X); } */ var itemsWidth = Width; @@ -673,7 +673,7 @@ namespace FlaxEngine.GUI var textRect = new Rectangle(margin, 0, clientRect.Width - boxSize - 2.0f * margin, clientRect.Height); Render2D.PushClip(textRect); var textColor = TextColor; - FallbackTextUtils.DrawText(Font.GetFont(), FontMaterial, _items[_selectedIndex], textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(Font.GetFont(), FontMaterial, _items[_selectedIndex], textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center); Render2D.PopClip(); } diff --git a/Source/Engine/UI/GUI/Common/Label.cs b/Source/Engine/UI/GUI/Common/Label.cs index 3d1caad28..3c7c04fb2 100644 --- a/Source/Engine/UI/GUI/Common/Label.cs +++ b/Source/Engine/UI/GUI/Common/Label.cs @@ -233,7 +233,7 @@ namespace FlaxEngine.GUI } } - FallbackTextUtils.DrawText(_font.GetFont(), Material, _text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale); + Render2D.DrawText(_font.GetFont(), Material, _text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale); if (ClipText) Render2D.PopClip(); @@ -254,7 +254,7 @@ namespace FlaxEngine.GUI layout.Bounds.Size.X = Width - Margin.Width; else if (_autoWidth && !_autoHeight) layout.Bounds.Size.Y = Height - Margin.Height; - _textSize = FallbackTextUtils.MeasureText(font, _text, ref layout); + _textSize = font.MeasureText(_text, ref layout); _textSize.Y *= BaseLinesGapScale; // Check if size is controlled via text diff --git a/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs b/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs index 8d7858af0..46f0fb1ad 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs @@ -154,7 +154,7 @@ namespace FlaxEngine.GUI if (!font) break; height = font.Height / DpiScale; - return textBlock.Bounds.Location + FallbackTextUtils.GetCharPosition(font, _text, ref textBlock.Range, index - textBlock.Range.StartIndex); + return textBlock.Bounds.Location + font.GetCharPosition(_text, ref textBlock.Range, index - textBlock.Range.StartIndex); } } @@ -196,7 +196,7 @@ namespace FlaxEngine.GUI var font = textBlock.Style.Font.GetFont(); if (!font && textBlock.Range.Length > 0) break; - return FallbackTextUtils.HitTestText(font, _text, ref textBlock.Range, location - textBlock.Bounds.Location) + textBlock.Range.StartIndex; + return font.HitTestText(_text, ref textBlock.Range, location - textBlock.Bounds.Location) + textBlock.Range.StartIndex; } } @@ -288,8 +288,8 @@ namespace FlaxEngine.GUI // Selection if (hasSelection && textBlock.Style.BackgroundSelectedBrush != null && textBlock.Range.Intersect(ref selection)) { - var leftEdge = selection.StartIndex <= textBlock.Range.StartIndex ? textBlock.Bounds.UpperLeft : FallbackTextUtils.GetCharPosition(font, _text, selection.StartIndex); - var rightEdge = selection.EndIndex >= textBlock.Range.EndIndex ? textBlock.Bounds.UpperRight : FallbackTextUtils.GetCharPosition(font, _text, selection.EndIndex); + var leftEdge = selection.StartIndex <= textBlock.Range.StartIndex ? textBlock.Bounds.UpperLeft : font.GetCharPosition(_text, selection.StartIndex); + var rightEdge = selection.EndIndex >= textBlock.Range.EndIndex ? textBlock.Bounds.UpperRight : font.GetCharPosition(_text, selection.EndIndex); float height = font.Height / DpiScale; float alpha = Mathf.Min(1.0f, Mathf.Cos(_animateTime * BackgroundSelectedFlashSpeed) * 0.5f + 1.3f); alpha *= alpha; diff --git a/Source/Engine/UI/GUI/Common/TextBox.cs b/Source/Engine/UI/GUI/Common/TextBox.cs index b1861df56..3c0d55008 100644 --- a/Source/Engine/UI/GUI/Common/TextBox.cs +++ b/Source/Engine/UI/GUI/Common/TextBox.cs @@ -104,7 +104,7 @@ namespace FlaxEngine.GUI return Float2.Zero; } - return FallbackTextUtils.MeasureText(font, _text, ref _layout); + return font.MeasureText(_text, ref _layout); } /// @@ -117,8 +117,8 @@ namespace FlaxEngine.GUI return Float2.Zero; } - height = FallbackTextUtils.GetMaxHeight(font) / DpiScale; - return FallbackTextUtils.GetCharPosition(font, _text, index, ref _layout); + height = font.GetMaxHeight() / DpiScale; + return font.GetCharPosition(_text, index, ref _layout); } /// @@ -130,7 +130,7 @@ namespace FlaxEngine.GUI return 0; } - return FallbackTextUtils.HitTestText(font, _text, location, ref _layout); + return font.HitTestText(_text, location, ref _layout); } /// @@ -169,9 +169,9 @@ namespace FlaxEngine.GUI // Check if sth is selected to draw selection if (HasSelection) { - var leftEdge = FallbackTextUtils.GetCharPosition(font, _text, SelectionLeft, ref _layout); - var rightEdge = FallbackTextUtils.GetCharPosition(font, _text, SelectionRight, ref _layout); - float fontHeight = FallbackTextUtils.GetMaxHeight(font) / DpiScale; + var leftEdge = font.GetCharPosition(_text, SelectionLeft, ref _layout); + var rightEdge = font.GetCharPosition(_text, SelectionRight, ref _layout); + float fontHeight = font.GetMaxHeight() / DpiScale; // Draw selection background float alpha = Mathf.Min(1.0f, Mathf.Cos(_animateTime * BackgroundSelectedFlashSpeed) * 0.5f + 1.3f); @@ -211,11 +211,11 @@ namespace FlaxEngine.GUI var color = TextColor; if (!enabled) color *= 0.6f; - FallbackTextUtils.DrawText(font, _text, color, ref _layout, TextMaterial); + Render2D.DrawText(font, _text, color, ref _layout, TextMaterial); } else if (!string.IsNullOrEmpty(_watermarkText) && !IsFocused) { - FallbackTextUtils.DrawText(font, _watermarkText, WatermarkTextColor, ref _layout, TextMaterial); + Render2D.DrawText(font, _watermarkText, WatermarkTextColor, ref _layout, TextMaterial); } // Caret diff --git a/Source/Engine/UI/GUI/Panels/DropPanel.cs b/Source/Engine/UI/GUI/Panels/DropPanel.cs index 123e0f034..66e7413eb 100644 --- a/Source/Engine/UI/GUI/Panels/DropPanel.cs +++ b/Source/Engine/UI/GUI/Panels/DropPanel.cs @@ -374,7 +374,7 @@ namespace FlaxEngine.GUI textColor *= 0.6f; } - FallbackTextUtils.DrawText(HeaderTextFont.GetFont(), HeaderTextMaterial, HeaderText, textRect, textColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(HeaderTextFont.GetFont(), HeaderTextMaterial, HeaderText, textRect, textColor, TextAlignment.Near, TextAlignment.Center); if (!_isClosed && EnableContainmentLines) { diff --git a/Source/Engine/UI/GUI/Style.cs b/Source/Engine/UI/GUI/Style.cs index f4fca13eb..6092f0f72 100644 --- a/Source/Engine/UI/GUI/Style.cs +++ b/Source/Engine/UI/GUI/Style.cs @@ -69,12 +69,6 @@ namespace FlaxEngine.GUI set => _fontSmall = new FontReference(value); } - /// - /// The fallback fonts to use if the primary font can't render the char. - /// - [EditorOrder(50)] - public FallbackFonts Fallbacks; - /// /// The background color. /// diff --git a/Source/Engine/UI/GUI/Tooltip.cs b/Source/Engine/UI/GUI/Tooltip.cs index 41d06b017..8d35c21b9 100644 --- a/Source/Engine/UI/GUI/Tooltip.cs +++ b/Source/Engine/UI/GUI/Tooltip.cs @@ -234,7 +234,7 @@ namespace FlaxEngine.GUI Render2D.FillRectangle(new Rectangle(1.1f, 1.1f, Width - 2, Height - 2), style.Background); // Tooltip text - FallbackTextUtils.DrawText( + Render2D.DrawText( style.FontMedium, _currentText, GetClientArea(), From 6ab1663a1429591d0678bab1790e3e0b4a9628ca Mon Sep 17 00:00:00 2001 From: ExMatics HydrogenC <33123710+HydrogenC@users.noreply.github.com> Date: Sun, 3 Dec 2023 15:18:27 +0800 Subject: [PATCH 44/61] Add missing xml annotations --- Source/Engine/Core/Config/GraphicsSettings.h | 4 +-- Source/Engine/Render2D/Render2D.h | 26 ++++++++++++++++++++ Source/Engine/UI/GUI/Style.cs | 1 - Source/Engine/UI/GUI/Tooltip.cs | 1 - 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Core/Config/GraphicsSettings.h b/Source/Engine/Core/Config/GraphicsSettings.h index 7abfbb048..e109a55e4 100644 --- a/Source/Engine/Core/Config/GraphicsSettings.h +++ b/Source/Engine/Core/Config/GraphicsSettings.h @@ -121,13 +121,13 @@ public: PostProcessSettings PostProcessSettings; /// - /// + /// Whether to enable font fallbacking globally. /// API_FIELD(Attributes = "EditorOrder(12000), EditorDisplay(\"Text Render Settings\", EditorDisplayAttribute.InlineStyle)") bool EnableFontFallback = true; /// - /// + /// The fallback fonts used for text rendering, ignored if null. /// API_FIELD(Attributes = "EditorOrder(12005), EditorDisplay(\"Text Render Settings\", EditorDisplayAttribute.InlineStyle)") FontFallbackList* FallbackFonts; diff --git a/Source/Engine/Render2D/Render2D.h b/Source/Engine/Render2D/Render2D.h index 5813d6f87..6657d8542 100644 --- a/Source/Engine/Render2D/Render2D.h +++ b/Source/Engine/Render2D/Render2D.h @@ -281,6 +281,15 @@ public: } } + /// + /// Draws a text, follows the fallback settings defined in . + /// + /// The font to use. + /// The text to render. + /// The input text range (substring range of the input text parameter). + /// The text color. + /// The text location. + /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. API_FUNCTION() FORCE_INLINE static void DrawText(Font* font, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr) { if (EnableFontFallback && FallbackFonts) { DrawTextInternal(font, FallbackFonts, text, textRange, color, location, customMaterial); @@ -290,6 +299,14 @@ public: } } + /// + /// Draws a text with formatting, follows the fallback settings defined in . + /// + /// The font to use. + /// The text to render. + /// The text color. + /// The text layout properties. + /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. API_FUNCTION() FORCE_INLINE static void DrawText(Font* font, const StringView& text, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr) { if (EnableFontFallback && FallbackFonts) { DrawTextInternal(font, FallbackFonts, text, color, layout, customMaterial); @@ -299,6 +316,15 @@ public: } } + /// + /// Draws a text with formatting, follows the fallback settings defined in . + /// + /// The font to use. + /// The text to render. + /// The input text range (substring range of the input text parameter). + /// The text color. + /// The text layout properties. + /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. API_FUNCTION() FORCE_INLINE static void DrawText(Font* font, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr) { if (EnableFontFallback && FallbackFonts) { DrawTextInternal(font, FallbackFonts, text, textRange, color, layout, customMaterial); diff --git a/Source/Engine/UI/GUI/Style.cs b/Source/Engine/UI/GUI/Style.cs index 6092f0f72..22b8f52af 100644 --- a/Source/Engine/UI/GUI/Style.cs +++ b/Source/Engine/UI/GUI/Style.cs @@ -1,6 +1,5 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. - namespace FlaxEngine.GUI { /// diff --git a/Source/Engine/UI/GUI/Tooltip.cs b/Source/Engine/UI/GUI/Tooltip.cs index 8d35c21b9..734fb078f 100644 --- a/Source/Engine/UI/GUI/Tooltip.cs +++ b/Source/Engine/UI/GUI/Tooltip.cs @@ -243,7 +243,6 @@ namespace FlaxEngine.GUI TextAlignment.Center, TextWrapping.WrapWords ); - } /// From 360c75355c014bdb7be5051ea41460a777781ba4 Mon Sep 17 00:00:00 2001 From: ExMatics HydrogenC <33123710+HydrogenC@users.noreply.github.com> Date: Sun, 3 Dec 2023 16:04:50 +0800 Subject: [PATCH 45/61] Fix build error under non-windows platforms --- Source/Engine/Render2D/Font.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Render2D/Font.cpp b/Source/Engine/Render2D/Font.cpp index 9f28aac01..9a2037b3f 100644 --- a/Source/Engine/Render2D/Font.cpp +++ b/Source/Engine/Render2D/Font.cpp @@ -103,7 +103,7 @@ void Font::Invalidate() _characters.Clear(); } -inline API_FUNCTION() float Font::GetMaxHeight(FontFallbackList* fallbacks) const +float Font::GetMaxHeight(FontFallbackList* fallbacks) const { float height = GetHeight(); auto& fallbackFonts = fallbacks->GetFontList(GetSize()); From 4497b2ca7d5ee86f67d8ae19cd1db3220fe1464a Mon Sep 17 00:00:00 2001 From: ExMatics HydrogenC <33123710+HydrogenC@users.noreply.github.com> Date: Tue, 19 Dec 2023 12:36:12 +0800 Subject: [PATCH 46/61] Add fallback settings for control --- Source/Engine/UI/GUI/Common/Label.cs | 27 ++++++++++++++++-- Source/Engine/UI/GUI/Common/TextBox.cs | 39 ++++++++++++++++++++------ 2 files changed, 56 insertions(+), 10 deletions(-) diff --git a/Source/Engine/UI/GUI/Common/Label.cs b/Source/Engine/UI/GUI/Common/Label.cs index 3c7c04fb2..8de54fda3 100644 --- a/Source/Engine/UI/GUI/Common/Label.cs +++ b/Source/Engine/UI/GUI/Common/Label.cs @@ -182,6 +182,12 @@ namespace FlaxEngine.GUI set => _autoFitTextRange = value; } + /// + /// Gets or sets whether to fallback when the primary font cannot render a char. + /// + [EditorOrder(120), DefaultValue(true), Tooltip("Whether to fallback when the font cannot render a char.")] + public bool EnableFontFallback { get; set; } = true; + /// /// Initializes a new instance of the class. /// @@ -233,7 +239,23 @@ namespace FlaxEngine.GUI } } - Render2D.DrawText(_font.GetFont(), Material, _text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale); + if (EnableFontFallback) + { + Render2D.DrawText(_font.GetFont(), Material, _text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale); + } + else + { + var layout = new TextLayoutOptions + { + Bounds = rect, + HorizontalAlignment = hAlignment, + VerticalAlignment = wAlignment, + TextWrapping = Wrapping, + Scale = scale, + BaseLinesGapScale = BaseLinesGapScale, + }; + Render2D.DrawTextInternal(_font.GetFont(), _text, color, ref layout, Material); + } if (ClipText) Render2D.PopClip(); @@ -254,7 +276,8 @@ namespace FlaxEngine.GUI layout.Bounds.Size.X = Width - Margin.Width; else if (_autoWidth && !_autoHeight) layout.Bounds.Size.Y = Height - Margin.Height; - _textSize = font.MeasureText(_text, ref layout); + _textSize = EnableFontFallback ? + font.MeasureText(_text, ref layout) : font.MeasureTextInternal(_text, ref layout); _textSize.Y *= BaseLinesGapScale; // Check if size is controlled via text diff --git a/Source/Engine/UI/GUI/Common/TextBox.cs b/Source/Engine/UI/GUI/Common/TextBox.cs index 3c0d55008..53266a1b2 100644 --- a/Source/Engine/UI/GUI/Common/TextBox.cs +++ b/Source/Engine/UI/GUI/Common/TextBox.cs @@ -1,6 +1,8 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. +using System.ComponentModel; + namespace FlaxEngine.GUI { /// @@ -65,6 +67,12 @@ namespace FlaxEngine.GUI [EditorDisplay("Text Style"), EditorOrder(2022), Tooltip("The color of the selection (Transparent if not used).")] public Color SelectionColor { get; set; } + /// + /// Gets or sets whether to fallback when the primary font cannot render a char. + /// + [EditorOrder(120), DefaultValue(true), Tooltip("Whether to fallback when the font cannot render a char.")] + public bool EnableFontFallback { get; set; } = true; + /// /// Initializes a new instance of the class. /// @@ -104,7 +112,8 @@ namespace FlaxEngine.GUI return Float2.Zero; } - return font.MeasureText(_text, ref _layout); + return EnableFontFallback ? font.MeasureText(_text, ref _layout) : + font.MeasureTextInternal(_text, ref _layout); } /// @@ -117,8 +126,9 @@ namespace FlaxEngine.GUI return Float2.Zero; } - height = font.GetMaxHeight() / DpiScale; - return font.GetCharPosition(_text, index, ref _layout); + height = (EnableFontFallback ? font.GetMaxHeight() : font.Height) / DpiScale; + return EnableFontFallback ? font.GetCharPosition(_text, index, ref _layout) : + font.GetCharPositionInternal(_text, index, ref _layout); } /// @@ -130,7 +140,8 @@ namespace FlaxEngine.GUI return 0; } - return font.HitTestText(_text, location, ref _layout); + return EnableFontFallback ? font.HitTestText(_text, location, ref _layout) : + font.HitTestTextInternal(_text, location, ref _layout); } /// @@ -169,8 +180,12 @@ namespace FlaxEngine.GUI // Check if sth is selected to draw selection if (HasSelection) { - var leftEdge = font.GetCharPosition(_text, SelectionLeft, ref _layout); - var rightEdge = font.GetCharPosition(_text, SelectionRight, ref _layout); + var leftEdge = EnableFontFallback ? + font.GetCharPosition(_text, SelectionLeft, ref _layout) : + font.GetCharPositionInternal(_text, SelectionLeft, ref _layout); + var rightEdge = EnableFontFallback ? + font.GetCharPosition(_text, SelectionRight, ref _layout) : + font.GetCharPositionInternal(_text, SelectionRight, ref _layout); float fontHeight = font.GetMaxHeight() / DpiScale; // Draw selection background @@ -211,11 +226,19 @@ namespace FlaxEngine.GUI var color = TextColor; if (!enabled) color *= 0.6f; - Render2D.DrawText(font, _text, color, ref _layout, TextMaterial); + if (EnableFontFallback) + Render2D.DrawText(font, _text, color, ref _layout, TextMaterial); + else + // Draw without fallback + Render2D.DrawTextInternal(font, _text, color, ref _layout, TextMaterial); } else if (!string.IsNullOrEmpty(_watermarkText) && !IsFocused) { - Render2D.DrawText(font, _watermarkText, WatermarkTextColor, ref _layout, TextMaterial); + if (EnableFontFallback) + Render2D.DrawText(font, _watermarkText, WatermarkTextColor, ref _layout, TextMaterial); + else + // Draw without fallback + Render2D.DrawTextInternal(font, _watermarkText, WatermarkTextColor, ref _layout, TextMaterial); } // Caret From 52da42e62e4a88096984971a84157937961f5cef Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 11 Jan 2024 14:19:51 -0600 Subject: [PATCH 47/61] Add reload project menu button --- Source/Editor/Modules/UIModule.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Source/Editor/Modules/UIModule.cs b/Source/Editor/Modules/UIModule.cs index 3386d411c..195df7728 100644 --- a/Source/Editor/Modules/UIModule.cs +++ b/Source/Editor/Modules/UIModule.cs @@ -535,6 +535,7 @@ namespace FlaxEditor.Modules _menuFileRecompileScripts = cm.AddButton("Recompile scripts", inputOptions.RecompileScripts, ScriptsBuilder.Compile); cm.AddSeparator(); cm.AddButton("Open project...", OpenProject); + cm.AddButton("Reload project", ReloadProject); cm.AddSeparator(); cm.AddButton("Exit", "Alt+F4", () => Editor.Windows.MainWindow.Close(ClosingReason.User)); @@ -822,6 +823,13 @@ namespace FlaxEditor.Modules } } + private void ReloadProject() + { + // Open project, then close it + Editor.OpenProject(Editor.GameProject.ProjectPath); + Editor.Windows.MainWindow.Close(ClosingReason.User); + } + private void OnMenuFileShowHide(Control control) { if (control.Visible == false) From 64e3db3a98a2d5c9577f8783574df2c114f18daa Mon Sep 17 00:00:00 2001 From: NoriteSC <53096989+NoriteSC@users.noreply.github.com> Date: Sat, 13 Jan 2024 17:32:07 +0100 Subject: [PATCH 48/61] Update Array.h added IsValidIndex --- Source/Engine/Core/Collections/Array.h | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Core/Collections/Array.h b/Source/Engine/Core/Collections/Array.h index a8390f051..9c50c5ccf 100644 --- a/Source/Engine/Core/Collections/Array.h +++ b/Source/Engine/Core/Collections/Array.h @@ -737,7 +737,18 @@ public: ::Swap(other, *this); } } - + + /// + /// Determines if is valid index. + /// + /// The index. + /// + /// true if is valid a index; otherwise, false. + /// + bool IsValidIndex(int index) const + { + return index < _count && index >= 0; + } /// /// Reverses the order of the added items in the collection. /// From a2b8312fbaed18af9bad302fe15b6d21fd36988f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 18 Feb 2024 19:48:43 +0100 Subject: [PATCH 49/61] Cleanup stuff in #2019 --- .gitignore | 2 - Content/Editor/Fonts/NotoSansSC-Regular.flax | 3 + Source/Editor/EditorAssets.cs | 5 +- Source/Editor/GUI/Row.cs | 6 +- .../Editor/GUI/Timeline/GUI/PositionHandle.cs | 20 +- Source/Editor/Options/InterfaceOptions.cs | 12 +- Source/Editor/Options/OptionsModule.cs | 11 +- Source/Editor/Windows/OutputLogWindow.cs | 12 +- Source/Editor/Windows/SplashScreen.cpp | 8 +- Source/Engine/Core/Config/GameSettings.h | 2 - Source/Engine/Core/Config/GraphicsSettings.h | 15 +- Source/Engine/Graphics/Graphics.cpp | 8 +- Source/Engine/Render2D/FallbackFonts.cpp | 11 - Source/Engine/Render2D/FallbackFonts.h | 119 ---- Source/Engine/Render2D/Font.cpp | 509 +--------------- Source/Engine/Render2D/Font.h | 572 ++---------------- Source/Engine/Render2D/FontAsset.cpp | 28 +- Source/Engine/Render2D/FontAsset.h | 7 +- Source/Engine/Render2D/FontManager.cpp | 7 +- Source/Engine/Render2D/Render2D.cpp | 368 +---------- Source/Engine/Render2D/Render2D.cs | 6 +- Source/Engine/Render2D/Render2D.h | 144 +---- Source/Engine/Scripting/Scripting.cs | 14 +- Source/Engine/UI/GUI/Common/Label.cs | 27 +- .../UI/GUI/Common/RichTextBox.Parsing.cs | 1 - .../Engine/UI/GUI/Common/RichTextBox.Tags.cs | 42 +- .../Engine/UI/GUI/Common/RichTextBoxBase.cs | 1 - Source/Engine/UI/GUI/Common/TextBox.cs | 42 +- Source/Engine/UI/GUI/Common/TextBoxBase.cs | 4 +- Source/Engine/UI/TextRender.cpp | 2 +- 30 files changed, 252 insertions(+), 1756 deletions(-) create mode 100644 Content/Editor/Fonts/NotoSansSC-Regular.flax delete mode 100644 Source/Engine/Render2D/FallbackFonts.cpp delete mode 100644 Source/Engine/Render2D/FallbackFonts.h diff --git a/.gitignore b/.gitignore index b653b7f77..30c2caeb2 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,6 @@ Source/*.Gen.* Source/*.csproj /Package_*/ !Source/Engine/Debug -/Source/Platforms/Editor/Linux/Mono/etc/mono/registry PackageEditor_Cert.command PackageEditor_Cert.bat PackagePlatforms_Cert.bat @@ -157,4 +156,3 @@ obj/ .idea/ *.code-workspace omnisharp.json -Content/Editor/Fonts/NotoSansSC-Regular.flax diff --git a/Content/Editor/Fonts/NotoSansSC-Regular.flax b/Content/Editor/Fonts/NotoSansSC-Regular.flax new file mode 100644 index 000000000..39964e6c5 --- /dev/null +++ b/Content/Editor/Fonts/NotoSansSC-Regular.flax @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:19fa43eb7b31ee3936348b1f271c464c79d7020a21d33e3cdbe54f98c3b14304 +size 10560899 diff --git a/Source/Editor/EditorAssets.cs b/Source/Editor/EditorAssets.cs index c894abe6b..8b2049ebd 100644 --- a/Source/Editor/EditorAssets.cs +++ b/Source/Editor/EditorAssets.cs @@ -54,7 +54,10 @@ namespace FlaxEditor /// public static string PrimaryFont = "Editor/Fonts/Roboto-Regular"; - public static string CjkFont = "Editor/Fonts/NotoSansSC-Regular"; + /// + /// The secondary (fallback) font to use for missing characters rendering (CJK - Chinese/Japanese/Korean characters). + /// + public static string FallbackFont = "Editor/Fonts/NotoSansSC-Regular"; /// /// The Inconsolata Regular font. diff --git a/Source/Editor/GUI/Row.cs b/Source/Editor/GUI/Row.cs index f75f11922..3e2bb63bd 100644 --- a/Source/Editor/GUI/Row.cs +++ b/Source/Editor/GUI/Row.cs @@ -43,9 +43,9 @@ namespace FlaxEditor.GUI { Depth = -1; - var mediumHeight = Style.Current.FontMedium.GetMaxHeight(); - if (Height < mediumHeight) - Height = mediumHeight + 4; + var fontHeight = Style.Current.FontMedium.Height; + if (Height < fontHeight) + Height = fontHeight + 4; } /// diff --git a/Source/Editor/GUI/Timeline/GUI/PositionHandle.cs b/Source/Editor/GUI/Timeline/GUI/PositionHandle.cs index 3f835a35f..bedb61a5e 100644 --- a/Source/Editor/GUI/Timeline/GUI/PositionHandle.cs +++ b/Source/Editor/GUI/Timeline/GUI/PositionHandle.cs @@ -36,16 +36,16 @@ namespace FlaxEditor.GUI.Timeline.GUI string labelText; switch (_timeline.TimeShowMode) { - case Timeline.TimeShowModes.Frames: - labelText = _timeline.CurrentFrame.ToString("###0", CultureInfo.InvariantCulture); - break; - case Timeline.TimeShowModes.Seconds: - labelText = _timeline.CurrentTime.ToString("###0.##'s'", CultureInfo.InvariantCulture); - break; - case Timeline.TimeShowModes.Time: - labelText = TimeSpan.FromSeconds(_timeline.CurrentTime).ToString("g"); - break; - default: throw new ArgumentOutOfRangeException(); + case Timeline.TimeShowModes.Frames: + labelText = _timeline.CurrentFrame.ToString("###0", CultureInfo.InvariantCulture); + break; + case Timeline.TimeShowModes.Seconds: + labelText = _timeline.CurrentTime.ToString("###0.##'s'", CultureInfo.InvariantCulture); + break; + case Timeline.TimeShowModes.Time: + labelText = TimeSpan.FromSeconds(_timeline.CurrentTime).ToString("g"); + break; + default: throw new ArgumentOutOfRangeException(); } var color = (_timeline.IsMovingPositionHandle ? style.ProgressNormal : style.Foreground).AlphaMultiplied(0.6f); Matrix3x3.RotationZ(Mathf.PiOverTwo, out var m1); diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index af432baa7..6180b5a66 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -172,9 +172,9 @@ namespace FlaxEditor.Options set { if (value == null) - _outputLogFont = new FontReference(FlaxEngine.Content.LoadAsyncInternal(EditorAssets.InconsolataRegularFont), 10); + _outputLogFont = new FontReference(ConsoleFont, 10); else if (!value.Font) - _outputLogFont.Font = FlaxEngine.Content.LoadAsyncInternal(EditorAssets.InconsolataRegularFont); + _outputLogFont.Font = ConsoleFont; else _outputLogFont = value; } @@ -237,8 +237,7 @@ namespace FlaxEditor.Options public int NumberOfGameClientsToLaunch = 1; private static FontAsset DefaultFont => FlaxEngine.Content.LoadAsyncInternal(EditorAssets.PrimaryFont); - - private static FontAsset ConsoleFont => FlaxEngine.Content.LoadAsyncInternal(EditorAssets.PrimaryFont); + private static FontAsset ConsoleFont => FlaxEngine.Content.LoadAsyncInternal(EditorAssets.InconsolataRegularFont); private FontReference _titleFont = new FontReference(DefaultFont, 18); private FontReference _largeFont = new FontReference(DefaultFont, 14); @@ -247,9 +246,10 @@ namespace FlaxEditor.Options private FontReference _outputLogFont = new FontReference(ConsoleFont, 10); /// - /// The fallback fonts. + /// The list of fallback fonts to use when main text font is missing certain characters. Empty to use fonts from GraphicsSettings. /// - public FontAsset[] Fallbacks = [FlaxEngine.Content.LoadAsyncInternal(EditorAssets.CjkFont)]; + [EditorDisplay("Fonts"), EditorOrder(650)] + public FontAsset[] FallbackFonts = [FlaxEngine.Content.LoadAsyncInternal(EditorAssets.FallbackFont)]; /// /// Gets or sets the title font for editor UI. diff --git a/Source/Editor/Options/OptionsModule.cs b/Source/Editor/Options/OptionsModule.cs index bf35105a5..bb90be6d9 100644 --- a/Source/Editor/Options/OptionsModule.cs +++ b/Source/Editor/Options/OptionsModule.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using FlaxEditor.Content.Settings; using FlaxEditor.Modules; using FlaxEngine; @@ -225,11 +226,11 @@ namespace FlaxEditor.Options } } - var graphicsSetttings = GameSettings.Load(); - if (graphicsSetttings.EnableFontFallback && graphicsSetttings.FallbackFonts == null) - { - Render2D.FallbackFonts = graphicsSetttings.FallbackFonts = FontFallbackList.Create(Options.Interface.Fallbacks); - } + // Set fallback fonts + var fallbackFonts = Options.Interface.FallbackFonts; + if (fallbackFonts == null || fallbackFonts.Length == 0 || fallbackFonts.All(x => x == null)) + fallbackFonts = GameSettings.Load().FallbackFonts; + Font.FallbackFonts = fallbackFonts; } /// diff --git a/Source/Editor/Windows/OutputLogWindow.cs b/Source/Editor/Windows/OutputLogWindow.cs index f168d8d45..6526d7c8a 100644 --- a/Source/Editor/Windows/OutputLogWindow.cs +++ b/Source/Editor/Windows/OutputLogWindow.cs @@ -562,12 +562,12 @@ namespace FlaxEditor.Windows { switch (match.Groups["level"].Value) { - case "error": - textBlock.Style = _output.ErrorStyle; - break; - case "warning": - textBlock.Style = _output.WarningStyle; - break; + case "error": + textBlock.Style = _output.ErrorStyle; + break; + case "warning": + textBlock.Style = _output.WarningStyle; + break; } textBlock.Tag = new TextBlockTag { diff --git a/Source/Editor/Windows/SplashScreen.cpp b/Source/Editor/Windows/SplashScreen.cpp index cba59191e..92894b537 100644 --- a/Source/Editor/Windows/SplashScreen.cpp +++ b/Source/Editor/Windows/SplashScreen.cpp @@ -258,13 +258,13 @@ void SplashScreen::OnDraw() return; // Title - const auto titleLength = _titleFont->MeasureTextInternal(GetTitle()); + const auto titleLength = _titleFont->MeasureText(GetTitle()); TextLayoutOptions layout; layout.Bounds = Rectangle(10 * s, 10 * s, width - 10 * s, 50 * s); layout.HorizontalAlignment = TextAlignment::Near; layout.VerticalAlignment = TextAlignment::Near; layout.Scale = Math::Min((width - 20 * s) / titleLength.X, 1.0f); - Render2D::DrawTextInternal(_titleFont, GetTitle(), Color::White, layout); + Render2D::DrawText(_titleFont, GetTitle(), Color::White, layout); // Subtitle String subtitle(_quote); @@ -279,14 +279,14 @@ void SplashScreen::OnDraw() layout.Scale = 1.0f; layout.HorizontalAlignment = TextAlignment::Far; layout.VerticalAlignment = TextAlignment::Far; - Render2D::DrawTextInternal(_subtitleFont, subtitle, Color::FromRGB(0x8C8C8C), layout); + Render2D::DrawText(_subtitleFont, subtitle, Color::FromRGB(0x8C8C8C), layout); // Additional info const float infoMargin = 6 * s; layout.Bounds = Rectangle(infoMargin, lightBarHeight + infoMargin, width - (2 * infoMargin), height - lightBarHeight - (2 * infoMargin)); layout.HorizontalAlignment = TextAlignment::Near; layout.VerticalAlignment = TextAlignment::Center; - Render2D::DrawTextInternal(_subtitleFont, _infoText, Color::FromRGB(0xFFFFFF) * 0.9f, layout); + Render2D::DrawText(_subtitleFont, _infoText, Color::FromRGB(0xFFFFFF) * 0.9f, layout); } bool SplashScreen::HasLoadedFonts() const diff --git a/Source/Engine/Core/Config/GameSettings.h b/Source/Engine/Core/Config/GameSettings.h index 675c26bd1..54ad29a7b 100644 --- a/Source/Engine/Core/Config/GameSettings.h +++ b/Source/Engine/Core/Config/GameSettings.h @@ -7,8 +7,6 @@ #include "Engine/Core/Types/String.h" #include "Engine/Core/Collections/Dictionary.h" -class FontFallbackList; - /// /// The main game engine configuration service. Loads and applies game configuration. /// diff --git a/Source/Engine/Core/Config/GraphicsSettings.h b/Source/Engine/Core/Config/GraphicsSettings.h index e109a55e4..b639b9e64 100644 --- a/Source/Engine/Core/Config/GraphicsSettings.h +++ b/Source/Engine/Core/Config/GraphicsSettings.h @@ -6,7 +6,7 @@ #include "Engine/Graphics/Enums.h" #include "Engine/Graphics/PostProcessSettings.h" -class FontFallbackList; +class FontAsset; /// /// Graphics rendering settings. @@ -15,6 +15,7 @@ API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings", NoConstructor) class { API_AUTO_SERIALIZATION(); DECLARE_SCRIPTING_TYPE_MINIMAL(GraphicsSettings); + public: /// /// Enables rendering synchronization with the refresh rate of the display device to avoid "tearing" artifacts. @@ -121,16 +122,10 @@ public: PostProcessSettings PostProcessSettings; /// - /// Whether to enable font fallbacking globally. + /// The list of fallback fonts used for text rendering. Ignored if empty. /// - API_FIELD(Attributes = "EditorOrder(12000), EditorDisplay(\"Text Render Settings\", EditorDisplayAttribute.InlineStyle)") - bool EnableFontFallback = true; - - /// - /// The fallback fonts used for text rendering, ignored if null. - /// - API_FIELD(Attributes = "EditorOrder(12005), EditorDisplay(\"Text Render Settings\", EditorDisplayAttribute.InlineStyle)") - FontFallbackList* FallbackFonts; + API_FIELD(Attributes="EditorOrder(5000), EditorDisplay(\"Text\")") + Array> FallbackFonts; private: /// diff --git a/Source/Engine/Graphics/Graphics.cpp b/Source/Engine/Graphics/Graphics.cpp index 20f29288b..a9bfef470 100644 --- a/Source/Engine/Graphics/Graphics.cpp +++ b/Source/Engine/Graphics/Graphics.cpp @@ -8,7 +8,7 @@ #include "Engine/Core/Config/GraphicsSettings.h" #include "Engine/Engine/CommandLine.h" #include "Engine/Engine/EngineService.h" -#include "Engine/Render2D/Render2D.h" +#include "Engine/Render2D/Font.h" bool Graphics::UseVSync = false; Quality Graphics::AAQuality = Quality::Medium; @@ -70,9 +70,9 @@ void GraphicsSettings::Apply() Graphics::GIQuality = GIQuality; Graphics::PostProcessSettings = ::PostProcessSettings(); Graphics::PostProcessSettings.BlendWith(PostProcessSettings, 1.0f); - - Render2D::EnableFontFallback = EnableFontFallback; - Render2D::FallbackFonts = FallbackFonts; +#if !USE_EDITOR // OptionsModule handles fallback fonts in Editor + Font::FallbackFonts = FallbackFonts; +#endif } void Graphics::DisposeDevice() diff --git a/Source/Engine/Render2D/FallbackFonts.cpp b/Source/Engine/Render2D/FallbackFonts.cpp deleted file mode 100644 index 7f81a42bb..000000000 --- a/Source/Engine/Render2D/FallbackFonts.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "FallbackFonts.h" -#include "FontManager.h" -#include "Engine/Core/Math/Math.h" - -FontFallbackList::FontFallbackList(const Array& fonts) - : ManagedScriptingObject(SpawnParams(Guid::New(), Font::TypeInitializer)), - _fontAssets(fonts) -{ - -} - diff --git a/Source/Engine/Render2D/FallbackFonts.h b/Source/Engine/Render2D/FallbackFonts.h deleted file mode 100644 index fb247e4e2..000000000 --- a/Source/Engine/Render2D/FallbackFonts.h +++ /dev/null @@ -1,119 +0,0 @@ -#pragma once - -#include "Engine/Core/Collections/Array.h" -#include "Engine/Core/Collections/Dictionary.h" -#include "Font.h" -#include "FontAsset.h" - -struct TextRange; -class Font; -class FontAsset; - -/// -/// Defines a list of fonts that can be used as a fallback, ordered by priority. -/// -API_CLASS(Sealed, NoSpawn) class FLAXENGINE_API FontFallbackList : public ManagedScriptingObject -{ - DECLARE_SCRIPTING_TYPE_NO_SPAWN(FontFallbackList); -private: - Array _fontAssets; - - // Cache fallback fonts of various sizes - Dictionary*> _cache; - -public: - /// - /// Initializes a new instance of the class. - /// - /// The fallback font assets. - FontFallbackList(const Array& fonts); - - /// - /// Initializes a new instance of the class, exposed for C#. - /// - /// The fallback font assets. - /// The new instance. - API_FUNCTION() FORCE_INLINE static FontFallbackList* Create(const Array& fonts) { - return New(fonts); - } - - /// - /// Get the parent assets of fallback fonts. - /// - /// The font assets. - API_PROPERTY() FORCE_INLINE Array& GetFonts() { - return _fontAssets; - } - - /// - /// Set the fallback fonts. - /// - /// The parent assets of the new fonts. - API_PROPERTY() FORCE_INLINE void SetFonts(const Array& val) { - _fontAssets = val; - } - - /// - /// Gets the fallback fonts with the given size. - /// - /// The size. - /// The generated fonts. - API_FUNCTION() FORCE_INLINE Array& GetFontList(float size) { - Array* result; - if (_cache.TryGet(size, result)) { - return *result; - } - - result = New>(_fontAssets.Count()); - auto& arr = *result; - for (int32 i = 0; i < _fontAssets.Count(); i++) - { - arr.Add(_fontAssets[i]->CreateFont(size)); - } - - _cache[size] = result; - return *result; - } - - - /// - /// Gets the index of the fallback font that should be used to render the char - /// - /// The char. - /// The primary font. - /// The number to return if none of the fonts can render. - /// -1 if char can be rendered with primary font, index if it matches a fallback font. - API_FUNCTION() FORCE_INLINE int32 GetCharFallbackIndex(Char c, Font* primaryFont = nullptr, int32 missing = -1) { - if (primaryFont && primaryFont->GetAsset()->ContainsChar(c)) { - return -1; - } - - int32 fontIndex = 0; - while (fontIndex < _fontAssets.Count() && _fontAssets[fontIndex] && !_fontAssets[fontIndex]->ContainsChar(c)) - { - fontIndex++; - } - - if (fontIndex < _fontAssets.Count()) { - return fontIndex; - - } - - return missing; - } - - /// - /// Checks if every font is properly loaded. - /// - /// True if every font asset is non-null, otherwise false. - API_FUNCTION() FORCE_INLINE bool Verify() { - for (int32 i = 0; i < _fontAssets.Count(); i++) - { - if (!_fontAssets[i]) { - return false; - } - } - - return true; - } -}; diff --git a/Source/Engine/Render2D/Font.cpp b/Source/Engine/Render2D/Font.cpp index 9a2037b3f..71eca1f59 100644 --- a/Source/Engine/Render2D/Font.cpp +++ b/Source/Engine/Render2D/Font.cpp @@ -6,7 +6,8 @@ #include "Engine/Core/Log.h" #include "Engine/Threading/Threading.h" #include "IncludeFreeType.h" -#include "FallbackFonts.h" + +Array, HeapAllocation> Font::FallbackFonts; Font::Font(FontAsset* parentAsset, float size) : ManagedScriptingObject(SpawnParams(Guid::New(), Font::TypeInitializer)) @@ -33,7 +34,7 @@ Font::~Font() _asset->_fonts.Remove(this); } -void Font::GetCharacter(Char c, FontCharacterEntry& result) +void Font::GetCharacter(Char c, FontCharacterEntry& result, bool enableFallback) { // Try to get the character or cache it if cannot be found if (!_characters.TryGet(c, result)) @@ -45,6 +46,20 @@ void Font::GetCharacter(Char c, FontCharacterEntry& result) if (_characters.TryGet(c, result)) return; + // Try to use fallback font if character is missing + if (enableFallback && !_asset->ContainsChar(c)) + { + for (int32 fallbackIndex = 0; fallbackIndex < FallbackFonts.Count(); fallbackIndex++) + { + FontAsset* fallbackFont = FallbackFonts.Get()[fallbackIndex].Get(); + if (fallbackFont && fallbackFont->ContainsChar(c)) + { + fallbackFont->CreateFont(GetSize())->GetCharacter(c, result, enableFallback); + return; + } + } + } + // Create character cache FontManager::AddNewEntry(this, c, result); @@ -88,7 +103,7 @@ void Font::CacheText(const StringView& text) FontCharacterEntry entry; for (int32 i = 0; i < text.Length(); i++) { - GetCharacter(text[i], entry); + GetCharacter(text[i], entry, false); } } @@ -103,26 +118,16 @@ void Font::Invalidate() _characters.Clear(); } -float Font::GetMaxHeight(FontFallbackList* fallbacks) const -{ - float height = GetHeight(); - auto& fallbackFonts = fallbacks->GetFontList(GetSize()); - for (int32 i = 0; i < fallbackFonts.Count(); i++) - { - height = Math::Max(height, static_cast(fallbackFonts[i]->GetHeight())); - } - - return height; -} - void Font::ProcessText(const StringView& text, Array& outputLines, const TextLayoutOptions& layout) { + int32 textLength = text.Length(); + if (textLength == 0) + return; float cursorX = 0; int32 kerning; FontLineCache tmpLine; FontCharacterEntry entry; FontCharacterEntry previous; - int32 textLength = text.Length(); float scale = layout.Scale / FontManager::FontScale; float boundsWidth = layout.Bounds.GetWidth(); float baseLinesDistance = static_cast(_height) * layout.BaseLinesGapScale * scale; @@ -131,10 +136,6 @@ void Font::ProcessText(const StringView& text, Array& outputLines tmpLine.FirstCharIndex = 0; tmpLine.LastCharIndex = -1; - if (textLength == 0) { - return; - } - int32 lastWrapCharIndex = INVALID_INDEX; float lastWrapCharX = 0; bool lastMoveLine = false; @@ -146,11 +147,6 @@ void Font::ProcessText(const StringView& text, Array& outputLines float xAdvance = 0; int32 nextCharIndex = currentIndex + 1; - // Submit line if text ends - if (nextCharIndex == textLength) { - moveLine = true; - } - // Cache current character const Char currentChar = text[currentIndex]; const bool isWhitespace = StringUtils::IsWhitespace(currentChar); @@ -168,6 +164,7 @@ void Font::ProcessText(const StringView& text, Array& outputLines { // Break line moveLine = true; + currentIndex++; tmpLine.LastCharIndex++; } else @@ -178,7 +175,7 @@ void Font::ProcessText(const StringView& text, Array& outputLines // Get kerning if (!isWhitespace && previous.IsValid) { - kerning = GetKerning(previous.Character, entry.Character); + kerning = entry.Font->GetKerning(previous.Character, entry.Character); } else { @@ -247,8 +244,8 @@ void Font::ProcessText(const StringView& text, Array& outputLines // Reset line tmpLine.Location.Y += baseLinesDistance; - tmpLine.FirstCharIndex = nextCharIndex; - tmpLine.LastCharIndex = nextCharIndex - 1; + tmpLine.FirstCharIndex = currentIndex; + tmpLine.LastCharIndex = currentIndex - 1; cursorX = 0; lastWrapCharIndex = INVALID_INDEX; lastWrapCharX = 0; @@ -260,7 +257,7 @@ void Font::ProcessText(const StringView& text, Array& outputLines } // Check if an additional line should be created - if (text[textLength - 1] == '\n') + if (tmpLine.LastCharIndex >= tmpLine.FirstCharIndex || text[textLength - 1] == '\n') { // Add line tmpLine.Size.X = cursorX; @@ -305,262 +302,7 @@ void Font::ProcessText(const StringView& text, Array& outputLines } } -void Font::ProcessText(FontFallbackList* fallbacks, const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout) -{ - const Array& fallbackFonts = fallbacks->GetFontList(GetSize()); - float cursorX = 0; - int32 kerning; - BlockedTextLineCache tmpLine; - FontBlockCache tmpBlock; - FontCharacterEntry entry; - FontCharacterEntry previous; - int32 textLength = text.Length(); - float scale = layout.Scale / FontManager::FontScale; - float boundsWidth = layout.Bounds.GetWidth(); - float baseLinesDistanceScale = layout.BaseLinesGapScale * scale; - - tmpBlock.Location = Float2::Zero; - tmpBlock.Size = Float2::Zero; - tmpBlock.FirstCharIndex = 0; - tmpBlock.LastCharIndex = -1; - - tmpLine.Location = Float2::Zero; - tmpLine.Size = Float2::Zero; - tmpLine.Blocks = Array(); - - if (textLength == 0) { - return; - } - - int32 lastWrapCharIndex = INVALID_INDEX; - float lastWrapCharX = 0; - bool lastMoveLine = false; - // The index of the font used by the current block - int32 currentFontIndex = fallbacks->GetCharFallbackIndex(text[0], this); - // The maximum font height of the current line - float maxHeight = 0; - float maxAscender = 0; - float lastCursorX = 0; - - auto getFont = [&](int32 index)->Font* { - return index >= 0 ? fallbackFonts[index] : this; - }; - - // Process each character to split text into single blocks - for (int32 currentIndex = 0; currentIndex < textLength;) - { - bool moveLine = false; - bool moveBlock = false; - float xAdvance = 0; - int32 nextCharIndex = currentIndex + 1; - - // Submit line and block if text ends - if (nextCharIndex == textLength) { - moveLine = moveBlock = true; - } - - // Cache current character - const Char currentChar = text[currentIndex]; - const bool isWhitespace = StringUtils::IsWhitespace(currentChar); - - // Check if character can wrap words - const bool isWrapChar = !StringUtils::IsAlnum(currentChar) || isWhitespace || StringUtils::IsUpper(currentChar) || (currentChar >= 0x3040 && currentChar <= 0x9FFF); - if (isWrapChar && currentIndex != 0) - { - lastWrapCharIndex = currentIndex; - lastWrapCharX = cursorX; - } - - int32 nextFontIndex = currentFontIndex; - // Check if it's a newline character - if (currentChar == '\n') - { - // Break line - moveLine = moveBlock = true; - tmpBlock.LastCharIndex++; - } - else - { - // Get character entry - if (nextCharIndex < textLength) { - nextFontIndex = fallbacks->GetCharFallbackIndex(text[nextCharIndex], this, currentFontIndex); - } - - // Get character entry - getFont(currentFontIndex)->GetCharacter(currentChar, entry); - - maxHeight = Math::Max(maxHeight, - static_cast(getFont(currentFontIndex)->GetHeight())); - maxAscender = Math::Max(maxAscender, - static_cast(getFont(currentFontIndex)->GetAscender())); - - // Move block if the font changes or text ends - if (nextFontIndex != currentFontIndex || nextCharIndex == textLength) { - moveBlock = true; - } - - // Get kerning, only when the font hasn't changed - if (!isWhitespace && previous.IsValid && !moveBlock) - { - kerning = getFont(currentFontIndex)->GetKerning(previous.Character, entry.Character); - } - else - { - kerning = 0; - } - previous = entry; - xAdvance = (kerning + entry.AdvanceX) * scale; - - // Check if character fits the line or skip wrapping - if (cursorX + xAdvance <= boundsWidth || layout.TextWrapping == TextWrapping::NoWrap) - { - // Move character - cursorX += xAdvance; - tmpBlock.LastCharIndex++; - } - else if (layout.TextWrapping == TextWrapping::WrapWords) - { - if (lastWrapCharIndex != INVALID_INDEX) - { - // Skip moving twice for the same character - int32 lastLineLastCharIndex = outputLines.HasItems() && outputLines.Last().Blocks.HasItems() ? outputLines.Last().Blocks.Last().LastCharIndex : -10000; - if (lastLineLastCharIndex == lastWrapCharIndex || lastLineLastCharIndex == lastWrapCharIndex - 1 || lastLineLastCharIndex == lastWrapCharIndex - 2) - { - currentIndex = nextCharIndex; - lastMoveLine = moveLine; - continue; - } - - // Move line - const Char wrapChar = text[lastWrapCharIndex]; - moveLine = true; - moveBlock = tmpBlock.FirstCharIndex < lastWrapCharIndex; - - cursorX = lastWrapCharX; - if (StringUtils::IsWhitespace(wrapChar)) - { - // Skip whitespaces - tmpBlock.LastCharIndex = lastWrapCharIndex - 1; - nextCharIndex = currentIndex = lastWrapCharIndex + 1; - } - else - { - tmpBlock.LastCharIndex = lastWrapCharIndex - 1; - nextCharIndex = currentIndex = lastWrapCharIndex; - } - } - } - else if (layout.TextWrapping == TextWrapping::WrapChars) - { - // Move line - moveLine = true; - moveBlock = tmpBlock.FirstCharIndex < currentChar; - nextCharIndex = currentIndex; - - // Skip moving twice for the same character - if (lastMoveLine) - break; - } - } - - if (moveBlock) { - // Add block - tmpBlock.Size.X = lastCursorX - cursorX; - tmpBlock.Size.Y = baseLinesDistanceScale * getFont(currentFontIndex)->GetHeight(); - tmpBlock.LastCharIndex = Math::Max(tmpBlock.LastCharIndex, tmpBlock.FirstCharIndex); - tmpBlock.FallbackFontIndex = currentFontIndex; - tmpLine.Blocks.Add(tmpBlock); - - // Reset block - tmpBlock.Location.X = cursorX; - tmpBlock.FirstCharIndex = nextCharIndex; - tmpBlock.LastCharIndex = nextCharIndex - 1; - - currentFontIndex = nextFontIndex; - lastCursorX = cursorX; - } - - // Check if move to another line - if (moveLine) - { - // Add line - tmpLine.Size.X = cursorX; - tmpLine.Size.Y = baseLinesDistanceScale * maxHeight; - tmpLine.MaxAscender = maxAscender; - outputLines.Add(tmpLine); - - // Reset line - tmpLine.Blocks.Clear(); - tmpLine.Location.Y += baseLinesDistanceScale * maxHeight; - cursorX = 0; - tmpBlock.Location.X = cursorX; - lastWrapCharIndex = INVALID_INDEX; - lastWrapCharX = 0; - previous.IsValid = false; - - // Reset max font height - maxHeight = 0; - maxAscender = 0; - lastCursorX = 0; - } - - currentIndex = nextCharIndex; - lastMoveLine = moveLine; - } - - // Check if an additional line should be created - if (text[textLength - 1] == '\n') - { - // Add line - tmpLine.Size.X = cursorX; - tmpLine.Size.Y = baseLinesDistanceScale * maxHeight; - outputLines.Add(tmpLine); - - tmpLine.Location.Y += baseLinesDistanceScale * maxHeight; - } - - // Check amount of lines - if (outputLines.IsEmpty()) - return; - - float totalHeight = tmpLine.Location.Y; - - Float2 offset = Float2::Zero; - if (layout.VerticalAlignment == TextAlignment::Center) - { - offset.Y += (layout.Bounds.GetHeight() - totalHeight) * 0.5f; - } - else if (layout.VerticalAlignment == TextAlignment::Far) - { - offset.Y += layout.Bounds.GetHeight() - totalHeight; - } - for (int32 i = 0; i < outputLines.Count(); i++) - { - BlockedTextLineCache& line = outputLines[i]; - Float2 rootPos = line.Location + offset; - - // Fix upper left line corner to match desire text alignment - if (layout.HorizontalAlignment == TextAlignment::Center) - { - rootPos.X += (layout.Bounds.GetWidth() - line.Size.X) * 0.5f; - } - else if (layout.HorizontalAlignment == TextAlignment::Far) - { - rootPos.X += layout.Bounds.GetWidth() - line.Size.X; - } - - line.Location = rootPos; - - // Align all blocks to center in case they have different heights - for (int32 j = 0; j < line.Blocks.Count(); j++) - { - FontBlockCache& block = line.Blocks[j]; - block.Location.Y += (line.MaxAscender - getFont(block.FallbackFontIndex)->GetAscender()) / 2; - } - } -} - -Float2 Font::MeasureTextInternal(const StringView& text, const TextLayoutOptions& layout) +Float2 Font::MeasureText(const StringView& text, const TextLayoutOptions& layout) { // Check if there is no need to do anything if (text.IsEmpty()) @@ -581,28 +323,7 @@ Float2 Font::MeasureTextInternal(const StringView& text, const TextLayoutOptions return max; } -Float2 Font::MeasureTextInternal(FontFallbackList* fallbacks, const StringView& text, const TextLayoutOptions& layout) -{ - // Check if there is no need to do anything - if (text.IsEmpty()) - return Float2::Zero; - - // Process text - Array lines; - ProcessText(fallbacks, text, lines, layout); - - // Calculate bounds - Float2 max = Float2::Zero; - for (int32 i = 0; i < lines.Count(); i++) - { - const BlockedTextLineCache& line = lines[i]; - max = Float2::Max(max, line.Location + line.Size); - } - - return max; -} - -int32 Font::HitTestTextInternal(const StringView& text, const Float2& location, const TextLayoutOptions& layout) +int32 Font::HitTestText(const StringView& text, const Float2& location, const TextLayoutOptions& layout) { // Check if there is no need to do anything if (text.Length() <= 0) @@ -639,7 +360,7 @@ int32 Font::HitTestTextInternal(const StringView& text, const Float2& location, // Apply kerning if (!isWhitespace && previous.IsValid) { - x += GetKerning(previous.Character, entry.Character); + x += entry.Font->GetKerning(previous.Character, entry.Character); } previous = entry; @@ -676,107 +397,7 @@ int32 Font::HitTestTextInternal(const StringView& text, const Float2& location, return smallestIndex; } -int32 Font::HitTestTextInternal(FontFallbackList* fallbacks, const StringView& text, const Float2& location, const TextLayoutOptions& layout) -{ - // Check if there is no need to do anything - if (text.Length() <= 0) - return 0; - - // Process text - const Array& fallbackFonts = fallbacks->GetFontList(GetSize()); - Array lines; - ProcessText(fallbacks, text, lines, layout); - ASSERT(lines.HasItems()); - float scale = layout.Scale / FontManager::FontScale; - - // Offset position to match lines origin space - Float2 rootOffset = layout.Bounds.Location + lines.First().Location; - Float2 testPoint = location - rootOffset; - - // Get block which may intersect with the position (it's possible because lines have fixed height) - int32 lineIndex = 0; - while (lineIndex < lines.Count()) - { - if (lines[lineIndex].Location.Y + lines[lineIndex].Size.Y >= location.Y) { - break; - } - - lineIndex++; - } - lineIndex = Math::Clamp(lineIndex, 0, lines.Count() - 1); - const BlockedTextLineCache& line = lines[lineIndex]; - - int32 blockIndex = 0; - while (blockIndex < line.Blocks.Count() - 1) - { - if (line.Location.X + line.Blocks[blockIndex + 1].Location.X >= location.X) { - break; - } - - blockIndex++; - } - const FontBlockCache& block = line.Blocks[blockIndex]; - float x = line.Location.X; - - // Check all characters in the line to find hit point - FontCharacterEntry previous; - FontCharacterEntry entry; - int32 smallestIndex = INVALID_INDEX; - float dst, smallestDst = MAX_float; - - auto getFont = [&](int32 index)->Font* { - return index >= 0 ? fallbackFonts[index] : this; - }; - - for (int32 currentIndex = block.FirstCharIndex; currentIndex <= block.LastCharIndex; currentIndex++) - { - // Cache current character - const Char currentChar = text[currentIndex]; - - getFont(block.FallbackFontIndex)->GetCharacter(currentChar, entry); - const bool isWhitespace = StringUtils::IsWhitespace(currentChar); - - // Apply kerning - if (!isWhitespace && previous.IsValid) - { - x += getFont(block.FallbackFontIndex)->GetKerning(previous.Character, entry.Character); - } - previous = entry; - - // Test - dst = Math::Abs(testPoint.X - x); - if (dst < smallestDst) - { - // Found closer character - smallestIndex = currentIndex; - smallestDst = dst; - } - else if (dst > smallestDst) - { - // Current char is worse so return the best result - return smallestIndex; - } - - // Move - x += entry.AdvanceX * scale; - } - - // Test line end edge - dst = Math::Abs(testPoint.X - x); - if (dst < smallestDst) - { - // Pointer is behind the last character in the line - smallestIndex = block.LastCharIndex; - - // Fix for last line - if (lineIndex == lines.Count() - 1) - smallestIndex++; - } - - return smallestIndex; -} - -Float2 Font::GetCharPositionInternal(const StringView& text, int32 index, const TextLayoutOptions& layout) +Float2 Font::GetCharPosition(const StringView& text, int32 index, const TextLayoutOptions& layout) { // Check if there is no need to do anything if (text.IsEmpty()) @@ -788,7 +409,7 @@ Float2 Font::GetCharPositionInternal(const StringView& text, int32 index, const ASSERT(lines.HasItems()); float scale = layout.Scale / FontManager::FontScale; float baseLinesDistance = static_cast(_height) * layout.BaseLinesGapScale * scale; - Float2 rootOffset = layout.Bounds.Location; + Float2 rootOffset = layout.Bounds.Location + lines.First().Location; // Find line with that position FontCharacterEntry previous; @@ -813,7 +434,7 @@ Float2 Font::GetCharPositionInternal(const StringView& text, int32 index, const // Apply kerning if (!isWhitespace && previous.IsValid) { - x += GetKerning(previous.Character, entry.Character); + x += entry.Font->GetKerning(previous.Character, entry.Character); } previous = entry; @@ -827,71 +448,7 @@ Float2 Font::GetCharPositionInternal(const StringView& text, int32 index, const } // Position after last character in the last line - return rootOffset + Float2(lines.Last().Location.X + lines.Last().Size.X, static_cast((lines.Count() - 1) * baseLinesDistance)); -} - -Float2 Font::GetCharPositionInternal(FontFallbackList* fallbacks, const StringView& text, int32 index, const TextLayoutOptions& layout) -{ - // Check if there is no need to do anything - if (text.IsEmpty()) - return layout.Bounds.Location; - - // Process text - const Array& fallbackFonts = fallbacks->GetFontList(GetSize()); - Array lines; - ProcessText(fallbacks, text, lines, layout); - ASSERT(lines.HasItems()); - float scale = layout.Scale / FontManager::FontScale; - float baseLinesDistance = layout.BaseLinesGapScale * scale; - Float2 rootOffset = layout.Bounds.Location; - - // Find line with that position - FontCharacterEntry previous; - FontCharacterEntry entry; - - auto getFont = [&](int32 index)->Font* { - return index >= 0 ? fallbackFonts[index] : this; - }; - - for (int32 lineIndex = 0; lineIndex < lines.Count(); lineIndex++) - { - const BlockedTextLineCache& line = lines[lineIndex]; - for (int32 blockIndex = 0; blockIndex < line.Blocks.Count(); blockIndex++) - { - const FontBlockCache& block = line.Blocks[blockIndex]; - // Check if desire position is somewhere inside characters in line range - if (Math::IsInRange(index, block.FirstCharIndex, block.LastCharIndex)) - { - float x = line.Location.X + block.Location.X; - float y = line.Location.Y + block.Location.Y; - - // Check all characters in the line - for (int32 currentIndex = block.FirstCharIndex; currentIndex < index; currentIndex++) - { - // Cache current character - const Char currentChar = text[currentIndex]; - getFont(block.FallbackFontIndex)->GetCharacter(currentChar, entry); - const bool isWhitespace = StringUtils::IsWhitespace(currentChar); - - // Apply kerning - if (!isWhitespace && previous.IsValid) - { - x += getFont(block.FallbackFontIndex)->GetKerning(previous.Character, entry.Character); - } - previous = entry; - - // Move - x += entry.AdvanceX * scale; - } - - // Upper left corner of the character - return rootOffset + Float2(x, y); - } - } - } - - // Position after last character in the last line - return rootOffset + Float2(lines.Last().Location.X + lines.Last().Size.X, lines.Last().Location.Y); + return rootOffset + Float2(lines.Last().Size.X, static_cast((lines.Count() - 1) * baseLinesDistance)); } void Font::FlushFaceSize() const diff --git a/Source/Engine/Render2D/Font.h b/Source/Engine/Render2D/Font.h index 1fb41d032..cd92103cb 100644 --- a/Source/Engine/Render2D/Font.h +++ b/Source/Engine/Render2D/Font.h @@ -8,12 +8,9 @@ #include "Engine/Content/AssetReference.h" #include "Engine/Scripting/ScriptingObject.h" #include "TextLayoutOptions.h" -#include "Render2D.h" class FontAsset; -class FontFallbackList; struct FontTextureAtlasSlot; -struct BlockedTextLineCache; // The default DPI that engine is using #define DefaultDPI 96 @@ -38,7 +35,7 @@ API_STRUCT(NoDefault) struct TextRange /// /// Gets the range length. /// - int32 Length() const + FORCE_INLINE int32 Length() const { return EndIndex - StartIndex; } @@ -46,7 +43,7 @@ API_STRUCT(NoDefault) struct TextRange /// /// Gets a value indicating whether range is empty. /// - bool IsEmpty() const + FORCE_INLINE bool IsEmpty() const { return (EndIndex - StartIndex) <= 0; } @@ -56,7 +53,7 @@ API_STRUCT(NoDefault) struct TextRange /// /// The index. /// true if range contains the specified character index; otherwise, false. - bool Contains(int32 index) const + FORCE_INLINE bool Contains(int32 index) const { return index >= StartIndex && index < EndIndex; } @@ -122,74 +119,6 @@ struct TIsPODType enum { Value = true }; }; -/// -/// The font block info generated during text processing. -/// A block means a range of text that belongs to the same line and can be rendered with the same font. -/// -API_STRUCT(NoDefault) struct FontBlockCache -{ - DECLARE_SCRIPTING_TYPE_MINIMAL(FontBlockCache); - - /// - /// The root position of the block (upper left corner), relative to line. - /// - API_FIELD() Float2 Location; - - /// - /// The size of the current block - /// - API_FIELD() Float2 Size; - - /// - /// The first character index (from the input text). - /// - API_FIELD() int32 FirstCharIndex; - - /// - /// The last character index (from the input text), inclusive. - /// - API_FIELD() int32 LastCharIndex; - - /// - /// Indicates the fallback font to render this block with, -1 if doesn't require fallback. - /// - API_FIELD() int32 FallbackFontIndex; -}; - -template<> -struct TIsPODType -{ - enum { Value = true }; -}; - -/// -/// Line of font blocks info generated during text processing. -/// -API_STRUCT(NoDefault) struct BlockedTextLineCache -{ - DECLARE_SCRIPTING_TYPE_MINIMAL(BlockedTextLineCache); - - /// - /// The root position of the line (upper left corner). - /// - API_FIELD() Float2 Location; - - /// - /// The line bounds (width and height). - /// - API_FIELD() Float2 Size; - - /// - /// The maximum ascender of the line. - /// - API_FIELD() float MaxAscender; - - /// - /// The blocks that belongs to this line - /// - API_FIELD() Array Blocks; -}; - // Font glyph metrics: // // xmin xmax @@ -281,6 +210,11 @@ API_STRUCT(NoDefault) struct FontCharacterEntry /// The slot in texture atlas, containing the pixel data of the glyph. /// API_FIELD() const FontTextureAtlasSlot* Slot; + + /// + /// The owner font. + /// + API_FIELD() const class Font* Font; }; template<> @@ -296,8 +230,8 @@ API_CLASS(Sealed, NoSpawn) class FLAXENGINE_API Font : public ManagedScriptingOb { DECLARE_SCRIPTING_TYPE_NO_SPAWN(Font); friend FontAsset; -private: +private: FontAsset* _asset; float _size; int32 _height; @@ -309,7 +243,6 @@ private: mutable Dictionary _kerningTable; public: - /// /// Initializes a new instance of the class. /// @@ -323,6 +256,10 @@ public: ~Font(); public: + /// + /// The active fallback fonts. + /// + API_FIELD() static Array, HeapAllocation> FallbackFonts; /// /// Gets parent font asset that contains font family used by this font. @@ -373,13 +310,13 @@ public: } public: - /// /// Gets character entry. /// /// The character. /// The output character entry. - void GetCharacter(Char c, FontCharacterEntry& result); + /// True if fallback to secondary font when the primary font doesn't contains this character. + void GetCharacter(Char c, FontCharacterEntry& result, bool enableFallback = true); /// /// Gets the kerning amount for a pair of characters. @@ -401,33 +338,8 @@ public: API_FUNCTION() void Invalidate(); public: - /// - /// Gets the maximum height among the font and the fallback fonts. - /// - /// The fallback fonts. - /// The maximum height. - API_FUNCTION() float GetMaxHeight(FontFallbackList* fallbacks) const; - - /// - /// Gets the maximum height among the font and the fallback fonts, uses the default font defined in . - /// - /// The fallback fonts. - /// The maximum height. - API_FUNCTION() FORCE_INLINE float GetMaxHeight() const - { - if (Render2D::EnableFontFallback && Render2D::FallbackFonts) - { - return GetMaxHeight(Render2D::FallbackFonts); - } - else - { - return GetHeight(); - } - } - - /// - /// Processes text to get cached lines for rendering, with font fallbacking disabled. + /// Processes text to get cached lines for rendering. /// /// The input text. /// The layout properties. @@ -435,12 +347,12 @@ public: void ProcessText(const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout); /// - /// Processes text to get cached lines for rendering, with font fallbacking disabled. + /// Processes text to get cached lines for rendering. /// /// The input text. /// The layout properties. /// The output lines list. - API_FUNCTION() FORCE_INLINE Array ProcessText(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout) + API_FUNCTION() Array ProcessText(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout) { Array lines; ProcessText(text, lines, layout); @@ -448,13 +360,13 @@ public: } /// - /// Processes text to get cached lines for rendering, with font fallbacking disabled. + /// Processes text to get cached lines for rendering. /// /// The input text. /// The input text range (substring range of the input text parameter). /// The layout properties. /// The output lines list. - API_FUNCTION() FORCE_INLINE Array ProcessText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) + API_FUNCTION() Array ProcessText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) { Array lines; ProcessText(textRange.Substring(text), lines, layout); @@ -462,7 +374,7 @@ public: } /// - /// Processes text to get cached lines for rendering, with font fallbacking disabled. + /// Processes text to get cached lines for rendering. /// /// The input text. /// The output lines list. @@ -472,7 +384,7 @@ public: } /// - /// Processes text to get cached lines for rendering, with font fallbacking disabled. + /// Processes text to get cached lines for rendering. /// /// The input text. /// The input text range (substring range of the input text parameter). @@ -483,349 +395,81 @@ public: } /// - /// Processes text to get cached lines for rendering, using custom fallback options. - /// - /// The input text. - /// The layout properties. - /// The output lines list. - void ProcessText(FontFallbackList* fallbacks, const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout); - - /// - /// Processes text to get cached lines for rendering, using custom fallback options. - /// - /// The input text. - /// The layout properties. - /// The output lines list. - API_FUNCTION() FORCE_INLINE Array ProcessText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout) - { - Array lines; - ProcessText(fallbacks, text, lines, layout); - return lines; - } - - /// - /// Processes text to get cached lines for rendering, using custom fallback options. - /// - /// The input text. - /// The input text range (substring range of the input text parameter). - /// The layout properties. - /// The output lines list. - API_FUNCTION() FORCE_INLINE Array ProcessText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) - { - Array lines; - ProcessText(fallbacks, textRange.Substring(text), lines, layout); - return lines; - } - - /// - /// Processes text to get cached lines for rendering, using custom fallback options. - /// - /// The input text. - /// The output lines list. - API_FUNCTION() FORCE_INLINE Array ProcessText(FontFallbackList* fallbacks, const StringView& text) - { - return ProcessText(fallbacks, text, TextLayoutOptions()); - } - - /// - /// Processes text to get cached lines for rendering, using custom fallback options. - /// - /// The input text. - /// The input text range (substring range of the input text parameter). - /// The output lines list. - API_FUNCTION() FORCE_INLINE Array ProcessText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange) - { - return ProcessText(fallbacks, textRange.Substring(text), TextLayoutOptions()); - } - - /// - /// Measures minimum size of the rectangle that will be needed to draw given text, with font fallbacking disabled. + /// Measures minimum size of the rectangle that will be needed to draw given text. /// /// The input text to test. /// The layout properties. /// The minimum size for that text and fot to render properly. - API_FUNCTION() Float2 MeasureTextInternal(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout); + API_FUNCTION() Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout); /// - /// Measures minimum size of the rectangle that will be needed to draw given text, with font fallbacking disabled. + /// Measures minimum size of the rectangle that will be needed to draw given text. /// /// The input text to test. /// The input text range (substring range of the input text parameter). /// The layout properties. /// The minimum size for that text and fot to render properly. - API_FUNCTION() FORCE_INLINE Float2 MeasureTextInternal(const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) + API_FUNCTION() Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) { - return MeasureTextInternal(textRange.Substring(text), layout); + return MeasureText(textRange.Substring(text), layout); } /// - /// Measures minimum size of the rectangle that will be needed to draw given text, with font fallbacking disabled. - /// . - /// The input text to test. - /// The minimum size for that text and fot to render properly. - API_FUNCTION() FORCE_INLINE Float2 MeasureTextInternal(const StringView& text) - { - return MeasureTextInternal(text, TextLayoutOptions()); - } - - /// - /// Measures minimum size of the rectangle that will be needed to draw given text, with font fallbacking disabled. - /// . - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The minimum size for that text and fot to render properly. - API_FUNCTION() FORCE_INLINE Float2 MeasureTextInternal(const StringView& text, API_PARAM(Ref) const TextRange& textRange) - { - return MeasureTextInternal(textRange.Substring(text), TextLayoutOptions()); - } - - /// - /// Measures minimum size of the rectangle that will be needed to draw given text, using custom fallback options. - /// - /// The input text to test. - /// The layout properties. - /// The minimum size for that text and fot to render properly. - API_FUNCTION() Float2 MeasureTextInternal(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout); - - /// - /// Measures minimum size of the rectangle that will be needed to draw given text, using custom fallback options. - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The layout properties. - /// The minimum size for that text and fot to render properly. - API_FUNCTION() FORCE_INLINE Float2 MeasureTextInternal(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) - { - return MeasureTextInternal(fallbacks, textRange.Substring(text), layout); - } - - /// - /// Measures minimum size of the rectangle that will be needed to draw given text, using custom fallback options. - /// . - /// The input text to test. - /// The minimum size for that text and fot to render properly. - API_FUNCTION() FORCE_INLINE Float2 MeasureTextInternal(FontFallbackList* fallbacks, const StringView& text) - { - return MeasureTextInternal(fallbacks, text, TextLayoutOptions()); - } - - /// - /// Measures minimum size of the rectangle that will be needed to draw given text, using custom fallback options. - /// . - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The minimum size for that text and fot to render properly. - API_FUNCTION() FORCE_INLINE Float2 MeasureTextInternal(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange) - { - return MeasureTextInternal(fallbacks, textRange.Substring(text), TextLayoutOptions()); - } - - /// - /// Measures minimum size of the rectangle that will be needed to draw given text, follows the fallback settings defined in . - /// - /// The input text to test. - /// The layout properties. - /// The minimum size for that text and fot to render properly. - API_FUNCTION() FORCE_INLINE Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout) { - if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { - return MeasureTextInternal(Render2D::FallbackFonts, text, layout); - } - else { - return MeasureTextInternal(text, layout); - } - } - - /// - /// Measures minimum size of the rectangle that will be needed to draw given text, follows the fallback settings defined in . - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The layout properties. - /// The minimum size for that text and fot to render properly. - API_FUNCTION() FORCE_INLINE Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) - { - if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { - return MeasureTextInternal(Render2D::FallbackFonts, textRange.Substring(text), layout); - } - else { - return MeasureTextInternal(textRange.Substring(text), layout); - } - } - - /// - /// Measures minimum size of the rectangle that will be needed to draw given text, follows the fallback settings defined in . + /// Measures minimum size of the rectangle that will be needed to draw given text /// . /// The input text to test. /// The minimum size for that text and fot to render properly. API_FUNCTION() FORCE_INLINE Float2 MeasureText(const StringView& text) { - if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { - return MeasureTextInternal(Render2D::FallbackFonts, text, TextLayoutOptions()); - } - else { - return MeasureTextInternal(text, TextLayoutOptions()); - } + return MeasureText(text, TextLayoutOptions()); } /// - /// Measures minimum size of the rectangle that will be needed to draw given text, follows the fallback settings defined in . + /// Measures minimum size of the rectangle that will be needed to draw given text /// . /// The input text to test. /// The input text range (substring range of the input text parameter). /// The minimum size for that text and fot to render properly. API_FUNCTION() FORCE_INLINE Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextRange& textRange) { - if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { - return MeasureTextInternal(Render2D::FallbackFonts, textRange.Substring(text), TextLayoutOptions()); - } - else { - return MeasureTextInternal(textRange.Substring(text), TextLayoutOptions()); - } + return MeasureText(textRange.Substring(text), TextLayoutOptions()); } /// - /// Calculates hit character index at given location, with font fallbacking disabled. - /// - /// The input text to test. - /// The input location to test. - /// The text layout properties. - /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() int32 HitTestTextInternal(const StringView& text, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout); - - /// - /// Calculates hit character index at given location, with font fallbacking disabled. + /// Calculates hit character index at given location. /// /// The input text to test. /// The input text range (substring range of the input text parameter). /// The input location to test. /// The text layout properties. /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() FORCE_INLINE int32 HitTestTextInternal(const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout) + API_FUNCTION() int32 HitTestText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout) { - return HitTestTextInternal(textRange.Substring(text), location, layout); + return HitTestText(textRange.Substring(text), location, layout); } /// - /// Calculates hit character index at given location, with font fallbacking disabled. - /// - /// The input text to test. - /// The input location to test. - /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() FORCE_INLINE int32 HitTestTextInternal(const StringView& text, const Float2& location) - { - return HitTestTextInternal(text, location, TextLayoutOptions()); - } - - /// - /// Calculates hit character index at given location, with font fallbacking disabled. - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The input location to test. - /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() FORCE_INLINE int32 HitTestTextInternal(const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location) - { - return HitTestTextInternal(textRange.Substring(text), location, TextLayoutOptions()); - } - - /// - /// Calculates hit character index at given location, using custom fallback options. + /// Calculates hit character index at given location. /// /// The input text to test. /// The input location to test. /// The text layout properties. /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() int32 HitTestTextInternal(FontFallbackList* fallbacks, const StringView& text, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout); + API_FUNCTION() int32 HitTestText(const StringView& text, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout); /// - /// Calculates hit character index at given location, using custom fallback options. - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The input location to test. - /// The text layout properties. - /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() FORCE_INLINE int32 HitTestTextInternal(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout) - { - return HitTestTextInternal(fallbacks, textRange.Substring(text), location, layout); - } - - /// - /// Calculates hit character index at given location, using custom fallback options. - /// - /// The input text to test. - /// The input location to test. - /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() FORCE_INLINE int32 HitTestTextInternal(FontFallbackList* fallbacks, const StringView& text, const Float2& location) - { - return HitTestTextInternal(fallbacks, text, location, TextLayoutOptions()); - } - - /// - /// Calculates hit character index at given location, using custom fallback options. - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The input location to test. - /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() FORCE_INLINE int32 HitTestTextInternal(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location) - { - return HitTestTextInternal(fallbacks, textRange.Substring(text), location, TextLayoutOptions()); - } - - /// - /// Calculates hit character index at given location, follows the fallback settings defined in . - /// - /// The input text to test. - /// The input location to test. - /// The text layout properties. - /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() FORCE_INLINE int32 HitTestText(const StringView& text, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout) { - if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { - return HitTestTextInternal(Render2D::FallbackFonts, text, location, layout); - } - else { - return HitTestTextInternal(text, location, layout); - } - } - - /// - /// Calculates hit character index at given location, follows the fallback settings defined in . - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The input location to test. - /// The text layout properties. - /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() FORCE_INLINE int32 HitTestText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout) - { - if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { - return HitTestTextInternal(Render2D::FallbackFonts, textRange.Substring(text), location, layout); - } - else { - return HitTestTextInternal(textRange.Substring(text), location, layout); - } - - } - - /// - /// Calculates hit character index at given location, follows the fallback settings defined in . + /// Calculates hit character index at given location. /// /// The input text to test. /// The input location to test. /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). API_FUNCTION() FORCE_INLINE int32 HitTestText(const StringView& text, const Float2& location) { - if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { - return HitTestTextInternal(Render2D::FallbackFonts, text, location, TextLayoutOptions()); - } - else { - return HitTestTextInternal(text, location, TextLayoutOptions()); - } + return HitTestText(text, location, TextLayoutOptions()); } /// - /// Calculates hit character index at given location, follows the fallback settings defined in . + /// Calculates hit character index at given location. /// /// The input text to test. /// The input text range (substring range of the input text parameter). @@ -833,156 +477,44 @@ public: /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). API_FUNCTION() FORCE_INLINE int32 HitTestText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location) { - if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { - return HitTestTextInternal(Render2D::FallbackFonts, textRange.Substring(text), location, TextLayoutOptions()); - } - else { - return HitTestTextInternal(textRange.Substring(text), location, TextLayoutOptions()); - } + return HitTestText(textRange.Substring(text), location, TextLayoutOptions()); } /// - /// Calculates character position for given text and character index, with font fallbacking disabled. + /// Calculates character position for given text and character index. /// /// The input text to test. /// The text position to get coordinates of. /// The text layout properties. /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() Float2 GetCharPositionInternal(const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout); + API_FUNCTION() Float2 GetCharPosition(const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout); /// - /// Calculates character position for given text and character index, with font fallbacking disabled. + /// Calculates character position for given text and character index. /// /// The input text to test. /// The input text range (substring range of the input text parameter). /// The text position to get coordinates of. /// The text layout properties. /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() FORCE_INLINE Float2 GetCharPositionInternal(const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout) + API_FUNCTION() Float2 GetCharPosition(const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout) { - return GetCharPositionInternal(textRange.Substring(text), index, layout); + return GetCharPosition(textRange.Substring(text), index, layout); } /// - /// Calculates character position for given text and character index, with font fallbacking disabled. - /// - /// The input text to test. - /// The text position to get coordinates of. - /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() FORCE_INLINE Float2 GetCharPositionInternal(const StringView& text, int32 index) - { - return GetCharPositionInternal(text, index, TextLayoutOptions()); - } - - /// - /// Calculates character position for given text and character index, with font fallbacking disabled. - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The text position to get coordinates of. - /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() FORCE_INLINE Float2 GetCharPositionInternal(const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index) - { - return GetCharPositionInternal(textRange.Substring(text), index, TextLayoutOptions()); - } - - /// - /// Calculates character position for given text and character index, using custom fallback options. - /// - /// The input text to test. - /// The text position to get coordinates of. - /// The text layout properties. - /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() Float2 GetCharPositionInternal(FontFallbackList* fallbacks, const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout); - - /// - /// Calculates character position for given text and character index, using custom fallback options. - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The text position to get coordinates of. - /// The text layout properties. - /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() FORCE_INLINE Float2 GetCharPositionInternal(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout) - { - return GetCharPositionInternal(fallbacks, textRange.Substring(text), index, layout); - } - - /// - /// Calculates character position for given text and character index, using custom fallback options. - /// - /// The input text to test. - /// The text position to get coordinates of. - /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() FORCE_INLINE Float2 GetCharPositionInternal(FontFallbackList* fallbacks, const StringView& text, int32 index) - { - return GetCharPositionInternal(fallbacks, text, index, TextLayoutOptions()); - } - - /// - /// Calculates character position for given text and character index, using custom fallback options. - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The text position to get coordinates of. - /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() FORCE_INLINE Float2 GetCharPositionInternal(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index) - { - return GetCharPositionInternal(fallbacks, textRange.Substring(text), index, TextLayoutOptions()); - } - - /// - /// Calculates character position for given text and character index, follows the fallback settings defined in . - /// - /// The input text to test. - /// The text position to get coordinates of. - /// The text layout properties. - /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout) { - if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { - return GetCharPositionInternal(Render2D::FallbackFonts, text, index, layout); - } - else { - return GetCharPositionInternal(text, index, layout); - } - } - - /// - /// Calculates character position for given text and character index, follows the fallback settings defined in . - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The text position to get coordinates of. - /// The text layout properties. - /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout) - { - if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { - return GetCharPositionInternal(Render2D::FallbackFonts, textRange.Substring(text), index, layout); - } - else { - return GetCharPositionInternal(textRange.Substring(text), index, layout); - } - } - - /// - /// Calculates character position for given text and character index, follows the fallback settings defined in . + /// Calculates character position for given text and character index /// /// The input text to test. /// The text position to get coordinates of. /// The character position (upper left corner which can be used for a caret position). API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(const StringView& text, int32 index) { - if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { - return GetCharPositionInternal(Render2D::FallbackFonts, text, index, TextLayoutOptions()); - } - else { - return GetCharPositionInternal(text, index, TextLayoutOptions()); - } + return GetCharPosition(text, index, TextLayoutOptions()); } /// - /// Calculates character position for given text and character index, follows the fallback settings defined in . + /// Calculates character position for given text and character index /// /// The input text to test. /// The input text range (substring range of the input text parameter). @@ -990,12 +522,7 @@ public: /// The character position (upper left corner which can be used for a caret position). API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index) { - if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { - return GetCharPositionInternal(Render2D::FallbackFonts, textRange.Substring(text), index, TextLayoutOptions()); - } - else { - return GetCharPositionInternal(textRange.Substring(text), index, TextLayoutOptions()); - } + return GetCharPosition(textRange.Substring(text), index, TextLayoutOptions()); } /// @@ -1004,7 +531,6 @@ public: void FlushFaceSize() const; public: - // [Object] String ToString() const override; }; diff --git a/Source/Engine/Render2D/FontAsset.cpp b/Source/Engine/Render2D/FontAsset.cpp index ea629367f..53fbdc930 100644 --- a/Source/Engine/Render2D/FontAsset.cpp +++ b/Source/Engine/Render2D/FontAsset.cpp @@ -199,25 +199,29 @@ bool FontAsset::Save(const StringView& path) #endif - -/// -/// Check if the font contains the glyph of a char -/// -/// The char to test. -/// True if the font contains the glyph of the char, otherwise false. - -bool FontAsset::ContainsChar(Char c) const { - return FT_Get_Char_Index(GetFTFace(), c) > 0; +bool FontAsset::ContainsChar(Char c) const +{ + return FT_Get_Char_Index(_face, c) > 0; } void FontAsset::Invalidate() { ScopeLock lock(Locker); - for (auto font : _fonts) - { font->Invalidate(); - } +} + +uint64 FontAsset::GetMemoryUsage() const +{ + Locker.Lock(); + uint64 result = BinaryAsset::GetMemoryUsage(); + result += sizeof(FontAsset) - sizeof(BinaryAsset); + result += sizeof(FT_FaceRec); + result += _fontFile.Length(); + for (auto font : _fonts) + result += sizeof(Font); + Locker.Unlock(); + return result; } bool FontAsset::init(AssetInitData& initData) diff --git a/Source/Engine/Render2D/FontAsset.h b/Source/Engine/Render2D/FontAsset.h index f3c909911..a93276bf1 100644 --- a/Source/Engine/Render2D/FontAsset.h +++ b/Source/Engine/Render2D/FontAsset.h @@ -93,6 +93,7 @@ API_CLASS(NoSpawn) class FLAXENGINE_API FontAsset : public BinaryAsset { DECLARE_BINARY_ASSET_HEADER(FontAsset, 3); friend Font; + private: FT_Face _face; FontOptions _options; @@ -175,7 +176,7 @@ public: #endif /// - /// Check if the font contains the glyph of a char + /// Check if the font contains the glyph of a char. /// /// The char to test. /// True if the font contains the glyph of the char, otherwise false. @@ -186,6 +187,10 @@ public: /// API_FUNCTION() void Invalidate(); +public: + // [BinaryAsset] + uint64 GetMemoryUsage() const override; + protected: // [BinaryAsset] bool init(AssetInitData& initData) override; diff --git a/Source/Engine/Render2D/FontManager.cpp b/Source/Engine/Render2D/FontManager.cpp index 5b08c1d87..2e1ba8c6b 100644 --- a/Source/Engine/Render2D/FontManager.cpp +++ b/Source/Engine/Render2D/FontManager.cpp @@ -27,7 +27,6 @@ using namespace FontManagerImpl; class FontManagerService : public EngineService { public: - FontManagerService() : EngineService(TEXT("Font Manager"), -700) { @@ -155,9 +154,12 @@ bool FontManager::AddNewEntry(Font* font, Char c, FontCharacterEntry& entry) // Get the index to the glyph in the font face const FT_UInt glyphIndex = FT_Get_Char_Index(face, c); - if (glyphIndex == 0) { +#if !BUILD_RELEASE + if (glyphIndex == 0) + { LOG(Warning, "Font `{}` doesn't contain character `\\u{:x}`, consider choosing another font. ", String(face->family_name), c); } +#endif // Load the glyph const FT_Error error = FT_Load_Glyph(face, glyphIndex, glyphFlags); @@ -287,6 +289,7 @@ bool FontManager::AddNewEntry(Font* font, Char c, FontCharacterEntry& entry) entry.UVSize.X = static_cast(slot->Width - 2 * padding); entry.UVSize.Y = static_cast(slot->Height - 2 * padding); entry.Slot = slot; + entry.Font = font; return false; } diff --git a/Source/Engine/Render2D/Render2D.cpp b/Source/Engine/Render2D/Render2D.cpp index fa52baff0..34cb2c693 100644 --- a/Source/Engine/Render2D/Render2D.cpp +++ b/Source/Engine/Render2D/Render2D.cpp @@ -3,7 +3,6 @@ #include "Render2D.h" #include "Font.h" #include "FontManager.h" -#include "FallbackFonts.h" #include "FontTextureAtlas.h" #include "RotatedRectangle.h" #include "SpriteAtlas.h" @@ -55,7 +54,7 @@ const bool DownsampleForBlur = false; PACK_STRUCT(struct Data { Matrix ViewProjection; -}); + }); PACK_STRUCT(struct BlurData { Float2 InvBufferSize; @@ -63,7 +62,7 @@ PACK_STRUCT(struct BlurData { float Dummy0; Float4 Bounds; Float4 WeightAndOffsets[RENDER2D_BLUR_MAX_SAMPLES / 2]; -}); + }); enum class DrawCallType : byte { @@ -181,9 +180,7 @@ struct ClipMask Rectangle Bounds; }; -Render2D::RenderingFeatures Render2D::Features = RenderingFeatures::VertexSnapping; -bool Render2D::EnableFontFallback = true; -FontFallbackList* Render2D::FallbackFonts = nullptr; +Render2D::RenderingFeatures Render2D::Features = RenderingFeatures::VertexSnapping | RenderingFeatures::FallbackFonts; namespace { @@ -197,7 +194,6 @@ namespace // Drawing Array DrawCalls; Array Lines; - Array BlockedTextLines; Array Lines2; bool IsScissorsRectEmpty; bool IsScissorsRectEnabled; @@ -1141,12 +1137,12 @@ void DrawBatch(int32 startIndex, int32 count) } // Draw - Context->BindVB(ToSpan(&vb, 1)); // TODO: reduce bindings frequency - Context->BindIB(ib); // TODO: reduce bindings frequency + Context->BindVB(ToSpan(&vb, 1)); + Context->BindIB(ib); Context->DrawIndexed(countIb, 0, d.StartIB); } -void Render2D::DrawTextInternal(Font* font, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial) +void Render2D::DrawText(Font* font, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial) { RENDER2D_CHECK_RENDERING_STATE; @@ -1163,6 +1159,7 @@ void Render2D::DrawTextInternal(Font* font, const StringView& text, const Color& FontCharacterEntry previous; int32 kerning; float scale = 1.0f / FontManager::FontScale; + const bool enableFallbackFonts = EnumHasAllFlags(Features, RenderingFeatures::FallbackFonts); // Render all characters FontCharacterEntry entry; @@ -1178,7 +1175,7 @@ void Render2D::DrawTextInternal(Font* font, const StringView& text, const Color& drawCall.AsChar.Mat = nullptr; } Float2 pointer = location; - for (int32 currentIndex = 0; currentIndex < text.Length(); currentIndex++) + for (int32 currentIndex = 0; currentIndex <= text.Length(); currentIndex++) { // Cache current character const Char currentChar = text[currentIndex]; @@ -1187,7 +1184,7 @@ void Render2D::DrawTextInternal(Font* font, const StringView& text, const Color& if (currentChar != '\n') { // Get character entry - font->GetCharacter(currentChar, entry); + font->GetCharacter(currentChar, entry, enableFallbackFonts); // Check if need to select/change font atlas (since characters even in the same font may be located in different atlases) if (fontAtlas == nullptr || entry.TextureIndex != fontAtlasIndex) @@ -1214,7 +1211,7 @@ void Render2D::DrawTextInternal(Font* font, const StringView& text, const Color& // Get kerning if (!isWhitespace && previous.IsValid) { - kerning = font->GetKerning(previous.Character, entry.Character); + kerning = entry.Font->GetKerning(previous.Character, entry.Character); } else { @@ -1254,12 +1251,12 @@ void Render2D::DrawTextInternal(Font* font, const StringView& text, const Color& } } -void Render2D::DrawTextInternal(Font* font, const StringView& text, const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial) +void Render2D::DrawText(Font* font, const StringView& text, const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial) { - DrawTextInternal(font, textRange.Substring(text), color, location, customMaterial); + DrawText(font, textRange.Substring(text), color, location, customMaterial); } -void Render2D::DrawTextInternal(Font* font, const StringView& text, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) +void Render2D::DrawText(Font* font, const StringView& text, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) { RENDER2D_CHECK_RENDERING_STATE; @@ -1277,6 +1274,7 @@ void Render2D::DrawTextInternal(Font* font, const StringView& text, const Color& FontCharacterEntry previous; int32 kerning; float scale = layout.Scale / FontManager::FontScale; + const bool enableFallbackFonts = EnumHasAllFlags(Features, RenderingFeatures::FallbackFonts); // Process text to get lines Lines.Clear(); @@ -1303,10 +1301,14 @@ void Render2D::DrawTextInternal(Font* font, const StringView& text, const Color& // Render all characters from the line for (int32 charIndex = line.FirstCharIndex; charIndex <= line.LastCharIndex; charIndex++) { - const Char c = text[charIndex]; - if (c != '\n') + // Cache current character + const Char currentChar = text[charIndex]; + + // Check if it isn't a newline character + if (currentChar != '\n') { - font->GetCharacter(c, entry); + // Get character entry + font->GetCharacter(currentChar, entry, enableFallbackFonts); // Check if need to select/change font atlas (since characters even in the same font may be located in different atlases) if (fontAtlas == nullptr || entry.TextureIndex != fontAtlasIndex) @@ -1328,10 +1330,10 @@ void Render2D::DrawTextInternal(Font* font, const StringView& text, const Color& } // Get kerning - const bool isWhitespace = StringUtils::IsWhitespace(c); + const bool isWhitespace = StringUtils::IsWhitespace(currentChar); if (!isWhitespace && previous.IsValid) { - kerning = font->GetKerning(previous.Character, entry.Character); + kerning = entry.Font->GetKerning(previous.Character, entry.Character); } else { @@ -1367,317 +1369,9 @@ void Render2D::DrawTextInternal(Font* font, const StringView& text, const Color& } } -void Render2D::DrawTextInternal(Font* font, const StringView& text, const TextRange& textRange, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) +void Render2D::DrawText(Font* font, const StringView& text, const TextRange& textRange, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) { - DrawTextInternal(font, textRange.Substring(text), color, layout, customMaterial); -} - -void Render2D::DrawTextInternal(Font* font, FontFallbackList* fallbacks, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial) -{ - RENDER2D_CHECK_RENDERING_STATE; - - // Check if there is no need to do anything - if (font == nullptr || text.Length() < 0) - return; - - // Temporary data - const Array& fallbackFonts = fallbacks->GetFontList(font->GetSize()); - uint32 fontAtlasIndex = 0; - FontTextureAtlas* fontAtlas = nullptr; - Float2 invAtlasSize = Float2::One; - FontCharacterEntry previous; - int32 kerning; - float scale = 1.0f / FontManager::FontScale; - - // Process text to get lines - Array maxAscenders; - - // Render all characters - FontCharacterEntry entry; - Render2DDrawCall drawCall; - if (customMaterial) - { - drawCall.Type = DrawCallType::DrawCharMaterial; - drawCall.AsChar.Mat = customMaterial; - } - else - { - drawCall.Type = DrawCallType::DrawChar; - drawCall.AsChar.Mat = nullptr; - } - - int32 lineIndex = 0; - maxAscenders.Add(0); - - auto getFont = [&](int32 index)->Font* { - return index >= 0 ? fallbackFonts[index] : font; - }; - - // Preprocess the text to determine vertical offset of blocks - for (int32 currentIndex = 0; currentIndex < text.Length(); currentIndex++) - { - const Char c = text[currentIndex]; - if (c != '\n') { - int32 fontIndex = fallbacks->GetCharFallbackIndex(c, font); - maxAscenders[lineIndex] = Math::Max(maxAscenders[lineIndex], - static_cast(getFont(fontIndex)->GetAscender())); - } - else { - lineIndex++; - maxAscenders.Add(0); - } - } - - lineIndex = 0; - // The following code cut the text into blocks, according to the font used to render - Float2 pointer = location; - // The starting index of the current block - int32 startIndex = 0; - // The index of the font used by the current block - int32 currentFontIndex = fallbacks->GetCharFallbackIndex(text[0], font); - // The maximum font height of the current line - float maxHeight = 0; - for (int32 currentIndex = 0; currentIndex < text.Length(); currentIndex++) - { - // Cache current character - const Char currentChar = text[currentIndex]; - int32 nextCharIndex = currentIndex + 1; - bool moveBlock = false; - bool moveLine = false; - int32 nextFontIndex = currentFontIndex; - - // Submit block if text ends - if (nextCharIndex == text.Length()) { - moveBlock = true; - } - - // Check if it isn't a newline character - if (currentChar != '\n') - { - // Get character entry - if (nextCharIndex < text.Length()) { - nextFontIndex = fallbacks->GetCharFallbackIndex(text[nextCharIndex], font); - } - - if (nextFontIndex != currentFontIndex) { - moveBlock = true; - } - } - else - { - // Move - moveLine = moveBlock = true; - } - - if (moveBlock) { - // Render the pending block before beginning the new block - auto fontHeight = getFont(currentFontIndex)->GetHeight(); - maxHeight = Math::Max(maxHeight, static_cast(fontHeight)); - auto fontDescender = getFont(currentFontIndex)->GetDescender(); - for (int32 renderIndex = startIndex; renderIndex <= currentIndex; renderIndex++) - { - // Get character entry - getFont(currentFontIndex)->GetCharacter(text[renderIndex], entry); - - // Check if need to select/change font atlas (since characters even in the same font may be located in different atlases) - if (fontAtlas == nullptr || entry.TextureIndex != fontAtlasIndex) - { - // Get texture atlas that contains current character - fontAtlasIndex = entry.TextureIndex; - fontAtlas = FontManager::GetAtlas(fontAtlasIndex); - if (fontAtlas) - { - fontAtlas->EnsureTextureCreated(); - drawCall.AsChar.Tex = fontAtlas->GetTexture(); - invAtlasSize = 1.0f / fontAtlas->GetSize(); - } - else - { - drawCall.AsChar.Tex = nullptr; - invAtlasSize = 1.0f; - } - } - - // Check if character is a whitespace - const bool isWhitespace = StringUtils::IsWhitespace(text[renderIndex]); - - // Get kerning - if (!isWhitespace && previous.IsValid) - { - kerning = getFont(currentFontIndex)->GetKerning(previous.Character, entry.Character); - } - else - { - kerning = 0; - } - pointer.X += kerning * scale; - previous = entry; - - // Omit whitespace characters - if (!isWhitespace) - { - // Calculate character size and atlas coordinates - const float x = pointer.X + entry.OffsetX * scale; - const float y = pointer.Y + (fontHeight + fontDescender - entry.OffsetY) * scale; - - Rectangle charRect(x, y + (maxAscenders[lineIndex] - getFont(currentFontIndex)->GetAscender()) / 2, entry.UVSize.X * scale, entry.UVSize.Y * scale); - - Float2 upperLeftUV = entry.UV * invAtlasSize; - Float2 rightBottomUV = (entry.UV + entry.UVSize) * invAtlasSize; - - // Add draw call - drawCall.StartIB = IBIndex; - drawCall.CountIB = 6; - DrawCalls.Add(drawCall); - WriteRect(charRect, color, upperLeftUV, rightBottomUV); - } - - // Move - pointer.X += entry.AdvanceX * scale; - } - - if (moveLine) { - pointer.X = location.X; - pointer.Y += maxHeight * scale; - // Clear max height - maxHeight = 0; - lineIndex++; - } - - // Start new block - startIndex = nextCharIndex; - currentFontIndex = nextFontIndex; - } - } -} - -void Render2D::DrawTextInternal(Font* font, FontFallbackList* fallbacks, const StringView& text, const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial) -{ - DrawTextInternal(font, fallbacks, textRange.Substring(text), color, location, customMaterial); -} - -void Render2D::DrawTextInternal(Font* font, FontFallbackList* fallbacks, const StringView& text, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) -{ - RENDER2D_CHECK_RENDERING_STATE; - - // Check if there is no need to do anything - if (font == nullptr || text.IsEmpty() || layout.Scale <= ZeroTolerance) - return; - - // Temporary data - const Array& fallbackFonts = fallbacks->GetFontList(font->GetSize()); - uint32 fontAtlasIndex = 0; - FontTextureAtlas* fontAtlas = nullptr; - Float2 invAtlasSize = Float2::One; - FontCharacterEntry previous; - int32 kerning; - float scale = layout.Scale / FontManager::FontScale; - - // Process text to get lines - BlockedTextLines.Clear(); - font->ProcessText(fallbacks, text, BlockedTextLines, layout); - - // Render all lines - FontCharacterEntry entry; - Render2DDrawCall drawCall; - if (customMaterial) - { - drawCall.Type = DrawCallType::DrawCharMaterial; - drawCall.AsChar.Mat = customMaterial; - } - else - { - drawCall.Type = DrawCallType::DrawChar; - drawCall.AsChar.Mat = nullptr; - } - - auto getFont = [&](int32 index)->Font* { - return index >= 0 ? fallbackFonts[index] : font; - }; - - for (int32 lineIndex = 0; lineIndex < BlockedTextLines.Count(); lineIndex++) - { - const BlockedTextLineCache& line = BlockedTextLines[lineIndex]; - for (int32 blockIndex = 0; blockIndex < line.Blocks.Count(); blockIndex++) - { - const FontBlockCache& block = BlockedTextLines[lineIndex].Blocks[blockIndex]; - auto fontHeight = getFont(block.FallbackFontIndex)->GetHeight(); - auto fontDescender = getFont(block.FallbackFontIndex)->GetDescender(); - Float2 pointer = line.Location + block.Location; - - for (int32 charIndex = block.FirstCharIndex; charIndex <= block.LastCharIndex; charIndex++) - { - Char c = text[charIndex]; - if (c == '\n') - { - continue; - } - - // Get character entry - getFont(block.FallbackFontIndex)->GetCharacter(c, entry); - - // Check if need to select/change font atlas (since characters even in the same font may be located in different atlases) - if (fontAtlas == nullptr || entry.TextureIndex != fontAtlasIndex) - { - // Get texture atlas that contains current character - fontAtlasIndex = entry.TextureIndex; - fontAtlas = FontManager::GetAtlas(fontAtlasIndex); - if (fontAtlas) - { - fontAtlas->EnsureTextureCreated(); - invAtlasSize = 1.0f / fontAtlas->GetSize(); - drawCall.AsChar.Tex = fontAtlas->GetTexture(); - } - else - { - invAtlasSize = 1.0f; - drawCall.AsChar.Tex = nullptr; - } - } - - // Get kerning - const bool isWhitespace = StringUtils::IsWhitespace(c); - if (!isWhitespace && previous.IsValid) - { - kerning = getFont(block.FallbackFontIndex)->GetKerning(previous.Character, entry.Character); - } - else - { - kerning = 0; - } - pointer.X += (float)kerning * scale; - previous = entry; - - // Omit whitespace characters - if (!isWhitespace) - { - // Calculate character size and atlas coordinates - const float x = pointer.X + entry.OffsetX * scale; - const float y = pointer.Y - entry.OffsetY * scale + Math::Ceil((fontHeight + fontDescender) * scale); - - Rectangle charRect(x, y, entry.UVSize.X * scale, entry.UVSize.Y * scale); - charRect.Offset(layout.Bounds.Location); - - Float2 upperLeftUV = entry.UV * invAtlasSize; - Float2 rightBottomUV = (entry.UV + entry.UVSize) * invAtlasSize; - - // Add draw call - drawCall.StartIB = IBIndex; - drawCall.CountIB = 6; - DrawCalls.Add(drawCall); - WriteRect(charRect, color, upperLeftUV, rightBottomUV); - } - - // Move - pointer.X += entry.AdvanceX * scale; - } - } - } -} - -void Render2D::DrawTextInternal(Font* font, FontFallbackList* fallbacks, const StringView& text, const TextRange& textRange, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) -{ - DrawTextInternal(font, fallbacks, textRange.Substring(text), color, layout, customMaterial); + DrawText(font, textRange.Substring(text), color, layout, customMaterial); } FORCE_INLINE bool NeedAlphaWithTint(const Color& color) @@ -2181,22 +1875,22 @@ void Render2D::DrawBezier(const Float2& p1, const Float2& p2, const Float2& p3, { RENDER2D_CHECK_RENDERING_STATE; - // Find amount of blocks to use + // Find amount of segments to use const Float2 d1 = p2 - p1; const Float2 d2 = p3 - p2; const Float2 d3 = p4 - p3; const float len = d1.Length() + d2.Length() + d3.Length(); - const int32 blockCount = Math::Clamp(Math::CeilToInt(len * 0.05f), 1, 100); - const float blockCountInv = 1.0f / blockCount; + const int32 segmentCount = Math::Clamp(Math::CeilToInt(len * 0.05f), 1, 100); + const float segmentCountInv = 1.0f / segmentCount; - // Draw blocked curve + // Draw segmented curve Float2 p; AnimationUtils::Bezier(p1, p2, p3, p4, 0, p); Lines2.Clear(); Lines2.Add(p); - for (int32 i = 1; i <= blockCount; i++) + for (int32 i = 1; i <= segmentCount; i++) { - const float t = i * blockCountInv; + const float t = i * segmentCountInv; AnimationUtils::Bezier(p1, p2, p3, p4, t, p); Lines2.Add(p); } diff --git a/Source/Engine/Render2D/Render2D.cs b/Source/Engine/Render2D/Render2D.cs index b36f155df..c4d9e81b4 100644 --- a/Source/Engine/Render2D/Render2D.cs +++ b/Source/Engine/Render2D/Render2D.cs @@ -1,13 +1,11 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. -using FlaxEngine.GUI; using System; namespace FlaxEngine { partial class Render2D { - /// /// Pushes transformation layer. /// @@ -102,7 +100,7 @@ namespace FlaxEngine } /// - /// Draws a text, follows the font fallback settings defined in . + /// Draws a text. /// /// The font to use. /// The text to render. @@ -128,7 +126,7 @@ namespace FlaxEngine } /// - /// Draws a text using a custom material shader. Given material must have GUI domain and a public parameter named Font (texture parameter used for a font atlas sampling). Follows the font fallback settings defined in . + /// Draws a text using a custom material shader. Given material must have GUI domain and a public parameter named Font (texture parameter used for a font atlas sampling). /// /// The font to use. /// Custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. diff --git a/Source/Engine/Render2D/Render2D.h b/Source/Engine/Render2D/Render2D.h index 6657d8542..5e5e952dc 100644 --- a/Source/Engine/Render2D/Render2D.h +++ b/Source/Engine/Render2D/Render2D.h @@ -15,7 +15,6 @@ struct Matrix3x3; struct Viewport; struct TextRange; class Font; -class FontFallbackList; class GPUPipelineState; class GPUTexture; class GPUTextureView; @@ -34,7 +33,7 @@ API_CLASS(Static) class FLAXENGINE_API Render2D /// /// The rendering features and options flags. /// - API_ENUM(Attributes = "Flags") enum class RenderingFeatures + API_ENUM(Attributes="Flags") enum class RenderingFeatures { /// /// The none. @@ -45,6 +44,11 @@ API_CLASS(Static) class FLAXENGINE_API Render2D /// Enables automatic geometry vertices snapping to integer coordinates in screen space. Reduces aliasing and sampling artifacts. Might be disabled for 3D projection viewport or for complex UI transformations. /// VertexSnapping = 1, + + /// + /// Enables automatic characters usage from fallback fonts. + /// + FallbackFonts = 2, }; struct CustomData @@ -54,7 +58,6 @@ API_CLASS(Static) class FLAXENGINE_API Render2D }; public: - /// /// Checks if interface is during rendering phrase (Draw calls may be performed without failing). /// @@ -70,10 +73,6 @@ public: /// API_FIELD() static RenderingFeatures Features; - API_FIELD() static bool EnableFontFallback; - - API_FIELD() static FontFallbackList* FallbackFonts; - /// /// Called when frame rendering begins by the graphics device. /// @@ -180,17 +179,17 @@ public: public: /// - /// Draws a text, with font fallbacking disabled. + /// Draws a text. /// /// The font to use. /// The text to render. /// The text color. /// The text location. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawTextInternal(Font* font, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawText(Font* font, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); /// - /// Draws a text, with font fallbacking disabled. + /// Draws a text. /// /// The font to use. /// The text to render. @@ -198,20 +197,20 @@ public: /// The text color. /// The text location. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawTextInternal(Font* font, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawText(Font* font, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); /// - /// Draws a text with formatting, with font fallbacking disabled. + /// Draws a text with formatting. /// /// The font to use. /// The text to render. /// The text color. /// The text layout properties. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawTextInternal(Font* font, const StringView& text, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawText(Font* font, const StringView& text, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); /// - /// Draws a text with formatting, with font fallbacking disabled. + /// Draws a text with formatting. /// /// The font to use. /// The text to render. @@ -219,120 +218,7 @@ public: /// The text color. /// The text layout properties. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawTextInternal(Font* font, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); - - /// - /// Draws a text, using custom fallback options. - /// - /// The fonts to use, ordered by priority. - /// The text to render. - /// The input text range (substring range of the input text parameter). - /// The text color. - /// The text location. - /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawTextInternal(Font* font, FontFallbackList* fallbacks, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); - - /// - /// Draws a text with formatting, using custom fallback options. - /// - /// The fonts to use, ordered by priority. - /// The text to render. - /// The text color. - /// The text layout properties. - /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawTextInternal(Font* font, FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); - - /// - /// Draws a text with formatting, using custom fallback options. - /// - /// The fonts to use, ordered by priority. - /// The text to render. - /// The input text range (substring range of the input text parameter). - /// The text color. - /// The text layout properties. - /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawTextInternal(Font* font, FontFallbackList* fallbacks, const StringView& text, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); - - /// - /// Draws a text with formatting, using custom fallback options. - /// - /// The fonts to use, ordered by priority. - /// The text to render. - /// The input text range (substring range of the input text parameter). - /// The text color. - /// The text layout properties. - /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawTextInternal(Font* font, FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); - - /// - /// Draws a text, follows the fallback settings defined in . - /// - /// The font to use. - /// The text to render. - /// The text color. - /// The text location. - /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() FORCE_INLINE static void DrawText(Font* font, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr) { - if (EnableFontFallback && FallbackFonts) { - DrawTextInternal(font, FallbackFonts, text, color, location, customMaterial); - } - else { - DrawTextInternal(font, text, color, location, customMaterial); - } - } - - /// - /// Draws a text, follows the fallback settings defined in . - /// - /// The font to use. - /// The text to render. - /// The input text range (substring range of the input text parameter). - /// The text color. - /// The text location. - /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() FORCE_INLINE static void DrawText(Font* font, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr) { - if (EnableFontFallback && FallbackFonts) { - DrawTextInternal(font, FallbackFonts, text, textRange, color, location, customMaterial); - } - else { - DrawTextInternal(font, text, textRange, color, location, customMaterial); - } - } - - /// - /// Draws a text with formatting, follows the fallback settings defined in . - /// - /// The font to use. - /// The text to render. - /// The text color. - /// The text layout properties. - /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() FORCE_INLINE static void DrawText(Font* font, const StringView& text, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr) { - if (EnableFontFallback && FallbackFonts) { - DrawTextInternal(font, FallbackFonts, text, color, layout, customMaterial); - } - else { - DrawTextInternal(font, text, color, layout, customMaterial); - } - } - - /// - /// Draws a text with formatting, follows the fallback settings defined in . - /// - /// The font to use. - /// The text to render. - /// The input text range (substring range of the input text parameter). - /// The text color. - /// The text layout properties. - /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() FORCE_INLINE static void DrawText(Font* font, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr) { - if (EnableFontFallback && FallbackFonts) { - DrawTextInternal(font, FallbackFonts, text, textRange, color, layout, customMaterial); - } - else { - DrawTextInternal(font, text, textRange, color, layout, customMaterial); - } - } + API_FUNCTION() static void DrawText(Font* font, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); /// /// Fills a rectangle area. @@ -571,3 +457,5 @@ public: /// The color. API_FUNCTION() static void FillTriangle(const Float2& p0, const Float2& p1, const Float2& p2, const Color& color); }; + +DECLARE_ENUM_OPERATORS(Render2D::RenderingFeatures); diff --git a/Source/Engine/Scripting/Scripting.cs b/Source/Engine/Scripting/Scripting.cs index a4cfd76b1..188333ff1 100644 --- a/Source/Engine/Scripting/Scripting.cs +++ b/Source/Engine/Scripting/Scripting.cs @@ -294,12 +294,14 @@ namespace FlaxEngine style.DragWindow = style.BackgroundSelected * 0.7f; // Use optionally bundled default font (matches Editor) - FontAsset defaultFont = Content.LoadAsyncInternal("Editor/Fonts/Roboto-Regular"); - - style.FontTitle = new FontReference(defaultFont, 18).GetFont(); - style.FontLarge = new FontReference(defaultFont, 14).GetFont(); - style.FontMedium = new FontReference(defaultFont, 9).GetFont(); - style.FontSmall = new FontReference(defaultFont, 9).GetFont(); + var defaultFont = Content.LoadAsyncInternal("Editor/Fonts/Roboto-Regular"); + if (defaultFont) + { + style.FontTitle = defaultFont.CreateFont(18); + style.FontLarge = defaultFont.CreateFont(14); + style.FontMedium = defaultFont.CreateFont(9); + style.FontSmall = defaultFont.CreateFont(9); + } Style.Current = style; } diff --git a/Source/Engine/UI/GUI/Common/Label.cs b/Source/Engine/UI/GUI/Common/Label.cs index 8de54fda3..3c7c04fb2 100644 --- a/Source/Engine/UI/GUI/Common/Label.cs +++ b/Source/Engine/UI/GUI/Common/Label.cs @@ -182,12 +182,6 @@ namespace FlaxEngine.GUI set => _autoFitTextRange = value; } - /// - /// Gets or sets whether to fallback when the primary font cannot render a char. - /// - [EditorOrder(120), DefaultValue(true), Tooltip("Whether to fallback when the font cannot render a char.")] - public bool EnableFontFallback { get; set; } = true; - /// /// Initializes a new instance of the class. /// @@ -239,23 +233,7 @@ namespace FlaxEngine.GUI } } - if (EnableFontFallback) - { - Render2D.DrawText(_font.GetFont(), Material, _text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale); - } - else - { - var layout = new TextLayoutOptions - { - Bounds = rect, - HorizontalAlignment = hAlignment, - VerticalAlignment = wAlignment, - TextWrapping = Wrapping, - Scale = scale, - BaseLinesGapScale = BaseLinesGapScale, - }; - Render2D.DrawTextInternal(_font.GetFont(), _text, color, ref layout, Material); - } + Render2D.DrawText(_font.GetFont(), Material, _text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale); if (ClipText) Render2D.PopClip(); @@ -276,8 +254,7 @@ namespace FlaxEngine.GUI layout.Bounds.Size.X = Width - Margin.Width; else if (_autoWidth && !_autoHeight) layout.Bounds.Size.Y = Height - Margin.Height; - _textSize = EnableFontFallback ? - font.MeasureText(_text, ref layout) : font.MeasureTextInternal(_text, ref layout); + _textSize = font.MeasureText(_text, ref layout); _textSize.Y *= BaseLinesGapScale; // Check if size is controlled via text diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs b/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs index ab01df12b..2270136ae 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs @@ -1,6 +1,5 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. -using System; using System.Collections.Generic; using System.Runtime.InteropServices; using FlaxEngine.Utilities; diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs b/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs index df8e0be7c..b4351da75 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs @@ -213,18 +213,18 @@ namespace FlaxEngine.GUI style.Alignment &= ~TextBlockStyle.Alignments.VerticalMask; switch (valign) { - case "top": - style.Alignment = TextBlockStyle.Alignments.Top; - break; - case "bottom": - style.Alignment = TextBlockStyle.Alignments.Bottom; - break; - case "middle": - style.Alignment = TextBlockStyle.Alignments.Middle; - break; - case "baseline": - style.Alignment = TextBlockStyle.Alignments.Baseline; - break; + case "top": + style.Alignment = TextBlockStyle.Alignments.Top; + break; + case "bottom": + style.Alignment = TextBlockStyle.Alignments.Bottom; + break; + case "middle": + style.Alignment = TextBlockStyle.Alignments.Middle; + break; + case "baseline": + style.Alignment = TextBlockStyle.Alignments.Baseline; + break; } } context.StyleStack.Push(style); @@ -245,15 +245,15 @@ namespace FlaxEngine.GUI style.Alignment &= ~TextBlockStyle.Alignments.VerticalMask; switch (valign) { - case "left": - style.Alignment = TextBlockStyle.Alignments.Left; - break; - case "right": - style.Alignment = TextBlockStyle.Alignments.Right; - break; - case "center": - style.Alignment = TextBlockStyle.Alignments.Center; - break; + case "left": + style.Alignment = TextBlockStyle.Alignments.Left; + break; + case "right": + style.Alignment = TextBlockStyle.Alignments.Right; + break; + case "center": + style.Alignment = TextBlockStyle.Alignments.Center; + break; } } context.StyleStack.Push(style); diff --git a/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs b/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs index 46f0fb1ad..438a7e3d8 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs @@ -316,7 +316,6 @@ namespace FlaxEngine.GUI color = textBlock.Style.ShadowColor; if (!enabled) color *= 0.6f; - // We don't need font fallbacks for rich text since the font is user-selected Render2D.DrawText(font, _text, ref textBlock.Range, color, textBlock.Bounds.Location + textBlock.Style.ShadowOffset, textBlock.Style.CustomMaterial); } diff --git a/Source/Engine/UI/GUI/Common/TextBox.cs b/Source/Engine/UI/GUI/Common/TextBox.cs index 53266a1b2..ee4f744a6 100644 --- a/Source/Engine/UI/GUI/Common/TextBox.cs +++ b/Source/Engine/UI/GUI/Common/TextBox.cs @@ -1,8 +1,5 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. - -using System.ComponentModel; - namespace FlaxEngine.GUI { /// @@ -67,12 +64,6 @@ namespace FlaxEngine.GUI [EditorDisplay("Text Style"), EditorOrder(2022), Tooltip("The color of the selection (Transparent if not used).")] public Color SelectionColor { get; set; } - /// - /// Gets or sets whether to fallback when the primary font cannot render a char. - /// - [EditorOrder(120), DefaultValue(true), Tooltip("Whether to fallback when the font cannot render a char.")] - public bool EnableFontFallback { get; set; } = true; - /// /// Initializes a new instance of the class. /// @@ -112,8 +103,7 @@ namespace FlaxEngine.GUI return Float2.Zero; } - return EnableFontFallback ? font.MeasureText(_text, ref _layout) : - font.MeasureTextInternal(_text, ref _layout); + return font.MeasureText(_text, ref _layout); } /// @@ -126,9 +116,8 @@ namespace FlaxEngine.GUI return Float2.Zero; } - height = (EnableFontFallback ? font.GetMaxHeight() : font.Height) / DpiScale; - return EnableFontFallback ? font.GetCharPosition(_text, index, ref _layout) : - font.GetCharPositionInternal(_text, index, ref _layout); + height = font.Height / DpiScale; + return font.GetCharPosition(_text, index, ref _layout); } /// @@ -140,8 +129,7 @@ namespace FlaxEngine.GUI return 0; } - return EnableFontFallback ? font.HitTestText(_text, location, ref _layout) : - font.HitTestTextInternal(_text, location, ref _layout); + return font.HitTestText(_text, location, ref _layout); } /// @@ -180,13 +168,9 @@ namespace FlaxEngine.GUI // Check if sth is selected to draw selection if (HasSelection) { - var leftEdge = EnableFontFallback ? - font.GetCharPosition(_text, SelectionLeft, ref _layout) : - font.GetCharPositionInternal(_text, SelectionLeft, ref _layout); - var rightEdge = EnableFontFallback ? - font.GetCharPosition(_text, SelectionRight, ref _layout) : - font.GetCharPositionInternal(_text, SelectionRight, ref _layout); - float fontHeight = font.GetMaxHeight() / DpiScale; + var leftEdge = font.GetCharPosition(_text, SelectionLeft, ref _layout); + var rightEdge = font.GetCharPosition(_text, SelectionRight, ref _layout); + float fontHeight = font.Height / DpiScale; // Draw selection background float alpha = Mathf.Min(1.0f, Mathf.Cos(_animateTime * BackgroundSelectedFlashSpeed) * 0.5f + 1.3f); @@ -226,19 +210,11 @@ namespace FlaxEngine.GUI var color = TextColor; if (!enabled) color *= 0.6f; - if (EnableFontFallback) - Render2D.DrawText(font, _text, color, ref _layout, TextMaterial); - else - // Draw without fallback - Render2D.DrawTextInternal(font, _text, color, ref _layout, TextMaterial); + Render2D.DrawText(font, _text, color, ref _layout, TextMaterial); } else if (!string.IsNullOrEmpty(_watermarkText) && !IsFocused) { - if (EnableFontFallback) - Render2D.DrawText(font, _watermarkText, WatermarkTextColor, ref _layout, TextMaterial); - else - // Draw without fallback - Render2D.DrawTextInternal(font, _watermarkText, WatermarkTextColor, ref _layout, TextMaterial); + Render2D.DrawText(font, _watermarkText, WatermarkTextColor, ref _layout, TextMaterial); } // Caret diff --git a/Source/Engine/UI/GUI/Common/TextBoxBase.cs b/Source/Engine/UI/GUI/Common/TextBoxBase.cs index 55b58634d..35819da79 100644 --- a/Source/Engine/UI/GUI/Common/TextBoxBase.cs +++ b/Source/Engine/UI/GUI/Common/TextBoxBase.cs @@ -280,13 +280,13 @@ namespace FlaxEngine.GUI /// [EditorDisplay("Border Style"), EditorOrder(2010), Tooltip("Whether to have a border."), ExpandGroups] public bool HasBorder { get; set; } = true; - + /// /// Gets or sets the border thickness. /// [EditorDisplay("Border Style"), EditorOrder(2011), Tooltip("The thickness of the border."), Limit(0)] public float BorderThickness { get; set; } = 1.0f; - + /// /// Gets or sets the color of the border (Transparent if not used). /// diff --git a/Source/Engine/UI/TextRender.cpp b/Source/Engine/UI/TextRender.cpp index aa49dac45..d190f1123 100644 --- a/Source/Engine/UI/TextRender.cpp +++ b/Source/Engine/UI/TextRender.cpp @@ -239,7 +239,7 @@ void TextRender::UpdateLayout() const bool isWhitespace = StringUtils::IsWhitespace(c); if (!isWhitespace && previous.IsValid) { - kerning = font->GetKerning(previous.Character, entry.Character); + kerning = entry.Font->GetKerning(previous.Character, entry.Character); } else { From 9335925b486c02a32c5c2ebb0ec03f15509482a8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 18 Feb 2024 20:16:20 +0100 Subject: [PATCH 50/61] Simplify code #1949 --- Source/Engine/Level/Actor.cpp | 11 +++-------- Source/Engine/Level/Actor.h | 2 +- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/Source/Engine/Level/Actor.cpp b/Source/Engine/Level/Actor.cpp index 6e8c2e7b4..cd6b156d8 100644 --- a/Source/Engine/Level/Actor.cpp +++ b/Source/Engine/Level/Actor.cpp @@ -1358,16 +1358,11 @@ bool Actor::IsPrefabRoot() const Actor* Actor::GetPrefabRoot() { - if (!this->HasPrefabLink()) - { - return NULL; - } - + if (!HasPrefabLink()) + return nullptr; Actor* result = this; - while (!result == NULL && !result->IsPrefabRoot()) - { + while (result && !result->IsPrefabRoot()) result = result->GetParent(); - } return result; } diff --git a/Source/Engine/Level/Actor.h b/Source/Engine/Level/Actor.h index d34d66e49..feba73997 100644 --- a/Source/Engine/Level/Actor.h +++ b/Source/Engine/Level/Actor.h @@ -794,7 +794,7 @@ public: { return (T*)FindActor(T::GetStaticClass(), name); } - + /// /// Tries to find the actor of the given type and tag in this actor hierarchy (checks this actor and all children hierarchy). /// From a5a16c3192fb10cef629cb975926b8411401682e Mon Sep 17 00:00:00 2001 From: envision3d Date: Sun, 18 Feb 2024 20:32:32 -0600 Subject: [PATCH 51/61] Fix issue with CharacterController initialization --- Source/Engine/Physics/Colliders/CharacterController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Physics/Colliders/CharacterController.cpp b/Source/Engine/Physics/Colliders/CharacterController.cpp index e9f86ebab..61fc4d471 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.cpp +++ b/Source/Engine/Physics/Colliders/CharacterController.cpp @@ -210,7 +210,7 @@ void CharacterController::CreateController() // Setup PhysicsBackend::SetControllerUpDirection(_controller, _upDirection); - PhysicsBackend::SetShapeLocalPose(_shape, _center, Quaternion::Identity); + PhysicsBackend::SetShapeLocalPose(_shape, Vector3.Zero, Quaternion::Identity); UpdateLayerBits(); UpdateBounds(); } From a061afd840da6b2af921b5f41cdb770644d5b188 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 19 Feb 2024 10:54:29 +0100 Subject: [PATCH 52/61] Cleanup for #1827 --- Source/Editor/Content/Proxy/ScriptProxy.cs | 3 +- .../CustomEditors/Dedicated/ScriptsEditor.cs | 65 +++++++++---------- Source/Editor/GUI/ItemsListContextMenu.cs | 25 +++---- 3 files changed, 45 insertions(+), 48 deletions(-) diff --git a/Source/Editor/Content/Proxy/ScriptProxy.cs b/Source/Editor/Content/Proxy/ScriptProxy.cs index f688f37df..d728dcc38 100644 --- a/Source/Editor/Content/Proxy/ScriptProxy.cs +++ b/Source/Editor/Content/Proxy/ScriptProxy.cs @@ -69,8 +69,7 @@ namespace FlaxEditor.Content /// public override bool IsFileNameValid(string filename) { - // Scripts cannot start with digit. - if (Char.IsDigit(filename[0])) + if (char.IsDigit(filename[0])) return false; if (filename.Equals("Script")) return false; diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs index 216c205a1..63374361d 100644 --- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs @@ -21,6 +21,7 @@ namespace FlaxEditor.CustomEditors.Dedicated internal class NewScriptItem : ItemsListContextMenu.Item { private string _scriptName; + public string ScriptName { get => _scriptName; @@ -37,6 +38,7 @@ namespace FlaxEditor.CustomEditors.Dedicated TooltipText = "Create a new script"; } } + /// /// Drag and drop scripts area control. /// @@ -99,18 +101,14 @@ namespace FlaxEditor.CustomEditors.Dedicated { if (!IsValidScriptName(text)) return; - var items = cm.ItemsPanel.Children.Count(x => x.Visible && x is not NewScriptItem); - if (items == 0) + if (!cm.ItemsPanel.Children.Any(x => x.Visible && x is not NewScriptItem)) { - // If there are no visible items, that means the search failed so we can find the create script - // button or create one if it's the first time. - - var createScriptItem = cm.ItemsPanel.Children.FirstOrDefault(x => x is NewScriptItem); - if (createScriptItem != null) + // If there are no visible items, that means the search failed so we can find the create script button or create one if it's the first time + var newScriptItem = (NewScriptItem)cm.ItemsPanel.Children.FirstOrDefault(x => x is NewScriptItem); + if (newScriptItem != null) { - var item = createScriptItem as NewScriptItem; - item.Visible = true; - item.ScriptName = text; + newScriptItem.Visible = true; + newScriptItem.ScriptName = text; } else { @@ -120,9 +118,9 @@ namespace FlaxEditor.CustomEditors.Dedicated else { // Make sure to hide the create script button if there - var createScriptButton = cm.ItemsPanel.Children.FirstOrDefault(x => x is NewScriptItem); - if (createScriptButton != null) - createScriptButton.Visible = false; + var newScriptItem = cm.ItemsPanel.Children.FirstOrDefault(x => x is NewScriptItem); + if (newScriptItem != null) + newScriptItem.Visible = false; } }; cm.ItemClicked += item => @@ -135,7 +133,6 @@ namespace FlaxEditor.CustomEditors.Dedicated { CreateScript(newScriptItem); } - }; cm.SortItems(); cm.Show(this, button.BottomLeft - new Float2((cm.Width - button.Width) / 2, 0)); @@ -174,16 +171,18 @@ namespace FlaxEditor.CustomEditors.Dedicated return scriptItem.ScriptType != ScriptType.Null; return false; } - + private static bool IsValidScriptName(string text) { + if (string.IsNullOrEmpty(text)) + return false; if (text.Contains(' ')) return false; if (char.IsDigit(text[0])) return false; if (text.Any(c => !char.IsLetterOrDigit(c) && c != '_')) return false; - return true; + return Editor.Instance.ContentDatabase.GetProxy("cs").IsFileNameValid(text); } /// @@ -236,6 +235,7 @@ namespace FlaxEditor.CustomEditors.Dedicated if (_dragScripts.HasValidDrag) { result = _dragScripts.Effect; + AddScripts(_dragScripts.Objects); } else if (_dragAssets.HasValidDrag) @@ -252,17 +252,16 @@ namespace FlaxEditor.CustomEditors.Dedicated private void CreateScript(NewScriptItem item) { - ScriptsEditor.NewScriptItem = item; + ScriptsEditor.NewScriptName = item.ScriptName; var paths = Directory.GetFiles(Globals.ProjectSourceFolder, "*.Build.cs"); string moduleName = null; foreach (var p in paths) { var file = File.ReadAllText(p); - // Skip if (!file.Contains("GameProjectTarget")) - continue; - + continue; // Skip + if (file.Contains("Modules.Add(\"Game\")")) { // Assume Game represents the main game module @@ -273,16 +272,14 @@ namespace FlaxEditor.CustomEditors.Dedicated // Ensure the path slashes are correct for the OS var correctedPath = Path.GetFullPath(Globals.ProjectSourceFolder); - if (string.IsNullOrEmpty(moduleName)) { var error = FileSystem.ShowBrowseFolderDialog(Editor.Instance.Windows.MainWindow, correctedPath, "Select a module folder to put the new script in", out moduleName); if (error) return; } - var path = Path.Combine(Globals.ProjectSourceFolder, moduleName, item.ScriptName + ".cs"); - new CSharpScriptProxy().Create(path, null); + Editor.Instance.ContentDatabase.GetProxy("cs").Create(path, null); } /// @@ -603,10 +600,10 @@ namespace FlaxEditor.CustomEditors.Dedicated /// public override IEnumerable UndoObjects => _scripts; - // We need somewhere to store the newly created script name. - // The problem is that the ScriptsEditor gets destroyed after scripts compilation - // so we must make it static to store this information. - internal static NewScriptItem NewScriptItem { get; set; } + /// + /// Cached the newly created script name - used to add script after compilation. + /// + internal static string NewScriptName; private void AddMissingScript(int index, LayoutElementsContainer layout) { @@ -715,17 +712,15 @@ namespace FlaxEditor.CustomEditors.Dedicated // Area for drag&drop scripts var dragArea = layout.CustomContainer(); dragArea.CustomControl.ScriptsEditor = this; - - // If the initialization is triggered by an editor recompilation, check if it - // was due to script generation from DragAreaControl. - if (NewScriptItem != null) + + // If the initialization is triggered by an editor recompilation, check if it was due to script generation from DragAreaControl + if (NewScriptName != null) { - var script = Editor.Instance.CodeEditing.Scripts.Get() - .FirstOrDefault(x => x.Name == NewScriptItem.ScriptName); + var script = Editor.Instance.CodeEditing.Scripts.Get().FirstOrDefault(x => x.Name == NewScriptName); + NewScriptName = null; if (script != null) { dragArea.CustomControl.AddScript(script); - NewScriptItem = null; } else { @@ -770,7 +765,7 @@ namespace FlaxEditor.CustomEditors.Dedicated var values = new ScriptsContainer(elementType, i, Values); var scriptType = TypeUtils.GetObjectType(script); var editor = CustomEditorsUtil.CreateEditor(scriptType, false); - + // Check if actor has all the required scripts bool hasAllRequirements = true; if (scriptType.HasAttribute(typeof(RequireScriptAttribute), false)) diff --git a/Source/Editor/GUI/ItemsListContextMenu.cs b/Source/Editor/GUI/ItemsListContextMenu.cs index a0351bf0f..c8c2a9c22 100644 --- a/Source/Editor/GUI/ItemsListContextMenu.cs +++ b/Source/Editor/GUI/ItemsListContextMenu.cs @@ -189,6 +189,9 @@ namespace FlaxEditor.GUI /// public event Action ItemClicked; + /// + /// Event fired when search text in this popup menu gets changed. + /// public event Action TextChanged; /// @@ -442,6 +445,7 @@ namespace FlaxEditor.GUI Hide(); return true; case KeyboardKeys.ArrowDown: + { if (RootWindow.FocusedControl == null) { // Focus search box if nothing is focused @@ -450,20 +454,19 @@ namespace FlaxEditor.GUI } // Focus the first visible item or then next one + var items = GetVisibleItems(); + var focusedIndex = items.IndexOf(focusedItem); + if (focusedIndex == -1) + focusedIndex = -1; + if (focusedIndex + 1 < items.Count) { - var items = GetVisibleItems(); - var focusedIndex = items.IndexOf(focusedItem); - if (focusedIndex == -1) - focusedIndex = -1; - if (focusedIndex + 1 < items.Count) - { - var item = items[focusedIndex + 1]; - item.Focus(); - _scrollPanel.ScrollViewTo(item); - return true; - } + var item = items[focusedIndex + 1]; + item.Focus(); + _scrollPanel.ScrollViewTo(item); + return true; } break; + } case KeyboardKeys.ArrowUp: if (focusedItem != null) { From 7e10baf5ea46fc160299ea9d9fd706a0e213595b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 19 Feb 2024 14:53:34 +0100 Subject: [PATCH 53/61] Fixes to code --- Source/Engine/Debug/DebugDraw.cpp | 23 ++++++++++++----------- Source/Engine/Debug/DebugDraw.h | 9 ++++----- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Source/Engine/Debug/DebugDraw.cpp b/Source/Engine/Debug/DebugDraw.cpp index 633f059a7..8f65e609d 100644 --- a/Source/Engine/Debug/DebugDraw.cpp +++ b/Source/Engine/Debug/DebugDraw.cpp @@ -923,16 +923,16 @@ void DebugDraw::DrawActors(Actor** selectedActors, int32 selectedActorsCount, bo } } -void DebugDraw::DrawAxisFromDirection(const Vector3& origin, const Vector3& direction, const Color& color , float Size, float duration, bool depthTest) +void DebugDraw::DrawAxisFromDirection(const Vector3& origin, const Vector3& direction, float size, float duration, bool depthTest) { - auto rot = Quaternion::FromDirection(direction.GetNormalized()); - Vector3 Up = (rot * Vector3::Up ); - Vector3 Forward = (rot * Vector3::Forward); - Vector3 Right = (rot * Vector3::Right ); - - DrawLine(origin, origin + (Up * (Size * 0.5f)) + Up , Color::Green ,duration, depthTest); - DrawLine(origin, origin + (Forward * (Size * 0.5f)) + Forward, Color::Blue ,duration, depthTest); - DrawLine(origin, origin + (Right * (Size * 0.5f)) + Right , Color::Red ,duration, depthTest); + const auto rot = Quaternion::FromDirection(direction.GetNormalized()); + const Vector3 up = (rot * Vector3::Up); + const Vector3 forward = (rot * Vector3::Forward); + const Vector3 right = (rot * Vector3::Right); + const float sizeHalf = size * 0.5f; + DrawLine(origin, origin + up * sizeHalf + up, Color::Green, duration, depthTest); + DrawLine(origin, origin + forward * sizeHalf + forward, Color::Blue, duration, depthTest); + DrawLine(origin, origin + right * sizeHalf + right, Color::Red, duration, depthTest); } void DebugDraw::DrawDirection(const Vector3& origin, const Vector3& direction, const Color& color, float duration, bool depthTest) @@ -942,14 +942,15 @@ void DebugDraw::DrawDirection(const Vector3& origin, const Vector3& direction, c return; DrawLine(origin, origin + direction, color, duration, depthTest); } -void DebugDraw::DrawRay(const Vector3& origin, const Vector3& direction,const Color& color, float length, float duration, bool depthTest) + +void DebugDraw::DrawRay(const Vector3& origin, const Vector3& direction, const Color& color, float length, float duration, bool depthTest) { if (isnan(length) || isinf(length)) return; DrawLine(origin, origin + (direction.GetNormalized() * length), color, duration, depthTest); } -void DebugDraw::DrawRay(const Ray& ray,const Color& color, float length, float duration, bool depthTest) +void DebugDraw::DrawRay(const Ray& ray, const Color& color, float length, float duration, bool depthTest) { if (isnan(length) || isinf(length)) return; diff --git a/Source/Engine/Debug/DebugDraw.h b/Source/Engine/Debug/DebugDraw.h index dd6d5fee3..8d44b81e6 100644 --- a/Source/Engine/Debug/DebugDraw.h +++ b/Source/Engine/Debug/DebugDraw.h @@ -72,11 +72,10 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// /// The origin of the line. /// The direction of the line. - /// The color. - /// The size of the axis. + /// The size of the axis. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. - API_FUNCTION() static void DrawAxisFromDirection(const Vector3& origin, const Vector3& direction, const Color& color = Color::White, float Size = 100.0f, float duration = 0.0f, bool depthTest = true); + API_FUNCTION() static void DrawAxisFromDirection(const Vector3& origin, const Vector3& direction, float size = 100.0f, float duration = 0.0f, bool depthTest = true); /// /// Draws the line in a direction. @@ -656,7 +655,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw API_FUNCTION() static void DrawText(const StringView& text, const Transform& transform, const Color& color = Color::White, int32 size = 32, float duration = 0.0f); }; -#define DEBUG_DRAW_AXIS_FROM_DIRECTION(origin, direction, color, size, duration, depthTest) DebugDraw::DrawAxisFromDirection(origin, direction, color, size, duration, depthTest); +#define DEBUG_DRAW_AXIS_FROM_DIRECTION(origin, direction, size, duration, depthTest) DebugDraw::DrawAxisFromDirection(origin, direction, size, duration, depthTest); #define DEBUG_DRAW_DIRECTION(origin, direction, color, duration, depthTest) DebugDraw::DrawDirection(origin, direction, color, duration, depthTest); #define DEBUG_DRAW_RAY(origin, direction, color, length, duration, depthTest) DebugDraw::DrawRay(origin, direction, color, length, duration, depthTest); #define DEBUG_DRAW_RAY(ray, color, length, duration, depthTest) DebugDraw::DrawRay(ray, color, length, duration, depthTest); @@ -689,7 +688,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw #else -#define DEBUG_DRAW_AXIS_FROM_DIRECTION(origin, direction, color, size, duration, depthTest) +#define DEBUG_DRAW_AXIS_FROM_DIRECTION(origin, direction, size, duration, depthTest) #define DEBUG_DRAW_DIRECTION(origin, direction,color,duration, depthTest) #define DEBUG_DRAW_RAY(ray, color, length, duration, depthTest) #define DEBUG_DRAW_LINE(start, end, color, duration, depthTest) From 4c082ef17f03486bc19d70d26ebfe018b7e1c795 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 19 Feb 2024 14:59:02 +0100 Subject: [PATCH 54/61] Codestyle fixes --- Flax.sln.DotSettings | 4 ++++ Source/Engine/Level/Level.cs | 6 +++++- Source/Tools/Flax.Build/Build/FileCache.cs | 16 +++++++------- .../Build/NativeCpp/Builder.NativeCpp.cs | 8 +++---- Source/Tools/Flax.Build/Build/Platform.cs | 21 +++++++------------ .../Flax.Build/Deploy/Deployment.Editor.cs | 2 +- .../Flax.Build/Deps/Dependencies/Assimp.cs | 2 +- .../Flax.Build/Deps/Dependencies/NvCloth.cs | 8 +++---- .../Flax.Build/Deps/Dependencies/OpenAL.cs | 2 +- .../Flax.Build/Deps/Dependencies/curl.cs | 8 +++---- .../Flax.Build/Deps/Dependencies/freetype.cs | 2 +- .../Flax.Build/Deps/Dependencies/glslang.cs | 2 +- .../Tools/Flax.Build/Deps/Dependencies/ogg.cs | 2 +- .../Flax.Build/Deps/Dependencies/vorbis.cs | 2 +- Source/Tools/Flax.Build/Deps/Downloader.cs | 3 +-- .../Tools/Flax.Build/Deps/ProgressDisplay.cs | 2 +- .../Platforms/Android/AndroidSdk.cs | 2 +- .../Flax.Build/Platforms/Mac/MacPlatform.cs | 16 +++++++------- .../VisualStudioProjectGenerator.cs | 2 +- .../VisualStudioCodeProjectGenerator.cs | 5 ++--- .../Flax.Build/Utilities/CppNameMangling.cs | 3 +-- Source/Tools/Flax.Build/Utilities/WinAPI.cs | 2 +- 22 files changed, 58 insertions(+), 62 deletions(-) diff --git a/Flax.sln.DotSettings b/Flax.sln.DotSettings index ff396d824..80cf8fa1e 100644 --- a/Flax.sln.DotSettings +++ b/Flax.sln.DotSettings @@ -73,8 +73,12 @@ <Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /> + AI LO + RPC + SDK VS + <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> diff --git a/Source/Engine/Level/Level.cs b/Source/Engine/Level/Level.cs index b43d9e91a..3e367195e 100644 --- a/Source/Engine/Level/Level.cs +++ b/Source/Engine/Level/Level.cs @@ -109,6 +109,8 @@ namespace FlaxEngine public static T[] GetScripts() where T : Script { var scripts = GetScripts(typeof(T)); + if (scripts.Length == 0) + return Array.Empty(); var result = new T[scripts.Length]; for (int i = 0; i < scripts.Length; i++) result[i] = scripts[i] as T; @@ -119,11 +121,13 @@ namespace FlaxEngine /// Finds all the actors of the given type in all the loaded scenes. /// /// Type of the object. - /// Finds only active actors. + /// Finds only active actors. /// Found actors list. public static T[] GetActors(bool activeOnly = false) where T : Actor { var actors = GetActors(typeof(T), activeOnly); + if (actors.Length == 0) + return Array.Empty(); var result = new T[actors.Length]; for (int i = 0; i < actors.Length; i++) result[i] = actors[i] as T; diff --git a/Source/Tools/Flax.Build/Build/FileCache.cs b/Source/Tools/Flax.Build/Build/FileCache.cs index a11b53415..d21e0848a 100644 --- a/Source/Tools/Flax.Build/Build/FileCache.cs +++ b/Source/Tools/Flax.Build/Build/FileCache.cs @@ -9,32 +9,30 @@ namespace Flax.Build /// public static class FileCache { - private static Dictionary fileInfoCache = new Dictionary(); + private static readonly Dictionary _cache = new(); public static void FileRemoveFromCache(string path) { - //fileInfoCache[path].Refresh(); - fileInfoCache.Remove(path); + _cache.Remove(path); } - + public static bool Exists(string path) { - if (fileInfoCache.TryGetValue(path, out var fileInfo)) + if (_cache.TryGetValue(path, out var fileInfo)) return fileInfo.Exists; fileInfo = new FileInfo(path); - fileInfoCache.Add(path, fileInfo); + _cache.Add(path, fileInfo); return fileInfo.Exists; } public static DateTime GetLastWriteTime(string path) { - - if (fileInfoCache.TryGetValue(path, out var fileInfo)) + if (_cache.TryGetValue(path, out var fileInfo)) return fileInfo.LastWriteTime; fileInfo = new FileInfo(path); - fileInfoCache.Add(path, fileInfo); + _cache.Add(path, fileInfo); return fileInfo.LastWriteTime; } } diff --git a/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs b/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs index 3efe33b38..f6586c910 100644 --- a/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs +++ b/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs @@ -808,11 +808,11 @@ namespace Flax.Build foreach (var moduleName in moduleOptions.PrivateDependencies.Concat(moduleOptions.PublicDependencies)) { var dependencyModule = buildData.Rules.GetModule(moduleName); - if (dependencyModule != null && - !string.IsNullOrEmpty(dependencyModule.BinaryModuleName) && - dependencyModule.BinaryModuleName != binaryModule.Key && + if (dependencyModule != null && + !string.IsNullOrEmpty(dependencyModule.BinaryModuleName) && + dependencyModule.BinaryModuleName != binaryModule.Key && !moduleNamesUsed.Contains(dependencyModule.BinaryModuleName) && - GetModuleProject(dependencyModule, project) != null && + GetModuleProject(dependencyModule, project) != null && buildData.Modules.TryGetValue(dependencyModule, out var dependencyOptions)) { // Import symbols from referenced binary module diff --git a/Source/Tools/Flax.Build/Build/Platform.cs b/Source/Tools/Flax.Build/Build/Platform.cs index f458225fa..6b0d48dba 100644 --- a/Source/Tools/Flax.Build/Build/Platform.cs +++ b/Source/Tools/Flax.Build/Build/Platform.cs @@ -77,14 +77,10 @@ namespace Flax.Build var architectureId = RuntimeInformation.ProcessArchitecture; switch (architectureId) { - case Architecture.X86: - return TargetArchitecture.x86; - case Architecture.X64: - return TargetArchitecture.x64; - case Architecture.Arm: - return TargetArchitecture.ARM; - case Architecture.Arm64: - return TargetArchitecture.ARM64; + case Architecture.X86: return TargetArchitecture.x86; + case Architecture.X64: return TargetArchitecture.x64; + case Architecture.Arm: return TargetArchitecture.ARM; + case Architecture.Arm64: return TargetArchitecture.ARM64; default: throw new NotImplementedException(string.Format("Unsupported build platform {0}.", architectureId)); } } @@ -290,12 +286,9 @@ namespace Flax.Build var subdir = "Binaries/Editor/"; switch (Platform.BuildTargetPlatform) { - case TargetPlatform.Windows: - return subdir + "Win64"; - case TargetPlatform.Linux: - return subdir + "Linux"; - case TargetPlatform.Mac: - return subdir + "Mac"; + case TargetPlatform.Windows: return subdir + "Win64"; + case TargetPlatform.Linux: return subdir + "Linux"; + case TargetPlatform.Mac: return subdir + "Mac"; } throw new NotImplementedException(); } diff --git a/Source/Tools/Flax.Build/Deploy/Deployment.Editor.cs b/Source/Tools/Flax.Build/Deploy/Deployment.Editor.cs index e1f2ab4e6..75f43fd68 100644 --- a/Source/Tools/Flax.Build/Deploy/Deployment.Editor.cs +++ b/Source/Tools/Flax.Build/Deploy/Deployment.Editor.cs @@ -58,7 +58,7 @@ namespace Flax.Deploy DeployFile(src, dst, buildToolExe); CodeSign(Path.Combine(dst, buildToolExe)); var buildToolDll = "Flax.Build.dll"; - DeployFile(src, dst,buildToolDll); + DeployFile(src, dst, buildToolDll); CodeSign(Path.Combine(dst, buildToolDll)); DeployFile(src, dst, "Flax.Build.xml", true); DeployFile(src, dst, "Flax.Build.pdb"); diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/Assimp.cs b/Source/Tools/Flax.Build/Deps/Dependencies/Assimp.cs index 528256f69..bc501c3a5 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/Assimp.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/Assimp.cs @@ -128,7 +128,7 @@ namespace Flax.Deps.Dependencies case TargetPlatform.Mac: { // Build for Mac - foreach (var architecture in new []{ TargetArchitecture.x64, TargetArchitecture.ARM64 }) + foreach (var architecture in new[] { TargetArchitecture.x64, TargetArchitecture.ARM64 }) { RunCmake(root, platform, architecture, " -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF " + globalConfig); Utilities.Run("make", null, null, root, Utilities.RunOptions.ThrowExceptionOnError); diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/NvCloth.cs b/Source/Tools/Flax.Build/Deps/Dependencies/NvCloth.cs index 6e565b94c..bb42fc253 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/NvCloth.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/NvCloth.cs @@ -49,7 +49,7 @@ namespace Flax.Deps.Dependencies } } } - + /// public override void Build(BuildOptions options) { @@ -185,16 +185,16 @@ namespace Flax.Deps.Dependencies // Print the NvCloth version Log.Info($"Building {File.ReadAllLines(Path.Combine(root, "README.md"))[0].Trim()} to {platform} {architecture}"); - + // Generate project files SetupDirectory(buildFolder, false); Utilities.FileDelete(Path.Combine(cmakeFolder, "CMakeCache.txt")); cmakeArgs += $" -DPX_STATIC_LIBRARIES=1 -DPX_OUTPUT_DLL_DIR=\"{Path.Combine(buildFolder, "bin")}\" -DPX_OUTPUT_LIB_DIR=\"{Path.Combine(buildFolder, "lib")}\" -DPX_OUTPUT_EXE_DIR=\"{Path.Combine(buildFolder, "bin")}\""; RunCmake(cmakeFolder, platform, architecture, " -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF " + cmakeArgs, envVars); - + // Run build Utilities.Run("cmake", "--build . --config Release", null, cmakeFolder, Utilities.RunOptions.ThrowExceptionOnError, envVars); - + // Deploy binaries var libs = new[] { diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs b/Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs index 1adb73bb3..f9f5d7ed2 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs @@ -172,7 +172,7 @@ namespace Flax.Deps.Dependencies var buildDir = Path.Combine(root, "build"); // Build for Mac - foreach (var architecture in new []{ TargetArchitecture.x64, TargetArchitecture.ARM64 }) + foreach (var architecture in new[] { TargetArchitecture.x64, TargetArchitecture.ARM64 }) { SetupDirectory(buildDir, true); RunCmake(buildDir, platform, architecture, ".. -DLIBTYPE=STATIC -DCMAKE_BUILD_TYPE=Release " + config); diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/curl.cs b/Source/Tools/Flax.Build/Deps/Dependencies/curl.cs index 382c5707c..651c849d8 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/curl.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/curl.cs @@ -95,7 +95,7 @@ namespace Flax.Deps.Dependencies case TargetPlatform.Linux: { // Build for Linux - var settings = new [] + var settings = new[] { "--without-librtmp", "--without-ssl", @@ -126,7 +126,7 @@ namespace Flax.Deps.Dependencies case TargetPlatform.Mac: { // Build for Mac - var settings = new [] + var settings = new[] { "--with-secure-transport", "--without-librtmp", @@ -137,7 +137,7 @@ namespace Flax.Deps.Dependencies "--enable-static", "-disable-ldap --disable-sspi --disable-ftp --disable-file --disable-dict --disable-telnet --disable-tftp --disable-rtsp --disable-pop3 --disable-imap --disable-smtp --disable-gopher --disable-smb", }; - foreach (var architecture in new []{ TargetArchitecture.x64, TargetArchitecture.ARM64 }) + foreach (var architecture in new[] { TargetArchitecture.x64, TargetArchitecture.ARM64 }) { var arch = GetAppleArchName(architecture); var archName = arch + "-apple-darwin19"; @@ -146,7 +146,7 @@ namespace Flax.Deps.Dependencies var compilerFlags = string.Format("-mmacosx-version-min={0} -arch {1}", Configuration.MacOSXMinVer, arch); var envVars = new Dictionary { - { "CC", "clang" }, + { "CC", "clang" }, { "CXX", "clang" }, { "CFLAGS", compilerFlags }, { "CXXFLAGS", compilerFlags }, diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/freetype.cs b/Source/Tools/Flax.Build/Deps/Dependencies/freetype.cs index 165317180..6be0d3397 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/freetype.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/freetype.cs @@ -247,7 +247,7 @@ namespace Flax.Deps.Dependencies case TargetPlatform.Mac: { // Build for Mac - foreach (var architecture in new []{ TargetArchitecture.x64, TargetArchitecture.ARM64 }) + foreach (var architecture in new[] { TargetArchitecture.x64, TargetArchitecture.ARM64 }) { SetupDirectory(buildDir, true); RunCmake(buildDir, platform, architecture, ".. -DCMAKE_BUILD_TYPE=Release"); diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/glslang.cs b/Source/Tools/Flax.Build/Deps/Dependencies/glslang.cs index f88ded6a1..5d11c4728 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/glslang.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/glslang.cs @@ -130,7 +130,7 @@ namespace Flax.Deps.Dependencies }; // Build for Mac - foreach (var architecture in new []{ TargetArchitecture.x64, TargetArchitecture.ARM64 }) + foreach (var architecture in new[] { TargetArchitecture.x64, TargetArchitecture.ARM64 }) { RunCmake(root, platform, architecture, cmakeArgs); Utilities.Run("cmake", string.Format("--build . --config {0} --target install", configuration), null, buildDir, Utilities.RunOptions.None); diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/ogg.cs b/Source/Tools/Flax.Build/Deps/Dependencies/ogg.cs index fe6800001..4fefceb09 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/ogg.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/ogg.cs @@ -217,7 +217,7 @@ namespace Flax.Deps.Dependencies case TargetPlatform.Mac: { // Build for Mac - foreach (var architecture in new []{ TargetArchitecture.x64, TargetArchitecture.ARM64 }) + foreach (var architecture in new[] { TargetArchitecture.x64, TargetArchitecture.ARM64 }) { SetupDirectory(buildDir, true); RunCmake(buildDir, platform, architecture, ".. -DCMAKE_BUILD_TYPE=Release"); diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/vorbis.cs b/Source/Tools/Flax.Build/Deps/Dependencies/vorbis.cs index 75b2810be..847ff8872 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/vorbis.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/vorbis.cs @@ -376,7 +376,7 @@ namespace Flax.Deps.Dependencies GitCheckout(oggRoot, "master", "4380566a44b8d5e85ad511c9c17eb04197863ec5"); // Build for Mac - foreach (var architecture in new []{ TargetArchitecture.x64, TargetArchitecture.ARM64 }) + foreach (var architecture in new[] { TargetArchitecture.x64, TargetArchitecture.ARM64 }) { SetupDirectory(oggBuildDir, true); RunCmake(oggBuildDir, platform, architecture, ".. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=\"../install\""); diff --git a/Source/Tools/Flax.Build/Deps/Downloader.cs b/Source/Tools/Flax.Build/Deps/Downloader.cs index dcba01780..19135d634 100644 --- a/Source/Tools/Flax.Build/Deps/Downloader.cs +++ b/Source/Tools/Flax.Build/Deps/Downloader.cs @@ -105,8 +105,7 @@ namespace Flax.Deps if (totalBytes.HasValue) progress.Update(totalBytesRead, totalBytes.Value); } - } - while (hasMoreToRead); + } while (hasMoreToRead); } } } diff --git a/Source/Tools/Flax.Build/Deps/ProgressDisplay.cs b/Source/Tools/Flax.Build/Deps/ProgressDisplay.cs index 5ad6e10d3..cce10f2eb 100644 --- a/Source/Tools/Flax.Build/Deps/ProgressDisplay.cs +++ b/Source/Tools/Flax.Build/Deps/ProgressDisplay.cs @@ -96,7 +96,7 @@ namespace Flax.Deps Console.WriteLine(); return false; } - + return true; */ } diff --git a/Source/Tools/Flax.Build/Platforms/Android/AndroidSdk.cs b/Source/Tools/Flax.Build/Platforms/Android/AndroidSdk.cs index a023d33b2..0fd5b6a41 100644 --- a/Source/Tools/Flax.Build/Platforms/Android/AndroidSdk.cs +++ b/Source/Tools/Flax.Build/Platforms/Android/AndroidSdk.cs @@ -18,7 +18,7 @@ namespace Flax.Build.Platforms public static readonly AndroidSdk Instance = new AndroidSdk(); /// - public override TargetPlatform[] Platforms => new [] + public override TargetPlatform[] Platforms => new[] { TargetPlatform.Windows, TargetPlatform.Linux, diff --git a/Source/Tools/Flax.Build/Platforms/Mac/MacPlatform.cs b/Source/Tools/Flax.Build/Platforms/Mac/MacPlatform.cs index 4a8115fcd..4173e160b 100644 --- a/Source/Tools/Flax.Build/Platforms/Mac/MacPlatform.cs +++ b/Source/Tools/Flax.Build/Platforms/Mac/MacPlatform.cs @@ -77,15 +77,15 @@ namespace Flax.Build.Platforms /// Returns true if running an x64 binary an arm64 host machine. /// public unsafe static bool GetProcessIsTranslated() - { - int ret = 0; - ulong size = sizeof(int); - if (sysctlbyname("sysctl.proc_translated", &ret, &size, null, 0) == -1) - return false; - return ret != 0; - } + { + int ret = 0; + ulong size = sizeof(int); + if (sysctlbyname("sysctl.proc_translated", &ret, &size, null, 0) == -1) + return false; + return ret != 0; + } [DllImport("c")] - private static unsafe extern int sysctlbyname(string name, void* oldp, ulong* oldlenp, void* newp, ulong newlen); + private static unsafe extern int sysctlbyname(string name, void* oldp, ulong* oldlenp, void* newp, ulong newlen); } } diff --git a/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs index 3438f8960..7bd5495b2 100644 --- a/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs +++ b/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs @@ -702,7 +702,7 @@ namespace Flax.Build.Projects.VisualStudio { // Build command for the build tool var buildToolPath = Path.ChangeExtension(typeof(Builder).Assembly.Location, null); - + var targetsFileContent = new StringBuilder(); targetsFileContent.AppendLine(""); targetsFileContent.AppendLine(" "); diff --git a/Source/Tools/Flax.Build/Projects/VisualStudioCode/VisualStudioCodeProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudioCode/VisualStudioCodeProjectGenerator.cs index 15b236972..7f3b11e15 100644 --- a/Source/Tools/Flax.Build/Projects/VisualStudioCode/VisualStudioCodeProjectGenerator.cs +++ b/Source/Tools/Flax.Build/Projects/VisualStudioCode/VisualStudioCodeProjectGenerator.cs @@ -410,8 +410,7 @@ namespace Flax.Build.Projects.VisualStudioCode json.AddField("stopAtEntry", false); json.AddField("externalConsole", true); break; - case TargetPlatform.Linux: - break; + case TargetPlatform.Linux: break; } } json.EndObject(); @@ -622,7 +621,7 @@ namespace Flax.Build.Projects.VisualStudioCode json.AddField("**/Output", true); json.AddField("**/*.flax", true); json.EndObject(); - + // Extension settings json.AddField("omnisharp.useModernNet", true); diff --git a/Source/Tools/Flax.Build/Utilities/CppNameMangling.cs b/Source/Tools/Flax.Build/Utilities/CppNameMangling.cs index 1d0166a72..ba00db071 100644 --- a/Source/Tools/Flax.Build/Utilities/CppNameMangling.cs +++ b/Source/Tools/Flax.Build/Utilities/CppNameMangling.cs @@ -79,8 +79,7 @@ namespace Flax.Build } } break; - default: - throw new InvalidPlatformException(buildData.Platform.Target); + default: throw new InvalidPlatformException(buildData.Platform.Target); } var result = sb.ToString(); BindingsGenerator.PutStringBuilder(sb); diff --git a/Source/Tools/Flax.Build/Utilities/WinAPI.cs b/Source/Tools/Flax.Build/Utilities/WinAPI.cs index ae0c5a5fb..47939fc1f 100644 --- a/Source/Tools/Flax.Build/Utilities/WinAPI.cs +++ b/Source/Tools/Flax.Build/Utilities/WinAPI.cs @@ -130,7 +130,7 @@ namespace Flax.Build System = 0x00001000, Task = 0x00002000 } - + public enum Icon : uint { Warning = 0x00000030, From cb0969893dc920ff1c27b434c329733fd84dd075 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 19 Feb 2024 15:12:37 +0100 Subject: [PATCH 55/61] Fix build with older C# compiler --- Source/Editor/Options/InterfaceOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index 6180b5a66..c33cd3674 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -249,7 +249,7 @@ namespace FlaxEditor.Options /// The list of fallback fonts to use when main text font is missing certain characters. Empty to use fonts from GraphicsSettings. /// [EditorDisplay("Fonts"), EditorOrder(650)] - public FontAsset[] FallbackFonts = [FlaxEngine.Content.LoadAsyncInternal(EditorAssets.FallbackFont)]; + public FontAsset[] FallbackFonts = new FontAsset[1] { FlaxEngine.Content.LoadAsyncInternal(EditorAssets.FallbackFont) }; /// /// Gets or sets the title font for editor UI. From 2de890ca6a24e861c5ca96401e6c86ce7fa267b5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 19 Feb 2024 17:33:21 +0100 Subject: [PATCH 56/61] Codecleanup for #1482 --- Source/Editor/SceneGraph/Actors/SplineNode.cs | 122 ++++++------------ Source/Editor/Viewport/EditorViewport.cs | 16 +++ Source/Engine/Core/Math/BoundingFrustum.cs | 10 ++ Source/Engine/Graphics/RenderView.cs | 2 +- Source/Engine/UI/UICanvas.cs | 2 +- 5 files changed, 69 insertions(+), 83 deletions(-) diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs index a3ceb66f9..e6ff43e9f 100644 --- a/Source/Editor/SceneGraph/Actors/SplineNode.cs +++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs @@ -294,8 +294,6 @@ namespace FlaxEditor.SceneGraph.Actors private const Real SnapIndicatorSize = 1.7f; private const Real SnapPointIndicatorSize = 2f; - private static Spline _currentEditSpline; - /// public SplineNode(Actor actor) : base(actor) @@ -306,17 +304,19 @@ namespace FlaxEditor.SceneGraph.Actors private void OnUpdate() { - if (Input.Keyboard.GetKey(KeyboardKeys.Shift)) + // If this node's point is selected + var selection = Editor.Instance.SceneEditing.Selection; + if (selection.Count == 1 && selection[0] is SplinePointNode selectedPoint && selectedPoint.ParentNode == this) { - EditSplineWithSnap(); + if (Input.Keyboard.GetKey(KeyboardKeys.Shift)) + EditSplineWithSnap(selectedPoint); + + var canAddSplinePoint = Input.Mouse.PositionDelta == Float2.Zero && Input.Mouse.Position != Float2.Zero; + var requestAddSplinePoint = Input.Keyboard.GetKey(KeyboardKeys.Control) && Input.Mouse.GetButtonDown(MouseButton.Right); + if (requestAddSplinePoint && canAddSplinePoint) + AddSplinePoint(selectedPoint); } - var canAddSplinePoint = Input.Mouse.PositionDelta == Float2.Zero && Input.Mouse.Position != Float2.Zero; - var requestAddSplinePoint = Input.Keyboard.GetKey(KeyboardKeys.Control) && Input.Mouse.GetButtonDown(MouseButton.Right); - - if (requestAddSplinePoint && canAddSplinePoint) - AddSplinePoint(); - SyncSplineKeyframeWithNodes(); } @@ -351,20 +351,15 @@ namespace FlaxEditor.SceneGraph.Actors } } - private unsafe void AddSplinePoint() + private unsafe void AddSplinePoint(SplinePointNode selectedPoint) { - var selectedPoint = GetSelectedSplineNode(); - if (selectedPoint == null) - return; - - // checking mouse hit on scene + // Check mouse hit on scene var spline = (Spline)Actor; var viewport = Editor.Instance.Windows.EditWin.Viewport; var mouseRay = viewport.MouseRay; - var viewRay = new Ray(viewport.ViewPosition, viewport.ViewDirection); + var viewRay = viewport.ViewRay; var flags = RayCastData.FlagTypes.SkipColliders | RayCastData.FlagTypes.SkipEditorPrimitives; var hit = Editor.Instance.Scene.Root.RayCast(ref mouseRay, ref viewRay, out var closest, out var normal, flags); - if (hit == null) return; @@ -373,7 +368,7 @@ namespace FlaxEditor.SceneGraph.Actors var editAction = new EditSplineAction(spline, oldSpline); Root.Undo.AddAction(editAction); - // Getting spline point to duplicate + // Get spline point to duplicate var hitPoint = mouseRay.Position + mouseRay.Direction * closest; var lastPointIndex = selectedPoint.Index; var newPointIndex = lastPointIndex > 0 ? lastPointIndex + 1 : 0; @@ -381,24 +376,22 @@ namespace FlaxEditor.SceneGraph.Actors var isLastPoint = lastPointIndex == spline.SplinePointsCount - 1; var isFirstPoint = lastPointIndex == 0; - // Getting data to create new point - + // Get data to create new point var lastPointTime = spline.GetSplineTime(lastPointIndex); var nextPointTime = isLastPoint ? lastPointTime : spline.GetSplineTime(newPointIndex); var newTime = isLastPoint ? lastPointTime + 1.0f : (lastPointTime + nextPointTime) * 0.5f; var distanceFromLastPoint = Vector3.Distance(hitPoint, spline.GetSplinePoint(lastPointIndex)); var newPointDirection = spline.GetSplineTangent(lastPointIndex, false).Translation - hitPoint; - // set correctly keyframe direction on spawn point + // Set correctly keyframe direction on spawn point if (isFirstPoint) newPointDirection = hitPoint - spline.GetSplineTangent(lastPointIndex, true).Translation; else if (isLastPoint) newPointDirection = spline.GetSplineTangent(lastPointIndex, false).Translation - hitPoint; - var newPointLocalPosition = spline.Transform.WorldToLocal(hitPoint); var newPointLocalOrientation = Quaternion.LookRotation(newPointDirection); - // Adding new point + // Add new point spline.InsertSplinePoint(newPointIndex, newTime, Transform.Identity, false); var newKeyframe = lastKeyframe.DeepClone(); var newKeyframeTransform = newKeyframe.Value; @@ -406,13 +399,13 @@ namespace FlaxEditor.SceneGraph.Actors newKeyframeTransform.Orientation = newPointLocalOrientation; newKeyframe.Value = newKeyframeTransform; - // Setting new point keyframe - var newkeyframeTangentIn = Transform.Identity; - var newkeyframeTangentOut = Transform.Identity; - newkeyframeTangentIn.Translation = (Vector3.Forward * newPointLocalOrientation) * distanceFromLastPoint; - newkeyframeTangentOut.Translation = (Vector3.Backward * newPointLocalOrientation) * distanceFromLastPoint; - newKeyframe.TangentIn = newkeyframeTangentIn; - newKeyframe.TangentOut = newkeyframeTangentOut; + // Set new point keyframe + var newKeyframeTangentIn = Transform.Identity; + var newKeyframeTangentOut = Transform.Identity; + newKeyframeTangentIn.Translation = (Vector3.Forward * newPointLocalOrientation) * distanceFromLastPoint; + newKeyframeTangentOut.Translation = (Vector3.Backward * newPointLocalOrientation) * distanceFromLastPoint; + newKeyframe.TangentIn = newKeyframeTangentIn; + newKeyframe.TangentOut = newKeyframeTangentOut; spline.SetSplineKeyframe(newPointIndex, newKeyframe); for (int i = 1; i < spline.SplinePointsCount; i++) @@ -430,20 +423,13 @@ namespace FlaxEditor.SceneGraph.Actors spline.UpdateSpline(); } - private void EditSplineWithSnap() + private void EditSplineWithSnap(SplinePointNode selectedPoint) { - if (_currentEditSpline == null || _currentEditSpline != Actor) - return; - - var selectedNode = GetSelectedSplineNode(); - if (selectedNode == null) - return; - - var selectedNodeBounds = new BoundingSphere(selectedNode.Transform.Translation, 1f); - var allSplinesInView = GetSplinesOnView(); - allSplinesInView.Remove(_currentEditSpline); - - if (allSplinesInView.Count == 0 || selectedNode == null) + var spline = (Spline)Actor; + var selectedPointBounds = new BoundingSphere(selectedPoint.Transform.Translation, 1f); + var allSplinesInView = GetSplinesInView(); + allSplinesInView.Remove(spline); + if (allSplinesInView.Count == 0) return; var snappedOnSplinePoint = false; @@ -456,9 +442,9 @@ namespace FlaxEditor.SceneGraph.Actors var keyframeBounds = new BoundingSphere(keyframePosition, pointIndicatorSize); DebugDraw.DrawSphere(keyframeBounds, Color.Red, 0, false); - if (keyframeBounds.Intersects(selectedNodeBounds)) + if (keyframeBounds.Intersects(selectedPointBounds)) { - _currentEditSpline.SetSplinePoint(selectedNode.Index, keyframeBounds.Center); + spline.SetSplinePoint(selectedPoint.Index, keyframeBounds.Center); snappedOnSplinePoint = true; break; } @@ -467,15 +453,13 @@ namespace FlaxEditor.SceneGraph.Actors if (!snappedOnSplinePoint) { - var nearSplineSnapPoint = GetNearSplineSnapPosition(selectedNode.Transform.Translation, allSplinesInView); + var nearSplineSnapPoint = GetNearSplineSnapPosition(selectedPoint.Transform.Translation, allSplinesInView); var snapIndicatorSize = NodeSizeByDistance(nearSplineSnapPoint, SnapIndicatorSize); var snapBounds = new BoundingSphere(nearSplineSnapPoint, snapIndicatorSize); - - if (snapBounds.Intersects(selectedNodeBounds)) + if (snapBounds.Intersects(selectedPointBounds)) { - _currentEditSpline.SetSplinePoint(selectedNode.Index, snapBounds.Center); + spline.SetSplinePoint(selectedPoint.Index, snapBounds.Center); } - DebugDraw.DrawSphere(snapBounds, Color.Yellow, 0, true); } } @@ -494,14 +478,12 @@ namespace FlaxEditor.SceneGraph.Actors var spline = (Spline)Actor; spline.AddSplineLocalPoint(Vector3.Zero, false); spline.AddSplineLocalPoint(new Vector3(0, 0, 100.0f)); - spline.SetSplineKeyframe(0, new BezierCurve.Keyframe() { Value = new Transform(Vector3.Zero, Quaternion.Identity, Vector3.One), TangentIn = new Transform(Vector3.Backward * 100, Quaternion.Identity, Vector3.One), TangentOut = new Transform(Vector3.Forward * 100, Quaternion.Identity, Vector3.One), }); - spline.SetSplineKeyframe(1, new BezierCurve.Keyframe() { Value = new Transform(Vector3.Forward * 100, Quaternion.Identity, Vector3.One), @@ -553,7 +535,6 @@ namespace FlaxEditor.SceneGraph.Actors internal static void OnSplineEdited(Spline spline) { - _currentEditSpline = spline; var collider = spline.GetChild(); if (collider && collider.Scene && collider.IsActiveInHierarchy && collider.HasStaticFlag(StaticFlags.Navigation) && !Editor.IsPlayMode) { @@ -565,39 +546,18 @@ namespace FlaxEditor.SceneGraph.Actors } } - private static SplinePointNode GetSelectedSplineNode() + private static List GetSplinesInView() { - var selection = Editor.Instance.SceneEditing.Selection; - if (selection.Count != 1) - return null; - if (selection[0] is not SplineNode.SplinePointNode) - return null; - - return (SplinePointNode)selection[0]; - } - - private static List GetSplinesOnView() - { - var splines = Level.GetActors(); - var splinesOnView = new List(); - - var viewTransform = Editor.Instance.Windows.EditWin.Viewport.ViewTransform; - var viewFov = Editor.Instance.Windows.EditWin.Viewport.FieldOfView; - var viewNear = Editor.Instance.Windows.EditWin.Viewport.NearPlane; - var viewFar = Editor.Instance.Windows.EditWin.Viewport.FarPlane; - var viewAspect = Editor.Instance.Windows.EditWin.Width / Editor.Instance.Windows.EditWin.Height; - var viewBounds = BoundingFrustum.FromCamera(viewTransform.Translation, viewTransform.Forward, viewTransform.Up, viewFov, viewNear, viewFar, viewAspect); - + var splines = Level.GetActors(true); + var result = new List(); + var viewBounds = Editor.Instance.Windows.EditWin.Viewport.ViewFrustum; foreach (var s in splines) { var contains = viewBounds.Contains(s.EditorBox); if (contains == ContainmentType.Contains || contains == ContainmentType.Intersects) - { - splinesOnView.Add(s); - } + result.Add(s); } - - return splinesOnView; + return result; } private static Vector3 GetNearSplineSnapPosition(Vector3 position, List splines) diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index 526a84c45..759c3736a 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -334,6 +334,22 @@ namespace FlaxEditor.Viewport } } + /// + /// Gets the bounding frustum of the current viewport camera. + /// + public BoundingFrustum ViewFrustum + { + get + { + Vector3 viewOrigin = Task.View.Origin; + Float3 position = ViewPosition - viewOrigin; + CreateViewMatrix(position, out var view); + CreateProjectionMatrix(out var projection); + Matrix.Multiply(ref view, ref projection, out var viewProjection); + return new BoundingFrustum(ref viewProjection); + } + } + /// /// Gets or sets the yaw angle (in degrees). /// diff --git a/Source/Engine/Core/Math/BoundingFrustum.cs b/Source/Engine/Core/Math/BoundingFrustum.cs index 970957fbb..25cd0c33b 100644 --- a/Source/Engine/Core/Math/BoundingFrustum.cs +++ b/Source/Engine/Core/Math/BoundingFrustum.cs @@ -104,6 +104,16 @@ namespace FlaxEngine GetPlanesFromMatrix(ref pMatrix, out pNear, out pFar, out pLeft, out pRight, out pTop, out pBottom); } + /// + /// Creates a new instance of BoundingFrustum. + /// + /// Combined matrix that usually takes view × projection matrix. + public BoundingFrustum(ref Matrix matrix) + { + pMatrix = matrix; + GetPlanesFromMatrix(ref pMatrix, out pNear, out pFar, out pLeft, out pRight, out pTop, out pBottom); + } + /// /// Returns a hash code for this instance. /// diff --git a/Source/Engine/Graphics/RenderView.cs b/Source/Engine/Graphics/RenderView.cs index b770037a3..72cfe3577 100644 --- a/Source/Engine/Graphics/RenderView.cs +++ b/Source/Engine/Graphics/RenderView.cs @@ -27,7 +27,7 @@ namespace FlaxEngine Matrix.Invert(ref View, out IV); Matrix.Invert(ref Projection, out IP); Matrix.Multiply(ref View, ref Projection, out var viewProjection); - Frustum = new BoundingFrustum(viewProjection); + Frustum = new BoundingFrustum(ref viewProjection); Matrix.Invert(ref viewProjection, out IVP); CullingFrustum = Frustum; } diff --git a/Source/Engine/UI/UICanvas.cs b/Source/Engine/UI/UICanvas.cs index bc8c360d1..dda941745 100644 --- a/Source/Engine/UI/UICanvas.cs +++ b/Source/Engine/UI/UICanvas.cs @@ -450,7 +450,7 @@ namespace FlaxEngine { camera.GetMatrices(out tmp1, out var tmp3, ref viewport); Matrix.Multiply(ref tmp1, ref tmp3, out tmp2); - var frustum = new BoundingFrustum(tmp2); + var frustum = new BoundingFrustum(ref tmp2); _guiRoot.Size = new Float2(frustum.GetWidthAtDepth(Distance), frustum.GetHeightAtDepth(Distance)); } else From 2fd47f91e263d030e0a83333a379c8e6b27b5d4d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 19 Feb 2024 17:36:37 +0100 Subject: [PATCH 57/61] Fix codestyle #2179 --- Source/Engine/Core/Collections/Array.h | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/Source/Engine/Core/Collections/Array.h b/Source/Engine/Core/Collections/Array.h index 9c50c5ccf..d67aecbc1 100644 --- a/Source/Engine/Core/Collections/Array.h +++ b/Source/Engine/Core/Collections/Array.h @@ -250,6 +250,16 @@ public: return _count == 0; } + /// + /// Determines if given index is valid. + /// + /// The index. + /// true if is valid a index; otherwise, false. + bool IsValidIndex(int32 index) const + { + return index < _count && index >= 0; + } + /// /// Gets the pointer to the first item in the collection (linear allocation). /// @@ -737,18 +747,7 @@ public: ::Swap(other, *this); } } - - /// - /// Determines if is valid index. - /// - /// The index. - /// - /// true if is valid a index; otherwise, false. - /// - bool IsValidIndex(int index) const - { - return index < _count && index >= 0; - } + /// /// Reverses the order of the added items in the collection. /// From 5cada42842f2e30e3d9abe8e5a4bcc1abeac958a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 19 Feb 2024 17:37:59 +0100 Subject: [PATCH 58/61] Fix missing engine api expose in `Font.h` #2245 --- Source/Engine/Render2D/Font.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Render2D/Font.h b/Source/Engine/Render2D/Font.h index cd92103cb..c55bb58a1 100644 --- a/Source/Engine/Render2D/Font.h +++ b/Source/Engine/Render2D/Font.h @@ -18,7 +18,7 @@ struct FontTextureAtlasSlot; /// /// The text range. /// -API_STRUCT(NoDefault) struct TextRange +API_STRUCT(NoDefault) struct FLAXENGINE_API TextRange { DECLARE_SCRIPTING_TYPE_MINIMAL(TextRange); @@ -88,7 +88,7 @@ struct TIsPODType /// /// The font line info generated during text processing. /// -API_STRUCT(NoDefault) struct FontLineCache +API_STRUCT(NoDefault) struct FLAXENGINE_API FontLineCache { DECLARE_SCRIPTING_TYPE_MINIMAL(FontLineCache); @@ -152,7 +152,7 @@ struct TIsPODType /// /// The cached font character entry (read for rendering and further processing). /// -API_STRUCT(NoDefault) struct FontCharacterEntry +API_STRUCT(NoDefault) struct FLAXENGINE_API FontCharacterEntry { DECLARE_SCRIPTING_TYPE_MINIMAL(FontCharacterEntry); From 056deb58ad095ab6830bade86213000d42387513 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 19 Feb 2024 17:48:18 +0100 Subject: [PATCH 59/61] Fix compilation in #2257 --- Source/Engine/Physics/Colliders/CharacterController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Physics/Colliders/CharacterController.cpp b/Source/Engine/Physics/Colliders/CharacterController.cpp index 61fc4d471..db15dd46f 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.cpp +++ b/Source/Engine/Physics/Colliders/CharacterController.cpp @@ -210,7 +210,7 @@ void CharacterController::CreateController() // Setup PhysicsBackend::SetControllerUpDirection(_controller, _upDirection); - PhysicsBackend::SetShapeLocalPose(_shape, Vector3.Zero, Quaternion::Identity); + PhysicsBackend::SetShapeLocalPose(_shape, Vector3::Zero, Quaternion::Identity); UpdateLayerBits(); UpdateBounds(); } From 0035c347f433867f29d6f32703d33994f83f4728 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 19 Feb 2024 18:11:57 +0100 Subject: [PATCH 60/61] Add safety check for splatmap sampling --- Source/Engine/Terrain/TerrainPatch.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Engine/Terrain/TerrainPatch.cpp b/Source/Engine/Terrain/TerrainPatch.cpp index c5d1ace05..e24376f1f 100644 --- a/Source/Engine/Terrain/TerrainPatch.cpp +++ b/Source/Engine/Terrain/TerrainPatch.cpp @@ -610,6 +610,7 @@ FORCE_INLINE byte GetPhysicalMaterial(const Color32& raw, const TerrainDataUpdat uint8 layer = 0; uint8 layerWeight = 0; const int32 splatmapTextureIndex = (chunkZ * info.ChunkSize + z) * info.HeightmapSize + chunkX * info.ChunkSize + x; + ASSERT(splatmapTextureIndex < info.HeightmapLength); for (int32 splatIndex = 0; splatIndex < TERRAIN_MAX_SPLATMAPS_COUNT; splatIndex++) { for (int32 channelIndex = 0; channelIndex < 4; channelIndex++) From f8d9817911d182fa09cc5125e49abcb24de24b5a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 19 Feb 2024 18:23:14 +0100 Subject: [PATCH 61/61] Fix terrain paint crash refression --- Source/Editor/Tools/Terrain/PaintTerrainGizmoMode.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Tools/Terrain/PaintTerrainGizmoMode.cs b/Source/Editor/Tools/Terrain/PaintTerrainGizmoMode.cs index 4e7925dd9..7d6af355a 100644 --- a/Source/Editor/Tools/Terrain/PaintTerrainGizmoMode.cs +++ b/Source/Editor/Tools/Terrain/PaintTerrainGizmoMode.cs @@ -48,6 +48,7 @@ namespace FlaxEditor.Tools.Terrain if (DataPtr != IntPtr.Zero) Marshal.FreeHGlobal(DataPtr); DataPtr = Marshal.AllocHGlobal(size); + Utils.MemoryClear(DataPtr, (ulong)size); Size = size; } } @@ -294,7 +295,7 @@ namespace FlaxEditor.Tools.Terrain base.OnDeactivated(); // Free temporary memory buffer - foreach (var splatmapData in _cachedSplatmapData) + foreach (ref var splatmapData in _cachedSplatmapData.AsSpan()) splatmapData.Free(); }