Merge remote-tracking branch 'origin/master' into 1.7
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
using FlaxEditor.GUI;
|
using FlaxEditor.GUI;
|
||||||
|
using FlaxEditor.GUI.ContextMenu;
|
||||||
using FlaxEditor.GUI.Drag;
|
using FlaxEditor.GUI.Drag;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.GUI;
|
using FlaxEngine.GUI;
|
||||||
@@ -31,7 +32,7 @@ namespace FlaxEditor.Content.GUI
|
|||||||
: base(x, y, height)
|
: base(x, y, height)
|
||||||
{
|
{
|
||||||
TargetNode = targetNode;
|
TargetNode = targetNode;
|
||||||
Text = targetNode.NavButtonLabel + "/";
|
Text = targetNode.NavButtonLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -72,7 +73,6 @@ namespace FlaxEditor.Content.GUI
|
|||||||
|
|
||||||
if (_dragOverItems == null)
|
if (_dragOverItems == null)
|
||||||
_dragOverItems = new DragItems(ValidateDragItem);
|
_dragOverItems = new DragItems(ValidateDragItem);
|
||||||
|
|
||||||
_dragOverItems.OnDragEnter(data);
|
_dragOverItems.OnDragEnter(data);
|
||||||
var result = GetDragEffect(data);
|
var result = GetDragEffect(data);
|
||||||
_validDragOver = result != DragDropEffect.None;
|
_validDragOver = result != DragDropEffect.None;
|
||||||
@@ -122,4 +122,70 @@ namespace FlaxEditor.Content.GUI
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sealed class ContentNavigationSeparator : ComboBox
|
||||||
|
{
|
||||||
|
public ContentNavigationButton Target;
|
||||||
|
|
||||||
|
public ContentNavigationSeparator(ContentNavigationButton target, float x, float y, float height)
|
||||||
|
{
|
||||||
|
Target = target;
|
||||||
|
Bounds = new Rectangle(x, y, 16, height);
|
||||||
|
Offsets = new Margin(Bounds.X, Bounds.Width, Bounds.Y, Bounds.Height);
|
||||||
|
UpdateTransform();
|
||||||
|
|
||||||
|
MaximumItemsInViewCount = 20;
|
||||||
|
var style = Style.Current;
|
||||||
|
BackgroundColor = style.BackgroundNormal;
|
||||||
|
BackgroundColorHighlighted = BackgroundColor;
|
||||||
|
BackgroundColorSelected = BackgroundColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override ContextMenu OnCreatePopup()
|
||||||
|
{
|
||||||
|
// Update items
|
||||||
|
ClearItems();
|
||||||
|
foreach (var child in Target.TargetNode.Children)
|
||||||
|
{
|
||||||
|
if (child is ContentTreeNode node)
|
||||||
|
{
|
||||||
|
if (node.Folder.VisibleInHierarchy) // Respect the filter set by ContentFilterConfig.Filter(...)
|
||||||
|
AddItem(node.Folder.ShortName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.OnCreatePopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Draw()
|
||||||
|
{
|
||||||
|
var style = Style.Current;
|
||||||
|
var rect = new Rectangle(Float2.Zero, Size);
|
||||||
|
var color = IsDragOver ? style.BackgroundSelected * 0.6f : (_mouseDown ? style.BackgroundSelected : (IsMouseOver ? style.BackgroundHighlighted : Color.Transparent));
|
||||||
|
Render2D.FillRectangle(rect, color);
|
||||||
|
Render2D.DrawSprite(Editor.Instance.Icons.ArrowRight12, new Rectangle(rect.Location.X, rect.Y + rect.Size.Y * 0.25f, rect.Size.X, rect.Size.X), EnabledInHierarchy ? style.Foreground : style.ForegroundDisabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnLayoutMenuButton(ContextMenuButton button, int index, bool construct = false)
|
||||||
|
{
|
||||||
|
button.Icon = Editor.Instance.Icons.FolderClosed32;
|
||||||
|
if (_tooltips != null && _tooltips.Length > index)
|
||||||
|
button.TooltipText = _tooltips[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnItemClicked(int index)
|
||||||
|
{
|
||||||
|
base.OnItemClicked(index);
|
||||||
|
|
||||||
|
var item = _items[index];
|
||||||
|
foreach (var child in Target.TargetNode.Children)
|
||||||
|
{
|
||||||
|
if (child is ContentTreeNode node && node.Folder.ShortName == item)
|
||||||
|
{
|
||||||
|
Editor.Instance.Windows.ContentWin.Navigate(node);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -261,11 +261,14 @@ namespace FlaxEditor.Content.GUI
|
|||||||
ClearItems();
|
ClearItems();
|
||||||
|
|
||||||
// Add references and link items
|
// Add references and link items
|
||||||
_items.AddRange(items);
|
|
||||||
for (int i = 0; i < items.Count; i++)
|
for (int i = 0; i < items.Count; i++)
|
||||||
|
{
|
||||||
|
if (items[i].Visible)
|
||||||
{
|
{
|
||||||
items[i].Parent = this;
|
items[i].Parent = this;
|
||||||
items[i].AddReference(this);
|
items[i].AddReference(this);
|
||||||
|
_items.Add(items[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (selection != null)
|
if (selection != null)
|
||||||
{
|
{
|
||||||
@@ -711,7 +714,7 @@ namespace FlaxEditor.Content.GUI
|
|||||||
protected override void PerformLayoutBeforeChildren()
|
protected override void PerformLayoutBeforeChildren()
|
||||||
{
|
{
|
||||||
float width = GetClientArea().Width;
|
float width = GetClientArea().Width;
|
||||||
float x = 0, y = 0;
|
float x = 0, y = 1;
|
||||||
float viewScale = _viewScale * 0.97f;
|
float viewScale = _viewScale * 0.97f;
|
||||||
|
|
||||||
switch (ViewType)
|
switch (ViewType)
|
||||||
@@ -722,21 +725,22 @@ namespace FlaxEditor.Content.GUI
|
|||||||
int itemsToFit = Mathf.FloorToInt(width / defaultItemsWidth) - 1;
|
int itemsToFit = Mathf.FloorToInt(width / defaultItemsWidth) - 1;
|
||||||
if (itemsToFit < 1)
|
if (itemsToFit < 1)
|
||||||
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);
|
float itemsHeight = itemsWidth / defaultItemsWidth * (ContentItem.DefaultHeight * viewScale);
|
||||||
var flooredItemsWidth = Mathf.Floor(itemsWidth);
|
var flooredItemsWidth = Mathf.Floor(itemsWidth);
|
||||||
var flooredItemsHeight = Mathf.Floor(itemsHeight);
|
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++)
|
for (int i = 0; i < _children.Count; i++)
|
||||||
{
|
{
|
||||||
var c = _children[i];
|
var c = _children[i];
|
||||||
c.Bounds = new Rectangle(Mathf.Floor(x), Mathf.Floor(y), flooredItemsWidth, flooredItemsHeight);
|
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)
|
if (x + itemsWidth > width)
|
||||||
{
|
{
|
||||||
x = itemsToFit == 1 ? 0 : itemsWidth / itemsToFit;
|
x = itemsToFit == 1 ? 1 : itemsWidth / itemsToFit + xSpace;
|
||||||
y += itemsHeight + 5;
|
y += itemsHeight + 7;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (x > 0)
|
if (x > 0)
|
||||||
@@ -751,7 +755,7 @@ namespace FlaxEditor.Content.GUI
|
|||||||
{
|
{
|
||||||
var c = _children[i];
|
var c = _children[i];
|
||||||
c.Bounds = new Rectangle(x, y, width, itemsHeight);
|
c.Bounds = new Rectangle(x, y, width, itemsHeight);
|
||||||
y += itemsHeight + 5;
|
y += itemsHeight + 1;
|
||||||
}
|
}
|
||||||
y += 40.0f;
|
y += 40.0f;
|
||||||
|
|
||||||
|
|||||||
@@ -441,6 +441,9 @@ namespace FlaxEditor.Content
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
// Skip when hidden
|
||||||
|
if (!Visible)
|
||||||
|
return Rectangle.Empty;
|
||||||
var view = Parent as ContentView;
|
var view = Parent as ContentView;
|
||||||
var size = Size;
|
var size = Size;
|
||||||
switch (view?.ViewType ?? ContentViewType.Tiles)
|
switch (view?.ViewType ?? ContentViewType.Tiles)
|
||||||
@@ -484,6 +487,30 @@ namespace FlaxEditor.Content
|
|||||||
Render2D.FillRectangle(rectangle, Color.Black);
|
Render2D.FillRectangle(rectangle, Color.Black);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Draws the item thumbnail.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="rectangle">The thumbnail rectangle.</param>
|
||||||
|
/// /// <param name="shadow">Whether or not to draw the shadow. Overrides DrawShadow.</param>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the amount of references to that item.
|
/// Gets the amount of references to that item.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -642,7 +669,6 @@ namespace FlaxEditor.Content
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Draw()
|
public override void Draw()
|
||||||
{
|
{
|
||||||
// Cache data
|
|
||||||
var size = Size;
|
var size = Size;
|
||||||
var style = Style.Current;
|
var style = Style.Current;
|
||||||
var view = Parent as ContentView;
|
var view = Parent as ContentView;
|
||||||
@@ -655,9 +681,51 @@ namespace FlaxEditor.Content
|
|||||||
{
|
{
|
||||||
case ContentViewType.Tiles:
|
case ContentViewType.Tiles:
|
||||||
{
|
{
|
||||||
var thumbnailSize = size.X - 2 * DefaultMarginSize;
|
var thumbnailSize = size.X;
|
||||||
thumbnailRect = new Rectangle(DefaultMarginSize, DefaultMarginSize, thumbnailSize, thumbnailSize);
|
thumbnailRect = new Rectangle(0, 0, thumbnailSize, thumbnailSize);
|
||||||
nameAlignment = TextAlignment.Center;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
case ContentViewType.List:
|
case ContentViewType.List:
|
||||||
@@ -665,23 +733,21 @@ namespace FlaxEditor.Content
|
|||||||
var thumbnailSize = size.Y - 2 * DefaultMarginSize;
|
var thumbnailSize = size.Y - 2 * DefaultMarginSize;
|
||||||
thumbnailRect = new Rectangle(DefaultMarginSize, DefaultMarginSize, thumbnailSize, thumbnailSize);
|
thumbnailRect = new Rectangle(DefaultMarginSize, DefaultMarginSize, thumbnailSize, thumbnailSize);
|
||||||
nameAlignment = TextAlignment.Near;
|
nameAlignment = TextAlignment.Near;
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: throw new ArgumentOutOfRangeException();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw background
|
|
||||||
if (isSelected)
|
if (isSelected)
|
||||||
Render2D.FillRectangle(clientRect, Parent.ContainsFocus ? style.BackgroundSelected : style.LightBackground);
|
Render2D.FillRectangle(clientRect, Parent.ContainsFocus ? style.BackgroundSelected : style.LightBackground);
|
||||||
else if (IsMouseOver)
|
else if (IsMouseOver)
|
||||||
Render2D.FillRectangle(clientRect, style.BackgroundHighlighted);
|
Render2D.FillRectangle(clientRect, style.BackgroundHighlighted);
|
||||||
|
|
||||||
// Draw preview
|
|
||||||
DrawThumbnail(ref thumbnailRect);
|
DrawThumbnail(ref thumbnailRect);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
|
||||||
// Draw short name
|
// Draw short name
|
||||||
Render2D.PushClip(ref textRect);
|
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();
|
Render2D.PopClip();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ namespace FlaxEditor.Content
|
|||||||
ShowFileExtension = true;
|
ShowFileExtension = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string FilterScriptName(string input)
|
internal static string FilterScriptName(string input)
|
||||||
{
|
{
|
||||||
var length = input.Length;
|
var length = input.Length;
|
||||||
var sb = new StringBuilder(length);
|
var sb = new StringBuilder(length);
|
||||||
|
|||||||
@@ -17,6 +17,6 @@ namespace FlaxEditor.Content
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override string NavButtonLabel => string.Empty;
|
public override string NavButtonLabel => " /";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,9 +123,9 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
{
|
{
|
||||||
if (tree.IsLayoutLocked)
|
if (tree.IsLayoutLocked)
|
||||||
return;
|
return;
|
||||||
root.LockChildrenRecursive();
|
tree.LockChildrenRecursive();
|
||||||
Utilities.Utils.UpdateSearchPopupFilter(root, searchBox.Text);
|
Utilities.Utils.UpdateSearchPopupFilter(root, searchBox.Text);
|
||||||
root.UnlockChildrenRecursive();
|
tree.UnlockChildrenRecursive();
|
||||||
menu.PerformLayout();
|
menu.PerformLayout();
|
||||||
};
|
};
|
||||||
root.ExpandAll(true);
|
root.ExpandAll(true);
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
_entryIndex = entryIndex;
|
_entryIndex = entryIndex;
|
||||||
_modelInstance = modelInstance;
|
_modelInstance = modelInstance;
|
||||||
var slots = modelInstance.MaterialSlots;
|
var slots = modelInstance.MaterialSlots;
|
||||||
|
if (slots == null || entryIndex >= slots.Length)
|
||||||
|
return;
|
||||||
if (entry.Material == slots[entryIndex].Material)
|
if (entry.Material == slots[entryIndex].Material)
|
||||||
{
|
{
|
||||||
// Ensure that entry with default material set is set back to null
|
// Ensure that entry with default material set is set back to null
|
||||||
|
|||||||
@@ -593,9 +593,9 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
{
|
{
|
||||||
if (tree.IsLayoutLocked)
|
if (tree.IsLayoutLocked)
|
||||||
return;
|
return;
|
||||||
root.LockChildrenRecursive();
|
tree.LockChildrenRecursive();
|
||||||
Utilities.Utils.UpdateSearchPopupFilter(root, searchBox.Text);
|
Utilities.Utils.UpdateSearchPopupFilter(root, searchBox.Text);
|
||||||
root.UnlockChildrenRecursive();
|
tree.UnlockChildrenRecursive();
|
||||||
menu.PerformLayout();
|
menu.PerformLayout();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ public class Editor : EditorModule
|
|||||||
options.ScriptingAPI.SystemReferences.Add("System.Xml.ReaderWriter");
|
options.ScriptingAPI.SystemReferences.Add("System.Xml.ReaderWriter");
|
||||||
options.ScriptingAPI.SystemReferences.Add("System.Text.RegularExpressions");
|
options.ScriptingAPI.SystemReferences.Add("System.Text.RegularExpressions");
|
||||||
options.ScriptingAPI.SystemReferences.Add("System.ComponentModel.TypeConverter");
|
options.ScriptingAPI.SystemReferences.Add("System.ComponentModel.TypeConverter");
|
||||||
|
options.ScriptingAPI.SystemReferences.Add("System.IO.Compression.ZipFile");
|
||||||
|
|
||||||
// Enable optimizations for Editor, disable this for debugging the editor
|
// Enable optimizations for Editor, disable this for debugging the editor
|
||||||
if (options.Configuration == TargetConfiguration.Development)
|
if (options.Configuration == TargetConfiguration.Development)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using FlaxEditor.GUI.ContextMenu;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.GUI;
|
using FlaxEngine.GUI;
|
||||||
|
|
||||||
@@ -396,13 +397,23 @@ namespace FlaxEditor.GUI
|
|||||||
_popupMenu.ButtonClicked += btn =>
|
_popupMenu.ButtonClicked += btn =>
|
||||||
{
|
{
|
||||||
OnItemClicked((int)btn.Tag);
|
OnItemClicked((int)btn.Tag);
|
||||||
|
if (SupportMultiSelect)
|
||||||
|
{
|
||||||
|
// Don't hide in multi-select, so user can edit multiple elements instead of just one
|
||||||
|
UpdateButtons();
|
||||||
|
_popupMenu?.PerformLayout();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
_popupMenu?.Hide();
|
_popupMenu?.Hide();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if menu hs been already shown
|
// Check if menu hs been already shown
|
||||||
if (_popupMenu.Visible)
|
if (_popupMenu.Visible)
|
||||||
{
|
{
|
||||||
|
if (!SupportMultiSelect)
|
||||||
_popupMenu.Hide();
|
_popupMenu.Hide();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -412,27 +423,7 @@ namespace FlaxEditor.GUI
|
|||||||
// Check if has any items
|
// Check if has any items
|
||||||
if (_items.Count > 0)
|
if (_items.Count > 0)
|
||||||
{
|
{
|
||||||
// Setup items list
|
UpdateButtons();
|
||||||
var itemControls = _popupMenu.Items.ToArray();
|
|
||||||
foreach (var e in itemControls)
|
|
||||||
e.Dispose();
|
|
||||||
if (Sorted)
|
|
||||||
_items.Sort();
|
|
||||||
var style = Style.Current;
|
|
||||||
for (int i = 0; i < _items.Count; i++)
|
|
||||||
{
|
|
||||||
var btn = _popupMenu.AddButton(_items[i]);
|
|
||||||
if (_selectedIndices.Contains(i))
|
|
||||||
{
|
|
||||||
btn.Icon = style.CheckBoxTick;
|
|
||||||
}
|
|
||||||
if (_tooltips != null && _tooltips.Length > i)
|
|
||||||
{
|
|
||||||
btn.TooltipText = _tooltips[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
btn.Tag = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show dropdown list
|
// Show dropdown list
|
||||||
_popupMenu.MinimumWidth = Width;
|
_popupMenu.MinimumWidth = Width;
|
||||||
@@ -440,6 +431,54 @@ namespace FlaxEditor.GUI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates buttons layout.
|
||||||
|
/// </summary>
|
||||||
|
private void UpdateButtons()
|
||||||
|
{
|
||||||
|
if (_popupMenu.Items.Count() != _items.Count)
|
||||||
|
{
|
||||||
|
var itemControls = _popupMenu.Items.ToArray();
|
||||||
|
foreach (var e in itemControls)
|
||||||
|
e.Dispose();
|
||||||
|
if (Sorted)
|
||||||
|
_items.Sort();
|
||||||
|
for (int i = 0; i < _items.Count; i++)
|
||||||
|
{
|
||||||
|
var btn = _popupMenu.AddButton(_items[i]);
|
||||||
|
OnLayoutMenuButton(btn, i, true);
|
||||||
|
btn.Tag = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var itemControls = _popupMenu.Items.ToArray();
|
||||||
|
if (Sorted)
|
||||||
|
_items.Sort();
|
||||||
|
for (int i = 0; i < _items.Count; i++)
|
||||||
|
{
|
||||||
|
if (itemControls[i] is ContextMenuButton btn)
|
||||||
|
{
|
||||||
|
btn.Text = _items[i];
|
||||||
|
OnLayoutMenuButton(btn, i, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when button is created or updated. Can be used to customize the visuals.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="button">The button.</param>
|
||||||
|
/// <param name="index">The item index.</param>
|
||||||
|
/// <param name="construct">true if button is created else it is repainting the button</param>
|
||||||
|
protected virtual void OnLayoutMenuButton(ContextMenuButton button, int index, bool construct = false)
|
||||||
|
{
|
||||||
|
button.Checked = _selectedIndices.Contains(index);
|
||||||
|
if (_tooltips != null && _tooltips.Length > index)
|
||||||
|
button.TooltipText = _tooltips[index];
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates the popup menu.
|
/// Creates the popup menu.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -460,6 +499,8 @@ namespace FlaxEditor.GUI
|
|||||||
_popupMenu = null;
|
_popupMenu = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IsDisposing)
|
||||||
|
return;
|
||||||
_selectedIndices.Clear();
|
_selectedIndices.Clear();
|
||||||
_selectedIndices = null;
|
_selectedIndices = null;
|
||||||
_items.Clear();
|
_items.Clear();
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ namespace FlaxEditor.GUI.ContextMenu
|
|||||||
|
|
||||||
// Draw icon
|
// Draw icon
|
||||||
const float iconSize = 14;
|
const float iconSize = 14;
|
||||||
var icon = Checked ? Style.Current.CheckBoxTick : Icon;
|
var icon = Checked ? style.CheckBoxTick : Icon;
|
||||||
if (icon.IsValid)
|
if (icon.IsValid)
|
||||||
Render2D.DrawSprite(icon, new Rectangle(-iconSize - 1, (Height - iconSize) / 2, iconSize, iconSize), textColor);
|
Render2D.DrawSprite(icon, new Rectangle(-iconSize - 1, (Height - iconSize) / 2, iconSize, iconSize), textColor);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -304,6 +304,17 @@ namespace FlaxEditor.GUI
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool OnKeyDown(KeyboardKeys key)
|
||||||
|
{
|
||||||
|
if (base.OnKeyDown(key))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Fallback to the edit window for shortcuts
|
||||||
|
var editor = Editor.Instance;
|
||||||
|
return editor.Windows.EditWin.InputActions.Process(editor, this, key);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void PerformLayoutAfterChildren()
|
protected override void PerformLayoutAfterChildren()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -50,14 +50,9 @@ namespace FlaxEditor.GUI
|
|||||||
{
|
{
|
||||||
if (toolstrip == null)
|
if (toolstrip == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var lastToolstripButton = toolstrip.LastButton;
|
var lastToolstripButton = toolstrip.LastButton;
|
||||||
var parentSize = Parent.Size;
|
var parentSize = Parent.Size;
|
||||||
Bounds = new Rectangle
|
Bounds = new Rectangle(lastToolstripButton.Right + 8.0f, 0, parentSize.X - X - 8.0f, toolstrip.Height);
|
||||||
(
|
|
||||||
new Float2(lastToolstripButton.Right + 8.0f, 0),
|
|
||||||
new Float2(parentSize.X - X - 8.0f, toolstrip.Height)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using FlaxEditor.Windows;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.GUI;
|
using FlaxEngine.GUI;
|
||||||
|
|
||||||
@@ -181,5 +182,25 @@ namespace FlaxEditor.GUI
|
|||||||
|
|
||||||
PerformLayout();
|
PerformLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool OnKeyDown(KeyboardKeys key)
|
||||||
|
{
|
||||||
|
if (base.OnKeyDown(key))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Fallback to the owning window for shortcuts
|
||||||
|
EditorWindow editorWindow = null;
|
||||||
|
ContainerControl c = Parent;
|
||||||
|
while (c != null && editorWindow == null)
|
||||||
|
{
|
||||||
|
editorWindow = c as EditorWindow;
|
||||||
|
c = c.Parent;
|
||||||
|
}
|
||||||
|
var editor = Editor.Instance;
|
||||||
|
if (editorWindow == null)
|
||||||
|
editorWindow = editor.Windows.EditWin; // Fallback to main editor window
|
||||||
|
return editorWindow.InputActions.Process(editor, this, key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -762,6 +762,10 @@ namespace FlaxEditor.GUI.Tree
|
|||||||
// Add/Remove
|
// Add/Remove
|
||||||
tree.AddOrRemoveSelection(this);
|
tree.AddOrRemoveSelection(this);
|
||||||
}
|
}
|
||||||
|
else if (button == MouseButton.Right && tree.Selection.Contains(this))
|
||||||
|
{
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Select
|
// Select
|
||||||
|
|||||||
@@ -890,6 +890,23 @@ namespace FlaxEditor.Modules
|
|||||||
}
|
}
|
||||||
if (sortChildren)
|
if (sortChildren)
|
||||||
node.SortChildren();
|
node.SortChildren();
|
||||||
|
|
||||||
|
// Ignore some special folders
|
||||||
|
if (node is MainContentTreeNode mainNode && mainNode.Folder.ShortName == "Source")
|
||||||
|
{
|
||||||
|
var mainNodeChild = mainNode.Folder.Find(StringUtils.CombinePaths(mainNode.Path, "obj")) as ContentFolder;
|
||||||
|
if (mainNodeChild != null)
|
||||||
|
{
|
||||||
|
mainNodeChild.Visible = false;
|
||||||
|
mainNodeChild.Node.Visible = false;
|
||||||
|
}
|
||||||
|
mainNodeChild = mainNode.Folder.Find(StringUtils.CombinePaths(mainNode.Path, "Properties")) as ContentFolder;
|
||||||
|
if (mainNodeChild != null)
|
||||||
|
{
|
||||||
|
mainNodeChild.Visible = false;
|
||||||
|
mainNodeChild.Node.Visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LoadScripts(ContentTreeNode parent, string[] files)
|
private void LoadScripts(ContentTreeNode parent, string[] files)
|
||||||
|
|||||||
@@ -166,6 +166,8 @@ namespace FlaxEditor.Modules
|
|||||||
public void ShowFinder(Control control)
|
public void ShowFinder(Control control)
|
||||||
{
|
{
|
||||||
var finder = _finder ?? (_finder = new ContentFinder());
|
var finder = _finder ?? (_finder = new ContentFinder());
|
||||||
|
if (control == null)
|
||||||
|
control = Editor.Instance.Windows.MainWindow.GUI;
|
||||||
var position = (control.Size - new Float2(finder.Width, 300.0f)) * 0.5f;
|
var position = (control.Size - new Float2(finder.Width, 300.0f)) * 0.5f;
|
||||||
finder.Show(control, position);
|
finder.Show(control, position);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -591,7 +591,7 @@ namespace FlaxEditor.Modules
|
|||||||
private void OnActorNameChanged(Actor actor)
|
private void OnActorNameChanged(Actor actor)
|
||||||
{
|
{
|
||||||
ActorNode node = GetActorNode(actor);
|
ActorNode node = GetActorNode(actor);
|
||||||
node?.TreeNode.OnNameChanged();
|
node?.TreeNode.UpdateText();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnActorActiveChanged(Actor actor)
|
private void OnActorActiveChanged(Actor actor)
|
||||||
|
|||||||
@@ -97,11 +97,6 @@ namespace FlaxEditor.SceneGraph.GUI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void OnNameChanged()
|
|
||||||
{
|
|
||||||
UpdateText();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the tree node text.
|
/// Updates the tree node text.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -553,6 +553,18 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 0),
|
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 0),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
new NodeArchetype
|
||||||
|
{
|
||||||
|
TypeID = 112,
|
||||||
|
Title = "Particle Scale",
|
||||||
|
Description = "Particle scale.",
|
||||||
|
Flags = NodeFlags.ParticleEmitterGraph,
|
||||||
|
Size = new Float2(200, 30),
|
||||||
|
Elements = new[]
|
||||||
|
{
|
||||||
|
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(Float3), 0),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// Simulation data access nodes
|
// Simulation data access nodes
|
||||||
new NodeArchetype
|
new NodeArchetype
|
||||||
|
|||||||
@@ -430,7 +430,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
{
|
{
|
||||||
if (actorNode.Actor)
|
if (actorNode.Actor)
|
||||||
{
|
{
|
||||||
actorNode.TreeNode.OnNameChanged();
|
actorNode.TreeNode.UpdateText();
|
||||||
actorNode.TreeNode.OnOrderInParentChanged();
|
actorNode.TreeNode.OnOrderInParentChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -202,6 +202,13 @@ namespace FlaxEditor.Windows
|
|||||||
button.PerformLayout();
|
button.PerformLayout();
|
||||||
x += button.Width + NavigationBar.DefaultButtonsMargin;
|
x += button.Width + NavigationBar.DefaultButtonsMargin;
|
||||||
_navigationBar.AddChild(button);
|
_navigationBar.AddChild(button);
|
||||||
|
if (i > 0)
|
||||||
|
{
|
||||||
|
var separator = new ContentNavigationSeparator(button, x, ToolStrip.DefaultMarginV, h);
|
||||||
|
separator.PerformLayout();
|
||||||
|
x += separator.Width + NavigationBar.DefaultButtonsMargin;
|
||||||
|
_navigationBar.AddChild(separator);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
nodes.Clear();
|
nodes.Clear();
|
||||||
|
|
||||||
@@ -218,21 +225,13 @@ namespace FlaxEditor.Windows
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the current view folder.
|
/// Gets the current view folder.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ContentFolder CurrentViewFolder
|
public ContentFolder CurrentViewFolder => SelectedNode?.Folder;
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var node = SelectedNode;
|
|
||||||
return node?.Folder;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Shows the root folder.
|
/// Shows the root folder.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void ShowRoot()
|
public void ShowRoot()
|
||||||
{
|
{
|
||||||
// Show root folder
|
|
||||||
_tree.Select(_root);
|
_tree.Select(_root);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -191,6 +191,7 @@ namespace FlaxEditor.Windows
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Search by filter only
|
// Search by filter only
|
||||||
|
bool showAllFiles = _showAllFiles;
|
||||||
if (string.IsNullOrWhiteSpace(query))
|
if (string.IsNullOrWhiteSpace(query))
|
||||||
{
|
{
|
||||||
if (SelectedNode == _root)
|
if (SelectedNode == _root)
|
||||||
@@ -199,12 +200,12 @@ namespace FlaxEditor.Windows
|
|||||||
for (int i = 0; i < _root.ChildrenCount; i++)
|
for (int i = 0; i < _root.ChildrenCount; i++)
|
||||||
{
|
{
|
||||||
if (_root.GetChild(i) is ContentTreeNode node)
|
if (_root.GetChild(i) is ContentTreeNode node)
|
||||||
UpdateItemsSearchFilter(node.Folder, items, filters);
|
UpdateItemsSearchFilter(node.Folder, items, filters, showAllFiles);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
UpdateItemsSearchFilter(CurrentViewFolder, items, filters);
|
UpdateItemsSearchFilter(CurrentViewFolder, items, filters, showAllFiles);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Search by asset ID
|
// Search by asset ID
|
||||||
@@ -221,12 +222,12 @@ namespace FlaxEditor.Windows
|
|||||||
for (int i = 0; i < _root.ChildrenCount; i++)
|
for (int i = 0; i < _root.ChildrenCount; i++)
|
||||||
{
|
{
|
||||||
if (_root.GetChild(i) is ContentTreeNode node)
|
if (_root.GetChild(i) is ContentTreeNode node)
|
||||||
UpdateItemsSearchFilter(node.Folder, items, filters, query);
|
UpdateItemsSearchFilter(node.Folder, items, filters, showAllFiles, query);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
UpdateItemsSearchFilter(CurrentViewFolder, items, filters, query);
|
UpdateItemsSearchFilter(CurrentViewFolder, items, filters, showAllFiles, query);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,39 +235,32 @@ namespace FlaxEditor.Windows
|
|||||||
_view.ShowItems(items, _sortType);
|
_view.ShowItems(items, _sortType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateItemsSearchFilter(ContentFolder folder, List<ContentItem> items, bool[] filters)
|
private void UpdateItemsSearchFilter(ContentFolder folder, List<ContentItem> items, bool[] filters, bool showAllFiles)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < folder.Children.Count; i++)
|
for (int i = 0; i < folder.Children.Count; i++)
|
||||||
{
|
{
|
||||||
var child = folder.Children[i];
|
var child = folder.Children[i];
|
||||||
|
|
||||||
if (child is ContentFolder childFolder)
|
if (child is ContentFolder childFolder)
|
||||||
{
|
{
|
||||||
UpdateItemsSearchFilter(childFolder, items, filters);
|
UpdateItemsSearchFilter(childFolder, items, filters, showAllFiles);
|
||||||
}
|
}
|
||||||
else
|
else if (filters[(int)child.SearchFilter] && (showAllFiles || !(child is FileItem)))
|
||||||
{
|
|
||||||
if (filters[(int)child.SearchFilter])
|
|
||||||
{
|
{
|
||||||
items.Add(child);
|
items.Add(child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateItemsSearchFilter(ContentFolder folder, List<ContentItem> items, bool[] filters, string filterText)
|
private void UpdateItemsSearchFilter(ContentFolder folder, List<ContentItem> items, bool[] filters, bool showAllFiles, string filterText)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < folder.Children.Count; i++)
|
for (int i = 0; i < folder.Children.Count; i++)
|
||||||
{
|
{
|
||||||
var child = folder.Children[i];
|
var child = folder.Children[i];
|
||||||
|
|
||||||
if (child is ContentFolder childFolder)
|
if (child is ContentFolder childFolder)
|
||||||
{
|
{
|
||||||
UpdateItemsSearchFilter(childFolder, items, filters, filterText);
|
UpdateItemsSearchFilter(childFolder, items, filters, showAllFiles, filterText);
|
||||||
}
|
}
|
||||||
else if (filters[(int)child.SearchFilter])
|
else if (filters[(int)child.SearchFilter] && (showAllFiles || !(child is FileItem)) && QueryFilterHelper.Match(filterText, child.ShortName))
|
||||||
{
|
|
||||||
if (filters[(int)child.SearchFilter] && QueryFilterHelper.Match(filterText, child.ShortName))
|
|
||||||
{
|
{
|
||||||
items.Add(child);
|
items.Add(child);
|
||||||
}
|
}
|
||||||
@@ -274,4 +268,3 @@ namespace FlaxEditor.Windows
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
using FlaxEditor.Content;
|
using FlaxEditor.Content;
|
||||||
using FlaxEditor.Content.GUI;
|
using FlaxEditor.Content.GUI;
|
||||||
@@ -45,6 +46,7 @@ namespace FlaxEditor.Windows
|
|||||||
private TextBox _itemsSearchBox;
|
private TextBox _itemsSearchBox;
|
||||||
private ViewDropdown _viewDropdown;
|
private ViewDropdown _viewDropdown;
|
||||||
private SortType _sortType;
|
private SortType _sortType;
|
||||||
|
private bool _showEngineFiles = true, _showPluginsFiles = true, _showAllFiles = true;
|
||||||
|
|
||||||
private RootContentTreeNode _root;
|
private RootContentTreeNode _root;
|
||||||
|
|
||||||
@@ -64,6 +66,57 @@ namespace FlaxEditor.Windows
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ContentView View => _view;
|
public ContentView View => _view;
|
||||||
|
|
||||||
|
internal bool ShowEngineFiles
|
||||||
|
{
|
||||||
|
get => _showEngineFiles;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_showEngineFiles != value)
|
||||||
|
{
|
||||||
|
_showEngineFiles = value;
|
||||||
|
if (Editor.ContentDatabase.Engine != null)
|
||||||
|
{
|
||||||
|
Editor.ContentDatabase.Engine.Visible = value;
|
||||||
|
Editor.ContentDatabase.Engine.Folder.Visible = value;
|
||||||
|
RefreshView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool ShowPluginsFiles
|
||||||
|
{
|
||||||
|
get => _showPluginsFiles;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_showPluginsFiles != value)
|
||||||
|
{
|
||||||
|
_showPluginsFiles = value;
|
||||||
|
foreach (var project in Editor.ContentDatabase.Projects)
|
||||||
|
{
|
||||||
|
if (project == Editor.ContentDatabase.Game || project == Editor.ContentDatabase.Engine)
|
||||||
|
continue;
|
||||||
|
project.Visible = value;
|
||||||
|
project.Folder.Visible = value;
|
||||||
|
RefreshView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool ShowAllFiles
|
||||||
|
{
|
||||||
|
get => _showAllFiles;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_showAllFiles != value)
|
||||||
|
{
|
||||||
|
_showAllFiles = value;
|
||||||
|
RefreshView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="ContentWindow"/> class.
|
/// Initializes a new instance of the <see cref="ContentWindow"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -107,11 +160,12 @@ namespace FlaxEditor.Windows
|
|||||||
_navigateBackwardButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Left64, NavigateBackward).LinkTooltip("Navigate backward");
|
_navigateBackwardButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Left64, NavigateBackward).LinkTooltip("Navigate backward");
|
||||||
_navigateForwardButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Right64, NavigateForward).LinkTooltip("Navigate forward");
|
_navigateForwardButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Right64, NavigateForward).LinkTooltip("Navigate forward");
|
||||||
_navigateUpButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Up64, NavigateUp).LinkTooltip("Navigate up");
|
_navigateUpButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Up64, NavigateUp).LinkTooltip("Navigate up");
|
||||||
|
_toolStrip.AddSeparator();
|
||||||
|
|
||||||
// Navigation bar
|
// Navigation bar
|
||||||
_navigationBar = new NavigationBar
|
_navigationBar = new NavigationBar
|
||||||
{
|
{
|
||||||
Parent = this,
|
Parent = _toolStrip,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Split panel
|
// Split panel
|
||||||
@@ -216,10 +270,6 @@ namespace FlaxEditor.Windows
|
|||||||
{
|
{
|
||||||
var menu = new ContextMenu();
|
var menu = new ContextMenu();
|
||||||
|
|
||||||
var showFileExtensionsButton = menu.AddButton("Show file extensions", () => View.ShowFileExtensions = !View.ShowFileExtensions);
|
|
||||||
showFileExtensionsButton.Checked = View.ShowFileExtensions;
|
|
||||||
showFileExtensionsButton.AutoCheck = true;
|
|
||||||
|
|
||||||
var viewScale = menu.AddButton("View Scale");
|
var viewScale = menu.AddButton("View Scale");
|
||||||
viewScale.CloseMenuOnClick = false;
|
viewScale.CloseMenuOnClick = false;
|
||||||
var scaleValue = new FloatValueBox(1, 75, 2, 50.0f, 0.3f, 3.0f, 0.01f)
|
var scaleValue = new FloatValueBox(1, 75, 2, 50.0f, 0.3f, 3.0f, 0.01f)
|
||||||
@@ -243,6 +293,33 @@ namespace FlaxEditor.Windows
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var show = menu.AddChildMenu("Show");
|
||||||
|
{
|
||||||
|
var b = show.ContextMenu.AddButton("File extensions", () => View.ShowFileExtensions = !View.ShowFileExtensions);
|
||||||
|
b.TooltipText = "Shows all files with extensions";
|
||||||
|
b.Checked = View.ShowFileExtensions;
|
||||||
|
b.CloseMenuOnClick = false;
|
||||||
|
b.AutoCheck = true;
|
||||||
|
|
||||||
|
b = show.ContextMenu.AddButton("Engine files", () => ShowEngineFiles = !ShowEngineFiles);
|
||||||
|
b.TooltipText = "Shows in-built engine content";
|
||||||
|
b.Checked = ShowEngineFiles;
|
||||||
|
b.CloseMenuOnClick = false;
|
||||||
|
b.AutoCheck = true;
|
||||||
|
|
||||||
|
b = show.ContextMenu.AddButton("Plugins files", () => ShowPluginsFiles = !ShowPluginsFiles);
|
||||||
|
b.TooltipText = "Shows plugin projects content";
|
||||||
|
b.Checked = ShowPluginsFiles;
|
||||||
|
b.CloseMenuOnClick = false;
|
||||||
|
b.AutoCheck = true;
|
||||||
|
|
||||||
|
b = show.ContextMenu.AddButton("All files", () => ShowAllFiles = !ShowAllFiles);
|
||||||
|
b.TooltipText = "Shows all files including other than assets and source code";
|
||||||
|
b.Checked = ShowAllFiles;
|
||||||
|
b.CloseMenuOnClick = false;
|
||||||
|
b.AutoCheck = true;
|
||||||
|
}
|
||||||
|
|
||||||
var filters = menu.AddChildMenu("Filters");
|
var filters = menu.AddChildMenu("Filters");
|
||||||
for (int i = 0; i < _viewDropdown.Items.Count; i++)
|
for (int i = 0; i < _viewDropdown.Items.Count; i++)
|
||||||
{
|
{
|
||||||
@@ -428,10 +505,7 @@ namespace FlaxEditor.Windows
|
|||||||
if (!Editor.ContentEditing.IsValidAssetName(item, newShortName, out string hint))
|
if (!Editor.ContentEditing.IsValidAssetName(item, newShortName, out string hint))
|
||||||
{
|
{
|
||||||
// Invalid name
|
// Invalid name
|
||||||
MessageBox.Show("Given asset name is invalid. " + hint,
|
MessageBox.Show("Given asset name is invalid. " + hint, "Invalid name", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
"Invalid name",
|
|
||||||
MessageBoxButtons.OK,
|
|
||||||
MessageBoxIcon.Error);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -876,7 +950,7 @@ namespace FlaxEditor.Windows
|
|||||||
if (target == _root)
|
if (target == _root)
|
||||||
{
|
{
|
||||||
// Special case for root folder
|
// Special case for root folder
|
||||||
List<ContentItem> items = new List<ContentItem>(8);
|
var items = new List<ContentItem>(8);
|
||||||
for (int i = 0; i < _root.ChildrenCount; i++)
|
for (int i = 0; i < _root.ChildrenCount; i++)
|
||||||
{
|
{
|
||||||
if (_root.GetChild(i) is ContentTreeNode node)
|
if (_root.GetChild(i) is ContentTreeNode node)
|
||||||
@@ -889,7 +963,10 @@ namespace FlaxEditor.Windows
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Show folder contents
|
// Show folder contents
|
||||||
_view.ShowItems(target.Folder.Children, _sortType, false, true);
|
var items = target.Folder.Children;
|
||||||
|
if (!_showAllFiles)
|
||||||
|
items = items.Where(x => !(x is FileItem)).ToList();
|
||||||
|
_view.ShowItems(items, _sortType, false, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -912,12 +989,6 @@ namespace FlaxEditor.Windows
|
|||||||
_navigateUpButton.Enabled = folder != null && _tree.SelectedNode != _root;
|
_navigateUpButton.Enabled = folder != null && _tree.SelectedNode != _root;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RemoveFolder2Root(ContentTreeNode node)
|
|
||||||
{
|
|
||||||
// Remove from the root
|
|
||||||
_root.RemoveChild(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnInit()
|
public override void OnInit()
|
||||||
{
|
{
|
||||||
@@ -930,13 +1001,18 @@ namespace FlaxEditor.Windows
|
|||||||
|
|
||||||
// Add game project on top, plugins in the middle and engine at bottom
|
// Add game project on top, plugins in the middle and engine at bottom
|
||||||
_root.AddChild(Editor.ContentDatabase.Game);
|
_root.AddChild(Editor.ContentDatabase.Game);
|
||||||
|
Editor.ContentDatabase.Projects.Sort();
|
||||||
foreach (var project in Editor.ContentDatabase.Projects)
|
foreach (var project in Editor.ContentDatabase.Projects)
|
||||||
{
|
{
|
||||||
project.SortChildrenRecursive();
|
project.SortChildrenRecursive();
|
||||||
if (project == Editor.ContentDatabase.Game || project == Editor.ContentDatabase.Engine)
|
if (project == Editor.ContentDatabase.Game || project == Editor.ContentDatabase.Engine)
|
||||||
continue;
|
continue;
|
||||||
|
project.Visible = _showPluginsFiles;
|
||||||
|
project.Folder.Visible = _showPluginsFiles;
|
||||||
_root.AddChild(project);
|
_root.AddChild(project);
|
||||||
}
|
}
|
||||||
|
Editor.ContentDatabase.Engine.Visible = _showEngineFiles;
|
||||||
|
Editor.ContentDatabase.Engine.Folder.Visible = _showEngineFiles;
|
||||||
_root.AddChild(Editor.ContentDatabase.Engine);
|
_root.AddChild(Editor.ContentDatabase.Engine);
|
||||||
|
|
||||||
Editor.ContentDatabase.Game?.Expand(true);
|
Editor.ContentDatabase.Game?.Expand(true);
|
||||||
@@ -1008,7 +1084,6 @@ namespace FlaxEditor.Windows
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool OnMouseUp(Float2 location, MouseButton button)
|
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||||
{
|
{
|
||||||
// Check if it's a right mouse button
|
|
||||||
if (button == MouseButton.Right)
|
if (button == MouseButton.Right)
|
||||||
{
|
{
|
||||||
// Find control that is under the mouse
|
// Find control that is under the mouse
|
||||||
@@ -1064,6 +1139,9 @@ namespace FlaxEditor.Windows
|
|||||||
LayoutSerializeSplitter(writer, "Split", _split);
|
LayoutSerializeSplitter(writer, "Split", _split);
|
||||||
writer.WriteAttributeString("Scale", _view.ViewScale.ToString(CultureInfo.InvariantCulture));
|
writer.WriteAttributeString("Scale", _view.ViewScale.ToString(CultureInfo.InvariantCulture));
|
||||||
writer.WriteAttributeString("ShowFileExtensions", _view.ShowFileExtensions.ToString());
|
writer.WriteAttributeString("ShowFileExtensions", _view.ShowFileExtensions.ToString());
|
||||||
|
writer.WriteAttributeString("ShowEngineFiles", ShowEngineFiles.ToString());
|
||||||
|
writer.WriteAttributeString("ShowPluginsFiles", ShowPluginsFiles.ToString());
|
||||||
|
writer.WriteAttributeString("ShowAllFiles", ShowAllFiles.ToString());
|
||||||
writer.WriteAttributeString("ViewType", _view.ViewType.ToString());
|
writer.WriteAttributeString("ViewType", _view.ViewType.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1075,6 +1153,12 @@ namespace FlaxEditor.Windows
|
|||||||
_view.ViewScale = value1;
|
_view.ViewScale = value1;
|
||||||
if (bool.TryParse(node.GetAttribute("ShowFileExtensions"), out bool value2))
|
if (bool.TryParse(node.GetAttribute("ShowFileExtensions"), out bool value2))
|
||||||
_view.ShowFileExtensions = value2;
|
_view.ShowFileExtensions = value2;
|
||||||
|
if (bool.TryParse(node.GetAttribute("ShowEngineFiles"), out value2))
|
||||||
|
ShowEngineFiles = value2;
|
||||||
|
if (bool.TryParse(node.GetAttribute("ShowPluginsFiles"), out value2))
|
||||||
|
ShowPluginsFiles = value2;
|
||||||
|
if (bool.TryParse(node.GetAttribute("ShowAllFiles"), out value2))
|
||||||
|
ShowAllFiles = value2;
|
||||||
if (Enum.TryParse(node.GetAttribute("ViewType"), out ContentViewType viewType))
|
if (Enum.TryParse(node.GetAttribute("ViewType"), out ContentViewType viewType))
|
||||||
_view.ViewType = viewType;
|
_view.ViewType = viewType;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,18 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using FlaxEditor.GUI;
|
using FlaxEditor.GUI;
|
||||||
|
using FlaxEditor.GUI.ContextMenu;
|
||||||
using FlaxEditor.GUI.Tabs;
|
using FlaxEditor.GUI.Tabs;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.GUI;
|
using FlaxEngine.GUI;
|
||||||
|
using FlaxEngine.Json;
|
||||||
|
|
||||||
namespace FlaxEditor.Windows
|
namespace FlaxEditor.Windows
|
||||||
{
|
{
|
||||||
@@ -17,6 +24,8 @@ namespace FlaxEditor.Windows
|
|||||||
public sealed class PluginsWindow : EditorWindow
|
public sealed class PluginsWindow : EditorWindow
|
||||||
{
|
{
|
||||||
private Tabs _tabs;
|
private Tabs _tabs;
|
||||||
|
private Button _addPluginProjectButton;
|
||||||
|
private Button _cloneProjectButton;
|
||||||
private readonly List<CategoryEntry> _categories = new List<CategoryEntry>();
|
private readonly List<CategoryEntry> _categories = new List<CategoryEntry>();
|
||||||
private readonly Dictionary<Plugin, PluginEntry> _entries = new Dictionary<Plugin, PluginEntry>();
|
private readonly Dictionary<Plugin, PluginEntry> _entries = new Dictionary<Plugin, PluginEntry>();
|
||||||
|
|
||||||
@@ -174,19 +183,576 @@ namespace FlaxEditor.Windows
|
|||||||
{
|
{
|
||||||
Title = "Plugins";
|
Title = "Plugins";
|
||||||
|
|
||||||
|
var vp = new Panel
|
||||||
|
{
|
||||||
|
AnchorPreset = AnchorPresets.StretchAll,
|
||||||
|
Parent = this,
|
||||||
|
};
|
||||||
|
_addPluginProjectButton = new Button
|
||||||
|
{
|
||||||
|
Text = "Create Plugin Project",
|
||||||
|
TooltipText = "Add new plugin project.",
|
||||||
|
AnchorPreset = AnchorPresets.TopLeft,
|
||||||
|
LocalLocation = new Float2(70, 18),
|
||||||
|
Size = new Float2(150, 25),
|
||||||
|
Parent = vp,
|
||||||
|
};
|
||||||
|
_addPluginProjectButton.Clicked += OnAddButtonClicked;
|
||||||
|
|
||||||
|
_cloneProjectButton = new Button
|
||||||
|
{
|
||||||
|
Text = "Clone Plugin Project",
|
||||||
|
TooltipText = "Git Clone a plugin project.",
|
||||||
|
AnchorPreset = AnchorPresets.TopLeft,
|
||||||
|
LocalLocation = new Float2(70 + _addPluginProjectButton.Size.X + 8, 18),
|
||||||
|
Size = new Float2(150, 25),
|
||||||
|
Parent = vp,
|
||||||
|
};
|
||||||
|
_cloneProjectButton.Clicked += OnCloneProjectButtonClicked;
|
||||||
|
|
||||||
_tabs = new Tabs
|
_tabs = new Tabs
|
||||||
{
|
{
|
||||||
Orientation = Orientation.Vertical,
|
Orientation = Orientation.Vertical,
|
||||||
AnchorPreset = AnchorPresets.StretchAll,
|
AnchorPreset = AnchorPresets.StretchAll,
|
||||||
Offsets = Margin.Zero,
|
Offsets = new Margin(0, 0, _addPluginProjectButton.Bottom + 8, 0),
|
||||||
TabsSize = new Float2(120, 32),
|
TabsSize = new Float2(120, 32),
|
||||||
Parent = this
|
Parent = vp
|
||||||
};
|
};
|
||||||
|
|
||||||
OnPluginsChanged();
|
OnPluginsChanged();
|
||||||
PluginManager.PluginsChanged += OnPluginsChanged;
|
PluginManager.PluginsChanged += OnPluginsChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnCloneProjectButtonClicked()
|
||||||
|
{
|
||||||
|
var popup = new ContextMenuBase
|
||||||
|
{
|
||||||
|
Size = new Float2(300, 125),
|
||||||
|
ClipChildren = false,
|
||||||
|
CullChildren = false,
|
||||||
|
};
|
||||||
|
popup.Show(_cloneProjectButton, new Float2(_cloneProjectButton.Width, 0));
|
||||||
|
|
||||||
|
var nameLabel = new Label
|
||||||
|
{
|
||||||
|
Parent = popup,
|
||||||
|
AnchorPreset = AnchorPresets.TopLeft,
|
||||||
|
AutoWidth = true,
|
||||||
|
Text = "Name",
|
||||||
|
HorizontalAlignment = TextAlignment.Near,
|
||||||
|
};
|
||||||
|
nameLabel.LocalY += 10;
|
||||||
|
|
||||||
|
var nameTextBox = new TextBox
|
||||||
|
{
|
||||||
|
Parent = popup,
|
||||||
|
WatermarkText = "Plugin Name",
|
||||||
|
TooltipText = "If left blank, this will take the git name.",
|
||||||
|
AnchorPreset = AnchorPresets.TopLeft,
|
||||||
|
IsMultiline = false,
|
||||||
|
};
|
||||||
|
nameTextBox.LocalX += (300 - (10)) * 0.5f;
|
||||||
|
nameTextBox.LocalY += 10;
|
||||||
|
nameLabel.LocalX += (300 - (nameLabel.Width + nameTextBox.Width)) * 0.5f + 10;
|
||||||
|
|
||||||
|
var defaultTextBoxBorderColor = nameTextBox.BorderColor;
|
||||||
|
var defaultTextBoxBorderSelectedColor = nameTextBox.BorderSelectedColor;
|
||||||
|
nameTextBox.TextChanged += () =>
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(nameTextBox.Text))
|
||||||
|
{
|
||||||
|
nameTextBox.BorderColor = defaultTextBoxBorderColor;
|
||||||
|
nameTextBox.BorderSelectedColor = defaultTextBoxBorderSelectedColor;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pluginPath = Path.Combine(Globals.ProjectFolder, "Plugins", nameTextBox.Text);
|
||||||
|
if (Directory.Exists(pluginPath))
|
||||||
|
{
|
||||||
|
nameTextBox.BorderColor = Color.Red;
|
||||||
|
nameTextBox.BorderSelectedColor = Color.Red;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nameTextBox.BorderColor = defaultTextBoxBorderColor;
|
||||||
|
nameTextBox.BorderSelectedColor = defaultTextBoxBorderSelectedColor;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var gitPathLabel = new Label
|
||||||
|
{
|
||||||
|
Parent = popup,
|
||||||
|
AnchorPreset = AnchorPresets.TopLeft,
|
||||||
|
AutoWidth = true,
|
||||||
|
Text = "Git Path",
|
||||||
|
HorizontalAlignment = TextAlignment.Near,
|
||||||
|
};
|
||||||
|
gitPathLabel.LocalX += (300 - gitPathLabel.Width) * 0.5f;
|
||||||
|
gitPathLabel.LocalY += 35;
|
||||||
|
|
||||||
|
var gitPathTextBox = new TextBox
|
||||||
|
{
|
||||||
|
Parent = popup,
|
||||||
|
WatermarkText = "https://github.com/FlaxEngine/ExamplePlugin.git",
|
||||||
|
AnchorPreset = AnchorPresets.TopLeft,
|
||||||
|
Size = new Float2(280, TextBox.DefaultHeight),
|
||||||
|
IsMultiline = false,
|
||||||
|
};
|
||||||
|
gitPathTextBox.LocalY += 60;
|
||||||
|
gitPathTextBox.LocalX += 10;
|
||||||
|
|
||||||
|
var submitButton = new Button
|
||||||
|
{
|
||||||
|
Parent = popup,
|
||||||
|
AnchorPreset = AnchorPresets.TopLeft,
|
||||||
|
Text = "Clone",
|
||||||
|
Width = 70,
|
||||||
|
};
|
||||||
|
submitButton.LocalX += 300 * 0.5f - submitButton.Width - 10;
|
||||||
|
submitButton.LocalY += 90;
|
||||||
|
|
||||||
|
submitButton.Clicked += () =>
|
||||||
|
{
|
||||||
|
if (Directory.Exists(Path.Combine(Globals.ProjectFolder, "Plugins", nameTextBox.Text)) && !string.IsNullOrEmpty(nameTextBox.Text))
|
||||||
|
{
|
||||||
|
Editor.LogWarning("Cannot create plugin due to name conflict.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
OnCloneButtonClicked(nameTextBox.Text, gitPathTextBox.Text);
|
||||||
|
nameTextBox.Clear();
|
||||||
|
gitPathTextBox.Clear();
|
||||||
|
popup.Hide();
|
||||||
|
};
|
||||||
|
|
||||||
|
var cancelButton = new Button
|
||||||
|
{
|
||||||
|
Parent = popup,
|
||||||
|
AnchorPreset = AnchorPresets.TopLeft,
|
||||||
|
Text = "Cancel",
|
||||||
|
Width = 70,
|
||||||
|
};
|
||||||
|
cancelButton.LocalX += 300 * 0.5f + 10;
|
||||||
|
cancelButton.LocalY += 90;
|
||||||
|
|
||||||
|
cancelButton.Clicked += () =>
|
||||||
|
{
|
||||||
|
nameTextBox.Clear();
|
||||||
|
gitPathTextBox.Clear();
|
||||||
|
popup.Hide();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void OnCloneButtonClicked(string pluginName, string gitPath)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(gitPath))
|
||||||
|
{
|
||||||
|
Editor.LogError("Failed to create plugin project due to no GIT path.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (string.IsNullOrEmpty(pluginName))
|
||||||
|
{
|
||||||
|
var split = gitPath.Split('/');
|
||||||
|
if (string.IsNullOrEmpty(split[^1]))
|
||||||
|
{
|
||||||
|
var name = split[^2].Replace(".git", "");
|
||||||
|
pluginName = name;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var name = split[^1].Replace(".git", "");
|
||||||
|
pluginName = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var clonePath = Path.Combine(Globals.ProjectFolder, "Plugins", pluginName);
|
||||||
|
if (!Directory.Exists(clonePath))
|
||||||
|
Directory.CreateDirectory(clonePath);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Editor.LogError("Plugin Name is already used. Pick a different Name.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Start git clone
|
||||||
|
var settings = new CreateProcessSettings
|
||||||
|
{
|
||||||
|
FileName = "git",
|
||||||
|
Arguments = $"clone {gitPath} \"{clonePath}\"",
|
||||||
|
ShellExecute = false,
|
||||||
|
LogOutput = true,
|
||||||
|
};
|
||||||
|
Platform.CreateProcess(ref settings);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Editor.LogError($"Failed Git process. {e}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Editor.Log("Plugin project has been cloned.");
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
if (file.Contains(".flaxproj", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
pluginProjectName = Path.GetFileNameWithoutExtension(file);
|
||||||
|
Debug.Log(pluginProjectName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnAddButtonClicked()
|
||||||
|
{
|
||||||
|
var popup = new ContextMenuBase
|
||||||
|
{
|
||||||
|
Size = new Float2(230, 125),
|
||||||
|
ClipChildren = false,
|
||||||
|
CullChildren = false,
|
||||||
|
};
|
||||||
|
popup.Show(_addPluginProjectButton, new Float2(_addPluginProjectButton.Width, 0));
|
||||||
|
|
||||||
|
var nameLabel = new Label
|
||||||
|
{
|
||||||
|
Parent = popup,
|
||||||
|
AnchorPreset = AnchorPresets.TopLeft,
|
||||||
|
Text = "Name",
|
||||||
|
HorizontalAlignment = TextAlignment.Near,
|
||||||
|
};
|
||||||
|
nameLabel.LocalX += 10;
|
||||||
|
nameLabel.LocalY += 10;
|
||||||
|
|
||||||
|
var nameTextBox = new TextBox
|
||||||
|
{
|
||||||
|
Parent = popup,
|
||||||
|
WatermarkText = "Plugin Name",
|
||||||
|
AnchorPreset = AnchorPresets.TopLeft,
|
||||||
|
IsMultiline = false,
|
||||||
|
};
|
||||||
|
nameTextBox.LocalX += 100;
|
||||||
|
nameTextBox.LocalY += 10;
|
||||||
|
var defaultTextBoxBorderColor = nameTextBox.BorderColor;
|
||||||
|
var defaultTextBoxBorderSelectedColor = nameTextBox.BorderSelectedColor;
|
||||||
|
nameTextBox.TextChanged += () =>
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(nameTextBox.Text))
|
||||||
|
{
|
||||||
|
nameTextBox.BorderColor = defaultTextBoxBorderColor;
|
||||||
|
nameTextBox.BorderSelectedColor = defaultTextBoxBorderSelectedColor;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pluginPath = Path.Combine(Globals.ProjectFolder, "Plugins", nameTextBox.Text);
|
||||||
|
if (Directory.Exists(pluginPath))
|
||||||
|
{
|
||||||
|
nameTextBox.BorderColor = Color.Red;
|
||||||
|
nameTextBox.BorderSelectedColor = Color.Red;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nameTextBox.BorderColor = defaultTextBoxBorderColor;
|
||||||
|
nameTextBox.BorderSelectedColor = defaultTextBoxBorderSelectedColor;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var versionLabel = new Label
|
||||||
|
{
|
||||||
|
Parent = popup,
|
||||||
|
AnchorPreset = AnchorPresets.TopLeft,
|
||||||
|
Text = "Version",
|
||||||
|
HorizontalAlignment = TextAlignment.Near,
|
||||||
|
};
|
||||||
|
versionLabel.LocalX += 10;
|
||||||
|
versionLabel.LocalY += 35;
|
||||||
|
|
||||||
|
var versionTextBox = new TextBox
|
||||||
|
{
|
||||||
|
Parent = popup,
|
||||||
|
WatermarkText = "1.0.0",
|
||||||
|
AnchorPreset = AnchorPresets.TopLeft,
|
||||||
|
IsMultiline = false,
|
||||||
|
};
|
||||||
|
versionTextBox.LocalY += 35;
|
||||||
|
versionTextBox.LocalX += 100;
|
||||||
|
|
||||||
|
var companyLabel = new Label
|
||||||
|
{
|
||||||
|
Parent = popup,
|
||||||
|
AnchorPreset = AnchorPresets.TopLeft,
|
||||||
|
Text = "Company",
|
||||||
|
HorizontalAlignment = TextAlignment.Near,
|
||||||
|
};
|
||||||
|
companyLabel.LocalX += 10;
|
||||||
|
companyLabel.LocalY += 60;
|
||||||
|
|
||||||
|
var companyTextBox = new TextBox
|
||||||
|
{
|
||||||
|
Parent = popup,
|
||||||
|
WatermarkText = "Company Name",
|
||||||
|
AnchorPreset = AnchorPresets.TopLeft,
|
||||||
|
IsMultiline = false,
|
||||||
|
};
|
||||||
|
companyTextBox.LocalY += 60;
|
||||||
|
companyTextBox.LocalX += 100;
|
||||||
|
|
||||||
|
var submitButton = new Button
|
||||||
|
{
|
||||||
|
Parent = popup,
|
||||||
|
AnchorPreset = AnchorPresets.TopLeft,
|
||||||
|
Text = "Create",
|
||||||
|
Width = 70,
|
||||||
|
};
|
||||||
|
submitButton.LocalX += 40;
|
||||||
|
submitButton.LocalY += 90;
|
||||||
|
|
||||||
|
submitButton.Clicked += () =>
|
||||||
|
{
|
||||||
|
if (Directory.Exists(Path.Combine(Globals.ProjectFolder, "Plugins", nameTextBox.Text)))
|
||||||
|
{
|
||||||
|
Editor.LogWarning("Cannot create plugin due to name conflict.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
OnCreateButtonClicked(nameTextBox.Text, versionTextBox.Text, companyTextBox.Text);
|
||||||
|
nameTextBox.Clear();
|
||||||
|
versionTextBox.Clear();
|
||||||
|
companyTextBox.Clear();
|
||||||
|
popup.Hide();
|
||||||
|
};
|
||||||
|
|
||||||
|
var cancelButton = new Button
|
||||||
|
{
|
||||||
|
Parent = popup,
|
||||||
|
AnchorPreset = AnchorPresets.TopLeft,
|
||||||
|
Text = "Cancel",
|
||||||
|
Width = 70,
|
||||||
|
};
|
||||||
|
cancelButton.LocalX += 120;
|
||||||
|
cancelButton.LocalY += 90;
|
||||||
|
|
||||||
|
cancelButton.Clicked += () =>
|
||||||
|
{
|
||||||
|
nameTextBox.Clear();
|
||||||
|
versionTextBox.Clear();
|
||||||
|
companyTextBox.Clear();
|
||||||
|
popup.Hide();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void OnCreateButtonClicked(string pluginName, string pluginVersion, string companyName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(pluginName))
|
||||||
|
{
|
||||||
|
Editor.LogError("Failed to create plugin project due to no plugin name.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var templateUrl = "https://github.com/FlaxEngine/ExamplePlugin/archive/refs/heads/master.zip";
|
||||||
|
var localTemplateFolderLocation = Path.Combine(Editor.LocalCachePath, "TemplatePluginCache");
|
||||||
|
if (!Directory.Exists(localTemplateFolderLocation))
|
||||||
|
Directory.CreateDirectory(localTemplateFolderLocation);
|
||||||
|
var localTemplatePath = Path.Combine(localTemplateFolderLocation, "TemplatePlugin.zip");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Download example plugin
|
||||||
|
using (var client = new HttpClient())
|
||||||
|
{
|
||||||
|
byte[] zipBytes = await client.GetByteArrayAsync(templateUrl);
|
||||||
|
await File.WriteAllBytesAsync(!File.Exists(localTemplatePath) ? Path.Combine(localTemplatePath) : Path.Combine(Editor.LocalCachePath, "TemplatePluginCache", "TemplatePlugin1.zip"), zipBytes);
|
||||||
|
|
||||||
|
Editor.Log("Template for plugin project has downloaded");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Editor.LogError($"Failed to download template project. Trying to use local file. {e}");
|
||||||
|
if (!File.Exists(localTemplatePath))
|
||||||
|
{
|
||||||
|
Editor.LogError("Failed to use local file. Does not exist.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if any changes in new downloaded file
|
||||||
|
if (File.Exists(Path.Combine(Editor.LocalCachePath, "TemplatePluginCache", "TemplatePlugin1.zip")))
|
||||||
|
{
|
||||||
|
var localTemplatePath2 = Path.Combine(Editor.LocalCachePath, "TemplatePluginCache", "TemplatePlugin1.zip");
|
||||||
|
bool areDifferent = false;
|
||||||
|
using (var zip1 = ZipFile.OpenRead(localTemplatePath))
|
||||||
|
{
|
||||||
|
using (var zip2 = ZipFile.OpenRead(localTemplatePath2))
|
||||||
|
{
|
||||||
|
if (zip1.Entries.Count != zip2.Entries.Count)
|
||||||
|
{
|
||||||
|
areDifferent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (ZipArchiveEntry entry1 in zip1.Entries)
|
||||||
|
{
|
||||||
|
ZipArchiveEntry entry2 = zip2.GetEntry(entry1.FullName);
|
||||||
|
if (entry2 == null)
|
||||||
|
{
|
||||||
|
areDifferent = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (entry1.Length != entry2.Length || entry1.CompressedLength != entry2.CompressedLength || entry1.Crc32 != entry2.Crc32)
|
||||||
|
{
|
||||||
|
areDifferent = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (areDifferent)
|
||||||
|
{
|
||||||
|
File.Delete(localTemplatePath);
|
||||||
|
File.Move(localTemplatePath2, localTemplatePath);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
File.Delete(localTemplatePath2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var extractPath = Path.Combine(Globals.ProjectFolder, "Plugins");
|
||||||
|
if (!Directory.Exists(extractPath))
|
||||||
|
Directory.CreateDirectory(extractPath);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Task.Run(() => ZipFile.ExtractToDirectory(localTemplatePath, extractPath));
|
||||||
|
Editor.Log("Template for plugin project successfully moved to project.");
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
Editor.LogError($"Failed to add plugin to project. {e}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format plugin name into a valid name for code (C#/C++ typename)
|
||||||
|
var pluginCodeName = Content.ScriptItem.FilterScriptName(pluginName);
|
||||||
|
if (string.IsNullOrEmpty(pluginCodeName))
|
||||||
|
pluginCodeName = "MyPlugin";
|
||||||
|
Editor.Log($"Using plugin code type name: {pluginCodeName}");
|
||||||
|
|
||||||
|
var oldPluginPath = Path.Combine(extractPath, "ExamplePlugin-master");
|
||||||
|
var newPluginPath = Path.Combine(extractPath, pluginName);
|
||||||
|
Directory.Move(oldPluginPath, newPluginPath);
|
||||||
|
|
||||||
|
var oldFlaxProjFile = Path.Combine(newPluginPath, "ExamplePlugin.flaxproj");
|
||||||
|
var newFlaxProjFile = Path.Combine(newPluginPath, $"{pluginName}.flaxproj");
|
||||||
|
File.Move(oldFlaxProjFile, newFlaxProjFile);
|
||||||
|
|
||||||
|
var readme = Path.Combine(newPluginPath, "README.md");
|
||||||
|
if (File.Exists(readme))
|
||||||
|
File.Delete(readme);
|
||||||
|
var license = Path.Combine(newPluginPath, "LICENSE");
|
||||||
|
if (File.Exists(license))
|
||||||
|
File.Delete(license);
|
||||||
|
|
||||||
|
// Flax plugin project file
|
||||||
|
var flaxPluginProjContents = JsonSerializer.Deserialize<ProjectInfo>(await File.ReadAllTextAsync(newFlaxProjFile));
|
||||||
|
flaxPluginProjContents.Name = pluginName;
|
||||||
|
if (!string.IsNullOrEmpty(pluginVersion))
|
||||||
|
flaxPluginProjContents.Version = new Version(pluginVersion);
|
||||||
|
if (!string.IsNullOrEmpty(companyName))
|
||||||
|
flaxPluginProjContents.Company = companyName;
|
||||||
|
flaxPluginProjContents.GameTarget = $"{pluginCodeName}Target";
|
||||||
|
flaxPluginProjContents.EditorTarget = $"{pluginCodeName}EditorTarget";
|
||||||
|
await File.WriteAllTextAsync(newFlaxProjFile, JsonSerializer.Serialize(flaxPluginProjContents, typeof(ProjectInfo)), Encoding.UTF8);
|
||||||
|
|
||||||
|
// Format game settings
|
||||||
|
var gameSettingsPath = Path.Combine(newPluginPath, "Content", "GameSettings.json");
|
||||||
|
if (File.Exists(gameSettingsPath))
|
||||||
|
{
|
||||||
|
var contents = await File.ReadAllTextAsync(gameSettingsPath);
|
||||||
|
contents = contents.Replace("Example Plugin", pluginName);
|
||||||
|
contents = contents.Replace("\"CompanyName\": \"Flax\"", $"\"CompanyName\": \"{companyName}\"");
|
||||||
|
contents = contents.Replace("1.0", pluginVersion);
|
||||||
|
await File.WriteAllTextAsync(gameSettingsPath, contents, Encoding.UTF8);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rename source directories
|
||||||
|
var sourcePath = Path.Combine(newPluginPath, "Source");
|
||||||
|
var sourceDirectories = Directory.GetDirectories(sourcePath);
|
||||||
|
foreach (var directory in sourceDirectories)
|
||||||
|
{
|
||||||
|
var files = Directory.GetFiles(directory);
|
||||||
|
foreach (var file in files)
|
||||||
|
{
|
||||||
|
if (file.Contains("MyPluginEditor.cs"))
|
||||||
|
{
|
||||||
|
File.Delete(file);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileName = Path.GetFileName(file).Replace("ExamplePlugin", pluginCodeName);
|
||||||
|
var fileText = await File.ReadAllTextAsync(file);
|
||||||
|
fileText = fileText.Replace("ExamplePlugin", pluginCodeName);
|
||||||
|
if (file.Contains("MyPlugin.cs"))
|
||||||
|
{
|
||||||
|
fileName = "ExamplePlugin.cs";
|
||||||
|
fileText = fileText.Replace("MyPlugin", pluginCodeName);
|
||||||
|
fileText = fileText.Replace("My Plugin", pluginName);
|
||||||
|
fileText = fileText.Replace("Flax Engine", companyName);
|
||||||
|
fileText = fileText.Replace("new Version(1, 0)", $"new Version({pluginVersion.Trim().Replace(".", ", ")})");
|
||||||
|
}
|
||||||
|
await File.WriteAllTextAsync(file, fileText);
|
||||||
|
File.Move(file, Path.Combine(directory, fileName));
|
||||||
|
}
|
||||||
|
|
||||||
|
var newName = directory.Replace("ExamplePlugin", pluginCodeName);
|
||||||
|
Directory.Move(directory, newName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rename targets
|
||||||
|
var targetFiles = Directory.GetFiles(sourcePath);
|
||||||
|
foreach (var file in targetFiles)
|
||||||
|
{
|
||||||
|
var fileText = await File.ReadAllTextAsync(file);
|
||||||
|
await File.WriteAllTextAsync(file, fileText.Replace("ExamplePlugin", pluginCodeName), Encoding.UTF8);
|
||||||
|
var newName = file.Replace("ExamplePlugin", pluginCodeName);
|
||||||
|
File.Move(file, newName);
|
||||||
|
}
|
||||||
|
Editor.Log($"Plugin project {pluginName} has successfully been created.");
|
||||||
|
|
||||||
|
await AddReferenceToProject(pluginName, pluginName);
|
||||||
|
MessageBox.Show($"{pluginName} has been successfully created. Restart editor for changes to take effect.", "Plugin Project Created", MessageBoxButtons.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task AddReferenceToProject(string pluginFolderName, string pluginName)
|
||||||
|
{
|
||||||
|
// Project flax config file
|
||||||
|
var flaxProjPath = Editor.GameProject.ProjectPath;
|
||||||
|
if (File.Exists(flaxProjPath))
|
||||||
|
{
|
||||||
|
var flaxProjContents = JsonSerializer.Deserialize<ProjectInfo>(await File.ReadAllTextAsync(flaxProjPath));
|
||||||
|
var oldReferences = flaxProjContents.References;
|
||||||
|
var references = new List<ProjectInfo.Reference>(oldReferences);
|
||||||
|
var newPath = $"$(ProjectPath)/Plugins/{pluginFolderName}/{pluginName}.flaxproj";
|
||||||
|
if (!references.Exists(x => string.Equals(x.Name, newPath)))
|
||||||
|
{
|
||||||
|
var newReference = new ProjectInfo.Reference
|
||||||
|
{
|
||||||
|
Name = newPath,
|
||||||
|
};
|
||||||
|
references.Add(newReference);
|
||||||
|
}
|
||||||
|
flaxProjContents.References = references.ToArray();
|
||||||
|
await File.WriteAllTextAsync(flaxProjPath, JsonSerializer.Serialize(flaxProjContents, typeof(ProjectInfo)), Encoding.UTF8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void OnPluginsChanged()
|
private void OnPluginsChanged()
|
||||||
{
|
{
|
||||||
List<PluginEntry> toRemove = null;
|
List<PluginEntry> toRemove = null;
|
||||||
@@ -299,19 +865,21 @@ namespace FlaxEditor.Windows
|
|||||||
{
|
{
|
||||||
if (pluginType == null)
|
if (pluginType == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
foreach (var e in _entries.Keys)
|
foreach (var e in _entries.Keys)
|
||||||
{
|
{
|
||||||
if (e.GetType() == pluginType && _entries.ContainsKey(e))
|
if (e.GetType() == pluginType && _entries.ContainsKey(e))
|
||||||
return _entries[e];
|
return _entries[e];
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnDestroy()
|
public override void OnDestroy()
|
||||||
{
|
{
|
||||||
|
if (_addPluginProjectButton != null)
|
||||||
|
_addPluginProjectButton.Clicked -= OnAddButtonClicked;
|
||||||
|
if (_cloneProjectButton != null)
|
||||||
|
_cloneProjectButton.Clicked -= OnCloneProjectButtonClicked;
|
||||||
PluginManager.PluginsChanged -= OnPluginsChanged;
|
PluginManager.PluginsChanged -= OnPluginsChanged;
|
||||||
|
|
||||||
base.OnDestroy();
|
base.OnDestroy();
|
||||||
|
|||||||
@@ -1251,7 +1251,7 @@ void VisualScriptExecutor::ProcessGroupFlow(Box* boxBase, Node* node, Value& val
|
|||||||
boxBase = node->GetBox(3);
|
boxBase = node->GetBox(3);
|
||||||
if (boxBase->HasConnection())
|
if (boxBase->HasConnection())
|
||||||
eatBox(node, boxBase->FirstConnection());
|
eatBox(node, boxBase->FirstConnection());
|
||||||
Dictionary<Variant, Variant>::Iterator it(dictionary, iteratorValue.Value.AsInt);
|
Dictionary<Variant, Variant>::Iterator it(&dictionary, iteratorValue.Value.AsInt);
|
||||||
++it;
|
++it;
|
||||||
iteratorValue.Value.AsInt = it.Index();
|
iteratorValue.Value.AsInt = it.Index();
|
||||||
}
|
}
|
||||||
@@ -1269,12 +1269,12 @@ void VisualScriptExecutor::ProcessGroupFlow(Box* boxBase, Node* node, Value& val
|
|||||||
// Key
|
// Key
|
||||||
case 1:
|
case 1:
|
||||||
if (iteratorIndex != scope->ReturnedValues.Count() && dictionaryIndex != scope->ReturnedValues.Count())
|
if (iteratorIndex != scope->ReturnedValues.Count() && dictionaryIndex != scope->ReturnedValues.Count())
|
||||||
value = Dictionary<Variant, Variant>::Iterator(*scope->ReturnedValues[dictionaryIndex].Value.AsDictionary, scope->ReturnedValues[iteratorIndex].Value.AsInt)->Key;
|
value = Dictionary<Variant, Variant>::Iterator(scope->ReturnedValues[dictionaryIndex].Value.AsDictionary, scope->ReturnedValues[iteratorIndex].Value.AsInt)->Key;
|
||||||
break;
|
break;
|
||||||
// Value
|
// Value
|
||||||
case 2:
|
case 2:
|
||||||
if (iteratorIndex != scope->ReturnedValues.Count() && dictionaryIndex != scope->ReturnedValues.Count())
|
if (iteratorIndex != scope->ReturnedValues.Count() && dictionaryIndex != scope->ReturnedValues.Count())
|
||||||
value = Dictionary<Variant, Variant>::Iterator(*scope->ReturnedValues[dictionaryIndex].Value.AsDictionary, scope->ReturnedValues[iteratorIndex].Value.AsInt)->Value;
|
value = Dictionary<Variant, Variant>::Iterator(scope->ReturnedValues[dictionaryIndex].Value.AsDictionary, scope->ReturnedValues[iteratorIndex].Value.AsInt)->Value;
|
||||||
break;
|
break;
|
||||||
// Break
|
// Break
|
||||||
case 5:
|
case 5:
|
||||||
|
|||||||
@@ -938,12 +938,12 @@ public:
|
|||||||
|
|
||||||
FORCE_INLINE bool IsEnd() const
|
FORCE_INLINE bool IsEnd() const
|
||||||
{
|
{
|
||||||
return _index == _array->Count();
|
return _index == _array->_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
FORCE_INLINE bool IsNotEnd() const
|
FORCE_INLINE bool IsNotEnd() const
|
||||||
{
|
{
|
||||||
return _index != _array->Count();
|
return _index != _array->_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
FORCE_INLINE T& operator*() const
|
FORCE_INLINE T& operator*() const
|
||||||
@@ -975,7 +975,7 @@ public:
|
|||||||
|
|
||||||
Iterator& operator++()
|
Iterator& operator++()
|
||||||
{
|
{
|
||||||
if (_index != _array->Count())
|
if (_index != _array->_count)
|
||||||
_index++;
|
_index++;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@@ -983,7 +983,7 @@ public:
|
|||||||
Iterator operator++(int)
|
Iterator operator++(int)
|
||||||
{
|
{
|
||||||
Iterator temp = *this;
|
Iterator temp = *this;
|
||||||
if (_index != _array->Count())
|
if (_index != _array->_count)
|
||||||
_index++;
|
_index++;
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,7 +95,6 @@ public:
|
|||||||
struct Iterator
|
struct Iterator
|
||||||
{
|
{
|
||||||
friend ChunkedArray;
|
friend ChunkedArray;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ChunkedArray* _collection;
|
ChunkedArray* _collection;
|
||||||
int32 _chunkIndex;
|
int32 _chunkIndex;
|
||||||
|
|||||||
@@ -237,22 +237,28 @@ public:
|
|||||||
{
|
{
|
||||||
friend Dictionary;
|
friend Dictionary;
|
||||||
private:
|
private:
|
||||||
Dictionary& _collection;
|
Dictionary* _collection;
|
||||||
int32 _index;
|
int32 _index;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Iterator(Dictionary& collection, const int32 index)
|
Iterator(Dictionary* collection, const int32 index)
|
||||||
: _collection(collection)
|
: _collection(collection)
|
||||||
, _index(index)
|
, _index(index)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterator(Dictionary const& collection, const int32 index)
|
Iterator(Dictionary const* collection, const int32 index)
|
||||||
: _collection((Dictionary&)collection)
|
: _collection(const_cast<Dictionary*>(collection))
|
||||||
, _index(index)
|
, _index(index)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Iterator()
|
||||||
|
: _collection(nullptr)
|
||||||
|
, _index(-1)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
Iterator(const Iterator& i)
|
Iterator(const Iterator& i)
|
||||||
: _collection(i._collection)
|
: _collection(i._collection)
|
||||||
, _index(i._index)
|
, _index(i._index)
|
||||||
@@ -273,27 +279,27 @@ public:
|
|||||||
|
|
||||||
FORCE_INLINE bool IsEnd() const
|
FORCE_INLINE bool IsEnd() const
|
||||||
{
|
{
|
||||||
return _index == _collection._size;
|
return _index == _collection->_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
FORCE_INLINE bool IsNotEnd() const
|
FORCE_INLINE bool IsNotEnd() const
|
||||||
{
|
{
|
||||||
return _index != _collection._size;
|
return _index != _collection->_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
FORCE_INLINE Bucket& operator*() const
|
FORCE_INLINE Bucket& operator*() const
|
||||||
{
|
{
|
||||||
return _collection._allocation.Get()[_index];
|
return _collection->_allocation.Get()[_index];
|
||||||
}
|
}
|
||||||
|
|
||||||
FORCE_INLINE Bucket* operator->() const
|
FORCE_INLINE Bucket* operator->() const
|
||||||
{
|
{
|
||||||
return &_collection._allocation.Get()[_index];
|
return &_collection->_allocation.Get()[_index];
|
||||||
}
|
}
|
||||||
|
|
||||||
FORCE_INLINE explicit operator bool() const
|
FORCE_INLINE explicit operator bool() const
|
||||||
{
|
{
|
||||||
return _index >= 0 && _index < _collection._size;
|
return _index >= 0 && _index < _collection->_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
FORCE_INLINE bool operator!() const
|
FORCE_INLINE bool operator!() const
|
||||||
@@ -308,7 +314,7 @@ public:
|
|||||||
|
|
||||||
FORCE_INLINE bool operator!=(const Iterator& v) const
|
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)
|
Iterator& operator=(const Iterator& v)
|
||||||
@@ -320,10 +326,10 @@ public:
|
|||||||
|
|
||||||
Iterator& operator++()
|
Iterator& operator++()
|
||||||
{
|
{
|
||||||
const int32 capacity = _collection.Capacity();
|
const int32 capacity = _collection->_size;
|
||||||
if (_index != capacity)
|
if (_index != capacity)
|
||||||
{
|
{
|
||||||
const Bucket* data = _collection._allocation.Get();
|
const Bucket* data = _collection->_allocation.Get();
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
_index++;
|
_index++;
|
||||||
@@ -343,7 +349,7 @@ public:
|
|||||||
{
|
{
|
||||||
if (_index > 0)
|
if (_index > 0)
|
||||||
{
|
{
|
||||||
const Bucket* data = _collection._allocation.Get();
|
const Bucket* data = _collection->_allocation.Get();
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
_index--;
|
_index--;
|
||||||
@@ -633,7 +639,7 @@ public:
|
|||||||
/// <param name="i">Iterator with key and value.</param>
|
/// <param name="i">Iterator with key and value.</param>
|
||||||
void Add(const Iterator& i)
|
void Add(const Iterator& i)
|
||||||
{
|
{
|
||||||
ASSERT(&i._collection != this && i);
|
ASSERT(i._collection != this && i);
|
||||||
const Bucket& bucket = *i;
|
const Bucket& bucket = *i;
|
||||||
Add(bucket.Key, bucket.Value);
|
Add(bucket.Key, bucket.Value);
|
||||||
}
|
}
|
||||||
@@ -667,7 +673,7 @@ public:
|
|||||||
/// <returns>True if cannot remove item from the collection because cannot find it, otherwise false.</returns>
|
/// <returns>True if cannot remove item from the collection because cannot find it, otherwise false.</returns>
|
||||||
bool Remove(const Iterator& i)
|
bool Remove(const Iterator& i)
|
||||||
{
|
{
|
||||||
ASSERT(&i._collection == this);
|
ASSERT(i._collection == this);
|
||||||
if (i)
|
if (i)
|
||||||
{
|
{
|
||||||
ASSERT(_allocation.Get()[i._index].IsOccupied());
|
ASSERT(_allocation.Get()[i._index].IsOccupied());
|
||||||
@@ -711,7 +717,7 @@ public:
|
|||||||
return End();
|
return End();
|
||||||
FindPositionResult pos;
|
FindPositionResult pos;
|
||||||
FindPosition(key, pos);
|
FindPosition(key, pos);
|
||||||
return pos.ObjectIndex != -1 ? Iterator(*this, pos.ObjectIndex) : End();
|
return pos.ObjectIndex != -1 ? Iterator(this, pos.ObjectIndex) : End();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -812,38 +818,38 @@ public:
|
|||||||
public:
|
public:
|
||||||
Iterator Begin() const
|
Iterator Begin() const
|
||||||
{
|
{
|
||||||
Iterator i(*this, -1);
|
Iterator i(this, -1);
|
||||||
++i;
|
++i;
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterator End() const
|
Iterator End() const
|
||||||
{
|
{
|
||||||
return Iterator(*this, _size);
|
return Iterator(this, _size);
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterator begin()
|
Iterator begin()
|
||||||
{
|
{
|
||||||
Iterator i(*this, -1);
|
Iterator i(this, -1);
|
||||||
++i;
|
++i;
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
FORCE_INLINE Iterator end()
|
FORCE_INLINE Iterator end()
|
||||||
{
|
{
|
||||||
return Iterator(*this, _size);
|
return Iterator(this, _size);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Iterator begin() const
|
const Iterator begin() const
|
||||||
{
|
{
|
||||||
Iterator i(*this, -1);
|
Iterator i(this, -1);
|
||||||
++i;
|
++i;
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
FORCE_INLINE const Iterator end() const
|
FORCE_INLINE const Iterator end() const
|
||||||
{
|
{
|
||||||
return Iterator(*this, _size);
|
return Iterator(this, _size);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@@ -213,17 +213,17 @@ public:
|
|||||||
{
|
{
|
||||||
friend HashSet;
|
friend HashSet;
|
||||||
private:
|
private:
|
||||||
HashSet& _collection;
|
HashSet* _collection;
|
||||||
int32 _index;
|
int32 _index;
|
||||||
|
|
||||||
Iterator(HashSet& collection, const int32 index)
|
Iterator(HashSet* collection, const int32 index)
|
||||||
: _collection(collection)
|
: _collection(collection)
|
||||||
, _index(index)
|
, _index(index)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterator(HashSet const& collection, const int32 index)
|
Iterator(HashSet const* collection, const int32 index)
|
||||||
: _collection((HashSet&)collection)
|
: _collection(const_cast<HashSet*>(collection))
|
||||||
, _index(index)
|
, _index(index)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -244,27 +244,27 @@ public:
|
|||||||
public:
|
public:
|
||||||
FORCE_INLINE bool IsEnd() const
|
FORCE_INLINE bool IsEnd() const
|
||||||
{
|
{
|
||||||
return _index == _collection.Capacity();
|
return _index == _collection->_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
FORCE_INLINE bool IsNotEnd() const
|
FORCE_INLINE bool IsNotEnd() const
|
||||||
{
|
{
|
||||||
return _index != _collection.Capacity();
|
return _index != _collection->_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
FORCE_INLINE Bucket& operator*() const
|
FORCE_INLINE Bucket& operator*() const
|
||||||
{
|
{
|
||||||
return _collection._allocation.Get()[_index];
|
return _collection->_allocation.Get()[_index];
|
||||||
}
|
}
|
||||||
|
|
||||||
FORCE_INLINE Bucket* operator->() const
|
FORCE_INLINE Bucket* operator->() const
|
||||||
{
|
{
|
||||||
return &_collection._allocation.Get()[_index];
|
return &_collection->_allocation.Get()[_index];
|
||||||
}
|
}
|
||||||
|
|
||||||
FORCE_INLINE explicit operator bool() const
|
FORCE_INLINE explicit operator bool() const
|
||||||
{
|
{
|
||||||
return _index >= 0 && _index < _collection._size;
|
return _index >= 0 && _index < _collection->_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
FORCE_INLINE bool operator !() const
|
FORCE_INLINE bool operator !() const
|
||||||
@@ -274,12 +274,12 @@ public:
|
|||||||
|
|
||||||
FORCE_INLINE bool operator==(const Iterator& v) const
|
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
|
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)
|
Iterator& operator=(const Iterator& v)
|
||||||
@@ -291,10 +291,10 @@ public:
|
|||||||
|
|
||||||
Iterator& operator++()
|
Iterator& operator++()
|
||||||
{
|
{
|
||||||
const int32 capacity = _collection.Capacity();
|
const int32 capacity = _collection->_size;
|
||||||
if (_index != capacity)
|
if (_index != capacity)
|
||||||
{
|
{
|
||||||
const Bucket* data = _collection._allocation.Get();
|
const Bucket* data = _collection->_allocation.Get();
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
_index++;
|
_index++;
|
||||||
@@ -314,7 +314,7 @@ public:
|
|||||||
{
|
{
|
||||||
if (_index > 0)
|
if (_index > 0)
|
||||||
{
|
{
|
||||||
const Bucket* data = _collection._allocation.Get();
|
const Bucket* data = _collection->_allocation.Get();
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
_index--;
|
_index--;
|
||||||
@@ -464,7 +464,7 @@ public:
|
|||||||
/// <param name="i">Iterator with item to add</param>
|
/// <param name="i">Iterator with item to add</param>
|
||||||
void Add(const Iterator& i)
|
void Add(const Iterator& i)
|
||||||
{
|
{
|
||||||
ASSERT(&i._collection != this && i);
|
ASSERT(i._collection != this && i);
|
||||||
const Bucket& bucket = *i;
|
const Bucket& bucket = *i;
|
||||||
Add(bucket.Item);
|
Add(bucket.Item);
|
||||||
}
|
}
|
||||||
@@ -498,7 +498,7 @@ public:
|
|||||||
/// <returns>True if cannot remove item from the collection because cannot find it, otherwise false.</returns>
|
/// <returns>True if cannot remove item from the collection because cannot find it, otherwise false.</returns>
|
||||||
bool Remove(const Iterator& i)
|
bool Remove(const Iterator& i)
|
||||||
{
|
{
|
||||||
ASSERT(&i._collection == this);
|
ASSERT(i._collection == this);
|
||||||
if (i)
|
if (i)
|
||||||
{
|
{
|
||||||
ASSERT(_allocation.Get()[i._index].IsOccupied());
|
ASSERT(_allocation.Get()[i._index].IsOccupied());
|
||||||
@@ -523,7 +523,7 @@ public:
|
|||||||
return End();
|
return End();
|
||||||
FindPositionResult pos;
|
FindPositionResult pos;
|
||||||
FindPosition(item, pos);
|
FindPosition(item, pos);
|
||||||
return pos.ObjectIndex != -1 ? Iterator(*this, pos.ObjectIndex) : End();
|
return pos.ObjectIndex != -1 ? Iterator(this, pos.ObjectIndex) : End();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -559,38 +559,38 @@ public:
|
|||||||
public:
|
public:
|
||||||
Iterator Begin() const
|
Iterator Begin() const
|
||||||
{
|
{
|
||||||
Iterator i(*this, -1);
|
Iterator i(this, -1);
|
||||||
++i;
|
++i;
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterator End() const
|
Iterator End() const
|
||||||
{
|
{
|
||||||
return Iterator(*this, _size);
|
return Iterator(this, _size);
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterator begin()
|
Iterator begin()
|
||||||
{
|
{
|
||||||
Iterator i(*this, -1);
|
Iterator i(this, -1);
|
||||||
++i;
|
++i;
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
FORCE_INLINE Iterator end()
|
FORCE_INLINE Iterator end()
|
||||||
{
|
{
|
||||||
return Iterator(*this, _size);
|
return Iterator(this, _size);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Iterator begin() const
|
const Iterator begin() const
|
||||||
{
|
{
|
||||||
Iterator i(*this, -1);
|
Iterator i(this, -1);
|
||||||
++i;
|
++i;
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
FORCE_INLINE const Iterator end() const
|
FORCE_INLINE const Iterator end() const
|
||||||
{
|
{
|
||||||
return Iterator(*this, _size);
|
return Iterator(this, _size);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@@ -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)
|
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;
|
const Float3 startF = start - Context->Origin, endF = end - Context->Origin;
|
||||||
|
|||||||
@@ -31,6 +31,18 @@ namespace FlaxEngine
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Draws the line in a direction.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="origin">The origin of the line.</param>
|
||||||
|
/// <param name="direction">The direction of the line.</param>
|
||||||
|
/// <param name="color">The color.</param>
|
||||||
|
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
|
||||||
|
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
|
||||||
|
public static void DrawRay(Vector3 origin, Vector3 direction, Color color, float duration = 0.0f, bool depthTest = true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Draws the line.
|
/// Draws the line.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -69,6 +69,16 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
|
|||||||
/// <param name="drawScenes">True if draw all debug shapes from scenes too or false if draw just from specified actor list.</param>
|
/// <param name="drawScenes">True if draw all debug shapes from scenes too or false if draw just from specified actor list.</param>
|
||||||
API_FUNCTION() static void DrawActors(Actor** selectedActors, int32 selectedActorsCount, bool drawScenes);
|
API_FUNCTION() static void DrawActors(Actor** selectedActors, int32 selectedActorsCount, bool drawScenes);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Draws the line in a direction.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="origin">The origin of the line.</param>
|
||||||
|
/// <param name="direction">The direction of the line.</param>
|
||||||
|
/// <param name="color">The color.</param>
|
||||||
|
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
|
||||||
|
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
|
||||||
|
API_FUNCTION() static void DrawRay(const Vector3& origin, const Vector3& direction, const Color& color, float duration = 0.0f, bool depthTest = true);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Draws the line.
|
/// Draws the line.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -615,6 +625,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);
|
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_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_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_BEZIER(p1, p2, p3, p4, color, duration, depthTest) DebugDraw::DrawBezier(p1, p2, p3, p4, color, duration, depthTest)
|
||||||
|
|||||||
@@ -581,9 +581,9 @@ namespace FlaxEngine.Interop
|
|||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly]
|
[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]
|
[UnmanagedCallersOnly]
|
||||||
|
|||||||
@@ -476,6 +476,11 @@ void Actor::AddTag(const Tag& tag)
|
|||||||
Tags.AddUnique(tag);
|
Tags.AddUnique(tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Actor::RemoveTag(const Tag& tag)
|
||||||
|
{
|
||||||
|
Tags.Remove(tag);
|
||||||
|
}
|
||||||
|
|
||||||
PRAGMA_DISABLE_DEPRECATION_WARNINGS
|
PRAGMA_DISABLE_DEPRECATION_WARNINGS
|
||||||
|
|
||||||
const String& Actor::GetTag() const
|
const String& Actor::GetTag() const
|
||||||
|
|||||||
@@ -136,6 +136,12 @@ public:
|
|||||||
/// <param name="tag">The tag to add.</param>
|
/// <param name="tag">The tag to add.</param>
|
||||||
API_FUNCTION() void AddTag(const Tag& tag);
|
API_FUNCTION() void AddTag(const Tag& tag);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a tag to the actor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tag">The tag to remove.</param>
|
||||||
|
API_FUNCTION() void RemoveTag(const Tag& tag);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the name of the tag.
|
/// Gets the name of the tag.
|
||||||
/// [Deprecated in v1.5]
|
/// [Deprecated in v1.5]
|
||||||
|
|||||||
@@ -1255,20 +1255,19 @@ void Prefab::SyncNestedPrefabs(const NestedPrefabsList& allPrefabs, Array<Prefab
|
|||||||
auto nestedPrefab = allPrefabs[i].Get();
|
auto nestedPrefab = allPrefabs[i].Get();
|
||||||
if (nestedPrefab)
|
if (nestedPrefab)
|
||||||
{
|
{
|
||||||
if (WaitForLoaded())
|
if (nestedPrefab->WaitForLoaded())
|
||||||
{
|
{
|
||||||
LOG(Warning, "Waiting for prefab asset load failed.");
|
LOG(Warning, "Waiting for prefab asset load failed.");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sync only if prefab is used by this prefab (directly) and it has been captured before
|
||||||
const int32 nestedPrefabIndex = nestedPrefab->NestedPrefabs.Find(GetID());
|
const int32 nestedPrefabIndex = nestedPrefab->NestedPrefabs.Find(GetID());
|
||||||
if (nestedPrefabIndex != -1)
|
if (nestedPrefabIndex != -1)
|
||||||
{
|
{
|
||||||
if (nestedPrefab->SyncChangesInternal(allPrefabsInstancesData[nestedPrefabIndex]))
|
if (nestedPrefab->SyncChangesInternal(allPrefabsInstancesData[i]))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
nestedPrefab->SyncNestedPrefabs(allPrefabs, allPrefabsInstancesData);
|
nestedPrefab->SyncNestedPrefabs(allPrefabs, allPrefabsInstancesData);
|
||||||
|
|
||||||
ObjectsRemovalService::Flush();
|
ObjectsRemovalService::Flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -331,6 +331,12 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupParticles(Box* box, Node* node
|
|||||||
value = GET_PARTICLE_ATTRIBUTE(0, float);
|
value = GET_PARTICLE_ATTRIBUTE(0, float);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// Particle Scale
|
||||||
|
case 112:
|
||||||
|
{
|
||||||
|
value = GET_PARTICLE_ATTRIBUTE(0, Float3);
|
||||||
|
break;
|
||||||
|
}
|
||||||
// Effect Position
|
// Effect Position
|
||||||
case 200:
|
case 200:
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -331,6 +331,10 @@ void ParticleEmitterGPUGenerator::ProcessGroupParticles(Box* box, Node* node, Va
|
|||||||
case 111:
|
case 111:
|
||||||
value = AccessParticleAttribute(node, TEXT("Radius"), ParticleAttribute::ValueTypes::Float, AccessMode::Read);
|
value = AccessParticleAttribute(node, TEXT("Radius"), ParticleAttribute::ValueTypes::Float, AccessMode::Read);
|
||||||
break;
|
break;
|
||||||
|
// Particle Scale
|
||||||
|
case 112:
|
||||||
|
value = AccessParticleAttribute(node, TEXT("Scale"), ParticleAttribute::ValueTypes::Float3, AccessMode::Read);
|
||||||
|
break;
|
||||||
// Effect Position
|
// Effect Position
|
||||||
case 200:
|
case 200:
|
||||||
value = Value(VariantType::Float3, TEXT("EffectPosition"));
|
value = Value(VariantType::Float3, TEXT("EffectPosition"));
|
||||||
|
|||||||
@@ -269,13 +269,20 @@ public:
|
|||||||
USE_ATTRIBUTE(Lifetime, Float, 1);
|
USE_ATTRIBUTE(Lifetime, Float, 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Particle Mass
|
// Particle Radius
|
||||||
case GRAPH_NODE_MAKE_TYPE(14, 111):
|
case GRAPH_NODE_MAKE_TYPE(14, 111):
|
||||||
{
|
{
|
||||||
node->UsesParticleData = true;
|
node->UsesParticleData = true;
|
||||||
USE_ATTRIBUTE(Radius, Float, 0);
|
USE_ATTRIBUTE(Radius, Float, 0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// Particle Scale
|
||||||
|
case GRAPH_NODE_MAKE_TYPE(14, 112):
|
||||||
|
{
|
||||||
|
node->UsesParticleData = true;
|
||||||
|
USE_ATTRIBUTE(Scale, Float3, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
// Random
|
// Random
|
||||||
case GRAPH_NODE_MAKE_TYPE(14, 208):
|
case GRAPH_NODE_MAKE_TYPE(14, 208):
|
||||||
case GRAPH_NODE_MAKE_TYPE(14, 209):
|
case GRAPH_NODE_MAKE_TYPE(14, 209):
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
#include "Engine/Serialization/Serialization.h"
|
#include "Engine/Serialization/Serialization.h"
|
||||||
#include "Engine/Engine/Time.h"
|
#include "Engine/Engine/Time.h"
|
||||||
|
|
||||||
|
#define CC_MIN_SIZE 0.001f
|
||||||
|
|
||||||
CharacterController::CharacterController(const SpawnParams& params)
|
CharacterController::CharacterController(const SpawnParams& params)
|
||||||
: Collider(params)
|
: Collider(params)
|
||||||
, _controller(nullptr)
|
, _controller(nullptr)
|
||||||
@@ -100,10 +102,9 @@ void CharacterController::SetStepOffset(float value)
|
|||||||
{
|
{
|
||||||
const float scaling = _cachedScale.GetAbsolute().MaxValue();
|
const float scaling = _cachedScale.GetAbsolute().MaxValue();
|
||||||
const float contactOffset = Math::Max(_contactOffset, ZeroTolerance);
|
const float contactOffset = Math::Max(_contactOffset, ZeroTolerance);
|
||||||
const float minSize = 0.001f;
|
const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE);
|
||||||
const float height = Math::Max(Math::Abs(_height) * scaling, minSize);
|
const float radius = Math::Max(Math::Abs(_radius) * scaling - contactOffset, CC_MIN_SIZE);
|
||||||
const float radius = Math::Max(Math::Abs(_radius) * scaling - contactOffset, minSize);
|
PhysicsBackend::SetControllerStepOffset(_controller, Math::Min(value, height + radius * 2.0f - CC_MIN_SIZE));
|
||||||
PhysicsBackend::SetControllerStepOffset(_controller, Math::Min(value, height + radius * 2.0f - minSize));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,9 +176,8 @@ CharacterController::CollisionFlags CharacterController::Move(const Vector3& dis
|
|||||||
void CharacterController::DrawPhysicsDebug(RenderView& view)
|
void CharacterController::DrawPhysicsDebug(RenderView& view)
|
||||||
{
|
{
|
||||||
const float scaling = _cachedScale.GetAbsolute().MaxValue();
|
const float scaling = _cachedScale.GetAbsolute().MaxValue();
|
||||||
const float minSize = 0.001f;
|
const float radius = Math::Max(Math::Abs(_radius) * scaling, CC_MIN_SIZE);
|
||||||
const float radius = Math::Max(Math::Abs(_radius) * scaling, minSize);
|
const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE);
|
||||||
const float height = Math::Max(Math::Abs(_height) * scaling, minSize);
|
|
||||||
const Vector3 position = _transform.LocalToWorld(_center);
|
const Vector3 position = _transform.LocalToWorld(_center);
|
||||||
if (view.Mode == ViewMode::PhysicsColliders)
|
if (view.Mode == ViewMode::PhysicsColliders)
|
||||||
DEBUG_DRAW_TUBE(position, Quaternion::Euler(90, 0, 0), radius, height, Color::LightYellow, 0, true);
|
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()
|
void CharacterController::OnDebugDrawSelected()
|
||||||
{
|
{
|
||||||
const float scaling = _cachedScale.GetAbsolute().MaxValue();
|
const float scaling = _cachedScale.GetAbsolute().MaxValue();
|
||||||
const float minSize = 0.001f;
|
const float radius = Math::Max(Math::Abs(_radius) * scaling, CC_MIN_SIZE);
|
||||||
const float radius = Math::Max(Math::Abs(_radius) * scaling, minSize);
|
const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE);
|
||||||
const float height = Math::Max(Math::Abs(_height) * scaling, minSize);
|
|
||||||
const Vector3 position = _transform.LocalToWorld(_center);
|
const Vector3 position = _transform.LocalToWorld(_center);
|
||||||
DEBUG_DRAW_WIRE_TUBE(position, Quaternion::Euler(90, 0, 0), radius, height, Color::GreenYellow, 0, false);
|
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)
|
if (_controller)
|
||||||
{
|
{
|
||||||
const float scaling = _cachedScale.GetAbsolute().MaxValue();
|
const float scaling = _cachedScale.GetAbsolute().MaxValue();
|
||||||
const float minSize = 0.001f;
|
const float radius = Math::Max(Math::Abs(_radius) * scaling - Math::Max(_contactOffset, ZeroTolerance), CC_MIN_SIZE);
|
||||||
const float radius = Math::Max(Math::Abs(_radius) * scaling - Math::Max(_contactOffset, ZeroTolerance), minSize);
|
const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE);
|
||||||
const float height = Math::Max(Math::Abs(_height) * scaling, minSize);
|
|
||||||
PhysicsBackend::SetControllerSize(_controller, radius, height);
|
PhysicsBackend::SetControllerSize(_controller, radius, height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -245,11 +243,12 @@ void CharacterController::CreateShape()
|
|||||||
|
|
||||||
void CharacterController::UpdateBounds()
|
void CharacterController::UpdateBounds()
|
||||||
{
|
{
|
||||||
void* actor = _shape ? PhysicsBackend::GetShapeActor(_shape) : nullptr;
|
const float scaling = GetScale().GetAbsolute().MaxValue();
|
||||||
if (actor)
|
const float radius = Math::Max(Math::Abs(_radius) * scaling, CC_MIN_SIZE);
|
||||||
PhysicsBackend::GetActorBounds(actor, _box);
|
const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE);
|
||||||
else
|
const Vector3 position = _transform.LocalToWorld(_center);
|
||||||
_box = BoundingBox(_transform.Translation);
|
const Vector3 extent(radius, height * 0.5f + radius, radius);
|
||||||
|
_box = BoundingBox(position - extent, position + extent);
|
||||||
BoundingSphere::FromBox(_box, _sphere);
|
BoundingSphere::FromBox(_box, _sphere);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -405,6 +405,7 @@ void WindowBase::OnResize(int32 width, int32 height)
|
|||||||
_swapChain->Resize(width, height);
|
_swapChain->Resize(width, height);
|
||||||
if (RenderTask)
|
if (RenderTask)
|
||||||
RenderTask->Resize(width, height);
|
RenderTask->Resize(width, height);
|
||||||
|
Resized({ static_cast<float>(width), static_cast<float>(height) });
|
||||||
INVOKE_EVENT_PARAMS_2(OnResize, &width, &height);
|
INVOKE_EVENT_PARAMS_2(OnResize, &width, &height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -315,6 +315,11 @@ public:
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
Action Closed;
|
Action Closed;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event fired when window gets resized.
|
||||||
|
/// </summary>
|
||||||
|
Delegate<Float2> Resized;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event fired when window gets focused.
|
/// Event fired when window gets focused.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "MTypes.h"
|
#include "MTypes.h"
|
||||||
|
#include "MClass.h"
|
||||||
#include "MCore.h"
|
#include "MCore.h"
|
||||||
#include "Engine/Core/Types/StringView.h"
|
#include "Engine/Core/Types/StringView.h"
|
||||||
#include "Engine/Core/Types/DataContainer.h"
|
#include "Engine/Core/Types/DataContainer.h"
|
||||||
@@ -354,7 +355,7 @@ struct MConverter<Array<T>>
|
|||||||
{
|
{
|
||||||
if (!klass)
|
if (!klass)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
MArray* result = MCore::Array::New(klass, data.Count());
|
MArray* result = MCore::Array::New(klass->GetElementClass(), data.Count());
|
||||||
MConverter<T> converter;
|
MConverter<T> converter;
|
||||||
converter.ToManagedArray(result, Span<T>(data.Get(), data.Count()));
|
converter.ToManagedArray(result, Span<T>(data.Get(), data.Count()));
|
||||||
return (MObject*)result;
|
return (MObject*)result;
|
||||||
|
|||||||
@@ -365,8 +365,8 @@ MString* MCore::String::GetEmpty(MDomain* domain)
|
|||||||
|
|
||||||
MString* MCore::String::New(const char* str, int32 length, MDomain* domain)
|
MString* MCore::String::New(const char* str, int32 length, MDomain* domain)
|
||||||
{
|
{
|
||||||
static void* NewStringLengthPtr = GetStaticMethodPointer(TEXT("NewStringLength"));
|
static void* NewStringUTF8Ptr = GetStaticMethodPointer(TEXT("NewStringUTF8"));
|
||||||
return (MString*)CallStaticMethod<void*, const char*, int>(NewStringLengthPtr, str, length);
|
return (MString*)CallStaticMethod<void*, const char*, int>(NewStringUTF8Ptr, str, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
MString* MCore::String::New(const Char* str, int32 length, MDomain* domain)
|
MString* MCore::String::New(const Char* str, int32 length, MDomain* domain)
|
||||||
|
|||||||
@@ -205,6 +205,13 @@ namespace FlaxEngine
|
|||||||
|
|
||||||
internal static void AddDictionaryItem(IDictionary dictionary, object key, object value)
|
internal static void AddDictionaryItem(IDictionary dictionary, object key, object value)
|
||||||
{
|
{
|
||||||
|
// TODO: more generic approach to properly add value that is of custom boxed type? (eg. via NativeInterop.MarshalToManaged)
|
||||||
|
if (value is ManagedArray managedArray)
|
||||||
|
{
|
||||||
|
var managedArrayHandle = ManagedHandle.Alloc(managedArray, GCHandleType.Normal);
|
||||||
|
value = NativeInterop.MarshalToManaged((IntPtr)managedArrayHandle, managedArray.ArrayType);
|
||||||
|
managedArrayHandle.Free();
|
||||||
|
}
|
||||||
dictionary.Add(key, value);
|
dictionary.Add(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
365
Source/Engine/UI/GUI/Common/Slider.cs
Normal file
365
Source/Engine/UI/GUI/Common/Slider.cs
Normal file
@@ -0,0 +1,365 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace FlaxEngine.GUI;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The slider control.
|
||||||
|
/// </summary>
|
||||||
|
public class Slider : ContainerControl
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The minimum value.
|
||||||
|
/// </summary>
|
||||||
|
protected float _minimum;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum value.
|
||||||
|
/// </summary>
|
||||||
|
protected float _maximum = 100f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the minimum value.
|
||||||
|
/// </summary>
|
||||||
|
[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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the maximum value.
|
||||||
|
/// </summary>
|
||||||
|
[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;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the value (normalized to range 0-100).
|
||||||
|
/// </summary>
|
||||||
|
[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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The local position of the thumb center
|
||||||
|
/// </summary>
|
||||||
|
[HideInEditor]
|
||||||
|
public Float2 ThumbCenter => new(_thumbCenter, Height / 2);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The local position of the beginning of the track.
|
||||||
|
/// </summary>
|
||||||
|
[HideInEditor]
|
||||||
|
public Float2 TrackBeginning => new(_thumbSize.X / 2, Height / 2);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The local position of the end of the track.
|
||||||
|
/// </summary>
|
||||||
|
[HideInEditor]
|
||||||
|
public Float2 TrackEnd => new(Width - _thumbSize.X / 2, Height / 2);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The height of the track.
|
||||||
|
/// </summary>
|
||||||
|
[EditorOrder(40), Tooltip("The track height.")]
|
||||||
|
public int TrackHeight { get; set; } = 2;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The thumb size.
|
||||||
|
/// </summary>
|
||||||
|
[EditorOrder(41), Tooltip("The size of the thumb.")]
|
||||||
|
public Float2 ThumbSize {
|
||||||
|
get => _thumbSize;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_thumbSize = value;
|
||||||
|
UpdateThumb();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to fill the track.
|
||||||
|
/// </summary>
|
||||||
|
[EditorOrder(42), Tooltip("Fill the track.")]
|
||||||
|
public bool FillTrack = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to use whole numbers.
|
||||||
|
/// </summary>
|
||||||
|
[EditorOrder(43), Tooltip("Use whole numbers.")]
|
||||||
|
public bool WholeNumbers = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The color of the slider track line
|
||||||
|
/// </summary>
|
||||||
|
[EditorDisplay("Track Style"), EditorOrder(2010), Tooltip("The color of the slider track line."), ExpandGroups]
|
||||||
|
public Color TrackLineColor { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The color of the slider fill track line
|
||||||
|
/// </summary>
|
||||||
|
[EditorDisplay("Track Style"), EditorOrder(2011), VisibleIf(nameof(FillTrack)), Tooltip("The color of the slider fill track line.")]
|
||||||
|
public Color TrackFillLineColor { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the size of the track.
|
||||||
|
/// </summary>
|
||||||
|
private float TrackWidth => Width;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the brush used for slider track drawing.
|
||||||
|
/// </summary>
|
||||||
|
[EditorDisplay("Track Style"), EditorOrder(2012), Tooltip("The brush used for slider track drawing.")]
|
||||||
|
public IBrush TrackBrush { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the brush used for slider fill track drawing.
|
||||||
|
/// </summary>
|
||||||
|
[EditorDisplay("Track Style"), EditorOrder(2013), VisibleIf(nameof(FillTrack)), Tooltip("The brush used for slider fill track drawing.")]
|
||||||
|
public IBrush FillTrackBrush { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The color of the slider thumb when it's not selected
|
||||||
|
/// </summary>
|
||||||
|
[EditorDisplay("Thumb Style"), EditorOrder(2030), Tooltip("The color of the slider thumb when it's not selected."), ExpandGroups]
|
||||||
|
public Color ThumbColor { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The color of the slider thumb when it's selected
|
||||||
|
/// </summary>
|
||||||
|
[EditorDisplay("Thumb Style"), EditorOrder(2031), Tooltip("The color of the slider thumb when it's selected.")]
|
||||||
|
public Color ThumbColorSelected { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the brush used for slider thumb drawing.
|
||||||
|
/// </summary>
|
||||||
|
[EditorDisplay("Thumb Style"), EditorOrder(2032), Tooltip("The brush of the slider thumb.")]
|
||||||
|
public IBrush ThumbBrush { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether user is using a slider.
|
||||||
|
/// </summary>
|
||||||
|
[HideInEditor]
|
||||||
|
public bool IsSliding => _isSliding;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when sliding starts.
|
||||||
|
/// </summary>
|
||||||
|
public event Action SlidingStart;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when sliding ends.
|
||||||
|
/// </summary>
|
||||||
|
public event Action SlidingEnd;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when value gets changed.
|
||||||
|
/// </summary>
|
||||||
|
public event Action ValueChanged;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Slider"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public Slider()
|
||||||
|
: this(120, 30)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Slider"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="width">The width.</param>
|
||||||
|
/// <param name="height">The height.</param>
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnLostFocus()
|
||||||
|
{
|
||||||
|
if (_isSliding)
|
||||||
|
{
|
||||||
|
EndSliding();
|
||||||
|
}
|
||||||
|
|
||||||
|
base.OnLostFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||||
|
{
|
||||||
|
if (button == MouseButton.Left && _isSliding)
|
||||||
|
{
|
||||||
|
EndSliding();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.OnMouseUp(location, button);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnEndMouseCapture()
|
||||||
|
{
|
||||||
|
// Check if was sliding
|
||||||
|
if (_isSliding)
|
||||||
|
{
|
||||||
|
EndSliding();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
base.OnEndMouseCapture();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void OnSizeChanged()
|
||||||
|
{
|
||||||
|
base.OnSizeChanged();
|
||||||
|
|
||||||
|
UpdateThumb();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1279,16 +1279,16 @@ void VisjectExecutor::ProcessGroupParticles(Box* box, Node* node, Value& value)
|
|||||||
// Random Float Range
|
// Random Float Range
|
||||||
case 213:
|
case 213:
|
||||||
{
|
{
|
||||||
auto a = tryGetValue(node->TryGetBox(1), node->Values[0]).AsFloat;
|
auto a = (float)tryGetValue(node->TryGetBox(1), node->Values[0]);
|
||||||
auto b = tryGetValue(node->TryGetBox(2), node->Values[1]).AsFloat;
|
auto b = (float)tryGetValue(node->TryGetBox(2), node->Values[1]);
|
||||||
value = Math::Lerp(a, b, RAND);
|
value = Math::Lerp(a, b, RAND);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Random Vector2 Range
|
// Random Vector2 Range
|
||||||
case 214:
|
case 214:
|
||||||
{
|
{
|
||||||
auto a = tryGetValue(node->TryGetBox(1), node->Values[0]).AsFloat2();
|
auto a = (Float2)tryGetValue(node->TryGetBox(1), node->Values[0]);
|
||||||
auto b = tryGetValue(node->TryGetBox(2), node->Values[1]).AsFloat2();
|
auto b = (Float2)tryGetValue(node->TryGetBox(2), node->Values[1]);
|
||||||
value = Float2(
|
value = Float2(
|
||||||
Math::Lerp(a.X, b.X, RAND),
|
Math::Lerp(a.X, b.X, RAND),
|
||||||
Math::Lerp(a.Y, b.Y, RAND)
|
Math::Lerp(a.Y, b.Y, RAND)
|
||||||
@@ -1298,8 +1298,8 @@ void VisjectExecutor::ProcessGroupParticles(Box* box, Node* node, Value& value)
|
|||||||
// Random Vector3 Range
|
// Random Vector3 Range
|
||||||
case 215:
|
case 215:
|
||||||
{
|
{
|
||||||
auto a = tryGetValue(node->TryGetBox(1), node->Values[0]).AsFloat3();
|
auto a = (Float3)tryGetValue(node->TryGetBox(1), node->Values[0]);
|
||||||
auto b = tryGetValue(node->TryGetBox(2), node->Values[1]).AsFloat3();
|
auto b = (Float3)tryGetValue(node->TryGetBox(2), node->Values[1]);
|
||||||
value = Float3(
|
value = Float3(
|
||||||
Math::Lerp(a.X, b.X, RAND),
|
Math::Lerp(a.X, b.X, RAND),
|
||||||
Math::Lerp(a.Y, b.Y, RAND),
|
Math::Lerp(a.Y, b.Y, RAND),
|
||||||
@@ -1310,8 +1310,8 @@ void VisjectExecutor::ProcessGroupParticles(Box* box, Node* node, Value& value)
|
|||||||
// Random Vector4 Range
|
// Random Vector4 Range
|
||||||
case 216:
|
case 216:
|
||||||
{
|
{
|
||||||
auto a = tryGetValue(node->TryGetBox(1), node->Values[0]).AsFloat4();
|
auto a = (Float4)tryGetValue(node->TryGetBox(1), node->Values[0]);
|
||||||
auto b = tryGetValue(node->TryGetBox(2), node->Values[1]).AsFloat4();
|
auto b = (Float4)tryGetValue(node->TryGetBox(2), node->Values[1]);
|
||||||
value = Float4(
|
value = Float4(
|
||||||
Math::Lerp(a.X, b.X, RAND),
|
Math::Lerp(a.X, b.X, RAND),
|
||||||
Math::Lerp(a.Y, b.Y, RAND),
|
Math::Lerp(a.Y, b.Y, RAND),
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 24
|
compileSdkVersion 24
|
||||||
|
namespace "${PackageName}"
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "${PackageName}"
|
applicationId "${PackageName}"
|
||||||
minSdkVersion 24
|
minSdkVersion 24
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ buildscript {
|
|||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:4.1.0'
|
classpath 'com.android.tools.build:gradle:8.1.1'
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// in the individual module build.gradle files
|
||||||
|
|||||||
@@ -9,5 +9,4 @@
|
|||||||
|
|
||||||
# Specifies the JVM arguments used for the daemon process.
|
# Specifies the JVM arguments used for the daemon process.
|
||||||
# The setting is particularly useful for tweaking memory settings.
|
# The setting is particularly useful for tweaking memory settings.
|
||||||
# Default value: -Xmx10248m -XX:MaxPermSize=256m
|
org.gradle.jvmargs=-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||||
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
|
||||||
|
|||||||
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
|||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
|
||||||
|
|||||||
Reference in New Issue
Block a user