using System; using System.IO; using FlaxEditor.Scripting; using FlaxEngine; using FlaxEngine.Utilities; namespace FlaxEditor.Content; /// /// Manages and converts the selected content item to the appropriate types. Useful for drag operations. /// public class AssetPickerValidator: IContentItemOwner { private Asset _selected; private ContentItem _selectedItem; private ScriptType _type; private string _fileExtension; /// /// Gets or sets the selected item. /// public ContentItem SelectedItem { get => _selectedItem; set { if (_selectedItem == value) return; if (value == null) { if (_selected == null && _selectedItem is SceneItem) { // Deselect scene reference _selectedItem.RemoveReference(this); _selectedItem = null; _selected = null; OnSelectedItemChanged(); return; } // Deselect _selectedItem?.RemoveReference(this); _selectedItem = null; _selected = null; OnSelectedItemChanged(); } 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); OnSelectedItemChanged(); } else if (value is AssetItem assetItem) { SelectedAsset = FlaxEngine.Content.LoadAsync(assetItem.ID); } else { // Change value _selectedItem?.RemoveReference(this); _selectedItem = value; _selected = null; OnSelectedItemChanged(); } } } /// /// Gets or sets the selected asset identifier. /// public Guid SelectedID { get { if (_selected != null) return _selected.ID; if (_selectedItem is AssetItem assetItem) return assetItem.ID; return Guid.Empty; } set => SelectedItem = Editor.Instance.ContentDatabase.FindAsset(value); } /// /// Gets or sets the selected content item path. /// public string SelectedPath { get { string path = _selectedItem?.Path ?? _selected?.Path; if (path != null) { // Convert into path relative to the project (cross-platform) var projectFolder = Globals.ProjectFolder; if (path.StartsWith(projectFolder)) path = path.Substring(projectFolder.Length + 1); } return path; } set { if (string.IsNullOrEmpty(value)) { SelectedItem = null; } else { var path = StringUtils.IsRelative(value) ? Path.Combine(Globals.ProjectFolder, value) : value; SelectedItem = Editor.Instance.ContentDatabase.Find(path); } } } /// /// Gets or sets the selected asset object. /// 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); OnSelectedItemChanged(); } } /// /// Gets or sets the assets types that this picker accepts (it supports types derived from the given type). Use for generic file picker. /// 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; } } } /// /// Gets or sets the content items extensions filter. Null if unused. /// public string FileExtension { get => _fileExtension; set { if (_fileExtension != value) { _fileExtension = value; // Auto deselect if the current value is invalid if (_selectedItem != null && !IsValid(_selectedItem)) SelectedItem = null; } } } /// /// Occurs when selected item gets changed. /// public event Action SelectedItemChanged; /// /// The custom callback for assets validation. Cane be used to implement a rule for assets to pick. /// public Func CheckValid; /// /// Returns whether item is valid. /// /// /// public bool IsValid(ContentItem item) { if (_fileExtension != null && !item.Path.EndsWith(_fileExtension)) return false; if (CheckValid != null && !CheckValid(item)) return false; if (_type == ScriptType.Null) return true; if (item is AssetItem assetItem) { // Faster path for binary items (in-built) if (assetItem is BinaryAssetItem binaryItem) return _type.IsAssignableFrom(new ScriptType(binaryItem.Type)); // Type filter var type = TypeUtils.GetType(assetItem.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 (assetItem is JsonAssetItem && (_type.Type == typeof(JsonAsset) || _type.Type == typeof(Asset))) return true; // Special case for scene asset references if (_type.Type == typeof(SceneReference) && assetItem is SceneItem) return true; } return false; } /// /// Initializes a new instance of the class. /// public AssetPickerValidator() : this(new ScriptType(typeof(Asset))) { } /// /// Initializes a new instance of the class. /// /// The assets types that this picker accepts. public AssetPickerValidator(ScriptType assetType) { _type = assetType; } /// /// Called when selected item gets changed. /// protected virtual void OnSelectedItemChanged() { SelectedItemChanged?.Invoke(); } /// public void OnItemDeleted(ContentItem item) { // Deselect item SelectedItem = null; } /// public void OnItemRenamed(ContentItem item) { } /// public void OnItemReimported(ContentItem item) { } /// public void OnItemDispose(ContentItem item) { // Deselect item SelectedItem = null; } /// /// Call to remove reference from the selected item. /// public void OnDestroy() { _selectedItem?.RemoveReference(this); _selectedItem = null; _selected = null; } }