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

Windows - Fix crash when clicking on update notification

Meta - Code cleanup
This commit is contained in:
Robert 2022-09-03 10:34:53 +02:00
parent cdd814d920
commit 3994b49f08
36 changed files with 201 additions and 193 deletions

View File

@ -91,11 +91,6 @@ public static class Constants
/// </summary>
public static readonly Plugin CorePlugin = new(CorePluginInfo, new DirectoryInfo(ApplicationFolder), null);
/// <summary>
/// Gets the startup arguments provided to the application
/// </summary>
public static ReadOnlyCollection<string> StartupArguments { get; set; } = null!;
internal static readonly CorePluginFeature CorePluginFeature = new() {Plugin = CorePlugin, Profiler = CorePlugin.GetProfiler("Feature - Core")};
internal static readonly EffectPlaceholderPlugin EffectPlaceholderPlugin = new() {Plugin = CorePlugin, Profiler = CorePlugin.GetProfiler("Feature - Effect Placeholder")};
@ -153,6 +148,11 @@ public static class Constants
typeof(decimal)
};
/// <summary>
/// Gets the startup arguments provided to the application
/// </summary>
public static ReadOnlyCollection<string> StartupArguments { get; set; } = null!;
/// <summary>
/// Gets the graphics context to be used for rendering by SkiaSharp. Can be set via
/// <see cref="IRgbService.UpdateGraphicsContext" />.

View File

