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

Merge pull request #716 from Artemis-RGB/development

Avalonia Bugfixes
This commit is contained in:
RobertBeekman 2022-08-22 20:27:58 +02:00 committed by GitHub
commit 1951477590
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 190 additions and 22 deletions

View File

@ -51,6 +51,7 @@ public class ColorGradient : IList<ColorGradientStop>, IList, INotifyCollectionC
{ {
ColorGradientStop stop = new(colorGradientStop.Color, colorGradientStop.Position); ColorGradientStop stop = new(colorGradientStop.Color, colorGradientStop.Position);
stop.PropertyChanged += ItemOnPropertyChanged; stop.PropertyChanged += ItemOnPropertyChanged;
stop.ColorGradient = this;
_stops.Add(stop); _stops.Add(stop);
} }
} }
@ -66,6 +67,7 @@ public class ColorGradient : IList<ColorGradientStop>, IList, INotifyCollectionC
{ {
ColorGradientStop stop = new(colorGradientStop.Color, colorGradientStop.Position); ColorGradientStop stop = new(colorGradientStop.Color, colorGradientStop.Position);
stop.PropertyChanged += ItemOnPropertyChanged; stop.PropertyChanged += ItemOnPropertyChanged;
stop.ColorGradient = this;
_stops.Add(stop); _stops.Add(stop);
} }
} }
@ -465,6 +467,7 @@ public class ColorGradient : IList<ColorGradientStop>, IList, INotifyCollectionC
public void Add(ColorGradientStop item) public void Add(ColorGradientStop item)
{ {
_stops.Add(item); _stops.Add(item);
item.ColorGradient = this;
item.PropertyChanged += ItemOnPropertyChanged; item.PropertyChanged += ItemOnPropertyChanged;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, _stops.IndexOf(item))); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, _stops.IndexOf(item)));

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Linq;
using SkiaSharp; using SkiaSharp;
namespace Artemis.Core; namespace Artemis.Core;
@ -35,9 +36,11 @@ public class ColorGradientStop : CorePropertyChanged
public float Position public float Position
{ {
get => _position; get => _position;
set => SetAndNotify(ref _position, value); set => SetAndNotify(ref _position, GetUpdatedPosition(value));
} }
internal ColorGradient? ColorGradient { get; set; }
#region Equality members #region Equality members
/// <inheritdoc cref="object.Equals(object)" /> /// <inheritdoc cref="object.Equals(object)" />
@ -64,5 +67,32 @@ public class ColorGradientStop : CorePropertyChanged
return HashCode.Combine(_color, _position); return HashCode.Combine(_color, _position);
} }
/// <summary>
/// Gets the position of the given color stop in a safe manner that avoids overlap with other stops.
/// </summary>
/// <param name="stopPosition">The new position.</param>
private float GetUpdatedPosition(float stopPosition)
{
if (ColorGradient == null)
return stopPosition;
// Find the first available spot going down
while (ColorGradient.Any(s => !ReferenceEquals(s, this) && Math.Abs(s.Position - stopPosition) < 0.001f))
stopPosition -= 0.001f;
// If we ran out of space, try going up
if (stopPosition < 0)
{
stopPosition = 0;
while (ColorGradient.Any(s => !ReferenceEquals(s, this) && Math.Abs(s.Position - stopPosition) < 0.001f))
stopPosition += 0.001f;
}
// If we ran out of space there too, movement isn't possible
if (stopPosition > 1)
stopPosition = Position;
return stopPosition;
}
#endregion #endregion
} }

View File

