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

Shared UI - Added XML comments

This commit is contained in:
SpoinkyNL 2020-11-18 23:40:24 +01:00
parent 69f02d83ac
commit 32e80c3d05
7 changed files with 175 additions and 42 deletions

View File

@ -6,14 +6,19 @@ using Microsoft.Xaml.Behaviors;
namespace Artemis.UI.Shared
{
/// <summary>
/// A behavior that makes a scroll viewer bubble up its events if it is at its scroll limit
/// </summary>
public class ScrollParentWhenAtMax : Behavior<FrameworkElement>
{
/// <inheritdoc />
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.PreviewMouseWheel += PreviewMouseWheel;
}
/// <inheritdoc />
protected override void OnDetaching()
{
AssociatedObject.PreviewMouseWheel -= PreviewMouseWheel;
@ -22,10 +27,12 @@ namespace Artemis.UI.Shared
private void PreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
ScrollViewer scrollViewer = GetVisualChild<ScrollViewer>(AssociatedObject);
ScrollViewer? scrollViewer = GetVisualChild<ScrollViewer>(AssociatedObject);
if (scrollViewer == null)
return;
double scrollPos = scrollViewer.ContentVerticalOffset;
if (scrollPos == scrollViewer.ScrollableHeight && e.Delta < 0
|| scrollPos == 0 && e.Delta > 0)
// ReSharper disable once CompareOfFloatsByEqualityOperator
if (scrollPos == scrollViewer.ScrollableHeight && e.Delta < 0 || scrollPos == 0 && e.Delta > 0)
{
e.Handled = true;
MouseWheelEventArgs e2 = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta);
@ -34,9 +41,9 @@ namespace Artemis.UI.Shared
}
}
private static T GetVisualChild<T>(DependencyObject parent) where T : Visual
private static T? GetVisualChild<T>(DependencyObject parent) where T : Visual
{
T child = default;
T? child = default;
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)

View File

@ -38,8 +38,8 @@ namespace Artemis.UI.Shared
private readonly DrawingGroup _backingStore;
private readonly List<DeviceVisualizerLed> _deviceVisualizerLeds;
private readonly DispatcherTimer _timer;
private BitmapImage _deviceImage;
private ArtemisDevice _oldDevice;
private BitmapImage? _deviceImage;
private ArtemisDevice? _oldDevice;
/// <summary>
/// Creates a new instance of the <see cref="DeviceVisualizer" /> class
@ -60,7 +60,7 @@ namespace Artemis.UI.Shared
/// <summary>
/// Gets or sets the device to visualize
/// </summary>
public ArtemisDevice Device
public ArtemisDevice? Device
{
get => (ArtemisDevice) GetValue(DeviceProperty);
set => SetValue(DeviceProperty, value);
@ -78,16 +78,32 @@ namespace Artemis.UI.Shared
/// <summary>
/// Gets or sets a list of LEDs to highlight
/// </summary>
public IEnumerable<ArtemisLed> HighlightedLeds
public IEnumerable<ArtemisLed>? HighlightedLeds
{
get => (IEnumerable<ArtemisLed>) GetValue(HighlightedLedsProperty);
set => SetValue(HighlightedLedsProperty, value);
}
/// <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)
{
_timer.Stop();
}
}
/// <inheritdoc />
public void Dispose()
{
_timer.Stop();
Dispose(true);
GC.SuppressFinalize(this);
}
/// <inheritdoc />
@ -174,7 +190,8 @@ namespace Artemis.UI.Shared
if (_oldDevice != null)
{
Device.RgbDevice.PropertyChanged -= DevicePropertyChanged;
if (Device != null)
Device.RgbDevice.PropertyChanged -= DevicePropertyChanged;
_oldDevice = null;
}
}
@ -224,7 +241,7 @@ namespace Artemis.UI.Shared
UpdateTransform();
// Load the device main image
if (Device.RgbDevice?.DeviceInfo?.Image?.AbsolutePath != null && File.Exists(Device.RgbDevice.DeviceInfo.Image.AbsolutePath))
if (Device.RgbDevice.DeviceInfo?.Image?.AbsolutePath != null && File.Exists(Device.RgbDevice.DeviceInfo.Image.AbsolutePath))
_deviceImage = new BitmapImage(Device.RgbDevice.DeviceInfo.Image);
// Create all the LEDs
@ -266,7 +283,7 @@ namespace Artemis.UI.Shared
InvalidateMeasure();
}
private void DevicePropertyChanged(object sender, PropertyChangedEventArgs e)
private void DevicePropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(Device.RgbDevice.Scale) || e.PropertyName == nameof(Device.RgbDevice.Rotation))
UpdateTransform();

