1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00
Artemis/src/Artemis.VisualScripting/Editor/Controls/VisualScriptNodeCreationBox.cs

166 lines
6.5 KiB
C#

using System;
using System.Collections;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using Artemis.Core;
using Artemis.VisualScripting.Editor.Controls.Wrapper;
namespace Artemis.VisualScripting.Editor.Controls
{
[TemplatePart(Name = PART_SEARCHBOX, Type = typeof(TextBox))]
[TemplatePart(Name = PART_CONTENT, Type = typeof(ListBox))]
public class VisualScriptNodeCreationBox : Control
{
#region Constants
private const string PART_SEARCHBOX = "PART_SearchBox";
private const string PART_CONTENT = "PART_Content";
#endregion
#region Properties & Fields
private TextBox _searchBox;
private ListBox _contentList;
private CollectionViewSource _collectionViewSource;
private ICollectionView _contentView;
#endregion
#region DependencyProperties
public static readonly DependencyProperty AvailableNodesProperty = DependencyProperty.Register(
"AvailableNodes", typeof(IEnumerable), typeof(VisualScriptNodeCreationBox), new PropertyMetadata(default(IEnumerable), OnItemsSourceChanged));
public IEnumerable AvailableNodes
{
get => (IEnumerable) GetValue(AvailableNodesProperty);
set => SetValue(AvailableNodesProperty, value);
}
public static readonly DependencyProperty SourcePinProperty = DependencyProperty.Register(
"SourcePin", typeof(VisualScriptPin), typeof(VisualScriptNodeCreationBox), new PropertyMetadata(default(VisualScriptPin), OnSourcePinChanged));
public VisualScriptPin SourcePin
{
get => (VisualScriptPin) GetValue(SourcePinProperty);
set => SetValue(SourcePinProperty, value);
}
public static readonly DependencyProperty CreateNodeCommandProperty = DependencyProperty.Register(
"CreateNodeCommand", typeof(ICommand), typeof(VisualScriptNodeCreationBox), new PropertyMetadata(default(ICommand)));
public ICommand CreateNodeCommand
{
get => (ICommand) GetValue(CreateNodeCommandProperty);
set => SetValue(CreateNodeCommandProperty, value);
}
public static readonly DependencyProperty SearchInputProperty = DependencyProperty.Register(
"SearchInput", typeof(string), typeof(VisualScriptNodeCreationBox), new PropertyMetadata(default(string), OnSearchInputChanged));
public string SearchInput
{
get => (string) GetValue(SearchInputProperty);
set => SetValue(SearchInputProperty, value);
}
#endregion
#region Methods
public override void OnApplyTemplate()
{
_searchBox = GetTemplateChild(PART_SEARCHBOX) as TextBox ?? throw new NullReferenceException($"The Element '{PART_SEARCHBOX}' is missing.");
_contentList = GetTemplateChild(PART_CONTENT) as ListBox ?? throw new NullReferenceException($"The Element '{PART_CONTENT}' is missing.");
_contentList.IsSynchronizedWithCurrentItem = false;
_contentList.SelectionChanged += OnContentListSelectionChanged;
_contentList.SelectionMode = SelectionMode.Single;
IsVisibleChanged += OnIsVisibleChanged;
_searchBox.Focus();
_contentView?.Refresh();
ItemsSourceChanged();
}
private void OnIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue is not true)
return;
_searchBox.Focus();
_searchBox.SelectionStart = 0;
_searchBox.SelectionLength = _searchBox.Text.Length;
}
private void OnContentListSelectionChanged(object sender, SelectionChangedEventArgs args)
{
if ((args == null) || (_contentList?.SelectedItem == null)) return;
CreateNodeCommand?.Execute(_contentList.SelectedItem);
_contentList.SelectedItem = null;
}
private bool Filter(object o)
{
if (o is not NodeData nodeData) return false;
bool searchContains;
if (string.IsNullOrWhiteSpace(SearchInput))
searchContains = true;
else
{
searchContains = nodeData.Name.Contains(SearchInput, StringComparison.OrdinalIgnoreCase) ||
nodeData.Description.Contains(SearchInput, StringComparison.OrdinalIgnoreCase);
}
if (SourcePin == null || SourcePin.Pin.Type == typeof(object))
return searchContains;
if (SourcePin.Pin.Direction == PinDirection.Input)
return nodeData.OutputType != null && searchContains && (nodeData.OutputType == typeof(object) || nodeData.OutputType.IsAssignableTo(SourcePin.Pin.Type));
return nodeData.InputType != null && searchContains && (nodeData.InputType == typeof(object) || nodeData.InputType.IsAssignableFrom(SourcePin.Pin.Type));
}
private void ItemsSourceChanged()
{
if (_contentList == null) return;
if (AvailableNodes == null)
{
_contentView = null;
_collectionViewSource = null;
}
else
{
_collectionViewSource = new CollectionViewSource
{
Source = AvailableNodes,
SortDescriptions =
{
new SortDescription(nameof(NodeData.Category), ListSortDirection.Ascending),
new SortDescription(nameof(NodeData.Name), ListSortDirection.Ascending)
},
GroupDescriptions = {new PropertyGroupDescription(nameof(NodeData.Category))}
};
_contentView = _collectionViewSource.View;
_contentView.Filter += Filter;
}
_contentList.ItemsSource = _contentView;
}
private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs args) => (d as VisualScriptNodeCreationBox)?.ItemsSourceChanged();
private static void OnSourcePinChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) => (d as VisualScriptNodeCreationBox)?._contentView.Refresh();
private static void OnSearchInputChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) => (d as VisualScriptNodeCreationBox)?._contentView.Refresh();
#endregion
}
}