From 86641149c689a57529cb61bacfdd7abedfcf68f9 Mon Sep 17 00:00:00 2001 From: hexawyz <8518235+hexawyz@users.noreply.github.com> Date: Sun, 9 Feb 2025 02:27:43 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Push=20embedded=20monitor=20configu?= =?UTF-8?q?ration=20changes=20to=20the=20frontend.=20(2/2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Service/Exo.Service.Grpc/GrpcConvert.cs | 2 +- .../EmbeddedMonitorFeaturesViewModel.cs | 142 +++++++++++++++++- 2 files changed, 136 insertions(+), 8 deletions(-) diff --git a/src/Exo/Service/Exo.Service.Grpc/GrpcConvert.cs b/src/Exo/Service/Exo.Service.Grpc/GrpcConvert.cs index 3543d47..5698d3b 100644 --- a/src/Exo/Service/Exo.Service.Grpc/GrpcConvert.cs +++ b/src/Exo/Service/Exo.Service.Grpc/GrpcConvert.cs @@ -171,7 +171,7 @@ public static GrpcEmbeddedMonitorConfigurationUpdate ToGrpc(this EmbeddedMonitor DeviceId = configurationUpdate.DeviceId, MonitorId = configurationUpdate.MonitorId, GraphicsId = configurationUpdate.GraphicsId, - ImageConfiguration = configurationUpdate.GraphicsId != default ? + ImageConfiguration = configurationUpdate.GraphicsId == default ? new() { ImageId = configurationUpdate.ImageId, ImageRegion = configurationUpdate.ImageRegion.ToGrpc() } : null, }; diff --git a/src/Exo/Ui/Exo.Settings.Ui/ViewModels/EmbeddedMonitorFeaturesViewModel.cs b/src/Exo/Ui/Exo.Settings.Ui/ViewModels/EmbeddedMonitorFeaturesViewModel.cs index 2f33dc2..0055c9e 100644 --- a/src/Exo/Ui/Exo.Settings.Ui/ViewModels/EmbeddedMonitorFeaturesViewModel.cs +++ b/src/Exo/Ui/Exo.Settings.Ui/ViewModels/EmbeddedMonitorFeaturesViewModel.cs @@ -18,6 +18,7 @@ internal sealed class EmbeddedMonitorFeaturesViewModel : BindableObject, IDispos private readonly ObservableCollection _embeddedMonitors; private readonly ReadOnlyObservableCollection _readOnlyEmbeddedMonitors; private readonly Dictionary _embeddedMonitorById; + private readonly Dictionary _pendingConfigurationUpdates; private bool _isExpanded; private readonly PropertyChangedEventHandler _onRasterizationScaleProviderPropertyChanged; @@ -37,6 +38,7 @@ IEmbeddedMonitorService embeddedMonitorService _embeddedMonitorService = embeddedMonitorService; _embeddedMonitors = new(); _embeddedMonitorById = new(); + _pendingConfigurationUpdates = new(); _readOnlyEmbeddedMonitors = new(_embeddedMonitors); _onRasterizationScaleProviderPropertyChanged = OnRasterizationScaleProviderPropertyChanged; rasterizationScaleProvider.PropertyChanged += _onRasterizationScaleProviderPropertyChanged; @@ -97,11 +99,23 @@ internal void UpdateInformation(EmbeddedMonitorDeviceInformation information) _embeddedMonitorById.Add(monitorInformation.MonitorId, vm); _embeddedMonitors.Add(vm); } + if (_pendingConfigurationUpdates.Remove(monitorInformation.MonitorId, out var configuration)) + { + vm.UpdateConfiguration(configuration); + } } } internal void UpdateConfiguration(EmbeddedMonitorConfigurationUpdate configuration) { + if (_embeddedMonitorById.TryGetValue(configuration.MonitorId, out var monitor)) + { + monitor.UpdateConfiguration(configuration); + } + else + { + _pendingConfigurationUpdates.Add(configuration.MonitorId, configuration); + } } } @@ -114,7 +128,7 @@ internal sealed class EmbeddedMonitorViewModel : ApplicableResettableBindableObj private EmbeddedMonitorCapabilities _capabilities; private readonly ObservableCollection _supportedGraphics; private readonly ReadOnlyObservableCollection _readOnlySupportedGraphics; - private EmbeddedMonitorGraphicsViewModel? _initialCurrentGraphics; + private Guid _initialCurrentGraphicsId; private EmbeddedMonitorGraphicsViewModel? _currentGraphics; public EmbeddedMonitorViewModel(EmbeddedMonitorFeaturesViewModel owner, EmbeddedMonitorInformation information) @@ -141,12 +155,13 @@ public EmbeddedMonitorViewModel(EmbeddedMonitorFeaturesViewModel owner, Embedded _readOnlySupportedGraphics = new(_supportedGraphics); if (_supportedGraphics.Count > 0) { - _initialCurrentGraphics = _currentGraphics = imageGraphics ?? _supportedGraphics[0]; + _currentGraphics = imageGraphics ?? _supportedGraphics[0]; + _initialCurrentGraphicsId = _currentGraphics.Id; } } - private bool IsChangedExceptGraphics => !ReferenceEquals(_initialCurrentGraphics, _currentGraphics); - public override bool IsChanged => IsChangedExceptGraphics || CurrentGraphics?.IsChanged == true; + private bool IsChangedNonRecursive => _currentGraphics?.Id != _initialCurrentGraphicsId; + public override bool IsChanged => IsChangedNonRecursive || CurrentGraphics?.IsChanged == true; protected override bool CanApply => IsChanged && _currentGraphics?.IsValid == true; public Guid MonitorId => _monitorId; @@ -230,7 +245,7 @@ internal void NotifyDpiChange() internal void NotifyGraphicsChanged(EmbeddedMonitorGraphicsViewModel graphics, bool isChanged) { - if (ReferenceEquals(graphics, _currentGraphics) && !IsChangedExceptGraphics) + if (ReferenceEquals(graphics, _currentGraphics) && !IsChangedNonRecursive) { OnChanged(isChanged); } @@ -248,7 +263,55 @@ protected override async Task ApplyChangesAsync(CancellationToken cancellationTo protected override void Reset() { - CurrentGraphics = _initialCurrentGraphics; + foreach (var supportedGraphics in _supportedGraphics) + { + if (supportedGraphics.Id == _initialCurrentGraphicsId) + { + CurrentGraphics = supportedGraphics; + break; + } + } + _currentGraphics?.Reset(); + } + + internal void UpdateConfiguration(EmbeddedMonitorConfigurationUpdate configuration) + { + bool wasChanged = IsChanged; + + EmbeddedMonitorGraphicsViewModel? newGraphics = null; + if (_currentGraphics?.Id == configuration.GraphicsId) + { + newGraphics = _currentGraphics; + } + else + { + foreach (var supportedGraphics in _supportedGraphics) + { + if (supportedGraphics.Id == configuration.GraphicsId) + { + newGraphics = supportedGraphics; + break; + } + } + } + + // Just as a small optimization, we update the contents of the graphics first, so that in the event where it is not the one displayed, we will avoid unnecessary UI updates. + if (newGraphics is not null && configuration.GraphicsId == default && configuration.ImageConfiguration is { } imageConfiguration) + { + ((EmbeddedMonitorImageGraphicsViewModel)newGraphics).UpdateConfiguration(imageConfiguration); + } + + if (_initialCurrentGraphicsId != configuration.GraphicsId) + { + if (_currentGraphics is null ? newGraphics is not null : (_currentGraphics.Id == _initialCurrentGraphicsId && newGraphics is not null)) + { + _currentGraphics = newGraphics; + NotifyPropertyChanged(ChangedProperty.CurrentGraphics); + } + _initialCurrentGraphicsId = configuration.GraphicsId; + } + + OnChangeStateChange(wasChanged); } } @@ -288,6 +351,8 @@ protected override void OnChanged(bool isChanged) } internal abstract ValueTask ApplyAsync(CancellationToken cancellationToken); + + internal virtual void Reset() { } } internal sealed class EmbeddedMonitorBuiltInGraphicsViewModel : EmbeddedMonitorGraphicsViewModel @@ -315,6 +380,7 @@ internal override async ValueTask ApplyAsync(CancellationToken cancellationToken internal sealed class EmbeddedMonitorImageGraphicsViewModel : EmbeddedMonitorGraphicsViewModel, IDisposable { private UInt128 _initialImageId; + private Rectangle _initialCropRectangle; private Rectangle _cropRectangle; private ImageViewModel? _image; private readonly PropertyChangedEventHandler _onMonitorPropertyChanged; @@ -344,7 +410,7 @@ private void OnMonitorPropertyChanged(object? sender, PropertyChangedEventArgs e } } - public override bool IsChanged => (_image?.Id).GetValueOrDefault() != _initialImageId; + public override bool IsChanged => (_image?.Id).GetValueOrDefault() != _initialImageId || _initialCropRectangle != _cropRectangle; public override bool IsValid => _image is not null && IsRegionValid(_cropRectangle); @@ -432,4 +498,66 @@ await Monitor.Owner.EmbeddedMonitorService.SetImageAsync cancellationToken ); } + + internal void UpdateConfiguration(EmbeddedMonitorImageConfiguration configuration) + { + bool wasChanged = IsChanged; + bool imageChanged = false; + bool cropRectangleChanged = false; + if (_initialImageId != configuration.ImageId) + { + if (_image is null || _image.Id == _initialImageId) + { + foreach (var image in AvailableImages) + { + if (image.Id == configuration.ImageId) + { + _image = image; + imageChanged = true; + break; + } + } + } + _initialImageId = configuration.ImageId; + } + if (_initialCropRectangle != configuration.ImageRegion) + { + if (_cropRectangle == _initialCropRectangle) + { + _cropRectangle = configuration.ImageRegion; + cropRectangleChanged = true; + } + _initialCropRectangle = configuration.ImageRegion; + } + if (imageChanged) NotifyPropertyChanged(ChangedProperty.Image); + if (cropRectangleChanged) NotifyPropertyChanged(ChangedProperty.CropRectangle); + OnChangeStateChange(wasChanged); + } + + internal override void Reset() + { + if (!IsChanged) return; + bool imageChanged = false; + bool cropRectangleChanged = false; + if (_image?.Id != _initialImageId) + { + foreach (var image in AvailableImages) + { + if (image.Id == _initialImageId) + { + _image = image; + imageChanged = true; + break; + } + } + } + if (_cropRectangle != _initialCropRectangle) + { + _cropRectangle = _initialCropRectangle; + cropRectangleChanged = true; + } + if (imageChanged) NotifyPropertyChanged(ChangedProperty.Image); + if (cropRectangleChanged) NotifyPropertyChanged(ChangedProperty.CropRectangle); + OnChangeStateChange(true); + } }