@ -1,20 +1,19 @@
using System;
namespace Artemis.Core
{
/// <summary>
/// Provides data for layer property events.
/// </summary>
public class LayerPropertyKeyframeEventArgs : EventArgs
{
internal LayerPropertyKeyframeEventArgs(ILayerPropertyKeyframe keyframe)
{
Keyframe = keyframe;
}
namespace Artemis.Core;
/// <summary>
/// Gets the keyframe this event is related to
/// </summary>
public ILayerPropertyKeyframe Keyframe { get; }
/// <summary>
/// Provides data for layer property events.
/// </summary>
public class LayerPropertyKeyframeEventArgs : EventArgs
{
internal LayerPropertyKeyframeEventArgs(ILayerPropertyKeyframe keyframe)
{
Keyframe = keyframe;
}
/// <summary>
/// Gets the keyframe this event is related to
/// </summary>
public ILayerPropertyKeyframe Keyframe { get; }
}

View File

@ -69,16 +69,16 @@ public abstract class BreakableModel : CorePropertyChanged, IBreakableModel
/// <inheritdoc />
public void ClearBrokenState(string state)
{
if (state == null)
if (state == null)
throw new ArgumentNullException(nameof(state));
// If there was no broken state to begin with, done!
if (BrokenState == null)
return;
// Only clear similar broken states
if (BrokenState != state)
return;
BrokenState = null;
BrokenStateException = null;
OnBrokenStateChanged();

View File

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
namespace Artemis.Core.Services;
@ -27,7 +26,7 @@ public interface ICoreService : IArtemisService, IDisposable
/// Gets or sets whether profiles are rendered each frame by calling their Render method
/// </summary>
bool ProfileRenderingDisabled { get; set; }
/// <summary>
/// Gets a boolean indicating whether Artemis is running in an elevated environment (admin permissions)
/// </summary>

View File

@ -0,0 +1,20 @@
using System.Collections.Generic;
using System.Diagnostics;
namespace Artemis.Core.Services;
internal class ProcessComparer : IEqualityComparer<Process>
{
public bool Equals(Process? x, Process? y)
{
if (x == null && y == null) return true;
if (x == null || y == null) return false;
return x.Id == y.Id && x.ProcessName == y.ProcessName && x.SessionId == y.SessionId;
}
public int GetHashCode(Process? obj)
{
if (obj == null) return 0;
return obj.Id;
}
}

View File

@ -4,7 +4,6 @@ using System.Diagnostics;
using System.Linq;
using System.Timers;
using Artemis.Core.Modules;
using Serilog;
namespace Artemis.Core.Services;
@ -42,20 +41,4 @@ internal class ProcessMonitorService : IProcessMonitorService
{
return _lastScannedProcesses;
}
}
internal class ProcessComparer : IEqualityComparer<Process>
{
public bool Equals(Process? x, Process? y)
{
if (x == null && y == null) return true;
if (x == null || y == null) return false;
return x.Id == y.Id && x.ProcessName == y.ProcessName && x.SessionId == y.SessionId;
}
public int GetHashCode(Process? obj)
{
if (obj == null) return 0;
return obj.Id;
}
}

View File

@ -38,6 +38,24 @@ internal class WebServerService : IWebServerService, IDisposable
StartWebServer();
}
public event EventHandler? WebServerStopped;
public event EventHandler? WebServerStarted;
protected virtual void OnWebServerStopped()
{
WebServerStopped?.Invoke(this, EventArgs.Empty);
}
protected virtual void OnWebServerStarting()
{
WebServerStarting?.Invoke(this, EventArgs.Empty);
}
protected virtual void OnWebServerStarted()
{
WebServerStarted?.Invoke(this, EventArgs.Empty);
}
private void WebServerEnabledSettingOnSettingChanged(object? sender, EventArgs e)
{
StartWebServer();
@ -76,6 +94,7 @@ internal class WebServerService : IWebServerService, IDisposable
public WebServer? Server { get; private set; }
public PluginsModule PluginsModule { get; }
public event EventHandler? WebServerStarting;
#region Web server managament
@ -129,7 +148,7 @@ internal class WebServerService : IWebServerService, IDisposable
if (!_webServerEnabledSetting.Value)
return;
if (Constants.StartupArguments.Contains("--disable-webserver"))
{
_logger.Warning("Artemis launched with --disable-webserver, not enabling the webserver");
@ -302,27 +321,4 @@ internal class WebServerService : IWebServerService, IDisposable
}
#endregion
#region Events
protected virtual void OnWebServerStopped()
{
WebServerStopped?.Invoke(this, EventArgs.Empty);
}
protected virtual void OnWebServerStarting()
{
WebServerStarting?.Invoke(this, EventArgs.Empty);
}
protected virtual void OnWebServerStarted()
{
WebServerStarted?.Invoke(this, EventArgs.Empty);
}
public event EventHandler? WebServerStopped;
public event EventHandler? WebServerStarting;
public event EventHandler? WebServerStarted;
#endregion
}

View File

@ -48,7 +48,7 @@ public readonly struct Numeric : IComparable<Numeric>, IConvertible
{
_value = value;
}
/// <summary>
/// Creates a new instance of <see cref="Numeric" /> from a <see cref="long" />
/// </summary>
@ -160,10 +160,25 @@ public readonly struct Numeric : IComparable<Numeric>, IConvertible
return (byte) Math.Clamp(p._value, 0, 255);
}
public static implicit operator Numeric(double d) => new(d);
public static implicit operator Numeric(float f) => new(f);
public static implicit operator Numeric(int i) => new(i);
public static implicit operator Numeric(byte b) => new(b);
public static implicit operator Numeric(double d)
{
return new(d);
}
public static implicit operator Numeric(float f)
{
return new(f);
}
public static implicit operator Numeric(int i)
{
return new(i);
}
public static implicit operator Numeric(byte b)
{
return new(b);
}
public static implicit operator long(Numeric p)
{

View File

@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
namespace Artemis.Core;
@ -96,7 +94,7 @@ public sealed class InputPin : Pin
{
if (type == _type)
return;
base.ChangeType(type, ref _type);
Value = type.GetDefault();
}
@ -111,9 +109,13 @@ public sealed class InputPin : Pin
Value = Type.GetDefault()!;
}
else if (ConnectedTo.Count > 0)
{
Value = ConnectedTo[0].PinValue;
}
else
{
Value = null;
}
}
#endregion

View File

@ -59,7 +59,7 @@ public interface INode : INotifyPropertyChanged, IBreakableModel
/// Called when the node resets
/// </summary>
event EventHandler Resetting;
/// <summary>
/// Occurs when a pin was added to the node
/// </summary>
@ -69,7 +69,7 @@ public interface INode : INotifyPropertyChanged, IBreakableModel
/// Occurs when a pin was removed from the node
/// </summary>
event EventHandler<SingleValueEventArgs<IPin>> PinRemoved;
/// <summary>
/// Occurs when a pin collection was added to the node
/// </summary>

