diff --git a/Source/Editor/Content/GUI/ContentView.cs b/Source/Editor/Content/GUI/ContentView.cs
index 6065ca9f8..40a06c1b6 100644
--- a/Source/Editor/Content/GUI/ContentView.cs
+++ b/Source/Editor/Content/GUI/ContentView.cs
@@ -711,7 +711,7 @@ namespace FlaxEditor.Content.GUI
protected override void PerformLayoutBeforeChildren()
{
float width = GetClientArea().Width;
- float x = 0, y = 0;
+ float x = 0, y = 1;
float viewScale = _viewScale * 0.97f;
switch (ViewType)
@@ -722,21 +722,22 @@ namespace FlaxEditor.Content.GUI
int itemsToFit = Mathf.FloorToInt(width / defaultItemsWidth) - 1;
if (itemsToFit < 1)
itemsToFit = 1;
- float itemsWidth = width / Mathf.Max(itemsToFit, 1);
+ int xSpace = 4;
+ float itemsWidth = width / Mathf.Max(itemsToFit, 1) - xSpace;
float itemsHeight = itemsWidth / defaultItemsWidth * (ContentItem.DefaultHeight * viewScale);
var flooredItemsWidth = Mathf.Floor(itemsWidth);
var flooredItemsHeight = Mathf.Floor(itemsHeight);
- x = itemsToFit == 1 ? 0 : itemsWidth / itemsToFit;
+ x = itemsToFit == 1 ? 1 : itemsWidth / itemsToFit + xSpace;
for (int i = 0; i < _children.Count; i++)
{
var c = _children[i];
c.Bounds = new Rectangle(Mathf.Floor(x), Mathf.Floor(y), flooredItemsWidth, flooredItemsHeight);
- x += itemsWidth + itemsWidth / itemsToFit;
+ x += (itemsWidth + xSpace) + (itemsWidth + xSpace) / itemsToFit;
if (x + itemsWidth > width)
{
- x = itemsToFit == 1 ? 0 : itemsWidth / itemsToFit;
- y += itemsHeight + 5;
+ x = itemsToFit == 1 ? 1 : itemsWidth / itemsToFit + xSpace;
+ y += itemsHeight + 7;
}
}
if (x > 0)
@@ -751,7 +752,7 @@ namespace FlaxEditor.Content.GUI
{
var c = _children[i];
c.Bounds = new Rectangle(x, y, width, itemsHeight);
- y += itemsHeight + 5;
+ y += itemsHeight + 1;
}
y += 40.0f;
diff --git a/Source/Editor/Content/Items/ContentItem.cs b/Source/Editor/Content/Items/ContentItem.cs
index 66825fb42..b27159aa0 100644
--- a/Source/Editor/Content/Items/ContentItem.cs
+++ b/Source/Editor/Content/Items/ContentItem.cs
@@ -483,6 +483,30 @@ namespace FlaxEditor.Content
else
Render2D.FillRectangle(rectangle, Color.Black);
}
+
+ ///
+ /// Draws the item thumbnail.
+ ///
+ /// The thumbnail rectangle.
+ /// /// Whether or not to draw the shadow. Overrides DrawShadow.
+ public void DrawThumbnail(ref Rectangle rectangle, bool shadow)
+ {
+ // Draw shadow
+ if (shadow)
+ {
+ const float thumbnailInShadowSize = 50.0f;
+ var shadowRect = rectangle.MakeExpanded((DefaultThumbnailSize - thumbnailInShadowSize) * rectangle.Width / DefaultThumbnailSize * 1.3f);
+ if (!_shadowIcon.IsValid)
+ _shadowIcon = Editor.Instance.Icons.AssetShadow128;
+ Render2D.DrawSprite(_shadowIcon, shadowRect);
+ }
+
+ // Draw thumbnail
+ if (_thumbnail.IsValid)
+ Render2D.DrawSprite(_thumbnail, rectangle);
+ else
+ Render2D.FillRectangle(rectangle, Color.Black);
+ }
///
/// Gets the amount of references to that item.
@@ -655,9 +679,51 @@ namespace FlaxEditor.Content
{
case ContentViewType.Tiles:
{
- var thumbnailSize = size.X - 2 * DefaultMarginSize;
- thumbnailRect = new Rectangle(DefaultMarginSize, DefaultMarginSize, thumbnailSize, thumbnailSize);
+ var thumbnailSize = size.X;
+ thumbnailRect = new Rectangle(0, 0, thumbnailSize, thumbnailSize);
nameAlignment = TextAlignment.Center;
+
+ if (this is ContentFolder)
+ {
+ // Small shadow
+ var shadowRect = new Rectangle(2, 2, clientRect.Width + 1, clientRect.Height + 1);
+ var color = Color.Black.AlphaMultiplied(0.2f);
+ Render2D.FillRectangle(shadowRect, color);
+ Render2D.FillRectangle(clientRect, style.Background.RGBMultiplied(1.25f));
+
+ if (isSelected)
+ Render2D.FillRectangle(clientRect, Parent.ContainsFocus ? style.BackgroundSelected : style.LightBackground);
+ else if (IsMouseOver)
+ Render2D.FillRectangle(clientRect, style.BackgroundHighlighted);
+
+ DrawThumbnail(ref thumbnailRect, false);
+ }
+ else
+ {
+ // Small shadow
+ var shadowRect = new Rectangle(2, 2, clientRect.Width + 1, clientRect.Height + 1);
+ var color = Color.Black.AlphaMultiplied(0.2f);
+ Render2D.FillRectangle(shadowRect, color);
+
+ Render2D.FillRectangle(clientRect, style.Background.RGBMultiplied(1.25f));
+ Render2D.FillRectangle(TextRectangle, style.LightBackground);
+
+ var accentHeight = 2 * view.ViewScale;
+ var barRect = new Rectangle(0, thumbnailRect.Height - accentHeight, clientRect.Width, accentHeight);
+ Render2D.FillRectangle(barRect, Color.DimGray);
+
+ DrawThumbnail(ref thumbnailRect, false);
+ if (isSelected)
+ {
+ Render2D.FillRectangle(textRect, Parent.ContainsFocus ? style.BackgroundSelected : style.LightBackground);
+ Render2D.DrawRectangle(clientRect, Parent.ContainsFocus ? style.BackgroundSelected : style.LightBackground);
+ }
+ else if (IsMouseOver)
+ {
+ Render2D.FillRectangle(textRect, style.BackgroundHighlighted);
+ Render2D.DrawRectangle(clientRect, style.BackgroundHighlighted);
+ }
+ }
break;
}
case ContentViewType.List:
@@ -665,23 +731,21 @@ namespace FlaxEditor.Content
var thumbnailSize = size.Y - 2 * DefaultMarginSize;
thumbnailRect = new Rectangle(DefaultMarginSize, DefaultMarginSize, thumbnailSize, thumbnailSize);
nameAlignment = TextAlignment.Near;
+
+ if (isSelected)
+ Render2D.FillRectangle(clientRect, Parent.ContainsFocus ? style.BackgroundSelected : style.LightBackground);
+ else if (IsMouseOver)
+ Render2D.FillRectangle(clientRect, style.BackgroundHighlighted);
+
+ DrawThumbnail(ref thumbnailRect);
break;
}
default: throw new ArgumentOutOfRangeException();
}
-
- // Draw background
- if (isSelected)
- Render2D.FillRectangle(clientRect, Parent.ContainsFocus ? style.BackgroundSelected : style.LightBackground);
- else if (IsMouseOver)
- Render2D.FillRectangle(clientRect, style.BackgroundHighlighted);
-
- // Draw preview
- DrawThumbnail(ref thumbnailRect);
-
+
// Draw short name
Render2D.PushClip(ref textRect);
- Render2D.DrawText(style.FontMedium, ShowFileExtension || view.ShowFileExtensions ? FileName : ShortName, textRect, style.Foreground, nameAlignment, TextAlignment.Center, TextWrapping.WrapWords, 0.75f, 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/GUI/Tree/TreeNode.cs b/Source/Editor/GUI/Tree/TreeNode.cs
index ced70a281..7e3af05bf 100644
--- a/Source/Editor/GUI/Tree/TreeNode.cs
+++ b/Source/Editor/GUI/Tree/TreeNode.cs
@@ -762,6 +762,10 @@ namespace FlaxEditor.GUI.Tree
// Add/Remove
tree.AddOrRemoveSelection(this);
}
+ else if (button == MouseButton.Right && tree.Selection.Contains(this))
+ {
+ // Do nothing
+ }
else
{
// Select
diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs
index b1f04c8f5..9d782b13b 100644
--- a/Source/Editor/Windows/ContentWindow.cs
+++ b/Source/Editor/Windows/ContentWindow.cs
@@ -931,6 +931,7 @@ namespace FlaxEditor.Windows
// Add game project on top, plugins in the middle and engine at bottom
_root.AddChild(Editor.ContentDatabase.Game);
+ Editor.ContentDatabase.Projects.Sort();
foreach (var project in Editor.ContentDatabase.Projects)
{
project.SortChildrenRecursive();
diff --git a/Source/Engine/Content/Assets/VisualScript.cpp b/Source/Engine/Content/Assets/VisualScript.cpp
index e292a0133..698f1ca8c 100644
--- a/Source/Engine/Content/Assets/VisualScript.cpp
+++ b/Source/Engine/Content/Assets/VisualScript.cpp
@@ -1251,7 +1251,7 @@ void VisualScriptExecutor::ProcessGroupFlow(Box* boxBase, Node* node, Value& val
boxBase = node->GetBox(3);
if (boxBase->HasConnection())
eatBox(node, boxBase->FirstConnection());
- Dictionary::Iterator it(dictionary, iteratorValue.Value.AsInt);
+ Dictionary::Iterator it(&dictionary, iteratorValue.Value.AsInt);
++it;
iteratorValue.Value.AsInt = it.Index();
}
@@ -1269,12 +1269,12 @@ void VisualScriptExecutor::ProcessGroupFlow(Box* boxBase, Node* node, Value& val
// Key
case 1:
if (iteratorIndex != scope->ReturnedValues.Count() && dictionaryIndex != scope->ReturnedValues.Count())
- value = Dictionary::Iterator(*scope->ReturnedValues[dictionaryIndex].Value.AsDictionary, scope->ReturnedValues[iteratorIndex].Value.AsInt)->Key;
+ value = Dictionary::Iterator(scope->ReturnedValues[dictionaryIndex].Value.AsDictionary, scope->ReturnedValues[iteratorIndex].Value.AsInt)->Key;
break;
// Value
case 2:
if (iteratorIndex != scope->ReturnedValues.Count() && dictionaryIndex != scope->ReturnedValues.Count())
- value = Dictionary::Iterator(*scope->ReturnedValues[dictionaryIndex].Value.AsDictionary, scope->ReturnedValues[iteratorIndex].Value.AsInt)->Value;
+ value = Dictionary::Iterator(scope->ReturnedValues[dictionaryIndex].Value.AsDictionary, scope->ReturnedValues[iteratorIndex].Value.AsInt)->Value;
break;
// Break
case 5:
diff --git a/Source/Engine/Core/Collections/Array.h b/Source/Engine/Core/Collections/Array.h
index 3feae4e73..58117cf0a 100644
--- a/Source/Engine/Core/Collections/Array.h
+++ b/Source/Engine/Core/Collections/Array.h
@@ -938,12 +938,12 @@ public:
FORCE_INLINE bool IsEnd() const
{
- return _index == _array->Count();
+ return _index == _array->_count;
}
FORCE_INLINE bool IsNotEnd() const
{
- return _index != _array->Count();
+ return _index != _array->_count;
}
FORCE_INLINE T& operator*() const
@@ -975,7 +975,7 @@ public:
Iterator& operator++()
{
- if (_index != _array->Count())
+ if (_index != _array->_count)
_index++;
return *this;
}
@@ -983,7 +983,7 @@ public:
Iterator operator++(int)
{
Iterator temp = *this;
- if (_index != _array->Count())
+ if (_index != _array->_count)
_index++;
return temp;
}
diff --git a/Source/Engine/Core/Collections/ChunkedArray.h b/Source/Engine/Core/Collections/ChunkedArray.h
index 86473763b..d01711e38 100644
--- a/Source/Engine/Core/Collections/ChunkedArray.h
+++ b/Source/Engine/Core/Collections/ChunkedArray.h
@@ -95,7 +95,6 @@ public:
struct Iterator
{
friend ChunkedArray;
-
private:
ChunkedArray* _collection;
int32 _chunkIndex;
diff --git a/Source/Engine/Core/Collections/Dictionary.h b/Source/Engine/Core/Collections/Dictionary.h
index 2327f3c24..f5ffb1bfa 100644
--- a/Source/Engine/Core/Collections/Dictionary.h
+++ b/Source/Engine/Core/Collections/Dictionary.h
@@ -237,22 +237,28 @@ public:
{
friend Dictionary;
private:
- Dictionary& _collection;
+ Dictionary* _collection;
int32 _index;
public:
- Iterator(Dictionary& collection, const int32 index)
+ Iterator(Dictionary* collection, const int32 index)
: _collection(collection)
, _index(index)
{
}
- Iterator(Dictionary const& collection, const int32 index)
- : _collection((Dictionary&)collection)
+ Iterator(Dictionary const* collection, const int32 index)
+ : _collection(const_cast(collection))
, _index(index)
{
}
+ Iterator()
+ : _collection(nullptr)
+ , _index(-1)
+ {
+ }
+
Iterator(const Iterator& i)
: _collection(i._collection)
, _index(i._index)
@@ -273,27 +279,27 @@ public:
FORCE_INLINE bool IsEnd() const
{
- return _index == _collection._size;
+ return _index == _collection->_size;
}
FORCE_INLINE bool IsNotEnd() const
{
- return _index != _collection._size;
+ return _index != _collection->_size;
}
FORCE_INLINE Bucket& operator*() const
{
- return _collection._allocation.Get()[_index];
+ return _collection->_allocation.Get()[_index];
}
FORCE_INLINE Bucket* operator->() const
{
- return &_collection._allocation.Get()[_index];
+ return &_collection->_allocation.Get()[_index];
}
FORCE_INLINE explicit operator bool() const
{
- return _index >= 0 && _index < _collection._size;
+ return _index >= 0 && _index < _collection->_size;
}
FORCE_INLINE bool operator!() const
@@ -308,7 +314,7 @@ public:
FORCE_INLINE bool operator!=(const Iterator& v) const
{
- return _index != v._index || &_collection != &v._collection;
+ return _index != v._index || _collection != v._collection;
}
Iterator& operator=(const Iterator& v)
@@ -320,10 +326,10 @@ public:
Iterator& operator++()
{
- const int32 capacity = _collection.Capacity();
+ const int32 capacity = _collection->_size;
if (_index != capacity)
{
- const Bucket* data = _collection._allocation.Get();
+ const Bucket* data = _collection->_allocation.Get();
do
{
_index++;
@@ -343,7 +349,7 @@ public:
{
if (_index > 0)
{
- const Bucket* data = _collection._allocation.Get();
+ const Bucket* data = _collection->_allocation.Get();
do
{
_index--;
@@ -633,7 +639,7 @@ public:
/// Iterator with key and value.
void Add(const Iterator& i)
{
- ASSERT(&i._collection != this && i);
+ ASSERT(i._collection != this && i);
const Bucket& bucket = *i;
Add(bucket.Key, bucket.Value);
}
@@ -667,7 +673,7 @@ public:
/// True if cannot remove item from the collection because cannot find it, otherwise false.
bool Remove(const Iterator& i)
{
- ASSERT(&i._collection == this);
+ ASSERT(i._collection == this);
if (i)
{
ASSERT(_allocation.Get()[i._index].IsOccupied());
@@ -711,7 +717,7 @@ public:
return End();
FindPositionResult pos;
FindPosition(key, pos);
- return pos.ObjectIndex != -1 ? Iterator(*this, pos.ObjectIndex) : End();
+ return pos.ObjectIndex != -1 ? Iterator(this, pos.ObjectIndex) : End();
}
///
@@ -812,38 +818,38 @@ public:
public:
Iterator Begin() const
{
- Iterator i(*this, -1);
+ Iterator i(this, -1);
++i;
return i;
}
Iterator End() const
{
- return Iterator(*this, _size);
+ return Iterator(this, _size);
}
Iterator begin()
{
- Iterator i(*this, -1);
+ Iterator i(this, -1);
++i;
return i;
}
FORCE_INLINE Iterator end()
{
- return Iterator(*this, _size);
+ return Iterator(this, _size);
}
const Iterator begin() const
{
- Iterator i(*this, -1);
+ Iterator i(this, -1);
++i;
return i;
}
FORCE_INLINE const Iterator end() const
{
- return Iterator(*this, _size);
+ return Iterator(this, _size);
}
protected:
diff --git a/Source/Engine/Core/Collections/HashSet.h b/Source/Engine/Core/Collections/HashSet.h
index ad6f8ffc6..107e42e65 100644
--- a/Source/Engine/Core/Collections/HashSet.h
+++ b/Source/Engine/Core/Collections/HashSet.h
@@ -213,17 +213,17 @@ public:
{
friend HashSet;
private:
- HashSet& _collection;
+ HashSet* _collection;
int32 _index;
- Iterator(HashSet& collection, const int32 index)
+ Iterator(HashSet* collection, const int32 index)
: _collection(collection)
, _index(index)
{
}
- Iterator(HashSet const& collection, const int32 index)
- : _collection((HashSet&)collection)
+ Iterator(HashSet const* collection, const int32 index)
+ : _collection(const_cast(collection))
, _index(index)
{
}
@@ -244,27 +244,27 @@ public:
public:
FORCE_INLINE bool IsEnd() const
{
- return _index == _collection.Capacity();
+ return _index == _collection->_size;
}
FORCE_INLINE bool IsNotEnd() const
{
- return _index != _collection.Capacity();
+ return _index != _collection->_size;
}
FORCE_INLINE Bucket& operator*() const
{
- return _collection._allocation.Get()[_index];
+ return _collection->_allocation.Get()[_index];
}
FORCE_INLINE Bucket* operator->() const
{
- return &_collection._allocation.Get()[_index];
+ return &_collection->_allocation.Get()[_index];
}
FORCE_INLINE explicit operator bool() const
{
- return _index >= 0 && _index < _collection._size;
+ return _index >= 0 && _index < _collection->_size;
}
FORCE_INLINE bool operator !() const
@@ -274,12 +274,12 @@ public:
FORCE_INLINE bool operator==(const Iterator& v) const
{
- return _index == v._index && &_collection == &v._collection;
+ return _index == v._index && _collection == v._collection;
}
FORCE_INLINE bool operator!=(const Iterator& v) const
{
- return _index != v._index || &_collection != &v._collection;
+ return _index != v._index || _collection != v._collection;
}
Iterator& operator=(const Iterator& v)
@@ -291,10 +291,10 @@ public:
Iterator& operator++()
{
- const int32 capacity = _collection.Capacity();
+ const int32 capacity = _collection->_size;
if (_index != capacity)
{
- const Bucket* data = _collection._allocation.Get();
+ const Bucket* data = _collection->_allocation.Get();
do
{
_index++;
@@ -314,7 +314,7 @@ public:
{
if (_index > 0)
{
- const Bucket* data = _collection._allocation.Get();
+ const Bucket* data = _collection->_allocation.Get();
do
{
_index--;
@@ -464,7 +464,7 @@ public:
/// Iterator with item to add
void Add(const Iterator& i)
{
- ASSERT(&i._collection != this && i);
+ ASSERT(i._collection != this && i);
const Bucket& bucket = *i;
Add(bucket.Item);
}
@@ -498,7 +498,7 @@ public:
/// True if cannot remove item from the collection because cannot find it, otherwise false.
bool Remove(const Iterator& i)
{
- ASSERT(&i._collection == this);
+ ASSERT(i._collection == this);
if (i)
{
ASSERT(_allocation.Get()[i._index].IsOccupied());
@@ -523,7 +523,7 @@ public:
return End();
FindPositionResult pos;
FindPosition(item, pos);
- return pos.ObjectIndex != -1 ? Iterator(*this, pos.ObjectIndex) : End();
+ return pos.ObjectIndex != -1 ? Iterator(this, pos.ObjectIndex) : End();
}
///
@@ -559,38 +559,38 @@ public:
public:
Iterator Begin() const
{
- Iterator i(*this, -1);
+ Iterator i(this, -1);
++i;
return i;
}
Iterator End() const
{
- return Iterator(*this, _size);
+ return Iterator(this, _size);
}
Iterator begin()
{
- Iterator i(*this, -1);
+ Iterator i(this, -1);
++i;
return i;
}
FORCE_INLINE Iterator end()
{
- return Iterator(*this, _size);
+ return Iterator(this, _size);
}
const Iterator begin() const
{
- Iterator i(*this, -1);
+ Iterator i(this, -1);
++i;
return i;
}
FORCE_INLINE const Iterator end() const
{
- return Iterator(*this, _size);
+ return Iterator(this, _size);
}
protected:
diff --git a/Source/Engine/Debug/DebugDraw.cpp b/Source/Engine/Debug/DebugDraw.cpp
index 97422b9e2..e8489f5b5 100644
--- a/Source/Engine/Debug/DebugDraw.cpp
+++ b/Source/Engine/Debug/DebugDraw.cpp
@@ -920,6 +920,11 @@ void DebugDraw::DrawActors(Actor** selectedActors, int32 selectedActorsCount, bo
}
}
+void DebugDraw::DrawRay(const Vector3& origin, const Vector3& direction, const Color& color, float duration, bool depthTest)
+{
+ DrawLine(origin, origin + direction, color, duration, depthTest);
+}
+
void DebugDraw::DrawLine(const Vector3& start, const Vector3& end, const Color& color, float duration, bool depthTest)
{
const Float3 startF = start - Context->Origin, endF = end - Context->Origin;
diff --git a/Source/Engine/Debug/DebugDraw.cs b/Source/Engine/Debug/DebugDraw.cs
index af39ebeea..bc000e03d 100644
--- a/Source/Engine/Debug/DebugDraw.cs
+++ b/Source/Engine/Debug/DebugDraw.cs
@@ -31,6 +31,18 @@ namespace FlaxEngine
{
}
+ ///
+ /// 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.
+ public static void DrawRay(Vector3 origin, Vector3 direction, Color color, 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 8cc5452ed..a4f69678a 100644
--- a/Source/Engine/Debug/DebugDraw.h
+++ b/Source/Engine/Debug/DebugDraw.h
@@ -69,6 +69,16 @@ 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 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 DrawRay(const Vector3& origin, const Vector3& direction, const Color& color, float duration = 0.0f, bool depthTest = true);
+
///
/// Draws the line.
///
@@ -604,6 +614,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
API_FUNCTION() static void DrawText(const StringView& text, const Transform& transform, const Color& color, 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)
diff --git a/Source/Engine/Engine/NativeInterop.Unmanaged.cs b/Source/Engine/Engine/NativeInterop.Unmanaged.cs
index 879f804d2..2c1bab744 100644
--- a/Source/Engine/Engine/NativeInterop.Unmanaged.cs
+++ b/Source/Engine/Engine/NativeInterop.Unmanaged.cs
@@ -581,9 +581,9 @@ namespace FlaxEngine.Interop
}
[UnmanagedCallersOnly]
- internal static IntPtr NewStringLength(sbyte* text, int length)
+ internal static IntPtr NewStringUTF8(sbyte* text, int length)
{
- return ManagedString.ToNativeWeak(new string(text, 0, length));
+ return ManagedString.ToNativeWeak(new string(text, 0, length, System.Text.Encoding.UTF8));
}
[UnmanagedCallersOnly]
diff --git a/Source/Engine/Level/Actor.cpp b/Source/Engine/Level/Actor.cpp
index 388379237..394d5c9dc 100644
--- a/Source/Engine/Level/Actor.cpp
+++ b/Source/Engine/Level/Actor.cpp
@@ -496,6 +496,11 @@ void Actor::AddTagRecursive(const Tag& tag)
Tags.AddUnique(tag);
}
+void Actor::RemoveTag(const Tag& tag)
+{
+ Tags.Remove(tag);
+}
+
PRAGMA_DISABLE_DEPRECATION_WARNINGS
const String& Actor::GetTag() const
diff --git a/Source/Engine/Level/Actor.h b/Source/Engine/Level/Actor.h
index 31cfe2473..5d66e7a16 100644
--- a/Source/Engine/Level/Actor.h
+++ b/Source/Engine/Level/Actor.h
@@ -153,6 +153,11 @@ public:
/// The tag to add.
API_FUNCTION() void AddTagRecursive(const Tag& tag);
+ /// Removes a tag to the actor
+ ///
+ /// The tag to remove.
+ API_FUNCTION() void RemoveTag(const Tag& tag);
+
///
/// Gets the name of the tag.
/// [Deprecated in v1.5]
diff --git a/Source/Engine/Physics/Colliders/CharacterController.cpp b/Source/Engine/Physics/Colliders/CharacterController.cpp
index 488acd6e4..42455daa4 100644
--- a/Source/Engine/Physics/Colliders/CharacterController.cpp
+++ b/Source/Engine/Physics/Colliders/CharacterController.cpp
@@ -8,6 +8,8 @@
#include "Engine/Serialization/Serialization.h"
#include "Engine/Engine/Time.h"
+#define CC_MIN_SIZE 0.001f
+
CharacterController::CharacterController(const SpawnParams& params)
: Collider(params)
, _controller(nullptr)
@@ -100,10 +102,9 @@ void CharacterController::SetStepOffset(float value)
{
const float scaling = _cachedScale.GetAbsolute().MaxValue();
const float contactOffset = Math::Max(_contactOffset, ZeroTolerance);
- const float minSize = 0.001f;
- const float height = Math::Max(Math::Abs(_height) * scaling, minSize);
- const float radius = Math::Max(Math::Abs(_radius) * scaling - contactOffset, minSize);
- PhysicsBackend::SetControllerStepOffset(_controller, Math::Min(value, height + radius * 2.0f - minSize));
+ const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE);
+ const float radius = Math::Max(Math::Abs(_radius) * scaling - contactOffset, CC_MIN_SIZE);
+ PhysicsBackend::SetControllerStepOffset(_controller, Math::Min(value, height + radius * 2.0f - CC_MIN_SIZE));
}
}
@@ -175,9 +176,8 @@ CharacterController::CollisionFlags CharacterController::Move(const Vector3& dis
void CharacterController::DrawPhysicsDebug(RenderView& view)
{
const float scaling = _cachedScale.GetAbsolute().MaxValue();
- const float minSize = 0.001f;
- const float radius = Math::Max(Math::Abs(_radius) * scaling, minSize);
- const float height = Math::Max(Math::Abs(_height) * scaling, minSize);
+ const float radius = Math::Max(Math::Abs(_radius) * scaling, CC_MIN_SIZE);
+ const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE);
const Vector3 position = _transform.LocalToWorld(_center);
if (view.Mode == ViewMode::PhysicsColliders)
DEBUG_DRAW_TUBE(position, Quaternion::Euler(90, 0, 0), radius, height, Color::LightYellow, 0, true);
@@ -188,9 +188,8 @@ void CharacterController::DrawPhysicsDebug(RenderView& view)
void CharacterController::OnDebugDrawSelected()
{
const float scaling = _cachedScale.GetAbsolute().MaxValue();
- const float minSize = 0.001f;
- const float radius = Math::Max(Math::Abs(_radius) * scaling, minSize);
- const float height = Math::Max(Math::Abs(_height) * scaling, minSize);
+ const float radius = Math::Max(Math::Abs(_radius) * scaling, CC_MIN_SIZE);
+ const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE);
const Vector3 position = _transform.LocalToWorld(_center);
DEBUG_DRAW_WIRE_TUBE(position, Quaternion::Euler(90, 0, 0), radius, height, Color::GreenYellow, 0, false);
@@ -231,9 +230,8 @@ void CharacterController::UpdateSize() const
if (_controller)
{
const float scaling = _cachedScale.GetAbsolute().MaxValue();
- const float minSize = 0.001f;
- const float radius = Math::Max(Math::Abs(_radius) * scaling - Math::Max(_contactOffset, ZeroTolerance), minSize);
- const float height = Math::Max(Math::Abs(_height) * scaling, minSize);
+ const float radius = Math::Max(Math::Abs(_radius) * scaling - Math::Max(_contactOffset, ZeroTolerance), CC_MIN_SIZE);
+ const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE);
PhysicsBackend::SetControllerSize(_controller, radius, height);
}
}
@@ -245,12 +243,14 @@ void CharacterController::CreateShape()
void CharacterController::UpdateBounds()
{
- void* actor = _shape ? PhysicsBackend::GetShapeActor(_shape) : nullptr;
- if (actor)
- PhysicsBackend::GetActorBounds(actor, _box);
- else
- _box = BoundingBox(_transform.Translation);
+ const float scaling = GetScale().GetAbsolute().MaxValue();
+ const float radius = Math::Max(Math::Abs(_radius) * scaling, CC_MIN_SIZE);
+ const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE);
+ const Vector3 position = _transform.LocalToWorld(_center);
+ const Vector3 extent(radius, height * 0.5f + radius, radius);
+ _box = BoundingBox(position - extent, position + extent);
BoundingSphere::FromBox(_box, _sphere);
+ DEBUG_DRAW_BOX(_box, Color::Red.AlphaMultiplied(0.4f), 2.0f, true);
}
void CharacterController::AddMovement(const Vector3& translation, const Quaternion& rotation)
diff --git a/Source/Engine/Platform/Base/WindowBase.cpp b/Source/Engine/Platform/Base/WindowBase.cpp
index 5cff76361..49b4cc631 100644
--- a/Source/Engine/Platform/Base/WindowBase.cpp
+++ b/Source/Engine/Platform/Base/WindowBase.cpp
@@ -405,6 +405,7 @@ void WindowBase::OnResize(int32 width, int32 height)
_swapChain->Resize(width, height);
if (RenderTask)
RenderTask->Resize(width, height);
+ Resized({ static_cast(width), static_cast(height) });
INVOKE_EVENT_PARAMS_2(OnResize, &width, &height);
}
diff --git a/Source/Engine/Platform/Base/WindowBase.h b/Source/Engine/Platform/Base/WindowBase.h
index e4e7d2080..812359fb4 100644
--- a/Source/Engine/Platform/Base/WindowBase.h
+++ b/Source/Engine/Platform/Base/WindowBase.h
@@ -315,6 +315,11 @@ public:
///
Action Closed;
+ ///
+ /// Event fired when window gets resized.
+ ///
+ Delegate Resized;
+
///
/// Event fired when window gets focused.
///
diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp
index f4ae5b1ec..1bcef50b8 100644
--- a/Source/Engine/Scripting/Runtime/DotNet.cpp
+++ b/Source/Engine/Scripting/Runtime/DotNet.cpp
@@ -365,8 +365,8 @@ MString* MCore::String::GetEmpty(MDomain* domain)
MString* MCore::String::New(const char* str, int32 length, MDomain* domain)
{
- static void* NewStringLengthPtr = GetStaticMethodPointer(TEXT("NewStringLength"));
- return (MString*)CallStaticMethod(NewStringLengthPtr, str, length);
+ static void* NewStringUTF8Ptr = GetStaticMethodPointer(TEXT("NewStringUTF8"));
+ return (MString*)CallStaticMethod(NewStringUTF8Ptr, str, length);
}
MString* MCore::String::New(const Char* str, int32 length, MDomain* domain)
diff --git a/Source/Engine/UI/GUI/Common/Slider.cs b/Source/Engine/UI/GUI/Common/Slider.cs
new file mode 100644
index 000000000..d7324ae0e
--- /dev/null
+++ b/Source/Engine/UI/GUI/Common/Slider.cs
@@ -0,0 +1,365 @@
+using System;
+
+namespace FlaxEngine.GUI;
+
+///
+/// The slider control.
+///
+public class Slider : ContainerControl
+{
+ ///
+ /// The minimum value.
+ ///
+ protected float _minimum;
+
+ ///
+ /// The maximum value.
+ ///
+ protected float _maximum = 100f;
+
+ ///
+ /// Gets or sets the minimum value.
+ ///
+ [EditorOrder(20), Tooltip("The minimum value.")]
+ public float Minimum
+ {
+ get => _minimum;
+ set
+ {
+ if (value > _maximum)
+ throw new ArgumentOutOfRangeException();
+ if (WholeNumbers)
+ value = Mathf.RoundToInt(value);
+ _minimum = value;
+ if (Value < _minimum)
+ Value = _minimum;
+ }
+ }
+
+ ///
+ /// Gets or sets the maximum value.
+ ///
+ [EditorOrder(30), Tooltip("The maximum value.")]
+ public float Maximum
+ {
+ get => _maximum;
+ set
+ {
+ if (value < _minimum || Mathf.IsZero(value))
+ throw new ArgumentOutOfRangeException();
+ if (WholeNumbers)
+ value = Mathf.RoundToInt(value);
+ _maximum = value;
+ if (Value > _maximum)
+ Value = _maximum;
+ }
+ }
+
+ private float _value = 100f;
+ private Rectangle _thumbRect;
+ private float _thumbCenter;
+ private Float2 _thumbSize = new Float2(16, 16);
+ private bool _isSliding;
+
+ ///
+ /// Gets or sets the value (normalized to range 0-100).
+ ///
+ [EditorOrder(10), Tooltip("The current value.")]
+ public float Value
+ {
+ get => _value;
+ set
+ {
+ value = Mathf.Clamp(value, Minimum, Maximum);
+ if (WholeNumbers)
+ value = Mathf.RoundToInt(value);
+ if (!Mathf.NearEqual(value, _value))
+ {
+ _value = value;
+
+ // Update
+ UpdateThumb();
+ ValueChanged?.Invoke();
+ }
+ }
+ }
+
+ ///
+ /// The local position of the thumb center
+ ///
+ [HideInEditor]
+ public Float2 ThumbCenter => new(_thumbCenter, Height / 2);
+
+ ///
+ /// The local position of the beginning of the track.
+ ///
+ [HideInEditor]
+ public Float2 TrackBeginning => new(_thumbSize.X / 2, Height / 2);
+
+ ///
+ /// The local position of the end of the track.
+ ///
+ [HideInEditor]
+ public Float2 TrackEnd => new(Width - _thumbSize.X / 2, Height / 2);
+
+ ///
+ /// The height of the track.
+ ///
+ [EditorOrder(40), Tooltip("The track height.")]
+ public int TrackHeight { get; set; } = 2;
+
+ ///
+ /// The thumb size.
+ ///
+ [EditorOrder(41), Tooltip("The size of the thumb.")]
+ public Float2 ThumbSize {
+ get => _thumbSize;
+ set
+ {
+ _thumbSize = value;
+ UpdateThumb();
+ }
+ }
+
+ ///
+ /// Whether to fill the track.
+ ///
+ [EditorOrder(42), Tooltip("Fill the track.")]
+ public bool FillTrack = true;
+
+ ///
+ /// Whether to use whole numbers.
+ ///
+ [EditorOrder(43), Tooltip("Use whole numbers.")]
+ public bool WholeNumbers = false;
+
+ ///
+ /// The color of the slider track line
+ ///
+ [EditorDisplay("Track Style"), EditorOrder(2010), Tooltip("The color of the slider track line."), ExpandGroups]
+ public Color TrackLineColor { get; set; }
+
+ ///
+ /// The color of the slider fill track line
+ ///
+ [EditorDisplay("Track Style"), EditorOrder(2011), VisibleIf(nameof(FillTrack)), Tooltip("The color of the slider fill track line.")]
+ public Color TrackFillLineColor { get; set; }
+
+ ///
+ /// Gets the size of the track.
+ ///
+ private float TrackWidth => Width;
+
+ ///
+ /// Gets or sets the brush used for slider track drawing.
+ ///
+ [EditorDisplay("Track Style"), EditorOrder(2012), Tooltip("The brush used for slider track drawing.")]
+ public IBrush TrackBrush { get; set; }
+
+ ///
+ /// Gets or sets the brush used for slider fill track drawing.
+ ///
+ [EditorDisplay("Track Style"), EditorOrder(2013), VisibleIf(nameof(FillTrack)), Tooltip("The brush used for slider fill track drawing.")]
+ public IBrush FillTrackBrush { get; set; }
+
+ ///
+ /// The color of the slider thumb when it's not selected
+ ///
+ [EditorDisplay("Thumb Style"), EditorOrder(2030), Tooltip("The color of the slider thumb when it's not selected."), ExpandGroups]
+ public Color ThumbColor { get; set; }
+
+ ///
+ /// The color of the slider thumb when it's selected
+ ///
+ [EditorDisplay("Thumb Style"), EditorOrder(2031), Tooltip("The color of the slider thumb when it's selected.")]
+ public Color ThumbColorSelected { get; set; }
+
+ ///
+ /// Gets or sets the brush used for slider thumb drawing.
+ ///
+ [EditorDisplay("Thumb Style"), EditorOrder(2032), Tooltip("The brush of the slider thumb.")]
+ public IBrush ThumbBrush { get; set; }
+
+ ///
+ /// Gets a value indicating whether user is using a slider.
+ ///
+ [HideInEditor]
+ public bool IsSliding => _isSliding;
+
+ ///
+ /// Occurs when sliding starts.
+ ///
+ public event Action SlidingStart;
+
+ ///
+ /// Occurs when sliding ends.
+ ///
+ public event Action SlidingEnd;
+
+ ///
+ /// Occurs when value gets changed.
+ ///
+ public event Action ValueChanged;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public Slider()
+ : this(120, 30)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The width.
+ /// The height.
+ public Slider(float width, float height)
+ : base(0, 0, width, height)
+ {
+ var style = Style.Current;
+ TrackLineColor = style.BackgroundHighlighted;
+ TrackFillLineColor = style.LightBackground;
+ ThumbColor = style.BackgroundNormal;
+ ThumbColorSelected = style.BackgroundSelected;
+ UpdateThumb();
+ }
+
+ private void UpdateThumb()
+ {
+ // Cache data
+ float trackSize = TrackWidth;
+ float range = Maximum - Minimum;
+ float pixelRange = trackSize - _thumbSize.X;
+ float perc = (_value - Minimum) / range;
+ float thumbPosition = (int)(perc * pixelRange);
+ _thumbCenter = thumbPosition + _thumbSize.X / 2;
+ _thumbRect = new Rectangle(thumbPosition, (Height - _thumbSize.Y) / 2, _thumbSize.X, _thumbSize.Y);
+ }
+
+ private void EndSliding()
+ {
+ _isSliding = false;
+ EndMouseCapture();
+ SlidingEnd?.Invoke();
+ }
+
+ ///
+ public override void Draw()
+ {
+ base.Draw();
+
+ // Draw track line
+ //var lineRect = new Rectangle(4, (Height - TrackHeight) / 2, Width - 8, TrackHeight);
+ var lineRect = new Rectangle(_thumbSize.X / 2, (Height - TrackHeight) / 2, Width - _thumbSize.X, TrackHeight);
+ if (TrackBrush != null)
+ TrackBrush.Draw(lineRect, TrackLineColor);
+ else
+ Render2D.FillRectangle(lineRect, TrackLineColor);
+
+ // Draw track fill
+ if (FillTrack)
+ {
+ var fillLineRect = new Rectangle(_thumbSize.X / 2, (Height - TrackHeight - 2) / 2, Width - (Width - _thumbCenter) - _thumbSize.X / 2, TrackHeight + 2);
+ Render2D.PushClip(ref fillLineRect);
+ if (FillTrackBrush != null)
+ FillTrackBrush.Draw(lineRect, TrackFillLineColor);
+ else
+ Render2D.FillRectangle(lineRect, TrackFillLineColor);
+ Render2D.PopClip();
+ }
+
+ // Draw thumb
+ var thumbColor = _isSliding ? ThumbColorSelected : ThumbColor;
+ if (ThumbBrush != null)
+ ThumbBrush.Draw(_thumbRect, thumbColor);
+ else
+ Render2D.FillRectangle(_thumbRect, thumbColor);
+ }
+
+ ///
+ public override void OnLostFocus()
+ {
+ if (_isSliding)
+ {
+ EndSliding();
+ }
+
+ base.OnLostFocus();
+ }
+
+ ///
+ public override bool OnMouseDown(Float2 location, MouseButton button)
+ {
+ if (button == MouseButton.Left)
+ {
+ Focus();
+ float mousePosition = location.X;
+
+ if (_thumbRect.Contains(ref location))
+ {
+ // Start sliding
+ _isSliding = true;
+ StartMouseCapture();
+ SlidingStart?.Invoke();
+ return true;
+ }
+ else
+ {
+ // Click change
+ Value += (mousePosition < _thumbCenter ? -1 : 1) * 10;
+ }
+ }
+
+ return base.OnMouseDown(location, button);
+ }
+
+ ///
+ public override void OnMouseMove(Float2 location)
+ {
+ if (_isSliding)
+ {
+ // Update sliding
+ var slidePosition = location + Root.TrackingMouseOffset;
+ Value = Mathf.Remap(slidePosition.X, 4, TrackWidth - 4, Minimum, Maximum);
+ }
+ else
+ {
+ base.OnMouseMove(location);
+ }
+ }
+
+ ///
+ public override bool OnMouseUp(Float2 location, MouseButton button)
+ {
+ if (button == MouseButton.Left && _isSliding)
+ {
+ EndSliding();
+ return true;
+ }
+
+ return base.OnMouseUp(location, button);
+ }
+
+ ///
+ public override void OnEndMouseCapture()
+ {
+ // Check if was sliding
+ if (_isSliding)
+ {
+ EndSliding();
+ }
+ else
+ {
+ base.OnEndMouseCapture();
+ }
+ }
+
+ ///
+ protected override void OnSizeChanged()
+ {
+ base.OnSizeChanged();
+
+ UpdateThumb();
+ }
+}