mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
commit
1951477590
@ -51,6 +51,7 @@ public class ColorGradient : IList<ColorGradientStop>, IList, INotifyCollectionC
|
||||
{
|
||||
ColorGradientStop stop = new(colorGradientStop.Color, colorGradientStop.Position);
|
||||
stop.PropertyChanged += ItemOnPropertyChanged;
|
||||
stop.ColorGradient = this;
|
||||
_stops.Add(stop);
|
||||
}
|
||||
}
|
||||
@ -66,6 +67,7 @@ public class ColorGradient : IList<ColorGradientStop>, IList, INotifyCollectionC
|
||||
{
|
||||
ColorGradientStop stop = new(colorGradientStop.Color, colorGradientStop.Position);
|
||||
stop.PropertyChanged += ItemOnPropertyChanged;
|
||||
stop.ColorGradient = this;
|
||||
_stops.Add(stop);
|
||||
}
|
||||
}
|
||||
@ -465,6 +467,7 @@ public class ColorGradient : IList<ColorGradientStop>, IList, INotifyCollectionC
|
||||
public void Add(ColorGradientStop item)
|
||||
{
|
||||
_stops.Add(item);
|
||||
item.ColorGradient = this;
|
||||
item.PropertyChanged += ItemOnPropertyChanged;
|
||||
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, _stops.IndexOf(item)));
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.Core;
|
||||
@ -35,9 +36,11 @@ public class ColorGradientStop : CorePropertyChanged
|
||||
public float Position
|
||||
{
|
||||
get => _position;
|
||||
set => SetAndNotify(ref _position, value);
|
||||
set => SetAndNotify(ref _position, GetUpdatedPosition(value));
|
||||
}
|
||||
|
||||
internal ColorGradient? ColorGradient { get; set; }
|
||||
|
||||
#region Equality members
|
||||
|
||||
/// <inheritdoc cref="object.Equals(object)" />
|
||||
@ -64,5 +67,32 @@ public class ColorGradientStop : CorePropertyChanged
|
||||
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
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -4,7 +4,8 @@
|
||||
xmlns:core="clr-namespace:Artemis.Core;assembly=Artemis.Core"
|
||||
xmlns:fluent="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
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>
|
||||
<VisualBrush x:Key="LightCheckerboardBrush" TileMode="Tile" Stretch="Uniform" DestinationRect="3,0,10,10">
|
||||
<VisualBrush.Visual>
|
||||
@ -198,7 +199,11 @@
|
||||
<Border CornerRadius="4" Margin="-2" Background="{Binding Color, Converter={StaticResource SKColorToBrushConverter}}" />
|
||||
</Border>
|
||||
<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"
|
||||
Grid.Column="3"
|
||||
Classes="icon-button"
|
||||
|
||||
@ -37,7 +37,7 @@
|
||||
ShowColors="True"
|
||||
Margin="20"
|
||||
LedClicked="DeviceVisualizer_OnLedClicked"
|
||||
Clicked="DeviceVisualizer_OnClicked"/>
|
||||
Clicked="DeviceVisualizer_OnClicked" />
|
||||
|
||||
<StackPanel Orientation="Horizontal"
|
||||
VerticalAlignment="Bottom"
|
||||
@ -53,13 +53,20 @@
|
||||
<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">
|
||||
<TabControl Items="{CompiledBinding Tabs}" Padding="12">
|
||||
<TabControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{CompiledBinding DisplayName}" />
|
||||
</DataTemplate>
|
||||
</TabControl.ItemTemplate>
|
||||
</TabControl>
|
||||
<Panel>
|
||||
<TabControl Items="{CompiledBinding Tabs}" IsVisible="{CompiledBinding Tabs.Count}" Padding="12">
|
||||
<TabControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{CompiledBinding DisplayName}" />
|
||||
</DataTemplate>
|
||||
</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>
|
||||
|
||||
<StackPanel Grid.Column="0" Grid.ColumnSpan="3" Classes="notification-container" Name="NotificationContainer" VerticalAlignment="Bottom" HorizontalAlignment="Right" />
|
||||
|
||||
@ -14,20 +14,22 @@ namespace Artemis.UI.Screens.Device;
|
||||
|
||||
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>();
|
||||
Tabs = new ObservableCollection<ActivatableViewModelBase>();
|
||||
|
||||
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));
|
||||
|
||||
AddTabs();
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
rgbService.DeviceAdded += RgbServiceOnDeviceAdded;
|
||||
rgbService.DeviceRemoved += RgbServiceOnDeviceRemoved;
|
||||
coreService.FrameRendering += CoreServiceOnFrameRendering;
|
||||
Disposable.Create(() => coreService.FrameRendering -= CoreServiceOnFrameRendering).DisposeWith(d);
|
||||
});
|
||||
@ -35,12 +37,40 @@ public class DevicePropertiesViewModel : DialogViewModelBase<object>
|
||||
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<ActivatableViewModelBase> Tabs { 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()
|
||||
{
|
||||
SelectedLeds.Clear();
|
||||
|
||||
@ -21,6 +21,7 @@ public class SurfaceEditorViewModel : MainScreenViewModel
|
||||
private readonly IDeviceService _deviceService;
|
||||
private readonly IDeviceVmFactory _deviceVmFactory;
|
||||
private readonly IRgbService _rgbService;
|
||||
private readonly ISurfaceVmFactory _surfaceVmFactory;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly IWindowService _windowService;
|
||||
private bool _colorDevices;
|
||||
@ -39,6 +40,7 @@ public class SurfaceEditorViewModel : MainScreenViewModel
|
||||
IDeviceService deviceService) : base(hostScreen, "surface-editor")
|
||||
{
|
||||
_rgbService = rgbService;
|
||||
_surfaceVmFactory = surfaceVmFactory;
|
||||
_settingsService = settingsService;
|
||||
_deviceVmFactory = deviceVmFactory;
|
||||
_windowService = windowService;
|
||||
@ -59,10 +61,38 @@ public class SurfaceEditorViewModel : MainScreenViewModel
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
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
|
||||
{
|
||||
get => _colorDevices;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user