@ -0,0 +1,63 @@
using System;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Interactivity;
using Avalonia.Xaml.Interactivity;
using FluentAvalonia.UI.Controls;
namespace Artemis.UI.Shared.Behaviors;
/// <summary>
/// Represents a behavior that can be used to make a numeric up down only update it's binding on focus loss.
/// </summary>
public class LostFocusNumericUpDownBindingBehavior : Behavior<NumericUpDown>
{
/// <summary>
/// Gets or sets the value of the binding.
/// </summary>
public static readonly StyledProperty<double> ValueProperty = AvaloniaProperty.Register<LostFocusNumericUpDownBindingBehavior, double>(
nameof(Value), defaultBindingMode: BindingMode.TwoWay);
static LostFocusNumericUpDownBindingBehavior()
{
ValueProperty.Changed.Subscribe(e => ((LostFocusNumericUpDownBindingBehavior) e.Sender).OnBindingValueChanged());
}
/// <summary>
/// Gets or sets the value of the binding.
/// </summary>
public double Value
{
get => GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}
/// <inheritdoc />
protected override void OnAttached()
{
if (AssociatedObject != null)
AssociatedObject.LostFocus += OnLostFocus;
base.OnAttached();
}
/// <inheritdoc />
protected override void OnDetaching()
{
if (AssociatedObject != null)
AssociatedObject.LostFocus -= OnLostFocus;
base.OnDetaching();
}
private void OnLostFocus(object? sender, RoutedEventArgs e)
{
if (AssociatedObject != null)
Value = AssociatedObject.Value;
}
private void OnBindingValueChanged()
{
if (AssociatedObject != null)
AssociatedObject.Value = Value;
}
}

View File

@ -4,7 +4,8 @@
xmlns:core="clr-namespace:Artemis.Core;assembly=Artemis.Core" xmlns:core="clr-namespace:Artemis.Core;assembly=Artemis.Core"
xmlns:fluent="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:fluent="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:converters="clr-namespace:Artemis.UI.Shared.Converters" xmlns:converters="clr-namespace:Artemis.UI.Shared.Converters"
xmlns:gradientPicker="clr-namespace:Artemis.UI.Shared.Controls.GradientPicker"> xmlns:gradientPicker="clr-namespace:Artemis.UI.Shared.Controls.GradientPicker"
xmlns:behaviors="clr-namespace:Artemis.UI.Shared.Behaviors">
<Styles.Resources> <Styles.Resources>
<VisualBrush x:Key="LightCheckerboardBrush" TileMode="Tile" Stretch="Uniform" DestinationRect="3,0,10,10"> <VisualBrush x:Key="LightCheckerboardBrush" TileMode="Tile" Stretch="Uniform" DestinationRect="3,0,10,10">
<VisualBrush.Visual> <VisualBrush.Visual>
@ -198,7 +199,11 @@
<Border CornerRadius="4" Margin="-2" Background="{Binding Color, Converter={StaticResource SKColorToBrushConverter}}" /> <Border CornerRadius="4" Margin="-2" Background="{Binding Color, Converter={StaticResource SKColorToBrushConverter}}" />
</Border> </Border>
<TextBox Grid.Column="1" Text="{Binding Color, Converter={StaticResource SKColorToStringConverter}}" /> <TextBox Grid.Column="1" Text="{Binding Color, Converter={StaticResource SKColorToStringConverter}}" />
<NumericUpDown Grid.Column="2" Value="{Binding Position}" FormatString="F3" ShowButtonSpinner="False" Margin="5 0" /> <NumericUpDown Grid.Column="2" FormatString="F3" ShowButtonSpinner="False" Margin="5 0" Minimum="0" Maximum="1" Increment="0.01">
<Interaction.Behaviors>
<behaviors:LostFocusNumericUpDownBindingBehavior Value="{Binding Position}"/>
</Interaction.Behaviors>
</NumericUpDown>
<Button Name="DeleteButton" <Button Name="DeleteButton"
Grid.Column="3" Grid.Column="3"
Classes="icon-button" Classes="icon-button"

View File

