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.Services.Interfaces; using Avalonia; using Avalonia.Controls.Primitives; using Avalonia.Data; using Avalonia.Media; using ReactiveUI; namespace Artemis.UI.Shared.Controls; public class DataModelPicker : TemplatedControl { private static IDataModelUIService? _dataModelUIService; /// /// Gets or sets data model path. /// public static readonly StyledProperty DataModelPathProperty = AvaloniaProperty.Register(nameof(DataModelPath), defaultBindingMode: BindingMode.TwoWay, notifying: DataModelPathPropertyChanged); /// /// Gets or sets the placeholder to show when nothing is selected. /// public static readonly StyledProperty PlaceholderProperty = AvaloniaProperty.Register(nameof(Placeholder), "Click to select"); /// /// Gets or sets a boolean indicating whether the data model picker should show current values when selecting a path. /// public static readonly StyledProperty ShowDataModelValuesProperty = AvaloniaProperty.Register(nameof(ShowDataModelValues)); /// /// Gets or sets a boolean indicating whether the data model picker should show the full path of the selected value. /// public static readonly StyledProperty ShowFullPathProperty = AvaloniaProperty.Register(nameof(ShowFullPath), notifying: ShowFullPathPropertyChanged); /// /// Gets or sets the brush to use when drawing the button. /// public static readonly StyledProperty ButtonBrushProperty = AvaloniaProperty.Register(nameof(ButtonBrush)); /// /// A list of extra modules to show data models of. /// public static readonly StyledProperty?> ModulesProperty = AvaloniaProperty.Register?>(nameof(Modules), new ObservableCollection(), notifying: ModulesPropertyChanged); /// /// The data model view model to show, if not provided one will be retrieved by the control. /// public static readonly StyledProperty DataModelViewModelProperty = AvaloniaProperty.Register(nameof(DataModelViewModel), notifying: DataModelViewModelPropertyChanged); /// /// A list of data model view models to show /// public static readonly StyledProperty?> ExtraDataModelViewModelsProperty = AvaloniaProperty.Register?>( nameof(ExtraDataModelViewModels), new ObservableCollection(), notifying: ExtraDataModelViewModelsPropertyChanged ); /// /// A list of types to filter the selectable paths on. /// public static readonly StyledProperty?> FilterTypesProperty = AvaloniaProperty.Register?>(nameof(FilterTypes), new ObservableCollection()); /// /// Creates a new instance of the class. /// public DataModelPicker() { SelectPropertyCommand = ReactiveCommand.Create(selected => ExecuteSelectPropertyCommand(selected)); } /// /// Gets a command that selects the path by it's view model. /// public ReactiveCommand SelectPropertyCommand { get; } internal static IDataModelUIService DataModelUIService { set { if (_dataModelUIService != null) throw new AccessViolationException("This is not for you to touch"); _dataModelUIService = value; } } /// /// Gets or sets data model path. /// public DataModelPath? DataModelPath { get => GetValue(DataModelPathProperty); set => SetValue(DataModelPathProperty, value); } /// /// Gets or sets the placeholder to show when nothing is selected. /// public string Placeholder { get => GetValue(PlaceholderProperty); set => SetValue(PlaceholderProperty, value); } /// /// Gets or sets a boolean indicating whether the data model picker should show the full path of the selected value. /// public bool ShowFullPath { get => GetValue(ShowFullPathProperty); set => SetValue(ShowFullPathProperty, value); } /// /// Gets or sets a boolean indicating whether the data model picker should show current values when selecting a path. /// public bool ShowDataModelValues { get => GetValue(ShowDataModelValuesProperty); set => SetValue(ShowDataModelValuesProperty, value); } /// /// Gets or sets the brush to use when drawing the button. /// public Brush ButtonBrush { get => GetValue(ButtonBrushProperty); set => SetValue(ButtonBrushProperty, value); } /// /// A list of extra modules to show data models of. /// public ObservableCollection? Modules { get => GetValue(ModulesProperty); set => SetValue(ModulesProperty, value); } /// /// The data model view model to show, if not provided one will be retrieved by the control. /// public DataModelPropertiesViewModel? DataModelViewModel { get => GetValue(DataModelViewModelProperty); set => SetValue(DataModelViewModelProperty, value); } /// /// A list of data model view models to show. /// public ObservableCollection? ExtraDataModelViewModels { get => GetValue(ExtraDataModelViewModelsProperty); set => SetValue(ExtraDataModelViewModelsProperty, value); } /// /// A list of types to filter the selectable paths on. /// public ObservableCollection? FilterTypes { get => GetValue(FilterTypesProperty); set => SetValue(FilterTypesProperty, value); } /// /// Occurs when a new path has been selected /// public event EventHandler? DataModelPathSelected; /// /// Invokes the event /// /// protected virtual void OnDataModelPathSelected(DataModelSelectedEventArgs e) { DataModelPathSelected?.Invoke(this, e); } 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 GetDataModel() { if (_dataModelUIService == null) return; ChangeDataModel(_dataModelUIService.GetPluginDataModelVisualization(Modules?.ToList() ?? new List(), true)); } private void ChangeDataModel(DataModelPropertiesViewModel? dataModel) { if (DataModelViewModel != null) { DataModelViewModel.Dispose(); DataModelViewModel.UpdateRequested -= DataModelOnUpdateRequested; } DataModelViewModel = dataModel; if (DataModelViewModel != null) DataModelViewModel.UpdateRequested += DataModelOnUpdateRequested; } #region Overrides of Visual /// protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { base.OnAttachedToVisualTree(e); } /// protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) { base.OnDetachedFromVisualTree(e); } #endregion private void UpdateValueDisplay() { ValueDisplay.Visibility = DataModelPath == null || DataModelPath.IsValid ? Visibility.Visible : Visibility.Collapsed; ValuePlaceholder.Visibility = DataModelPath == null || DataModelPath.IsValid ? Visibility.Collapsed : Visibility.Visible; string? formattedPath = null; if (DataModelPath != null && DataModelPath.IsValid) formattedPath = string.Join(" › ", DataModelPath.Segments.Where(s => s.GetPropertyDescription() != null).Select(s => s.GetPropertyDescription()!.Name)); DataModelButton.ToolTip = formattedPath; ValueDisplayTextBlock.Text = ShowFullPath ? formattedPath : DataModelPath?.Segments.LastOrDefault()?.GetPropertyDescription()?.Name ?? DataModelPath?.Segments.LastOrDefault()?.Identifier; } private void DataModelOnUpdateRequested(object? sender, EventArgs e) { DataModelViewModel?.ApplyTypeFilter(true, FilterTypes?.ToArray() ?? Type.EmptyTypes); if (ExtraDataModelViewModels == null) return; foreach (DataModelPropertiesViewModel extraDataModelViewModel in ExtraDataModelViewModels) extraDataModelViewModel.ApplyTypeFilter(true, FilterTypes?.ToArray() ?? Type.EmptyTypes); } private static void DataModelPathPropertyChanged(IAvaloniaObject sender, bool before) { } private static void ShowFullPathPropertyChanged(IAvaloniaObject sender, bool before) { } private static void ModulesPropertyChanged(IAvaloniaObject sender, bool before) { } private static void DataModelViewModelPropertyChanged(IAvaloniaObject sender, bool before) { } private static void ExtraDataModelViewModelsPropertyChanged(IAvaloniaObject sender, bool before) { } }