1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-31 09:43:46 +00:00

Core - Fixed an error when enabling a module without a datamodel

Shared UI - XML comments
This commit is contained in:
Robert 2020-11-18 19:28:05 +01:00
parent fb3466e102
commit 833a61ecab
38 changed files with 622 additions and 210 deletions

View File

@ -3,7 +3,7 @@
namespace Artemis.Core namespace Artemis.Core
{ {
/// <summary> /// <summary>
/// Represents errors that occur withing the Artemis Core /// Represents errors that occur within the Artemis Core
/// </summary> /// </summary>
public class ArtemisCoreException : Exception public class ArtemisCoreException : Exception
{ {

View File

@ -11,7 +11,7 @@ namespace Artemis.Core.Services
public DataModelService(IPluginManagementService pluginManagementService) public DataModelService(IPluginManagementService pluginManagementService)
{ {
// Add data models of already loaded plugins // Add data models of already loaded plugins
foreach (Module module in pluginManagementService.GetFeaturesOfType<Module>().Where(p => p.IsEnabled && p.InternalDataModel != null)) foreach (Module module in pluginManagementService.GetFeaturesOfType<Module>().Where(p => p.IsEnabled))
AddModuleDataModel(module); AddModuleDataModel(module);
foreach (BaseDataModelExpansion dataModelExpansion in pluginManagementService.GetFeaturesOfType<BaseDataModelExpansion>().Where(p => p.IsEnabled)) foreach (BaseDataModelExpansion dataModelExpansion in pluginManagementService.GetFeaturesOfType<BaseDataModelExpansion>().Where(p => p.IsEnabled))
AddDataModelExpansionDataModel(dataModelExpansion); AddDataModelExpansionDataModel(dataModelExpansion);
@ -61,7 +61,8 @@ namespace Artemis.Core.Services
private void AddModuleDataModel(Module module) private void AddModuleDataModel(Module module)
{ {
if (module.InternalDataModel == null) if (module.InternalDataModel == null)
throw new ArtemisCoreException("Cannot add module data model that is not enabled"); return;
if (module.InternalDataModel.DataModelDescription == null) if (module.InternalDataModel.DataModelDescription == null)
throw new ArtemisPluginFeatureException(module, "Module overrides GetDataModelDescription but returned null"); throw new ArtemisPluginFeatureException(module, "Module overrides GetDataModelDescription but returned null");

View File

@ -8,6 +8,8 @@
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=events/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=events/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=exceptions/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=exceptions/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=extensions/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=extensions/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=ninject/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=ninject_005Cfactories/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=propertyinput/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=propertyinput/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Cdatamodelvisualization/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Cdatamodelvisualization/@EntryIndexedValue">True</s:Boolean>

View File

@ -4,10 +4,14 @@ using Microsoft.Xaml.Behaviors;
namespace Artemis.UI.Shared namespace Artemis.UI.Shared
{ {
/// <summary>
/// Represents a behavior that puts the cursor at the end of a text box when it receives focus
/// </summary>
public class PutCursorAtEndTextBox : Behavior<UIElement> public class PutCursorAtEndTextBox : Behavior<UIElement>
{ {
private TextBox _textBox; private TextBox? _textBox;
/// <inheritdoc />
protected override void OnAttached() protected override void OnAttached()
{ {
base.OnAttached(); base.OnAttached();
@ -18,6 +22,7 @@ namespace Artemis.UI.Shared
_textBox.GotFocus += TextBoxGotFocus; _textBox.GotFocus += TextBoxGotFocus;
} }
/// <inheritdoc />
protected override void OnDetaching() protected override void OnDetaching()
{ {
if (_textBox == null) return; if (_textBox == null) return;
@ -28,6 +33,7 @@ namespace Artemis.UI.Shared
private void TextBoxGotFocus(object sender, RoutedEventArgs routedEventArgs) private void TextBoxGotFocus(object sender, RoutedEventArgs routedEventArgs)
{ {
if (_textBox == null) return;
_textBox.CaretIndex = _textBox.Text.Length; _textBox.CaretIndex = _textBox.Text.Length;
} }
} }

View File

@ -3,10 +3,21 @@ using Ninject;
namespace Artemis.UI.Shared namespace Artemis.UI.Shared
{ {
/// <summary>
/// Represents the main entry point for the shared UI library
/// <para>The Artemis UI calls this so there's no need to deal with this in a plugin</para>
/// </summary>
public static class Bootstrapper public static class Bootstrapper
{ {
/// <summary>
/// Gets a boolean indicating whether or not the shared UI library has been initialized
/// </summary>
public static bool Initialized { get; private set; } public static bool Initialized { get; private set; }
/// <summary>
/// Initializes the shared UI library
/// </summary>
/// <param name="kernel"></param>
public static void Initialize(IKernel kernel) public static void Initialize(IKernel kernel)
{ {
if (Initialized) if (Initialized)

View File

@ -1,9 +1,8 @@
<UserControl x:Class="Artemis.UI.Shared.Controls.ArtemisIcon" <UserControl x:Class="Artemis.UI.Shared.ArtemisIcon"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Artemis.UI.Shared.Controls"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:system="clr-namespace:System;assembly=System.Runtime" xmlns:system="clr-namespace:System;assembly=System.Runtime"
xmlns:svgc="http://sharpvectors.codeplex.com/svgc/" xmlns:svgc="http://sharpvectors.codeplex.com/svgc/"

View File

@ -3,24 +3,37 @@ using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using MaterialDesignThemes.Wpf; using MaterialDesignThemes.Wpf;
namespace Artemis.UI.Shared.Controls namespace Artemis.UI.Shared
{ {
/// <summary> /// <summary>
/// Interaction logic for ArtemisIcon.xaml /// Interaction logic for ArtemisIcon.xaml
/// </summary> /// </summary>
public partial class ArtemisIcon : UserControl public partial class ArtemisIcon : UserControl
{ {
/// <summary>
/// Gets or sets the currently displayed icon as either a <see cref="PackIconKind" /> or an <see cref="Uri" /> pointing
/// to an SVG
/// </summary>
public static readonly DependencyProperty IconProperty = DependencyProperty.Register(nameof(Icon), typeof(object), typeof(ArtemisIcon), public static readonly DependencyProperty IconProperty = DependencyProperty.Register(nameof(Icon), typeof(object), typeof(ArtemisIcon),
new FrameworkPropertyMetadata(IconPropertyChangedCallback)); new FrameworkPropertyMetadata(IconPropertyChangedCallback));
/// <summary>
/// Gets or sets the <see cref="PackIconKind" />
/// </summary>
public static readonly DependencyProperty PackIconProperty = DependencyProperty.Register(nameof(PackIcon), typeof(PackIconKind?), typeof(ArtemisIcon), public static readonly DependencyProperty PackIconProperty = DependencyProperty.Register(nameof(PackIcon), typeof(PackIconKind?), typeof(ArtemisIcon),
new FrameworkPropertyMetadata(IconPropertyChangedCallback)); new FrameworkPropertyMetadata(IconPropertyChangedCallback));
/// <summary>
/// Gets or sets the <see cref="Uri" /> pointing to the SVG
/// </summary>
public static readonly DependencyProperty SvgSourceProperty = DependencyProperty.Register(nameof(SvgSource), typeof(Uri), typeof(ArtemisIcon), public static readonly DependencyProperty SvgSourceProperty = DependencyProperty.Register(nameof(SvgSource), typeof(Uri), typeof(ArtemisIcon),
new FrameworkPropertyMetadata(IconPropertyChangedCallback)); new FrameworkPropertyMetadata(IconPropertyChangedCallback));
private bool _inCallback; private bool _inCallback;
/// <summary>
/// Creates a new instance of the <see cref="ArtemisIcon"/> class
/// </summary>
public ArtemisIcon() public ArtemisIcon()
{ {
InitializeComponent(); InitializeComponent();
@ -30,7 +43,7 @@ namespace Artemis.UI.Shared.Controls
/// Gets or sets the currently displayed icon as either a <see cref="PackIconKind" /> or an <see cref="Uri" /> pointing /// Gets or sets the currently displayed icon as either a <see cref="PackIconKind" /> or an <see cref="Uri" /> pointing
/// to an SVG /// to an SVG
/// </summary> /// </summary>
public object Icon public object? Icon
{ {
get => GetValue(IconProperty); get => GetValue(IconProperty);
set => SetValue(IconProperty, value); set => SetValue(IconProperty, value);
@ -73,9 +86,9 @@ namespace Artemis.UI.Shared.Controls
} }
else if (artemisIcon.Icon is string iconString) else if (artemisIcon.Icon is string iconString)
{ {
if (Uri.TryCreate(iconString, UriKind.Absolute, out Uri uriResult)) if (Uri.TryCreate(iconString, UriKind.Absolute, out Uri? uriResult))
artemisIcon.Icon = uriResult; artemisIcon.Icon = uriResult;
else if (Enum.TryParse(typeof(PackIconKind), iconString, true, out object result)) else if (Enum.TryParse(typeof(PackIconKind), iconString, true, out object? result))
artemisIcon.Icon = result; artemisIcon.Icon = result;
} }
} }

View File

@ -2,7 +2,6 @@
using System.ComponentModel; using System.ComponentModel;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Windows; using System.Windows;
using System.Windows.Controls;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Media; using System.Windows.Media;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
@ -12,22 +11,34 @@ namespace Artemis.UI.Shared
/// <summary> /// <summary>
/// Interaction logic for ColorPicker.xaml /// Interaction logic for ColorPicker.xaml
/// </summary> /// </summary>
public partial class ColorPicker : UserControl, INotifyPropertyChanged public partial class ColorPicker : INotifyPropertyChanged
{ {
private static IColorPickerService _colorPickerService; private static IColorPickerService? _colorPickerService;
/// <summary>
/// Gets or sets the color
/// </summary>
public static readonly DependencyProperty ColorProperty = DependencyProperty.Register(nameof(Color), typeof(Color), typeof(ColorPicker), public static readonly DependencyProperty ColorProperty = DependencyProperty.Register(nameof(Color), typeof(Color), typeof(ColorPicker),
new FrameworkPropertyMetadata(default(Color), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, ColorPropertyChangedCallback)); new FrameworkPropertyMetadata(default(Color), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, ColorPropertyChangedCallback));
/// <summary>
/// Gets or sets a boolean indicating that the popup containing the color picker is open
/// </summary>
public static readonly DependencyProperty PopupOpenProperty = DependencyProperty.Register(nameof(PopupOpen), typeof(bool), typeof(ColorPicker), public static readonly DependencyProperty PopupOpenProperty = DependencyProperty.Register(nameof(PopupOpen), typeof(bool), typeof(ColorPicker),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, PopupOpenPropertyChangedCallback)); new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, PopupOpenPropertyChangedCallback));
/// <summary>
/// Gets or sets a boolean indicating whether the popup should stay open when clicked outside of it
/// </summary>
public static readonly DependencyProperty StaysOpenProperty = DependencyProperty.Register(nameof(StaysOpen), typeof(bool), typeof(ColorPicker), public static readonly DependencyProperty StaysOpenProperty = DependencyProperty.Register(nameof(StaysOpen), typeof(bool), typeof(ColorPicker),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, StaysOpenPropertyChangedCallback)); new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, StaysOpenPropertyChangedCallback));
internal static readonly DependencyProperty ColorOpacityProperty = DependencyProperty.Register(nameof(ColorOpacity), typeof(byte), typeof(ColorPicker), internal static readonly DependencyProperty ColorOpacityProperty = DependencyProperty.Register(nameof(ColorOpacity), typeof(byte), typeof(ColorPicker),
new FrameworkPropertyMetadata((byte) 255, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, ColorOpacityPropertyChangedCallback)); new FrameworkPropertyMetadata((byte) 255, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, ColorOpacityPropertyChangedCallback));
/// <summary>
/// Occurs when the selected color has changed
/// </summary>
public static readonly RoutedEvent ColorChangedEvent = public static readonly RoutedEvent ColorChangedEvent =
EventManager.RegisterRoutedEvent( EventManager.RegisterRoutedEvent(
nameof(Color), nameof(Color),
@ -35,6 +46,9 @@ namespace Artemis.UI.Shared
typeof(RoutedPropertyChangedEventHandler<Color>), typeof(RoutedPropertyChangedEventHandler<Color>),
typeof(ColorPicker)); typeof(ColorPicker));
/// <summary>
/// Occurs when the popup opens or closes
/// </summary>
public static readonly RoutedEvent PopupOpenChangedEvent = public static readonly RoutedEvent PopupOpenChangedEvent =
EventManager.RegisterRoutedEvent( EventManager.RegisterRoutedEvent(
nameof(PopupOpen), nameof(PopupOpen),
@ -44,6 +58,9 @@ namespace Artemis.UI.Shared
private bool _inCallback; private bool _inCallback;
/// <summary>
/// Creates a new instance of the <see cref="ColorPicker" /> class
/// </summary>
public ColorPicker() public ColorPicker()
{ {
InitializeComponent(); InitializeComponent();
@ -51,6 +68,33 @@ namespace Artemis.UI.Shared
Unloaded += OnUnloaded; Unloaded += OnUnloaded;
} }
/// <summary>
/// Gets or sets the color
/// </summary>
public Color Color
{
get => (Color) GetValue(ColorProperty);
set => SetValue(ColorProperty, value);
}
/// <summary>
/// Gets or sets a boolean indicating that the popup containing the color picker is open
/// </summary>
public bool PopupOpen
{
get => (bool) GetValue(PopupOpenProperty);
set => SetValue(PopupOpenProperty, value);
}
/// <summary>
/// Gets or sets a boolean indicating whether the popup should stay open when clicked outside of it
/// </summary>
public bool StaysOpen
{
get => (bool) GetValue(StaysOpenProperty);
set => SetValue(StaysOpenProperty, value);
}
/// <summary> /// <summary>
/// Used by the gradient picker to load saved gradients, do not touch or it'll just throw an exception /// Used by the gradient picker to load saved gradients, do not touch or it'll just throw an exception
/// </summary> /// </summary>
@ -64,33 +108,17 @@ namespace Artemis.UI.Shared
} }
} }
public Color Color
{
get => (Color) GetValue(ColorProperty);
set => SetValue(ColorProperty, value);
}
public bool PopupOpen
{
get => (bool) GetValue(PopupOpenProperty);
set => SetValue(PopupOpenProperty, value);
}
public bool StaysOpen
{
get => (bool) GetValue(StaysOpenProperty);
set => SetValue(StaysOpenProperty, value);
}
internal byte ColorOpacity internal byte ColorOpacity
{ {
get => (byte) GetValue(ColorOpacityProperty); get => (byte) GetValue(ColorOpacityProperty);
set => SetValue(ColorOpacityProperty, value); set => SetValue(ColorOpacityProperty, value);
} }
public event PropertyChangedEventHandler PropertyChanged; /// <summary>
/// Invokes the <see cref="PropertyChanged" /> event
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) /// </summary>
/// <param name="propertyName"></param>
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{ {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
} }
@ -105,7 +133,7 @@ namespace Artemis.UI.Shared
colorPicker.SetCurrentValue(ColorOpacityProperty, ((Color) e.NewValue).A); colorPicker.SetCurrentValue(ColorOpacityProperty, ((Color) e.NewValue).A);
colorPicker.OnPropertyChanged(nameof(Color)); colorPicker.OnPropertyChanged(nameof(Color));
_colorPickerService.UpdateColorDisplay(colorPicker.Color); _colorPickerService?.UpdateColorDisplay(colorPicker.Color);
colorPicker._inCallback = false; colorPicker._inCallback = false;
} }
@ -145,7 +173,7 @@ namespace Artemis.UI.Shared
color = Color.FromArgb(opacity, color.R, color.G, color.B); color = Color.FromArgb(opacity, color.R, color.G, color.B);
colorPicker.SetCurrentValue(ColorProperty, color); colorPicker.SetCurrentValue(ColorProperty, color);
colorPicker.OnPropertyChanged(nameof(ColorOpacity)); colorPicker.OnPropertyChanged(nameof(ColorOpacity));
_colorPickerService.UpdateColorDisplay(colorPicker.Color); _colorPickerService?.UpdateColorDisplay(colorPicker.Color);
colorPicker._inCallback = false; colorPicker._inCallback = false;
} }
@ -163,6 +191,7 @@ namespace Artemis.UI.Shared
private void Slider_OnMouseDown(object sender, MouseButtonEventArgs e) private void Slider_OnMouseDown(object sender, MouseButtonEventArgs e)
{ {
if (_colorPickerService == null) return;
OnDragStarted(); OnDragStarted();
if (_colorPickerService.PreviewSetting.Value) if (_colorPickerService.PreviewSetting.Value)
@ -171,42 +200,63 @@ namespace Artemis.UI.Shared
private void Slider_OnMouseUp(object sender, MouseButtonEventArgs e) private void Slider_OnMouseUp(object sender, MouseButtonEventArgs e)
{ {
if (_colorPickerService == null) return;
OnDragEnded(); OnDragEnded();
_colorPickerService.StopColorDisplay(); _colorPickerService.StopColorDisplay();
} }
private void PreviewCheckBoxClick(object sender, RoutedEventArgs e) private void PreviewCheckBoxClick(object sender, RoutedEventArgs e)
{ {
_colorPickerService.PreviewSetting.Value = PreviewCheckBox.IsChecked.Value; if (_colorPickerService == null) return;
_colorPickerService.PreviewSetting.Value = PreviewCheckBox.IsChecked ?? false;
} }
private void OnLoaded(object sender, RoutedEventArgs e) private void OnLoaded(object sender, RoutedEventArgs e)
{ {
if (_colorPickerService == null) return;
PreviewCheckBox.IsChecked = _colorPickerService.PreviewSetting.Value; PreviewCheckBox.IsChecked = _colorPickerService.PreviewSetting.Value;
_colorPickerService.PreviewSetting.SettingChanged += PreviewSettingOnSettingChanged; _colorPickerService.PreviewSetting.SettingChanged += PreviewSettingOnSettingChanged;
} }
private void OnUnloaded(object sender, RoutedEventArgs e) private void OnUnloaded(object sender, RoutedEventArgs e)
{ {
if (_colorPickerService == null) return;
_colorPickerService.PreviewSetting.SettingChanged -= PreviewSettingOnSettingChanged; _colorPickerService.PreviewSetting.SettingChanged -= PreviewSettingOnSettingChanged;
} }
private void PreviewSettingOnSettingChanged(object sender, EventArgs e) private void PreviewSettingOnSettingChanged(object? sender, EventArgs e)
{ {
if (_colorPickerService == null) return;
PreviewCheckBox.IsChecked = _colorPickerService.PreviewSetting.Value; PreviewCheckBox.IsChecked = _colorPickerService.PreviewSetting.Value;
} }
/// <inheritdoc />
public event PropertyChangedEventHandler? PropertyChanged;
#region Events #region Events
public event EventHandler DragStarted; /// <summary>
public event EventHandler DragEnded; /// Occurs when dragging the color picker has started
/// </summary>
public event EventHandler? DragStarted;
/// <summary>
/// Occurs when dragging the color picker has ended
/// </summary>
public event EventHandler? DragEnded;
/// <summary>
/// Invokes the <see cref="DragStarted" /> event
/// </summary>
protected virtual void OnDragStarted() protected virtual void OnDragStarted()
{ {
DragStarted?.Invoke(this, EventArgs.Empty); DragStarted?.Invoke(this, EventArgs.Empty);
} }
/// <summary>
/// Invokes the <see cref="DragEnded" /> event
/// </summary>
protected virtual void OnDragEnded() protected virtual void OnDragEnded()
{ {
DragEnded?.Invoke(this, EventArgs.Empty); DragEnded?.Invoke(this, EventArgs.Empty);

View File

@ -30,9 +30,8 @@ namespace Artemis.UI.Shared
public ArtemisLed Led { get; } public ArtemisLed Led { get; }
public Rect LedRect { get; set; } public Rect LedRect { get; set; }
public BitmapImage LedImage { get; set; } public BitmapImage? LedImage { get; set; }
public Geometry? DisplayGeometry { get; private set; }
public Geometry DisplayGeometry { get; private set; }
public void RenderColor(DrawingContext drawingContext, bool isDimmed) public void RenderColor(DrawingContext drawingContext, bool isDimmed)
{ {

View File

@ -4,7 +4,6 @@ using System.Globalization;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Windows; using System.Windows;
using System.Windows.Controls;
using System.Windows.Input; using System.Windows.Input;
namespace Artemis.UI.Shared namespace Artemis.UI.Shared
@ -12,15 +11,32 @@ namespace Artemis.UI.Shared
/// <summary> /// <summary>
/// Interaction logic for DraggableFloat.xaml /// Interaction logic for DraggableFloat.xaml
/// </summary> /// </summary>
public partial class DraggableFloat : UserControl, INotifyPropertyChanged public partial class DraggableFloat : INotifyPropertyChanged
{ {
/// <summary>
/// Gets or sets the current value
/// </summary>
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(nameof(Value), typeof(float), typeof(DraggableFloat), public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(nameof(Value), typeof(float), typeof(DraggableFloat),
new FrameworkPropertyMetadata(default(float), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, FloatPropertyChangedCallback)); new FrameworkPropertyMetadata(default(float), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, FloatPropertyChangedCallback));
/// <summary>
/// Gets or sets the step size when dragging
/// </summary>
public static readonly DependencyProperty StepSizeProperty = DependencyProperty.Register(nameof(StepSize), typeof(float), typeof(DraggableFloat)); public static readonly DependencyProperty StepSizeProperty = DependencyProperty.Register(nameof(StepSize), typeof(float), typeof(DraggableFloat));
/// <summary>
/// Gets or sets the minimum value
/// </summary>
public static readonly DependencyProperty MinProperty = DependencyProperty.Register(nameof(Min), typeof(float?), typeof(DraggableFloat)); public static readonly DependencyProperty MinProperty = DependencyProperty.Register(nameof(Min), typeof(float?), typeof(DraggableFloat));
/// <summary>
/// Gets or sets the maximum value
/// </summary>
public static readonly DependencyProperty MaxProperty = DependencyProperty.Register(nameof(Max), typeof(float?), typeof(DraggableFloat)); public static readonly DependencyProperty MaxProperty = DependencyProperty.Register(nameof(Max), typeof(float?), typeof(DraggableFloat));
/// <summary>
/// Occurs when the value has changed
/// </summary>
public static readonly RoutedEvent ValueChangedEvent = public static readonly RoutedEvent ValueChangedEvent =
EventManager.RegisterRoutedEvent( EventManager.RegisterRoutedEvent(
nameof(Value), nameof(Value),
@ -28,42 +44,61 @@ namespace Artemis.UI.Shared
typeof(RoutedPropertyChangedEventHandler<float>), typeof(RoutedPropertyChangedEventHandler<float>),
typeof(DraggableFloat)); typeof(DraggableFloat));
private readonly Regex _inputRegex = new Regex("^[.][-|0-9]+$|^-?[0-9]*[.]{0,1}[0-9]*$");
private bool _calledDragStarted; private bool _calledDragStarted;
private bool _inCallback; private bool _inCallback;
private readonly Regex _inputRegex = new Regex("^[.][-|0-9]+$|^-?[0-9]*[.]{0,1}[0-9]*$");
private Point _mouseDragStartPoint; private Point _mouseDragStartPoint;
private float _startValue; private float _startValue;
/// <summary>
/// Creates a new instance of the <see cref="DraggableFloat" /> class
/// </summary>
public DraggableFloat() public DraggableFloat()
{ {
InitializeComponent(); InitializeComponent();
} }
/// <summary>
/// Gets or sets the current value
/// </summary>
public float Value public float Value
{ {
get => (float) GetValue(ValueProperty); get => (float) GetValue(ValueProperty);
set => SetValue(ValueProperty, value); set => SetValue(ValueProperty, value);
} }
/// <summary>
/// Gets or sets the current value as a string
/// </summary>
public string InputValue public string InputValue
{ {
get => Value.ToString("N3", CultureInfo.InvariantCulture); get => Value.ToString("N3", CultureInfo.InvariantCulture);
set => UpdateValue(value); set => UpdateValue(value);
} }
/// <summary>
/// Gets or sets the step size when dragging
/// </summary>
public float StepSize public float StepSize
{ {
get => (float) GetValue(StepSizeProperty); get => (float) GetValue(StepSizeProperty);
set => SetValue(StepSizeProperty, value); set => SetValue(StepSizeProperty, value);
} }
/// <summary>
/// Gets or sets the minimum value
/// </summary>
public float? Min public float? Min
{ {
get => (float?) GetValue(MinProperty); get => (float?) GetValue(MinProperty);
set => SetValue(MinProperty, value); set => SetValue(MinProperty, value);
} }
/// <summary>
/// Gets or sets the maximum value
/// </summary>
public float? Max public float? Max
{ {
get => (float?) GetValue(MaxProperty); get => (float?) GetValue(MaxProperty);
@ -135,7 +170,9 @@ namespace Artemis.UI.Shared
Point position = e.GetPosition((IInputElement) sender); Point position = e.GetPosition((IInputElement) sender);
if (position == _mouseDragStartPoint) if (position == _mouseDragStartPoint)
{
DisplayInput(); DisplayInput();
}
else else
{ {
OnDragEnded(); OnDragEnded();
@ -186,10 +223,12 @@ namespace Artemis.UI.Shared
private void InputKeyDown(object sender, KeyEventArgs e) private void InputKeyDown(object sender, KeyEventArgs e)
{ {
if (e.Key == Key.Enter) if (e.Key == Key.Enter)
{
DisplayDragHandle(); DisplayDragHandle();
}
else if (e.Key == Key.Escape) else if (e.Key == Key.Escape)
{ {
DraggableFloatInputTextBox.Text = _startValue.ToString(); DraggableFloatInputTextBox.Text = _startValue.ToString(CultureInfo.InvariantCulture);
DisplayDragHandle(); DisplayDragHandle();
} }
} }
@ -208,32 +247,51 @@ namespace Artemis.UI.Shared
{ {
if (e.DataObject.GetDataPresent(typeof(string))) if (e.DataObject.GetDataPresent(typeof(string)))
{ {
string text = (string) e.DataObject.GetData(typeof(string)); if (e.DataObject.GetData(typeof(string)) is string text && !_inputRegex.IsMatch(text))
if (!_inputRegex.IsMatch(text))
e.CancelCommand(); e.CancelCommand();
} }
else else
{
e.CancelCommand(); e.CancelCommand();
}
} }
#endregion #endregion
#region Events #region Events
public event PropertyChangedEventHandler PropertyChanged; /// <inheritdoc />
public event EventHandler DragStarted; public event PropertyChangedEventHandler? PropertyChanged;
public event EventHandler DragEnded;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) /// <summary>
/// Occurs when dragging has started
/// </summary>
public event EventHandler? DragStarted;
/// <summary>
/// Occurs when dragging has ended
/// </summary>
public event EventHandler? DragEnded;
/// <summary>
/// Invokes the <see cref="PropertyChanged" /> event
/// </summary>
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{ {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
} }
/// <summary>
/// Invokes the <see cref="DragStarted" /> event
/// </summary>
protected virtual void OnDragStarted() protected virtual void OnDragStarted()
{ {
DragStarted?.Invoke(this, EventArgs.Empty); DragStarted?.Invoke(this, EventArgs.Empty);
} }
/// <summary>
/// Invokes the <see cref="DragEnded" /> event
/// </summary>
protected virtual void OnDragEnded() protected virtual void OnDragEnded()
{ {
DragEnded?.Invoke(this, EventArgs.Empty); DragEnded?.Invoke(this, EventArgs.Empty);

View File

@ -2,7 +2,6 @@
using System.ComponentModel; using System.ComponentModel;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Windows; using System.Windows;
using System.Windows.Controls;
using System.Windows.Input; using System.Windows.Input;
using Artemis.Core; using Artemis.Core;
using Artemis.UI.Shared.Properties; using Artemis.UI.Shared.Properties;
@ -14,16 +13,37 @@ namespace Artemis.UI.Shared
/// <summary> /// <summary>
/// Interaction logic for GradientPicker.xaml /// Interaction logic for GradientPicker.xaml
/// </summary> /// </summary>
public partial class GradientPicker : UserControl, INotifyPropertyChanged public partial class GradientPicker : INotifyPropertyChanged
{ {
private static IColorPickerService _colorPickerService; private static IColorPickerService? _colorPickerService;
private bool _inCallback; private bool _inCallback;
/// <summary>
/// Creates a new instance of the <see cref="GradientPicker" /> class
/// </summary>
public GradientPicker() public GradientPicker()
{ {
InitializeComponent(); InitializeComponent();
} }
/// <summary>
/// Gets or sets the color gradient
/// </summary>
public ColorGradient ColorGradient
{
get => (ColorGradient) GetValue(ColorGradientProperty);
set => SetValue(ColorGradientProperty, value);
}
/// <summary>
/// Gets or sets the dialog host in which to show the gradient dialog
/// </summary>
public string DialogHost
{
get => (string) GetValue(DialogHostProperty);
set => SetValue(DialogHostProperty, value);
}
/// <summary> /// <summary>
/// Used by the gradient picker to load saved gradients, do not touch or it'll just throw an exception /// Used by the gradient picker to load saved gradients, do not touch or it'll just throw an exception
/// </summary> /// </summary>
@ -38,39 +58,35 @@ namespace Artemis.UI.Shared
} }
/// <summary> /// <summary>
/// Gets or sets the currently selected color gradient /// Occurs when the dialog has opened
/// </summary> /// </summary>
public ColorGradient ColorGradient public event EventHandler? DialogOpened;
{
get => (ColorGradient) GetValue(ColorGradientProperty);
set => SetValue(ColorGradientProperty, value);
}
/// <summary> /// <summary>
/// Gets or sets the currently selected color gradient /// Occurs when the dialog has closed
/// </summary> /// </summary>
public string DialogHost public event EventHandler? DialogClosed;
{
get => (string) GetValue(DialogHostProperty);
set => SetValue(DialogHostProperty, value);
}
public event PropertyChangedEventHandler PropertyChanged;
public event EventHandler DialogOpened;
public event EventHandler DialogClosed;
/// <summary>
/// Invokes the <see cref="PropertyChanged" /> event
/// </summary>
[NotifyPropertyChangedInvocator] [NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{ {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
} }
/// <summary>
/// Invokes the <see cref="DialogOpened" /> event
/// </summary>
protected virtual void OnDialogOpened() protected virtual void OnDialogOpened()
{ {
DialogOpened?.Invoke(this, EventArgs.Empty); DialogOpened?.Invoke(this, EventArgs.Empty);
} }
/// <summary>
/// Invokes the <see cref="DialogClosed" /> event
/// </summary>
protected virtual void OnDialogClosed() protected virtual void OnDialogClosed()
{ {
DialogClosed?.Invoke(this, EventArgs.Empty); DialogClosed?.Invoke(this, EventArgs.Empty);
@ -89,6 +105,9 @@ namespace Artemis.UI.Shared
private void UIElement_OnMouseUp(object sender, MouseButtonEventArgs e) private void UIElement_OnMouseUp(object sender, MouseButtonEventArgs e)
{ {
if (_colorPickerService == null)
return;
Execute.OnUIThread(async () => Execute.OnUIThread(async () =>
{ {
OnDialogOpened(); OnDialogOpened();
@ -97,14 +116,26 @@ namespace Artemis.UI.Shared
}); });
} }
/// <inheritdoc />
public event PropertyChangedEventHandler? PropertyChanged;
#region Static WPF fields #region Static WPF fields
/// <summary>
/// Gets or sets the color gradient
/// </summary>
public static readonly DependencyProperty ColorGradientProperty = DependencyProperty.Register(nameof(ColorGradient), typeof(ColorGradient), typeof(GradientPicker), public static readonly DependencyProperty ColorGradientProperty = DependencyProperty.Register(nameof(ColorGradient), typeof(ColorGradient), typeof(GradientPicker),
new FrameworkPropertyMetadata(default(ColorGradient), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, ColorGradientPropertyChangedCallback)); new FrameworkPropertyMetadata(default(ColorGradient), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, ColorGradientPropertyChangedCallback));
/// <summary>
/// Gets or sets the dialog host in which to show the gradient dialog
/// </summary>
public static readonly DependencyProperty DialogHostProperty = DependencyProperty.Register(nameof(DialogHost), typeof(string), typeof(GradientPicker), public static readonly DependencyProperty DialogHostProperty = DependencyProperty.Register(nameof(DialogHost), typeof(string), typeof(GradientPicker),
new FrameworkPropertyMetadata(default(string))); new FrameworkPropertyMetadata(default(string)));
/// <summary>
/// Occurs when the color gradient has changed
/// </summary>
public static readonly RoutedEvent ColorGradientChangedEvent = public static readonly RoutedEvent ColorGradientChangedEvent =
EventManager.RegisterRoutedEvent(nameof(ColorGradient), RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler<ColorGradient>), typeof(GradientPicker)); EventManager.RegisterRoutedEvent(nameof(ColorGradient), RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler<ColorGradient>), typeof(GradientPicker));

View File

@ -3,24 +3,30 @@ using System.Windows.Controls.Primitives;
namespace Artemis.UI.Shared namespace Artemis.UI.Shared
{ {
/// <summary>
/// Represents a toggle button that can be locked using a property
/// </summary>
public class LockableToggleButton : ToggleButton public class LockableToggleButton : ToggleButton
{ {
protected override void OnToggle() /// <summary>
{ /// Gets or sets a boolean indicating whether the toggle button is locked
if (!IsLocked) /// </summary>
{
base.OnToggle();
}
}
public bool IsLocked
{
get { return (bool)GetValue(IsLockedProperty); }
set { SetValue(IsLockedProperty, value); }
}
// Using a DependencyProperty as the backing store for LockToggle. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsLockedProperty = public static readonly DependencyProperty IsLockedProperty =
DependencyProperty.Register("IsLocked", typeof(bool), typeof(LockableToggleButton), new UIPropertyMetadata(false)); DependencyProperty.Register("IsLocked", typeof(bool), typeof(LockableToggleButton), new UIPropertyMetadata(false));
/// <summary>
/// Gets or sets a boolean indicating whether the toggle button is locked
/// </summary>
public bool IsLocked
{
get => (bool) GetValue(IsLockedProperty);
set => SetValue(IsLockedProperty, value);
}
/// <inheritdoc />
protected override void OnToggle()
{
if (!IsLocked) base.OnToggle();
}
} }
} }

View File

@ -2,6 +2,9 @@
namespace Artemis.UI.Shared namespace Artemis.UI.Shared
{ {
/// <summary>
/// Represents errors that occur within the Artemis Shared UI library
/// </summary>
public class ArtemisSharedUIException : Exception public class ArtemisSharedUIException : Exception
{ {
internal ArtemisSharedUIException() internal ArtemisSharedUIException()

View File

@ -4,8 +4,18 @@ using Artemis.UI.Shared.Services;
namespace Artemis.UI.Shared namespace Artemis.UI.Shared
{ {
/// <summary>
/// Provides extensions for special data model wrappers used by events and list conditions
/// </summary>
public static class DataModelWrapperExtensions 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) public static DataModelPropertiesViewModel CreateViewModel(this EventPredicateWrapperDataModel wrapper, IDataModelUIService dataModelUIService, DataModelUpdateConfiguration configuration)
{ {
DataModelPropertiesViewModel viewModel = new DataModelPropertiesViewModel(wrapper, null, new DataModelPath(wrapper)); DataModelPropertiesViewModel viewModel = new DataModelPropertiesViewModel(wrapper, null, new DataModelPath(wrapper));
@ -16,6 +26,13 @@ namespace Artemis.UI.Shared
return viewModel; 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) public static DataModelPropertiesViewModel CreateViewModel(this ListPredicateWrapperDataModel wrapper, IDataModelUIService dataModelUIService, DataModelUpdateConfiguration configuration)
{ {
DataModelPropertiesViewModel viewModel = new DataModelPropertiesViewModel(wrapper, null, new DataModelPath(wrapper)); DataModelPropertiesViewModel viewModel = new DataModelPropertiesViewModel(wrapper, null, new DataModelPath(wrapper));

View File

@ -3,15 +3,33 @@ using Artemis.Core.DataModelExpansions;
using Artemis.Core.Modules; using Artemis.Core.Modules;
using Artemis.UI.Shared.Input; using Artemis.UI.Shared.Input;
namespace Artemis.UI.Shared.Ninject.Factories namespace Artemis.UI.Shared
{ {
/// <summary>
/// Represents a factory for view models provided by the Artemis Shared UI library
/// </summary>
public interface ISharedVmFactory public interface ISharedVmFactory
{ {
} }
/// <summary>
/// A factory that allows the creation of data model view models
/// </summary>
public interface IDataModelVmFactory : ISharedVmFactory public interface IDataModelVmFactory : ISharedVmFactory
{ {
/// <summary>
/// Creates a new instance of the <see cref="DataModelDynamicViewModel" /> class
/// </summary>
/// <param name="module">The module to associate the dynamic view model with</param>
/// <returns>A new instance of the <see cref="DataModelDynamicViewModel" /> class</returns>
DataModelDynamicViewModel DataModelDynamicViewModel(Module module); DataModelDynamicViewModel DataModelDynamicViewModel(Module module);
/// <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); DataModelStaticViewModel DataModelStaticViewModel(Type targetType, DataModelPropertyAttribute targetDescription);
} }
} }

View File

@ -1,11 +1,10 @@
using System; using System;
using Artemis.UI.Shared.Ninject.Factories;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using MaterialDesignThemes.Wpf; using MaterialDesignThemes.Wpf;
using Ninject.Extensions.Conventions; using Ninject.Extensions.Conventions;
using Ninject.Modules; using Ninject.Modules;
namespace Artemis.UI.Shared.Ninject namespace Artemis.UI.Shared
{ {
/// <summary> /// <summary>
/// The main <see cref="NinjectModule" /> of the Artemis Shared UI toolkit that binds all services /// The main <see cref="NinjectModule" /> of the Artemis Shared UI toolkit that binds all services

View File

@ -4,6 +4,9 @@ using Artemis.UI.Shared.Services;
namespace Artemis.UI.Shared namespace Artemis.UI.Shared
{ {
/// <summary>
/// Represents a property input registration, registered through <see cref="IProfileEditorService.RegisterPropertyInput"/>
/// </summary>
public class PropertyInputRegistration public class PropertyInputRegistration
{ {
private readonly IProfileEditorService _profileEditorService; private readonly IProfileEditorService _profileEditorService;
@ -19,8 +22,19 @@ namespace Artemis.UI.Shared
Plugin.Disabled += InstanceOnDisabled; Plugin.Disabled += InstanceOnDisabled;
} }
/// <summary>
/// Gets the plugin that registered the property input
/// </summary>
public Plugin Plugin { get; } public Plugin Plugin { get; }
/// <summary>
/// Gets the type supported by the property input
/// </summary>
public Type SupportedType { get; } public Type SupportedType { get; }
/// <summary>
/// Gets the view model type of the property input
/// </summary>
public Type ViewModelType { get; } public Type ViewModelType { get; }
internal void Unsubscribe() internal void Unsubscribe()
@ -29,7 +43,7 @@ namespace Artemis.UI.Shared
Plugin.Disabled -= InstanceOnDisabled; Plugin.Disabled -= InstanceOnDisabled;
} }
private void InstanceOnDisabled(object sender, EventArgs e) private void InstanceOnDisabled(object? sender, EventArgs e)
{ {
// Profile editor service will call Unsubscribe // Profile editor service will call Unsubscribe
_profileEditorService.RemovePropertyInput(this); _profileEditorService.RemovePropertyInput(this);

View File

@ -5,11 +5,20 @@ using Stylet;
namespace Artemis.UI.Shared namespace Artemis.UI.Shared
{ {
/// <summary>
/// Represents the base class for a property input view model that is used to edit layer properties
/// </summary>
/// <typeparam name="T">The type of property this input view model supports</typeparam>
public abstract class PropertyInputViewModel<T> : PropertyInputViewModel public abstract class PropertyInputViewModel<T> : PropertyInputViewModel
{ {
private bool _inputDragging; private bool _inputDragging;
private T _inputValue; private T _inputValue;
/// <summary>
/// Creates a new instance of the <see cref="PropertyInputViewModel" /> class
/// </summary>
/// <param name="layerProperty">The layer property this view model will edit</param>
/// <param name="profileEditorService">The profile editor service</param>
protected PropertyInputViewModel(LayerProperty<T> layerProperty, IProfileEditorService profileEditorService) protected PropertyInputViewModel(LayerProperty<T> layerProperty, IProfileEditorService profileEditorService)
{ {
LayerProperty = layerProperty; LayerProperty = layerProperty;
@ -21,6 +30,12 @@ namespace Artemis.UI.Shared
UpdateInputValue(); UpdateInputValue();
} }
/// <summary>
/// Creates a new instance of the <see cref="PropertyInputViewModel" /> class
/// </summary>
/// <param name="layerProperty">The layer property this view model will edit</param>
/// <param name="profileEditorService">The profile editor service</param>
/// <param name="validator">The validator used to validate the input</param>
protected PropertyInputViewModel(LayerProperty<T> layerProperty, IProfileEditorService profileEditorService, IModelValidator validator) : base(validator) protected PropertyInputViewModel(LayerProperty<T> layerProperty, IProfileEditorService profileEditorService, IModelValidator validator) : base(validator)
{ {
LayerProperty = layerProperty; LayerProperty = layerProperty;
@ -32,17 +47,32 @@ namespace Artemis.UI.Shared
UpdateInputValue(); UpdateInputValue();
} }
/// <summary>
/// Gets the layer property this view model is editing
/// </summary>
public LayerProperty<T> LayerProperty { get; } public LayerProperty<T> LayerProperty { get; }
/// <summary>
/// Gets the profile editor service
/// </summary>
public IProfileEditorService ProfileEditorService { get; } public IProfileEditorService ProfileEditorService { get; }
internal override object InternalGuard { get; } = null; /// <summary>
/// Gets or sets a boolean indicating whether the input is currently being dragged
/// <para>
/// Only applicable when using something like a <see cref="DraggableFloat" />, see
/// <see cref="InputDragStarted" /> and <see cref="InputDragEnded" />
/// </para>
/// </summary>
public bool InputDragging public bool InputDragging
{ {
get => _inputDragging; get => _inputDragging;
private set => SetAndNotify(ref _inputDragging, value); private set => SetAndNotify(ref _inputDragging, value);
} }
/// <summary>
/// Gets or sets the input value
/// </summary>
public T InputValue public T InputValue
{ {
get => _inputValue; get => _inputValue;
@ -53,29 +83,50 @@ namespace Artemis.UI.Shared
} }
} }
public override void Dispose() internal override object InternalGuard { get; } = new object();
#region IDisposable
/// <inheritdoc />
protected override void Dispose(bool disposing)
{ {
LayerProperty.Updated -= LayerPropertyOnUpdated; if (disposing)
LayerProperty.CurrentValueSet -= LayerPropertyOnUpdated; {
LayerProperty.DataBindingEnabled -= LayerPropertyOnDataBindingChange; LayerProperty.Updated -= LayerPropertyOnUpdated;
LayerProperty.DataBindingDisabled -= LayerPropertyOnDataBindingChange; LayerProperty.CurrentValueSet -= LayerPropertyOnUpdated;
Dispose(true); LayerProperty.DataBindingEnabled -= LayerPropertyOnDataBindingChange;
GC.SuppressFinalize(this); LayerProperty.DataBindingDisabled -= LayerPropertyOnDataBindingChange;
}
base.Dispose(disposing);
} }
#endregion
/// <summary>
/// Called when the input value has been applied to the layer property
/// </summary>
protected virtual void OnInputValueApplied() protected virtual void OnInputValueApplied()
{ {
} }
/// <summary>
/// Called when the input value has changed
/// </summary>
protected virtual void OnInputValueChanged() protected virtual void OnInputValueChanged()
{ {
} }
/// <summary>
/// Called when data bindings have been enabled or disabled on the layer property
/// </summary>
protected virtual void OnDataBindingsChanged() protected virtual void OnDataBindingsChanged()
{ {
} }
/// <summary>
/// Applies the input value to the layer property
/// </summary>
protected void ApplyInputValue() protected void ApplyInputValue()
{ {
// Force the validator to run // Force the validator to run
@ -89,9 +140,13 @@ namespace Artemis.UI.Shared
OnInputValueApplied(); OnInputValueApplied();
} }
private void LayerPropertyOnUpdated(object sender, EventArgs e) private void SetCurrentValue(T value, bool saveChanges)
{ {
UpdateInputValue(); LayerProperty.SetCurrentValue(value, ProfileEditorService.CurrentTime);
if (saveChanges)
ProfileEditorService.UpdateSelectedProfileElement();
else
ProfileEditorService.UpdateProfilePreview();
} }
private void UpdateInputValue() private void UpdateInputValue()
@ -114,27 +169,35 @@ namespace Artemis.UI.Shared
#region Event handlers #region Event handlers
/// <summary>
/// Called by the view input drag has started
/// <para>
/// To use, add the following to DraggableFloat in your xaml: <c>DragStarted="{s:Action InputDragStarted}"</c>
/// </para>
/// </summary>
public void InputDragStarted(object sender, EventArgs e) public void InputDragStarted(object sender, EventArgs e)
{ {
InputDragging = true; InputDragging = true;
} }
/// <summary>
/// Called by the view when input drag has ended
/// <para>
/// To use, add the following to DraggableFloat in your xaml: <c>DragEnded="{s:Action InputDragEnded}"</c>
/// </para>
/// </summary>
public void InputDragEnded(object sender, EventArgs e) public void InputDragEnded(object sender, EventArgs e)
{ {
InputDragging = false; InputDragging = false;
ProfileEditorService.UpdateSelectedProfileElement(); ProfileEditorService.UpdateSelectedProfileElement();
} }
private void SetCurrentValue(T value, bool saveChanges) private void LayerPropertyOnUpdated(object? sender, EventArgs e)
{ {
LayerProperty.SetCurrentValue(value, ProfileEditorService.CurrentTime); UpdateInputValue();
if (saveChanges)
ProfileEditorService.UpdateSelectedProfileElement();
else
ProfileEditorService.UpdateProfilePreview();
} }
private void LayerPropertyOnDataBindingChange(object sender, LayerPropertyEventArgs<T> e) private void LayerPropertyOnDataBindingChange(object? sender, LayerPropertyEventArgs<T> e)
{ {
OnDataBindingsChanged(); OnDataBindingsChanged();
} }
@ -147,10 +210,16 @@ namespace Artemis.UI.Shared
/// </summary> /// </summary>
public abstract class PropertyInputViewModel : ValidatingModelBase, IDisposable public abstract class PropertyInputViewModel : ValidatingModelBase, IDisposable
{ {
/// <summary>
/// For internal use only, implement <see cref="PropertyInputViewModel{T}" /> instead.
/// </summary>
protected PropertyInputViewModel() protected PropertyInputViewModel()
{ {
} }
/// <summary>
/// For internal use only, implement <see cref="PropertyInputViewModel{T}" /> instead.
/// </summary>
protected PropertyInputViewModel(IModelValidator validator) : base(validator) protected PropertyInputViewModel(IModelValidator validator) : base(validator)
{ {
} }
@ -161,13 +230,29 @@ namespace Artemis.UI.Shared
// ReSharper disable once UnusedMember.Global // ReSharper disable once UnusedMember.Global
internal abstract object InternalGuard { get; } internal abstract object InternalGuard { get; }
public abstract void Dispose(); #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) protected virtual void Dispose(bool disposing)
{ {
if (disposing) if (disposing)
{ {
} }
} }
/// <inheritdoc />
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
} }
} }

View File

@ -1,12 +0,0 @@
using System;
namespace Artemis.UI.Shared.Services
{
internal class DataBindingUIService : IDataBindingUIService
{
public object GetDataBindingViewModel(Type propertyType)
{
return null;
}
}
}

View File

@ -7,7 +7,6 @@ using Artemis.Core.Modules;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Shared.DefaultTypes.DataModel.Display; using Artemis.UI.Shared.DefaultTypes.DataModel.Display;
using Artemis.UI.Shared.Input; using Artemis.UI.Shared.Input;
using Artemis.UI.Shared.Ninject.Factories;
using Ninject; using Ninject;
using Ninject.Parameters; using Ninject.Parameters;

View File

@ -3,38 +3,55 @@ using Stylet;
namespace Artemis.UI.Shared.Services namespace Artemis.UI.Shared.Services
{ {
/// <summary>
/// Represents the base class for a dialog view model
/// </summary>
public abstract class DialogViewModelBase : ValidatingModelBase public abstract class DialogViewModelBase : ValidatingModelBase
{ {
private DialogViewModelHost _dialogViewModelHost; private DialogViewModelHost? _dialogViewModelHost;
private DialogSession _session; private DialogSession? _session;
/// <summary>
/// Creates a new instance of the <see cref="DialogViewModelBase" /> class with a model validator
/// </summary>
/// <param name="validator">A validator to apply to the model</param>
protected DialogViewModelBase(IModelValidator validator) : base(validator) protected DialogViewModelBase(IModelValidator validator) : base(validator)
{ {
} }
/// <summary>
/// Creates a new instance of the <see cref="DialogViewModelBase" />
/// </summary>
protected DialogViewModelBase() protected DialogViewModelBase()
{ {
} }
public DialogViewModelHost DialogViewModelHost /// <summary>
{ /// Gets the dialog session that created this dialog
get => _dialogViewModelHost; /// <para>Not available until after the dialog has been opened</para>
set => SetAndNotify(ref _dialogViewModelHost, value); /// </summary>
} public DialogSession? Session
public DialogSession Session
{ {
get => _session; get => _session;
private set => SetAndNotify(ref _session, value); private set => SetAndNotify(ref _session, value);
} }
public void OnDialogOpened(object sender, DialogOpenedEventArgs e) internal DialogViewModelHost? DialogViewModelHost
{ {
Session = e.Session; get => _dialogViewModelHost;
set => SetAndNotify(ref _dialogViewModelHost, value);
} }
/// <summary>
/// Called when the dialog has closed
/// </summary>
public virtual void OnDialogClosed(object sender, DialogClosingEventArgs e) public virtual void OnDialogClosed(object sender, DialogClosingEventArgs e)
{ {
} }
internal void OnDialogOpened(object sender, DialogOpenedEventArgs e)
{
Session = e.Session;
}
} }
} }

View File

@ -4,7 +4,7 @@ using Stylet;
namespace Artemis.UI.Shared.Services namespace Artemis.UI.Shared.Services
{ {
public class DialogViewModelHost : PropertyChangedBase internal class DialogViewModelHost : PropertyChangedBase
{ {
private readonly IViewManager _viewManager; private readonly IViewManager _viewManager;

View File

@ -1,6 +1,8 @@
namespace Artemis.UI.Shared.Services namespace Artemis.UI.Shared.Services
{ {
// ReSharper disable once InconsistentNaming /// <summary>
/// Represents a service provided by the Artemis Shared UI library
/// </summary>
public interface IArtemisSharedUIService public interface IArtemisSharedUIService
{ {
} }

View File

@ -1,9 +0,0 @@
using System;
namespace Artemis.UI.Shared.Services
{
public interface IDataBindingUIService : IArtemisSharedUIService
{
object GetDataBindingViewModel(Type propertyType);
}
}

View File

@ -79,6 +79,7 @@ namespace Artemis.UI.Shared.Services
/// Shows a dialog displaying the provided message and exception. Does not handle, log or throw the exception. /// Shows a dialog displaying the provided message and exception. Does not handle, log or throw the exception.
/// </summary> /// </summary>
/// <param name="message">The message to display in the dialog title</param> /// <param name="message">The message to display in the dialog title</param>
/// <param name="exception">The exception to display</param>
/// <returns>A task resolving when the dialog is closed</returns> /// <returns>A task resolving when the dialog is closed</returns>
void ShowExceptionDialog(string message, Exception exception); void ShowExceptionDialog(string message, Exception exception);
} }

View File

@ -3,27 +3,93 @@ using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Modules; using Artemis.Core.Modules;
using Ninject;
namespace Artemis.UI.Shared.Services namespace Artemis.UI.Shared.Services
{ {
/// <summary>
/// Provides access the the profile editor back-end logic
/// </summary>
public interface IProfileEditorService : IArtemisSharedUIService public interface IProfileEditorService : IArtemisSharedUIService
{ {
/// <summary>
/// Gets the currently selected profile
/// </summary>
Profile SelectedProfile { get; } Profile SelectedProfile { get; }
/// <summary>
/// Gets the currently selected profile element
/// </summary>
RenderProfileElement SelectedProfileElement { get; } RenderProfileElement SelectedProfileElement { get; }
/// <summary>
/// Gets the currently selected data binding property
/// </summary>
ILayerProperty SelectedDataBinding { get; } ILayerProperty SelectedDataBinding { get; }
/// <summary>
/// Gets or sets the current time
/// </summary>
TimeSpan CurrentTime { get; set; } TimeSpan CurrentTime { get; set; }
/// <summary>
/// Gets or sets the pixels per second (zoom level)
/// </summary>
int PixelsPerSecond { get; set; } int PixelsPerSecond { get; set; }
/// <summary>
/// Gets a read-only collection of all registered property editors
/// </summary>
ReadOnlyCollection<PropertyInputRegistration> RegisteredPropertyEditors { get; } ReadOnlyCollection<PropertyInputRegistration> RegisteredPropertyEditors { get; }
IKernel Kernel { get; }
/// <summary>
/// Changes the selected profile
/// </summary>
/// <param name="profile">The profile to select</param>
void ChangeSelectedProfile(Profile profile); void ChangeSelectedProfile(Profile profile);
/// <summary>
/// Updates the selected profile and saves it to persistent storage
/// </summary>
void UpdateSelectedProfile(); void UpdateSelectedProfile();
/// <summary>
/// Changes the selected profile element
/// </summary>
/// <param name="profileElement">The profile element to select</param>
void ChangeSelectedProfileElement(RenderProfileElement profileElement); void ChangeSelectedProfileElement(RenderProfileElement profileElement);
/// <summary>
/// Updates the selected profile element and saves the profile it is contained in to persistent storage
/// </summary>
void UpdateSelectedProfileElement(); void UpdateSelectedProfileElement();
/// <summary>
/// Changes the selected data binding property
/// </summary>
/// <param name="layerProperty">The data binding property to select</param>
void ChangeSelectedDataBinding(ILayerProperty layerProperty); void ChangeSelectedDataBinding(ILayerProperty layerProperty);
/// <summary>
/// Updates the profile preview, forcing UI-elements to re-render
/// </summary>
void UpdateProfilePreview(); void UpdateProfilePreview();
/// <summary>
/// Restores the profile to the last <see cref="UpdateSelectedProfile" /> call
/// </summary>
/// <returns><see langword="true" /> if undo was successful, otherwise <see langword="false" /></returns>
bool UndoUpdateProfile(); bool UndoUpdateProfile();
/// <summary>
/// Restores the profile to the last <see cref="UndoUpdateProfile" /> call
/// </summary>
/// <returns><see langword="true" /> if redo was successful, otherwise <see langword="false" /></returns>
bool RedoUpdateProfile(); bool RedoUpdateProfile();
/// <summary>
/// Gets the current module the profile editor is initialized for
/// </summary>
/// <returns>The current module the profile editor is initialized for</returns>
ProfileModule GetCurrentModule(); ProfileModule GetCurrentModule();
/// <summary> /// <summary>
@ -85,6 +151,10 @@ namespace Artemis.UI.Shared.Services
/// <returns></returns> /// <returns></returns>
PropertyInputRegistration RegisterPropertyInput(Type viewModelType, Plugin plugin); PropertyInputRegistration RegisterPropertyInput(Type viewModelType, Plugin plugin);
/// <summary>
/// Removes the property input view model
/// </summary>
/// <param name="registration"></param>
void RemovePropertyInput(PropertyInputRegistration registration); void RemovePropertyInput(PropertyInputRegistration registration);
/// <summary> /// <summary>
@ -97,10 +167,11 @@ namespace Artemis.UI.Shared.Services
/// <param name="snapToCurrentTime">Enable snapping to the current time of the editor</param> /// <param name="snapToCurrentTime">Enable snapping to the current time of the editor</param>
/// <param name="snapTimes">An optional extra list of times to snap to</param> /// <param name="snapTimes">An optional extra list of times to snap to</param>
/// <returns></returns> /// <returns></returns>
TimeSpan SnapToTimeline(TimeSpan time, TimeSpan tolerance, bool snapToSegments, bool snapToCurrentTime, List<TimeSpan> snapTimes = null); TimeSpan SnapToTimeline(TimeSpan time, TimeSpan tolerance, bool snapToSegments, bool snapToCurrentTime, List<TimeSpan>? snapTimes = null);
/// <summary> /// <summary>
/// If a matching registration is found, creates a new <see cref="PropertyInputViewModel{T}"/> supporting <typeparamref name="T"/> /// If a matching registration is found, creates a new <see cref="PropertyInputViewModel{T}" /> supporting
/// <typeparamref name="T" />
/// </summary> /// </summary>
PropertyInputViewModel<T> CreatePropertyInputViewModel<T>(LayerProperty<T> layerProperty); PropertyInputViewModel<T> CreatePropertyInputViewModel<T>(LayerProperty<T> layerProperty);
} }

View File

@ -22,6 +22,7 @@ namespace Artemis.UI.Shared.Services
private readonly object _selectedProfileLock = new object(); private readonly object _selectedProfileLock = new object();
private TimeSpan _currentTime; private TimeSpan _currentTime;
private int _pixelsPerSecond; private int _pixelsPerSecond;
private IKernel _kernel;
public ProfileEditorService(IProfileService profileService, IKernel kernel, ILogger logger, ICoreService coreService) public ProfileEditorService(IProfileService profileService, IKernel kernel, ILogger logger, ICoreService coreService)
{ {
@ -29,8 +30,8 @@ namespace Artemis.UI.Shared.Services
_logger = logger; _logger = logger;
_coreService = coreService; _coreService = coreService;
_registeredPropertyEditors = new List<PropertyInputRegistration>(); _registeredPropertyEditors = new List<PropertyInputRegistration>();
_kernel = kernel;
Kernel = kernel;
PixelsPerSecond = 100; PixelsPerSecond = 100;
} }
@ -40,7 +41,6 @@ namespace Artemis.UI.Shared.Services
Execute.PostToUIThread(OnProfilePreviewUpdated); Execute.PostToUIThread(OnProfilePreviewUpdated);
} }
public IKernel Kernel { get; }
public ReadOnlyCollection<PropertyInputRegistration> RegisteredPropertyEditors => _registeredPropertyEditors.AsReadOnly(); public ReadOnlyCollection<PropertyInputRegistration> RegisteredPropertyEditors => _registeredPropertyEditors.AsReadOnly();
public Profile SelectedProfile { get; private set; } public Profile SelectedProfile { get; private set; }
public RenderProfileElement SelectedProfileElement { get; private set; } public RenderProfileElement SelectedProfileElement { get; private set; }
@ -207,7 +207,7 @@ namespace Artemis.UI.Shared.Services
return existing; return existing;
} }
Kernel.Bind(viewModelType).ToSelf(); _kernel.Bind(viewModelType).ToSelf();
PropertyInputRegistration registration = new PropertyInputRegistration(this, plugin, supportedType, viewModelType); PropertyInputRegistration registration = new PropertyInputRegistration(this, plugin, supportedType, viewModelType);
_registeredPropertyEditors.Add(registration); _registeredPropertyEditors.Add(registration);
return registration; return registration;
@ -223,7 +223,7 @@ namespace Artemis.UI.Shared.Services
registration.Unsubscribe(); registration.Unsubscribe();
_registeredPropertyEditors.Remove(registration); _registeredPropertyEditors.Remove(registration);
Kernel.Unbind(registration.ViewModelType); _kernel.Unbind(registration.ViewModelType);
} }
} }
} }
@ -282,7 +282,7 @@ namespace Artemis.UI.Shared.Services
return null; return null;
ConstructorArgument parameter = new ConstructorArgument("layerProperty", layerProperty); ConstructorArgument parameter = new ConstructorArgument("layerProperty", layerProperty);
IKernel kernel = registration != null ? registration.Plugin.Kernel : Kernel; IKernel kernel = registration != null ? registration.Plugin.Kernel : _kernel;
return (PropertyInputViewModel<T>) kernel.Get(viewModelType, parameter); return (PropertyInputViewModel<T>) kernel.Get(viewModelType, parameter);
} }

View File

@ -3,24 +3,47 @@ using System.Windows.Input;
namespace Artemis.UI.Shared namespace Artemis.UI.Shared
{ {
/// <summary>
/// Provides a command that simply calls a delegate when invoked
/// </summary>
public class DelegateCommand : ICommand public class DelegateCommand : ICommand
{ {
private readonly Predicate<object> _canExecute; private readonly Predicate<object?>? _canExecute;
private readonly Action<object> _execute; private readonly Action<object?> _execute;
public DelegateCommand(Action<object> execute) : this(execute, null) /// <summary>
/// Creates a new instance of the <see cref="DelegateCommand" /> class
/// </summary>
/// <param name="execute">The delegate to execute</param>
public DelegateCommand(Action<object?> execute) : this(execute, null)
{ {
} }
public DelegateCommand(Action<object> execute, Predicate<object> canExecute) /// <summary>
/// Creates a new instance of the <see cref="DelegateCommand" /> class with a predicate indicating whether the command
/// can be executed
/// </summary>
/// <param name="execute">The delegate to execute</param>
/// <param name="canExecute">The predicate that determines whether the command can execute</param>
public DelegateCommand(Action<object?> execute, Predicate<object?>? canExecute)
{ {
_execute = execute; _execute = execute;
_canExecute = canExecute; _canExecute = canExecute;
} }
public event EventHandler CanExecuteChanged; /// <summary>
/// Invokes the <see cref="CanExecuteChanged" /> event
/// </summary>
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
public bool CanExecute(object parameter) /// <inheritdoc />
public event EventHandler? CanExecuteChanged;
/// <inheritdoc />
public bool CanExecute(object? parameter)
{ {
if (_canExecute == null) if (_canExecute == null)
return true; return true;
@ -28,14 +51,10 @@ namespace Artemis.UI.Shared
return _canExecute(parameter); return _canExecute(parameter);
} }
public void Execute(object parameter) /// <inheritdoc />
public void Execute(object? parameter)
{ {
_execute(parameter); _execute(parameter);
} }
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
} }
} }

View File

@ -20,7 +20,7 @@ namespace Artemis.UI.Shared
if (!t.IsEnum) if (!t.IsEnum)
throw new ArgumentException($"{nameof(t)} must be an enum type"); throw new ArgumentException($"{nameof(t)} must be an enum type");
return Enum.GetValues(t).Cast<Enum>().Select(e => new ValueDescription {Value = e, Description = e.Humanize()}).ToList(); return Enum.GetValues(t).Cast<Enum>().Select(e => new ValueDescription(e, e.Humanize())).ToList();
} }
/// <summary> /// <summary>
@ -34,9 +34,30 @@ namespace Artemis.UI.Shared
} }
} }
/// <summary>
/// Represents a value and a description for an enum value
/// </summary>
public class ValueDescription public class ValueDescription
{ {
public object Value { get; set; } /// <summary>
public string Description { get; set; } /// Creates a new instance of the <see cref="ValueDescription"/> class
/// </summary>
/// <param name="value">The enum value</param>
/// <param name="description">The description of the value</param>
public ValueDescription(object value, string description)
{
Value = value ?? throw new ArgumentNullException(nameof(value));
Description = description ?? throw new ArgumentNullException(nameof(description));
}
/// <summary>
/// The enum value
/// </summary>
public object Value { get; }
/// <summary>
/// The description of the value
/// </summary>
public string Description { get; }
} }
} }

View File

@ -1,7 +1,6 @@
using System; using System;
using System.IO; using System.IO;
using Artemis.Core; using Artemis.Core;
using Artemis.UI.Shared.Controls;
using MaterialDesignThemes.Wpf; using MaterialDesignThemes.Wpf;
namespace Artemis.UI.Shared namespace Artemis.UI.Shared

View File

@ -1,6 +1,5 @@
using System.Diagnostics; using System.Diagnostics;
using System.Windows; using System.Windows;
using System.Windows.Markup;
using System.Windows.Media.Animation; using System.Windows.Media.Animation;
// Code from http://www.wpfmentor.com/2009/01/how-to-debug-triggers-using-trigger.html // Code from http://www.wpfmentor.com/2009/01/how-to-debug-triggers-using-trigger.html
@ -22,7 +21,7 @@ using System.Windows.Media.Animation;
namespace Artemis.UI.Shared namespace Artemis.UI.Shared
{ {
#if DEBUG #if DEBUG
/// <summary> /// <summary>
/// Contains attached properties to activate Trigger Tracing on the specified Triggers. /// Contains attached properties to activate Trigger Tracing on the specified Triggers.
@ -65,37 +64,31 @@ namespace Artemis.UI.Shared
/// </summary> /// </summary>
private class TriggerTraceListener : TraceListener private class TriggerTraceListener : TraceListener
{ {
public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string format, params object[] args) public override void TraceEvent(TraceEventCache? eventCache, string source, TraceEventType eventType, int id, string format, params object?[]? args)
{ {
base.TraceEvent(eventCache, source, eventType, id, format, args); base.TraceEvent(eventCache, source, eventType, id, format, args);
if (format.StartsWith("Storyboard has begun;")) if (format.StartsWith("Storyboard has begun;") && args != null)
{ if (args[1] is TriggerTraceStoryboard storyboard)
TriggerTraceStoryboard storyboard = args[1] as TriggerTraceStoryboard;
if (storyboard != null)
{ {
// add a breakpoint here to see when your trigger has been // add a breakpoint here to see when your trigger has been
// entered or exited // entered or exited
// the element being acted upon // the element being acted upon
object targetElement = args[5]; object? targetElement = args[5];
// the namescope of the element being acted upon
INameScope namescope = (INameScope) args[7];
TriggerBase triggerBase = storyboard.TriggerBase; TriggerBase triggerBase = storyboard.TriggerBase;
string triggerName = GetTriggerName(storyboard.TriggerBase); string triggerName = GetTriggerName(storyboard.TriggerBase);
Debug.WriteLine("Element: {0}, {1}: {2}: {3}", targetElement, triggerBase.GetType().Name, triggerName, storyboard.StoryboardType); Debug.WriteLine("Element: {0}, {1}: {2}: {3}", targetElement, triggerBase.GetType().Name, triggerName, storyboard.StoryboardType);
} }
}
} }
public override void Write(string message) public override void Write(string? message)
{ {
} }
public override void WriteLine(string message) public override void WriteLine(string? message)
{ {
} }
} }
@ -118,12 +111,16 @@ namespace Artemis.UI.Shared
/// to identify the trigger in the debug output. /// to identify the trigger in the debug output.
/// </summary> /// </summary>
/// <param name="trigger">The trigger.</param> /// <param name="trigger">The trigger.</param>
/// <param name="value">The value.</param>
/// <returns></returns> /// <returns></returns>
public static void SetTriggerName(TriggerBase trigger, string value) public static void SetTriggerName(TriggerBase trigger, string value)
{ {
trigger.SetValue(TriggerNameProperty, value); trigger.SetValue(TriggerNameProperty, value);
} }
/// <summary>
/// Gets or sets the trigger name property
/// </summary>
public static readonly DependencyProperty TriggerNameProperty = public static readonly DependencyProperty TriggerNameProperty =
DependencyProperty.RegisterAttached( DependencyProperty.RegisterAttached(
"TriggerName", "TriggerName",
@ -155,6 +152,9 @@ namespace Artemis.UI.Shared
trigger.SetValue(TraceEnabledProperty, value); trigger.SetValue(TraceEnabledProperty, value);
} }
/// <summary>
/// Gets or sets whether the trace is enabled
/// </summary>
public static readonly DependencyProperty TraceEnabledProperty = public static readonly DependencyProperty TraceEnabledProperty =
DependencyProperty.RegisterAttached( DependencyProperty.RegisterAttached(
"TraceEnabled", "TraceEnabled",
@ -164,9 +164,7 @@ namespace Artemis.UI.Shared
private static void OnTraceEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) private static void OnTraceEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{ {
TriggerBase triggerBase = d as TriggerBase; if (!(d is TriggerBase triggerBase))
if (triggerBase == null)
return; return;
if (!(e.NewValue is bool)) if (!(e.NewValue is bool))
@ -187,22 +185,16 @@ namespace Artemis.UI.Shared
// remove the dummy storyboards // remove the dummy storyboards
foreach (TriggerActionCollection actionCollection in new[] {triggerBase.EnterActions, triggerBase.ExitActions}) foreach (TriggerActionCollection actionCollection in new[] {triggerBase.EnterActions, triggerBase.ExitActions})
{ foreach (TriggerAction triggerAction in actionCollection)
foreach (TriggerAction triggerAction in actionCollection) if (triggerAction is BeginStoryboard bsb && bsb.Storyboard is TriggerTraceStoryboard)
{ {
BeginStoryboard bsb = triggerAction as BeginStoryboard; actionCollection.Remove(bsb);
break;
if (bsb != null && bsb.Storyboard != null && bsb.Storyboard is TriggerTraceStoryboard)
{
actionCollection.Remove(bsb);
break;
}
} }
}
} }
} }
#endregion #endregion
} }
#endif #endif
} }

View File

@ -15,7 +15,7 @@ using Artemis.Core.Services;
using Artemis.UI.Ninject; using Artemis.UI.Ninject;
using Artemis.UI.Screens; using Artemis.UI.Screens;
using Artemis.UI.Screens.Splash; using Artemis.UI.Screens.Splash;
using Artemis.UI.Shared.Ninject; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using Artemis.UI.Stylet; using Artemis.UI.Stylet;
using Ninject; using Ninject;

View File

@ -1,8 +1,8 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:model="clr-namespace:MaterialDesignExtensions.Model;assembly=MaterialDesignExtensions" xmlns:model="clr-namespace:MaterialDesignExtensions.Model;assembly=MaterialDesignExtensions"
xmlns:shared="clr-namespace:Artemis.UI.Shared.Controls;assembly=Artemis.UI.Shared" xmlns:controls="clr-namespace:MaterialDesignExtensions.Controls;assembly=MaterialDesignExtensions"
xmlns:controls="clr-namespace:MaterialDesignExtensions.Controls;assembly=MaterialDesignExtensions"> xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared">
<DataTemplate DataType="{x:Type model:FirstLevelNavigationItem}"> <DataTemplate DataType="{x:Type model:FirstLevelNavigationItem}">
<Grid Height="48"> <Grid Height="48">

View File

@ -6,7 +6,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:home="clr-namespace:Artemis.UI.Screens.Home" xmlns:home="clr-namespace:Artemis.UI.Screens.Home"
xmlns:controls="clr-namespace:Artemis.UI.Shared.Controls;assembly=Artemis.UI.Shared" xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="574.026" d:DesignHeight="574.026"
d:DesignWidth="1029.87" d:DesignWidth="1029.87"
@ -37,7 +37,7 @@
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<controls:ArtemisIcon SvgSource="/Resources/Images/Logo/bow.svg" Width="100" Height="100"/> <shared:ArtemisIcon SvgSource="/Resources/Images/Logo/bow.svg" Width="100" Height="100"/>
<StackPanel Grid.Column="1" Margin="24 0 0 0" VerticalAlignment="Center"> <StackPanel Grid.Column="1" Margin="24 0 0 0" VerticalAlignment="Center">
<TextBlock Style="{StaticResource MaterialDesignHeadline4TextBlock}" TextWrapping="Wrap">Welcome to Artemis, RGB on steroids.</TextBlock> <TextBlock Style="{StaticResource MaterialDesignHeadline4TextBlock}" TextWrapping="Wrap">Welcome to Artemis, RGB on steroids.</TextBlock>
<Button Style="{StaticResource MaterialDesignFlatButton}" <Button Style="{StaticResource MaterialDesignFlatButton}"

View File

@ -4,6 +4,7 @@ using System.Linq;
using System.Windows.Media; using System.Windows.Media;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Extensions;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;

View File

@ -4,6 +4,7 @@ using System.Linq;
using System.Windows.Media; using System.Windows.Media;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Extensions;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;

View File

@ -8,7 +8,6 @@
xmlns:wpf="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:wpf="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:modules="clr-namespace:Artemis.Core.Modules;assembly=Artemis.Core" xmlns:modules="clr-namespace:Artemis.Core.Modules;assembly=Artemis.Core"
xmlns:dataModel="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared" xmlns:dataModel="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
xmlns:controls="clr-namespace:Artemis.UI.Shared.Controls;assembly=Artemis.UI.Shared"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" d:DataContext="{d:DesignInstance local:DataModelDebugViewModel}"> d:DesignHeight="450" d:DesignWidth="800" d:DataContext="{d:DesignInstance local:DataModelDebugViewModel}">
<UserControl.Resources> <UserControl.Resources>

View File

@ -6,7 +6,6 @@
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:s="https://github.com/canton7/Stylet" xmlns:s="https://github.com/canton7/Stylet"
xmlns:devices="clr-namespace:Artemis.UI.Screens.Settings.Tabs.Plugins" xmlns:devices="clr-namespace:Artemis.UI.Screens.Settings.Tabs.Plugins"
xmlns:controls="clr-namespace:Artemis.UI.Shared.Controls;assembly=Artemis.UI.Shared"
xmlns:b="http://schemas.microsoft.com/xaml/behaviors" xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared" xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
d:DataContext="{d:DesignInstance devices:PluginSettingsViewModel}" d:DataContext="{d:DesignInstance devices:PluginSettingsViewModel}"
@ -34,7 +33,7 @@
<RowDefinition Height="*" /> <RowDefinition Height="*" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<controls:ArtemisIcon Icon="{Binding Icon}" <shared:ArtemisIcon Icon="{Binding Icon}"
Width="48" Width="48"
Height="48" Height="48"
Margin="0 5 0 0" Margin="0 5 0 0"