@ -37,7 +37,7 @@
ShowColors="True" ShowColors="True"
Margin="20" Margin="20"
LedClicked="DeviceVisualizer_OnLedClicked" LedClicked="DeviceVisualizer_OnLedClicked"
Clicked="DeviceVisualizer_OnClicked"/> Clicked="DeviceVisualizer_OnClicked" />
<StackPanel Orientation="Horizontal" <StackPanel Orientation="Horizontal"
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
@ -53,13 +53,20 @@
<GridSplitter Grid.Column="1" Width="15" Margin="-15 0 0 0" Background="Transparent" HorizontalAlignment="Stretch" /> <GridSplitter Grid.Column="1" Width="15" Margin="-15 0 0 0" Background="Transparent" HorizontalAlignment="Stretch" />
<Border Grid.Column="2" Classes="card-condensed" CornerRadius="10 0 0 0" Margin="0 10 0 0" Background="#ff323232"> <Border Grid.Column="2" Classes="card-condensed" CornerRadius="10 0 0 0" Margin="0 10 0 0" Background="#ff323232">
<TabControl Items="{CompiledBinding Tabs}" Padding="12"> <Panel>
<TabControl.ItemTemplate> <TabControl Items="{CompiledBinding Tabs}" IsVisible="{CompiledBinding Tabs.Count}" Padding="12">
<DataTemplate> <TabControl.ItemTemplate>
<TextBlock Text="{CompiledBinding DisplayName}" /> <DataTemplate>
</DataTemplate> <TextBlock Text="{CompiledBinding DisplayName}" />
</TabControl.ItemTemplate> </DataTemplate>
</TabControl> </TabControl.ItemTemplate>
</TabControl>
<StackPanel IsVisible="{CompiledBinding !Tabs.Count}" VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBlock TextAlignment="Center">Waiting for device...</TextBlock>
<ProgressBar IsIndeterminate="True" Margin="0 15 0 0"></ProgressBar>
</StackPanel>
</Panel>
</Border> </Border>
<StackPanel Grid.Column="0" Grid.ColumnSpan="3" Classes="notification-container" Name="NotificationContainer" VerticalAlignment="Bottom" HorizontalAlignment="Right" /> <StackPanel Grid.Column="0" Grid.ColumnSpan="3" Classes="notification-container" Name="NotificationContainer" VerticalAlignment="Bottom" HorizontalAlignment="Right" />

View File

