1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Data model picker - Implemented most of the picker

This commit is contained in:
Robert 2022-03-21 23:08:07 +01:00
parent 81ca8c1425
commit 4cd596602f
6 changed files with 177 additions and 38 deletions

View File

@ -1,6 +1,8 @@
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Artemis.UI.Shared.Controls">
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>
@ -9,7 +11,43 @@
<!-- Set Defaults -->
<Setter Property="Template">
<ControlTemplate>
<TextBlock Text="Templated Control" />
<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>

View File

@ -9,9 +9,11 @@ using Artemis.UI.Shared.DataModelVisualization.Shared;
using Artemis.UI.Shared.Events;
using Artemis.UI.Shared.Services.Interfaces;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
using Avalonia.Media;
using Avalonia.Threading;
using ReactiveUI;
namespace Artemis.UI.Shared.Controls;
@ -24,7 +26,7 @@ public class DataModelPicker : TemplatedControl
/// Gets or sets data model path.
/// </summary>
public static readonly StyledProperty<DataModelPath?> DataModelPathProperty =
AvaloniaProperty.Register<DataModelPicker, DataModelPath?>(nameof(DataModelPath), defaultBindingMode: BindingMode.TwoWay, notifying: DataModelPathPropertyChanged);
AvaloniaProperty.Register<DataModelPicker, DataModelPath?>(nameof(DataModelPath), defaultBindingMode: BindingMode.TwoWay);
/// <summary>
/// Gets or sets the placeholder to show when nothing is selected.
@ -42,7 +44,7 @@ public class DataModelPicker : TemplatedControl
/// Gets or sets a boolean indicating whether the data model picker should show the full path of the selected value.
/// </summary>
public static readonly StyledProperty<bool> ShowFullPathProperty =
AvaloniaProperty.Register<DataModelPicker, bool>(nameof(ShowFullPath), notifying: ShowFullPathPropertyChanged);
AvaloniaProperty.Register<DataModelPicker, bool>(nameof(ShowFullPath));
/// <summary>
/// Gets or sets the brush to use when drawing the button.
@ -54,23 +56,19 @@ public class DataModelPicker : TemplatedControl
/// 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>(), notifying: ModulesPropertyChanged);
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), notifying: DataModelViewModelPropertyChanged);
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>(),
notifying: ExtraDataModelViewModelsPropertyChanged
);
AvaloniaProperty.Register<DataModelPicker, ObservableCollection<DataModelPropertiesViewModel>?>(nameof(ExtraDataModelViewModels), new ObservableCollection<DataModelPropertiesViewModel>());
/// <summary>
/// A list of types to filter the selectable paths on.
@ -78,6 +76,70 @@ public class DataModelPicker : TemplatedControl
public static readonly StyledProperty<ObservableCollection<Type>?> FilterTypesProperty =
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;
static DataModelPicker()
{
DataModelPathProperty.Changed.Subscribe(DataModelPathChanged);
ShowFullPathProperty.Changed.Subscribe(ShowFullPathChanged);
ModulesProperty.Changed.Subscribe(ModulesChanged);
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>
@ -91,7 +153,10 @@ public class DataModelPicker : TemplatedControl
/// </summary>
public ReactiveCommand<DataModelVisualizationViewModel, Unit> SelectPropertyCommand { get; }
internal static IDataModelUIService DataModelUIService
/// <summary>
/// Internal, don't use.
/// </summary>
public static IDataModelUIService DataModelUIService
{
set
{
@ -182,6 +247,15 @@ public class DataModelPicker : TemplatedControl
set => SetValue(FilterTypesProperty, 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>
/// Occurs when a new path has been selected
/// </summary>
@ -228,18 +302,45 @@ public class DataModelPicker : TemplatedControl
DataModelViewModel.UpdateRequested += DataModelOnUpdateRequested;
}
#region Overrides of TemplatedControl
/// <inheritdoc />
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
_dataModelButton = e.NameScope.Find<Button>("DataModelButton");
GetDataModel();
UpdateValueDisplay();
}
#endregion
#region Overrides of Visual
/// <inheritdoc />
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
_attached = true;
if (DataModelPath != null)
{
DataModelPath.PathInvalidated += PathValidationChanged;
DataModelPath.PathValidated += PathValidationChanged;
}
base.OnAttachedToVisualTree(e);
}
/// <inheritdoc />
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
if (DataModelPath != null)
{
DataModelPath.PathInvalidated -= PathValidationChanged;
DataModelPath.PathValidated -= PathValidationChanged;
}
DataModelViewModel?.Dispose();
base.OnDetachedFromVisualTree(e);
}
@ -247,17 +348,27 @@ public class DataModelPicker : TemplatedControl
private void UpdateValueDisplay()
{
ValueDisplay.Visibility = DataModelPath == null || DataModelPath.IsValid ? Visibility.Visible : Visibility.Collapsed;
ValuePlaceholder.Visibility = DataModelPath == null || DataModelPath.IsValid ? Visibility.Collapsed : Visibility.Visible;
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));
DataModelButton.ToolTip = formattedPath;
ValueDisplayTextBlock.Text = ShowFullPath
? formattedPath
: DataModelPath?.Segments.LastOrDefault()?.GetPropertyDescription()?.Name ?? DataModelPath?.Segments.LastOrDefault()?.Identifier;
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)
@ -268,23 +379,8 @@ public class DataModelPicker : TemplatedControl
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)
private void PathValidationChanged(object? sender, EventArgs e)
{
Dispatcher.UIThread.InvokeAsync(UpdateValueDisplay, DispatcherPriority.DataBind);
}
}

View File

@ -21,7 +21,7 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
private ObservableCollection<DataModelVisualizationViewModel> _children;
private DataModel? _dataModel;
private bool _isMatchingFilteredTypes;
private bool _isVisualizationExpanded;
private bool _isVisualizationExpanded = true;
private DataModelVisualizationViewModel? _parent;
private DataModelPropertyAttribute? _propertyDescription;
private bool _populatedStaticChildren;

View File

@ -26,6 +26,7 @@
<!-- Custom controls -->
<StyleInclude Source="/Styles/Controls/GradientPicker.axaml" />
<StyleInclude Source="/Styles/Controls/GradientPickerButton.axaml" />
<StyleInclude Source="/Controls/DataModelPicker.axaml" />
<!-- Custom styles -->
<StyleInclude Source="/Styles/Border.axaml" />

View File

@ -5,6 +5,7 @@ using Artemis.Core;
using Artemis.UI.Exceptions;
using Artemis.UI.Ninject;
using Artemis.UI.Screens.Root;
using Artemis.UI.Shared.Controls;
using Artemis.UI.Shared.Ninject;
using Artemis.UI.Shared.Services.Interfaces;
using Artemis.VisualScripting.Ninject;
@ -42,6 +43,8 @@ namespace Artemis.UI
_kernel.UseNinjectDependencyResolver();
DataModelPicker.DataModelUIService = _kernel.Get<IDataModelUIService>();
return _kernel;
}

View File

@ -16,7 +16,8 @@
<Border Classes="card">
<StackPanel Spacing="5">
<TextBlock Classes="h4">Nodes tests</TextBlock>
<ContentControl Content="{CompiledBinding VisualEditorViewModel}" HorizontalAlignment="Stretch" Height="800"></ContentControl>
<controls:DataModelPicker ></controls:DataModelPicker>
<!-- <ContentControl Content="{CompiledBinding VisualEditorViewModel}" HorizontalAlignment="Stretch" Height="800"></ContentControl> -->
</StackPanel>
</Border>
<Border Classes="card">