View File

@ -85,7 +85,10 @@ public interface IPin
/// Determines whether this pin is compatible with the given type
/// </summary>
/// <param name="type">The type to check for compatibility</param>
/// <param name="forgivingEnumMatching">A boolean indicating whether or not enums should be exactly equal or just both be enums</param>
/// <param name="forgivingEnumMatching">
/// A boolean indicating whether or not enums should be exactly equal or just both be
/// enums
/// </param>
/// <returns><see langword="true" /> if the type is compatible, otherwise <see langword="false" />.</returns>
public bool IsTypeCompatible(Type type, bool forgivingEnumMatching = true);
}

View File

@ -26,6 +26,17 @@ internal class DataBindingExitNode<TLayerProperty> : Node, IExitNode
property.SetValue(pendingValue);
}
public override void Evaluate()
{
foreach ((IDataBindingProperty? property, InputPin? inputPin) in _propertyPins)
{
if (inputPin.ConnectedTo.Any())
_propertyValues[property] = inputPin.Value!;
else
_propertyValues.Remove(property);
}
}
private void ClearInputPins()
{
while (Pins.Any())
@ -59,15 +70,4 @@ internal class DataBindingExitNode<TLayerProperty> : Node, IExitNode
}
public override bool IsExitNode => true;
public override void Evaluate()
{
foreach ((IDataBindingProperty? property, InputPin? inputPin) in _propertyPins)
{
if (inputPin.ConnectedTo.Any())
_propertyValues[property] = inputPin.Value!;
else
_propertyValues.Remove(property);
}
}
}

View File

@ -97,7 +97,7 @@ public abstract class Node : BreakableModel, INode
/// <inheritdoc />
public override string BrokenDisplayName => Name;
#endregion
#region Construtors
@ -373,7 +373,7 @@ public abstract class Node : BreakableModel, INode
{
TryOrBreak(Evaluate, "Failed to evaluate");
}
/// <summary>
/// Called whenever the node must show it's custom view model, if <see langword="null" />, no custom view model is used
/// </summary>

View File

@ -14,6 +14,19 @@ namespace Artemis.Core;
/// </summary>
public abstract class NodeScript : CorePropertyChanged, INodeScript
{
private void NodeTypeStoreOnNodeTypeAdded(object? sender, NodeTypeStoreEvent e)
{
if (Entity.Nodes.Any(n => e.TypeRegistration.MatchesEntity(n)))
Load();
}
private void NodeTypeStoreOnNodeTypeRemoved(object? sender, NodeTypeStoreEvent e)
{
List<INode> nodes = Nodes.Where(n => n.GetType() == e.TypeRegistration.NodeData.Type).ToList();
foreach (INode node in nodes)
RemoveNode(node);
}
/// <inheritdoc />
public event EventHandler<SingleValueEventArgs<INode>>? NodeAdded;
@ -374,19 +387,6 @@ public abstract class NodeScript : CorePropertyChanged, INodeScript
}
#endregion
private void NodeTypeStoreOnNodeTypeAdded(object? sender, NodeTypeStoreEvent e)
{
if (Entity.Nodes.Any(n => e.TypeRegistration.MatchesEntity(n)))
Load();
}
private void NodeTypeStoreOnNodeTypeRemoved(object? sender, NodeTypeStoreEvent e)
{
List<INode> nodes = Nodes.Where(n => n.GetType() == e.TypeRegistration.NodeData.Type).ToList();
foreach (INode node in nodes)
RemoveNode(node);
}
}
/// <summary>

View File

@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
namespace Artemis.Core;

View File

@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using Artemis.Core.Events;
namespace Artemis.Core;

View File

