using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Linq; using System.Reactive.Disposables; using Artemis.Core; using Artemis.Core.Services; using Artemis.UI.Exceptions; using Artemis.UI.Shared; using HidSharp.Reports.Units; using ReactiveUI; using RGB.NET.Core; using Unit = System.Reactive.Unit; namespace Artemis.UI.Screens.Device; public class InputMappingsTabViewModel : ActivatableViewModelBase { private readonly IInputService _inputService; private readonly IDeviceService _deviceService; private readonly ObservableCollection _selectedLeds; private ArtemisLed? _selectedLed; public InputMappingsTabViewModel(ArtemisDevice device, ObservableCollection selectedLeds, IInputService inputService, IDeviceService deviceService) { if (device.DeviceType != RGBDeviceType.Keyboard) throw new ArtemisUIException("The input mappings tab only supports keyboards"); _inputService = inputService; _deviceService = deviceService; _selectedLeds = selectedLeds; Device = device; DisplayName = "Input Mappings"; InputMappings = new ObservableCollection(); DeleteMapping = ReactiveCommand.Create(ExecuteDeleteMapping); this.WhenActivated(d => { _selectedLeds.CollectionChanged += SelectedLedsOnCollectionChanged; _inputService.KeyboardKeyUp += InputServiceOnKeyboardKeyUp; UpdateInputMappings(); Disposable.Create(() => { _selectedLeds.CollectionChanged -= SelectedLedsOnCollectionChanged; _inputService.KeyboardKeyUp -= InputServiceOnKeyboardKeyUp; InputMappings.Clear(); }).DisposeWith(d); }); } public ReactiveCommand DeleteMapping { get; } public ArtemisDevice Device { get; } public ArtemisLed? SelectedLed { get => _selectedLed; set => RaiseAndSetIfChanged(ref _selectedLed, value); } public ObservableCollection InputMappings { get; } private void ExecuteDeleteMapping(ArtemisInputMapping inputMapping) { Device.InputMappings.Remove(inputMapping.Original); UpdateInputMappings(); } private void InputServiceOnKeyboardKeyUp(object? sender, ArtemisKeyboardKeyEventArgs e) { if (SelectedLed == null || e.Led == null) return; // Locate the original LED the same way the InputService did it, but supply false to Device.GetLed bool foundLedId = InputKeyUtilities.TryGetLedIdFromKeyboardKey(e.Key, out LedId ledId); if (!foundLedId) return; ArtemisLed? artemisLed = Device.GetLed(ledId, false); if (artemisLed == null) return; // Apply the new LED mapping Device.InputMappings[SelectedLed] = artemisLed; _deviceService.SaveDevice(Device); _selectedLeds.Clear(); UpdateInputMappings(); } private void UpdateInputMappings() { if (InputMappings.Any()) InputMappings.Clear(); foreach (ArtemisInputMapping tuple in Device.InputMappings.Select(m => new ArtemisInputMapping(m.Key, m.Value))) InputMappings.Add(tuple); } private void SelectedLedsOnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) { SelectedLed = _selectedLeds.FirstOrDefault(); } } /// /// Represents a pair of LEDs, the original and the replacement /// public class ArtemisInputMapping { /// /// Creates a new instance of the class /// public ArtemisInputMapping(ArtemisLed original, ArtemisLed replacement) { Original = original; Replacement = replacement; } /// /// The original LED /// public ArtemisLed Original { get; set; } /// /// The replacement LED /// public ArtemisLed Replacement { get; set; } }