mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Data model picker - Added flyout
This commit is contained in:
parent
4cd596602f
commit
75a0be0c98
@ -1,54 +0,0 @@
|
|||||||
<Styles xmlns="https://github.com/avaloniaui"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:controls="using:Artemis.UI.Shared.Controls"
|
|
||||||
xmlns:fluent="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
|
||||||
xmlns:dataModel="clr-namespace:Artemis.UI.Shared.DataModelVisualization.Shared">
|
|
||||||
<Design.PreviewWith>
|
|
||||||
<controls:DataModelPicker />
|
|
||||||
</Design.PreviewWith>
|
|
||||||
|
|
||||||
<Style Selector="controls|DataModelPicker">
|
|
||||||
<!-- Set Defaults -->
|
|
||||||
<Setter Property="Template">
|
|
||||||
<ControlTemplate>
|
|
||||||
<fluent:DropDownButton Name="DataModelButton">
|
|
||||||
<fluent:DropDownButton.DataTemplates>
|
|
||||||
<TreeDataTemplate DataType="{x:Type dataModel:DataModelPropertiesViewModel}" ItemsSource="{Binding Children}">
|
|
||||||
<Grid ColumnDefinitions="Auto,*">
|
|
||||||
<TextBlock Grid.Column="0" Text="{Binding PropertyDescription.Name}" ToolTip.Tip="{Binding PropertyDescription.Description}" />
|
|
||||||
<TextBlock Grid.Column="1"
|
|
||||||
Text="{Binding DisplayValue}"
|
|
||||||
FontFamily="Consolas"
|
|
||||||
HorizontalAlignment="Right" />
|
|
||||||
</Grid>
|
|
||||||
</TreeDataTemplate>
|
|
||||||
|
|
||||||
<TreeDataTemplate DataType="{x:Type dataModel:DataModelPropertyViewModel}">
|
|
||||||
<Grid ColumnDefinitions="Auto,*">
|
|
||||||
<TextBlock Grid.Column="0" Text="{Binding PropertyDescription.Name}" ToolTip.Tip="{Binding PropertyDescription.Description}" />
|
|
||||||
<ContentControl Grid.Column="1" Content="{Binding DisplayViewModel}" FontFamily="Consolas" />
|
|
||||||
</Grid>
|
|
||||||
</TreeDataTemplate>
|
|
||||||
|
|
||||||
<TreeDataTemplate DataType="{x:Type dataModel:DataModelListViewModel}">
|
|
||||||
<Grid ColumnDefinitions="Auto,*">
|
|
||||||
<TextBlock Grid.Column="0" Text="{Binding PropertyDescription.Name}" ToolTip.Tip="{Binding PropertyDescription.Description}" />
|
|
||||||
<TextBlock Grid.Column="1"
|
|
||||||
Text="{Binding CountDisplay, Mode=OneWay}"
|
|
||||||
FontFamily="Consolas"
|
|
||||||
HorizontalAlignment="Right" />
|
|
||||||
</Grid>
|
|
||||||
</TreeDataTemplate>
|
|
||||||
|
|
||||||
<TreeDataTemplate DataType="{x:Type dataModel:DataModelEventViewModel}" ItemsSource="{Binding Children}">
|
|
||||||
<TextBlock Text="{Binding PropertyDescription.Name}" ToolTip.Tip="{Binding PropertyDescription.Description}" />
|
|
||||||
</TreeDataTemplate>
|
|
||||||
</fluent:DropDownButton.DataTemplates>
|
|
||||||
<fluent:DropDownButton.Flyout>
|
|
||||||
<MenuFlyout Items="{Binding DataModelViewModel.Children, RelativeSource={RelativeSource TemplatedParent}}"/>
|
|
||||||
</fluent:DropDownButton.Flyout>
|
|
||||||
</fluent:DropDownButton>
|
|
||||||
</ControlTemplate>
|
|
||||||
</Setter>
|
|
||||||
</Style>
|
|
||||||
</Styles>
|
|
||||||
@ -0,0 +1,218 @@
|
|||||||
|
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 ReactiveUI;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Shared.Controls.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 data model view models to show
|
||||||
|
/// </summary>
|
||||||
|
public static readonly StyledProperty<ObservableCollection<DataModelPropertiesViewModel>?> ExtraDataModelViewModelsProperty =
|
||||||
|
AvaloniaProperty.Register<DataModelPicker, ObservableCollection<DataModelPropertiesViewModel>?>(nameof(ExtraDataModelViewModels), new ObservableCollection<DataModelPropertiesViewModel>());
|
||||||
|
|
||||||
|
/// <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>());
|
||||||
|
|
||||||
|
static DataModelPicker()
|
||||||
|
{
|
||||||
|
ModulesProperty.Changed.Subscribe(ModulesChanged);
|
||||||
|
DataModelViewModelProperty.Changed.Subscribe(DataModelViewModelPropertyChanged);
|
||||||
|
ExtraDataModelViewModelsProperty.Changed.Subscribe(ExtraDataModelViewModelsChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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);
|
||||||
|
set => SetValue(DataModelViewModelProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A list of data model view models to show.
|
||||||
|
/// </summary>
|
||||||
|
public ObservableCollection<DataModelPropertiesViewModel>? ExtraDataModelViewModels
|
||||||
|
{
|
||||||
|
get => GetValue(ExtraDataModelViewModelsProperty);
|
||||||
|
set => SetValue(ExtraDataModelViewModelsProperty, 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>
|
||||||
|
/// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Overrides of TemplatedControl
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||||
|
{
|
||||||
|
GetDataModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private static void ModulesChanged(AvaloniaPropertyChangedEventArgs<ObservableCollection<Module>?> e)
|
||||||
|
{
|
||||||
|
if (e.Sender is DataModelPicker dataModelPicker)
|
||||||
|
dataModelPicker.GetDataModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DataModelViewModelPropertyChanged(AvaloniaPropertyChangedEventArgs<DataModelPropertiesViewModel?> e)
|
||||||
|
{
|
||||||
|
if (e.Sender is DataModelPicker && e.OldValue.Value != null)
|
||||||
|
e.OldValue.Value.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ExtraDataModelViewModelsChanged(AvaloniaPropertyChangedEventArgs<ObservableCollection<DataModelPropertiesViewModel>?> e)
|
||||||
|
{
|
||||||
|
// TODO, the original did nothing here either and I can't remember why
|
||||||
|
}
|
||||||
|
|
||||||
|
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<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)
|
||||||
|
{
|
||||||
|
DataModelViewModel?.ApplyTypeFilter(true, FilterTypes?.ToArray() ?? Type.EmptyTypes);
|
||||||
|
if (ExtraDataModelViewModels == null) return;
|
||||||
|
foreach (DataModelPropertiesViewModel extraDataModelViewModel in ExtraDataModelViewModels)
|
||||||
|
extraDataModelViewModel.ApplyTypeFilter(true, FilterTypes?.ToArray() ?? Type.EmptyTypes);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,45 +1,31 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reactive;
|
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.Core.Modules;
|
using Artemis.Core.Modules;
|
||||||
|
using Artemis.UI.Shared.Controls.Flyouts;
|
||||||
using Artemis.UI.Shared.DataModelVisualization.Shared;
|
using Artemis.UI.Shared.DataModelVisualization.Shared;
|
||||||
using Artemis.UI.Shared.Events;
|
|
||||||
using Artemis.UI.Shared.Services.Interfaces;
|
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.Primitives;
|
using Avalonia.Controls.Primitives;
|
||||||
using Avalonia.Data;
|
using Avalonia.Data;
|
||||||
using Avalonia.Media;
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using ReactiveUI;
|
using FluentAvalonia.Core;
|
||||||
|
|
||||||
namespace Artemis.UI.Shared.Controls;
|
namespace Artemis.UI.Shared.Controls.DataModelPicker;
|
||||||
|
|
||||||
public class DataModelPicker : TemplatedControl
|
/// <summary>
|
||||||
|
/// Represents a button that can be used to pick a data model path in a flyout.
|
||||||
|
/// </summary>
|
||||||
|
public class DataModelPickerButton : TemplatedControl
|
||||||
{
|
{
|
||||||
private 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>
|
/// <summary>
|
||||||
/// Gets or sets the placeholder to show when nothing is selected.
|
/// Gets or sets the placeholder to show when nothing is selected.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly StyledProperty<string> PlaceholderProperty =
|
public static readonly StyledProperty<string> PlaceholderProperty =
|
||||||
AvaloniaProperty.Register<DataModelPicker, string>(nameof(Placeholder), "Click to select");
|
AvaloniaProperty.Register<DataModelPicker, string>(nameof(Placeholder), "Click to select");
|
||||||
|
|
||||||
/// <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>
|
/// <summary>
|
||||||
/// Gets or sets a boolean indicating whether the data model picker should show the full path of the selected value.
|
/// Gets or sets a boolean indicating whether the data model picker should show the full path of the selected value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -47,10 +33,28 @@ public class DataModelPicker : TemplatedControl
|
|||||||
AvaloniaProperty.Register<DataModelPicker, bool>(nameof(ShowFullPath));
|
AvaloniaProperty.Register<DataModelPicker, bool>(nameof(ShowFullPath));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the brush to use when drawing the button.
|
/// Gets a boolean indicating whether the data model picker has a value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly StyledProperty<Brush> ButtonBrushProperty =
|
public static readonly StyledProperty<bool> HasValueProperty =
|
||||||
AvaloniaProperty.Register<DataModelPicker, Brush>(nameof(ButtonBrush));
|
AvaloniaProperty.Register<DataModelPicker, bool>(nameof(HasValue));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the desired flyout placement.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly StyledProperty<FlyoutPlacementMode> PlacementProperty =
|
||||||
|
AvaloniaProperty.Register<FlyoutBase, FlyoutPlacementMode>(nameof(Placement));
|
||||||
|
|
||||||
|
/// <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>
|
/// <summary>
|
||||||
/// A list of extra modules to show data models of.
|
/// A list of extra modules to show data models of.
|
||||||
@ -76,103 +80,16 @@ public class DataModelPicker : TemplatedControl
|
|||||||
public static readonly StyledProperty<ObservableCollection<Type>?> FilterTypesProperty =
|
public static readonly StyledProperty<ObservableCollection<Type>?> FilterTypesProperty =
|
||||||
AvaloniaProperty.Register<DataModelPicker, ObservableCollection<Type>?>(nameof(FilterTypes), new ObservableCollection<Type>());
|
AvaloniaProperty.Register<DataModelPicker, ObservableCollection<Type>?>(nameof(FilterTypes), new ObservableCollection<Type>());
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a boolean indicating whether the data model picker has a value.
|
|
||||||
/// </summary>
|
|
||||||
public static readonly StyledProperty<bool> HasValueProperty =
|
|
||||||
AvaloniaProperty.Register<DataModelPicker, bool>(nameof(HasValue));
|
|
||||||
|
|
||||||
private Button? _dataModelButton;
|
|
||||||
private bool _attached;
|
private bool _attached;
|
||||||
|
private bool _flyoutActive;
|
||||||
|
private Button? _button;
|
||||||
|
private DataModelPickerFlyout? _flyout;
|
||||||
|
private IDisposable? _dataModelPathChanged;
|
||||||
|
|
||||||
static DataModelPicker()
|
static DataModelPickerButton()
|
||||||
{
|
{
|
||||||
DataModelPathProperty.Changed.Subscribe(DataModelPathChanged);
|
|
||||||
ShowFullPathProperty.Changed.Subscribe(ShowFullPathChanged);
|
ShowFullPathProperty.Changed.Subscribe(ShowFullPathChanged);
|
||||||
ModulesProperty.Changed.Subscribe(ModulesChanged);
|
DataModelPathProperty.Changed.Subscribe(DataModelPathChanged);
|
||||||
DataModelViewModelProperty.Changed.Subscribe(DataModelViewModelPropertyChanged);
|
|
||||||
ExtraDataModelViewModelsProperty.Changed.Subscribe(ExtraDataModelViewModelsChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DataModelPathChanged(AvaloniaPropertyChangedEventArgs<DataModelPath?> e)
|
|
||||||
{
|
|
||||||
if (e.Sender is not DataModelPicker dataModelPicker)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (e.OldValue.Value != null)
|
|
||||||
{
|
|
||||||
e.OldValue.Value.PathInvalidated -= dataModelPicker.PathValidationChanged;
|
|
||||||
e.OldValue.Value.PathValidated -= dataModelPicker.PathValidationChanged;
|
|
||||||
e.OldValue.Value.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dataModelPicker._attached)
|
|
||||||
return;
|
|
||||||
|
|
||||||
dataModelPicker.UpdateValueDisplay();
|
|
||||||
if (e.NewValue.Value != null)
|
|
||||||
{
|
|
||||||
e.NewValue.Value.PathInvalidated += dataModelPicker.PathValidationChanged;
|
|
||||||
e.NewValue.Value.PathValidated += dataModelPicker.PathValidationChanged;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ShowFullPathChanged(AvaloniaPropertyChangedEventArgs<bool> e)
|
|
||||||
{
|
|
||||||
if (e.Sender is DataModelPicker dataModelPicker)
|
|
||||||
dataModelPicker.UpdateValueDisplay();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ModulesChanged(AvaloniaPropertyChangedEventArgs<ObservableCollection<Module>?> e)
|
|
||||||
{
|
|
||||||
if (e.Sender is DataModelPicker dataModelPicker)
|
|
||||||
dataModelPicker.GetDataModel();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DataModelViewModelPropertyChanged(AvaloniaPropertyChangedEventArgs<DataModelPropertiesViewModel?> e)
|
|
||||||
{
|
|
||||||
if (e.Sender is DataModelPicker && e.OldValue.Value != null)
|
|
||||||
e.OldValue.Value.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ExtraDataModelViewModelsChanged(AvaloniaPropertyChangedEventArgs<ObservableCollection<DataModelPropertiesViewModel>?> e)
|
|
||||||
{
|
|
||||||
// TODO, the original did nothing here either and I can't remember why
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <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>
|
|
||||||
/// Internal, don't use.
|
|
||||||
/// </summary>
|
|
||||||
public static IDataModelUIService DataModelUIService
|
|
||||||
{
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_dataModelUIService != null)
|
|
||||||
throw new AccessViolationException("This is not for you to touch");
|
|
||||||
_dataModelUIService = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets data model path.
|
|
||||||
/// </summary>
|
|
||||||
public DataModelPath? DataModelPath
|
|
||||||
{
|
|
||||||
get => GetValue(DataModelPathProperty);
|
|
||||||
set => SetValue(DataModelPathProperty, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -193,6 +110,33 @@ public class DataModelPicker : TemplatedControl
|
|||||||
set => SetValue(ShowFullPathProperty, value);
|
set => SetValue(ShowFullPathProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a boolean indicating whether the data model picker has a value.
|
||||||
|
/// </summary>
|
||||||
|
public bool HasValue
|
||||||
|
{
|
||||||
|
get => GetValue(HasValueProperty);
|
||||||
|
private set => SetValue(HasValueProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the desired flyout placement.
|
||||||
|
/// </summary>
|
||||||
|
public FlyoutPlacementMode Placement
|
||||||
|
{
|
||||||
|
get => GetValue(PlacementProperty);
|
||||||
|
set => SetValue(PlacementProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets data model path.
|
||||||
|
/// </summary>
|
||||||
|
public DataModelPath? DataModelPath
|
||||||
|
{
|
||||||
|
get => GetValue(DataModelPathProperty);
|
||||||
|
set => SetValue(DataModelPathProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a boolean indicating whether the data model picker should show current values when selecting a path.
|
/// Gets or sets a boolean indicating whether the data model picker should show current values when selecting a path.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -202,15 +146,6 @@ public class DataModelPicker : TemplatedControl
|
|||||||
set => SetValue(ShowDataModelValuesProperty, value);
|
set => SetValue(ShowDataModelValuesProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the brush to use when drawing the button.
|
|
||||||
/// </summary>
|
|
||||||
public Brush ButtonBrush
|
|
||||||
{
|
|
||||||
get => GetValue(ButtonBrushProperty);
|
|
||||||
set => SetValue(ButtonBrushProperty, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A list of extra modules to show data models of.
|
/// A list of extra modules to show data models of.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -248,58 +183,111 @@ public class DataModelPicker : TemplatedControl
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a boolean indicating whether the data model picker has a value.
|
/// Raised when the flyout opens.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool HasValue
|
public event TypedEventHandler<DataModelPickerButton, EventArgs>? FlyoutOpened;
|
||||||
{
|
|
||||||
get => GetValue(HasValueProperty);
|
|
||||||
private set => SetValue(HasValueProperty, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Occurs when a new path has been selected
|
/// Raised when the flyout closes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event EventHandler<DataModelSelectedEventArgs>? DataModelPathSelected;
|
public event TypedEventHandler<DataModelPickerButton, EventArgs>? FlyoutClosed;
|
||||||
|
|
||||||
/// <summary>
|
private static void DataModelPathChanged(AvaloniaPropertyChangedEventArgs<DataModelPath?> e)
|
||||||
/// Invokes the <see cref="DataModelPathSelected" /> event
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="e"></param>
|
|
||||||
protected virtual void OnDataModelPathSelected(DataModelSelectedEventArgs e)
|
|
||||||
{
|
{
|
||||||
DataModelPathSelected?.Invoke(this, e);
|
if (e.Sender is not DataModelPickerButton self || !self._attached)
|
||||||
}
|
|
||||||
|
|
||||||
private void ExecuteSelectPropertyCommand(DataModelVisualizationViewModel selected)
|
|
||||||
{
|
|
||||||
if (selected.DataModelPath == null)
|
|
||||||
return;
|
|
||||||
if (selected.DataModelPath.Equals(DataModelPath))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
DataModelPath = new DataModelPath(selected.DataModelPath);
|
if (e.OldValue.Value != null)
|
||||||
OnDataModelPathSelected(new DataModelSelectedEventArgs(DataModelPath));
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
e.OldValue.Value.PathInvalidated -= self.PathValidationChanged;
|
||||||
DataModelViewModel.UpdateRequested -= DataModelOnUpdateRequested;
|
e.OldValue.Value.PathValidated -= self.PathValidationChanged;
|
||||||
|
e.OldValue.Value.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
DataModelViewModel = dataModel;
|
if (e.NewValue.Value != null)
|
||||||
if (DataModelViewModel != null)
|
{
|
||||||
DataModelViewModel.UpdateRequested += DataModelOnUpdateRequested;
|
e.NewValue.Value.PathInvalidated += self.PathValidationChanged;
|
||||||
|
e.NewValue.Value.PathValidated += self.PathValidationChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.UpdateValueDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ShowFullPathChanged(AvaloniaPropertyChangedEventArgs<bool> e)
|
||||||
|
{
|
||||||
|
if (e.Sender is DataModelPickerButton self)
|
||||||
|
self.UpdateValueDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FlyoutDataModelPathChanged(AvaloniaPropertyChangedEventArgs<DataModelPath?> e)
|
||||||
|
{
|
||||||
|
if (!ReferenceEquals(e.Sender, _flyout?.DataModelPicker))
|
||||||
|
return;
|
||||||
|
|
||||||
|
DataModelPath = e.NewValue.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PathValidationChanged(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
Dispatcher.UIThread.InvokeAsync(UpdateValueDisplay, DispatcherPriority.DataBind);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateValueDisplay()
|
||||||
|
{
|
||||||
|
HasValue = DataModelPath != null && DataModelPath.IsValid;
|
||||||
|
|
||||||
|
if (_button == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!HasValue)
|
||||||
|
{
|
||||||
|
ToolTip.SetTip(_button, null);
|
||||||
|
_button.Content = Placeholder;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string? formattedPath = null;
|
||||||
|
if (DataModelPath != null && DataModelPath.IsValid)
|
||||||
|
formattedPath = string.Join(" › ", DataModelPath.Segments.Where(s => s.GetPropertyDescription() != null).Select(s => s.GetPropertyDescription()!.Name));
|
||||||
|
|
||||||
|
ToolTip.SetTip(_button, formattedPath);
|
||||||
|
_button.Content = ShowFullPath
|
||||||
|
? formattedPath
|
||||||
|
: DataModelPath?.Segments.LastOrDefault()?.GetPropertyDescription()?.Name ?? DataModelPath?.Segments.LastOrDefault()?.Identifier;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnButtonClick(object? sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (_flyout == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Logic here is taken from Fluent Avalonia's ColorPicker which also reuses the same control since it's large
|
||||||
|
_flyout.DataModelPicker.DataModelPath = DataModelPath;
|
||||||
|
_flyout.DataModelPicker.DataModelViewModel = DataModelViewModel;
|
||||||
|
_flyout.DataModelPicker.ExtraDataModelViewModels = ExtraDataModelViewModels;
|
||||||
|
_flyout.DataModelPicker.FilterTypes = FilterTypes;
|
||||||
|
_flyout.DataModelPicker.Modules = Modules;
|
||||||
|
_flyout.DataModelPicker.ShowDataModelValues = ShowDataModelValues;
|
||||||
|
|
||||||
|
_flyout.Placement = Placement;
|
||||||
|
_flyout.ShowAt(_button != null ? _button : this);
|
||||||
|
_flyoutActive = true;
|
||||||
|
|
||||||
|
_dataModelPathChanged = DataModelPicker.DataModelPathProperty.Changed.Subscribe(FlyoutDataModelPathChanged);
|
||||||
|
FlyoutOpened?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnFlyoutClosed(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (_flyoutActive)
|
||||||
|
{
|
||||||
|
FlyoutClosed?.Invoke(this, EventArgs.Empty);
|
||||||
|
_flyoutActive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_dataModelPathChanged?.Dispose();
|
||||||
|
_dataModelPathChanged = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Overrides of TemplatedControl
|
#region Overrides of TemplatedControl
|
||||||
@ -307,17 +295,15 @@ public class DataModelPicker : TemplatedControl
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||||
{
|
{
|
||||||
|
if (_button != null)
|
||||||
|
_button.Click -= OnButtonClick;
|
||||||
base.OnApplyTemplate(e);
|
base.OnApplyTemplate(e);
|
||||||
_dataModelButton = e.NameScope.Find<Button>("DataModelButton");
|
_button = e.NameScope.Find<Button>("MainButton");
|
||||||
|
if (_button != null)
|
||||||
GetDataModel();
|
_button.Click += OnButtonClick;
|
||||||
UpdateValueDisplay();
|
UpdateValueDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Overrides of Visual
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||||
{
|
{
|
||||||
@ -328,7 +314,13 @@ public class DataModelPicker : TemplatedControl
|
|||||||
DataModelPath.PathValidated += PathValidationChanged;
|
DataModelPath.PathValidated += PathValidationChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
base.OnAttachedToVisualTree(e);
|
if (_flyout == null)
|
||||||
|
{
|
||||||
|
_flyout = new DataModelPickerFlyout();
|
||||||
|
_flyout.FlyoutPresenterClasses.Add("data-model-picker-presenter");
|
||||||
|
}
|
||||||
|
|
||||||
|
_flyout.Closed += OnFlyoutClosed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -341,46 +333,10 @@ public class DataModelPicker : TemplatedControl
|
|||||||
}
|
}
|
||||||
|
|
||||||
DataModelViewModel?.Dispose();
|
DataModelViewModel?.Dispose();
|
||||||
base.OnDetachedFromVisualTree(e);
|
|
||||||
|
if (_flyout != null)
|
||||||
|
_flyout.Closed -= OnFlyoutClosed;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private void UpdateValueDisplay()
|
|
||||||
{
|
|
||||||
HasValue = DataModelPath != null && DataModelPath.IsValid;
|
|
||||||
|
|
||||||
string? formattedPath = null;
|
|
||||||
if (DataModelPath != null && DataModelPath.IsValid)
|
|
||||||
formattedPath = string.Join(" › ", DataModelPath.Segments.Where(s => s.GetPropertyDescription() != null).Select(s => s.GetPropertyDescription()!.Name));
|
|
||||||
|
|
||||||
if (_dataModelButton != null)
|
|
||||||
{
|
|
||||||
if (!HasValue)
|
|
||||||
{
|
|
||||||
ToolTip.SetTip(_dataModelButton, null);
|
|
||||||
_dataModelButton.Content = Placeholder;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ToolTip.SetTip(_dataModelButton, formattedPath);
|
|
||||||
_dataModelButton.Content = 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 void PathValidationChanged(object? sender, EventArgs e)
|
|
||||||
{
|
|
||||||
Dispatcher.UIThread.InvokeAsync(UpdateValueDisplay, DispatcherPriority.DataBind);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Shared.Controls.Flyouts;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines a flyout that hosts a data model picker.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class DataModelPickerFlyout : Flyout
|
||||||
|
{
|
||||||
|
private DataModelPicker.DataModelPicker? _picker;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the data model picker that the flyout hosts.
|
||||||
|
/// </summary>
|
||||||
|
public DataModelPicker.DataModelPicker DataModelPicker => _picker ??= new DataModelPicker.DataModelPicker();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override Control CreatePresenter()
|
||||||
|
{
|
||||||
|
_picker ??= new DataModelPicker.DataModelPicker();
|
||||||
|
FlyoutPresenter presenter = new() {Content = DataModelPicker};
|
||||||
|
return presenter;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,4 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.Primitives;
|
|
||||||
|
|
||||||
namespace Artemis.UI.Shared.Controls.Flyouts;
|
namespace Artemis.UI.Shared.Controls.Flyouts;
|
||||||
|
|
||||||
@ -11,7 +10,7 @@ public sealed class GradientPickerFlyout : Flyout
|
|||||||
private GradientPicker.GradientPicker? _picker;
|
private GradientPicker.GradientPicker? _picker;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the gradient picker that this flyout hosts
|
/// Gets the gradient picker that this flyout hosts.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public GradientPicker.GradientPicker GradientPicker => _picker ??= new GradientPicker.GradientPicker();
|
public GradientPicker.GradientPicker GradientPicker => _picker ??= new GradientPicker.GradientPicker();
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,6 @@ using System.Reflection;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.Core.Modules;
|
using Artemis.Core.Modules;
|
||||||
using Artemis.UI.Shared.Services;
|
|
||||||
using Artemis.UI.Shared.Services.Interfaces;
|
using Artemis.UI.Shared.Services.Interfaces;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
|
|
||||||
@ -21,7 +20,7 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
|
|||||||
private ObservableCollection<DataModelVisualizationViewModel> _children;
|
private ObservableCollection<DataModelVisualizationViewModel> _children;
|
||||||
private DataModel? _dataModel;
|
private DataModel? _dataModel;
|
||||||
private bool _isMatchingFilteredTypes;
|
private bool _isMatchingFilteredTypes;
|
||||||
private bool _isVisualizationExpanded = true;
|
private bool _isVisualizationExpanded;
|
||||||
private DataModelVisualizationViewModel? _parent;
|
private DataModelVisualizationViewModel? _parent;
|
||||||
private DataModelPropertyAttribute? _propertyDescription;
|
private DataModelPropertyAttribute? _propertyDescription;
|
||||||
private bool _populatedStaticChildren;
|
private bool _populatedStaticChildren;
|
||||||
|
|||||||
@ -26,7 +26,8 @@
|
|||||||
<!-- Custom controls -->
|
<!-- Custom controls -->
|
||||||
<StyleInclude Source="/Styles/Controls/GradientPicker.axaml" />
|
<StyleInclude Source="/Styles/Controls/GradientPicker.axaml" />
|
||||||
<StyleInclude Source="/Styles/Controls/GradientPickerButton.axaml" />
|
<StyleInclude Source="/Styles/Controls/GradientPickerButton.axaml" />
|
||||||
<StyleInclude Source="/Controls/DataModelPicker.axaml" />
|
<StyleInclude Source="/Styles/Controls/DataModelPicker.axaml" />
|
||||||
|
<StyleInclude Source="/Styles/Controls/DataModelPickerButton.axaml" />
|
||||||
|
|
||||||
<!-- Custom styles -->
|
<!-- Custom styles -->
|
||||||
<StyleInclude Source="/Styles/Border.axaml" />
|
<StyleInclude Source="/Styles/Border.axaml" />
|
||||||
|
|||||||
@ -0,0 +1,75 @@
|
|||||||
|
<Styles xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:dataModel="clr-namespace:Artemis.UI.Shared.DataModelVisualization.Shared"
|
||||||
|
xmlns:dataModelPicker="clr-namespace:Artemis.UI.Shared.Controls.DataModelPicker"
|
||||||
|
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia">
|
||||||
|
<Design.PreviewWith>
|
||||||
|
<dataModelPicker:DataModelPicker />
|
||||||
|
</Design.PreviewWith>
|
||||||
|
|
||||||
|
<Style Selector="dataModelPicker|DataModelPicker">
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate>
|
||||||
|
<Grid RowDefinitions="Auto,Auto,*" Width="600" Height="400">
|
||||||
|
<TextBox Grid.Row="0" Watermark="Search" Name="SearchBox"></TextBox>
|
||||||
|
|
||||||
|
<Border Grid.Row="1" Classes="card card-condensed" Margin="0 15">
|
||||||
|
<Grid ColumnDefinitions="Auto,*" RowDefinitions="Auto,*,*">
|
||||||
|
<TextBlock Grid.Row="0" Grid.ColumnSpan="2" Classes="SubtitleTextBlockStyle">Current selection</TextBlock>
|
||||||
|
<avalonia:MaterialIcon Kind="CalculatorVariantOutline" Grid.Column="0" Grid.Row="1" Grid.RowSpan="2" Height="22" Width="22" Margin="5 0 15 0"></avalonia:MaterialIcon>
|
||||||
|
<TextBlock Grid.Row="1" Grid.Column="1" Classes="BodyStrongTextBlockStyle">Cursor Y-position</TextBlock>
|
||||||
|
<TextBlock Grid.Row="2" Grid.Column="1" Classes="BodyTextBlockStyle" Foreground="{DynamicResource TextFillColorSecondary}">The current Y-position of the cursor in pixels</TextBlock>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<TreeView Grid.Row="2" Items="{Binding DataModelViewModel.Children, RelativeSource={RelativeSource TemplatedParent}}">
|
||||||
|
<TreeView.Styles>
|
||||||
|
<Style Selector="TreeViewItem">
|
||||||
|
<Setter Property="IsExpanded" Value="{Binding IsVisualizationExpanded, Mode=TwoWay}" />
|
||||||
|
</Style>
|
||||||
|
</TreeView.Styles>
|
||||||
|
<TreeView.DataTemplates>
|
||||||
|
<TreeDataTemplate DataType="{x:Type dataModel:DataModelPropertiesViewModel}" ItemsSource="{Binding Children}">
|
||||||
|
<Grid ColumnDefinitions="Auto,*">
|
||||||
|
<TextBlock Grid.Column="0" Text="{Binding PropertyDescription.Name}" ToolTip.Tip="{Binding PropertyDescription.Description}" />
|
||||||
|
<TextBlock Grid.Column="1"
|
||||||
|
Text="{Binding DisplayValue}"
|
||||||
|
FontFamily="Consolas"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Margin="0 0 10 0" />
|
||||||
|
</Grid>
|
||||||
|
</TreeDataTemplate>
|
||||||
|
|
||||||
|
<TreeDataTemplate DataType="{x:Type dataModel:DataModelPropertyViewModel}">
|
||||||
|
<Grid ColumnDefinitions="Auto,*">
|
||||||
|
<TextBlock Grid.Column="0" Text="{Binding PropertyDescription.Name}" ToolTip.Tip="{Binding PropertyDescription.Description}" />
|
||||||
|
<ContentControl Grid.Column="1" Content="{Binding DisplayViewModel}" FontFamily="Consolas" Margin="0 0 10 0" />
|
||||||
|
</Grid>
|
||||||
|
</TreeDataTemplate>
|
||||||
|
|
||||||
|
<TreeDataTemplate DataType="{x:Type dataModel:DataModelListViewModel}">
|
||||||
|
<Grid ColumnDefinitions="Auto,*">
|
||||||
|
<TextBlock Grid.Column="0" Text="{Binding PropertyDescription.Name}" ToolTip.Tip="{Binding PropertyDescription.Description}" />
|
||||||
|
<TextBlock Grid.Column="1"
|
||||||
|
Text="{Binding CountDisplay, Mode=OneWay}"
|
||||||
|
FontFamily="Consolas"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Margin="0 0 10 0" />
|
||||||
|
</Grid>
|
||||||
|
</TreeDataTemplate>
|
||||||
|
|
||||||
|
<TreeDataTemplate DataType="{x:Type dataModel:DataModelEventViewModel}" ItemsSource="{Binding Children}">
|
||||||
|
<TextBlock Text="{Binding PropertyDescription.Name}" ToolTip.Tip="{Binding PropertyDescription.Description}" />
|
||||||
|
</TreeDataTemplate>
|
||||||
|
</TreeView.DataTemplates>
|
||||||
|
</TreeView>
|
||||||
|
<StackPanel Grid.Row="2" VerticalAlignment="Center" Spacing="20" IsVisible="False">
|
||||||
|
<avalonia:MaterialIcon Kind="CloseCircle" Width="64" Height="64"></avalonia:MaterialIcon>
|
||||||
|
<TextBlock Classes="h4" TextAlignment="Center">No parts of the data model match your search</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
</Styles>
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
<Styles xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:dataModelPicker="clr-namespace:Artemis.UI.Shared.Controls.DataModelPicker"
|
||||||
|
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia">
|
||||||
|
<Design.PreviewWith>
|
||||||
|
<Border Padding="20" Width="200">
|
||||||
|
<StackPanel Spacing="5">
|
||||||
|
<dataModelPicker:DataModelPickerButton />
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</Design.PreviewWith>
|
||||||
|
|
||||||
|
<Style Selector="FlyoutPresenter.data-model-picker-presenter">
|
||||||
|
<!-- <Setter Property="Padding" Value="0" /> -->
|
||||||
|
<Setter Property="MaxWidth" Value="1200" />
|
||||||
|
<Setter Property="MaxHeight" Value="1200" />
|
||||||
|
<Setter Property="Background" Value="{DynamicResource SolidBackgroundFillColorBaseBrush}" />
|
||||||
|
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden" />
|
||||||
|
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Hidden" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="dataModelPicker|DataModelPickerButton">
|
||||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrush}" />
|
||||||
|
<Setter Property="MinHeight" Value="{DynamicResource TextControlThemeMinHeight}" />
|
||||||
|
<Setter Property="MinWidth" Value="{DynamicResource TextControlThemeMinWidth}" />
|
||||||
|
<Setter Property="CornerRadius" Value="{DynamicResource ControlCornerRadius}" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate>
|
||||||
|
<controls:Button Name="MainButton"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}"
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}">
|
||||||
|
</controls:Button>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
</Styles>
|
||||||
@ -1,11 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Reactive;
|
using System.Reactive;
|
||||||
using Artemis.Core.Ninject;
|
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
|
using Artemis.Core.Ninject;
|
||||||
using Artemis.UI.Exceptions;
|
using Artemis.UI.Exceptions;
|
||||||
using Artemis.UI.Ninject;
|
using Artemis.UI.Ninject;
|
||||||
using Artemis.UI.Screens.Root;
|
using Artemis.UI.Screens.Root;
|
||||||
using Artemis.UI.Shared.Controls;
|
using Artemis.UI.Shared.Controls.DataModelPicker;
|
||||||
using Artemis.UI.Shared.Ninject;
|
using Artemis.UI.Shared.Ninject;
|
||||||
using Artemis.UI.Shared.Services.Interfaces;
|
using Artemis.UI.Shared.Services.Interfaces;
|
||||||
using Artemis.VisualScripting.Ninject;
|
using Artemis.VisualScripting.Ninject;
|
||||||
@ -17,57 +17,55 @@ using Ninject.Modules;
|
|||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
using Splat.Ninject;
|
using Splat.Ninject;
|
||||||
|
|
||||||
namespace Artemis.UI
|
namespace Artemis.UI;
|
||||||
|
|
||||||
|
public static class ArtemisBootstrapper
|
||||||
{
|
{
|
||||||
public static class ArtemisBootstrapper
|
private static StandardKernel? _kernel;
|
||||||
|
private static Application? _application;
|
||||||
|
|
||||||
|
public static StandardKernel Bootstrap(Application application, params INinjectModule[] modules)
|
||||||
{
|
{
|
||||||
private static StandardKernel? _kernel;
|
if (_application != null || _kernel != null)
|
||||||
private static Application? _application;
|
throw new ArtemisUIException("UI already bootstrapped");
|
||||||
|
|
||||||
public static StandardKernel Bootstrap(Application application, params INinjectModule[] modules)
|
Utilities.PrepareFirstLaunch();
|
||||||
{
|
|
||||||
if (_application != null || _kernel != null)
|
|
||||||
throw new ArtemisUIException("UI already bootstrapped");
|
|
||||||
|
|
||||||
Utilities.PrepareFirstLaunch();
|
_application = application;
|
||||||
|
_kernel = new StandardKernel();
|
||||||
|
_kernel.Settings.InjectNonPublic = true;
|
||||||
|
|
||||||
_application = application;
|
_kernel.Load<CoreModule>();
|
||||||
_kernel = new StandardKernel();
|
_kernel.Load<UIModule>();
|
||||||
_kernel.Settings.InjectNonPublic = true;
|
_kernel.Load<SharedUIModule>();
|
||||||
|
_kernel.Load<NoStringNinjectModule>();
|
||||||
|
_kernel.Load(modules);
|
||||||
|
_kernel.UseNinjectDependencyResolver();
|
||||||
|
|
||||||
_kernel.Load<CoreModule>();
|
DataModelPicker.DataModelUIService = _kernel.Get<IDataModelUIService>();
|
||||||
_kernel.Load<UIModule>();
|
|
||||||
_kernel.Load<SharedUIModule>();
|
|
||||||
_kernel.Load<NoStringNinjectModule>();
|
|
||||||
_kernel.Load(modules);
|
|
||||||
|
|
||||||
_kernel.UseNinjectDependencyResolver();
|
return _kernel;
|
||||||
|
}
|
||||||
|
|
||||||
DataModelPicker.DataModelUIService = _kernel.Get<IDataModelUIService>();
|
public static void Initialize()
|
||||||
|
{
|
||||||
|
if (_application == null || _kernel == null)
|
||||||
|
throw new ArtemisUIException("UI not yet bootstrapped");
|
||||||
|
if (_application.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop)
|
||||||
|
return;
|
||||||
|
|
||||||
return _kernel;
|
// Don't shut down when the last window closes, we might still be active in the tray
|
||||||
}
|
desktop.ShutdownMode = ShutdownMode.OnExplicitShutdown;
|
||||||
|
// Create the root view model that drives the UI
|
||||||
|
RootViewModel rootViewModel = _kernel.Get<RootViewModel>();
|
||||||
|
// Apply the root view model to the data context of the application so that tray icon commands work
|
||||||
|
_application.DataContext = rootViewModel;
|
||||||
|
|
||||||
public static void Initialize()
|
RxApp.DefaultExceptionHandler = Observer.Create<Exception>(DisplayUnhandledException);
|
||||||
{
|
}
|
||||||
if (_application == null || _kernel == null)
|
|
||||||
throw new ArtemisUIException("UI not yet bootstrapped");
|
|
||||||
if (_application.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Don't shut down when the last window closes, we might still be active in the tray
|
private static void DisplayUnhandledException(Exception exception)
|
||||||
desktop.ShutdownMode = ShutdownMode.OnExplicitShutdown;
|
{
|
||||||
// Create the root view model that drives the UI
|
_kernel?.Get<IWindowService>().ShowExceptionDialog("Exception", exception);
|
||||||
RootViewModel rootViewModel = _kernel.Get<RootViewModel>();
|
|
||||||
// Apply the root view model to the data context of the application so that tray icon commands work
|
|
||||||
_application.DataContext = rootViewModel;
|
|
||||||
|
|
||||||
RxApp.DefaultExceptionHandler = Observer.Create<Exception>(DisplayUnhandledException);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DisplayUnhandledException(Exception exception)
|
|
||||||
{
|
|
||||||
_kernel?.Get<IWindowService>().ShowExceptionDialog("Exception", exception);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -8,6 +8,7 @@
|
|||||||
xmlns:attachedProperties="clr-namespace:Artemis.UI.Shared.AttachedProperties;assembly=Artemis.UI.Shared"
|
xmlns:attachedProperties="clr-namespace:Artemis.UI.Shared.AttachedProperties;assembly=Artemis.UI.Shared"
|
||||||
xmlns:workshop="clr-namespace:Artemis.UI.Screens.Workshop"
|
xmlns:workshop="clr-namespace:Artemis.UI.Screens.Workshop"
|
||||||
xmlns:gradientPicker="clr-namespace:Artemis.UI.Shared.Controls.GradientPicker;assembly=Artemis.UI.Shared"
|
xmlns:gradientPicker="clr-namespace:Artemis.UI.Shared.Controls.GradientPicker;assembly=Artemis.UI.Shared"
|
||||||
|
xmlns:dataModelPicker="clr-namespace:Artemis.UI.Shared.Controls.DataModelPicker;assembly=Artemis.UI.Shared"
|
||||||
mc:Ignorable="d" d:DesignWidth="800"
|
mc:Ignorable="d" d:DesignWidth="800"
|
||||||
x:Class="Artemis.UI.Screens.Workshop.WorkshopView"
|
x:Class="Artemis.UI.Screens.Workshop.WorkshopView"
|
||||||
x:DataType="workshop:WorkshopViewModel">
|
x:DataType="workshop:WorkshopViewModel">
|
||||||
@ -16,8 +17,7 @@
|
|||||||
<Border Classes="card">
|
<Border Classes="card">
|
||||||
<StackPanel Spacing="5">
|
<StackPanel Spacing="5">
|
||||||
<TextBlock Classes="h4">Nodes tests</TextBlock>
|
<TextBlock Classes="h4">Nodes tests</TextBlock>
|
||||||
<controls:DataModelPicker ></controls:DataModelPicker>
|
<dataModelPicker:DataModelPickerButton Placement="BottomEdgeAlignedLeft"/>
|
||||||
<!-- <ContentControl Content="{CompiledBinding VisualEditorViewModel}" HorizontalAlignment="Stretch" Height="800"></ContentControl> -->
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
<Border Classes="card">
|
<Border Classes="card">
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user