@ -33,14 +33,12 @@ public class LinuxInputProvider : InputProvider
protected override void Dispose(bool disposing)
{
if (disposing)
{
for (int i = _readers.Count - 1; i >= 0; i--)
{
_readers[i].InputEvent -= OnInputEvent;
_readers[i].Dispose();
_readers.RemoveAt(i);
}
}
base.Dispose(disposing);
}
@ -51,7 +49,7 @@ public class LinuxInputProvider : InputProvider
{
if (sender is not LinuxInputDeviceReader reader)
return;
switch (reader.InputDevice.DeviceType)
{
case LinuxDeviceType.Keyboard:
@ -69,7 +67,7 @@ public class LinuxInputProvider : InputProvider
{
if (args.Type != LinuxInputEventType.KEY)
return;
KeyboardKey key = InputUtilities.KeyFromKeyCode((LinuxKeyboardKeyCodes) args.Code);
bool isDown = args.Value != 0;

View File

@ -4,7 +4,6 @@ using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Interactivity;
using Avalonia.Xaml.Interactivity;
using FluentAvalonia.UI.Controls;
namespace Artemis.UI.Shared.Behaviors;

View File

@ -19,12 +19,12 @@ public interface IMainWindowService : IArtemisSharedUIService
void ConfigureMainWindowProvider(IMainWindowProvider mainWindowProvider);
/// <summary>
/// Opens the main window if it is not already open
/// Opens the main window if it is not already open, must be called on the UI thread
/// </summary>
void OpenMainWindow();
/// <summary>
/// Closes the main window if it is not already closed
/// Closes the main window if it is not already closed, must be called on the UI thread
/// </summary>
void CloseMainWindow();
@ -37,7 +37,7 @@ public interface IMainWindowService : IArtemisSharedUIService
/// Occurs when the main window has been closed
/// </summary>
public event EventHandler? MainWindowClosed;
/// <summary>
/// Occurs when the main window has been focused
/// </summary>

View File

@ -15,7 +15,7 @@ internal class MainWindowService : IMainWindowService
{
MainWindowClosed?.Invoke(this, EventArgs.Empty);
}
protected virtual void OnMainWindowFocused()
{
MainWindowFocused?.Invoke(this, EventArgs.Empty);
@ -53,7 +53,7 @@ internal class MainWindowService : IMainWindowService
{
SyncWithManager();
}
private void HandleMainWindowFocused(object? sender, EventArgs e)
{
OnMainWindowFocused();
@ -83,7 +83,7 @@ internal class MainWindowService : IMainWindowService
_mainWindowManager.MainWindowClosed += HandleMainWindowClosed;
_mainWindowManager.MainWindowFocused += HandleMainWindowFocused;
_mainWindowManager.MainWindowUnfocused += HandleMainWindowUnfocused;
// Sync up with the new manager's state
SyncWithManager();
}

View File

@ -1,5 +1,4 @@
using System.Collections.Generic;
using System.Linq;
using System.Linq;
using Artemis.Core;
namespace Artemis.UI.Shared.Services.NodeEditor.Commands;
@ -9,9 +8,9 @@ namespace Artemis.UI.Shared.Services.NodeEditor.Commands;
/// </summary>
public class ConnectPins : INodeEditorCommand
{
private readonly IPin _output;
private readonly IPin _input;
private readonly IPin? _originalConnection;
private readonly IPin _output;
/// <summary>
/// Creates a new instance of the <see cref="ConnectPins" /> class.

View File

@ -31,7 +31,7 @@ public class NodeConnectionStore
public void Store()
{
_pinConnections.Clear();
// Iterate to save
foreach (IPin nodePin in Node.Pins.ToList())
_pinConnections.Add(nodePin, new List<IPin>(nodePin.ConnectedTo));

View File

@ -24,11 +24,6 @@ public class App : Application
private StandardKernel? _kernel;
private bool _shutDown;
// ReSharper disable NotAccessedField.Local
private ApplicationStateManager? _applicationStateManager;
private Mutex? _artemisMutex;
// ReSharper restore NotAccessedField.Local
public override void Initialize()
{
// If Artemis is already running, bring it to foreground and stop this process
@ -110,4 +105,10 @@ public class App : Application
}
}
}
// ReSharper disable NotAccessedField.Local
private ApplicationStateManager? _applicationStateManager;
private Mutex? _artemisMutex;
// ReSharper restore NotAccessedField.Local
}

View File

@ -17,10 +17,10 @@ public class WindowsInputProvider : InputProvider
private readonly IInputService _inputService;
private readonly ILogger _logger;
private readonly SpongeWindow _sponge;
private readonly Timer _taskManagerTimer;
private DateTime _lastMouseUpdate;
private int _lastProcessId;
private readonly SpongeWindow _sponge;
public WindowsInputProvider(ILogger logger, IInputService inputService)
{

View File

@ -24,8 +24,8 @@ namespace Artemis.UI.Windows.Providers;
public class UpdateProvider : IUpdateProvider, IDisposable
{
private const string ApiUrl = "https://dev.azure.com/artemis-rgb/Artemis/_apis/";
private const string InstallerUrl = "https://builds.artemis-rgb.com/binaries/Artemis.Installer.exe";
private const string API_URL = "https://dev.azure.com/artemis-rgb/Artemis/_apis/";
private const string INSTALLER_URL = "https://builds.artemis-rgb.com/binaries/Artemis.Installer.exe";
private readonly ILogger _logger;
private readonly IMainWindowService _mainWindowService;
@ -42,7 +42,7 @@ public class UpdateProvider : IUpdateProvider, IDisposable
public async Task<DevOpsBuild?> GetBuildInfo(int buildDefinition, string? buildNumber = null)
{
Url request = ApiUrl.AppendPathSegments("build", "builds")
Url request = API_URL.AppendPathSegments("build", "builds")
.SetQueryParam("definitions", buildDefinition)
.SetQueryParam("resultFilter", "succeeded")
.SetQueryParam("$top", 1)
@ -143,9 +143,9 @@ public class UpdateProvider : IUpdateProvider, IDisposable
string installerDirectory = Path.Combine(Constants.DataFolder, "installer");
string installerPath = Path.Combine(installerDirectory, "Artemis.Installer.exe");
_logger.Information("UpdateInstaller: Downloading installer from {DownloadUrl}", InstallerUrl);
_logger.Information("UpdateInstaller: Downloading installer from {DownloadUrl}", INSTALLER_URL);
using HttpClient client = new();
HttpResponseMessage httpResponseMessage = await client.GetAsync(InstallerUrl);
HttpResponseMessage httpResponseMessage = await client.GetAsync(INSTALLER_URL);
if (!httpResponseMessage.IsSuccessStatusCode)
throw new ArtemisUIException($"Failed to download installer, status code {httpResponseMessage.StatusCode}");

View File

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive;
using Artemis.Core;
using Artemis.Core.Ninject;

View File

@ -2,6 +2,7 @@ using System;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Shared.Services.MainWindow;
using Avalonia.Threading;
using EmbedIO;
using EmbedIO.Routing;
using EmbedIO.WebApi;
@ -22,7 +23,7 @@ public class RemoteController : WebApiController
[Route(HttpVerbs.Post, "/remote/bring-to-foreground")]
public void PostBringToForeground()
{
_mainWindowService.OpenMainWindow();
Dispatcher.UIThread.Post(() => _mainWindowService.OpenMainWindow());
}
[Route(HttpVerbs.Post, "/remote/restart")]

View File

@ -44,7 +44,7 @@ public class MainWindow : ReactiveCoreWindow<RootViewModel>
SetTitleBar(this.Get<Border>("DragHandle"));
}
}
private void OnActivated(object? sender, EventArgs e)
{
ViewModel?.Focused();

View File

@ -37,6 +37,16 @@ public class DevicePropertiesViewModel : DialogViewModelBase<object>
ClearSelectedLeds = ReactiveCommand.Create(ExecuteClearSelectedLeds);
}
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 RgbServiceOnDeviceAdded(object? sender, DeviceEventArgs e)
{
if (e.Device.Identifier != Device.Identifier || Device == e.Device)
@ -52,16 +62,6 @@ public class DevicePropertiesViewModel : DialogViewModelBase<object>
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));

View File

@ -15,10 +15,10 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree;
public class ProfileTreeView : ReactiveUserControl<ProfileTreeViewModel>
{
private readonly TreeView _treeView;
private Image? _dragAdorner;
private Point _dragStartPosition;
private Point _elementDragOffset;
private readonly TreeView _treeView;
public ProfileTreeView()
{

View File

@ -293,6 +293,7 @@ public abstract class TreeItemViewModel : ActivatableViewModelBase
CanPaste = false;
return;
}
CanPaste = formats.Contains(ProfileElementExtensions.ClipboardDataFormat);
}

View File

@ -22,10 +22,10 @@ public class DataBindingViewModel : ActivatableViewModelBase
private readonly IProfileEditorService _profileEditorService;
private readonly IWindowService _windowService;
private ObservableAsPropertyHelper<bool>? _dataBindingEnabled;
private bool _editorOpen;
private ObservableAsPropertyHelper<ILayerProperty?>? _layerProperty;
private ObservableAsPropertyHelper<NodeScriptViewModel?>? _nodeScriptViewModel;
private bool _playing;
private bool _editorOpen;
public DataBindingViewModel(IProfileEditorService profileEditorService, INodeVmFactory nodeVmFactory, IWindowService windowService, ISettingsService settingsService)
{

View File

@ -22,9 +22,9 @@ namespace Artemis.UI.Screens.ProfileEditor;
public class ProfileEditorViewModel : MainScreenViewModel
{
private readonly IMainWindowService _mainWindowService;
private readonly IProfileEditorService _profileEditorService;
private readonly ISettingsService _settingsService;
private readonly IMainWindowService _mainWindowService;
private readonly SourceList<IToolViewModel> _tools;
private DisplayConditionScriptViewModel? _displayConditionScriptViewModel;
private ObservableAsPropertyHelper<ProfileEditorHistory?>? _history;
@ -69,7 +69,7 @@ public class ProfileEditorViewModel : MainScreenViewModel
_profileConfiguration = profileEditorService.ProfileConfiguration.ToProperty(this, vm => vm.ProfileConfiguration).DisposeWith(d);
_history = profileEditorService.History.ToProperty(this, vm => vm.History).DisposeWith(d);
_suspendedEditing = profileEditorService.SuspendedEditing.ToProperty(this, vm => vm.SuspendedEditing).DisposeWith(d);
mainWindowService.MainWindowFocused += MainWindowServiceOnMainWindowFocused;
mainWindowService.MainWindowUnfocused += MainWindowServiceOnMainWindowUnfocused;
@ -78,7 +78,7 @@ public class ProfileEditorViewModel : MainScreenViewModel
mainWindowService.MainWindowFocused -= MainWindowServiceOnMainWindowFocused;
mainWindowService.MainWindowUnfocused -= MainWindowServiceOnMainWindowUnfocused;
}).DisposeWith(d);
// Slow and steady wins the race (and doesn't lock up the entire UI)
Dispatcher.UIThread.Post(() => StatusBarViewModel = statusBarViewModel, DispatcherPriority.Loaded);
Dispatcher.UIThread.Post(() => VisualEditorViewModel = visualEditorViewModel, DispatcherPriority.Loaded);
@ -165,7 +165,7 @@ public class ProfileEditorViewModel : MainScreenViewModel
toolViewModel.IsSelected = false;
});
}
private void MainWindowServiceOnMainWindowFocused(object? sender, EventArgs e)
{
if (_settingsService.GetSetting("ProfileEditor.AutoSuspend", true).Value)

View File

@ -54,7 +54,7 @@ public class RootViewModel : ActivatableViewModelBase, IScreen, IMainWindowProvi
_defaultTitleBarViewModel = defaultTitleBarViewModel;
_sidebarVmFactory = sidebarVmFactory;
_lifeTime = (IClassicDesktopStyleApplicationLifetime) Application.Current!.ApplicationLifetime!;
mainWindowService.ConfigureMainWindowProvider(this);
DisplayAccordingToSettings();
@ -162,20 +162,17 @@ public class RootViewModel : ActivatableViewModelBase, IScreen, IMainWindowProvi
/// <inheritdoc />
public void OpenMainWindow()
{
Dispatcher.UIThread.Post(() =>
if (_lifeTime.MainWindow == null)
{
if (_lifeTime.MainWindow == null)
{
SidebarViewModel = _sidebarVmFactory.SidebarViewModel(this);
_lifeTime.MainWindow = new MainWindow {DataContext = this};
_lifeTime.MainWindow.Show();
_lifeTime.MainWindow.Closing += CurrentMainWindowOnClosing;
}
SidebarViewModel = _sidebarVmFactory.SidebarViewModel(this);
_lifeTime.MainWindow = new MainWindow {DataContext = this};
_lifeTime.MainWindow.Show();
_lifeTime.MainWindow.Closing += CurrentMainWindowOnClosing;
}
_lifeTime.MainWindow.WindowState = WindowState.Normal;
_lifeTime.MainWindow.Activate();
OnMainWindowOpened();
});
_lifeTime.MainWindow.WindowState = WindowState.Normal;
_lifeTime.MainWindow.Activate();
OnMainWindowOpened();
}
/// <inheritdoc />
@ -183,7 +180,7 @@ public class RootViewModel : ActivatableViewModelBase, IScreen, IMainWindowProvi
{
Dispatcher.UIThread.Post(() => { _lifeTime.MainWindow?.Close(); });
}
public void Focused()
{
IsMainWindowFocused = true;
@ -201,7 +198,7 @@ public class RootViewModel : ActivatableViewModelBase, IScreen, IMainWindowProvi
/// <inheritdoc />
public event EventHandler? MainWindowClosed;
/// <inheritdoc />
public event EventHandler? MainWindowFocused;
@ -217,7 +214,7 @@ public class RootViewModel : ActivatableViewModelBase, IScreen, IMainWindowProvi
{
MainWindowClosed?.Invoke(this, EventArgs.Empty);
}
protected virtual void OnMainWindowFocused()
{
MainWindowFocused?.Invoke(this, EventArgs.Empty);

View File

@ -21,8 +21,8 @@ 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 ISurfaceVmFactory _surfaceVmFactory;
private readonly IWindowService _windowService;
private bool _colorDevices;
private bool _colorFirstLedOnly;
@ -72,27 +72,6 @@ public class SurfaceEditorViewModel : MainScreenViewModel
});
}
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;
@ -180,6 +159,27 @@ public class SurfaceEditorViewModel : MainScreenViewModel
surfaceDeviceViewModel.UpdateMouseDrag(mousePosition, round, ignoreOverlap);
}
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);
}
private async Task ExecuteAutoArrange()
{
bool confirmed = await _windowService.ShowConfirmContentDialog("Auto-arrange layout", "Are you sure you want to auto-arrange your layout? Your current settings will be overwritten.");

View File

@ -12,7 +12,6 @@ using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.NodeEditor;
using Artemis.UI.Shared.Services.NodeEditor.Commands;
using Artemis.UI.Shared.Services.ProfileEditor;
using Avalonia;
using Avalonia.Threading;
using DynamicData;
@ -25,8 +24,8 @@ public class NodeScriptWindowViewModel : DialogViewModelBase<bool>
{
private readonly INodeEditorService _nodeEditorService;
private readonly INodeService _nodeService;
private readonly ISettingsService _settingsService;
private readonly IProfileService _profileService;
private readonly ISettingsService _settingsService;
private readonly IWindowService _windowService;
public NodeScriptWindowViewModel(NodeScript nodeScript,
@ -66,10 +65,10 @@ public class NodeScriptWindowViewModel : DialogViewModelBase<bool>
DispatcherTimer updateTimer = new(TimeSpan.FromMilliseconds(25.0 / 1000), DispatcherPriority.Normal, Update);
// TODO: Remove in favor of saving each time a node editor command is executed
DispatcherTimer saveTimer = new(TimeSpan.FromMinutes(2), DispatcherPriority.Normal, Save);
updateTimer.Start();
saveTimer.Start();
Disposable.Create(() =>
{
updateTimer.Stop();