@ -14,20 +14,22 @@ namespace Artemis.UI.Screens.Device;
public class DevicePropertiesViewModel : DialogViewModelBase<object> public class DevicePropertiesViewModel : DialogViewModelBase<object>
{ {
public DevicePropertiesViewModel(ArtemisDevice device, ICoreService coreService, IDeviceVmFactory deviceVmFactory) private readonly IDeviceVmFactory _deviceVmFactory;
private ArtemisDevice _device;
public DevicePropertiesViewModel(ArtemisDevice device, ICoreService coreService, IRgbService rgbService, IDeviceVmFactory deviceVmFactory)
{ {
Device = device; _deviceVmFactory = deviceVmFactory;
_device = device;
SelectedLeds = new ObservableCollection<ArtemisLed>(); SelectedLeds = new ObservableCollection<ArtemisLed>();
Tabs = new ObservableCollection<ActivatableViewModelBase>(); Tabs = new ObservableCollection<ActivatableViewModelBase>();
Tabs.Add(deviceVmFactory.DevicePropertiesTabViewModel(device)); AddTabs();
Tabs.Add(deviceVmFactory.DeviceInfoTabViewModel(device));
if (Device.DeviceType == RGBDeviceType.Keyboard)
Tabs.Add(deviceVmFactory.InputMappingsTabViewModel(device, SelectedLeds));
Tabs.Add(deviceVmFactory.DeviceLedsTabViewModel(device, SelectedLeds));
this.WhenActivated(d => this.WhenActivated(d =>
{ {
rgbService.DeviceAdded += RgbServiceOnDeviceAdded;
rgbService.DeviceRemoved += RgbServiceOnDeviceRemoved;
coreService.FrameRendering += CoreServiceOnFrameRendering; coreService.FrameRendering += CoreServiceOnFrameRendering;
Disposable.Create(() => coreService.FrameRendering -= CoreServiceOnFrameRendering).DisposeWith(d); Disposable.Create(() => coreService.FrameRendering -= CoreServiceOnFrameRendering).DisposeWith(d);
}); });
@ -35,12 +37,40 @@ public class DevicePropertiesViewModel : DialogViewModelBase<object>
ClearSelectedLeds = ReactiveCommand.Create(ExecuteClearSelectedLeds); ClearSelectedLeds = ReactiveCommand.Create(ExecuteClearSelectedLeds);
} }
public ArtemisDevice Device { get; } private void RgbServiceOnDeviceAdded(object? sender, DeviceEventArgs e)
{
if (e.Device.Identifier != Device.Identifier || Device == e.Device)
return;
Device = e.Device;
AddTabs();
}
private void RgbServiceOnDeviceRemoved(object? sender, DeviceEventArgs e)
{
Tabs.Clear();
SelectedLeds.Clear();
}
public ArtemisDevice Device
{
get => _device;
set => RaiseAndSetIfChanged(ref _device, value);
}
public ObservableCollection<ArtemisLed> SelectedLeds { get; } public ObservableCollection<ArtemisLed> SelectedLeds { get; }
public ObservableCollection<ActivatableViewModelBase> Tabs { get; } public ObservableCollection<ActivatableViewModelBase> Tabs { get; }
public ReactiveCommand<Unit, Unit> ClearSelectedLeds { get; } public ReactiveCommand<Unit, Unit> ClearSelectedLeds { get; }
private void AddTabs()
{
Tabs.Add(_deviceVmFactory.DevicePropertiesTabViewModel(Device));
Tabs.Add(_deviceVmFactory.DeviceInfoTabViewModel(Device));
if (Device.DeviceType == RGBDeviceType.Keyboard)
Tabs.Add(_deviceVmFactory.InputMappingsTabViewModel(Device, SelectedLeds));
Tabs.Add(_deviceVmFactory.DeviceLedsTabViewModel(Device, SelectedLeds));
}
private void ExecuteClearSelectedLeds() private void ExecuteClearSelectedLeds()
{ {
SelectedLeds.Clear(); SelectedLeds.Clear();

View File

@ -21,6 +21,7 @@ public class SurfaceEditorViewModel : MainScreenViewModel
private readonly IDeviceService _deviceService; private readonly IDeviceService _deviceService;
private readonly IDeviceVmFactory _deviceVmFactory; private readonly IDeviceVmFactory _deviceVmFactory;
private readonly IRgbService _rgbService; private readonly IRgbService _rgbService;
private readonly ISurfaceVmFactory _surfaceVmFactory;
private readonly ISettingsService _settingsService; private readonly ISettingsService _settingsService;
private readonly IWindowService _windowService; private readonly IWindowService _windowService;
private bool _colorDevices; private bool _colorDevices;
@ -39,6 +40,7 @@ public class SurfaceEditorViewModel : MainScreenViewModel
IDeviceService deviceService) : base(hostScreen, "surface-editor") IDeviceService deviceService) : base(hostScreen, "surface-editor")
{ {
_rgbService = rgbService; _rgbService = rgbService;
_surfaceVmFactory = surfaceVmFactory;
_settingsService = settingsService; _settingsService = settingsService;
_deviceVmFactory = deviceVmFactory; _deviceVmFactory = deviceVmFactory;
_windowService = windowService; _windowService = windowService;
@ -59,10 +61,38 @@ public class SurfaceEditorViewModel : MainScreenViewModel
this.WhenActivated(d => this.WhenActivated(d =>
{ {
coreService.FrameRendering += CoreServiceOnFrameRendering; coreService.FrameRendering += CoreServiceOnFrameRendering;
Disposable.Create(() => coreService.FrameRendering -= CoreServiceOnFrameRendering).DisposeWith(d); _rgbService.DeviceAdded += RgbServiceOnDeviceAdded;
_rgbService.DeviceRemoved += RgbServiceOnDeviceRemoved;
Disposable.Create(() =>
{
coreService.FrameRendering -= CoreServiceOnFrameRendering;
_rgbService.DeviceAdded -= RgbServiceOnDeviceAdded;
_rgbService.DeviceRemoved -= RgbServiceOnDeviceRemoved;
}).DisposeWith(d);
}); });
} }
private void RgbServiceOnDeviceAdded(object? sender, DeviceEventArgs e)
{
if (!e.Device.IsEnabled)
return;
SurfaceDeviceViewModels.Add(_surfaceVmFactory.SurfaceDeviceViewModel(e.Device, this));
ListDeviceViewModels.Add(_surfaceVmFactory.ListDeviceViewModel(e.Device, this));
SurfaceDeviceViewModels.Sort(l => l.Device.ZIndex * -1);
ListDeviceViewModels.Sort(l => l.Device.ZIndex * -1);
}
private void RgbServiceOnDeviceRemoved(object? sender, DeviceEventArgs e)
{
SurfaceDeviceViewModel? surfaceVm = SurfaceDeviceViewModels.FirstOrDefault(vm => vm.Device == e.Device);
ListDeviceViewModel? listVm = ListDeviceViewModels.FirstOrDefault(vm => vm.Device == e.Device);
if (surfaceVm != null)
SurfaceDeviceViewModels.Remove(surfaceVm);
if (listVm != null)
ListDeviceViewModels.Remove(listVm);
}
public bool ColorDevices public bool ColorDevices
{ {
get => _colorDevices; get => _colorDevices;