mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-12 11:56:30 +00:00
Merge branch 'feature/visual-scripting' into feature/avalonia
This commit is contained in:
commit
4ef7c83c74
366 files changed
+11986
-10866
No files matched your search
@@ -77,4 +77,7 @@
|
||||
<ItemGroup>
|
||||
<Resource Include="Resources\Fonts\RobotoMono-Regular.ttf" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Extensions\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,4 +1,5 @@
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Artemis.UI.Shared.Controls;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Ninject;
|
||||
|
||||
namespace Artemis.UI.Shared
|
||||
@@ -23,9 +24,9 @@ namespace Artemis.UI.Shared
|
||||
if (Initialized)
|
||||
return;
|
||||
|
||||
IColorPickerService colorPickerService = kernel.Get<IColorPickerService>();
|
||||
GradientPicker.ColorPickerService = colorPickerService;
|
||||
ColorPicker.ColorPickerService = colorPickerService;
|
||||
GradientPicker.ColorPickerService = kernel.Get<IColorPickerService>();
|
||||
ColorPicker.ColorPickerService = kernel.Get<IColorPickerService>();
|
||||
DataModelPicker.DataModelUIService = kernel.Get<IDataModelUIService>();
|
||||
|
||||
Initialized = true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
<UserControl x:Class="Artemis.UI.Shared.Controls.DataModelPicker"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Artemis.UI.Shared.Controls"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:shared="clr-namespace:Artemis.UI.Shared"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/Artemis.UI.Shared;component/ResourceDictionaries/DataModelConditions.xaml" />
|
||||
<ResourceDictionary>
|
||||
<shared:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
|
||||
<shared:BindingProxy x:Key="DataContextProxy" Data="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" />
|
||||
<DataTemplate x:Key="DataModelDataTemplate">
|
||||
<Control x:Name="TemplateControl" Focusable="False" Template="{StaticResource DataModelSelectionTemplate}" />
|
||||
<DataTemplate.Triggers>
|
||||
<DataTrigger Binding="{Binding Data.ShowDataModelValues, Source={StaticResource DataContextProxy}}" Value="True">
|
||||
<Setter TargetName="TemplateControl" Property="Template" Value="{StaticResource DataModelSelectionTemplateWithValues}" />
|
||||
</DataTrigger>
|
||||
</DataTemplate.Triggers>
|
||||
</DataTemplate>
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<Button Background="{Binding ButtonBrush, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
|
||||
BorderBrush="{Binding ButtonBrush, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
|
||||
Style="{StaticResource DataModelConditionButton}"
|
||||
IsEnabled="{Binding IsEnabled, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
|
||||
HorizontalAlignment="Left"
|
||||
Click="PropertyButton_OnClick"
|
||||
x:Name="DataModelButton">
|
||||
<Button.ContextMenu>
|
||||
<ContextMenu>
|
||||
<ContextMenu.Resources>
|
||||
<Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource MaterialDesignMenuItem}">
|
||||
<Setter Property="ItemsSource" Value="{Binding Children}" />
|
||||
<Setter Property="Command" Value="{Binding Data.SelectPropertyCommand, Source={StaticResource DataContextProxy}}" />
|
||||
<Setter Property="CommandParameter" Value="{Binding}" />
|
||||
<Setter Property="CommandTarget" Value="{Binding}" />
|
||||
<Setter Property="IsEnabled" Value="{Binding IsMatchingFilteredTypes}" />
|
||||
<Setter Property="IsSubmenuOpen" Value="{Binding IsVisualizationExpanded, Mode=TwoWay}" />
|
||||
<Setter Property="HeaderTemplate" Value="{StaticResource DataModelDataTemplate}" />
|
||||
</Style>
|
||||
<shared:IntToVisibilityConverter x:Key="IntToVisibilityConverter" />
|
||||
</ContextMenu.Resources>
|
||||
<ContextMenu.ItemsSource>
|
||||
<CompositeCollection>
|
||||
<CollectionContainer Collection="{Binding Data.ExtraDataModelViewModels, Source={StaticResource DataContextProxy}}" />
|
||||
<Separator Visibility="{Binding Data.ExtraDataModelViewModels.Count, Source={StaticResource DataContextProxy}, Converter={StaticResource IntToVisibilityConverter}, Mode=OneWay}" />
|
||||
<CollectionContainer Collection="{Binding Data.DataModelViewModel.Children, Source={StaticResource DataContextProxy}}" />
|
||||
</CompositeCollection>
|
||||
</ContextMenu.ItemsSource>
|
||||
</ContextMenu>
|
||||
</Button.ContextMenu>
|
||||
<Grid>
|
||||
<Grid x:Name="ValueDisplay">
|
||||
<TextBlock x:Name="ValueDisplayTextBlock"
|
||||
Visibility="{Binding DataModelPath, Converter={StaticResource NullToVisibilityConverter}, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" />
|
||||
<TextBlock FontStyle="Italic"
|
||||
Visibility="{Binding DataModelPath, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}">
|
||||
<Run Text="« " /><Run Text="{Binding Placeholder, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" /><Run Text=" »" />
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
<TextBlock x:Name="ValuePlaceholder" FontStyle="Italic">
|
||||
« Invalid »
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
</Button>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,343 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Threading;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Modules;
|
||||
using Artemis.UI.Shared.Services;
|
||||
|
||||
namespace Artemis.UI.Shared.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for DataModelPicker.xaml
|
||||
/// </summary>
|
||||
public partial class DataModelPicker : INotifyPropertyChanged
|
||||
{
|
||||
private static IDataModelUIService _dataModelUIService;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets data model path
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty DataModelPathProperty = DependencyProperty.Register(
|
||||
nameof(DataModelPath), typeof(DataModelPath), typeof(DataModelPicker),
|
||||
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, DataModelPathPropertyChangedCallback)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the brush to use when drawing the button
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty PlaceholderProperty = DependencyProperty.Register(
|
||||
nameof(Placeholder), typeof(string), typeof(DataModelPicker),
|
||||
new FrameworkPropertyMetadata("Click to select")
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the brush to use when drawing the button
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty ShowDataModelValuesProperty = DependencyProperty.Register(nameof(ShowDataModelValues), typeof(bool), typeof(DataModelPicker));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the brush to use when drawing the button
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty ShowFullPathProperty = DependencyProperty.Register(
|
||||
nameof(ShowFullPath), typeof(bool), typeof(DataModelPicker),
|
||||
new FrameworkPropertyMetadata(ShowFullPathPropertyCallback)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the brush to use when drawing the button
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty ButtonBrushProperty = DependencyProperty.Register(nameof(ButtonBrush), typeof(Brush), typeof(DataModelPicker));
|
||||
|
||||
/// <summary>
|
||||
/// A list of extra modules to show data models of
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty ModulesProperty = DependencyProperty.Register(
|
||||
nameof(Modules), typeof(ObservableCollection<Module>), typeof(DataModelPicker),
|
||||
new FrameworkPropertyMetadata(new ObservableCollection<Module>(), FrameworkPropertyMetadataOptions.None, ModulesPropertyChangedCallback)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// The data model view model to show, if not provided one will be retrieved by the control
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty DataModelViewModelProperty = DependencyProperty.Register(
|
||||
nameof(DataModelViewModel), typeof(DataModelPropertiesViewModel), typeof(DataModelPicker),
|
||||
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, DataModelViewModelPropertyChangedCallback)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// A list of data model view models to show
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty ExtraDataModelViewModelsProperty = DependencyProperty.Register(
|
||||
nameof(ExtraDataModelViewModels), typeof(ObservableCollection<DataModelPropertiesViewModel>), typeof(DataModelPicker),
|
||||
new FrameworkPropertyMetadata(new ObservableCollection<DataModelPropertiesViewModel>(), FrameworkPropertyMetadataOptions.None, ExtraDataModelViewModelsPropertyChangedCallback)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// A list of data model view models to show
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty FilterTypesProperty = DependencyProperty.Register(
|
||||
nameof(FilterTypes), typeof(ObservableCollection<Type>), typeof(DataModelPicker),
|
||||
new FrameworkPropertyMetadata(new ObservableCollection<Type>())
|
||||
);
|
||||
|
||||
public DataModelPicker()
|
||||
{
|
||||
SelectPropertyCommand = new DelegateCommand(ExecuteSelectPropertyCommand);
|
||||
Unloaded += OnUnloaded;
|
||||
InitializeComponent();
|
||||
GetDataModel();
|
||||
UpdateValueDisplay();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the brush to use when drawing the button
|
||||
/// </summary>
|
||||
public DataModelPath? DataModelPath
|
||||
{
|
||||
get => (DataModelPath) GetValue(DataModelPathProperty);
|
||||
set => SetValue(DataModelPathProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the brush to use when drawing the button
|
||||
/// </summary>
|
||||
public string Placeholder
|
||||
{
|
||||
get => (string) GetValue(PlaceholderProperty);
|
||||
set => SetValue(PlaceholderProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the brush to use when drawing the button
|
||||
/// </summary>
|
||||
public bool ShowDataModelValues
|
||||
{
|
||||
get => (bool) GetValue(ShowDataModelValuesProperty);
|
||||
set => SetValue(ShowDataModelValuesProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the brush to use when drawing the button
|
||||
/// </summary>
|
||||
public bool ShowFullPath
|
||||
{
|
||||
get => (bool) GetValue(ShowFullPathProperty);
|
||||
set => SetValue(ShowFullPathProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the brush to use when drawing the button
|
||||
/// </summary>
|
||||
public Brush ButtonBrush
|
||||
{
|
||||
get => (Brush) GetValue(ButtonBrushProperty);
|
||||
set => SetValue(ButtonBrushProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a list of extra modules to show data models of
|
||||
/// </summary>
|
||||
public ObservableCollection<Module>? Modules
|
||||
{
|
||||
get => (ObservableCollection<Module>) GetValue(ModulesProperty);
|
||||
set => SetValue(ModulesProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the data model view model to show
|
||||
/// </summary>
|
||||
public DataModelPropertiesViewModel? DataModelViewModel
|
||||
{
|
||||
get => (DataModelPropertiesViewModel) GetValue(DataModelViewModelProperty);
|
||||
set => SetValue(DataModelViewModelProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a list of data model view models to show
|
||||
/// </summary>
|
||||
public ObservableCollection<DataModelPropertiesViewModel>? ExtraDataModelViewModels
|
||||
{
|
||||
get => (ObservableCollection<DataModelPropertiesViewModel>) GetValue(ExtraDataModelViewModelsProperty);
|
||||
set => SetValue(ExtraDataModelViewModelsProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the types of properties this view model will allow to be selected
|
||||
/// </summary>
|
||||
public ObservableCollection<Type>? FilterTypes
|
||||
{
|
||||
get => (ObservableCollection<Type>) GetValue(FilterTypesProperty);
|
||||
set => SetValue(FilterTypesProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Command used by view
|
||||
/// </summary>
|
||||
public DelegateCommand SelectPropertyCommand { get; }
|
||||
|
||||
internal static IDataModelUIService DataModelUIService
|
||||
{
|
||||
set
|
||||
{
|
||||
if (_dataModelUIService != null)
|
||||
throw new AccessViolationException("This is not for you to touch");
|
||||
_dataModelUIService = 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 void GetDataModel()
|
||||
{
|
||||
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 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 void ExecuteSelectPropertyCommand(object? context)
|
||||
{
|
||||
if (context is not DataModelVisualizationViewModel selected)
|
||||
return;
|
||||
|
||||
if (selected.DataModelPath == null)
|
||||
return;
|
||||
if (selected.DataModelPath.Equals(DataModelPath))
|
||||
return;
|
||||
|
||||
DataModelPath = new DataModelPath(selected.DataModelPath);
|
||||
OnDataModelPathSelected(new DataModelSelectedEventArgs(DataModelPath));
|
||||
}
|
||||
|
||||
private void PropertyButton_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// DataContext is not set when using left button, I don't know why but there it is
|
||||
if (sender is Button button && button.ContextMenu != null)
|
||||
{
|
||||
button.ContextMenu.DataContext = button.DataContext;
|
||||
button.ContextMenu.IsOpen = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static void DataModelPathPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is not DataModelPicker dataModelPicker)
|
||||
return;
|
||||
|
||||
if (e.OldValue is DataModelPath oldPath)
|
||||
{
|
||||
oldPath.PathInvalidated -= dataModelPicker.PathValidationChanged;
|
||||
oldPath.PathValidated -= dataModelPicker.PathValidationChanged;
|
||||
oldPath.Dispose();
|
||||
}
|
||||
|
||||
dataModelPicker.UpdateValueDisplay();
|
||||
if (e.NewValue is DataModelPath newPath)
|
||||
{
|
||||
newPath.PathInvalidated += dataModelPicker.PathValidationChanged;
|
||||
newPath.PathValidated += dataModelPicker.PathValidationChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ShowFullPathPropertyCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is not DataModelPicker dataModelPicker)
|
||||
return;
|
||||
|
||||
dataModelPicker.UpdateValueDisplay();
|
||||
}
|
||||
|
||||
private static void ModulesPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is not DataModelPicker dataModelPicker)
|
||||
return;
|
||||
|
||||
dataModelPicker.GetDataModel();
|
||||
}
|
||||
|
||||
private static void DataModelViewModelPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is not DataModelPicker dataModelPicker)
|
||||
return;
|
||||
|
||||
if (e.OldValue is DataModelPropertiesViewModel vm)
|
||||
vm.Dispose();
|
||||
}
|
||||
|
||||
private static void ExtraDataModelViewModelsPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is not DataModelPicker dataModelPicker)
|
||||
return;
|
||||
}
|
||||
|
||||
private void PathValidationChanged(object? sender, EventArgs e)
|
||||
{
|
||||
Dispatcher.Invoke(UpdateValueDisplay, DispatcherPriority.DataBind);
|
||||
}
|
||||
|
||||
private void OnUnloaded(object o, RoutedEventArgs routedEventArgs)
|
||||
{
|
||||
if (DataModelPath != null)
|
||||
{
|
||||
DataModelPath.PathInvalidated -= PathValidationChanged;
|
||||
DataModelPath.PathValidated -= PathValidationChanged;
|
||||
}
|
||||
|
||||
DataModelViewModel?.Dispose();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ namespace Artemis.UI.Shared
|
||||
{
|
||||
_gradientConverter = new ColorGradientToGradientStopsConverter();
|
||||
InitializeComponent();
|
||||
Unloaded += OnUnloaded;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -121,7 +122,7 @@ namespace Artemis.UI.Shared
|
||||
|
||||
private void UpdateGradientStops()
|
||||
{
|
||||
GradientPreview.GradientStops = (GradientStopCollection)_gradientConverter.Convert(ColorGradient, null!, null!, null!);
|
||||
GradientPreview.GradientStops = (GradientStopCollection) _gradientConverter.Convert(ColorGradient, null!, null!, null!);
|
||||
}
|
||||
|
||||
private void UIElement_OnMouseUp(object sender, MouseButtonEventArgs e)
|
||||
@@ -137,6 +138,11 @@ namespace Artemis.UI.Shared
|
||||
});
|
||||
}
|
||||
|
||||
private void OnUnloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ColorGradient.CollectionChanged -= GradientChanged;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace Artemis.UI.Shared
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts <see cref="int"/> to <see cref="Visibility"/>
|
||||
/// </summary>
|
||||
public class IntToVisibilityConverter : IValueConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
Parameters direction;
|
||||
if (parameter == null)
|
||||
direction = Parameters.Normal;
|
||||
else
|
||||
direction = (Parameters) Enum.Parse(typeof(Parameters), (string) parameter);
|
||||
|
||||
return direction == Parameters.Normal
|
||||
? value is > 1 ? Visibility.Visible : Visibility.Collapsed
|
||||
: value is > 1 ? Visibility.Collapsed : Visibility.Visible;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private enum Parameters
|
||||
{
|
||||
Normal,
|
||||
Inverted
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
using Artemis.Core;
|
||||
|
||||
namespace Artemis.UI.Shared
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts <see cref="T:System.String" /> into <see cref="Numeric" />.
|
||||
/// </summary>
|
||||
[ValueConversion(typeof(string), typeof(Numeric))]
|
||||
public class StringToNumericConverter : IValueConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public object? Convert(object? value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return value?.ToString();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object ConvertBack(object? value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
Numeric.TryParse(value as string, out Numeric result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
<UserControl x:Class="Artemis.UI.Shared.Input.DataModelDynamicView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:shared="clr-namespace:Artemis.UI.Shared"
|
||||
xmlns:input="clr-namespace:Artemis.UI.Shared.Input"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance input:DataModelDynamicViewModel}">
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/Artemis.UI.Shared;component/ResourceDictionaries/DataModelConditions.xaml" />
|
||||
<ResourceDictionary>
|
||||
<shared:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
|
||||
<shared:BindingProxy x:Key="DataContextProxy" Data="{Binding}" />
|
||||
<DataTemplate x:Key="DataModelDataTemplate">
|
||||
<Control x:Name="TemplateControl" Focusable="False" Template="{StaticResource DataModelSelectionTemplate}" />
|
||||
<DataTemplate.Triggers>
|
||||
<DataTrigger Binding="{Binding Data.ShowDataModelValues.Value, Source={StaticResource DataContextProxy}}" Value="True">
|
||||
<Setter TargetName="TemplateControl" Property="Template" Value="{StaticResource DataModelSelectionTemplateWithValues}" />
|
||||
</DataTrigger>
|
||||
</DataTemplate.Triggers>
|
||||
</DataTemplate>
|
||||
<Style x:Key="DataModelConditionButtonCornerToggle" BasedOn="{StaticResource DataModelConditionButton}" TargetType="Button">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding DisplaySwitchButton}" Value="True">
|
||||
<Setter Property="materialDesign:ButtonAssist.CornerRadius" Value="0 2 2 0" />
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding DisplaySwitchButton}" Value="False">
|
||||
<Setter Property="materialDesign:ButtonAssist.CornerRadius" Value="2" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Button Style="{StaticResource MaterialDesignFlatDarkBgButton}"
|
||||
Background="{Binding SwitchButtonBrush}"
|
||||
BorderBrush="{Binding SwitchButtonBrush}"
|
||||
Height="22"
|
||||
Padding="0"
|
||||
Width="22"
|
||||
FontSize="12"
|
||||
materialDesign:ButtonAssist.CornerRadius="2 0 0 2"
|
||||
Margin="3 0 -3 0"
|
||||
HorizontalAlignment="Left"
|
||||
ToolTip="Switch to manual input"
|
||||
Command="{s:Action SwitchToStatic}"
|
||||
Visibility="{Binding DisplaySwitchButton, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
|
||||
<materialDesign:PackIcon Kind="SwapHorizontal" />
|
||||
</Button>
|
||||
<Button Background="{Binding ButtonBrush}"
|
||||
BorderBrush="{Binding ButtonBrush}"
|
||||
Style="{StaticResource DataModelConditionButtonCornerToggle}"
|
||||
ToolTip="{Binding DisplayPath}"
|
||||
IsEnabled="{Binding IsEnabled}"
|
||||
HorizontalAlignment="Left"
|
||||
Click="PropertyButton_OnClick">
|
||||
<Button.ContextMenu>
|
||||
<ContextMenu IsOpen="{Binding IsDataModelViewModelOpen, Mode=OneWayToSource}">
|
||||
<ContextMenu.Resources>
|
||||
<Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource MaterialDesignMenuItem}">
|
||||
<Setter Property="ItemsSource" Value="{Binding Children}" />
|
||||
<Setter Property="Command" Value="{Binding Data.SelectPropertyCommand, Source={StaticResource DataContextProxy}}" />
|
||||
<Setter Property="CommandParameter" Value="{Binding}" />
|
||||
<Setter Property="CommandTarget" Value="{Binding}" />
|
||||
<Setter Property="IsEnabled" Value="{Binding IsMatchingFilteredTypes}" />
|
||||
<Setter Property="IsSubmenuOpen" Value="{Binding IsVisualizationExpanded, Mode=TwoWay}" />
|
||||
<Setter Property="HeaderTemplate" Value="{StaticResource DataModelDataTemplate}" />
|
||||
</Style>
|
||||
</ContextMenu.Resources>
|
||||
<ContextMenu.ItemsSource>
|
||||
<CompositeCollection>
|
||||
<MenuItem Header="No modules available" IsEnabled="False" Visibility="{Binding Data.HasNoModules, Source={StaticResource DataContextProxy}, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
|
||||
<MenuItem.Icon>
|
||||
<materialDesign:PackIcon Kind="EmojiSad" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<CollectionContainer Collection="{Binding Data.ExtraDataModelViewModels, Source={StaticResource DataContextProxy}}" />
|
||||
<Separator Visibility="{Binding Data.HasExtraDataModels, Source={StaticResource DataContextProxy}, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}"/>
|
||||
<CollectionContainer Collection="{Binding Data.DataModelViewModel.Children, Source={StaticResource DataContextProxy}}" />
|
||||
</CompositeCollection>
|
||||
</ContextMenu.ItemsSource>
|
||||
</ContextMenu>
|
||||
</Button.ContextMenu>
|
||||
<Grid>
|
||||
<Grid Visibility="{Binding IsValid,Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
|
||||
<TextBlock Text="{Binding DisplayValue}"
|
||||
Visibility="{Binding DataModelPath, Converter={StaticResource NullToVisibilityConverter}}" />
|
||||
<TextBlock FontStyle="Italic"
|
||||
Visibility="{Binding DataModelPath, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted}">
|
||||
<Run Text="« " /><Run Text="{Binding Placeholder}" /><Run Text=" »" />
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
<TextBlock FontStyle="Italic"
|
||||
Visibility="{Binding IsValid, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay}">
|
||||
« Invalid »
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
</UserControl>
|
||||
@@ -1,29 +0,0 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Artemis.UI.Shared.Input
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for DataModelDynamicView.xaml
|
||||
/// </summary>
|
||||
public partial class DataModelDynamicView : UserControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="DataModelDynamicView" /> class
|
||||
/// </summary>
|
||||
public DataModelDynamicView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void PropertyButton_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// DataContext is not set when using left button, I don't know why but there it is
|
||||
if (sender is Button button && button.ContextMenu != null)
|
||||
{
|
||||
button.ContextMenu.DataContext = button.DataContext;
|
||||
button.ContextMenu.IsOpen = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,387 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Timers;
|
||||
using System.Windows.Media;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Modules;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using MaterialDesignColors.ColorManipulation;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Shared.Input
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a view model that allows selecting a data model property used by boolean operations on a certain data
|
||||
/// model property
|
||||
/// </summary>
|
||||
public class DataModelDynamicViewModel : PropertyChangedBase, IDisposable
|
||||
{
|
||||
private readonly List<Module> _modules;
|
||||
private readonly IDataModelUIService _dataModelUIService;
|
||||
private readonly Timer _updateTimer;
|
||||
private SolidColorBrush _buttonBrush = new(Color.FromRgb(171, 71, 188));
|
||||
private DataModelPath? _dataModelPath;
|
||||
private DataModelPropertiesViewModel? _dataModelViewModel;
|
||||
private bool _displaySwitchButton;
|
||||
private Type[] _filterTypes = Array.Empty<Type>();
|
||||
private bool _isDataModelViewModelOpen;
|
||||
private bool _isEnabled = true;
|
||||
private string _placeholder = "Select a property";
|
||||
private readonly PluginSetting<bool> _showFullPath;
|
||||
|
||||
internal DataModelDynamicViewModel(List<Module> modules, ISettingsService settingsService, IDataModelUIService dataModelUIService)
|
||||
{
|
||||
_modules = modules;
|
||||
_dataModelUIService = dataModelUIService;
|
||||
_updateTimer = new Timer(500);
|
||||
_showFullPath = settingsService.GetSetting("ProfileEditor.ShowFullPaths", true);
|
||||
|
||||
ExtraDataModelViewModels = new BindableCollection<DataModelPropertiesViewModel>();
|
||||
ShowDataModelValues = settingsService.GetSetting<bool>("ProfileEditor.ShowDataModelValues");
|
||||
SelectPropertyCommand = new DelegateCommand(ExecuteSelectPropertyCommand);
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the brush to use for the input button
|
||||
/// </summary>
|
||||
public SolidColorBrush ButtonBrush
|
||||
{
|
||||
get => _buttonBrush;
|
||||
set
|
||||
{
|
||||
if (!SetAndNotify(ref _buttonBrush, value)) return;
|
||||
NotifyOfPropertyChange(nameof(SwitchButtonBrush));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the brush to use for the switch button
|
||||
/// </summary>
|
||||
public SolidColorBrush SwitchButtonBrush => new(ButtonBrush.Color.Darken());
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the placeholder text when no value is entered
|
||||
/// </summary>
|
||||
public string Placeholder
|
||||
{
|
||||
get => _placeholder;
|
||||
set => SetAndNotify(ref _placeholder, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the enabled state of the input
|
||||
/// </summary>
|
||||
public bool IsEnabled
|
||||
{
|
||||
get => _isEnabled;
|
||||
set => SetAndNotify(ref _isEnabled, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the switch button should be displayed
|
||||
/// </summary>
|
||||
public bool DisplaySwitchButton
|
||||
{
|
||||
get => _displaySwitchButton;
|
||||
set => SetAndNotify(ref _displaySwitchButton, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the types of properties this view model will allow to be selected
|
||||
/// </summary>
|
||||
public Type[] FilterTypes
|
||||
{
|
||||
get => _filterTypes;
|
||||
set
|
||||
{
|
||||
if (!SetAndNotify(ref _filterTypes, value)) return;
|
||||
DataModelViewModel?.ApplyTypeFilter(true, FilterTypes);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a bindable collection of extra data model view models to show
|
||||
/// </summary>
|
||||
public BindableCollection<DataModelPropertiesViewModel> ExtraDataModelViewModels { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether there are any modules providing data models
|
||||
/// </summary>
|
||||
public bool HasNoModules => (DataModelViewModel == null || !DataModelViewModel.Children.Any()) && !HasExtraDataModels;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether there are any extra data models
|
||||
/// </summary>
|
||||
public bool HasExtraDataModels => ExtraDataModelViewModels.Any();
|
||||
|
||||
/// <summary>
|
||||
/// Command used by view
|
||||
/// </summary>
|
||||
public DelegateCommand SelectPropertyCommand { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Setting used by view
|
||||
/// </summary>
|
||||
public PluginSetting<bool> ShowDataModelValues { get; }
|
||||
|
||||
public PluginSetting<bool> ShowFullPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets root the data model view model
|
||||
/// </summary>
|
||||
public DataModelPropertiesViewModel? DataModelViewModel
|
||||
{
|
||||
get => _dataModelViewModel;
|
||||
private set => SetAndNotify(ref _dataModelViewModel, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether the data model is open
|
||||
/// </summary>
|
||||
public bool IsDataModelViewModelOpen
|
||||
{
|
||||
get => _isDataModelViewModelOpen;
|
||||
set
|
||||
{
|
||||
if (!SetAndNotify(ref _isDataModelViewModelOpen, value)) return;
|
||||
if (value)
|
||||
{
|
||||
UpdateDataModelVisualization();
|
||||
if (DataModelViewModel != null)
|
||||
OpenSelectedValue(DataModelViewModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the data model path of the currently selected data model property
|
||||
/// </summary>
|
||||
public DataModelPath? DataModelPath
|
||||
{
|
||||
get => _dataModelPath;
|
||||
private set
|
||||
{
|
||||
if (!SetAndNotify(ref _dataModelPath, value)) return;
|
||||
NotifyOfPropertyChange(nameof(IsValid));
|
||||
NotifyOfPropertyChange(nameof(DisplayValue));
|
||||
NotifyOfPropertyChange(nameof(DisplayPath));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether the current selection is valid
|
||||
/// </summary>
|
||||
public bool IsValid => DataModelPath?.IsValid ?? true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the display name of the currently selected property
|
||||
/// </summary>
|
||||
public string? DisplayValue
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_showFullPath.Value)
|
||||
return DisplayPath;
|
||||
return DataModelPath?.GetPropertyDescription()?.Name ?? DataModelPath?.Segments.LastOrDefault()?.Identifier;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the human readable path of the currently selected property
|
||||
/// </summary>
|
||||
public string DisplayPath
|
||||
{
|
||||
get
|
||||
{
|
||||
if (DataModelPath == null)
|
||||
return "Click to select a property";
|
||||
if (!DataModelPath.IsValid)
|
||||
return "Invalid path";
|
||||
|
||||
return string.Join(" › ", DataModelPath.Segments.Where(s => s.GetPropertyDescription()!= null).Select(s => s.GetPropertyDescription()!.Name));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether event child VMs should be loaded, defaults to <see langword="true" />
|
||||
/// </summary>
|
||||
public bool LoadEventChildren { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Changes the root data model VM stored in <see cref="DataModelViewModel" /> to the provided
|
||||
/// <paramref name="dataModel" />
|
||||
/// </summary>
|
||||
/// <param name="dataModel">The data model VM to set the new root data model to</param>
|
||||
public void ChangeDataModel(DataModelPropertiesViewModel dataModel)
|
||||
{
|
||||
if (DataModelViewModel != null)
|
||||
DataModelViewModel.UpdateRequested -= DataModelOnUpdateRequested;
|
||||
|
||||
DataModelViewModel = dataModel;
|
||||
|
||||
if (DataModelViewModel != null)
|
||||
DataModelViewModel.UpdateRequested += DataModelOnUpdateRequested;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the currently selected property by its path
|
||||
/// </summary>
|
||||
/// <param name="dataModelPath">The path of the property to set the selection to</param>
|
||||
public void ChangeDataModelPath(DataModelPath? dataModelPath)
|
||||
{
|
||||
DataModelPath?.Dispose();
|
||||
DataModelPath = dataModelPath != null ? new DataModelPath(dataModelPath) : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Requests switching the input type to static using a <see cref="DataModelStaticViewModel" />
|
||||
/// </summary>
|
||||
public void SwitchToStatic()
|
||||
{
|
||||
ChangeDataModelPath(null);
|
||||
OnPropertySelected(new DataModelInputDynamicEventArgs(null));
|
||||
OnSwitchToStaticRequested();
|
||||
}
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
// Get the data models
|
||||
DataModelViewModel = _dataModelUIService.GetPluginDataModelVisualization(_modules, true);
|
||||
if (DataModelViewModel != null)
|
||||
DataModelViewModel.UpdateRequested += DataModelOnUpdateRequested;
|
||||
ExtraDataModelViewModels.CollectionChanged += ExtraDataModelViewModelsOnCollectionChanged;
|
||||
_updateTimer.Start();
|
||||
_updateTimer.Elapsed += OnUpdateTimerOnElapsed;
|
||||
_showFullPath.SettingChanged += ShowFullPathOnSettingChanged;
|
||||
}
|
||||
|
||||
|
||||
private void ExecuteSelectPropertyCommand(object? context)
|
||||
{
|
||||
if (context is not DataModelVisualizationViewModel selected)
|
||||
return;
|
||||
|
||||
ChangeDataModelPath(selected.DataModelPath);
|
||||
OnPropertySelected(new DataModelInputDynamicEventArgs(DataModelPath));
|
||||
}
|
||||
|
||||
#region IDisposable
|
||||
|
||||
/// <summary>
|
||||
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing">
|
||||
/// <see langword="true" /> to release both managed and unmanaged resources;
|
||||
/// <see langword="false" /> to release only unmanaged resources.
|
||||
/// </param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_updateTimer.Stop();
|
||||
_updateTimer.Dispose();
|
||||
_updateTimer.Elapsed -= OnUpdateTimerOnElapsed;
|
||||
_showFullPath.SettingChanged -= ShowFullPathOnSettingChanged;
|
||||
|
||||
DataModelViewModel?.Dispose();
|
||||
DataModelPath?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event handlers
|
||||
|
||||
private void DataModelOnUpdateRequested(object? sender, EventArgs e)
|
||||
{
|
||||
DataModelViewModel?.ApplyTypeFilter(true, FilterTypes);
|
||||
foreach (DataModelPropertiesViewModel extraDataModelViewModel in ExtraDataModelViewModels)
|
||||
extraDataModelViewModel.ApplyTypeFilter(true, FilterTypes);
|
||||
}
|
||||
|
||||
private void ExtraDataModelViewModelsOnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(HasExtraDataModels));
|
||||
}
|
||||
|
||||
private void OnUpdateTimerOnElapsed(object? sender, ElapsedEventArgs e)
|
||||
{
|
||||
if (!IsDataModelViewModelOpen)
|
||||
return;
|
||||
|
||||
UpdateDataModelVisualization();
|
||||
}
|
||||
|
||||
private void UpdateDataModelVisualization()
|
||||
{
|
||||
DataModelViewModel?.Update(_dataModelUIService, new DataModelUpdateConfiguration(LoadEventChildren));
|
||||
foreach (DataModelPropertiesViewModel extraDataModelViewModel in ExtraDataModelViewModels)
|
||||
extraDataModelViewModel.Update(_dataModelUIService, new DataModelUpdateConfiguration(LoadEventChildren));
|
||||
}
|
||||
|
||||
private void OpenSelectedValue(DataModelVisualizationViewModel dataModelPropertiesViewModel)
|
||||
{
|
||||
if (DataModelPath == null)
|
||||
return;
|
||||
|
||||
if (dataModelPropertiesViewModel.Children.Any(c => c.DataModelPath != null && DataModelPath.Path.StartsWith(c.DataModelPath.Path)))
|
||||
{
|
||||
dataModelPropertiesViewModel.IsVisualizationExpanded = true;
|
||||
foreach (DataModelVisualizationViewModel dataModelVisualizationViewModel in dataModelPropertiesViewModel.Children)
|
||||
{
|
||||
OpenSelectedValue(dataModelVisualizationViewModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowFullPathOnSettingChanged(object? sender, EventArgs e)
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(DisplayValue));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when anew property has been selected
|
||||
/// </summary>
|
||||
public event EventHandler<DataModelInputDynamicEventArgs>? PropertySelected;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a switch to static input has been requested
|
||||
/// </summary>
|
||||
public event EventHandler? SwitchToStaticRequested;
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="PropertySelected" /> event
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
protected virtual void OnPropertySelected(DataModelInputDynamicEventArgs e)
|
||||
{
|
||||
PropertySelected?.Invoke(this, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="SwitchToStaticRequested" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnSwitchToStaticRequested()
|
||||
{
|
||||
SwitchToStaticRequested?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
<UserControl x:Class="Artemis.UI.Shared.Input.DataModelStaticView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:input="clr-namespace:Artemis.UI.Shared.Input"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
xmlns:shared="clr-namespace:Artemis.UI.Shared"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance input:DataModelStaticViewModel}">
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/Artemis.UI.Shared;component/ResourceDictionaries/DataModelConditions.xaml" />
|
||||
<ResourceDictionary>
|
||||
<shared:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
|
||||
<Style x:Key="DataModelConditionButtonCornerToggle" BasedOn="{StaticResource DataModelConditionButton}" TargetType="Button">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding DisplaySwitchButton}" Value="True">
|
||||
<Setter Property="materialDesign:ButtonAssist.CornerRadius" Value="0 2 2 0" />
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding DisplaySwitchButton}" Value="False">
|
||||
<Setter Property="materialDesign:ButtonAssist.CornerRadius" Value="2" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<StackPanel Orientation="Horizontal" Visibility="{Binding InputViewModel, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted}">
|
||||
<Button Style="{StaticResource MaterialDesignFlatDarkBgButton}"
|
||||
Background="{Binding SwitchButtonBrush}"
|
||||
BorderBrush="{Binding SwitchButtonBrush}"
|
||||
Height="22"
|
||||
Padding="0"
|
||||
Width="22"
|
||||
FontSize="12"
|
||||
materialDesign:ButtonAssist.CornerRadius="2 0 0 2"
|
||||
Margin="3 0 -3 0"
|
||||
HorizontalAlignment="Left"
|
||||
ToolTip="Switch to data model value"
|
||||
Command="{s:Action SwitchToDynamic}"
|
||||
Visibility="{Binding DisplaySwitchButton, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
|
||||
<materialDesign:PackIcon Kind="SwapHorizontal" />
|
||||
</Button>
|
||||
<Button Style="{StaticResource DataModelConditionButtonCornerToggle}"
|
||||
Background="{Binding ButtonBrush}"
|
||||
BorderBrush="{Binding ButtonBrush}"
|
||||
Command="{s:Action ActivateInputViewModel}"
|
||||
HorizontalAlignment="Left"
|
||||
IsEnabled="{Binding IsEnabled}"
|
||||
ToolTip="Click to edit value">
|
||||
<Grid>
|
||||
<StackPanel Visibility="{Binding Value, Converter={StaticResource NullToVisibilityConverter}}" Orientation="Horizontal">
|
||||
<ContentControl s:View.Model="{Binding DisplayViewModel}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" />
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock FontStyle="Italic" Visibility="{Binding Value, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted}">
|
||||
<Run Text="« " /><Run Text="{Binding Placeholder}" /><Run Text=" »" />
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<Border BorderBrush="{Binding ButtonBrush}"
|
||||
Background="{DynamicResource MaterialDesignPaper}"
|
||||
Visibility="{Binding InputViewModel, Converter={StaticResource NullToVisibilityConverter}}"
|
||||
CornerRadius="3"
|
||||
Padding="3"
|
||||
Margin="0 -3"
|
||||
HorizontalAlignment="Left"
|
||||
MinWidth="140">
|
||||
<ContentControl s:View.Model="{Binding InputViewModel}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" />
|
||||
</Border>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -1,285 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Modules;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using MaterialDesignColors.ColorManipulation;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Shared.Input
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a view model that allows inputting a static value used by boolean operations on a certain data model
|
||||
/// property
|
||||
/// </summary>
|
||||
public class DataModelStaticViewModel : PropertyChangedBase, IDisposable
|
||||
{
|
||||
private readonly IDataModelUIService _dataModelUIService;
|
||||
private readonly Window? _rootView;
|
||||
private SolidColorBrush _buttonBrush = new(Color.FromRgb(171, 71, 188));
|
||||
private bool _displaySwitchButton;
|
||||
private DataModelDisplayViewModel? _displayViewModel;
|
||||
private DataModelInputViewModel? _inputViewModel;
|
||||
private bool _isEnabled;
|
||||
private string _placeholder = "Enter a value";
|
||||
private DataModelPropertyAttribute _targetDescription;
|
||||
private Type _targetType;
|
||||
private object? _value;
|
||||
|
||||
internal DataModelStaticViewModel(Type targetType, DataModelPropertyAttribute targetDescription, IDataModelUIService dataModelUIService)
|
||||
{
|
||||
_dataModelUIService = dataModelUIService;
|
||||
_rootView = Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive);
|
||||
|
||||
_targetType = targetType;
|
||||
_targetDescription = targetDescription;
|
||||
_isEnabled = TargetType != null;
|
||||
_displayViewModel = _dataModelUIService.GetDataModelDisplayViewModel(TargetType ?? typeof(object), TargetDescription, true);
|
||||
|
||||
if (_rootView != null)
|
||||
{
|
||||
_rootView.MouseUp += RootViewOnMouseUp;
|
||||
_rootView.KeyUp += RootViewOnKeyUp;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the brush to use for the input button
|
||||
/// </summary>
|
||||
public SolidColorBrush ButtonBrush
|
||||
{
|
||||
get => _buttonBrush;
|
||||
set
|
||||
{
|
||||
if (!SetAndNotify(ref _buttonBrush, value)) return;
|
||||
NotifyOfPropertyChange(nameof(SwitchButtonBrush));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the brush to use for the switch button
|
||||
/// </summary>
|
||||
public SolidColorBrush SwitchButtonBrush => new(ButtonBrush.Color.Darken());
|
||||
|
||||
/// <summary>
|
||||
/// Gets the view model used to display the value
|
||||
/// </summary>
|
||||
public DataModelDisplayViewModel? DisplayViewModel
|
||||
{
|
||||
get => _displayViewModel;
|
||||
private set => SetAndNotify(ref _displayViewModel, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the view model used to edit the value
|
||||
/// </summary>
|
||||
public DataModelInputViewModel? InputViewModel
|
||||
{
|
||||
get => _inputViewModel;
|
||||
private set => SetAndNotify(ref _inputViewModel, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the target property
|
||||
/// </summary>
|
||||
public Type TargetType
|
||||
{
|
||||
get => _targetType;
|
||||
private set => SetAndNotify(ref _targetType, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description of the target property
|
||||
/// </summary>
|
||||
public DataModelPropertyAttribute TargetDescription
|
||||
{
|
||||
get => _targetDescription;
|
||||
set => SetAndNotify(ref _targetDescription, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value of the target
|
||||
/// </summary>
|
||||
public object? Value
|
||||
{
|
||||
get => _value;
|
||||
set
|
||||
{
|
||||
if (!SetAndNotify(ref _value, value)) return;
|
||||
DisplayViewModel?.UpdateValue(_value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the placeholder text when no value is entered
|
||||
/// </summary>
|
||||
public string Placeholder
|
||||
{
|
||||
get => _placeholder;
|
||||
set => SetAndNotify(ref _placeholder, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the enabled state of the input
|
||||
/// </summary>
|
||||
public bool IsEnabled
|
||||
{
|
||||
get => _isEnabled;
|
||||
private set => SetAndNotify(ref _isEnabled, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the switch button should be displayed
|
||||
/// </summary>
|
||||
public bool DisplaySwitchButton
|
||||
{
|
||||
get => _displaySwitchButton;
|
||||
set => SetAndNotify(ref _displaySwitchButton, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Activates the input view model
|
||||
/// </summary>
|
||||
public void ActivateInputViewModel()
|
||||
{
|
||||
InputViewModel = _dataModelUIService.GetDataModelInputViewModel(
|
||||
TargetType,
|
||||
TargetDescription,
|
||||
Value,
|
||||
ApplyFreeInput
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the target type
|
||||
/// </summary>
|
||||
/// <param name="target">The new target type</param>
|
||||
public void UpdateTargetType(Type target)
|
||||
{
|
||||
TargetType = target;
|
||||
DisplayViewModel = _dataModelUIService.GetDataModelDisplayViewModel(TargetType ?? typeof(object), TargetDescription, true);
|
||||
IsEnabled = TargetType != null;
|
||||
|
||||
// If null, clear the input
|
||||
if (TargetType == null)
|
||||
{
|
||||
ApplyFreeInput(null, true);
|
||||
return;
|
||||
}
|
||||
|
||||
// If the type changed, reset to the default type
|
||||
if (Value == null || !TargetType.IsCastableFrom(Value.GetType()))
|
||||
// Force the VM to close if it was open and apply the new value
|
||||
ApplyFreeInput(TargetType.GetDefault(), true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Requests switching the input type to dynamic using a <see cref="DataModelDynamicViewModel"/>
|
||||
/// </summary>
|
||||
public void SwitchToDynamic()
|
||||
{
|
||||
InputViewModel?.Cancel();
|
||||
ApplyFreeInput(TargetType.GetDefault(), true);
|
||||
|
||||
OnSwitchToDynamicRequested();
|
||||
}
|
||||
|
||||
private void ApplyFreeInput(object? value, bool submitted)
|
||||
{
|
||||
if (submitted)
|
||||
OnValueUpdated(new DataModelInputStaticEventArgs(value));
|
||||
|
||||
InputViewModel = null;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
#region IDisposable
|
||||
|
||||
/// <summary>
|
||||
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing">
|
||||
/// <see langword="true" /> to release both managed and unmanaged resources;
|
||||
/// <see langword="false" /> to release only unmanaged resources.
|
||||
/// </param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (_rootView != null)
|
||||
{
|
||||
_rootView.MouseUp -= RootViewOnMouseUp;
|
||||
_rootView.KeyUp -= RootViewOnKeyUp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Event handlers
|
||||
|
||||
private void RootViewOnKeyUp(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (InputViewModel == null)
|
||||
return;
|
||||
|
||||
if (e.Key == Key.Escape)
|
||||
InputViewModel.Cancel();
|
||||
else if (e.Key == Key.Enter)
|
||||
InputViewModel.Submit();
|
||||
}
|
||||
|
||||
private void RootViewOnMouseUp(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (InputViewModel == null)
|
||||
return;
|
||||
|
||||
if (sender is FrameworkElement frameworkElement && !frameworkElement.IsDescendantOf(InputViewModel.View))
|
||||
InputViewModel.Submit();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the value of the property has been updated
|
||||
/// </summary>
|
||||
public event EventHandler<DataModelInputStaticEventArgs>? ValueUpdated;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a switch to dynamic input has been requested
|
||||
/// </summary>
|
||||
public event EventHandler? SwitchToDynamicRequested;
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="ValueUpdated" /> event
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
protected virtual void OnValueUpdated(DataModelInputStaticEventArgs e)
|
||||
{
|
||||
ValueUpdated?.Invoke(this, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="SwitchToDynamicRequested" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnSwitchToDynamicRequested()
|
||||
{
|
||||
SwitchToDynamicRequested?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
-5
@@ -11,15 +11,12 @@ namespace Artemis.UI.Shared
|
||||
/// </summary>
|
||||
public class DataModelListPropertiesViewModel : DataModelPropertiesViewModel
|
||||
{
|
||||
private readonly ListPredicateWrapperDataModel _listPredicateWrapper;
|
||||
private object? _displayValue;
|
||||
private int _index;
|
||||
private Type? _listType;
|
||||
|
||||
internal DataModelListPropertiesViewModel(Type listType, string? name) : base(null, null, null)
|
||||
{
|
||||
_listPredicateWrapper = ListPredicateWrapperDataModel.Create(listType, name);
|
||||
DataModel = _listPredicateWrapper;
|
||||
ListType = listType;
|
||||
}
|
||||
|
||||
@@ -61,8 +58,6 @@ namespace Artemis.UI.Shared
|
||||
/// <inheritdoc />
|
||||
public override void Update(IDataModelUIService dataModelUIService, DataModelUpdateConfiguration? configuration)
|
||||
{
|
||||
_listPredicateWrapper.UntypedValue = DisplayValue;
|
||||
|
||||
PopulateProperties(dataModelUIService, configuration);
|
||||
if (DisplayViewModel == null)
|
||||
return;
|
||||
|
||||
@@ -10,22 +10,17 @@ namespace Artemis.UI.Shared
|
||||
/// </summary>
|
||||
public class DataModelListPropertyViewModel : DataModelPropertyViewModel
|
||||
{
|
||||
private readonly ListPredicateWrapperDataModel _listPredicateWrapper;
|
||||
private int _index;
|
||||
private Type? _listType;
|
||||
|
||||
internal DataModelListPropertyViewModel(Type listType, DataModelDisplayViewModel displayViewModel, string? name) : base(null, null, null)
|
||||
{
|
||||
_listPredicateWrapper = ListPredicateWrapperDataModel.Create(listType, name);
|
||||
DataModel = _listPredicateWrapper;
|
||||
ListType = listType;
|
||||
DisplayViewModel = displayViewModel;
|
||||
}
|
||||
|
||||
internal DataModelListPropertyViewModel(Type listType, string? name) : base(null, null, null)
|
||||
{
|
||||
_listPredicateWrapper = ListPredicateWrapperDataModel.Create(listType, name);
|
||||
DataModel = _listPredicateWrapper;
|
||||
ListType = listType;
|
||||
}
|
||||
|
||||
@@ -60,8 +55,6 @@ namespace Artemis.UI.Shared
|
||||
if (DisplayValue == null)
|
||||
return;
|
||||
|
||||
_listPredicateWrapper.UntypedValue = DisplayValue;
|
||||
|
||||
if (DisplayViewModel == null)
|
||||
DisplayViewModel = dataModelUIService.GetDataModelDisplayViewModel(DisplayValue.GetType(), PropertyDescription, true);
|
||||
|
||||
|
||||
+1
-1
@@ -247,7 +247,7 @@ namespace Artemis.UI.Shared
|
||||
object? value = Parent == null || Parent.IsRootViewModel ? DataModel : DataModelPath?.GetValue();
|
||||
if (value is DataModel dataModel)
|
||||
{
|
||||
foreach (var (key, dynamicChild) in dataModel.DynamicChildren)
|
||||
foreach (string key in dataModel.DynamicChildren.Keys.ToList())
|
||||
{
|
||||
string childPath = AppendToPath(key);
|
||||
if (Children.Any(c => c.Path != null && c.Path.Equals(childPath)))
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
using System;
|
||||
using Artemis.UI.Shared.Input;
|
||||
|
||||
namespace Artemis.UI.Shared
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides data about submit events raised by <see cref="DataModelStaticViewModel" />
|
||||
/// </summary>
|
||||
public class DataModelInputStaticEventArgs : EventArgs
|
||||
{
|
||||
internal DataModelInputStaticEventArgs(object? value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The value that was submitted
|
||||
/// </summary>
|
||||
public object? Value { get; }
|
||||
}
|
||||
}
|
||||
+9
-9
@@ -1,22 +1,22 @@
|
||||
using System;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Shared.Input;
|
||||
using Artemis.UI.Shared.Controls;
|
||||
|
||||
namespace Artemis.UI.Shared
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides data about selection events raised by <see cref="DataModelDynamicViewModel" />
|
||||
/// Provides data about selection events raised by <see cref="DataModelPicker" />
|
||||
/// </summary>
|
||||
public class DataModelInputDynamicEventArgs : EventArgs
|
||||
public class DataModelSelectedEventArgs : EventArgs
|
||||
{
|
||||
internal DataModelInputDynamicEventArgs(DataModelPath? dataModelPath)
|
||||
{
|
||||
DataModelPath = dataModelPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data model path that was selected
|
||||
/// </summary>
|
||||
public DataModelPath? DataModelPath { get; }
|
||||
public DataModelPath? Path { get; }
|
||||
|
||||
internal DataModelSelectedEventArgs(DataModelPath? path)
|
||||
{
|
||||
Path = path;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
using System.Linq;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Shared.Services;
|
||||
|
||||
namespace Artemis.UI.Shared
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extensions for special data model wrappers used by events and list conditions
|
||||
/// </summary>
|
||||
public static class DataModelWrapperExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a view model for a <see cref="EventPredicateWrapperDataModel" />
|
||||
/// </summary>
|
||||
/// <param name="wrapper">The wrapper to create the view model for</param>
|
||||
/// <param name="dataModelUIService">The data model UI service to be used by the view model</param>
|
||||
/// <param name="configuration">The update configuration to be used by the view model</param>
|
||||
/// <returns>The created view model</returns>
|
||||
public static DataModelPropertiesViewModel CreateViewModel(this EventPredicateWrapperDataModel wrapper, IDataModelUIService dataModelUIService, DataModelUpdateConfiguration configuration)
|
||||
{
|
||||
DataModelPropertiesViewModel viewModel = new(wrapper, null, new DataModelPath(wrapper));
|
||||
viewModel.Update(dataModelUIService, configuration);
|
||||
viewModel.UpdateRequested += (sender, args) => viewModel.Update(dataModelUIService, configuration);
|
||||
viewModel.Children.First().IsVisualizationExpanded = true;
|
||||
|
||||
return viewModel;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a view model for a <see cref="ListPredicateWrapperDataModel" />
|
||||
/// </summary>
|
||||
/// <param name="wrapper">The wrapper to create the view model for</param>
|
||||
/// <param name="dataModelUIService">The data model UI service to be used by the view model</param>
|
||||
/// <param name="configuration">The update configuration to be used by the view model</param>
|
||||
/// <returns>The created view model</returns>
|
||||
public static DataModelPropertiesViewModel CreateViewModel(this ListPredicateWrapperDataModel wrapper, IDataModelUIService dataModelUIService, DataModelUpdateConfiguration configuration)
|
||||
{
|
||||
DataModelPropertiesViewModel viewModel = new(wrapper, null, new DataModelPath(wrapper));
|
||||
viewModel.Update(dataModelUIService, configuration);
|
||||
viewModel.UpdateRequested += (sender, args) => viewModel.Update(dataModelUIService, configuration);
|
||||
viewModel.Children.First().IsVisualizationExpanded = true;
|
||||
|
||||
return viewModel;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Artemis.Core.Modules;
|
||||
using Artemis.UI.Shared.Input;
|
||||
|
||||
namespace Artemis.UI.Shared
|
||||
namespace Artemis.UI.Shared
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a factory for view models provided by the Artemis Shared UI library
|
||||
@@ -11,25 +6,4 @@ namespace Artemis.UI.Shared
|
||||
public interface ISharedVmFactory
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A factory that allows the creation of data model view models
|
||||
/// </summary>
|
||||
public interface IDataModelVmFactory : ISharedVmFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="DataModelDynamicViewModel" /> class
|
||||
/// </summary>
|
||||
/// <param name="modules">The modules to associate the dynamic view model with</param>
|
||||
/// <returns>A new instance of the <see cref="DataModelDynamicViewModel" /> class</returns>
|
||||
DataModelDynamicViewModel DataModelDynamicViewModel(List<Module> modules);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="DataModelStaticViewModel" /> class
|
||||
/// </summary>
|
||||
/// <param name="targetType">The type of property that is expected in this input</param>
|
||||
/// <param name="targetDescription">The description of the property that this input is for</param>
|
||||
/// <returns>A new instance of the <see cref="DataModelStaticViewModel" /> class</returns>
|
||||
DataModelStaticViewModel DataModelStaticViewModel(Type targetType, DataModelPropertyAttribute targetDescription);
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,11 @@ namespace Artemis.UI.Shared
|
||||
/// </summary>
|
||||
public LayerProperty<T> LayerProperty { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether the layer property should be enabled
|
||||
/// </summary>
|
||||
public bool IsEnabled => !LayerProperty.HasDataBinding;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the profile editor service
|
||||
/// </summary>
|
||||
@@ -85,8 +90,8 @@ namespace Artemis.UI.Shared
|
||||
{
|
||||
LayerProperty.Updated += LayerPropertyOnUpdated;
|
||||
LayerProperty.CurrentValueSet += LayerPropertyOnUpdated;
|
||||
LayerProperty.DataBindingEnabled += LayerPropertyOnDataBindingChange;
|
||||
LayerProperty.DataBindingDisabled += LayerPropertyOnDataBindingChange;
|
||||
LayerProperty.DataBinding.DataBindingEnabled += OnDataBindingChange;
|
||||
LayerProperty.DataBinding.DataBindingDisabled += OnDataBindingChange;
|
||||
UpdateInputValue();
|
||||
base.OnInitialActivate();
|
||||
}
|
||||
@@ -96,8 +101,8 @@ namespace Artemis.UI.Shared
|
||||
{
|
||||
LayerProperty.Updated -= LayerPropertyOnUpdated;
|
||||
LayerProperty.CurrentValueSet -= LayerPropertyOnUpdated;
|
||||
LayerProperty.DataBindingEnabled -= LayerPropertyOnDataBindingChange;
|
||||
LayerProperty.DataBindingDisabled -= LayerPropertyOnDataBindingChange;
|
||||
LayerProperty.DataBinding.DataBindingEnabled -= OnDataBindingChange;
|
||||
LayerProperty.DataBinding.DataBindingDisabled -= OnDataBindingChange;
|
||||
base.OnClose();
|
||||
}
|
||||
|
||||
@@ -194,8 +199,9 @@ namespace Artemis.UI.Shared
|
||||
UpdateInputValue();
|
||||
}
|
||||
|
||||
private void LayerPropertyOnDataBindingChange(object? sender, LayerPropertyEventArgs e)
|
||||
private void OnDataBindingChange(object? sender, DataBindingEventArgs e)
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(IsEnabled));
|
||||
OnDataBindingsChanged();
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ using Artemis.Core;
|
||||
using Artemis.Core.Modules;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Shared.DefaultTypes.DataModel.Display;
|
||||
using Artemis.UI.Shared.Input;
|
||||
using Ninject;
|
||||
using Ninject.Parameters;
|
||||
|
||||
@@ -15,15 +14,13 @@ namespace Artemis.UI.Shared.Services
|
||||
internal class DataModelUIService : IDataModelUIService
|
||||
{
|
||||
private readonly IDataModelService _dataModelService;
|
||||
private readonly IDataModelVmFactory _dataModelVmFactory;
|
||||
private readonly IKernel _kernel;
|
||||
private readonly List<DataModelVisualizationRegistration> _registeredDataModelDisplays;
|
||||
private readonly List<DataModelVisualizationRegistration> _registeredDataModelEditors;
|
||||
|
||||
public DataModelUIService(IDataModelService dataModelService, IDataModelVmFactory dataModelVmFactory, IKernel kernel)
|
||||
public DataModelUIService(IDataModelService dataModelService, IKernel kernel)
|
||||
{
|
||||
_dataModelService = dataModelService;
|
||||
_dataModelVmFactory = dataModelVmFactory;
|
||||
_kernel = kernel;
|
||||
_registeredDataModelEditors = new List<DataModelVisualizationRegistration>();
|
||||
_registeredDataModelDisplays = new List<DataModelVisualizationRegistration>();
|
||||
@@ -227,22 +224,7 @@ namespace Artemis.UI.Shared.Services
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public DataModelDynamicViewModel GetDynamicSelectionViewModel(Module? module)
|
||||
{
|
||||
return _dataModelVmFactory.DataModelDynamicViewModel(module == null ? new List<Module>() : new List<Module> {module});
|
||||
}
|
||||
|
||||
public DataModelDynamicViewModel GetDynamicSelectionViewModel(List<Module> modules)
|
||||
{
|
||||
return _dataModelVmFactory.DataModelDynamicViewModel(modules);
|
||||
}
|
||||
|
||||
public DataModelStaticViewModel GetStaticInputViewModel(Type targetType, DataModelPropertyAttribute targetDescription)
|
||||
{
|
||||
return _dataModelVmFactory.DataModelStaticViewModel(targetType, targetDescription);
|
||||
}
|
||||
|
||||
|
||||
private DataModelInputViewModel InstantiateDataModelInputViewModel(DataModelVisualizationRegistration registration, DataModelPropertyAttribute? description, object? initialValue)
|
||||
{
|
||||
// This assumes the type can be converted, that has been checked when the VM was created
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using System.Collections.Generic;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Modules;
|
||||
using Artemis.UI.Shared.Input;
|
||||
|
||||
namespace Artemis.UI.Shared.Services
|
||||
{
|
||||
@@ -103,27 +102,5 @@ namespace Artemis.UI.Shared.Services
|
||||
/// <param name="updateCallback">A function to call whenever the input was updated (submitted or not)</param>
|
||||
/// <returns>The most appropriate input view model for the provided <paramref name="propertyType" /></returns>
|
||||
DataModelInputViewModel? GetDataModelInputViewModel(Type propertyType, DataModelPropertyAttribute? description, object? initialValue, Action<object?, bool> updateCallback);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a view model that allows selecting a value from the data model
|
||||
/// </summary>
|
||||
/// <param name="module">An extra non-always active module to include</param>
|
||||
/// <returns></returns>
|
||||
DataModelDynamicViewModel GetDynamicSelectionViewModel(Module? module);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a view model that allows selecting a value from the data model
|
||||
/// </summary>
|
||||
/// <param name="modules">A list of extra extra non-always active modules to include</param>
|
||||
/// <returns>A view model that allows selecting a value from the data model</returns>
|
||||
DataModelDynamicViewModel GetDynamicSelectionViewModel(List<Module> modules);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a view model that allows entering a value matching the target data model type
|
||||
/// </summary>
|
||||
/// <param name="targetType">The type of data model property to allow input for</param>
|
||||
/// <param name="targetDescription">The description of the target data model property</param>
|
||||
/// <returns>A view model that allows entering a value matching the target data model type</returns>
|
||||
DataModelStaticViewModel GetStaticInputViewModel(Type targetType, DataModelPropertyAttribute targetDescription);
|
||||
}
|
||||
}
|
||||
@@ -37,9 +37,9 @@ namespace Artemis.UI.Shared.Services
|
||||
RenderProfileElement? SelectedProfileElement { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the currently selected data binding property
|
||||
/// Gets the currently selected data binding
|
||||
/// </summary>
|
||||
ILayerProperty? SelectedDataBinding { get; }
|
||||
IDataBinding? SelectedDataBinding { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the current time
|
||||
@@ -89,10 +89,10 @@ namespace Artemis.UI.Shared.Services
|
||||
void SaveSelectedProfileElement();
|
||||
|
||||
/// <summary>
|
||||
/// Changes the selected data binding property
|
||||
/// Changes the selected data binding
|
||||
/// </summary>
|
||||
/// <param name="layerProperty">The data binding property to select</param>
|
||||
void ChangeSelectedDataBinding(ILayerProperty? layerProperty);
|
||||
/// <param name="dataBinding">The data binding to select</param>
|
||||
void ChangeSelectedDataBinding(IDataBinding? dataBinding);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the profile preview, forcing UI-elements to re-render
|
||||
@@ -114,7 +114,7 @@ namespace Artemis.UI.Shared.Services
|
||||
/// <summary>
|
||||
/// Registers a new property input view model used in the profile editor for the generic type defined in
|
||||
/// <see cref="PropertyInputViewModel{T}" />
|
||||
/// <para>Note: Registration will remove itself on plugin disable so you don't have to</para>
|
||||
/// <para>Note: DataBindingProperty will remove itself on plugin disable so you don't have to</para>
|
||||
/// </summary>
|
||||
/// <param name="plugin"></param>
|
||||
/// <returns></returns>
|
||||
@@ -123,7 +123,7 @@ namespace Artemis.UI.Shared.Services
|
||||
/// <summary>
|
||||
/// Registers a new property input view model used in the profile editor for the generic type defined in
|
||||
/// <see cref="PropertyInputViewModel{T}" />
|
||||
/// <para>Note: Registration will remove itself on plugin disable so you don't have to</para>
|
||||
/// <para>Note: DataBindingProperty will remove itself on plugin disable so you don't have to</para>
|
||||
/// </summary>
|
||||
/// <param name="viewModelType"></param>
|
||||
/// <param name="plugin"></param>
|
||||
|
||||
@@ -19,10 +19,10 @@ namespace Artemis.UI.Shared.Services
|
||||
{
|
||||
private readonly IKernel _kernel;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IModuleService _moduleService;
|
||||
private readonly IProfileService _profileService;
|
||||
private readonly List<PropertyInputRegistration> _registeredPropertyEditors;
|
||||
private readonly IRgbService _rgbService;
|
||||
private readonly IModuleService _moduleService;
|
||||
private readonly object _selectedProfileElementLock = new();
|
||||
private readonly object _selectedProfileLock = new();
|
||||
private TimeSpan _currentTime;
|
||||
@@ -30,7 +30,8 @@ namespace Artemis.UI.Shared.Services
|
||||
private int _pixelsPerSecond;
|
||||
private bool _suspendEditing;
|
||||
|
||||
public ProfileEditorService(IKernel kernel, ILogger logger, IProfileService profileService, ICoreService coreService, IRgbService rgbService, IModuleService moduleService)
|
||||
public ProfileEditorService(IKernel kernel, ILogger logger, ICoreService coreService, IProfileService profileService, IRgbService rgbService, IModuleService moduleService,
|
||||
INodeService nodeService)
|
||||
{
|
||||
_kernel = kernel;
|
||||
_logger = logger;
|
||||
@@ -41,9 +42,56 @@ namespace Artemis.UI.Shared.Services
|
||||
|
||||
RegisteredPropertyEditors = new(_registeredPropertyEditors);
|
||||
coreService.FrameRendered += CoreServiceOnFrameRendered;
|
||||
|
||||
TypeUtilities.NodeService = nodeService;
|
||||
PixelsPerSecond = 100;
|
||||
}
|
||||
|
||||
protected virtual void OnSelectedProfileChanged(ProfileConfigurationEventArgs e)
|
||||
{
|
||||
SelectedProfileChanged?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnSelectedProfileUpdated(ProfileConfigurationEventArgs e)
|
||||
{
|
||||
SelectedProfileSaved?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnSelectedProfileElementChanged(RenderProfileElementEventArgs e)
|
||||
{
|
||||
SelectedProfileElementChanged?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnSelectedProfileElementUpdated(RenderProfileElementEventArgs e)
|
||||
{
|
||||
SelectedProfileElementSaved?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnCurrentTimeChanged()
|
||||
{
|
||||
CurrentTimeChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
protected virtual void OnPixelsPerSecondChanged()
|
||||
{
|
||||
PixelsPerSecondChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
protected virtual void OnSuspendEditingChanged()
|
||||
{
|
||||
SuspendEditingChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
protected virtual void OnProfilePreviewUpdated()
|
||||
{
|
||||
ProfilePreviewUpdated?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
protected virtual void OnSelectedDataBindingChanged()
|
||||
{
|
||||
SelectedDataBindingChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void CoreServiceOnFrameRendered(object? sender, FrameRenderedEventArgs e)
|
||||
{
|
||||
if (!_doTick)
|
||||
@@ -70,7 +118,7 @@ namespace Artemis.UI.Shared.Services
|
||||
// Trigger selected data binding change
|
||||
if (SelectedDataBinding != null)
|
||||
{
|
||||
SelectedDataBinding = SelectedProfileElement?.GetAllLayerProperties().FirstOrDefault(p => p.Path == SelectedDataBinding.Path);
|
||||
SelectedDataBinding = SelectedProfileElement?.GetAllLayerProperties().FirstOrDefault(p => p.Path == SelectedDataBinding.BaseLayerProperty.Path)?.BaseDataBinding;
|
||||
OnSelectedDataBindingChanged();
|
||||
}
|
||||
|
||||
@@ -92,7 +140,9 @@ namespace Artemis.UI.Shared.Services
|
||||
return;
|
||||
|
||||
if (renderElement.Suspended)
|
||||
{
|
||||
renderElement.Disable();
|
||||
}
|
||||
else
|
||||
{
|
||||
renderElement.Enable();
|
||||
@@ -138,7 +188,7 @@ namespace Artemis.UI.Shared.Services
|
||||
public ProfileConfiguration? SelectedProfileConfiguration { get; private set; }
|
||||
public Profile? SelectedProfile => SelectedProfileConfiguration?.Profile;
|
||||
public RenderProfileElement? SelectedProfileElement { get; private set; }
|
||||
public ILayerProperty? SelectedDataBinding { get; private set; }
|
||||
public IDataBinding? SelectedDataBinding { get; private set; }
|
||||
|
||||
public TimeSpan CurrentTime
|
||||
{
|
||||
@@ -187,10 +237,10 @@ namespace Artemis.UI.Shared.Services
|
||||
// No need to deactivate the profile, if needed it will be deactivated next update
|
||||
if (SelectedProfileConfiguration != null)
|
||||
SelectedProfileConfiguration.IsBeingEdited = false;
|
||||
|
||||
|
||||
PreviousSelectedProfileConfiguration = SelectedProfileConfiguration;
|
||||
SelectedProfileConfiguration = profileConfiguration;
|
||||
|
||||
|
||||
// The new profile may need activation
|
||||
if (SelectedProfileConfiguration != null)
|
||||
{
|
||||
@@ -258,9 +308,9 @@ namespace Artemis.UI.Shared.Services
|
||||
}
|
||||
}
|
||||
|
||||
public void ChangeSelectedDataBinding(ILayerProperty? layerProperty)
|
||||
public void ChangeSelectedDataBinding(IDataBinding? dataBinding)
|
||||
{
|
||||
SelectedDataBinding = layerProperty;
|
||||
SelectedDataBinding = dataBinding;
|
||||
OnSelectedDataBindingChanged();
|
||||
}
|
||||
|
||||
@@ -323,10 +373,8 @@ namespace Artemis.UI.Shared.Services
|
||||
if (existing != null)
|
||||
{
|
||||
if (existing.Plugin != plugin)
|
||||
{
|
||||
throw new ArtemisSharedUIException($"Cannot register property editor for type {supportedType.Name} because an editor was already " +
|
||||
$"registered by {existing.Plugin}");
|
||||
}
|
||||
|
||||
return existing;
|
||||
}
|
||||
@@ -371,10 +419,8 @@ namespace Artemis.UI.Shared.Services
|
||||
|
||||
if (snapToCurrentTime)
|
||||
// Snap to the current time
|
||||
{
|
||||
if (Math.Abs(time.TotalMilliseconds - CurrentTime.TotalMilliseconds) < tolerance.TotalMilliseconds)
|
||||
return CurrentTime;
|
||||
}
|
||||
|
||||
if (snapTimes != null)
|
||||
{
|
||||
@@ -410,9 +456,13 @@ namespace Artemis.UI.Shared.Services
|
||||
viewModelType = registration.ViewModelType.MakeGenericType(layerProperty.GetType().GenericTypeArguments);
|
||||
}
|
||||
else if (registration != null)
|
||||
{
|
||||
viewModelType = registration.ViewModelType;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (viewModelType == null)
|
||||
return null;
|
||||
@@ -432,6 +482,16 @@ namespace Artemis.UI.Shared.Services
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public event EventHandler<ProfileConfigurationEventArgs>? SelectedProfileChanged;
|
||||
public event EventHandler<ProfileConfigurationEventArgs>? SelectedProfileSaved;
|
||||
public event EventHandler<RenderProfileElementEventArgs>? SelectedProfileElementChanged;
|
||||
public event EventHandler<RenderProfileElementEventArgs>? SelectedProfileElementSaved;
|
||||
public event EventHandler? SelectedDataBindingChanged;
|
||||
public event EventHandler? CurrentTimeChanged;
|
||||
public event EventHandler? PixelsPerSecondChanged;
|
||||
public event EventHandler? SuspendEditingChanged;
|
||||
public event EventHandler? ProfilePreviewUpdated;
|
||||
|
||||
#region Copy/paste
|
||||
|
||||
public ProfileElement? DuplicateProfileElement(ProfileElement profileElement)
|
||||
@@ -511,64 +571,5 @@ namespace Artemis.UI.Shared.Services
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
public event EventHandler<ProfileConfigurationEventArgs>? SelectedProfileChanged;
|
||||
public event EventHandler<ProfileConfigurationEventArgs>? SelectedProfileSaved;
|
||||
public event EventHandler<RenderProfileElementEventArgs>? SelectedProfileElementChanged;
|
||||
public event EventHandler<RenderProfileElementEventArgs>? SelectedProfileElementSaved;
|
||||
public event EventHandler? SelectedDataBindingChanged;
|
||||
public event EventHandler? CurrentTimeChanged;
|
||||
public event EventHandler? PixelsPerSecondChanged;
|
||||
public event EventHandler? SuspendEditingChanged;
|
||||
public event EventHandler? ProfilePreviewUpdated;
|
||||
|
||||
protected virtual void OnSelectedProfileChanged(ProfileConfigurationEventArgs e)
|
||||
{
|
||||
SelectedProfileChanged?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnSelectedProfileUpdated(ProfileConfigurationEventArgs e)
|
||||
{
|
||||
SelectedProfileSaved?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnSelectedProfileElementChanged(RenderProfileElementEventArgs e)
|
||||
{
|
||||
SelectedProfileElementChanged?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnSelectedProfileElementUpdated(RenderProfileElementEventArgs e)
|
||||
{
|
||||
SelectedProfileElementSaved?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnCurrentTimeChanged()
|
||||
{
|
||||
CurrentTimeChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
protected virtual void OnPixelsPerSecondChanged()
|
||||
{
|
||||
PixelsPerSecondChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
protected virtual void OnSuspendEditingChanged()
|
||||
{
|
||||
SuspendEditingChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
protected virtual void OnProfilePreviewUpdated()
|
||||
{
|
||||
ProfilePreviewUpdated?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
protected virtual void OnSelectedDataBindingChanged()
|
||||
{
|
||||
SelectedDataBindingChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Windows.Media;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using SkiaSharp;
|
||||
using SkiaSharp.Views.WPF;
|
||||
|
||||
namespace Artemis.UI.Shared
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides UI-oriented utilities for types
|
||||
/// </summary>
|
||||
public static class TypeUtilities
|
||||
{
|
||||
internal static INodeService? NodeService;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a tuple containing a color and a slightly darkened color for a given type
|
||||
/// </summary>
|
||||
/// <param name="type">The type to create the color for</param>
|
||||
public static (Color, Color) GetTypeColors(Type type)
|
||||
{
|
||||
if (type == typeof(object))
|
||||
return (SKColors.White.ToColor(), SKColors.White.Darken(0.35f).ToColor());
|
||||
|
||||
TypeColorRegistration? typeColorRegistration = NodeService?.GetTypeColor(type);
|
||||
if (typeColorRegistration != null)
|
||||
return (typeColorRegistration.Color.ToColor(), typeColorRegistration.DarkenedColor.ToColor());
|
||||
|
||||
// Come up with a random color based on the type name that should be the same each time
|
||||
MD5 md5Hasher = MD5.Create();
|
||||
byte[] hashed = md5Hasher.ComputeHash(Encoding.UTF8.GetBytes(type.FullName!));
|
||||
int hash = BitConverter.ToInt32(hashed, 0);
|
||||
|
||||
SKColor baseColor = SKColor.FromHsl(hash % 255, 50 + hash % 50, 50);
|
||||
SKColor darkenedColor = baseColor.Darken(0.35f);
|
||||
|
||||
return (baseColor.ToColor(), darkenedColor.ToColor());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user