mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Merge branch 'development'
This commit is contained in:
commit
76994ce596
@ -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" />.
|
||||
|
||||
@ -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; }
|
||||
}
|
||||
@ -69,11 +69,16 @@ public abstract class BreakableModel : CorePropertyChanged, IBreakableModel
|
||||
/// <inheritdoc />
|
||||
public void ClearBrokenState(string state)
|
||||
{
|
||||
if (state == null) throw new ArgumentNullException(nameof(state));
|
||||
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;
|
||||
|
||||
if (BrokenState != state) return;
|
||||
BrokenState = null;
|
||||
BrokenStateException = null;
|
||||
OnBrokenStateChanged();
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -115,7 +115,7 @@ internal class NodeService : INodeService
|
||||
}
|
||||
}
|
||||
|
||||
node.Initialize(script);
|
||||
node.TryInitialize(script);
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
@ -233,7 +233,7 @@ internal class PluginManagementService : IPluginManagementService
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Warning(new ArtemisPluginException("Failed to load plugin", e), "Plugin exception");
|
||||
_logger.Warning(new ArtemisPluginException($"Failed to load plugin at {subDirectory}", e), "Plugin exception");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
20
src/Artemis.Core/Services/ProcessMonitor/ProcessComparer.cs
Normal file
20
src/Artemis.Core/Services/ProcessMonitor/ProcessComparer.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -62,9 +62,10 @@ internal class NodeTypeStore
|
||||
|
||||
public static Plugin? GetPlugin(INode node)
|
||||
{
|
||||
Type nodeType = node.GetType();
|
||||
lock (Registrations)
|
||||
{
|
||||
return Registrations.FirstOrDefault(r => r.Plugin.GetType().Assembly == node.GetType().Assembly)?.Plugin;
|
||||
return Registrations.FirstOrDefault(r => r.NodeData.Type == nodeType)?.Plugin;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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 Numeric(d);
|
||||
}
|
||||
|
||||
public static implicit operator Numeric(float f)
|
||||
{
|
||||
return new Numeric(f);
|
||||
}
|
||||
|
||||
public static implicit operator Numeric(int i)
|
||||
{
|
||||
return new Numeric(i);
|
||||
}
|
||||
|
||||
public static implicit operator Numeric(byte b)
|
||||
{
|
||||
return new Numeric(b);
|
||||
}
|
||||
|
||||
public static implicit operator long(Numeric p)
|
||||
{
|
||||
|
||||
@ -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
|
||||
|
||||
@ -8,7 +8,7 @@ namespace Artemis.Core;
|
||||
/// <summary>
|
||||
/// Represents a kind of node inside a <see cref="INodeScript" />
|
||||
/// </summary>
|
||||
public interface INode : INotifyPropertyChanged
|
||||
public interface INode : INotifyPropertyChanged, IBreakableModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the ID of the node.
|
||||
@ -59,7 +59,7 @@ public interface INode : INotifyPropertyChanged
|
||||
/// 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
|
||||
/// 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>
|
||||
@ -81,15 +81,15 @@ public interface INode : INotifyPropertyChanged
|
||||
event EventHandler<SingleValueEventArgs<IPinCollection>> PinCollectionRemoved;
|
||||
|
||||
/// <summary>
|
||||
/// Called when the node was loaded from storage or newly created
|
||||
/// Attempts to initialize the node.
|
||||
/// </summary>
|
||||
/// <param name="script">The script the node is contained in</param>
|
||||
void Initialize(INodeScript script);
|
||||
void TryInitialize(INodeScript script);
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the value of the output pins of this node
|
||||
/// Attempts to evaluate the value of the output pins of this node
|
||||
/// </summary>
|
||||
void Evaluate();
|
||||
void TryEvaluate();
|
||||
|
||||
/// <summary>
|
||||
/// Resets the node causing all pins to re-evaluate the next time <see cref="Evaluate" /> is called
|
||||
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -12,7 +12,7 @@ namespace Artemis.Core;
|
||||
/// <summary>
|
||||
/// Represents a kind of node inside a <see cref="NodeScript" />
|
||||
/// </summary>
|
||||
public abstract class Node : CorePropertyChanged, INode
|
||||
public abstract class Node : BreakableModel, INode
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public event EventHandler? Resetting;
|
||||
@ -95,6 +95,9 @@ public abstract class Node : CorePropertyChanged, INode
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyCollection<IPinCollection> PinCollections => new ReadOnlyCollection<IPinCollection>(_pinCollections);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string BrokenDisplayName => Name;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Construtors
|
||||
@ -335,12 +338,17 @@ public abstract class Node : CorePropertyChanged, INode
|
||||
return isRemoved;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Called when the node was loaded from storage or newly created
|
||||
/// </summary>
|
||||
/// <param name="script">The script the node is contained in</param>
|
||||
public virtual void Initialize(INodeScript script)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Evaluates the value of the output pins of this node
|
||||
/// </summary>
|
||||
public abstract void Evaluate();
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -354,6 +362,18 @@ public abstract class Node : CorePropertyChanged, INode
|
||||
Resetting?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void TryInitialize(INodeScript script)
|
||||
{
|
||||
TryOrBreak(() => Initialize(script), "Failed to initialize");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void TryEvaluate()
|
||||
{
|
||||
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>
|
||||
|
||||
@ -14,13 +14,19 @@ namespace Artemis.Core;
|
||||
/// </summary>
|
||||
public abstract class NodeScript : CorePropertyChanged, INodeScript
|
||||
{
|
||||
private void NodeTypeStoreOnNodeTypeChanged(object? sender, NodeTypeStoreEvent e)
|
||||
private void NodeTypeStoreOnNodeTypeAdded(object? sender, NodeTypeStoreEvent e)
|
||||
{
|
||||
// Only respond to node changes applicable to the current script
|
||||
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;
|
||||
|
||||
@ -79,8 +85,8 @@ public abstract class NodeScript : CorePropertyChanged, INodeScript
|
||||
Entity = new NodeScriptEntity();
|
||||
ExitNode = null!;
|
||||
|
||||
NodeTypeStore.NodeTypeAdded += NodeTypeStoreOnNodeTypeChanged;
|
||||
NodeTypeStore.NodeTypeRemoved += NodeTypeStoreOnNodeTypeChanged;
|
||||
NodeTypeStore.NodeTypeAdded += NodeTypeStoreOnNodeTypeAdded;
|
||||
NodeTypeStore.NodeTypeRemoved += NodeTypeStoreOnNodeTypeRemoved;
|
||||
}
|
||||
|
||||
internal NodeScript(string name, string description, NodeScriptEntity entity, object? context = null)
|
||||
@ -91,8 +97,8 @@ public abstract class NodeScript : CorePropertyChanged, INodeScript
|
||||
Context = context;
|
||||
ExitNode = null!;
|
||||
|
||||
NodeTypeStore.NodeTypeAdded += NodeTypeStoreOnNodeTypeChanged;
|
||||
NodeTypeStore.NodeTypeRemoved += NodeTypeStoreOnNodeTypeChanged;
|
||||
NodeTypeStore.NodeTypeAdded += NodeTypeStoreOnNodeTypeAdded;
|
||||
NodeTypeStore.NodeTypeRemoved += NodeTypeStoreOnNodeTypeRemoved;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -108,7 +114,7 @@ public abstract class NodeScript : CorePropertyChanged, INodeScript
|
||||
node.Reset();
|
||||
}
|
||||
|
||||
ExitNode.Evaluate();
|
||||
ExitNode.TryEvaluate();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -136,8 +142,8 @@ public abstract class NodeScript : CorePropertyChanged, INodeScript
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
NodeTypeStore.NodeTypeAdded -= NodeTypeStoreOnNodeTypeChanged;
|
||||
NodeTypeStore.NodeTypeRemoved -= NodeTypeStoreOnNodeTypeChanged;
|
||||
NodeTypeStore.NodeTypeAdded -= NodeTypeStoreOnNodeTypeAdded;
|
||||
NodeTypeStore.NodeTypeRemoved -= NodeTypeStoreOnNodeTypeRemoved;
|
||||
|
||||
lock (_nodes)
|
||||
{
|
||||
@ -346,7 +352,7 @@ public abstract class NodeScript : CorePropertyChanged, INodeScript
|
||||
sourcePinId++;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
foreach (IPin targetPin in sourcePin.ConnectedTo)
|
||||
{
|
||||
int targetPinCollectionId = -1;
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Artemis.Core;
|
||||
@ -43,7 +41,7 @@ public sealed class OutputPin<T> : Pin
|
||||
get
|
||||
{
|
||||
if (!IsEvaluated)
|
||||
Node?.Evaluate();
|
||||
Node?.TryEvaluate();
|
||||
|
||||
return _value;
|
||||
}
|
||||
@ -115,7 +113,7 @@ public sealed class OutputPin : Pin
|
||||
get
|
||||
{
|
||||
if (!IsEvaluated)
|
||||
Node?.Evaluate();
|
||||
Node?.TryEvaluate();
|
||||
|
||||
return _value;
|
||||
}
|
||||
|
||||
@ -143,30 +143,20 @@ public abstract class Pin : CorePropertyChanged, IPin
|
||||
/// <param name="currentType">The backing field of the current type of the pin.</param>
|
||||
protected void ChangeType(Type type, ref Type currentType)
|
||||
{
|
||||
// Enums are a special case that disconnect and, if still compatible, reconnect
|
||||
if (type.IsEnum && currentType.IsEnum)
|
||||
{
|
||||
List<IPin> connections = new(ConnectedTo);
|
||||
DisconnectAll();
|
||||
if (currentType == type)
|
||||
return;
|
||||
|
||||
// Change the type
|
||||
SetAndNotify(ref currentType, type, nameof(Type));
|
||||
IsNumeric = type == typeof(Numeric);
|
||||
bool changingEnums = type.IsEnum && currentType.IsEnum;
|
||||
|
||||
foreach (IPin pin in connections.Where(p => p.IsTypeCompatible(type)))
|
||||
ConnectTo(pin);
|
||||
}
|
||||
// Disconnect pins incompatible with the new type
|
||||
else
|
||||
{
|
||||
List<IPin> toDisconnect = ConnectedTo.Where(p => !p.IsTypeCompatible(type, false)).ToList();
|
||||
foreach (IPin pin in toDisconnect)
|
||||
DisconnectFrom(pin);
|
||||
List<IPin> connections = new(ConnectedTo);
|
||||
DisconnectAll();
|
||||
|
||||
// Change the type
|
||||
SetAndNotify(ref currentType, type, nameof(Type));
|
||||
IsNumeric = type == typeof(Numeric);
|
||||
}
|
||||
// Change the type
|
||||
SetAndNotify(ref currentType, type, nameof(Type));
|
||||
IsNumeric = type == typeof(Numeric);
|
||||
|
||||
foreach (IPin pin in connections.Where(p => p.IsTypeCompatible(type, changingEnums)))
|
||||
ConnectTo(pin);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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
|
||||
}
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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}");
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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")]
|
||||
|
||||
@ -44,7 +44,7 @@ public class MainWindow : ReactiveCoreWindow<RootViewModel>
|
||||
SetTitleBar(this.Get<Border>("DragHandle"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void OnActivated(object? sender, EventArgs e)
|
||||
{
|
||||
ViewModel?.Focused();
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -32,7 +32,7 @@ public class LayerHintsDialogViewModel : DialogViewModelBase<bool>
|
||||
.Subscribe(c => AdaptionHints.Add(CreateHintViewModel(c.EventArgs.AdaptionHint)))
|
||||
.DisposeWith(d);
|
||||
Observable.FromEventPattern<LayerAdapterHintEventArgs>(x => layer.Adapter.AdapterHintRemoved += x, x => layer.Adapter.AdapterHintRemoved -= x)
|
||||
.Subscribe(c => AdaptionHints.Remove(AdaptionHints.FirstOrDefault(h => h.AdaptionHint == c.EventArgs.AdaptionHint)!))
|
||||
.Subscribe(c => AdaptionHints.RemoveMany(AdaptionHints.Where(h => h.AdaptionHint == c.EventArgs.AdaptionHint)))
|
||||
.DisposeWith(d);
|
||||
|
||||
AdaptionHints.AddRange(Layer.Adapter.AdaptionHints.Select(CreateHintViewModel));
|
||||
|
||||
@ -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()
|
||||
{
|
||||
|
||||
@ -293,6 +293,7 @@ public abstract class TreeItemViewModel : ActivatableViewModelBase
|
||||
CanPaste = false;
|
||||
return;
|
||||
}
|
||||
|
||||
CanPaste = formats.Contains(ProfileElementExtensions.ClipboardDataFormat);
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -42,7 +42,7 @@ public class TimelinePropertyViewModel<T> : ActivatableViewModelBase, ITimelineP
|
||||
.Subscribe(e => _keyframes.Add((LayerPropertyKeyframe<T>) e.EventArgs.Keyframe))
|
||||
.DisposeWith(d);
|
||||
Observable.FromEventPattern<LayerPropertyKeyframeEventArgs>(x => LayerProperty.KeyframeRemoved += x, x => LayerProperty.KeyframeRemoved -= x)
|
||||
.Subscribe(e => _keyframes.Remove((LayerPropertyKeyframe<T>) e.EventArgs.Keyframe))
|
||||
.Subscribe(e => _keyframes.RemoveMany(_keyframes.Items.Where(k => k == e.EventArgs.Keyframe)))
|
||||
.DisposeWith(d);
|
||||
|
||||
_keyframes.Edit(k =>
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -64,7 +64,7 @@ public class SidebarCategoryViewModel : ActivatableViewModelBase
|
||||
.Subscribe(e => profileConfigurations.Add(e.EventArgs.ProfileConfiguration))
|
||||
.DisposeWith(d);
|
||||
Observable.FromEventPattern<ProfileConfigurationEventArgs>(x => profileCategory.ProfileConfigurationRemoved += x, x => profileCategory.ProfileConfigurationRemoved -= x)
|
||||
.Subscribe(e => profileConfigurations.Remove(e.EventArgs.ProfileConfiguration))
|
||||
.Subscribe(e => profileConfigurations.RemoveMany(profileConfigurations.Items.Where(c => c == e.EventArgs.ProfileConfiguration)))
|
||||
.DisposeWith(d);
|
||||
|
||||
profileEditorService.ProfileConfiguration.Subscribe(p => SelectedProfileConfiguration = ProfileConfigurations.FirstOrDefault(c => ReferenceEquals(c.ProfileConfiguration, p)))
|
||||
|
||||
@ -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.");
|
||||
|
||||
@ -50,10 +50,10 @@ public class NodeScriptViewModel : ActivatableViewModelBase
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
Observable.FromEventPattern<SingleValueEventArgs<INode>>(x => NodeScript.NodeAdded += x, x => NodeScript.NodeAdded -= x)
|
||||
.Subscribe(e => HandleNodeAdded(e.EventArgs))
|
||||
.Subscribe(e => _nodeViewModels.Add(_nodeVmFactory.NodeViewModel(this, e.EventArgs.Value)))
|
||||
.DisposeWith(d);
|
||||
Observable.FromEventPattern<SingleValueEventArgs<INode>>(x => NodeScript.NodeRemoved += x, x => NodeScript.NodeRemoved -= x)
|
||||
.Subscribe(e => HandleNodeRemoved(e.EventArgs))
|
||||
.Subscribe(e => _nodeViewModels.RemoveMany(_nodeViewModels.Items.Where(n => n.Node == e.EventArgs.Value)))
|
||||
.DisposeWith(d);
|
||||
});
|
||||
|
||||
@ -276,16 +276,4 @@ public class NodeScriptViewModel : ActivatableViewModelBase
|
||||
private void ExecutePasteSelected()
|
||||
{
|
||||
}
|
||||
|
||||
private void HandleNodeAdded(SingleValueEventArgs<INode> eventArgs)
|
||||
{
|
||||
_nodeViewModels.Add(_nodeVmFactory.NodeViewModel(this, eventArgs.Value));
|
||||
}
|
||||
|
||||
private void HandleNodeRemoved(SingleValueEventArgs<INode> eventArgs)
|
||||
{
|
||||
NodeViewModel? toRemove = NodeViewModels.FirstOrDefault(vm => ReferenceEquals(vm.Node, eventArgs.Value));
|
||||
if (toRemove != null)
|
||||
_nodeViewModels.Remove(toRemove);
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
|
||||
@ -37,35 +37,38 @@
|
||||
ClipToBounds="True"
|
||||
Background="{DynamicResource ContentDialogBackground}">
|
||||
<Border Background="{DynamicResource TaskDialogHeaderBackground}">
|
||||
<Grid Classes="node-header"
|
||||
VerticalAlignment="Top"
|
||||
ColumnDefinitions="*,Auto">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
Margin="10 0 0 0"
|
||||
Text="{CompiledBinding Node.Name}"
|
||||
ToolTip.Tip="{CompiledBinding Node.Description}">
|
||||
</TextBlock>
|
||||
<Button VerticalAlignment="Center"
|
||||
<Grid Classes="node-header" VerticalAlignment="Top" ColumnDefinitions="Auto,*,Auto">
|
||||
<Button Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{CompiledBinding Node.BrokenState, Converter={x:Static ObjectConverters.IsNotNull}}"
|
||||
Classes="icon-button icon-button-small"
|
||||
Grid.Column="1"
|
||||
Margin="5"
|
||||
Command="{CompiledBinding DeleteNode}">
|
||||
Foreground="White"
|
||||
Background="#E74C4C"
|
||||
BorderBrush="#E74C4C"
|
||||
Margin="5 0 0 0"
|
||||
ToolTip.Tip="{CompiledBinding Node.BrokenState}"
|
||||
Command="{CompiledBinding ShowBrokenState}">
|
||||
<avalonia:MaterialIcon Kind="AlertCircle"></avalonia:MaterialIcon>
|
||||
</Button>
|
||||
|
||||
<TextBlock Grid.Column="1" VerticalAlignment="Center" Margin="10 0 0 0" Text="{CompiledBinding Node.Name}" ToolTip.Tip="{CompiledBinding Node.Description}"/>
|
||||
|
||||
<Button Grid.Column="2" VerticalAlignment="Center" Classes="icon-button icon-button-small" Margin="5" Command="{CompiledBinding DeleteNode}">
|
||||
<avalonia:MaterialIcon Kind="Close"></avalonia:MaterialIcon>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Border>
|
||||
|
||||
|
||||
<Grid Grid.Row="1" ColumnDefinitions="Auto,*,Auto" Margin="4">
|
||||
<StackPanel Grid.Column="0" IsVisible="{CompiledBinding HasInputPins}">
|
||||
<ItemsControl Items="{CompiledBinding InputPinViewModels}" Margin="4 0" />
|
||||
<ItemsControl Items="{CompiledBinding InputPinCollectionViewModels}" />
|
||||
</StackPanel>
|
||||
|
||||
<ContentControl Grid.Column="1"
|
||||
<ContentControl Grid.Column="1"
|
||||
Name="CustomViewModelContainer"
|
||||
Content="{CompiledBinding CustomNodeViewModel}"
|
||||
Content="{CompiledBinding CustomNodeViewModel}"
|
||||
IsVisible="{CompiledBinding CustomNodeViewModel, Converter={x:Static ObjectConverters.IsNotNull}}" />
|
||||
|
||||
<StackPanel Grid.Column="2" IsVisible="{CompiledBinding HasOutputPins}">
|
||||
|
||||
@ -8,6 +8,7 @@ using Artemis.Core.Events;
|
||||
using Artemis.UI.Ninject.Factories;
|
||||
using Artemis.UI.Screens.VisualScripting.Pins;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Artemis.UI.Shared.Services.NodeEditor;
|
||||
using Artemis.UI.Shared.Services.NodeEditor.Commands;
|
||||
using Avalonia;
|
||||
@ -21,9 +22,9 @@ namespace Artemis.UI.Screens.VisualScripting;
|
||||
public class NodeViewModel : ActivatableViewModelBase
|
||||
{
|
||||
private readonly INodeEditorService _nodeEditorService;
|
||||
private readonly IWindowService _windowService;
|
||||
|
||||
private ICustomNodeViewModel? _customNodeViewModel;
|
||||
private ReactiveCommand<Unit, Unit>? _deleteNode;
|
||||
private double _dragOffsetX;
|
||||
private double _dragOffsetY;
|
||||
private ObservableAsPropertyHelper<bool>? _hasInputPins;
|
||||
@ -34,14 +35,13 @@ public class NodeViewModel : ActivatableViewModelBase
|
||||
private double _startX;
|
||||
private double _startY;
|
||||
|
||||
public NodeViewModel(NodeScriptViewModel nodeScriptViewModel, INode node, INodeVmFactory nodeVmFactory, INodeEditorService nodeEditorService)
|
||||
public NodeViewModel(NodeScriptViewModel nodeScriptViewModel, INode node, INodeVmFactory nodeVmFactory, INodeEditorService nodeEditorService, IWindowService windowService)
|
||||
{
|
||||
_nodeEditorService = nodeEditorService;
|
||||
_windowService = windowService;
|
||||
NodeScriptViewModel = nodeScriptViewModel;
|
||||
Node = node;
|
||||
|
||||
DeleteNode = ReactiveCommand.Create(ExecuteDeleteNode, this.WhenAnyValue(vm => vm.IsStaticNode).Select(v => !v));
|
||||
|
||||
SourceList<PinViewModel> nodePins = new();
|
||||
SourceList<PinCollectionViewModel> nodePinCollections = new();
|
||||
|
||||
@ -62,6 +62,9 @@ public class NodeViewModel : ActivatableViewModelBase
|
||||
|
||||
PinViewModels = pins;
|
||||
|
||||
DeleteNode = ReactiveCommand.Create(ExecuteDeleteNode, this.WhenAnyValue(vm => vm.IsStaticNode).Select(v => !v));
|
||||
ShowBrokenState = ReactiveCommand.Create(ExecuteShowBrokenState);
|
||||
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
_isStaticNode = Node.WhenAnyValue(n => n.IsDefaultNode, n => n.IsExitNode)
|
||||
@ -90,7 +93,7 @@ public class NodeViewModel : ActivatableViewModelBase
|
||||
})
|
||||
.DisposeWith(d);
|
||||
Observable.FromEventPattern<SingleValueEventArgs<IPin>>(x => Node.PinRemoved += x, x => Node.PinRemoved -= x)
|
||||
.Subscribe(p => nodePins!.Remove(nodePins.Items.FirstOrDefault(vm => vm.Pin == p.EventArgs.Value)))
|
||||
.Subscribe(p => nodePins.RemoveMany(nodePins.Items.Where(vm => vm.Pin == p.EventArgs.Value)))
|
||||
.DisposeWith(d);
|
||||
nodePins.Edit(l =>
|
||||
{
|
||||
@ -115,7 +118,7 @@ public class NodeViewModel : ActivatableViewModelBase
|
||||
})
|
||||
.DisposeWith(d);
|
||||
Observable.FromEventPattern<SingleValueEventArgs<IPinCollection>>(x => Node.PinCollectionRemoved += x, x => Node.PinCollectionRemoved -= x)
|
||||
.Subscribe(p => nodePinCollections!.Remove(nodePinCollections.Items.FirstOrDefault(vm => vm.PinCollection == p.EventArgs.Value)))
|
||||
.Subscribe(p => nodePinCollections.RemoveMany(nodePinCollections.Items.Where(vm => vm.PinCollection == p.EventArgs.Value)))
|
||||
.DisposeWith(d);
|
||||
nodePinCollections.Edit(l =>
|
||||
{
|
||||
@ -152,18 +155,15 @@ public class NodeViewModel : ActivatableViewModelBase
|
||||
set => RaiseAndSetIfChanged(ref _customNodeViewModel, value);
|
||||
}
|
||||
|
||||
public ReactiveCommand<Unit, Unit>? DeleteNode
|
||||
{
|
||||
get => _deleteNode;
|
||||
set => RaiseAndSetIfChanged(ref _deleteNode, value);
|
||||
}
|
||||
|
||||
public bool IsSelected
|
||||
{
|
||||
get => _isSelected;
|
||||
set => RaiseAndSetIfChanged(ref _isSelected, value);
|
||||
}
|
||||
|
||||
public ReactiveCommand<Unit, Unit> ShowBrokenState { get; }
|
||||
public ReactiveCommand<Unit, Unit> DeleteNode { get; }
|
||||
|
||||
public void StartDrag(Point mouseStartPosition)
|
||||
{
|
||||
if (!IsSelected)
|
||||
@ -195,4 +195,10 @@ public class NodeViewModel : ActivatableViewModelBase
|
||||
{
|
||||
_nodeEditorService.ExecuteCommand(NodeScriptViewModel.NodeScript, new DeleteNode(NodeScriptViewModel.NodeScript, Node));
|
||||
}
|
||||
|
||||
private void ExecuteShowBrokenState()
|
||||
{
|
||||
if (Node.BrokenState != null && Node.BrokenStateException != null)
|
||||
_windowService.ShowExceptionDialog(Node.BrokenState, Node.BrokenStateException);
|
||||
}
|
||||
}
|
||||
@ -30,7 +30,7 @@ public abstract class PinCollectionViewModel : ActivatableViewModelBase
|
||||
.Subscribe(e => PinViewModels.Add(CreatePinViewModel(e.EventArgs.Value)))
|
||||
.DisposeWith(d);
|
||||
Observable.FromEventPattern<SingleValueEventArgs<IPin>>(x => PinCollection.PinRemoved += x, x => PinCollection.PinRemoved -= x)
|
||||
.Subscribe(e => PinViewModels.RemoveMany(PinViewModels.Where(p => p.Pin == e.EventArgs.Value).ToList()))
|
||||
.Subscribe(e => PinViewModels.RemoveMany(PinViewModels.Where(p => p.Pin == e.EventArgs.Value)))
|
||||
.DisposeWith(d);
|
||||
});
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reactive;
|
||||
using System.Reactive.Linq;
|
||||
using Artemis.Core;
|
||||
@ -38,7 +39,7 @@ public abstract class PinViewModel : ActivatableViewModelBase
|
||||
.Subscribe(e => connectedPins.Add(e.EventArgs.Value))
|
||||
.DisposeWith(d);
|
||||
Observable.FromEventPattern<SingleValueEventArgs<IPin>>(x => Pin.PinDisconnected += x, x => Pin.PinDisconnected -= x)
|
||||
.Subscribe(e => connectedPins.Remove(e.EventArgs.Value))
|
||||
.Subscribe(e => connectedPins.RemoveMany(connectedPins.Items.Where(p => p == e.EventArgs.Value)))
|
||||
.DisposeWith(d);
|
||||
Pin.WhenAnyValue(p => p.Type).Subscribe(_ => UpdatePinColor()).DisposeWith(d);
|
||||
});
|
||||
|
||||
@ -8,7 +8,7 @@ public class StringRegexMatchNode : Node
|
||||
{
|
||||
private string? _lastPattern;
|
||||
private Regex? _regex;
|
||||
private bool _broken;
|
||||
private Exception? _exception;
|
||||
|
||||
public StringRegexMatchNode() : base("Regex Match", "Checks provided regex pattern matches the input.")
|
||||
{
|
||||
@ -25,20 +25,27 @@ public class StringRegexMatchNode : Node
|
||||
{
|
||||
if (Input.Value == null || Pattern.Value == null)
|
||||
return;
|
||||
if (_broken && _lastPattern == Pattern.Value)
|
||||
return;
|
||||
|
||||
// If the regex was invalid output false and rethrow the exception
|
||||
if (_lastPattern == Pattern.Value && _exception != null)
|
||||
{
|
||||
Result.Value = false;
|
||||
throw _exception;
|
||||
}
|
||||
|
||||
// If there is no regex yet or the regex changed, recompile
|
||||
if (_regex == null || _lastPattern != Pattern.Value)
|
||||
{
|
||||
try
|
||||
{
|
||||
_regex = new Regex(Pattern.Value, RegexOptions.Compiled);
|
||||
_broken = false;
|
||||
_exception = null;
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception e)
|
||||
{
|
||||
_broken = true;
|
||||
return;
|
||||
// If there is an exception, save it to keep rethrowing until the regex is fixed
|
||||
_exception = e;
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user