1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00
Robert c50a572363 UI - Update Avalonia to preview7
UI - Update FluentAvalonia to preview7
2023-04-26 21:38:15 +02:00

326 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive;
using Artemis.Core;
using Artemis.Core.Modules;
using Artemis.UI.Shared.DataModelVisualization.Shared;
using Artemis.UI.Shared.Events;
using Artemis.UI.Shared.Extensions;
using Artemis.UI.Shared.Services;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
using Avalonia.Interactivity;
using Avalonia.LogicalTree;
using Avalonia.Threading;
using Material.Icons.Avalonia;
using ReactiveUI;
namespace Artemis.UI.Shared.DataModelPicker;
/// <summary>
/// Represents a data model picker picker that can be used to select a data model path.
/// </summary>
public class DataModelPicker : TemplatedControl
{
/// <summary>
/// The data model UI service this picker should use.
/// </summary>
public static IDataModelUIService? DataModelUIService;
/// <summary>
/// Gets or sets data model path.
/// </summary>
public static readonly StyledProperty<DataModelPath?> DataModelPathProperty =
AvaloniaProperty.Register<DataModelPicker, DataModelPath?>(nameof(DataModelPath), defaultBindingMode: BindingMode.TwoWay);
/// <summary>
/// Gets or sets a boolean indicating whether the data model picker should show current values when selecting a path.
/// </summary>
public static readonly StyledProperty<bool> ShowDataModelValuesProperty =
AvaloniaProperty.Register<DataModelPicker, bool>(nameof(ShowDataModelValues));
/// <summary>
/// A list of extra modules to show data models of.
/// </summary>
public static readonly StyledProperty<ObservableCollection<Module>?> ModulesProperty =
AvaloniaProperty.Register<DataModelPicker, ObservableCollection<Module>?>(nameof(Modules), new ObservableCollection<Module>());
/// <summary>
/// The data model view model to show, if not provided one will be retrieved by the control.
/// </summary>
public static readonly StyledProperty<DataModelPropertiesViewModel?> DataModelViewModelProperty =
AvaloniaProperty.Register<DataModelPicker, DataModelPropertiesViewModel?>(nameof(DataModelViewModel));
/// <summary>
/// A list of types to filter the selectable paths on.
/// </summary>
public static readonly StyledProperty<ObservableCollection<Type>?> FilterTypesProperty =
AvaloniaProperty.Register<DataModelPicker, ObservableCollection<Type>?>(nameof(FilterTypes), new ObservableCollection<Type>());
/// <summary>
/// Gets or sets a boolean indicating whether the picker is in event picker mode.
/// When <see langword="true" /> event children aren't selectable and non-events are described as "{PropertyName}
/// changed".
/// </summary>
public static readonly StyledProperty<bool> IsEventPickerProperty =
AvaloniaProperty.Register<DataModelPicker, bool>(nameof(IsEventPicker));
private TextBlock? _currentPathDescription;
private TextBlock? _currentPathDisplay;
private MaterialIcon? _currentPathIcon;
private TreeView? _dataModelTreeView;
private DispatcherTimer? _updateTimer;
static DataModelPicker()
{
ModulesProperty.Changed.Subscribe(ModulesChanged);
DataModelPathProperty.Changed.Subscribe(DataModelPathPropertyChanged);
DataModelViewModelProperty.Changed.Subscribe(DataModelViewModelPropertyChanged);
}
/// <summary>
/// Creates a new instance of the <see cref="DataModelPicker" /> class.
/// </summary>
public DataModelPicker()
{
SelectPropertyCommand = ReactiveCommand.Create<DataModelVisualizationViewModel>(selected => ExecuteSelectPropertyCommand(selected));
}
/// <summary>
/// Gets a command that selects the path by it's view model.
/// </summary>
public ReactiveCommand<DataModelVisualizationViewModel, Unit> SelectPropertyCommand { get; }
/// <summary>
/// Gets or sets data model path.
/// </summary>
public DataModelPath? DataModelPath
{
get => GetValue(DataModelPathProperty);
set => SetValue(DataModelPathProperty, value);
}
/// <summary>
/// Gets or sets a boolean indicating whether the data model picker should show current values when selecting a path.
/// </summary>
public bool ShowDataModelValues
{
get => GetValue(ShowDataModelValuesProperty);
set => SetValue(ShowDataModelValuesProperty, value);
}
/// <summary>
/// A list of extra modules to show data models of.
/// </summary>
public ObservableCollection<Module>? Modules
{
get => GetValue(ModulesProperty);
set => SetValue(ModulesProperty, value);
}
/// <summary>
/// The data model view model to show, if not provided one will be retrieved by the control.
/// </summary>
public DataModelPropertiesViewModel? DataModelViewModel
{
get => GetValue(DataModelViewModelProperty);
private set => SetValue(DataModelViewModelProperty, value);
}
/// <summary>
/// A list of types to filter the selectable paths on.
/// </summary>
public ObservableCollection<Type>? FilterTypes
{
get => GetValue(FilterTypesProperty);
set => SetValue(FilterTypesProperty, value);
}
/// <summary>
/// Gets or sets a boolean indicating whether the picker is in event picker mode.
/// When <see langword="true" /> event children aren't selectable and non-events are described as "{PropertyName}
/// changed".
/// </summary>
public bool IsEventPicker
{
get => GetValue(IsEventPickerProperty);
set => SetValue(IsEventPickerProperty, value);
}
/// <summary>
/// Occurs when a new path has been selected
/// </summary>
public event EventHandler<DataModelSelectedEventArgs>? DataModelPathSelected;
/// <summary>
/// Invokes the <see cref="DataModelPathSelected" /> event
/// </summary>
/// <param name="e"></param>
protected virtual void OnDataModelPathSelected(DataModelSelectedEventArgs e)
{
DataModelPathSelected?.Invoke(this, e);
}
private static void ModulesChanged(AvaloniaPropertyChangedEventArgs<ObservableCollection<Module>?> e)
{
if (e.Sender is DataModelPicker dataModelPicker)
dataModelPicker.GetDataModel();
}
private static void DataModelPathPropertyChanged(AvaloniaPropertyChangedEventArgs<DataModelPath?> e)
{
if (e.Sender is DataModelPicker dataModelPicker)
dataModelPicker.UpdateCurrentPath(false);
}
private static void DataModelViewModelPropertyChanged(AvaloniaPropertyChangedEventArgs<DataModelPropertiesViewModel?> e)
{
if (e.Sender is DataModelPicker && e.OldValue.Value != null)
e.OldValue.Value.Dispose();
}
private void ExecuteSelectPropertyCommand(DataModelVisualizationViewModel selected)
{
if (selected.DataModelPath == null)
return;
if (selected.DataModelPath.Equals(DataModelPath))
return;
DataModelPath = new DataModelPath(selected.DataModelPath);
OnDataModelPathSelected(new DataModelSelectedEventArgs(DataModelPath));
}
private void Update(object? sender, EventArgs e)
{
if (DataModelUIService == null)
return;
DataModelViewModel?.Update(DataModelUIService, new DataModelUpdateConfiguration(!IsEventPicker));
}
private void GetDataModel()
{
if (DataModelUIService == null)
return;
ChangeDataModel(DataModelUIService.GetPluginDataModelVisualization(Modules?.ToList() ?? new List<Module>(), true));
}
private void ChangeDataModel(DataModelPropertiesViewModel? dataModel)
{
if (DataModelViewModel != null)
{
DataModelViewModel.Dispose();
DataModelViewModel.UpdateRequested -= DataModelOnUpdateRequested;
}
DataModelViewModel = dataModel;
if (DataModelViewModel != null)
DataModelViewModel.UpdateRequested += DataModelOnUpdateRequested;
}
private void DataModelOnUpdateRequested(object? sender, EventArgs e)
{
if (DataModelUIService == null || DataModelViewModel == null)
return;
DataModelViewModel.Update(DataModelUIService, new DataModelUpdateConfiguration(IsEventPicker));
DataModelViewModel.ApplyTypeFilter(true, FilterTypes?.ToArray() ?? Type.EmptyTypes);
}
private void DataModelTreeViewOnSelectionChanged(object? sender, SelectionChangedEventArgs e)
{
// Multi-select isn't a think so grab the first one
object? selected = _dataModelTreeView?.SelectedItems[0];
if (selected == null)
return;
if (selected is DataModelPropertyViewModel property && property.DataModelPath != null)
DataModelPath = new DataModelPath(property.DataModelPath);
else if (selected is DataModelListViewModel list && list.DataModelPath != null)
DataModelPath = new DataModelPath(list.DataModelPath);
else if (selected is DataModelEventViewModel dataModelEvent && dataModelEvent.DataModelPath != null)
DataModelPath = new DataModelPath(dataModelEvent.DataModelPath);
}
private void DataModelTreeViewOnDoubleTapped(object? sender, RoutedEventArgs e)
{
TreeViewItem? treeViewItem = (e.Source as ILogical)?.FindLogicalAncestorOfType<TreeViewItem>();
if (treeViewItem != null)
treeViewItem.IsExpanded = !treeViewItem.IsExpanded;
}
private void UpdateCurrentPath(bool selectCurrentPath)
{
if (DataModelPath == null)
return;
if (_dataModelTreeView != null && selectCurrentPath)
{
// Expand the path
DataModel? start = DataModelPath.Target;
DataModelVisualizationViewModel? root = DataModelViewModel?.Children.FirstOrDefault(c => c.DataModel == start);
if (root != null)
{
root.ExpandToPath(DataModelPath);
_dataModelTreeView.SelectedItem = root.GetViewModelForPath(DataModelPath);
}
}
if (_currentPathDisplay != null)
_currentPathDisplay.Text = string.Join(" ", DataModelPath.Segments.Where(s => s.GetPropertyDescription() != null).Select(s => s.GetPropertyDescription()!.Name));
if (_currentPathDescription != null)
_currentPathDescription.Text = DataModelPath.GetPropertyDescription()?.Description;
if (_currentPathIcon != null)
_currentPathIcon.Kind = DataModelPath.GetPropertyType().GetTypeIcon();
}
#region Overrides of TemplatedControl
/// <inheritdoc />
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
if (_dataModelTreeView != null)
_dataModelTreeView.SelectionChanged -= DataModelTreeViewOnSelectionChanged;
if (_dataModelTreeView != null)
_dataModelTreeView.DoubleTapped -= DataModelTreeViewOnDoubleTapped;
_currentPathIcon = e.NameScope.Find<MaterialIcon>("CurrentPathIcon");
_currentPathDisplay = e.NameScope.Find<TextBlock>("CurrentPathDisplay");
_currentPathDescription = e.NameScope.Find<TextBlock>("CurrentPathDescription");
_dataModelTreeView = e.NameScope.Find<TreeView>("DataModelTreeView");
if (_dataModelTreeView != null)
_dataModelTreeView.SelectionChanged += DataModelTreeViewOnSelectionChanged;
if (_dataModelTreeView != null)
_dataModelTreeView.DoubleTapped += DataModelTreeViewOnDoubleTapped;
}
#region Overrides of Visual
/// <inheritdoc />
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
GetDataModel();
UpdateCurrentPath(true);
_updateTimer = new DispatcherTimer(TimeSpan.FromMilliseconds(200), DispatcherPriority.Background, Update);
_updateTimer.Start();
}
/// <inheritdoc />
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
DataModelViewModel?.Dispose();
_updateTimer?.Stop();
_updateTimer = null;
}
#endregion
#endregion
}