View File

@ -5,9 +5,13 @@ using System.Windows.Data;
namespace Artemis.UI.Shared
{
/// <summary>
/// Converts <see langword="null"/> to <see cref="Visibility"/>
/// </summary>
public class NullToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
/// <inheritdoc />
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
Parameters direction;
if (parameter == null)
@ -23,7 +27,8 @@ namespace Artemis.UI.Shared
return value == null ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
/// <inheritdoc />
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}

View File

@ -3,10 +3,17 @@ using Stylet;
namespace Artemis.UI.Shared
{
/// <summary>
/// Represents a <see cref="DataModel" /> display view model
/// </summary>
/// <typeparam name="T">The type of the data model</typeparam>
public abstract class DataModelDisplayViewModel<T> : DataModelDisplayViewModel
{
private T _displayValue;
/// <summary>
/// Gets or sets value that the view model must display
/// </summary>
public T DisplayValue
{
get => _displayValue;
@ -19,11 +26,15 @@ namespace Artemis.UI.Shared
internal override object InternalGuard => null;
/// <inheritdoc />
public override void UpdateValue(object model)
{
DisplayValue = model is T value ? value : default;
}
/// <summary>
/// Occurs when the display value is updated
/// </summary>
protected virtual void OnDisplayValueUpdated()
{
}
@ -50,6 +61,10 @@ namespace Artemis.UI.Shared
/// </summary>
internal abstract object InternalGuard { get; }
/// <summary>
/// Updates the display value
/// </summary>
/// <param name="model">The value to set</param>
public abstract void UpdateValue(object model);
}
}

View File

@ -9,25 +9,41 @@ using Stylet;
namespace Artemis.UI.Shared
{
/// <summary>
/// Represents a <see cref="DataModel" /> input view model
/// </summary>
/// <typeparam name="T">The type of the data model</typeparam>
public abstract class DataModelInputViewModel<T> : DataModelInputViewModel
{
private bool _closed;
private T _inputValue;
/// <summary>
/// Creates a new instance of the <see cref="DataModelInputViewModel{T}" /> class
/// </summary>
/// <param name="targetDescription">The description of the property this input VM is representing</param>
/// <param name="initialValue">The initial value to set the input value to</param>
protected DataModelInputViewModel(DataModelPropertyAttribute targetDescription, T initialValue)
{
TargetDescription = targetDescription;
InputValue = initialValue;
}
/// <summary>
/// Gets or sets the value shown in the input
/// </summary>
public T InputValue
{
get => _inputValue;
set => SetAndNotify(ref _inputValue, value);
}
/// <summary>
/// Gets the description of the property this input VM is representing
/// </summary>
public DataModelPropertyAttribute TargetDescription { get; }
internal override object InternalGuard { get; } = null;
internal override object InternalGuard { get; } = new object();
/// <inheritdoc />
public sealed override void Submit()
@ -74,23 +90,6 @@ namespace Artemis.UI.Shared
/// </summary>
internal IReadOnlyCollection<Type> CompatibleConversionTypes { get; set; }
public void AttachView(UIElement view)
{
if (View != null)
throw new InvalidOperationException(string.Format("Tried to attach View {0} to ViewModel {1}, but it already has a view attached", view.GetType().Name, GetType().Name));
View = view;
// After the animation finishes attempt to focus the input field
Task.Run(async () =>
{
await Task.Delay(50);
await Execute.OnUIThreadAsync(() => View.MoveFocus(new TraversalRequest(FocusNavigationDirection.First)));
});
}
public UIElement View { get; set; }
/// <summary>
/// Submits the input value and removes this view model.
/// <para>This is called automatically when the user presses enter or clicks outside the view</para>
@ -116,5 +115,22 @@ namespace Artemis.UI.Shared
protected virtual void OnCancel()
{
}
public void AttachView(UIElement view)
{
if (View != null)
throw new InvalidOperationException(string.Format("Tried to attach View {0} to ViewModel {1}, but it already has a view attached", view.GetType().Name, GetType().Name));
View = view;
// After the animation finishes attempt to focus the input field
Task.Run(async () =>
{
await Task.Delay(50);
await Execute.OnUIThreadAsync(() => View.MoveFocus(new TraversalRequest(FocusNavigationDirection.First)));
});
}
public UIElement View { get; set; }
}
}

View File

@ -8,6 +8,9 @@ namespace Artemis.UI.Shared.Input
/// </summary>
public partial class DataModelDynamicView : UserControl
{
/// <summary>
/// Creates a new instance of the <see cref="DataModelDynamicView" /> class
/// </summary>
public DataModelDynamicView()
{
InitializeComponent();

View File

@ -16,6 +16,7 @@ namespace Artemis.UI.Shared.Input
private readonly IDataModelUIService _dataModelUIService;
private readonly Window _rootView;
private SolidColorBrush _buttonBrush = new SolidColorBrush(Color.FromRgb(171, 71, 188));
private bool _displaySwitchButton;
private DataModelDisplayViewModel _displayViewModel;
private DataModelInputViewModel _inputViewModel;
private bool _isEnabled;
@ -23,7 +24,6 @@ namespace Artemis.UI.Shared.Input
private DataModelPropertyAttribute _targetDescription;
private Type _targetType;
private object _value;
private bool _displaySwitchButton;
public DataModelStaticViewModel(Type targetType, DataModelPropertyAttribute targetDescription, IDataModelUIService dataModelUIService)
{
@ -42,6 +42,9 @@ namespace Artemis.UI.Shared.Input
}
}
/// <summary>
/// Gets or sets the brush to use for the input button
/// </summary>
public SolidColorBrush ButtonBrush
{
get => _buttonBrush;
@ -52,32 +55,50 @@ namespace Artemis.UI.Shared.Input
}
}
/// <summary>
/// Gets the brush to use for the switch button
/// </summary>
public SolidColorBrush SwitchButtonBrush => new SolidColorBrush(ButtonBrush.Color.Darken());
/// <summary>
/// Gets the view model used to display the value
/// </summary>
public DataModelDisplayViewModel DisplayViewModel
{
get => _displayViewModel;
set => SetAndNotify(ref _displayViewModel, value);
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;
@ -88,24 +109,36 @@ namespace Artemis.UI.Shared.Input
}
}
/// <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(
@ -116,6 +149,10 @@ namespace Artemis.UI.Shared.Input
);
}
/// <summary>
/// Updates the target type
/// </summary>
/// <param name="target">The new target type</param>
public void UpdateTargetType(Type target)
{
TargetType = target;
@ -135,6 +172,9 @@ namespace Artemis.UI.Shared.Input
ApplyFreeInput(TargetType.GetDefault(), true);
}
/// <summary>
/// Requests switching the input type to dynamic
/// </summary>
public void SwitchToDynamic()
{
InputViewModel?.Cancel();
@ -154,17 +194,33 @@ namespace Artemis.UI.Shared.Input
#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()
{
if (_rootView != null)
{
_rootView.MouseUp -= RootViewOnMouseUp;
_rootView.KeyUp -= RootViewOnKeyUp;
}
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
#region Event handlers
private void RootViewOnKeyUp(object sender, KeyEventArgs e)
@ -191,14 +247,28 @@ namespace Artemis.UI.Shared.Input
#region Events
public event EventHandler<DataModelInputStaticEventArgs> ValueUpdated;
public event EventHandler SwitchToDynamicRequested;
/// <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);