504 lines
16 KiB
C#
504 lines
16 KiB
C#
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
|
|
|
using System;
|
|
using FlaxEditor.Content;
|
|
using FlaxEditor.GUI.Drag;
|
|
using FlaxEditor.Scripting;
|
|
using FlaxEngine;
|
|
using FlaxEngine.GUI;
|
|
|
|
namespace FlaxEditor.GUI
|
|
{
|
|
/// <summary>
|
|
/// Assets picking control.
|
|
/// </summary>
|
|
/// <seealso cref="Control" />
|
|
/// <seealso cref="IContentItemOwner" />
|
|
[HideInEditor]
|
|
public class AssetPicker : Control, IContentItemOwner
|
|
{
|
|
private const float DefaultIconSize = 64;
|
|
private const float ButtonsOffset = 2;
|
|
private const float ButtonsSize = 12;
|
|
|
|
private Asset _selected;
|
|
private AssetItem _selectedItem;
|
|
private ScriptType _type;
|
|
|
|
private bool _isMouseDown;
|
|
private Vector2 _mouseDownPos;
|
|
private Vector2 _mousePos;
|
|
private DragAssets _dragOverElement;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the selected item.
|
|
/// </summary>
|
|
public AssetItem SelectedItem
|
|
{
|
|
get => _selectedItem;
|
|
set
|
|
{
|
|
if (value == null)
|
|
{
|
|
if (_selected == null && _selectedItem is SceneItem)
|
|
{
|
|
// Deselect scene reference
|
|
_selectedItem.RemoveReference(this);
|
|
_selectedItem = null;
|
|
_selected = null;
|
|
TooltipText = string.Empty;
|
|
OnSelectedItemChanged();
|
|
return;
|
|
}
|
|
|
|
// Deselect
|
|
SelectedAsset = null;
|
|
}
|
|
else if (value is SceneItem item)
|
|
{
|
|
if (_selectedItem == item)
|
|
return;
|
|
if (!IsValid(item))
|
|
item = null;
|
|
|
|
// Change value to scene reference (cannot load asset because scene can be already loaded - duplicated ID issue)
|
|
_selectedItem?.RemoveReference(this);
|
|
_selectedItem = item;
|
|
_selected = null;
|
|
_selectedItem?.AddReference(this);
|
|
|
|
// Update tooltip
|
|
TooltipText = _selectedItem?.NamePath;
|
|
|
|
OnSelectedItemChanged();
|
|
}
|
|
else
|
|
{
|
|
SelectedAsset = FlaxEngine.Content.LoadAsync(value.ID);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the selected asset identifier.
|
|
/// </summary>
|
|
public Guid SelectedID
|
|
{
|
|
get
|
|
{
|
|
if (_selected != null)
|
|
return _selected.ID;
|
|
if (_selectedItem != null)
|
|
return _selectedItem.ID;
|
|
return Guid.Empty;
|
|
}
|
|
set => SelectedItem = Editor.Instance.ContentDatabase.FindAsset(value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the selected asset object.
|
|
/// </summary>
|
|
public Asset SelectedAsset
|
|
{
|
|
get => _selected;
|
|
set
|
|
{
|
|
// Check if value won't change
|
|
if (value == _selected)
|
|
return;
|
|
|
|
// Find item from content database and check it
|
|
var item = value ? Editor.Instance.ContentDatabase.FindAsset(value.ID) : null;
|
|
if (item != null && !IsValid(item))
|
|
item = null;
|
|
|
|
// Change value
|
|
_selectedItem?.RemoveReference(this);
|
|
_selectedItem = item;
|
|
_selected = value;
|
|
_selectedItem?.AddReference(this);
|
|
|
|
// Update tooltip
|
|
TooltipText = _selectedItem?.NamePath;
|
|
|
|
OnSelectedItemChanged();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the assets types that this picker accepts (it supports types derived from the given type).
|
|
/// </summary>
|
|
public ScriptType AssetType
|
|
{
|
|
get => _type;
|
|
set
|
|
{
|
|
if (_type != value)
|
|
{
|
|
_type = value;
|
|
|
|
// Auto deselect if the current value is invalid
|
|
if (_selectedItem != null && !IsValid(_selectedItem))
|
|
SelectedItem = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Occurs when selected item gets changed.
|
|
/// </summary>
|
|
public event Action SelectedItemChanged;
|
|
|
|
/// <summary>
|
|
/// False if changing selected item is disabled.
|
|
/// </summary>
|
|
public bool CanEdit = true;
|
|
|
|
private bool IsValid(AssetItem item)
|
|
{
|
|
// Faster path for binary items (in-build)
|
|
if (item is BinaryAssetItem binaryItem)
|
|
return _type.IsAssignableFrom(new ScriptType(binaryItem.Type));
|
|
|
|
// Type filter
|
|
var type = TypeUtils.GetType(item.TypeName);
|
|
if (_type.IsAssignableFrom(type))
|
|
return true;
|
|
|
|
// Json assets can contain any type of the object defined by the C# type (data oriented design)
|
|
if (item is JsonAssetItem && (_type.Type == typeof(JsonAsset) || _type.Type == typeof(Asset)))
|
|
return true;
|
|
|
|
// Special case for scene asset references
|
|
if (_type.Type == typeof(SceneReference) && item is SceneItem)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="AssetPicker"/> class.
|
|
/// </summary>
|
|
public AssetPicker()
|
|
: this(new ScriptType(typeof(Asset)), Vector2.Zero)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="AssetPicker"/> class.
|
|
/// </summary>
|
|
/// <param name="assetType">The assets types that this picker accepts.</param>
|
|
/// <param name="location">The control location.</param>
|
|
public AssetPicker(ScriptType assetType, Vector2 location)
|
|
: base(location, new Vector2(DefaultIconSize + ButtonsOffset + ButtonsSize, DefaultIconSize))
|
|
{
|
|
_type = assetType;
|
|
_mousePos = Vector2.Minimum;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when selected item gets changed.
|
|
/// </summary>
|
|
protected virtual void OnSelectedItemChanged()
|
|
{
|
|
SelectedItemChanged?.Invoke();
|
|
}
|
|
|
|
private void DoDrag()
|
|
{
|
|
// Do the drag drop operation if has selected element
|
|
if (_selected != null && new Rectangle(Vector2.Zero, Size).Contains(ref _mouseDownPos))
|
|
{
|
|
DoDragDrop(DragAssets.GetDragData(_selected));
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void OnItemDeleted(ContentItem item)
|
|
{
|
|
// Deselect item
|
|
SelectedItem = null;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void OnItemRenamed(ContentItem item)
|
|
{
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void OnItemReimported(ContentItem item)
|
|
{
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void OnItemDispose(ContentItem item)
|
|
{
|
|
// Deselect item
|
|
SelectedItem = null;
|
|
}
|
|
|
|
private Rectangle IconRect => new Rectangle(0, 0, Height, Height);
|
|
|
|
private Rectangle Button1Rect => new Rectangle(Height + ButtonsOffset, 0, ButtonsSize, ButtonsSize);
|
|
|
|
private Rectangle Button2Rect => new Rectangle(Height + ButtonsOffset, ButtonsSize, ButtonsSize, ButtonsSize);
|
|
|
|
private Rectangle Button3Rect => new Rectangle(Height + ButtonsOffset, ButtonsSize * 2, ButtonsSize, ButtonsSize);
|
|
|
|
/// <inheritdoc />
|
|
public override void Draw()
|
|
{
|
|
var style = Style.Current;
|
|
var iconRect = IconRect;
|
|
var button1Rect = Button1Rect;
|
|
var button2Rect = Button2Rect;
|
|
var button3Rect = Button3Rect;
|
|
|
|
// Draw asset picker button
|
|
if (CanEdit)
|
|
Render2D.DrawSprite(style.ArrowDown, button1Rect, button1Rect.Contains(_mousePos) ? style.Foreground : style.ForegroundGrey);
|
|
|
|
if (_selectedItem != null)
|
|
{
|
|
// Draw item preview
|
|
_selectedItem.DrawThumbnail(ref iconRect);
|
|
|
|
// Draw buttons
|
|
if (CanEdit)
|
|
{
|
|
Render2D.DrawSprite(style.Search, button2Rect, button2Rect.Contains(_mousePos) ? style.Foreground : style.ForegroundGrey);
|
|
Render2D.DrawSprite(style.Cross, button3Rect, button3Rect.Contains(_mousePos) ? style.Foreground : style.ForegroundGrey);
|
|
}
|
|
else
|
|
{
|
|
Render2D.DrawSprite(style.Search, button1Rect, button1Rect.Contains(_mousePos) ? style.Foreground : style.ForegroundGrey);
|
|
}
|
|
|
|
// Draw name
|
|
float sizeForTextLeft = Width - button1Rect.Right;
|
|
if (sizeForTextLeft > 30)
|
|
{
|
|
Render2D.DrawText(
|
|
style.FontSmall,
|
|
_selectedItem.ShortName,
|
|
new Rectangle(button1Rect.Right + 2, 0, sizeForTextLeft, ButtonsSize),
|
|
style.Foreground,
|
|
TextAlignment.Near,
|
|
TextAlignment.Center);
|
|
}
|
|
}
|
|
// Check if has no item but has an asset (eg. virtual asset)
|
|
else if (_selected)
|
|
{
|
|
// Draw remove button
|
|
Render2D.DrawSprite(style.Cross, button3Rect, button3Rect.Contains(_mousePos) ? style.Foreground : style.ForegroundGrey);
|
|
|
|
// Draw name
|
|
float sizeForTextLeft = Width - button1Rect.Right;
|
|
if (sizeForTextLeft > 30)
|
|
{
|
|
var name = _selected.GetType().Name;
|
|
if (_selected.IsVirtual)
|
|
name += " (virtual)";
|
|
Render2D.DrawText(
|
|
style.FontSmall,
|
|
name,
|
|
new Rectangle(button1Rect.Right + 2, 0, sizeForTextLeft, ButtonsSize),
|
|
style.Foreground,
|
|
TextAlignment.Near,
|
|
TextAlignment.Center);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No element selected
|
|
Render2D.FillRectangle(iconRect, new Color(0.2f));
|
|
Render2D.DrawText(style.FontMedium, "No asset\nselected", iconRect, Color.Wheat, TextAlignment.Center, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, Height / DefaultIconSize);
|
|
}
|
|
|
|
// Check if drag is over
|
|
if (IsDragOver && _dragOverElement != null && _dragOverElement.HasValidDrag)
|
|
Render2D.FillRectangle(iconRect, style.BackgroundSelected * 0.4f);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void OnDestroy()
|
|
{
|
|
_selectedItem?.RemoveReference(this);
|
|
_selectedItem = null;
|
|
_selected = null;
|
|
|
|
base.OnDestroy();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void OnMouseLeave()
|
|
{
|
|
_mousePos = Vector2.Minimum;
|
|
|
|
// Check if start drag drop
|
|
if (_isMouseDown)
|
|
{
|
|
// Clear flag
|
|
_isMouseDown = false;
|
|
|
|
// Do the drag
|
|
DoDrag();
|
|
}
|
|
|
|
base.OnMouseLeave();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void OnMouseEnter(Vector2 location)
|
|
{
|
|
_mousePos = location;
|
|
_mouseDownPos = Vector2.Minimum;
|
|
|
|
base.OnMouseEnter(location);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void OnMouseMove(Vector2 location)
|
|
{
|
|
_mousePos = location;
|
|
|
|
// Check if start drag drop
|
|
if (_isMouseDown && Vector2.Distance(location, _mouseDownPos) > 10.0f && IconRect.Contains(_mouseDownPos))
|
|
{
|
|
// Do the drag
|
|
_isMouseDown = false;
|
|
DoDrag();
|
|
}
|
|
|
|
base.OnMouseMove(location);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override bool OnMouseUp(Vector2 location, MouseButton button)
|
|
{
|
|
if (button == MouseButton.Left && _isMouseDown)
|
|
{
|
|
_isMouseDown = false;
|
|
|
|
// Buttons logic
|
|
if (!CanEdit)
|
|
{
|
|
if (Button1Rect.Contains(location) && _selectedItem != null)
|
|
{
|
|
// Select asset
|
|
Editor.Instance.Windows.ContentWin.Select(_selectedItem);
|
|
}
|
|
}
|
|
else if (Button1Rect.Contains(location))
|
|
{
|
|
// Show asset picker popup
|
|
Focus();
|
|
AssetSearchPopup.Show(this, Button1Rect.BottomLeft, IsValid, assetItem =>
|
|
{
|
|
SelectedItem = assetItem;
|
|
RootWindow.Focus();
|
|
Focus();
|
|
});
|
|
}
|
|
else if (_selected != null || _selectedItem != null)
|
|
{
|
|
if (Button2Rect.Contains(location) && _selectedItem != null)
|
|
{
|
|
// Select asset
|
|
Editor.Instance.Windows.ContentWin.Select(_selectedItem);
|
|
}
|
|
else if (Button3Rect.Contains(location))
|
|
{
|
|
// Deselect asset
|
|
Focus();
|
|
SelectedItem = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handled
|
|
return true;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override bool OnMouseDown(Vector2 location, MouseButton button)
|
|
{
|
|
if (button == MouseButton.Left)
|
|
{
|
|
_isMouseDown = true;
|
|
_mouseDownPos = location;
|
|
}
|
|
|
|
// Handled
|
|
return true;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override bool OnMouseDoubleClick(Vector2 location, MouseButton button)
|
|
{
|
|
// Focus
|
|
Focus();
|
|
|
|
// Check if has element selected
|
|
if (_selectedItem != null && IconRect.Contains(location))
|
|
{
|
|
// Open it
|
|
Editor.Instance.ContentEditing.Open(_selectedItem);
|
|
}
|
|
|
|
// Handled
|
|
return true;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override DragDropEffect OnDragEnter(ref Vector2 location, DragData data)
|
|
{
|
|
base.OnDragEnter(ref location, data);
|
|
|
|
// Check if drop asset
|
|
if (_dragOverElement == null)
|
|
_dragOverElement = new DragAssets(IsValid);
|
|
if (CanEdit && _dragOverElement.OnDragEnter(data))
|
|
{
|
|
}
|
|
|
|
return _dragOverElement.Effect;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override DragDropEffect OnDragMove(ref Vector2 location, DragData data)
|
|
{
|
|
base.OnDragMove(ref location, data);
|
|
|
|
return _dragOverElement.Effect;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void OnDragLeave()
|
|
{
|
|
// Clear cache
|
|
_dragOverElement.OnDragLeave();
|
|
|
|
base.OnDragLeave();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override DragDropEffect OnDragDrop(ref Vector2 location, DragData data)
|
|
{
|
|
base.OnDragDrop(ref location, data);
|
|
|
|
if (CanEdit && _dragOverElement.HasValidDrag)
|
|
{
|
|
// Select element
|
|
SelectedItem = _dragOverElement.Objects[0];
|
|
}
|
|
|
|
// Clear cache
|
|
_dragOverElement.OnDragDrop();
|
|
|
|
return DragDropEffect.Move;
|
|
}
|
|
}
|
|
}
|