diff --git a/.gitignore b/.gitignore index 60b2ea0..4d858fe 100644 --- a/.gitignore +++ b/.gitignore @@ -397,4 +397,10 @@ FodyWeavers.xsd *.msp # JetBrains Rider -*.sln.iml \ No newline at end of file +*.sln.iml + +# config file created by Avalonia by error sometimes +config.json + + +!MacroPad.Core/BasePlugin/** \ No newline at end of file diff --git a/ConsoleApp1/Program.cs b/ConsoleApp1/Program.cs index 2078c7a..7041328 100644 --- a/ConsoleApp1/Program.cs +++ b/ConsoleApp1/Program.cs @@ -1,6 +1,7 @@  using MacroPad.Core; using MacroPad.Core.Device; +using MacroPad.Core.Plugin; using MacroPad.Shared.Plugin; using MacroPad.Shared.Plugin.Protocol; using System.Reflection; diff --git a/MacroPad.Core/BasePlugin/BasePluginInfos.cs b/MacroPad.Core/BasePlugin/BasePluginInfos.cs index 619de8b..4831447 100644 --- a/MacroPad.Core/BasePlugin/BasePluginInfos.cs +++ b/MacroPad.Core/BasePlugin/BasePluginInfos.cs @@ -5,21 +5,9 @@ namespace MacroPad.Core.BasePlugin { public class BasePluginInfos : IPluginInfos { - public string Name => "Base Plugin"; - - public string Description => "The usefulest plugin of MacroPad."; - - public string Version => "1.0.0"; - - public string Author => "Piripe"; - - public string? AuthorUrl => null; - - public string? SourceUrl => null; - public IProtocol[] Protocols => []; - public INodeCategory[] NodeCategories => [new BranchingCategory(), new ConditionsCategory() ,new ConstantsCategory(), new DebugCategory(), new TextCategory()]; + public INodeCategory[] NodeCategories => [new BranchingCategory(), new ButtonCategory(), new ConditionsCategory(), new ConstantsCategory(), new DebugCategory(), new MathCategory(), new ProfileCategory(), new TextCategory(), new VariableCategory()]; public NodeType[] NodeTypes => [.. DefaultTypes.types]; public ISettingsComponent[] Settings => []; diff --git a/MacroPad.Core/BasePlugin/Branching/GetBranch.cs b/MacroPad.Core/BasePlugin/Branching/GetBranch.cs index 80a8b89..60f6e52 100644 --- a/MacroPad.Core/BasePlugin/Branching/GetBranch.cs +++ b/MacroPad.Core/BasePlugin/Branching/GetBranch.cs @@ -18,9 +18,9 @@ public class GetBranch : INodeGetter public INodeComponent[] Components => []; public bool IsVisible(IDeviceLayoutButton button, IDeviceOutput output) => true; - public object[] GetOutputs(IResourceManager resource) + public object[] GetOutputs(INodeResourceManager resource) { - return new object[] { resource.GetValue((bool)resource.GetValue(0) ? 2 : 1) }; + return [ resource.GetValue((bool)resource.GetValue(0) ? 2 : 1) ]; } } } diff --git a/MacroPad.Core/BasePlugin/Branching/RunBranch.cs b/MacroPad.Core/BasePlugin/Branching/RunBranch.cs index 677159a..9c5883d 100644 --- a/MacroPad.Core/BasePlugin/Branching/RunBranch.cs +++ b/MacroPad.Core/BasePlugin/Branching/RunBranch.cs @@ -21,7 +21,7 @@ public class SetPaletteColor : INodeRunner public INodeComponent[] Components => []; public bool IsVisible(IDeviceLayoutButton button, IDeviceOutput output) => true; - public NodeRunnerResult Run(IResourceManager resource) + public NodeRunnerResult Run(INodeResourceManager resource) { return new NodeRunnerResult() { Results = [], RunnerOutputIndex = (bool)resource.GetValue(0) ? 1 : 0 }; } diff --git a/MacroPad.Core/BasePlugin/Button/GetCurrentValue.cs b/MacroPad.Core/BasePlugin/Button/GetCurrentValue.cs index 781a493..fcff2ad 100644 --- a/MacroPad.Core/BasePlugin/Button/GetCurrentValue.cs +++ b/MacroPad.Core/BasePlugin/Button/GetCurrentValue.cs @@ -18,7 +18,7 @@ public class GetCurrentValue : INodeGetter public INodeComponent[] Components => []; public bool IsVisible(IDeviceLayoutButton button, IDeviceOutput output) => button.Type == ButtonType.Slider; - public object[] GetOutputs(IResourceManager resource) + public object[] GetOutputs(INodeResourceManager resource) { return [(decimal)(NodeManager.CurrentDevice != null && NodeManager.CurrentButton != null ? NodeManager.CurrentDevice.ButtonsCurrentValue[NodeManager.CurrentButton.Id].Value : 0)]; } diff --git a/MacroPad.Core/BasePlugin/Button/SetCurrentPaletteColor.cs b/MacroPad.Core/BasePlugin/Button/SetCurrentPaletteColor.cs index 209dfa9..6f6e734 100644 --- a/MacroPad.Core/BasePlugin/Button/SetCurrentPaletteColor.cs +++ b/MacroPad.Core/BasePlugin/Button/SetCurrentPaletteColor.cs @@ -1,6 +1,7 @@ using MacroPad.Shared.Device; using MacroPad.Shared.Plugin.Nodes; using MacroPad.Shared.Plugin.Components; +using MacroPad.Shared.Plugin; namespace MacroPad.Core.BasePlugin.Button { @@ -22,9 +23,9 @@ public class SetCurrentPaletteColor : INodeRunner public INodeComponent[] Components => [ new ComboBox() { - GetItems = (IResourceManager resource, IDeviceLayoutButton button, IDeviceOutput output) => + GetItems = (IResourceManager resource) => { - return output.Palette.Select(x=>x.Name).ToArray(); + return resource.GetVirtual(VirtualDataKey.DeviceOutput)?.Palette.Select(x=>x.Name).ToArray() ?? []; }, GetSelection = (IResourceManager resource) => { @@ -38,7 +39,7 @@ public class SetCurrentPaletteColor : INodeRunner ]; public bool IsVisible(IDeviceLayoutButton button, IDeviceOutput output) => output.OutputType == OutputType.Palette; - public NodeRunnerResult Run(IResourceManager resource) + public NodeRunnerResult Run(INodeResourceManager resource) { if (NodeManager.CurrentButton != null) NodeManager.CurrentDevice?.SetButtonContent(NodeManager.CurrentButton, resource.GetData("color")); return new NodeRunnerResult() { Results = [], RunnerOutputIndex = 0 }; diff --git a/MacroPad.Core/BasePlugin/Conditions/Condition.cs b/MacroPad.Core/BasePlugin/Conditions/Condition.cs index 7381dc6..2cb564d 100644 --- a/MacroPad.Core/BasePlugin/Conditions/Condition.cs +++ b/MacroPad.Core/BasePlugin/Conditions/Condition.cs @@ -1,6 +1,7 @@ using MacroPad.Shared.Device; using MacroPad.Shared.Plugin.Nodes; using MacroPad.Shared.Plugin.Components; +using MacroPad.Shared.Plugin; namespace MacroPad.Core.BasePlugin.Conditions { @@ -41,7 +42,7 @@ public class Condition : INodeGetter private static decimal ObjectToDecimal(object x) => (decimal.TryParse(x.ToString(), out decimal val) ? val : 0m); public bool IsVisible(IDeviceLayoutButton button, IDeviceOutput output) => true; - public object[] GetOutputs(IResourceManager resource) + public object[] GetOutputs(INodeResourceManager resource) { return [ _operations[resource.GetData("o")](resource.GetValue(0), resource.GetValue(1)) ]; } diff --git a/MacroPad.Core/BasePlugin/Constants/Boolean.cs b/MacroPad.Core/BasePlugin/Constants/Boolean.cs index c32063c..2325e13 100644 --- a/MacroPad.Core/BasePlugin/Constants/Boolean.cs +++ b/MacroPad.Core/BasePlugin/Constants/Boolean.cs @@ -1,6 +1,7 @@ using MacroPad.Shared.Device; using MacroPad.Shared.Plugin.Nodes; using MacroPad.Shared.Plugin.Components; +using MacroPad.Shared.Plugin; namespace MacroPad.Core.BasePlugin.Constants { @@ -23,7 +24,7 @@ internal class Boolean : INodeGetter } ]; public bool IsVisible(IDeviceLayoutButton button, IDeviceOutput output) => true; - public object[] GetOutputs(IResourceManager resource) + public object[] GetOutputs(INodeResourceManager resource) { return [resource.GetData("v") ?? false]; } diff --git a/MacroPad.Core/BasePlugin/Constants/Number.cs b/MacroPad.Core/BasePlugin/Constants/Number.cs index 2a39afc..1aaf840 100644 --- a/MacroPad.Core/BasePlugin/Constants/Number.cs +++ b/MacroPad.Core/BasePlugin/Constants/Number.cs @@ -1,6 +1,7 @@ using MacroPad.Shared.Device; using MacroPad.Shared.Plugin.Nodes; using MacroPad.Shared.Plugin.Components; +using MacroPad.Shared.Plugin; namespace MacroPad.Core.BasePlugin.Constants { @@ -24,9 +25,9 @@ internal class Number : INodeGetter } ]; public bool IsVisible(IDeviceLayoutButton button, IDeviceOutput output) => true; - public object[] GetOutputs(IResourceManager resource) + public object[] GetOutputs(INodeResourceManager resource) { - return new object[] { resource.GetData("v") ?? 0m }; + return [ resource.GetData("v") ?? 0m ]; } } } diff --git a/MacroPad.Core/BasePlugin/Constants/Text.cs b/MacroPad.Core/BasePlugin/Constants/Text.cs index f7151cd..46f93ad 100644 --- a/MacroPad.Core/BasePlugin/Constants/Text.cs +++ b/MacroPad.Core/BasePlugin/Constants/Text.cs @@ -1,6 +1,7 @@ using MacroPad.Shared.Device; using MacroPad.Shared.Plugin.Nodes; using MacroPad.Shared.Plugin.Components; +using MacroPad.Shared.Plugin; namespace MacroPad.Core.BasePlugin.Constants { @@ -22,7 +23,7 @@ internal class Text : INodeGetter } ]; public bool IsVisible(IDeviceLayoutButton button, IDeviceOutput output) => true; - public object[] GetOutputs(IResourceManager resource) + public object[] GetOutputs(INodeResourceManager resource) { return [resource.GetData("v") ?? ""]; } diff --git a/MacroPad.Core/BasePlugin/Debug/WriteLine.cs b/MacroPad.Core/BasePlugin/Debug/WriteLine.cs new file mode 100644 index 0000000..9574e41 --- /dev/null +++ b/MacroPad.Core/BasePlugin/Debug/WriteLine.cs @@ -0,0 +1,30 @@ +using MacroPad.Shared.Device; +using MacroPad.Shared.Plugin.Nodes; + +namespace MacroPad.Core.BasePlugin.Debug +{ + public class WriteLine : INodeRunner + { + public string Name => "Write Line"; + + public string Description => "Write a line in the console."; + + public string Id => "WriteLine"; + + public TypeNamePair[] Inputs => [new(typeof(string), "Text")]; + + public TypeNamePair[] Outputs => []; + + public int RunnerOutputCount => 1; + public string[] RunnerOutputsName => []; + + public INodeComponent[] Components => []; + + public bool IsVisible(IDeviceLayoutButton button, IDeviceOutput output) => true; + public NodeRunnerResult Run(INodeResourceManager resource) + { + System.Diagnostics.Debug.WriteLine(resource.GetValue(0)); + return new NodeRunnerResult() { Results = [], RunnerOutputIndex = 0 }; + } + } +} diff --git a/MacroPad.Core/BasePlugin/DefaultTypes.cs b/MacroPad.Core/BasePlugin/DefaultTypes.cs index e298cb1..b434dd1 100644 --- a/MacroPad.Core/BasePlugin/DefaultTypes.cs +++ b/MacroPad.Core/BasePlugin/DefaultTypes.cs @@ -1,5 +1,4 @@ using MacroPad.Shared.Plugin; -using MacroPad.Shared.Plugin.Nodes; using MacroPad.Shared.Plugin.Components; namespace MacroPad.Core.BasePlugin diff --git a/MacroPad.Core/BasePlugin/Math/Absolute.cs b/MacroPad.Core/BasePlugin/Math/Absolute.cs index 1556304..1fd9430 100644 --- a/MacroPad.Core/BasePlugin/Math/Absolute.cs +++ b/MacroPad.Core/BasePlugin/Math/Absolute.cs @@ -18,7 +18,7 @@ public class Absolute : INodeGetter public INodeComponent[] Components => []; public bool IsVisible(IDeviceLayoutButton button, IDeviceOutput output) => true; - public object[] GetOutputs(IResourceManager resource) + public object[] GetOutputs(INodeResourceManager resource) { return [System.Math.Abs((decimal)resource.GetValue(0))]; } diff --git a/MacroPad.Core/BasePlugin/Math/Floor.cs b/MacroPad.Core/BasePlugin/Math/Floor.cs index 4714e94..2454255 100644 --- a/MacroPad.Core/BasePlugin/Math/Floor.cs +++ b/MacroPad.Core/BasePlugin/Math/Floor.cs @@ -18,7 +18,7 @@ public class Floor : INodeGetter public INodeComponent[] Components => []; public bool IsVisible(IDeviceLayoutButton button, IDeviceOutput output) => true; - public object[] GetOutputs(IResourceManager resource) + public object[] GetOutputs(INodeResourceManager resource) { return [System.Math.Floor((decimal)resource.GetValue(0))]; } diff --git a/MacroPad.Core/BasePlugin/Math/Map.cs b/MacroPad.Core/BasePlugin/Math/Map.cs index d514792..a01051c 100644 --- a/MacroPad.Core/BasePlugin/Math/Map.cs +++ b/MacroPad.Core/BasePlugin/Math/Map.cs @@ -18,7 +18,7 @@ public class Map : INodeGetter public INodeComponent[] Components => []; public bool IsVisible(IDeviceLayoutButton button, IDeviceOutput output) => true; - public object[] GetOutputs(IResourceManager resource) + public object[] GetOutputs(INodeResourceManager resource) { decimal value = (decimal)resource.GetValue(0); decimal minIn = (decimal)resource.GetValue(1); diff --git a/MacroPad.Core/BasePlugin/Math/Max.cs b/MacroPad.Core/BasePlugin/Math/Max.cs index 98f45ff..e81cdec 100644 --- a/MacroPad.Core/BasePlugin/Math/Max.cs +++ b/MacroPad.Core/BasePlugin/Math/Max.cs @@ -18,7 +18,7 @@ public class Max : INodeGetter public INodeComponent[] Components => []; public bool IsVisible(IDeviceLayoutButton button, IDeviceOutput output) => true; - public object[] GetOutputs(IResourceManager resource) + public object[] GetOutputs(INodeResourceManager resource) { return [System.Math.Max((decimal)resource.GetValue(0), (decimal)resource.GetValue(1))]; } diff --git a/MacroPad.Core/BasePlugin/Math/Min.cs b/MacroPad.Core/BasePlugin/Math/Min.cs index 1ecb07f..19cda93 100644 --- a/MacroPad.Core/BasePlugin/Math/Min.cs +++ b/MacroPad.Core/BasePlugin/Math/Min.cs @@ -18,7 +18,7 @@ public class Min : INodeGetter public INodeComponent[] Components => []; public bool IsVisible(IDeviceLayoutButton button, IDeviceOutput output) => true; - public object[] GetOutputs(IResourceManager resource) + public object[] GetOutputs(INodeResourceManager resource) { return [System.Math.Min((decimal)resource.GetValue(0), (decimal)resource.GetValue(1))]; } diff --git a/MacroPad.Core/BasePlugin/Math/MinMax.cs b/MacroPad.Core/BasePlugin/Math/MinMax.cs index 1294267..ced0c69 100644 --- a/MacroPad.Core/BasePlugin/Math/MinMax.cs +++ b/MacroPad.Core/BasePlugin/Math/MinMax.cs @@ -18,7 +18,7 @@ public class MinMax : INodeGetter public INodeComponent[] Components => []; public bool IsVisible(IDeviceLayoutButton button, IDeviceOutput output) => true; - public object[] GetOutputs(IResourceManager resource) + public object[] GetOutputs(INodeResourceManager resource) { return [System.Math.Min(System.Math.Max((decimal)resource.GetValue(0), (decimal)resource.GetValue(1)), (decimal)resource.GetValue(2))]; } diff --git a/MacroPad.Core/BasePlugin/Math/Operation.cs b/MacroPad.Core/BasePlugin/Math/Operation.cs index 522833d..3938618 100644 --- a/MacroPad.Core/BasePlugin/Math/Operation.cs +++ b/MacroPad.Core/BasePlugin/Math/Operation.cs @@ -1,6 +1,7 @@ using MacroPad.Shared.Device; using MacroPad.Shared.Plugin.Nodes; using MacroPad.Shared.Plugin.Components; +using MacroPad.Shared.Plugin; namespace MacroPad.Core.BasePlugin.Math { @@ -41,7 +42,7 @@ public class Operation : INodeGetter ]; public bool IsVisible(IDeviceLayoutButton button, IDeviceOutput output) => true; - public object[] GetOutputs(IResourceManager resource) + public object[] GetOutputs(INodeResourceManager resource) { return [_operations[resource.GetData("o")]((decimal)resource.GetValue(0), (decimal)resource.GetValue(1))]; } diff --git a/MacroPad.Core/BasePlugin/Math/Round.cs b/MacroPad.Core/BasePlugin/Math/Round.cs index f0df097..6722493 100644 --- a/MacroPad.Core/BasePlugin/Math/Round.cs +++ b/MacroPad.Core/BasePlugin/Math/Round.cs @@ -18,7 +18,7 @@ public class Round : INodeGetter public INodeComponent[] Components => []; public bool IsVisible(IDeviceLayoutButton button, IDeviceOutput output) => true; - public object[] GetOutputs(IResourceManager resource) + public object[] GetOutputs(INodeResourceManager resource) { return [System.Math.Round((decimal)resource.GetValue(0))]; } diff --git a/MacroPad.Core/BasePlugin/Profile/SetProfile.cs b/MacroPad.Core/BasePlugin/Profile/SetProfile.cs index 12fa23b..2b4a75d 100644 --- a/MacroPad.Core/BasePlugin/Profile/SetProfile.cs +++ b/MacroPad.Core/BasePlugin/Profile/SetProfile.cs @@ -2,6 +2,7 @@ using MacroPad.Shared.Device; using MacroPad.Shared.Plugin.Nodes; using MacroPad.Shared.Plugin.Components; +using MacroPad.Shared.Plugin; namespace MacroPad.Core.BasePlugin.Profile { @@ -33,7 +34,7 @@ private int GetProfileIndex(DeviceCore? device, IResourceManager resource) public INodeComponent[] Components => [ new ComboBox() { - GetItems = (IResourceManager resource, IDeviceLayoutButton button, IDeviceOutput output) => + GetItems = (IResourceManager resource) => { return DeviceManager.SelectedDevice?.DeviceProfiles.Select(x=>x.Name).ToArray() ?? []; }, @@ -47,7 +48,7 @@ private int GetProfileIndex(DeviceCore? device, IResourceManager resource) ]; public bool IsVisible(IDeviceLayoutButton button, IDeviceOutput output) => true; - public NodeRunnerResult Run(IResourceManager resource) + public NodeRunnerResult Run(INodeResourceManager resource) { if (NodeManager.CurrentDevice != null) NodeManager.CurrentDevice.SelectProfile(GetProfileIndex(NodeManager.CurrentDevice, resource)); return new NodeRunnerResult() { Results = [], RunnerOutputIndex = 0 }; diff --git a/MacroPad.Core/BasePlugin/Text/Join.cs b/MacroPad.Core/BasePlugin/Text/Join.cs index 999d45e..26d503e 100644 --- a/MacroPad.Core/BasePlugin/Text/Join.cs +++ b/MacroPad.Core/BasePlugin/Text/Join.cs @@ -1,10 +1,5 @@ using MacroPad.Shared.Device; using MacroPad.Shared.Plugin.Nodes; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace MacroPad.Core.BasePlugin.Text { @@ -23,7 +18,7 @@ public class Join : INodeGetter public INodeComponent[] Components => []; public bool IsVisible(IDeviceLayoutButton button, IDeviceOutput output) => true; - public object[] GetOutputs(IResourceManager resource) + public object[] GetOutputs(INodeResourceManager resource) { return [string.Concat(resource.GetValue(0), resource.GetValue(1))]; } diff --git a/MacroPad.Core/BasePlugin/Text/Replace.cs b/MacroPad.Core/BasePlugin/Text/Replace.cs index a6fddc9..fe24d3b 100644 --- a/MacroPad.Core/BasePlugin/Text/Replace.cs +++ b/MacroPad.Core/BasePlugin/Text/Replace.cs @@ -1,10 +1,5 @@ using MacroPad.Shared.Device; using MacroPad.Shared.Plugin.Nodes; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace MacroPad.Core.BasePlugin.Text { @@ -23,7 +18,7 @@ public class Replace : INodeGetter public INodeComponent[] Components => []; public bool IsVisible(IDeviceLayoutButton button, IDeviceOutput output) => true; - public object[] GetOutputs(IResourceManager resource) + public object[] GetOutputs(INodeResourceManager resource) { return [((string)resource.GetValue(0)).Replace((string)resource.GetValue(1), (string)resource.GetValue(2))]; } diff --git a/MacroPad.Core/BasePlugin/Variables/Get.cs b/MacroPad.Core/BasePlugin/Variables/Get.cs index 9b62905..392dac9 100644 --- a/MacroPad.Core/BasePlugin/Variables/Get.cs +++ b/MacroPad.Core/BasePlugin/Variables/Get.cs @@ -1,7 +1,8 @@ using MacroPad.Shared.Device; using MacroPad.Shared.Plugin.Nodes; using MacroPad.Shared.Plugin.Components; -using Newtonsoft.Json.Linq; +using MacroPad.Shared.Plugin; +using System.Text.Json.Nodes; namespace MacroPad.Core.BasePlugin.Variables { @@ -23,10 +24,10 @@ public class Get : INodeGetter } ]; public bool IsVisible(IDeviceLayoutButton button, IDeviceOutput output) => true; - public object[] GetOutputs(IResourceManager resource) + public object[] GetOutputs(INodeResourceManager resource) { string var = resource.GetData("v") ?? ""; - return [(DeviceManager.Config.Variables.TryGetValue(var, out JToken? value) ? value.Value() : null) ?? ""]; + return [(DeviceManager.Config.Variables.TryGetValue(var, out JsonValue? value) ? value.TryGetValue(out object? value2) ? value2 : null : null) ?? ""]; } } } diff --git a/MacroPad.Core/BasePlugin/Variables/Set.cs b/MacroPad.Core/BasePlugin/Variables/Set.cs index 4394d4d..af890a5 100644 --- a/MacroPad.Core/BasePlugin/Variables/Set.cs +++ b/MacroPad.Core/BasePlugin/Variables/Set.cs @@ -1,7 +1,8 @@ using MacroPad.Shared.Device; using MacroPad.Shared.Plugin.Nodes; using MacroPad.Shared.Plugin.Components; -using Newtonsoft.Json.Linq; +using MacroPad.Shared.Plugin; +using System.Text.Json.Nodes; namespace MacroPad.Core.BasePlugin.Variables { @@ -28,11 +29,11 @@ public class Set : INodeRunner public bool IsVisible(IDeviceLayoutButton button, IDeviceOutput output) => true; - public NodeRunnerResult Run(IResourceManager resource) + public NodeRunnerResult Run(INodeResourceManager resource) { string var = resource.GetData("v") ?? ""; - JToken value = JToken.FromObject(resource.GetValue(0)); - if (!DeviceManager.Config.Variables.TryAdd(var, value)) DeviceManager.Config.Variables[var] = value; + JsonValue? value = JsonValue.Create(resource.GetValue(0)); + if (value != null && !DeviceManager.Config.Variables.TryAdd(var, value)) DeviceManager.Config.Variables[var] = value; return new NodeRunnerResult() { Results = [], RunnerOutputIndex = 0 }; } } diff --git a/MacroPad.Core/Config/ButtonStatus.cs b/MacroPad.Core/Config/ButtonStatus.cs deleted file mode 100644 index f2e9dd0..0000000 --- a/MacroPad.Core/Config/ButtonStatus.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -namespace MacroPad.Core.Config -{ - public class ButtonStatus - { - [JsonProperty("value")] - public JToken? Value { get; set; } - } -} diff --git a/MacroPad.Core/Config/DeviceProfile.cs b/MacroPad.Core/Config/DeviceProfile.cs deleted file mode 100644 index 5994e14..0000000 --- a/MacroPad.Core/Config/DeviceProfile.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Newtonsoft.Json; - -namespace MacroPad.Core.Config -{ - public class DeviceProfile { - [JsonProperty("name")] - public string Name { get; set; } = ""; - - [JsonProperty("buttons")] - public Dictionary> ButtonsConfig { get; set; } = []; - } -} diff --git a/MacroPad.Core/Config/MacroPadConfig.cs b/MacroPad.Core/Config/MacroPadConfig.cs deleted file mode 100644 index 126ae6b..0000000 --- a/MacroPad.Core/Config/MacroPadConfig.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -namespace MacroPad.Core.Config -{ - public class MacroPadConfig - { - [JsonProperty("plugins")] - public Dictionary PluginsConfig { get; set; } = []; - [JsonProperty("enabledDevices")] - public Dictionary EnabledDevices { get; set; } = []; - [JsonProperty("devicesProfile")] - public Dictionary DefaultProfile { get; set; } = []; - [JsonProperty("devices")] - public Dictionary> DevicesProfiles { get; set; } = []; - [JsonProperty("variables")] - public Dictionary Variables { get; set; } = []; - - - - public static MacroPadConfig LoadConfig() - { - if (File.Exists("config.json")) - { - MacroPadConfig? config = JsonConvert.DeserializeObject(File.ReadAllText("config.json")); - if (config != null ) return config; - } - return new MacroPadConfig(); - } - public void SaveConfig() - { - File.WriteAllText("config.json", JsonConvert.SerializeObject(this,Formatting.None)); - } - } -} diff --git a/MacroPad.Core/Config/NodeLine.cs b/MacroPad.Core/Config/NodeLine.cs deleted file mode 100644 index d50d8c9..0000000 --- a/MacroPad.Core/Config/NodeLine.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Newtonsoft.Json; - -namespace MacroPad.Core.Config -{ - public class NodeLine - { - [JsonProperty("n")] - public int Node; - [JsonProperty("i")] - public int PointIndex; - } -} diff --git a/MacroPad.Core/Config/NodeLinks.cs b/MacroPad.Core/Config/NodeLinks.cs deleted file mode 100644 index b2e73a0..0000000 --- a/MacroPad.Core/Config/NodeLinks.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -namespace MacroPad.Core.Config -{ - public class NodeLinks - { - [JsonProperty("id")] - public string Id { get; set; } = ""; - [JsonProperty("g")] - public Dictionary Getters { get; set; } = []; - [JsonProperty("r")] - public Dictionary Runners { get; set; } = []; - [JsonProperty("x")] - public int X { get; set; } - [JsonProperty("y")] - public int Y { get; set; } - [JsonProperty("d")] - public Dictionary Data { get; set; } = []; - [JsonProperty("c")] - public Dictionary> Consts { get; set; } = []; - } -} diff --git a/MacroPad.Core/Config/NodeScript.cs b/MacroPad.Core/Config/NodeScript.cs deleted file mode 100644 index 57de23b..0000000 --- a/MacroPad.Core/Config/NodeScript.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Newtonsoft.Json; - -namespace MacroPad.Core.Config -{ - public class NodeScript - { - [JsonProperty("x")] - public int StartX { get; set; } - [JsonProperty("y")] - public int StartY { get; set; } - [JsonProperty("links")] - public Dictionary NodesLinks { get; set; } = []; - [JsonProperty("lines")] - public Dictionary NodeLines { get; set; } = []; - } -} diff --git a/MacroPad.Core/Device/DeviceCore.cs b/MacroPad.Core/Device/DeviceCore.cs index 65ba433..2868d66 100644 --- a/MacroPad.Core/Device/DeviceCore.cs +++ b/MacroPad.Core/Device/DeviceCore.cs @@ -1,7 +1,6 @@ -using MacroPad.Core.Config; -using MacroPad.Shared.Plugin.Protocol; -using Newtonsoft.Json.Linq; +using MacroPad.Shared.Plugin.Protocol; using MacroPad.Shared.Device; +using MacroPad.Core.Models.Config; namespace MacroPad.Core.Device { @@ -21,7 +20,7 @@ public class DeviceCore public Dictionary ButtonsCurrentValue => _lastValue; - public string Name => Layout==null?ProtocolDevice.Name:Layout.Name; + public string Name => Layout?.Name ?? ProtocolDevice.Name; public DeviceCore(IProtocolDevice protocolDevice) { @@ -68,7 +67,7 @@ public void SelectProfile(int profile) case OutputType.Palette: if (buttonConfig.Status.Value != null) { - int value = buttonConfig.Status.Value.Value() ?? 0; + int value = buttonConfig.Status.Value.TryGetValue(out int? value2) ? value2??0 : 0; SetButtonContent(button, value); } else SetButtonContent(button, 0); @@ -76,7 +75,7 @@ public void SelectProfile(int profile) case OutputType.Number: if (buttonConfig.Status.Value != null) { - decimal value = buttonConfig.Status.Value.Value() ?? 0; + decimal value = buttonConfig.Status.Value.TryGetValue(out decimal? value2) ? value2 ?? 0 : 0; SetButtonContent(button, value); } else SetButtonContent(button, 0); diff --git a/MacroPad.Core/Device/DeviceLayout.cs b/MacroPad.Core/Device/DeviceLayout.cs index 25128e7..47ea2e7 100644 --- a/MacroPad.Core/Device/DeviceLayout.cs +++ b/MacroPad.Core/Device/DeviceLayout.cs @@ -1,30 +1,31 @@ using MacroPad.Shared.Plugin.Protocol; -using Newtonsoft.Json; +using System.Text.Json; +using System.Text.Json.Serialization; using System.Text.RegularExpressions; namespace MacroPad.Core.Device { public class DeviceLayout { - [JsonProperty("detectionMode")] + [JsonPropertyName("detectionMode")] public DeviceDetectionMode DetectionMode { get; set; } = DeviceDetectionMode.Name | DeviceDetectionMode.Equal; - [JsonProperty("detectionValue")] + [JsonPropertyName("detectionValue")] public string DetectionValue { get; set; } = ""; - [JsonProperty("protocol")] + [JsonPropertyName("protocol")] public string Protocol { get; set; } = ""; - [JsonProperty("name")] + [JsonPropertyName("name")] public string Name { get; set; } = ""; - [JsonProperty("outputs")] + [JsonPropertyName("outputs")] public Dictionary OutputTypes { get; set; } = []; - [JsonProperty("buttons")] + [JsonPropertyName("buttons")] public DeviceLayoutButton[] Buttons { get; set; } = []; - [JsonProperty("assets")] + [JsonPropertyName("assets")] public string AssetsFolder { get; set; } = ""; - [JsonProperty("width")] + [JsonPropertyName("width")] public int DWidth { get; set; } - [JsonProperty("height")] + [JsonPropertyName("height")] public int DHeight { get; set; } - [JsonProperty("image")] + [JsonPropertyName("image")] public string? DImage { get; set; } private string _layoutPath = ""; @@ -35,15 +36,15 @@ public string GetAssetPath(string path) } - public static List LoadLayouts() { - var layouts = new List(); + public static HashSet LoadLayouts() { + var layouts = new HashSet(); if (Directory.Exists("layouts")) { IEnumerable layoutFiles = Directory.EnumerateFiles("layouts", "*.layout.json", SearchOption.AllDirectories); foreach (string layoutFile in layoutFiles) { - var layout = JsonConvert.DeserializeObject(File.ReadAllText(layoutFile)); + var layout = JsonSerializer.Deserialize(File.ReadAllText(layoutFile)); if (layout != null) { layout.SetLayoutPath(Path.GetDirectoryName(layoutFile)??""); @@ -57,10 +58,10 @@ public static List LoadLayouts() { public static DeviceLayout? SearchLayout(IProtocolDevice device) { - return DeviceManager.Layouts.Find((layout) => + return DeviceManager.Layouts.FirstOrDefault((layout) => { if (layout.Protocol != device.Protocol) return false; - string input = layout.DetectionMode.HasFlag(DeviceDetectionMode.Name) ? device.Name : device.Id; + string input = layout.DetectionMode.HasFlag(DeviceDetectionMode.ID) ? device.Id : device.Name; if (layout.DetectionMode.HasFlag(DeviceDetectionMode.Equal)) return input == layout.DetectionValue; if (layout.DetectionMode.HasFlag(DeviceDetectionMode.Contains)) return input.Contains(layout.DetectionValue); diff --git a/MacroPad.Core/Device/DeviceLayoutButton.cs b/MacroPad.Core/Device/DeviceLayoutButton.cs index f7af4ba..a4ef17a 100644 --- a/MacroPad.Core/Device/DeviceLayoutButton.cs +++ b/MacroPad.Core/Device/DeviceLayoutButton.cs @@ -1,29 +1,29 @@ using MacroPad.Shared.Device; -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace MacroPad.Core.Device { public class DeviceLayoutButton : IDeviceLayoutButton { - [JsonProperty("id")] + [JsonPropertyName("id")] public int Id { get; set; } - [JsonProperty("type")] + [JsonPropertyName("type")] public ButtonType Type { get; set; } - [JsonProperty("output")] + [JsonPropertyName("output")] public string Output { get; set; } = ""; - [JsonProperty("x")] + [JsonPropertyName("x")] public int X { get; set; } - [JsonProperty("y")] + [JsonPropertyName("y")] public int Y { get; set; } - [JsonProperty("dx")] + [JsonPropertyName("dx")] public int DX { get; set; } - [JsonProperty("dy")] + [JsonPropertyName("dy")] public int DY { get; set; } - [JsonProperty("dw")] + [JsonPropertyName("dw")] public int DWidth { get; set; } - [JsonProperty("dh")] + [JsonPropertyName("dh")] public int DHeight { get; set; } - [JsonProperty("rotation")] + [JsonPropertyName("rotation")] public int Rotation { get; set; } } } diff --git a/MacroPad.Core/Device/DeviceOutput.cs b/MacroPad.Core/Device/DeviceOutput.cs index eeda063..d2f4192 100644 --- a/MacroPad.Core/Device/DeviceOutput.cs +++ b/MacroPad.Core/Device/DeviceOutput.cs @@ -1,19 +1,36 @@ using MacroPad.Shared.Device; -using Newtonsoft.Json; +using System.Text.Json; +using System.Text.Json.Serialization; namespace MacroPad.Core.Device { - public class DeviceOutput(OutputType type, PaletteValue[] palette, string? cornerRadius, string? image, uint color) : IDeviceOutput + public class DeviceOutput : IDeviceOutput { - [JsonProperty("type")] - public OutputType OutputType { get; set; } = type; - [JsonProperty("palette")] - public IPaletteValue[] Palette { get; set; } = palette; - [JsonProperty("cornerRadius")] - public string? CornerRadius { get; set; } = cornerRadius; - [JsonProperty("image")] - public string? Image { get; set; } = image; - [JsonProperty("color")] - public uint Color { get; set; } = color; + [JsonPropertyName("type")] + public OutputType OutputType { get; set; } = OutputType.Palette; + [JsonPropertyName("palette")] + [JsonConverter(typeof(PaletteArrayConverter))] + public IPaletteValue[] Palette { get; set; } = []; + [JsonPropertyName("cornerRadius")] + public string? CornerRadius { get; set; } + [JsonPropertyName("image")] + public string? Image { get; set; } + [JsonPropertyName("color")] + public uint Color { get; set; } } + + file class PaletteArrayConverter : JsonConverter + { + public override IPaletteValue[]? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var list = JsonSerializer.Deserialize(ref reader, options); + return list ?? Array.Empty(); + } + + public override void Write(Utf8JsonWriter writer, IPaletteValue[] value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value, options); + } + } + } diff --git a/MacroPad.Core/Device/PaletteValue.cs b/MacroPad.Core/Device/PaletteValue.cs index cef68e6..3c28a23 100644 --- a/MacroPad.Core/Device/PaletteValue.cs +++ b/MacroPad.Core/Device/PaletteValue.cs @@ -1,17 +1,17 @@ using MacroPad.Shared.Device; -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace MacroPad.Core.Device { public class PaletteValue : IPaletteValue { - [JsonProperty("value")] + [JsonPropertyName("value")] public int Value { get; set; } - [JsonProperty("name")] + [JsonPropertyName("name")] public string Name { get; set; } = ""; - [JsonProperty("color")] + [JsonPropertyName("color")] public uint Color { get; set; } - [JsonProperty("image")] + [JsonPropertyName("image")] public string? Image { get; set; } } } diff --git a/MacroPad.Core/DeviceManager.cs b/MacroPad.Core/DeviceManager.cs index 00dcb12..f760c28 100644 --- a/MacroPad.Core/DeviceManager.cs +++ b/MacroPad.Core/DeviceManager.cs @@ -1,15 +1,19 @@ -using MacroPad.Core.Config; -using MacroPad.Core.Device; +using MacroPad.Core.Device; +using MacroPad.Core.Models; +using MacroPad.Core.Models.Config; +using MacroPad.Core.Models.Plugin; using MacroPad.Shared.Plugin; using MacroPad.Shared.Plugin.Protocol; +using System.Diagnostics; +using System.Reflection; namespace MacroPad.Core { public class DeviceManager { public static MacroPadConfig Config { get; } = MacroPadConfig.LoadConfig(); - public static List Layouts { get; } = DeviceLayout.LoadLayouts(); - public static List ConnectedDevices { get; } = []; + public static HashSet Layouts { get; } = DeviceLayout.LoadLayouts(); + public static HashSet ConnectedDevices { get; } = []; public static DeviceCore? SelectedDevice { get; set; } public static event EventHandler? DeviceDetected; @@ -17,98 +21,93 @@ public class DeviceManager - public static void Init() { - PluginLoader.LoadPlugins(); - + public static void Init() + { NodeManager.Init(); - foreach (IProtocol protocol in PluginLoader.protocols) + PluginManager.PluginEnabled += PluginManager_PluginEnabled; + PluginManager.PluginDisabled += PluginManager_PluginDisabled; + + PluginManager.ScanPlugins(); + } + + private static void PluginManager_PluginDisabled(object? sender, IPluginInfos e) + { + foreach (IProtocol protocol in e.Protocols) + { + protocol.DeviceDetected -= Protocol_DeviceDetected; + protocol.DeviceDisconnected -= Protocol_DeviceDisconnected; + } + } + + private static void PluginManager_PluginEnabled(object? sender, IPluginInfos e) + { + foreach (IProtocol protocol in e.Protocols) { protocol.DeviceDetected += Protocol_DeviceDetected; protocol.DeviceDisconnected += Protocol_DeviceDisconnected; - string? protocolName = protocol.Id; - if (protocolName == null) return; - Config.PluginsConfig.TryAdd(protocolName, false); - if (Config.PluginsConfig[protocolName]) - { - protocol.Enable(); - } + protocol.Enable(); } } private static void Protocol_DeviceDetected(object? sender, DeviceDetectedEventArgs e) { - Console.WriteLine($"Device detected: {e.Device.Name} ({e.Device.Id})"); - - Config.EnabledDevices.TryAdd(e.Device.Id, false); if (!Config.DevicesProfiles.ContainsKey(e.Device.Id)) Config.DevicesProfiles.Add(e.Device.Id, new List() { { new DeviceProfile() { Name= "Profile"} } }); Config.DefaultProfile.TryAdd(e.Device.Id, 0); if (Config.DevicesProfiles[e.Device.Id].Count <= Config.DefaultProfile[e.Device.Id] || Config.DefaultProfile[e.Device.Id] < 0) Config.DefaultProfile[e.Device.Id] = 0; DeviceCore device = new(e.Device); - ConnectedDevices.Add(device); - - if (Config.EnabledDevices[e.Device.Id]) - { - device.Connect(); - } - DeviceDetected?.Invoke(sender, e); + AddDevice(device); } private static void Protocol_DeviceDisconnected(object? sender, DeviceDetectedEventArgs e) { - Console.WriteLine($"Device disconnected: {e.Device.Name} ({e.Device.Id})"); - ConnectedDevices.RemoveAll(device=>device.ProtocolDevice == e.Device); + ConnectedDevices.RemoveWhere(device=>device.ProtocolDevice == e.Device); DeviceDisconnected?.Invoke(sender, e); } - - public static void EnablePluginFeature(string pluginId) + public static void EnableDevice(string deviceId) { - EnableProtocol(pluginId); + DeviceCore? device = ConnectedDevices.FirstOrDefault((x) => x.ProtocolDevice.Id == deviceId); + if (device != null) + { + EnableDevice(device); + } } - public static void DisablePluginFeature(string pluginId) + public static void EnableDevice(DeviceCore device) { - DisableProtocol(pluginId); + device.Connect(); + Config.EnabledDevices.Add(device.ProtocolDevice.Id); } - public static void EnableProtocol(string pluginId) + public static void DisableDevice(string deviceId) { - IProtocol? protocol = PluginLoader.protocols.FirstOrDefault((x) => x.Id == pluginId); - if (protocol != null) + DeviceCore? device = ConnectedDevices.FirstOrDefault((x) => x.ProtocolDevice.Id == deviceId); + if (device != null) { - protocol.Enable(); - Config.PluginsConfig[pluginId] = true; + DisableDevice(device); } } - public static void DisableProtocol(string pluginId) + public static void DisableDevice(DeviceCore device) { - IProtocol? protocol = PluginLoader.protocols.FirstOrDefault((x) => x.Id == pluginId); - if (protocol != null) - { - protocol.Disable(); - Config.PluginsConfig[pluginId] = false; - } + device.Disconnect(); + Config.EnabledDevices.Remove(device.ProtocolDevice.Id); } - - public static void EnableDevice(string deviceId) + public static void RemoveDevice(DeviceCore device) { - DeviceCore? device = ConnectedDevices.Find((x) => x.ProtocolDevice.Id == deviceId); - if (device != null) - { - device.Connect(); - Config.EnabledDevices[deviceId] = true; - } + ConnectedDevices.Remove(device); + DeviceDisconnected?.Invoke(null, new DeviceDetectedEventArgs(device.ProtocolDevice)); } - public static void DisableDevice(string deviceId) + public static void AddDevice(DeviceCore device) { - DeviceCore? device = ConnectedDevices.Find((x) => x.ProtocolDevice.Id == deviceId); - if (device != null) + ConnectedDevices.Add(device); + + if (Config.EnabledDevices.Contains(device.ProtocolDevice.Id)) { - device.Disconnect(); - Config.EnabledDevices[deviceId] = false; + device.Connect(); } + DeviceDetected?.Invoke(null, new DeviceDetectedEventArgs(device.ProtocolDevice)); } } } diff --git a/MacroPad.Core/MacroPad.Core.csproj b/MacroPad.Core/MacroPad.Core.csproj index 19d5f9b..9e0c810 100644 --- a/MacroPad.Core/MacroPad.Core.csproj +++ b/MacroPad.Core/MacroPad.Core.csproj @@ -1,4 +1,4 @@ - + net8.0 @@ -8,7 +8,6 @@ - diff --git a/MacroPad.Core/Config/ButtonConfig.cs b/MacroPad.Core/Models/Config/ButtonConfig.cs similarity index 60% rename from MacroPad.Core/Config/ButtonConfig.cs rename to MacroPad.Core/Models/Config/ButtonConfig.cs index df07e0c..1c8d6f7 100644 --- a/MacroPad.Core/Config/ButtonConfig.cs +++ b/MacroPad.Core/Models/Config/ButtonConfig.cs @@ -1,13 +1,13 @@ using MacroPad.Core.Device; -using Newtonsoft.Json; +using System.Text.Json.Serialization; -namespace MacroPad.Core.Config +namespace MacroPad.Core.Models.Config { public class ButtonConfig { - [JsonProperty("events")] + [JsonPropertyName("events")] public Dictionary EventScripts { get; set; } = []; - [JsonProperty("status")] + [JsonPropertyName("status")] public ButtonStatus Status { get; set; } = new ButtonStatus(); } } diff --git a/MacroPad.Core/Models/Config/ButtonStatus.cs b/MacroPad.Core/Models/Config/ButtonStatus.cs new file mode 100644 index 0000000..884ddcc --- /dev/null +++ b/MacroPad.Core/Models/Config/ButtonStatus.cs @@ -0,0 +1,11 @@ +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; + +namespace MacroPad.Core.Models.Config +{ + public class ButtonStatus + { + [JsonPropertyName("value")] + public JsonValue? Value { get; set; } + } +} diff --git a/MacroPad.Core/Models/Config/DeviceProfile.cs b/MacroPad.Core/Models/Config/DeviceProfile.cs new file mode 100644 index 0000000..83bff53 --- /dev/null +++ b/MacroPad.Core/Models/Config/DeviceProfile.cs @@ -0,0 +1,13 @@ +using System.Text.Json.Serialization; + +namespace MacroPad.Core.Models.Config +{ + public class DeviceProfile + { + [JsonPropertyName("name")] + public string Name { get; set; } = ""; + + [JsonPropertyName("buttons")] + public Dictionary> ButtonsConfig { get; set; } = []; + } +} diff --git a/MacroPad.Core/Models/Config/NodeLine.cs b/MacroPad.Core/Models/Config/NodeLine.cs new file mode 100644 index 0000000..774cf68 --- /dev/null +++ b/MacroPad.Core/Models/Config/NodeLine.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace MacroPad.Core.Models.Config +{ + public class NodeLine + { + [JsonPropertyName("n")] + public int Node { get; set; } + [JsonPropertyName("i")] + public int PointIndex { get; set; } + } +} diff --git a/MacroPad.Core/Models/Config/NodeLinks.cs b/MacroPad.Core/Models/Config/NodeLinks.cs new file mode 100644 index 0000000..7ccc6b4 --- /dev/null +++ b/MacroPad.Core/Models/Config/NodeLinks.cs @@ -0,0 +1,23 @@ +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; + +namespace MacroPad.Core.Models.Config +{ + public class NodeLinks + { + [JsonPropertyName("id")] + public string Id { get; set; } = ""; + [JsonPropertyName("g")] + public Dictionary Getters { get; set; } = []; + [JsonPropertyName("r")] + public Dictionary Runners { get; set; } = []; + [JsonPropertyName("x")] + public int X { get; set; } + [JsonPropertyName("y")] + public int Y { get; set; } + [JsonPropertyName("d")] + public Dictionary Data { get; set; } = []; + [JsonPropertyName("c")] + public Dictionary> Consts { get; set; } = []; + } +} diff --git a/MacroPad.Core/Models/Config/NodeScript.cs b/MacroPad.Core/Models/Config/NodeScript.cs new file mode 100644 index 0000000..7de6d88 --- /dev/null +++ b/MacroPad.Core/Models/Config/NodeScript.cs @@ -0,0 +1,16 @@ +using System.Text.Json.Serialization; + +namespace MacroPad.Core.Models.Config +{ + public class NodeScript + { + [JsonPropertyName("x")] + public int StartX { get; set; } + [JsonPropertyName("y")] + public int StartY { get; set; } + [JsonPropertyName("links")] + public Dictionary NodesLinks { get; set; } = []; + [JsonPropertyName("lines")] + public Dictionary NodeLines { get; set; } = []; + } +} diff --git a/MacroPad.Core/Models/MacroPadConfig.cs b/MacroPad.Core/Models/MacroPadConfig.cs new file mode 100644 index 0000000..53c1af3 --- /dev/null +++ b/MacroPad.Core/Models/MacroPadConfig.cs @@ -0,0 +1,39 @@ +using MacroPad.Core.Models.Config; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; + +namespace MacroPad.Core.Models +{ + public class MacroPadConfig + { + [JsonPropertyName("enabledPlugins")] + public HashSet EnabledPlugins { get; set; } = []; + [JsonPropertyName("loadedPlugins")] + public HashSet LoadedPlugins { get; set; } = []; + [JsonPropertyName("enabledDevices")] + public HashSet EnabledDevices { get; set; } = []; + [JsonPropertyName("devicesProfile")] + public Dictionary DefaultProfile { get; set; } = []; + [JsonPropertyName("devices")] + public Dictionary> DevicesProfiles { get; set; } = []; + [JsonPropertyName("variables")] + public Dictionary Variables { get; set; } = []; + + + + public static MacroPadConfig LoadConfig() + { + if (File.Exists("config.json")) + { + MacroPadConfig? config = JsonSerializer.Deserialize(File.ReadAllText("config.json")); + if (config != null) return config; + } + return new MacroPadConfig(); + } + public void SaveConfig() + { + File.WriteAllText("config.json", JsonSerializer.Serialize(this)); + } + } +} diff --git a/MacroPad.Core/Models/Plugin/PluginIconType.cs b/MacroPad.Core/Models/Plugin/PluginIconType.cs new file mode 100644 index 0000000..3afe0ad --- /dev/null +++ b/MacroPad.Core/Models/Plugin/PluginIconType.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MacroPad.Core.Models.Plugin +{ + public enum PluginIconType + { + Symbol, + Image + } +} diff --git a/MacroPad.Core/Models/Plugin/PluginInfos.cs b/MacroPad.Core/Models/Plugin/PluginInfos.cs new file mode 100644 index 0000000..ef61317 --- /dev/null +++ b/MacroPad.Core/Models/Plugin/PluginInfos.cs @@ -0,0 +1,155 @@ +using MacroPad.Core.Device; +using MacroPad.Shared.Plugin; +using MacroPad.Shared.Plugin.Settings; +using McMaster.NETCore.Plugins; +using Microsoft.DotNet.PlatformAbstractions; +using System; +using System.Collections.Generic; +using System.Data; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace MacroPad.Core.Models.Plugin +{ + public class PluginInfos + { + [JsonPropertyName("filename")] + public string? Filename { get; set; } + [JsonIgnore] + public string? PluginDirectory { get; set; } + [JsonIgnore] + public string? PluginId { get; set; } + [JsonPropertyName("name")] + public string Name { get; set; } = "Another plugin"; + [JsonPropertyName("description")] + public string? Description { get; set; } + [JsonPropertyName("version")] + public string Version { get; set; } = "0.0.1"; + [JsonPropertyName("author")] + public string? Author { get; set; } + [JsonPropertyName("authorUrl")] + public string? AuthorUrl { get; set; } + [JsonPropertyName("sourceUrl")] + public string? SourceUrl { get; set; } + [JsonPropertyName("iconType")] + [JsonConverter(typeof(JsonStringEnumConverter))] + public PluginIconType IconType { get; set; } = PluginIconType.Symbol; + [JsonPropertyName("icon")] + public string Icon { get; set; } = "PlugDisconnected"; + [JsonPropertyName("supportedPlatforms")] + public Platform[] SupportedPlatforms { get; set; } = [Platform.Windows]; + [JsonIgnore] + public bool Enabled => DeviceManager.Config.EnabledPlugins.Contains(PluginId!); + + private PluginLoader? _pluginLoader; + [JsonIgnore] + private IPluginInfos? _pluginInfos; + public void Load() + { + if (PluginDirectory == null || Filename == null) throw new NoNullAllowedException("Plugin directory or filename are null"); + string pluginFile = Path.Combine(PluginDirectory, Filename); + + if (!File.Exists(pluginFile)) throw new FileNotFoundException("Plugin file not found"); + + _pluginLoader = PluginLoader.CreateFromAssemblyFile(Path.GetFullPath(pluginFile)); + + Assembly assembly; + + assembly = _pluginLoader.LoadDefaultAssembly(); + + Type? pluginInfosType = assembly.GetTypes().ToList().Find((type) => type.Name == "PluginInfos") ?? + throw new TypeAccessException("Can't find PluginInfos type"); + + object? pluginsInfosInstance = assembly.CreateInstance(pluginInfosType.FullName ?? "") ?? + throw new TypeAccessException($"Cant create instance of \"{pluginInfosType.FullName}\""); + + if (pluginsInfosInstance is not IPluginInfos pluginInfos) throw new InvalidCastException($"Can't cast \"{pluginInfosType.FullName}\" to IPluginInfos"); + _pluginInfos = pluginInfos; + + PluginManager.OnPluginLoaded(_pluginInfos); + } + [JsonIgnore] + public bool IsLoaded => _pluginLoader == null ? false : true; + [JsonIgnore] + public bool IsUnloadable => _pluginInfos?.NodeTypes.Length == 0 && _pluginInfos?.NodeCategories.Length == 0; + [JsonIgnore] + public bool IsReloadable => IsUnloadable || (_pluginInfos?.Protocols.Length >= 1); + [JsonIgnore] + public ISettingsComponent[]? Settings => _pluginInfos?.Settings; + private void Unload() + { + if (_pluginInfos == null) return; + + PluginManager.OnPluginDisabled(_pluginInfos); + + _pluginInfos = null; + _pluginLoader?.Dispose(); + _pluginLoader = null; + } + + private readonly HashSet DisabledDevices = []; + public void Enable() + { + if (_pluginInfos == null) Load(); + if (_pluginInfos == null) return; + PluginManager.OnPluginEnabled(_pluginInfos); + + PluginManager.Protocols.UnionWith(_pluginInfos.Protocols); + + if (!IsUnloadable) + { + foreach (var protocol in _pluginInfos.Protocols) + { + protocol.Enable(); + } + } + foreach (var device in DisabledDevices) + { + if (DeviceManager.Config.EnabledDevices.Contains(device.ProtocolDevice.Id)) DeviceManager.AddDevice(device); + } + } + public void Disable() + { + + if (_pluginInfos == null) return; + PluginManager.Protocols.ExceptWith(_pluginInfos.Protocols); + + foreach (var protocol in _pluginInfos.Protocols) + { + foreach (var device in DeviceManager.ConnectedDevices.Where(x => x.ProtocolDevice.Protocol == protocol.Id)) + { + device.Disconnect(); + DeviceManager.RemoveDevice(device); + if (!IsUnloadable) DisabledDevices.Add(device); + } + protocol.Disable(); + } + + if (IsUnloadable) + { + Unload(); + }else + { + PluginManager.OnPluginDisabled(_pluginInfos); + } + } + public void Reload() + { + if (_pluginInfos == null) return; + + if (IsUnloadable) + { + Unload(); + Load(); + } + else + { + } + } + } +} diff --git a/MacroPad.Core/Models/ResourceManager.cs b/MacroPad.Core/Models/ResourceManager.cs new file mode 100644 index 0000000..7255124 --- /dev/null +++ b/MacroPad.Core/Models/ResourceManager.cs @@ -0,0 +1,29 @@ +using MacroPad.Shared.Plugin; +using System.Text.Json.Nodes; + +namespace MacroPad.Core.Models +{ + public class ResourceManager(Dictionary data, Dictionary? virtualData = null) : IResourceManager + { + public Dictionary Data { get; set; } = data; + + public Dictionary VirtualData { get; set; } = virtualData ?? []; + + public T? GetData(string key) + { + if (Data.TryGetValue(key, out JsonValue? value)) return value.TryGetValue(out T? value2) ? value2 : default; + return default; + } + + public void SetData(string key, object value) + { + JsonValue? value2 = JsonValue.Create(value); + if (value2 != null && !Data.TryAdd(key, value2)) Data[key] = value2; + } + public T? GetVirtual(VirtualDataKey key) + { + if (VirtualData.TryGetValue(key, out object? value) && value is T result) return result; + return default; + } + } +} diff --git a/MacroPad.Core/Node/NodeResourceManager.cs b/MacroPad.Core/Node/NodeResourceManager.cs index 1630aac..6883264 100644 --- a/MacroPad.Core/Node/NodeResourceManager.cs +++ b/MacroPad.Core/Node/NodeResourceManager.cs @@ -1,28 +1,20 @@ -using MacroPad.Shared.Plugin.Nodes; -using Newtonsoft.Json.Linq; +using MacroPad.Core.Models; +using MacroPad.Shared.Plugin.Nodes; +using System.Text.Json.Nodes; namespace MacroPad.Core.Node { - public class NodeResourceManager(Func getValue, Dictionary data) : IResourceManager + public class NodeResourceManager : ResourceManager, INodeResourceManager { - public Dictionary Data { get; set; } = data; - private readonly Func _getValue = getValue; + private readonly Func _getValue; public object GetValue(int index) { return _getValue(index); } - public T? GetData(string key) - { - if (Data.TryGetValue(key, out JToken? value)) return value.Value(); - return default; - } - - public void SetData(string key, object value) - { - if (Data.ContainsKey(key)) Data[key] = JToken.FromObject(value); - else Data.Add(key, JToken.FromObject(value)); + public NodeResourceManager(Dictionary data, Func getValue) : base(data) { + _getValue = getValue; } } } diff --git a/MacroPad.Core/NodeManager.cs b/MacroPad.Core/NodeManager.cs index f8df13f..95e73e8 100644 --- a/MacroPad.Core/NodeManager.cs +++ b/MacroPad.Core/NodeManager.cs @@ -1,9 +1,11 @@ -using MacroPad.Core.Config; -using MacroPad.Core.Device; +using MacroPad.Core.Device; +using MacroPad.Core.Models; +using MacroPad.Core.Models.Config; using MacroPad.Core.Node; using MacroPad.Shared.Plugin; using MacroPad.Shared.Plugin.Nodes; -using Newtonsoft.Json.Linq; +using System.Diagnostics; +using System.Text.Json.Nodes; namespace MacroPad.Core { @@ -14,16 +16,30 @@ public static class NodeManager public static Dictionary Types = []; public readonly static Dictionary Runners = []; public readonly static Dictionary Getters = []; + + public readonly static HashSet NodeTypes = []; + public readonly static HashSet NodeCategories = []; + public static DeviceCore? CurrentDevice { get; private set; } public static DeviceLayoutButton? CurrentButton { get; private set; } public static void Init() { - foreach (NodeType type in PluginLoader.nodeTypes) { + PluginManager.PluginLoaded += PluginManager_PluginLoaded; + } + + + private static void PluginManager_PluginLoaded(object? sender, IPluginInfos e) + { + NodeTypes.UnionWith(e.NodeTypes); + NodeCategories.UnionWith(e.NodeCategories); + + foreach (NodeType type in e.NodeTypes) + { Types.Add(type.Type, type); } - foreach (INodeCategory category in PluginLoader.nodeCategories) + foreach (INodeCategory category in e.NodeCategories) { foreach (INodeRunner node in category.Runners) { @@ -65,13 +81,13 @@ object GetValue(int index) object value = type.DefaultValue; if (links.Getters.TryGetValue(index, out int value2)) value = GetLine(value2) ?? type.DefaultValue; else return GetConst(links.Consts, type, index) ?? type.DefaultValue; - if (value.GetType().IsAssignableFrom(typeof(JValue))) value = ((JValue)value).Value ?? type.DefaultValue; + if (value.GetType().IsAssignableFrom(typeof(JsonValue))) value = ((JsonValue)value).TryGetValue(out object? value3) ? value3 ?? type.DefaultValue : type.DefaultValue; if (type.Type.IsAssignableFrom(value.GetType())) return value; if (type.TypeConverter != null) return type.TypeConverter(value) ?? type.DefaultValue; return type.DefaultValue; } - NodeRunnerResult result = nodeRunner.Run(new NodeResourceManager(GetValue, links.Data)); + NodeRunnerResult result = nodeRunner.Run(new NodeResourceManager(links.Data, GetValue)); if (cache.ContainsKey(linksId)) cache[linksId] = result.Results; else cache.Add(linksId, result.Results); @@ -103,13 +119,13 @@ object GetValue(int index) object value = type.DefaultValue; if (links.Getters.TryGetValue(index, out int value2)) value = GetLine(value2) ?? type.DefaultValue; else return GetConst(links.Consts, type, index) ?? type.DefaultValue; - if (value.GetType().IsAssignableFrom(typeof(JValue))) value = ((JValue)value).Value ?? type.DefaultValue; + if (value.GetType().IsAssignableFrom(typeof(JsonValue))) value = ((JsonValue)value).TryGetValue(out object? value3) ? value3 ?? type.DefaultValue : type.DefaultValue; if (type.Type.IsAssignableFrom(value.GetType())) return value; if (type.TypeConverter != null) return type.TypeConverter(value) ?? type.DefaultValue; return type.DefaultValue; } - object[] result = nodeGetter.GetOutputs(new NodeResourceManager(GetValue, links.Data)); + object[] result = nodeGetter.GetOutputs(new NodeResourceManager(links.Data, GetValue)); if (!cache.TryAdd(linksId, result)) cache[linksId] = result; if (index < result.Length) return result[index]; @@ -117,10 +133,10 @@ object GetValue(int index) return null; } - object? GetConst(Dictionary> consts, NodeType type, int index) + object? GetConst(Dictionary> consts, NodeType type, int index) { - if (!consts.TryGetValue(index, out Dictionary? data)) return null; - return type.Load != null ? type.Load(new NodeResourceManager((i) => throw new Exception("Can't get value of a type"), data)) : null; + if (!consts.TryGetValue(index, out Dictionary? data)) return null; + return type.Load != null ? type.Load(new ResourceManager(data)) : null; } diff --git a/MacroPad.Core/PluginLoader.cs b/MacroPad.Core/PluginLoader.cs deleted file mode 100644 index cf7c3f4..0000000 --- a/MacroPad.Core/PluginLoader.cs +++ /dev/null @@ -1,70 +0,0 @@ -using MacroPad.Core.BasePlugin; -using MacroPad.Shared.Plugin; -using Newtonsoft.Json.Linq; -using System.Reflection; - -namespace MacroPad.Core -{ - public class PluginLoader - { - public readonly static HashSet plugins = []; - - public readonly static HashSet protocols = []; - public readonly static HashSet nodeTypes = [..DefaultTypes.types]; - public readonly static HashSet nodeCategories = [new BranchingCategory(), new ButtonCategory(), new ConditionsCategory(), new ConstantsCategory(), new DebugCategory(), new MathCategory(), new ProfileCategory(), new TextCategory(), new VariableCategory()]; - - public static void LoadPlugins() - { - if (!Directory.Exists("plugins")) return; - IEnumerable pluginDirs = Directory.EnumerateDirectories("plugins"); - - foreach (var pluginDir in pluginDirs) - { - string pluginJsonFile = Path.Combine(pluginDir,"plugin.json"); - - if (!File.Exists(pluginJsonFile)) continue; - - JObject pluginJson = JObject.Parse(File.ReadAllText(pluginJsonFile)); - - if (!pluginJson.ContainsKey("Main")) continue; - - string pluginFile = Path.Combine(pluginDir, pluginJson["Main"]?.ToString() ?? ""); - - if (!File.Exists(pluginFile)) continue; - - McMaster.NETCore.Plugins.PluginLoader pluginLoader = McMaster.NETCore.Plugins.PluginLoader.CreateFromAssemblyFile(Path.GetFullPath(pluginFile)); - - Assembly assembly; - try - { - assembly = pluginLoader.LoadDefaultAssembly(); - } catch - { - throw new Exception("Failed to load plugin assembly"); - } - - - Type? pluginInfosType = assembly.GetTypes().ToList().Find((Type type) => type.Name == "PluginInfos"); - - if (pluginInfosType == null) continue; - - object? pluginsInfosInstance = assembly.CreateInstance(pluginInfosType.FullName ?? ""); - - if (pluginsInfosInstance == null) continue; - - IPluginInfos? pluginInfos = pluginsInfosInstance as IPluginInfos; - - if (pluginInfos == null) continue; - - plugins.Add(pluginInfos); - if (pluginInfos.Protocols != null) protocols.UnionWith(pluginInfos.Protocols); - - if (pluginInfos.NodeCategories != null) nodeCategories.UnionWith(pluginInfos.NodeCategories); - if (pluginInfos.NodeTypes != null) nodeTypes.UnionWith(pluginInfos.NodeTypes); - } - - //plugins.Sort((a,b)=>a.Name.CompareTo(b.Name)); - //nodeCategories.Sort((a,b)=>a.Name.CompareTo(b.Name)); - } - } -} \ No newline at end of file diff --git a/MacroPad.Core/PluginManager.cs b/MacroPad.Core/PluginManager.cs new file mode 100644 index 0000000..f982c9e --- /dev/null +++ b/MacroPad.Core/PluginManager.cs @@ -0,0 +1,88 @@ +using MacroPad.Core.BasePlugin; +using MacroPad.Core.Models.Plugin; +using MacroPad.Shared.Plugin; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace MacroPad.Core +{ + public static class PluginManager + { + public readonly static HashSet Protocols = []; + + public readonly static HashSet Plugins = []; + public readonly static IPluginInfos BasePlugin = new BasePluginInfos(); + + public static event EventHandler? PluginLoaded; + public static event EventHandler? PluginEnabled; + public static event EventHandler? PluginDisabled; + public static event EventHandler? PluginAdded; + public static event EventHandler? PluginRemoved; + + public static void ScanPlugins() + { + if (!Directory.Exists("plugins")) Directory.CreateDirectory("plugins"); + + JsonSerializerOptions options = new JsonSerializerOptions(); + options.Converters.Add(new JsonStringEnumConverter()); + + Plugins.UnionWith(Directory.EnumerateDirectories("plugins").Where(directory=>!Plugins.Any(x=>x.PluginDirectory==directory)).Select((directory) => + { + string pluginJsonPath = Path.Combine(directory, "plugin.json"); + if (!File.Exists(pluginJsonPath)) return null; + + var file = File.OpenRead(pluginJsonPath); + PluginInfos? pluginInfos = JsonSerializer.Deserialize(file, options); + file.Close(); + + if (pluginInfos == null) return null; + pluginInfos.PluginDirectory = directory; + pluginInfos.PluginId = Path.GetFileName(directory); + PluginAdded?.Invoke(null, pluginInfos); + return pluginInfos; + }).Where(x=>x!=null).Select(x=>x!).ToHashSet()); + + PluginLoaded?.Invoke(null, BasePlugin); + PluginEnabled?.Invoke(null, BasePlugin); + + foreach (var plugin in Plugins) + { + if (DeviceManager.Config.LoadedPlugins.Contains(plugin.PluginId!)) + { + if (DeviceManager.Config.EnabledPlugins.Contains(plugin.PluginId!)) plugin.Enable(); + else plugin.Load(); + } + } + } + + public static void EnablePlugin(PluginInfos plugin) + { + DeviceManager.Config.EnabledPlugins.Add(plugin.PluginId!); + DeviceManager.Config.LoadedPlugins.Add(plugin.PluginId!); + plugin.Enable(); + } + public static void DisablePlugin(PluginInfos plugin) + { + DeviceManager.Config.EnabledPlugins.Remove(plugin.PluginId!); + if (plugin.IsUnloadable) DeviceManager.Config.LoadedPlugins.Remove(plugin.PluginId!); + plugin.Disable(); + } + public static void OnPluginLoaded(IPluginInfos plugin) + { + PluginLoaded?.Invoke(null, plugin); + } + public static void OnPluginEnabled(IPluginInfos plugin) + { + PluginEnabled?.Invoke(null, plugin); + } + public static void OnPluginDisabled(IPluginInfos plugin) + { + PluginDisabled?.Invoke(null, plugin); + } + } +} diff --git a/MacroPad.Shared/Plugin/Components/ComboBox.cs b/MacroPad.Shared/Plugin/Components/ComboBox.cs index d18ee99..de23257 100644 --- a/MacroPad.Shared/Plugin/Components/ComboBox.cs +++ b/MacroPad.Shared/Plugin/Components/ComboBox.cs @@ -10,7 +10,7 @@ public class ComboBox : INodeComponent, ISettingsComponent public Action? SelectionChanged { get; set; } public Func? GetSelection { get; set; } public Func? GetSelectedItem { get; set; } - public Func? GetItems { get; set; } + public Func? GetItems { get; set; } public ObservableCollection Items { get; set; } = []; } } diff --git a/MacroPad.Shared/Plugin/Components/IComponent.cs b/MacroPad.Shared/Plugin/Components/IComponent.cs new file mode 100644 index 0000000..c9a25f7 --- /dev/null +++ b/MacroPad.Shared/Plugin/Components/IComponent.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MacroPad.Shared.Plugin.Components +{ + public interface IComponent + { + } +} diff --git a/MacroPad.Shared/Plugin/IPluginInfos.cs b/MacroPad.Shared/Plugin/IPluginInfos.cs index 7c505cc..49318aa 100644 --- a/MacroPad.Shared/Plugin/IPluginInfos.cs +++ b/MacroPad.Shared/Plugin/IPluginInfos.cs @@ -4,13 +4,6 @@ namespace MacroPad.Shared.Plugin { public interface IPluginInfos { - public string Name { get; } - public string Description { get; } - public string Version { get; } - public string Author { get; } - public string? AuthorUrl { get; } - public string? SourceUrl { get; } - public IProtocol[] Protocols { get; } public INodeCategory[] NodeCategories { get; } public NodeType[] NodeTypes { get; } diff --git a/MacroPad.Shared/Plugin/Nodes/IResourceManager.cs b/MacroPad.Shared/Plugin/IResourceManager.cs similarity index 62% rename from MacroPad.Shared/Plugin/Nodes/IResourceManager.cs rename to MacroPad.Shared/Plugin/IResourceManager.cs index fb0c608..5c06e4b 100644 --- a/MacroPad.Shared/Plugin/Nodes/IResourceManager.cs +++ b/MacroPad.Shared/Plugin/IResourceManager.cs @@ -1,9 +1,9 @@ -namespace MacroPad.Shared.Plugin.Nodes +namespace MacroPad.Shared.Plugin { public interface IResourceManager { - public object GetValue(int index); public T? GetData(string key); public void SetData(string key, object value); + public T? GetVirtual(VirtualDataKey key); } } diff --git a/MacroPad.Shared/Plugin/Nodes/INodeComponent.cs b/MacroPad.Shared/Plugin/Nodes/INodeComponent.cs index 3349998..4701bc2 100644 --- a/MacroPad.Shared/Plugin/Nodes/INodeComponent.cs +++ b/MacroPad.Shared/Plugin/Nodes/INodeComponent.cs @@ -1,6 +1,8 @@ -namespace MacroPad.Shared.Plugin.Nodes +using MacroPad.Shared.Plugin.Components; + +namespace MacroPad.Shared.Plugin.Nodes { - public interface INodeComponent + public interface INodeComponent : IComponent { } } diff --git a/MacroPad.Shared/Plugin/Nodes/INodeGetter.cs b/MacroPad.Shared/Plugin/Nodes/INodeGetter.cs index bdf225b..b009dd7 100644 --- a/MacroPad.Shared/Plugin/Nodes/INodeGetter.cs +++ b/MacroPad.Shared/Plugin/Nodes/INodeGetter.cs @@ -12,7 +12,7 @@ public interface INodeGetter public TypeNamePair[] Outputs { get; } public INodeComponent[] Components { get; } public bool IsVisible(IDeviceLayoutButton button, IDeviceOutput output); - public object[] GetOutputs(IResourceManager resource); + public object[] GetOutputs(INodeResourceManager resource); } } diff --git a/MacroPad.Shared/Plugin/Nodes/INodeResourceManager.cs b/MacroPad.Shared/Plugin/Nodes/INodeResourceManager.cs new file mode 100644 index 0000000..1139acc --- /dev/null +++ b/MacroPad.Shared/Plugin/Nodes/INodeResourceManager.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MacroPad.Shared.Plugin.Nodes +{ + public interface INodeResourceManager : IResourceManager + { + public object GetValue(int index); + } +} diff --git a/MacroPad.Shared/Plugin/Nodes/INodeRunner.cs b/MacroPad.Shared/Plugin/Nodes/INodeRunner.cs index 5cf1f7a..7953c60 100644 --- a/MacroPad.Shared/Plugin/Nodes/INodeRunner.cs +++ b/MacroPad.Shared/Plugin/Nodes/INodeRunner.cs @@ -16,7 +16,7 @@ public interface INodeRunner public bool IsVisible(IDeviceLayoutButton button, IDeviceOutput output); - public NodeRunnerResult Run(IResourceManager resource); + public NodeRunnerResult Run(INodeResourceManager resource); } } diff --git a/MacroPad.Shared/Plugin/Settings/ISettingsComponent.cs b/MacroPad.Shared/Plugin/Settings/ISettingsComponent.cs index afbe948..5a8451e 100644 --- a/MacroPad.Shared/Plugin/Settings/ISettingsComponent.cs +++ b/MacroPad.Shared/Plugin/Settings/ISettingsComponent.cs @@ -1,6 +1,8 @@ -namespace MacroPad.Shared.Plugin.Settings +using MacroPad.Shared.Plugin.Components; + +namespace MacroPad.Shared.Plugin.Settings { - public interface ISettingsComponent + public interface ISettingsComponent : IComponent { } } diff --git a/MacroPad.Shared/Plugin/VirtualDataKey.cs b/MacroPad.Shared/Plugin/VirtualDataKey.cs new file mode 100644 index 0000000..74ec147 --- /dev/null +++ b/MacroPad.Shared/Plugin/VirtualDataKey.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MacroPad.Shared.Plugin +{ + public enum VirtualDataKey + { + DeviceLayoutButton, + DeviceOutput + } +} diff --git a/MacroPad/App.axaml b/MacroPad/App.axaml index a1bca98..f486beb 100644 --- a/MacroPad/App.axaml +++ b/MacroPad/App.axaml @@ -19,7 +19,6 @@ - diff --git a/MacroPad/AppViewModel.cs b/MacroPad/AppViewModel.cs index d9b7787..0d90069 100644 --- a/MacroPad/AppViewModel.cs +++ b/MacroPad/AppViewModel.cs @@ -14,7 +14,7 @@ public class AppViewModel public AppViewModel() { - _mainWindow = new MainWindow(); + _mainWindow = new MainWindow() { DataContext = new MainWindowViewModel()}; ExitCommand = ReactiveCommand.Create(Exit); } diff --git a/MacroPad/Controls/ComponentDisplay.cs b/MacroPad/Controls/ComponentDisplay.cs new file mode 100644 index 0000000..27deb00 --- /dev/null +++ b/MacroPad/Controls/ComponentDisplay.cs @@ -0,0 +1,185 @@ +using Avalonia; +using Avalonia.Controls; +using MacroPad.Controls.Home; +using MacroPad.Core.Device; +using MacroPad.Shared.Plugin; +using System.Collections.ObjectModel; +using IComponent = MacroPad.Shared.Plugin.Components.IComponent; + +namespace MacroPad.Controls +{ + public class ComponentDisplay : UserControl + { + + /// + /// Component StyledProperty definition + /// + public static readonly StyledProperty ComponentProperty = + AvaloniaProperty.Register(nameof(Component)); + + /// + /// Gets or sets the Component property. This StyledProperty + /// indicates the current component displayed. + /// + public IComponent? Component + { + get => GetValue(ComponentProperty); + set => SetValue(ComponentProperty, value); + } + + + /// + /// SmallMode StyledProperty definition + /// + public static readonly StyledProperty SmallModeProperty = + AvaloniaProperty.Register(nameof(SmallMode), false); + + /// + /// Gets or sets the SmallMode property. This StyledProperty + /// indicates if the component have a small height or not. + /// + public bool SmallMode + { + get => this.GetValue(SmallModeProperty); + set => SetValue(SmallModeProperty, value); + } + + + /// + /// ResourceManager StyledProperty definition + /// + public static readonly StyledProperty ResourceManagerProperty = + AvaloniaProperty.Register(nameof(ResourceManager)); + + /// + /// Gets or sets the ResourceManager property. This StyledProperty + /// indicates the resource manager used by the component. + /// + public IResourceManager ResourceManager + { + get => GetValue(ResourceManagerProperty); + set => SetValue(ResourceManagerProperty, value); + } + + + + + + + public ComponentDisplay() + { + + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == ComponentProperty) + { + if (change.NewValue is IComponent component) + { + UpdateComponent(component, SmallMode); + } + } + else if (change.Property == SmallModeProperty) { + IComponent? component = Component; + if (component != null) + { + UpdateComponent(component, (change.NewValue as bool?) ?? false); + } + } + } + + private void UpdateComponent(IComponent component, bool smallMode) + { + switch (component) + { + case Shared.Plugin.Components.TextBox textBox: + TextBox textBoxControl = new() { + Text = textBox.GetText != null ? textBox.GetText(ResourceManager) : "", + Width = 196, + }; + textBoxControl.TextChanged += (object? sender, TextChangedEventArgs e) => { + textBox.TextChanged?.Invoke(ResourceManager, textBoxControl.Text); + }; + if (smallMode) + { + textBoxControl.Height = 20d; + textBoxControl.Classes.Add("small"); + } + Content = textBoxControl; + break; + case Shared.Plugin.Components.NumericUpDown numericUpDown: + NumericUpDown numericUpDownControl = new() + { + Minimum = numericUpDown.Min, + Maximum = numericUpDown.Max, + Value = numericUpDown.GetValue != null ? numericUpDown.GetValue(ResourceManager) : 0, + Width = 196 + }; + if (smallMode) + { + numericUpDownControl.Height = 20d; + numericUpDownControl.Classes.Add("small"); + } + numericUpDownControl.ValueChanged += (object? sender, NumericUpDownValueChangedEventArgs e) => { + numericUpDown.ValueChanged?.Invoke(ResourceManager, e.NewValue ?? 0); + }; + Content = numericUpDownControl; + break; + case Shared.Plugin.Components.ComboBox comboBox: + ObservableCollection GetItems() + { + if (comboBox.GetItems != null) + { + return new ObservableCollection(comboBox.GetItems(ResourceManager)); + } + else + { + return comboBox.Items; + } + } + ObservableCollection items = GetItems(); + ComboBox comboBoxControl = new() { ItemsSource = items }; + + void UpdateSelection() + { + int index = 0; + if (comboBox.GetSelectedItem != null) + { + string value = comboBox.GetSelectedItem(ResourceManager); + index = items.IndexOf(value); + } + if (comboBox.GetSelection != null) + { + index = comboBox.GetSelection(ResourceManager); + } + if (index < items.Count && index >= 0) comboBoxControl.SelectedIndex = index; + } + UpdateSelection(); + + + comboBoxControl.Items.CollectionChanged += (s, e) => + { + UpdateSelection(); + }; + + comboBoxControl.SelectionChanged += (object? sender, SelectionChangedEventArgs e) => { + comboBox.SelectionChanged?.Invoke(ResourceManager, comboBoxControl.SelectedIndex); + }; + comboBoxControl.Width = 196; + if (smallMode) + { + comboBoxControl.Height = 20; + comboBoxControl.Classes.Add("small"); + } + Content = comboBoxControl; + break; + default: + Content = new TextBlock() { Text = "[Unsupported component]" }; + break; + } + } + } +} diff --git a/MacroPad/Controls/Home/DeviceViewer.axaml b/MacroPad/Controls/DeviceViewer.axaml similarity index 78% rename from MacroPad/Controls/Home/DeviceViewer.axaml rename to MacroPad/Controls/DeviceViewer.axaml index bd8e023..0a8061e 100644 --- a/MacroPad/Controls/Home/DeviceViewer.axaml +++ b/MacroPad/Controls/DeviceViewer.axaml @@ -4,18 +4,24 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:paz="using:Avalonia.Controls.PanAndZoom" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" - x:Class="MacroPad.Controls.Home.DeviceViewer"> + x:Class="MacroPad.Controls.DeviceViewer"> + + + + + + HorizontalAlignment="Stretch" > - + diff --git a/MacroPad/Controls/Home/DeviceViewer.axaml.cs b/MacroPad/Controls/DeviceViewer.axaml.cs similarity index 94% rename from MacroPad/Controls/Home/DeviceViewer.axaml.cs rename to MacroPad/Controls/DeviceViewer.axaml.cs index ae1e4b9..65282c4 100644 --- a/MacroPad/Controls/Home/DeviceViewer.axaml.cs +++ b/MacroPad/Controls/DeviceViewer.axaml.cs @@ -1,13 +1,8 @@ using Avalonia; using Avalonia.Controls; -using Avalonia.Markup.Xaml; using MacroPad.Core.Device; using MacroPad.Core.BasePlugin.Device; -using MacroPad.Controls.Settings; -using MacroPad.Pages.Settings; -using MacroPad.Shared.Plugin; using System.Linq; -using System.Diagnostics; using System.Collections.Generic; using Avalonia.Media; using Avalonia.Threading; @@ -16,18 +11,22 @@ using Avalonia.Interactivity; using System.Threading.Tasks; using Avalonia.Controls.Primitives; -using Avalonia.Markup.Xaml.Templates; using Avalonia.Controls.Templates; using Avalonia.Data; -using Newtonsoft.Json.Linq; using System.IO; using MacroPad.Shared.Device; -namespace MacroPad.Controls.Home; +namespace MacroPad.Controls; public partial class DeviceViewer : UserControl { - public DeviceCore Device { get; set; } = new DeviceCore(new BaseDevice()); + public static readonly StyledProperty DeviceProperty = + AvaloniaProperty.Register(nameof(Device), new DeviceCore(new BaseDevice())); + public DeviceCore Device + { + get => GetValue(DeviceProperty); + set => SetValue(DeviceProperty, value); + } private Dictionary _buttons = new Dictionary(); private Dictionary _buttonsDisplay = new Dictionary(); @@ -47,6 +46,20 @@ public DeviceViewer() ZoomAndPan.ZoomChanged += ZoomAndPan_ZoomChanged; } + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == DeviceProperty) { + DeviceCore? oldDevice = change.OldValue as DeviceCore; + if (oldDevice != null) { + oldDevice.Input -= Device_Input; + oldDevice.OutputSet -= Device_OutputSet; + } + LoadDeviceLayout(); + } + } + private async void ZoomAndPan_SizeChanged(object? sender, SizeChangedEventArgs e) { if (originalZoom && Device.Layout != null) @@ -100,6 +113,11 @@ private void ZoomAndPan_ZoomChanged(object sender, Avalonia.Controls.PanAndZoom. // protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + + } + + private void LoadDeviceLayout() { if (Device.Layout != null) { @@ -123,12 +141,8 @@ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) foreach (DeviceLayoutButton button in Device.Layout.Buttons) { - - if (Device.Layout.OutputTypes.ContainsKey(button.Output)) + if (Device.Layout.OutputTypes.TryGetValue(button.Output, out DeviceOutput? outputType)) { - DeviceOutput outputType = Device.Layout.OutputTypes[button.Output]; - - Grid buttonContainer = new Grid(); Control control; Grid container = new Grid(); @@ -333,7 +347,7 @@ private void Device_OutputSet(object? sender, DeviceCoreOutputSetEventArgs e) { if (_buttons.ContainsKey(e.Button.Id)) { - SetButtonDisplay(_buttons[e.Button.Id], _buttonsDisplay[e.Button.Id], e.Button, e.Value); + Dispatcher.UIThread.Invoke(()=> SetButtonDisplay(_buttons[e.Button.Id], _buttonsDisplay[e.Button.Id], e.Button, e.Value)); } } diff --git a/MacroPad/Controls/Home/DeviceViewerButtonPressedEventArgs.cs b/MacroPad/Controls/DeviceViewerButtonPressedEventArgs.cs similarity index 85% rename from MacroPad/Controls/Home/DeviceViewerButtonPressedEventArgs.cs rename to MacroPad/Controls/DeviceViewerButtonPressedEventArgs.cs index 70b389a..3a0fd2a 100644 --- a/MacroPad/Controls/Home/DeviceViewerButtonPressedEventArgs.cs +++ b/MacroPad/Controls/DeviceViewerButtonPressedEventArgs.cs @@ -1,6 +1,6 @@ using MacroPad.Core.Device; -namespace MacroPad.Controls.Home +namespace MacroPad.Controls { public class DeviceViewerButtonPressedEventArgs(DeviceLayoutButton? button) { diff --git a/MacroPad/Controls/Home/DeviceProfileSelector.axaml b/MacroPad/Controls/Home/DeviceProfileSelector.axaml deleted file mode 100644 index 78dceb8..0000000 --- a/MacroPad/Controls/Home/DeviceProfileSelector.axaml +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/MacroPad/Controls/Home/DeviceProfileSelector.axaml.cs b/MacroPad/Controls/Home/DeviceProfileSelector.axaml.cs deleted file mode 100644 index fa88513..0000000 --- a/MacroPad/Controls/Home/DeviceProfileSelector.axaml.cs +++ /dev/null @@ -1,179 +0,0 @@ -using Avalonia; -using Avalonia.Controls; -using Avalonia.Input; -using Avalonia.Interactivity; -using MacroPad.Core.BasePlugin.Device; -using MacroPad.Core.Device; -using MacroPad.Pages; -using System.Linq; -using System; -using MacroPad.Core.Config; -using FluentAvalonia.UI.Controls; -using ReactiveUI; -using Avalonia.Threading; - -namespace MacroPad.Controls.Home; - -public partial class DeviceProfileSelector : UserControl -{ - public DeviceCore Device { get; set; } = new DeviceCore(new BaseDevice()); - public DeviceViewer? DeviceViewer { get; set; } - public MainPage? MainPage { get; set; } - - - private bool _changingProfile = false; - public DeviceProfileSelector() - { - InitializeComponent(); - - KeyBindings.Add(new KeyBinding() { Gesture = new KeyGesture(Key.F2), Command = ReactiveCommand.Create(() => Rename_Click(this, new RoutedEventArgs())) }); - KeyBindings.Add(new KeyBinding() { Gesture = new KeyGesture(Key.Delete), Command = ReactiveCommand.Create(() => Remove_Click(this, new RoutedEventArgs())) }); - KeyBindings.Add(new KeyBinding() { Gesture = new KeyGesture(Key.Up, KeyModifiers.Alt), Command = ReactiveCommand.Create(() => MoveUp_Click(this, new RoutedEventArgs())) }); - KeyBindings.Add(new KeyBinding() { Gesture = new KeyGesture(Key.Down, KeyModifiers.Alt), Command = ReactiveCommand.Create(() => MoveDown_Click(this, new RoutedEventArgs())) }); - } - - private void Device_ProfileSelected(object? sender, EventArgs e) - { - Dispatcher.UIThread.Invoke(() => - { - if (DeviceProfileSelectorList.SelectedIndex != Device.CurrentProfileIndex) - { - _changingProfile = true; - DeviceProfileSelectorList.SelectedIndex = Device.CurrentProfileIndex; - } - }); - } - - private void DeviceProfileSelectorList_SelectionChanged(object? sender, SelectionChangedEventArgs e) - { - if (DeviceProfileSelectorList.SelectedItem != null && DeviceProfileSelectorList.ItemsSource != null) - { - if (_changingProfile) _changingProfile = false; - else Device.SelectProfile(((ProfileListBoxItemViewModel)DeviceProfileSelectorList.SelectedItem).Index); - DeviceViewer?.SelectButton(null); - } - } - - protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) - { - Device.ProfileSelected += Device_ProfileSelected; - RefreshList(Device.CurrentProfileIndex); - } - - private void AddProfile_Click(object? sender, RoutedEventArgs e) - { - int profileNumber = 1; - - while (Device.DeviceProfiles.Any(x=>x.Name=="New Profile" + (profileNumber == 1 ? "" : $" {profileNumber}"))) profileNumber++; - - Device.DeviceProfiles.Add(new DeviceProfile() { Name = "New Profile" + (profileNumber == 1 ? "" : $" {profileNumber}") }); - - RefreshList(); - } - private void MakeDefault_Click(object? sender, RoutedEventArgs e) - { - if (DeviceProfileSelectorList.SelectedItem != null) - { - Device.DefaultProfile = ((ProfileListBoxItemViewModel)DeviceProfileSelectorList.SelectedItem).Index; - - RefreshList(); - } - } - public void RefreshList(int? overrideIndex = null) - { - DeviceProfileSelectorList.SelectionChanged -= DeviceProfileSelectorList_SelectionChanged; - int selectedIndex = DeviceProfileSelectorList.SelectedIndex; - DeviceProfileSelectorList.ItemsSource = null; - int defaultProfile = Device.DefaultProfile; - DeviceProfileSelectorList.ItemsSource = Device.DeviceProfiles.Select(x => new ProfileListBoxItemViewModel() { Name = x.Name, Index = Device.DeviceProfiles.IndexOf(x), IsDefault = Device.DeviceProfiles.IndexOf(x) == defaultProfile }); - - DeviceProfileSelectorList.SelectionChanged += DeviceProfileSelectorList_SelectionChanged; - - if (DeviceProfileSelectorList.ItemCount > 0) - { - DeviceProfileSelectorList.SelectedIndex = Math.Min(overrideIndex ?? selectedIndex, DeviceProfileSelectorList.ItemCount - 1); - DeviceProfileSelectorList.SelectedItem = DeviceProfileSelectorList.Items[DeviceProfileSelectorList.SelectedIndex]; - } - } - private async void Rename_Click(object? sender, RoutedEventArgs e) - { - if (DeviceProfileSelectorList.SelectedItem != null) - { - var textBox = new TextBox() { Text = ((ProfileListBoxItemViewModel)DeviceProfileSelectorList.SelectedItem).Name }; - var dialog = new ContentDialog() { Title = "Rename Profile", IsPrimaryButtonEnabled = true, PrimaryButtonText = "Cancel", IsSecondaryButtonEnabled = true, SecondaryButtonText = "Rename", Content = textBox }; - dialog.KeyBindings.Add(new KeyBinding() { Gesture = new KeyGesture(Key.Enter), Command = ReactiveCommand.Create(() => dialog.Hide(ContentDialogResult.Secondary)) }); - textBox.SelectAll(); - ContentDialogResult result = await dialog.ShowAsync(); - if (result == ContentDialogResult.Secondary) - { - string? newName = textBox.Text; - if (newName != null) - { - Device.DeviceProfiles[DeviceProfileSelectorList.SelectedIndex].Name = newName; - - RefreshList(); - } - } - } - } - object? deletionWarning; - private async void Remove_Click(object? sender, RoutedEventArgs e) - { - if (DeviceProfileSelectorList.SelectedItem != null) - { - if (deletionWarning == null) TryGetResource("deletionWarning", null, out deletionWarning); - var dialog = new ContentDialog() - { - Title = "Remove Profile", - IsPrimaryButtonEnabled = true, - PrimaryButtonText = "Cancel", - IsSecondaryButtonEnabled = true, - SecondaryButtonText = "Remove", - Content = deletionWarning - }; - ContentDialogResult result = await dialog.ShowAsync(); - dialog.Content = null; - if (result == ContentDialogResult.Secondary) - { - if (DeviceProfileSelectorList.SelectedIndex == Device.DefaultProfile) Device.DefaultProfile = 0; - Device.DeviceProfiles.RemoveAt(DeviceProfileSelectorList.SelectedIndex); - RefreshList(); - } - - } - } - private void MoveUp_Click(object? sender, RoutedEventArgs e) - { - if (DeviceProfileSelectorList.SelectedItem != null) - { - MoveSelected(DeviceProfileSelectorList.SelectedIndex - 1); - } - } - private void MoveDown_Click(object? sender, RoutedEventArgs e) - { - if (DeviceProfileSelectorList.SelectedItem != null) - { - MoveSelected(DeviceProfileSelectorList.SelectedIndex + 1); - } - } - public void MoveSelected(int newIndex) - { - DeviceProfileSelectorList.SelectionChanged -= DeviceProfileSelectorList_SelectionChanged; - int defaultProfile = Device.DefaultProfile; - newIndex = Math.Min(Math.Max(newIndex, 0), Device.DeviceProfiles.Count - 1); - DeviceProfile profile = Device.DeviceProfiles[DeviceProfileSelectorList.SelectedIndex]; - if (DeviceProfileSelectorList.SelectedIndex > defaultProfile && newIndex <= defaultProfile) Device.DefaultProfile++; - else if (DeviceProfileSelectorList.SelectedIndex < defaultProfile && newIndex >= defaultProfile) Device.DefaultProfile--; - else if (DeviceProfileSelectorList.SelectedIndex == defaultProfile) Device.DefaultProfile = newIndex; - Device.DeviceProfiles.RemoveAt(DeviceProfileSelectorList.SelectedIndex); - Device.DeviceProfiles.Insert(newIndex, profile); - RefreshList(newIndex); - } -} - -public struct ProfileListBoxItemViewModel -{ - public string Name { get; set; } - public int Index { get; set; } - public bool IsDefault { get; set; } -} \ No newline at end of file diff --git a/MacroPad/Controls/Home/NodeLinksDisplay.axaml.cs b/MacroPad/Controls/Home/NodeLinksDisplay.axaml.cs index 3421667..a3bb0a2 100644 --- a/MacroPad/Controls/Home/NodeLinksDisplay.axaml.cs +++ b/MacroPad/Controls/Home/NodeLinksDisplay.axaml.cs @@ -5,16 +5,20 @@ using Avalonia.Media; using MacroPad.Controls.Home.NodesEditorHistory.Actions; using MacroPad.Core; -using MacroPad.Core.Config; using MacroPad.Core.Device; +using MacroPad.Core.Models; +using MacroPad.Core.Models.Config; using MacroPad.Core.Node; +using MacroPad.Shared.Device; +using MacroPad.Shared.Plugin; using MacroPad.Shared.Plugin.Nodes; -using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Diagnostics; using System.Linq; using System.Reactive.Linq; +using System.Text.Json.Nodes; namespace MacroPad.Controls.Home; @@ -106,12 +110,12 @@ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) foreach (INodeComponent component in components) { - AddNodeComponent(component, new NodeResourceManager(x => { throw new Exception("Can't get value of a component."); }, links.Data)); + AddNodeComponent(component, new ResourceManager(links.Data, new() { {VirtualDataKey.DeviceOutput, (NodesEditor?.Device?.Layout?.OutputTypes.TryGetValue(NodesEditor?.Button?.Output ?? "", out DeviceOutput? output) ?? false) ? output : null } })); } i = 0; foreach (TypeNamePair node3 in inputs) { - if (NodeManager.Types.TryGetValue(node3.Type, out Shared.Plugin.NodeType? value)) { + if (NodeManager.Types.TryGetValue(node3.Type, out NodeType? value)) { INodeComponent[] nodeComponents = value.Components; if (!links.Consts.ContainsKey(i)) links.Consts.Add(i,[]); } @@ -300,91 +304,9 @@ public DockPanel GetNodeComponent(INodeComponent component, IResourceManager res { DockPanel dockPanel = new() { - Margin = new Thickness(8, 0, 0, 0), + Margin = new Thickness(8, 0, 0, 0) }; - - switch (component) - { - case Shared.Plugin.Components.TextBox textBox: - TextBox textBoxControl = new() { Text = textBox.GetText != null ? textBox.GetText(resource) : "" }; - textBoxControl.TextChanged += (object? sender, TextChangedEventArgs e) => { - textBox.TextChanged?.Invoke(resource, textBoxControl.Text); - }; - textBoxControl.Width = 196; - if (smallMode) - { - textBoxControl.Height = 20d; - textBoxControl.Classes.Add("small"); - } - dockPanel.Children.Add(textBoxControl); - break; - case Shared.Plugin.Components.NumericUpDown numericUpDown: - NumericUpDown numericUpDownControl = new() - { - Minimum = numericUpDown.Min, - Maximum = numericUpDown.Max, - Value = numericUpDown.GetValue != null ? numericUpDown.GetValue(resource) : 0 - }; - numericUpDownControl.ValueChanged += (object? sender, NumericUpDownValueChangedEventArgs e) => { - numericUpDown.ValueChanged?.Invoke(resource, numericUpDownControl.Value ?? 0); - }; - numericUpDownControl.Width = 196; - if (smallMode) - { - numericUpDownControl.Height = 20d; - numericUpDownControl.Classes.Add("small"); - } - dockPanel.Children.Add(numericUpDownControl); - break; - case Shared.Plugin.Components.ComboBox comboBox: - ObservableCollection GetItems() - { - if (comboBox.GetItems != null && NodesEditor?.Button != null && NodesEditor.Device?.Layout != null && NodesEditor.Device.Layout.OutputTypes.TryGetValue(NodesEditor.Button.Output, out DeviceOutput? value)) - { - return new ObservableCollection(comboBox.GetItems(resource, NodesEditor.Button, value)); - } - else - { - return comboBox.Items; - } - } - ObservableCollection items = GetItems(); - ComboBox comboBoxControl = new() { ItemsSource = items}; - - void UpdateSelection() - { - int index = 0; - if (comboBox.GetSelectedItem != null) - { - string value = comboBox.GetSelectedItem(resource); - index = items.IndexOf(value); - } - if (comboBox.GetSelection != null) - { - index = comboBox.GetSelection(resource); - } - if (index < items.Count && index >= 0) comboBoxControl.SelectedIndex = index; - } - UpdateSelection(); - - - comboBoxControl.Items.CollectionChanged += (s, e) => - { - UpdateSelection(); - }; - - comboBoxControl.SelectionChanged += (object? sender, SelectionChangedEventArgs e) => { - comboBox.SelectionChanged?.Invoke(resource, comboBoxControl.SelectedIndex); - }; - comboBoxControl.Width = 196; - if (smallMode) - { - comboBoxControl.Height = 20; - comboBoxControl.Classes.Add("small"); - } - dockPanel.Children.Add(comboBoxControl); - break; - } + dockPanel.Children.Add(new ComponentDisplay() { ResourceManager = resource, SmallMode = smallMode, Component = component }); return dockPanel; } @@ -393,7 +315,7 @@ public void AddNodeComponent(INodeComponent component, IResourceManager resource { InputsContainer.Children.Add(GetNodeComponent(component, resource)); } - public void AddNodePoint(Type? type, string? name, int index, bool isOutput, bool isRunner = false, Dictionary>? data = null) + public void AddNodePoint(Type? type, string? name, int index, bool isOutput, bool isRunner = false, Dictionary>? data = null) { DockPanel dockPanel = new(); Shape linkShape; @@ -461,7 +383,7 @@ public void AddNodePoint(Type? type, string? name, int index, bool isOutput, boo INodeComponent component = components[i]; if (!data.ContainsKey(index)) data.Add(index, []); - DockPanel componentPanel = GetNodeComponent(component, new NodeResourceManager(x => { throw new Exception("Can't get value of a component."); }, data[index]), true); + DockPanel componentPanel = GetNodeComponent(component, new ResourceManager(data[index]), true); componentPanel.SetValue(DockPanel.DockProperty, Dock.Right); componentsPanel.Children.Add(componentPanel); } diff --git a/MacroPad/Controls/Home/NodePicker/NodePicker.axaml.cs b/MacroPad/Controls/Home/NodePicker/NodePicker.axaml.cs index bbf17fd..3e1aedb 100644 --- a/MacroPad/Controls/Home/NodePicker/NodePicker.axaml.cs +++ b/MacroPad/Controls/Home/NodePicker/NodePicker.axaml.cs @@ -2,13 +2,12 @@ using Avalonia.Controls; using Avalonia.Interactivity; using MacroPad.Controls.Home.NodesEditorHistory.Actions; -using MacroPad.Core; -using MacroPad.Core.Config; +using MacroPad.Core.Models.Config; using MacroPad.Core.Device; using MacroPad.Shared.Plugin; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; +using MacroPad.Core; namespace MacroPad.Controls.Home.NodePicker; @@ -25,12 +24,12 @@ public NodePicker() private List _categories = []; protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { - if (CategoriesPanel.Children.Count != PluginLoader.nodeCategories.Count && Editor != null && Editor.Button != null) + if (CategoriesPanel.Children.Count != NodeManager.NodeCategories.Count && Editor != null && Editor.Button != null) { DeviceOutput? output = null; if (Editor.Device != null && Editor.Device.Layout != null && Editor.Device.Layout.OutputTypes.TryGetValue(Editor.Button.Output, out DeviceOutput? value)) output = value; CategoriesPanel.Children.Clear(); - foreach (INodeCategory category in PluginLoader.nodeCategories) + foreach (INodeCategory category in NodeManager.NodeCategories) { var categoryDisplay = new NodePickerCategory() { Category = category, Button = Editor.Button, DeviceOutput = output }; @@ -49,8 +48,6 @@ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) if (nodePosition.HasValue && e.Id != null) { - Debug.WriteLine($"Adding node: {e.Id} at {nodePosition.Value.X}/{nodePosition.Value.Y}"); - var newLinks = new NodeLinks() { Id = e.Id, X = (int)(nodePosition.Value.X), Y = (int)(nodePosition.Value.Y) }; var action = new NodeAddition(newLinks, Editor.CurrentScript.NodesLinks.Count == 0 ? 0 : Editor.CurrentScript.NodesLinks.Keys.Max() + 1, Editor); diff --git a/MacroPad/Controls/Home/NodePicker/NodePickerCategory.axaml.cs b/MacroPad/Controls/Home/NodePicker/NodePickerCategory.axaml.cs index ad364ce..61d43b0 100644 --- a/MacroPad/Controls/Home/NodePicker/NodePickerCategory.axaml.cs +++ b/MacroPad/Controls/Home/NodePicker/NodePickerCategory.axaml.cs @@ -2,6 +2,7 @@ using Avalonia.Controls; using Avalonia.Interactivity; using MacroPad.Core.Device; +using MacroPad.Shared.Device; using MacroPad.Shared.Plugin; using MacroPad.Shared.Plugin.Nodes; using System; @@ -33,11 +34,11 @@ private void NodePickerCategory_Loaded(object? sender, RoutedEventArgs e) CategoryExpander.Header = Category.Name; foreach (INodeGetter getter in Category.Getters) { - if (getter.IsVisible(Button, DeviceOutput)) AddNode(getter.Name, getter.Description, getter.Id); + if (getter.IsVisible(Button, DeviceOutput as IDeviceOutput)) AddNode(getter.Name, getter.Description, getter.Id); } foreach (INodeRunner runner in Category.Runners) { - if (runner.IsVisible(Button, DeviceOutput)) AddNode(runner.Name, runner.Description, runner.Id); + if (runner.IsVisible(Button, DeviceOutput as IDeviceOutput)) AddNode(runner.Name, runner.Description, runner.Id); } } } diff --git a/MacroPad/Controls/Home/NodesEditor.axaml.cs b/MacroPad/Controls/Home/NodesEditor.axaml.cs index 466b1fd..33238a9 100644 --- a/MacroPad/Controls/Home/NodesEditor.axaml.cs +++ b/MacroPad/Controls/Home/NodesEditor.axaml.cs @@ -9,14 +9,14 @@ using FluentAvalonia.UI.Controls; using MacroPad.Controls.Home.NodesEditorHistory; using MacroPad.Controls.Home.NodesEditorHistory.Actions; -using MacroPad.Core.Config; using MacroPad.Core.Device; +using MacroPad.Core.Models.Config; using MacroPad.Shared.Device; -using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Text.Json; namespace MacroPad.Controls.Home; @@ -497,8 +497,6 @@ public void Redo() } public void RecordAction(IHistoryAction action) { - Debug.WriteLine($"Recording action: {action.GetType().Name}"); - _historyIndex++; if (_historyIndex < _history.Count) _history.RemoveRange(_historyIndex, _history.Count - _historyIndex); _history.Add(action); @@ -533,8 +531,6 @@ private void AddNode_Click(object? sender, RoutedEventArgs e) if (nodePosition.HasValue && nodeId != null) { - Debug.WriteLine($"Adding node: {nodeId} at {nodePosition.Value.X}/{nodePosition.Value.Y}"); - var newLinks = new NodeLinks() { Id = nodeId, X = (int)(nodePosition.Value.X / 32), Y = (int)(nodePosition.Value.Y / 32) }; var action = new NodeAddition(newLinks, CurrentScript.NodesLinks.Count == 0 ? 0 : CurrentScript.NodesLinks.Keys.Max()+1, this); @@ -579,7 +575,7 @@ private async void Import_Click(object? sender, RoutedEventArgs e) using var streamReader = new System.IO.StreamReader(stream); string fileContent = await streamReader.ReadToEndAsync(); - ButtonConfig.EventScripts[(ButtonEvent)EventSelector.SelectedIndex] = JsonConvert.DeserializeObject(fileContent) ?? CurrentScript; + ButtonConfig.EventScripts[(ButtonEvent)EventSelector.SelectedIndex] = JsonSerializer.Deserialize(fileContent) ?? CurrentScript; EventSelector_SelectionChanged(this, new SelectionChangedEventArgs(e.RoutedEvent ?? new RoutedEvent("", RoutingStrategies.Direct, typeof(object), typeof(object)), new List(), new List())); } } @@ -602,7 +598,7 @@ private async void Export_Click(object? sender, RoutedEventArgs e) await using var stream = await file.OpenWriteAsync(); using var streamWriter = new System.IO.StreamWriter(stream); - streamWriter.Write(JsonConvert.SerializeObject(CurrentScript)); + streamWriter.Write(JsonSerializer.Serialize(CurrentScript)); } } } \ No newline at end of file diff --git a/MacroPad/Controls/Home/NodesEditorHistory/Actions/NodeAddition.cs b/MacroPad/Controls/Home/NodesEditorHistory/Actions/NodeAddition.cs index 6debef8..df3bfbb 100644 --- a/MacroPad/Controls/Home/NodesEditorHistory/Actions/NodeAddition.cs +++ b/MacroPad/Controls/Home/NodesEditorHistory/Actions/NodeAddition.cs @@ -1,4 +1,4 @@ -using MacroPad.Core.Config; +using MacroPad.Core.Models.Config; namespace MacroPad.Controls.Home.NodesEditorHistory.Actions { diff --git a/MacroPad/Controls/Home/NodesEditorHistory/Actions/NodeDeletion.cs b/MacroPad/Controls/Home/NodesEditorHistory/Actions/NodeDeletion.cs index 60a62df..8834297 100644 --- a/MacroPad/Controls/Home/NodesEditorHistory/Actions/NodeDeletion.cs +++ b/MacroPad/Controls/Home/NodesEditorHistory/Actions/NodeDeletion.cs @@ -1,6 +1,6 @@ using Avalonia.Controls.Shapes; using Avalonia.Interactivity; -using MacroPad.Core.Config; +using MacroPad.Core.Models.Config; using System.Collections.Generic; using System.Linq; diff --git a/MacroPad/Controls/Home/NodesEditorHistory/Actions/NodeLineAddition.cs b/MacroPad/Controls/Home/NodesEditorHistory/Actions/NodeLineAddition.cs index 269bcac..f14f103 100644 --- a/MacroPad/Controls/Home/NodesEditorHistory/Actions/NodeLineAddition.cs +++ b/MacroPad/Controls/Home/NodesEditorHistory/Actions/NodeLineAddition.cs @@ -1,4 +1,4 @@ -using MacroPad.Core.Config; +using MacroPad.Core.Models.Config; namespace MacroPad.Controls.Home.NodesEditorHistory.Actions { diff --git a/MacroPad/Controls/Home/NodesEditorHistory/Actions/NodeLineDeletion.cs b/MacroPad/Controls/Home/NodesEditorHistory/Actions/NodeLineDeletion.cs index 5b8c447..4b32a33 100644 --- a/MacroPad/Controls/Home/NodesEditorHistory/Actions/NodeLineDeletion.cs +++ b/MacroPad/Controls/Home/NodesEditorHistory/Actions/NodeLineDeletion.cs @@ -1,7 +1,7 @@ using Avalonia; using Avalonia.Controls.Shapes; using Avalonia.Media; -using MacroPad.Core.Config; +using MacroPad.Core.Models.Config; using System.Linq; namespace MacroPad.Controls.Home.NodesEditorHistory.Actions diff --git a/MacroPad/Controls/Home/StatusEditor.axaml.cs b/MacroPad/Controls/Home/StatusEditor.axaml.cs index 5a7057b..7f3cb66 100644 --- a/MacroPad/Controls/Home/StatusEditor.axaml.cs +++ b/MacroPad/Controls/Home/StatusEditor.axaml.cs @@ -1,10 +1,9 @@ using Avalonia; using Avalonia.Controls; using MacroPad.Core.BasePlugin.Device; -using MacroPad.Core.Config; using MacroPad.Core.Device; +using MacroPad.Core.Models.Config; using MacroPad.Shared.Device; -using Newtonsoft.Json.Linq; namespace MacroPad.Controls.Home; @@ -25,7 +24,7 @@ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { case OutputType.Palette: int selectedColor = 0; - if (ButtonConfig.Status.Value != null) selectedColor = ButtonConfig.Status.Value.Value() ?? 0; + if (ButtonConfig.Status.Value != null) selectedColor = ButtonConfig.Status.Value.TryGetValue(out int? value) ? value ?? 0 : 0; StatusContainer.Content = new StatusEditorTypes.Palette() { Colors = (PaletteValue[])output.Palette, SelectedColor = selectedColor, Device = Device, Button = Button, ButtonConfig = ButtonConfig }; break; } diff --git a/MacroPad/Controls/Home/StatusEditorTypes/Palette.axaml.cs b/MacroPad/Controls/Home/StatusEditorTypes/Palette.axaml.cs index 61ee11c..4a548de 100644 --- a/MacroPad/Controls/Home/StatusEditorTypes/Palette.axaml.cs +++ b/MacroPad/Controls/Home/StatusEditorTypes/Palette.axaml.cs @@ -8,7 +8,8 @@ using Avalonia.Media; using MacroPad.Core.Device; using MacroPad.Core.BasePlugin.Device; -using MacroPad.Core.Config; +using MacroPad.Core.Models.Config; +using System.Text.Json.Nodes; namespace MacroPad.Controls.Home.StatusEditorTypes; @@ -28,7 +29,7 @@ public Palette() private void ColorSelector_SelectionChanged(object? sender, SelectionChangedEventArgs e) { - ButtonConfig.Status.Value = ColorSelector.SelectedIndex; + ButtonConfig.Status.Value = JsonValue.Create(ColorSelector.SelectedIndex); if (Button != null) Device.SetButtonContent(Button, ColorSelector.SelectedIndex); } diff --git a/MacroPad/Controls/Settings/DeviceCard.axaml b/MacroPad/Controls/Settings/DeviceCard.axaml deleted file mode 100644 index 99ce38a..0000000 --- a/MacroPad/Controls/Settings/DeviceCard.axaml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - diff --git a/MacroPad/Controls/Settings/DeviceCard.axaml.cs b/MacroPad/Controls/Settings/DeviceCard.axaml.cs deleted file mode 100644 index 5ffb233..0000000 --- a/MacroPad/Controls/Settings/DeviceCard.axaml.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Avalonia; -using Avalonia.Controls; -using MacroPad.Core; -using MacroPad.Core.BasePlugin.Device; -using MacroPad.Core.Device; - -namespace MacroPad.Controls.Settings; - -public partial class DeviceCard : UserControl -{ - public DeviceCore Device { get; set; } = new DeviceCore(new BaseDevice()); - public DeviceCard() - { - InitializeComponent(); - - EnableDevice.IsCheckedChanged += EnableDevice_IsCheckedChanged; - DeviceIcon.ZoomAndPan.CornerRadius = new CornerRadius(4); - } - - protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) - { - DeviceName.Text = Device.Name; - EnableDevice.IsChecked = Device.ProtocolDevice.IsConnected; - DeviceIcon.Device = Device; - } - - private void EnableDevice_IsCheckedChanged(object? sender, Avalonia.Interactivity.RoutedEventArgs e) - { - if (Device != null) { - if (EnableDevice.IsChecked ?? false) Device.Connect(); - else Device.Disconnect(); - DeviceManager.Config.EnabledDevices[Device.ProtocolDevice.Id] = EnableDevice.IsChecked ?? false; - } - } -} \ No newline at end of file diff --git a/MacroPad/Controls/Settings/PluginCard.axaml b/MacroPad/Controls/Settings/PluginCard.axaml deleted file mode 100644 index 1be2578..0000000 --- a/MacroPad/Controls/Settings/PluginCard.axaml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - Plugin features - - - - - - - - - - - diff --git a/MacroPad/Controls/Settings/PluginCard.axaml.cs b/MacroPad/Controls/Settings/PluginCard.axaml.cs deleted file mode 100644 index 8912d81..0000000 --- a/MacroPad/Controls/Settings/PluginCard.axaml.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Avalonia; -using Avalonia.Controls; -using MacroPad.Shared.Plugin; -using MacroPad.Core.BasePlugin; - -namespace MacroPad.Controls.Settings; - -public partial class PluginCard : UserControl -{ - public IPluginInfos Plugin { get; set; } = new BasePluginInfos(); - public PluginCard() - { - InitializeComponent(); - } - - protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) - { - PluginName.Text = Plugin.Name; - PluginDescription.Text = Plugin.Description; - - PluginFeatures.Children.Clear(); - foreach (IProtocol protocol in Plugin.Protocols) - { - PluginFeatures.Children.Add(new PluginFeature() { FeatureName = protocol.Name, FeatureId = protocol.Id }); - } - foreach (INodeCategory nodeCategory in Plugin.NodeCategories) - { - PluginFeatures.Children.Add(new PluginFeature() { FeatureName = nodeCategory.Name, FeatureId = nodeCategory.Id, Disabled = true }); - } - foreach (NodeType nodeType in Plugin.NodeTypes) - { - PluginFeatures.Children.Add(new PluginFeature() { FeatureName = nodeType.Name, Disabled = true }); - } - } -} \ No newline at end of file diff --git a/MacroPad/Controls/Settings/PluginFeature.axaml b/MacroPad/Controls/Settings/PluginFeature.axaml deleted file mode 100644 index 9e0e7d5..0000000 --- a/MacroPad/Controls/Settings/PluginFeature.axaml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - Enable Feature - - - diff --git a/MacroPad/Controls/Settings/PluginFeature.axaml.cs b/MacroPad/Controls/Settings/PluginFeature.axaml.cs deleted file mode 100644 index a5570c5..0000000 --- a/MacroPad/Controls/Settings/PluginFeature.axaml.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Avalonia; -using Avalonia.Controls; -using MacroPad.Core; - -namespace MacroPad.Controls.Settings; - -public partial class PluginFeature : UserControl -{ - public string FeatureName { get; set; } = ""; - public string FeatureId { get; set; } = ""; - public bool Disabled { get; set; } = false; - public PluginFeature() - { - InitializeComponent(); - } - protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) - { - FeatureNameDisplay.Text = FeatureName; - EnableFeature.IsEnabled = !Disabled; - EnableFeature.IsChecked = Disabled || (DeviceManager.Config.PluginsConfig.TryGetValue(FeatureId, out bool value) && value); - - EnableFeature.IsCheckedChanged += EnableFeature_IsCheckedChanged; - } - - private void EnableFeature_IsCheckedChanged(object? sender, Avalonia.Interactivity.RoutedEventArgs e) - { - if (EnableFeature.IsChecked??false) DeviceManager.EnablePluginFeature(FeatureId); - else DeviceManager.DisablePluginFeature(FeatureId); - } -} \ No newline at end of file diff --git a/MacroPad/Controls/Settings/PluginRow.axaml b/MacroPad/Controls/Settings/PluginRow.axaml new file mode 100644 index 0000000..8c4524c --- /dev/null +++ b/MacroPad/Controls/Settings/PluginRow.axaml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MacroPad/Controls/Settings/PluginRow.axaml.cs b/MacroPad/Controls/Settings/PluginRow.axaml.cs new file mode 100644 index 0000000..8f2918e --- /dev/null +++ b/MacroPad/Controls/Settings/PluginRow.axaml.cs @@ -0,0 +1,11 @@ +using Avalonia.Controls; + +namespace MacroPad.Controls.Settings; + +public partial class PluginRow : UserControl +{ + public PluginRow() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/MacroPad/Controls/Settings/PluginRowViewModel.cs b/MacroPad/Controls/Settings/PluginRowViewModel.cs new file mode 100644 index 0000000..0a5edd4 --- /dev/null +++ b/MacroPad/Controls/Settings/PluginRowViewModel.cs @@ -0,0 +1,54 @@ +using Avalonia.Media.Imaging; +using MacroPad.Core; +using MacroPad.Core.BasePlugin; +using MacroPad.Core.Models.Plugin; +using MacroPad.ViewModels; +using ReactiveUI; +using System.IO; + +namespace MacroPad.Controls.Settings +{ + public class PluginRowViewModel : ViewModelBase + { + public PluginInfos Plugin { get; set; } + public PluginRowViewModel(PluginInfos plugin) { + Plugin = plugin; + _isEnabled = plugin.Enabled; + if (_isEnabled) UpdatePluginStates(); + if (plugin.IconType == PluginIconType.Image) Icon = new Bitmap(Path.Combine(Plugin.PluginDirectory!, Plugin.Icon)); + } + public PluginRowViewModel() { Plugin = new PluginInfos() { Name = "Dummy Plugin"}; } + + private void UpdatePluginStates() + { + IsUnloadable = Plugin.IsUnloadable; + IsReloadable = Plugin.IsReloadable; + HasSettings = Plugin.Settings?.Length > 0; + } + + private bool _isEnabled = false; + public bool IsEnabled { get { return _isEnabled; } set { + this.RaiseAndSetIfChanged(ref _isEnabled, value); + if (value) + { + PluginManager.EnablePlugin(Plugin); + UpdatePluginStates(); + } + else PluginManager.DisablePlugin(Plugin); + } } + + public Bitmap? Icon { get; set; } + + private bool _isUnloadable = false; + public bool IsUnloadable { get => _isUnloadable; set => this.RaiseAndSetIfChanged(ref _isUnloadable, value); } + private bool _isReloadable = false; + public bool IsReloadable { get => _isReloadable; set => this.RaiseAndSetIfChanged(ref _isReloadable, value); } + private bool _hasSettings= false; + public bool HasSettings { get => _hasSettings; set => this.RaiseAndSetIfChanged(ref _hasSettings, value); } + + public void Reload() + { + + } + } +} diff --git a/MacroPad/MacroPad.csproj b/MacroPad/MacroPad.csproj index f933f3e..727c461 100644 --- a/MacroPad/MacroPad.csproj +++ b/MacroPad/MacroPad.csproj @@ -36,11 +36,22 @@ - + DeviceViewer.axaml NodeLinksDisplay.axaml + + MainEditor.axaml + + + MainSettings.axaml + + + + + + diff --git a/MacroPad/Pages/MainPage.axaml b/MacroPad/Pages/MainPage.axaml deleted file mode 100644 index 6821297..0000000 --- a/MacroPad/Pages/MainPage.axaml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/MacroPad/Pages/MainPage.axaml.cs b/MacroPad/Pages/MainPage.axaml.cs deleted file mode 100644 index 2405611..0000000 --- a/MacroPad/Pages/MainPage.axaml.cs +++ /dev/null @@ -1,76 +0,0 @@ -using Avalonia; -using Avalonia.Controls; -using FluentAvalonia.Core; -using MacroPad.Controls.Home; -using MacroPad.Core; -using MacroPad.Core.Config; -using MacroPad.Core.Device; -using System.Collections.Generic; -using System.Linq; - -namespace MacroPad.Pages; - -public partial class MainPage : UserControl -{ - public MainPage() - { - InitializeComponent(); - - DeviceManager.DeviceDetected += DeviceManager_DeviceDetected; - DeviceManager.DeviceDisconnected += DeviceManager_DeviceDetected; - DeviceSelector.SelectionChanged += DeviceSelector_SelectionChanged; - RefreshDeviceSelector(); - } - - private void DeviceManager_DeviceDetected(object? sender, Shared.Plugin.Protocol.DeviceDetectedEventArgs e) - { - RefreshDeviceSelector(); - } - - protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) - { - RefreshDeviceSelector(); - } - - private void RefreshDeviceSelector() - { - object? selectedDevice = DeviceSelector.SelectedItem; - DeviceSelector.ItemsSource = DeviceManager.ConnectedDevices.FindAll(x => x.ProtocolDevice.IsConnected); - if (DeviceSelector.ItemsSource.Count() > 0) - { - if (DeviceSelector.Items.Contains(selectedDevice)) DeviceSelector.SelectedItem = selectedDevice; - else DeviceSelector.SelectedIndex = 0; - } - } - - private void DeviceSelector_SelectionChanged(object? sender, SelectionChangedEventArgs e) - { - DeviceViewerContainer.Children.Clear(); - DeviceProfileSelectorContainer.Children.Clear(); - NodesEditorContainer.Children.Clear(); - ButtonStatusEditorContainer.Children.Clear(); - if (DeviceSelector.SelectedItem is DeviceCore selectedDevice) - { - DeviceManager.SelectedDevice = selectedDevice; - var deviceViewer = new DeviceViewer() { Device = selectedDevice }; - deviceViewer.ButtonPressed += DeviceViewer_ButtonPressed; - DeviceViewerContainer.Children.Add(deviceViewer); - DeviceProfileSelectorContainer.Children.Add(new DeviceProfileSelector() { Device = selectedDevice, DeviceViewer=deviceViewer, MainPage=this }); - } - } - - private void DeviceViewer_ButtonPressed(object? sender, DeviceViewerButtonPressedEventArgs e) - { - NodesEditorContainer.Children.Clear(); - ButtonStatusEditorContainer.Children.Clear(); - if (DeviceSelector.SelectedItem is DeviceCore selectedDevice && selectedDevice.CurrentProfile != null && e.Button != null) - { - if (!selectedDevice.CurrentProfile.ButtonsConfig.ContainsKey(e.Button.X)) selectedDevice.CurrentProfile.ButtonsConfig.Add(e.Button.X, []); - Dictionary buttonColumn = selectedDevice.CurrentProfile.ButtonsConfig[e.Button.X]; - if (!buttonColumn.ContainsKey(e.Button.Y)) buttonColumn.Add(e.Button.Y, new ButtonConfig()); - ButtonConfig buttonConfig = buttonColumn[e.Button.Y]; - NodesEditorContainer.Children.Add(new NodesEditor() { ButtonConfig = buttonConfig, Button=e.Button, Device = selectedDevice }); - ButtonStatusEditorContainer.Children.Add(new StatusEditor() { ButtonConfig = buttonConfig, Button = e.Button, Device = selectedDevice}); - } - } -} \ No newline at end of file diff --git a/MacroPad/Pages/Settings/Devices.axaml b/MacroPad/Pages/Settings/Devices.axaml deleted file mode 100644 index b83efad..0000000 --- a/MacroPad/Pages/Settings/Devices.axaml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - diff --git a/MacroPad/Pages/Settings/Devices.axaml.cs b/MacroPad/Pages/Settings/Devices.axaml.cs deleted file mode 100644 index 1c3e1c3..0000000 --- a/MacroPad/Pages/Settings/Devices.axaml.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Avalonia; -using Avalonia.Controls; -using MacroPad.Controls.Settings; -using MacroPad.Core; -using MacroPad.Core.Device; - -namespace MacroPad.Pages.Settings; - -public partial class Devices : UserControl -{ - public Devices() - { - InitializeComponent(); - } - - protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) - { - DevicesList.Children.Clear(); - foreach (DeviceCore device in DeviceManager.ConnectedDevices) - { - DevicesList.Children.Add(new DeviceCard() { Device = device, Margin = new Thickness(4), Width=400, Height=136}); - } - } -} \ No newline at end of file diff --git a/MacroPad/Pages/Settings/General.axaml b/MacroPad/Pages/Settings/General.axaml deleted file mode 100644 index 4896d9f..0000000 --- a/MacroPad/Pages/Settings/General.axaml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - Run on system startup - - - - - - - Minimize on startup - - - - - - - diff --git a/MacroPad/Pages/Settings/General.axaml.cs b/MacroPad/Pages/Settings/General.axaml.cs deleted file mode 100644 index 3f5ee3b..0000000 --- a/MacroPad/Pages/Settings/General.axaml.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Avalonia.Controls; -using MacroPad.Models; -using System; -using System.Runtime.Versioning; - -namespace MacroPad.Pages.Settings; - -public partial class General : UserControl -{ - public General() - { - InitializeComponent(); - if (OperatingSystem.IsWindows()) - { - WindowsUtils.StartupStatus status = WindowsUtils.GetAppStartupStatus(); - - RunStartup.IsChecked = status.OnStartup; - RunStartup.IsCheckedChanged += UpdateStartupSettings_Windows; - RunMinimized.IsChecked = status.Minimized; - RunMinimized.IsCheckedChanged += UpdateStartupSettings_Windows; - } - } - - [SupportedOSPlatform("windows")] - private void UpdateStartupSettings_Windows(object? sender, Avalonia.Interactivity.RoutedEventArgs e) - { - WindowsUtils.SetAppRunOnLaunch(RunStartup.IsChecked??false, RunMinimized.IsChecked??false); - } -} \ No newline at end of file diff --git a/MacroPad/Pages/Settings/Plugins.axaml b/MacroPad/Pages/Settings/Plugins.axaml deleted file mode 100644 index d0cd800..0000000 --- a/MacroPad/Pages/Settings/Plugins.axaml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - diff --git a/MacroPad/Pages/Settings/Plugins.axaml.cs b/MacroPad/Pages/Settings/Plugins.axaml.cs deleted file mode 100644 index 52794d5..0000000 --- a/MacroPad/Pages/Settings/Plugins.axaml.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Avalonia; -using Avalonia.Controls; -using MacroPad.Controls.Settings; -using MacroPad.Core; -using MacroPad.Shared.Plugin; -using MacroPad.ViewModels; - -namespace MacroPad.Pages.Settings; - -public partial class Plugins : UserControl -{ - public Plugins() - { - DataContext = new GeneralViewModel(); - InitializeComponent(); - } - - protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) - { - PluginsList.Children.Clear(); - foreach (IPluginInfos plugin in PluginLoader.plugins) - { - PluginsList.Children.Add(new PluginCard() { Plugin=plugin, HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Stretch}); - } - } -} \ No newline at end of file diff --git a/MacroPad/Pages/SettingsPage.axaml b/MacroPad/Pages/SettingsPage.axaml deleted file mode 100644 index 2b939ab..0000000 --- a/MacroPad/Pages/SettingsPage.axaml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/MacroPad/Pages/SettingsPage.axaml.cs b/MacroPad/Pages/SettingsPage.axaml.cs deleted file mode 100644 index 9f6ff4b..0000000 --- a/MacroPad/Pages/SettingsPage.axaml.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Avalonia.Controls; - -namespace MacroPad.Pages; - -public partial class SettingsPage : UserControl -{ - public SettingsPage() - { - InitializeComponent(); - } -} \ No newline at end of file diff --git a/MacroPad/Styles/MacroPad.axaml b/MacroPad/Styles/MacroPad.axaml index c25bbc0..01a8ade 100644 --- a/MacroPad/Styles/MacroPad.axaml +++ b/MacroPad/Styles/MacroPad.axaml @@ -4,9 +4,9 @@ - + - Coucou + FooBar @@ -56,6 +56,13 @@ + + + + + + + + + + + + + + + + diff --git a/MacroPad/Views/MainSettings.axaml.cs b/MacroPad/Views/MainSettings.axaml.cs new file mode 100644 index 0000000..6ffac63 --- /dev/null +++ b/MacroPad/Views/MainSettings.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +namespace MacroPad.Views; + +public partial class MainSettings : UserControl +{ + public MainSettings() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/MacroPad/Views/MainSettingsViewModel.cs b/MacroPad/Views/MainSettingsViewModel.cs new file mode 100644 index 0000000..14df743 --- /dev/null +++ b/MacroPad/Views/MainSettingsViewModel.cs @@ -0,0 +1,26 @@ +using Avalonia; +using Avalonia.Controls; +using MacroPad.ViewModels; +using MacroPad.Views.Settings; +using ReactiveUI; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MacroPad.Views +{ + public class MainSettingsViewModel : ViewModelBase + { + private bool _openend = false; + public bool Openend { get => _openend; set => this.RaiseAndSetIfChanged(ref _openend, value); } + private int _selectedCat = 0; + public int SelectedCat { get => _selectedCat; set => this.RaiseAndSetIfChanged(ref _selectedCat, value); } + + public void Open() => Openend = true; + public void Close() => Openend = false; + + public static StyledElement[] SettingsCategories { get; } = [new General() { DataContext = new GeneralViewModel()}, new Plugins() { DataContext = new PluginsViewModel()}]; + } +} diff --git a/MacroPad/Views/MainView.axaml b/MacroPad/Views/MainView.axaml deleted file mode 100644 index 6a0d25c..0000000 --- a/MacroPad/Views/MainView.axaml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/MacroPad/Views/MainView.axaml.cs b/MacroPad/Views/MainView.axaml.cs deleted file mode 100644 index 129691c..0000000 --- a/MacroPad/Views/MainView.axaml.cs +++ /dev/null @@ -1,133 +0,0 @@ -using Avalonia; -using Avalonia.Controls; -using FluentAvalonia.Core; -using FluentAvalonia.UI.Controls; -using FluentAvalonia.UI.Navigation; -using MacroPad.Pages; -using MacroPad.ViewModels; -using System.Collections.Generic; -using System.Linq; - -namespace MacroPad.Views -{ - public partial class MainView : UserControl - { - public static MainView? Instance { get; private set; } - public MainView() - { - Instance = this; - InitializeComponent(); - } - - - protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) - { - base.OnAttachedToVisualTree(e); - - var vm = new MainViewViewModel(); - DataContext = vm; - FrameView.NavigationPageFactory = vm.NavigationFactory; - - FrameView.Navigated += OnFrameViewNavigated; - NavView.ItemInvoked += OnNavigationViewItemInvoked; - - NavView.MenuItemsSource = GetNavigationViewItems(); - NavView.FooterMenuItemsSource = GetFooterNavigationViewItems(); - - NavView.IsPaneOpen = false; - - var navViewItems = NavView.MenuItemsSource.Cast(); - FrameView.NavigateFromObject(navViewItems.ElementAt(0).Tag); - - } - - private void SetNviIcon(NavigationViewItem? item, bool selected) - { - // Technically, yes you could set up binding and converters and whatnot to let the icon change - // between filled and unfilled based on selection, but this is so much simpler - - if (item == null) - return; - - var t = item.Tag; - - item.IconSource = t switch - { - MainPage => this.TryFindResource(selected ? "HomeIconFilled" : "HomeIcon", out var value) - ? (IconSource)value! - : null, - SettingsPage => this.TryFindResource(selected ? "SettingsIconFilled" : "SettingsIcon", out var value) - ? (IconSource)value! - : null, - _ => item.IconSource - }; - } - - private void OnFrameViewNavigated(object sender, NavigationEventArgs e) - { - var page = e.Content as Control; - - foreach (NavigationViewItem nvi in NavView.MenuItemsSource) - { - if (nvi.Tag != null && nvi.Tag.Equals(page)) - { - NavView.SelectedItem = nvi; - SetNviIcon(nvi, true); - } - else - { - SetNviIcon(nvi, false); - } - } - - foreach (NavigationViewItem nvi in NavView.FooterMenuItemsSource) - { - if (nvi.Tag != null && nvi.Tag.Equals(page)) - { - NavView.SelectedItem = nvi; - SetNviIcon(nvi, true); - } - else - { - SetNviIcon(nvi, false); - } - } - } - - private IEnumerable GetNavigationViewItems() - { - return - [ - new() - { - Content = "Home", - Tag = NavigationFactory.GetPages()[0], - IconSource = (IconSource)this.FindResource("HomeIcon")!, - Classes = { "MacroPadAppNav" } - } - ]; - } - - private IEnumerable GetFooterNavigationViewItems() - { - return - [ - new() - { - Content = "Settings", - Tag = NavigationFactory.GetPages()[1], - IconSource = (IconSource)this.FindResource("SettingsIcon")!, - Classes = { "MacroPadAppNav" } - } - ]; - } - - private void OnNavigationViewItemInvoked(object? sender, NavigationViewItemInvokedEventArgs e) - { - if (e.InvokedItemContainer is NavigationViewItem { Tag: Control c }) - { - _ = FrameView.NavigateFromObject(c); - } - } - } -} diff --git a/MacroPad/Views/MainWindow.axaml b/MacroPad/Views/MainWindow.axaml index 8c4ea0a..a612918 100644 --- a/MacroPad/Views/MainWindow.axaml +++ b/MacroPad/Views/MainWindow.axaml @@ -2,19 +2,64 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:vm="using:MacroPad.ViewModels" xmlns:views="using:MacroPad.Views" + xmlns:nav="using:MacroPad.Views.Navigation" + xmlns:ui="using:FluentAvalonia.UI.Controls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="MacroPad.Views.MainWindow" - x:DataType="vm:MainWindowViewModel" + x:DataType="views:MainWindowViewModel" Icon="/Assets/avalonia-logo.ico" Title="MacroPad"> - + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Select a device first + + + + + + diff --git a/MacroPad/Views/MainWindow.axaml.cs b/MacroPad/Views/MainWindow.axaml.cs index 68b1939..90fb9f6 100644 --- a/MacroPad/Views/MainWindow.axaml.cs +++ b/MacroPad/Views/MainWindow.axaml.cs @@ -1,5 +1,7 @@ using Avalonia.Controls; using MacroPad.Core; +using MacroPad.Views.Navigation; +using System.Diagnostics; namespace MacroPad.Views { @@ -21,5 +23,15 @@ private void MainWindow_Closing(object? sender, WindowClosingEventArgs e) Hide(); } } + + private void ListBox_SelectionChanged(object? sender, SelectionChangedEventArgs e) + { + MainEditorViewModel? editor = (DataContext as MainWindowViewModel)?.GetDeviceEditor(((sender as ListBox)?.SelectedItem as DeviceNavViewModel)?.Device); + + if (editor == null) return; + + if (deviceEditorView.Child?.GetType() != typeof(MainEditor)) deviceEditorView.Child = new MainEditor() { DataContext = editor}; + else deviceEditorView.Child.DataContext = editor; + } } } \ No newline at end of file diff --git a/MacroPad/Views/MainWindowViewModel.cs b/MacroPad/Views/MainWindowViewModel.cs new file mode 100644 index 0000000..0bd9de2 --- /dev/null +++ b/MacroPad/Views/MainWindowViewModel.cs @@ -0,0 +1,46 @@ +using DynamicData; +using MacroPad.Core; +using MacroPad.Core.Device; +using MacroPad.ViewModels; +using MacroPad.Views.Navigation; +using ReactiveUI; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Linq; + +namespace MacroPad.Views +{ + public class MainWindowViewModel : ViewModelBase + { + private readonly Dictionary _deviceEditors = []; + + public MainSettingsViewModel Settings { get; } = new(); + public MainWindowViewModel() { + DeviceManager.DeviceDetected += DeviceManager_DeviceDetected; + DeviceManager.DeviceDisconnected += DeviceManager_DeviceDisconnected; + DevicesInNav.AddRange(DeviceManager.ConnectedDevices.Select(x => new DeviceNavViewModel(x))); + } + + private void DeviceManager_DeviceDisconnected(object? sender, Shared.Plugin.Protocol.DeviceDetectedEventArgs e) + { + DevicesInNav.Remove(DevicesInNav.First(x=>x.Device.ProtocolDevice == e.Device)); + } + + private void DeviceManager_DeviceDetected(object? sender, Shared.Plugin.Protocol.DeviceDetectedEventArgs e) + { + DevicesInNav.Add(new DeviceNavViewModel(DeviceManager.ConnectedDevices.First(x => x.ProtocolDevice == e.Device))); + } + + public ObservableCollection DevicesInNav { get; set; } = []; + + public MainEditorViewModel? GetDeviceEditor(DeviceCore? device) + { + if (device == null) return null; + if (_deviceEditors.TryGetValue(device, out var editor)) return editor; + editor = new MainEditorViewModel(device); + _deviceEditors.Add(device, editor); + return editor; + } + } +} diff --git a/MacroPad/Views/Navigation/DeviceNav.axaml b/MacroPad/Views/Navigation/DeviceNav.axaml new file mode 100644 index 0000000..26c9f85 --- /dev/null +++ b/MacroPad/Views/Navigation/DeviceNav.axaml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/MacroPad/Views/Navigation/DeviceNav.axaml.cs b/MacroPad/Views/Navigation/DeviceNav.axaml.cs new file mode 100644 index 0000000..7b3e7aa --- /dev/null +++ b/MacroPad/Views/Navigation/DeviceNav.axaml.cs @@ -0,0 +1,15 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Media; +using Avalonia.Markup.Xaml; + +namespace MacroPad.Views.Navigation; + +public partial class DeviceNav : UserControl +{ + public DeviceNav() + { + InitializeComponent(); + deviceViewer.ZoomAndPan.Background = Brushes.Transparent; + } +} \ No newline at end of file diff --git a/MacroPad/Views/Navigation/DeviceNavViewModel.cs b/MacroPad/Views/Navigation/DeviceNavViewModel.cs new file mode 100644 index 0000000..17bf05c --- /dev/null +++ b/MacroPad/Views/Navigation/DeviceNavViewModel.cs @@ -0,0 +1,24 @@ +using MacroPad.Core; +using MacroPad.Core.Device; +using MacroPad.ViewModels; +using ReactiveUI; + +namespace MacroPad.Views.Navigation +{ + public class DeviceNavViewModel(DeviceCore device) : ViewModelBase + { + public DeviceCore Device { get; set; } = device; + + private bool _enabled = device.ProtocolDevice.IsConnected; + public bool Enabled + { + get => _enabled; + set + { + this.RaiseAndSetIfChanged(ref _enabled, value); + if (value) DeviceManager.EnableDevice(Device); + else Device.Disconnect(); + } + } + } +} diff --git a/MacroPad/Views/Settings/General.axaml b/MacroPad/Views/Settings/General.axaml new file mode 100644 index 0000000..75d0029 --- /dev/null +++ b/MacroPad/Views/Settings/General.axaml @@ -0,0 +1,28 @@ + + + + + + + + Run on system startup + + + + + + + Minimize on startup + + + + + + diff --git a/MacroPad/Views/Settings/General.axaml.cs b/MacroPad/Views/Settings/General.axaml.cs new file mode 100644 index 0000000..d32a441 --- /dev/null +++ b/MacroPad/Views/Settings/General.axaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls; + +namespace MacroPad.Views.Settings; + +public partial class General : UserControl +{ + public General() + { + InitializeComponent(); + } + public override string ToString() => "General"; +} \ No newline at end of file diff --git a/MacroPad/Views/Settings/GeneralViewModel.cs b/MacroPad/Views/Settings/GeneralViewModel.cs new file mode 100644 index 0000000..c3c5756 --- /dev/null +++ b/MacroPad/Views/Settings/GeneralViewModel.cs @@ -0,0 +1,34 @@ +using MacroPad.Utilities; +using MacroPad.ViewModels; +using ReactiveUI; +using System; + +namespace MacroPad.Views.Settings +{ + public class GeneralViewModel : ViewModelBase + { + private bool _runStartup = false; + public bool RunStartup { get => _runStartup; set => this.RaiseAndSetIfChanged(ref _runStartup, value); } + private bool _runMin = false; + public bool RunMin { get => _runMin; set => this.RaiseAndSetIfChanged(ref _runMin, value); } + public GeneralViewModel() + { + if (OperatingSystem.IsWindows()) + { + WindowsUtils.StartupStatus status = WindowsUtils.GetAppStartupStatus(); + + RunStartup = status.OnStartup; + RunMin = status.Minimized; + } + PropertyChanged += GeneralViewModel_PropertyChanged; + } + + private void GeneralViewModel_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e) + { + if (OperatingSystem.IsWindows()) + { + WindowsUtils.SetAppRunOnLaunch(RunStartup, RunMin); + } + } + } +} diff --git a/MacroPad/Views/Settings/Plugins.axaml b/MacroPad/Views/Settings/Plugins.axaml new file mode 100644 index 0000000..fb50702 --- /dev/null +++ b/MacroPad/Views/Settings/Plugins.axaml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + diff --git a/MacroPad/Views/Settings/Plugins.axaml.cs b/MacroPad/Views/Settings/Plugins.axaml.cs new file mode 100644 index 0000000..d37506a --- /dev/null +++ b/MacroPad/Views/Settings/Plugins.axaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls; + +namespace MacroPad.Views.Settings; + +public partial class Plugins : UserControl +{ + public Plugins() + { + InitializeComponent(); + } + public override string ToString() => "Plugins"; +} \ No newline at end of file diff --git a/MacroPad/Views/Settings/PluginsViewModel.cs b/MacroPad/Views/Settings/PluginsViewModel.cs new file mode 100644 index 0000000..af61d9e --- /dev/null +++ b/MacroPad/Views/Settings/PluginsViewModel.cs @@ -0,0 +1,17 @@ +using MacroPad.Controls.Settings; +using MacroPad.Core; +using System.Collections.ObjectModel; +using System.Linq; + +namespace MacroPad.Views.Settings +{ + public class PluginsViewModel + { + public ObservableCollection Plugins { get; set; } + public PluginsViewModel() { + Plugins = [.. PluginManager.Plugins.Select(x => new PluginRowViewModel(x))]; + PluginManager.PluginAdded += (s, e) => Plugins.Add(new PluginRowViewModel(e)); + PluginManager.PluginRemoved += (s, e) => Plugins.Remove(Plugins.First(x => x.Plugin == e)); + } + } +}