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

Updated ReactiveUI

Added order to render element ctors
This commit is contained in:
Robert 2021-12-30 23:33:26 +01:00
parent 6569558df4
commit 34757716aa
46 changed files with 838 additions and 370 deletions

View File

@ -22,7 +22,8 @@ namespace Artemis.Core
/// </summary> /// </summary>
/// <param name="parent">The parent of the folder</param> /// <param name="parent">The parent of the folder</param>
/// <param name="name">The name of the folder</param> /// <param name="name">The name of the folder</param>
public Folder(ProfileElement parent, string name) : base(parent.Profile) /// <param name="order">The order where to place the child (0-based), defaults to the end of the collection</param>
public Folder(ProfileElement parent, string name, int order) : base(parent.Profile)
{ {
FolderEntity = new FolderEntity(); FolderEntity = new FolderEntity();
EntityId = Guid.NewGuid(); EntityId = Guid.NewGuid();
@ -31,7 +32,7 @@ namespace Artemis.Core
Profile = Parent.Profile; Profile = Parent.Profile;
Name = name; Name = name;
Parent.AddChild(this); Parent.AddChild(this, order);
} }
/// <summary> /// <summary>

View File

@ -29,7 +29,8 @@ namespace Artemis.Core
/// </summary> /// </summary>
/// <param name="parent">The parent of the layer</param> /// <param name="parent">The parent of the layer</param>
/// <param name="name">The name of the layer</param> /// <param name="name">The name of the layer</param>
public Layer(ProfileElement parent, string name) : base(parent.Profile) /// <param name="order">The order where to place the child (0-based), defaults to the end of the collection</param>
public Layer(ProfileElement parent, string name, int order) : base(parent.Profile)
{ {
LayerEntity = new LayerEntity(); LayerEntity = new LayerEntity();
EntityId = Guid.NewGuid(); EntityId = Guid.NewGuid();
@ -47,7 +48,7 @@ namespace Artemis.Core
Adapter = new LayerAdapter(this); Adapter = new LayerAdapter(this);
Initialize(); Initialize();
Parent.AddChild(this, 0); Parent.AddChild(this, order);
} }
/// <summary> /// <summary>

View File

@ -198,7 +198,7 @@ namespace Artemis.Core
FolderEntity? rootFolder = ProfileEntity.Folders.FirstOrDefault(f => f.ParentId == EntityId); FolderEntity? rootFolder = ProfileEntity.Folders.FirstOrDefault(f => f.ParentId == EntityId);
if (rootFolder == null) if (rootFolder == null)
{ {
Folder _ = new(this, "Root folder"); Folder _ = new(this, "Root folder", 0);
} }
else else
{ {

View File

@ -44,6 +44,9 @@ namespace Artemis.Core
/// <param name="extraArgs">A list of extra arguments to pass to Artemis when restarting</param> /// <param name="extraArgs">A list of extra arguments to pass to Artemis when restarting</param>
public static void Restart(bool elevate, TimeSpan delay, params string[] extraArgs) public static void Restart(bool elevate, TimeSpan delay, params string[] extraArgs)
{ {
if (!OperatingSystem.IsWindows() && elevate)
throw new ArtemisCoreException("Elevation on non-Windows platforms is not supported.");
OnRestartRequested(new RestartEventArgs(elevate, delay, extraArgs.ToList())); OnRestartRequested(new RestartEventArgs(elevate, delay, extraArgs.ToList()));
} }

View File

@ -137,7 +137,7 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
if (!SupportsChildren) if (!SupportsChildren)
throw new ArtemisUIException("Cannot add a folder to a profile element of type " + ProfileElement.GetType().Name); throw new ArtemisUIException("Cannot add a folder to a profile element of type " + ProfileElement.GetType().Name);
Folder _ = new(ProfileElement, "New folder"); Folder _ = new(ProfileElement, "New folder", 0);
_profileEditorService.SaveSelectedProfileConfiguration(); _profileEditorService.SaveSelectedProfileConfiguration();
} }
@ -146,7 +146,7 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
if (!SupportsChildren) if (!SupportsChildren)
throw new ArtemisUIException("Cannot add a layer to a profile element of type " + ProfileElement.GetType().Name); throw new ArtemisUIException("Cannot add a layer to a profile element of type " + ProfileElement.GetType().Name);
Layer layer = new(ProfileElement, "New layer"); Layer layer = new(ProfileElement, "New layer", 0);
// Could be null if the default brush got disabled // Could be null if the default brush got disabled
LayerBrushDescriptor brush = _layerBrushService.GetDefaultLayerBrush(); LayerBrushDescriptor brush = _layerBrushService.GetDefaultLayerBrush();

View File

@ -14,6 +14,7 @@
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.--> <!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="0.10.11" /> <PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="0.10.11" />
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.11" /> <PackageReference Include="Avalonia.ReactiveUI" Version="0.10.11" />
<PackageReference Include="ReactiveUI" Version="17.1.9" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\Artemis.Core\Artemis.Core.csproj" /> <ProjectReference Include="..\..\Artemis.Core\Artemis.Core.csproj" />

View File

@ -53,6 +53,20 @@
"System.Reactive": "5.0.0" "System.Reactive": "5.0.0"
} }
}, },
"ReactiveUI": {
"type": "Direct",
"requested": "[17.1.9, )",
"resolved": "17.1.9",
"contentHash": "bxH6uzEi1b6cfGoBBvCXWrdT18+Rleggi0R/vOaCYc+zvlSleJW6wlUtcWjrKzgQHD8EN3c02A4JBTt9oGSWWQ==",
"dependencies": {
"DynamicData": "7.4.3",
"Splat": "14.1.1",
"System.ComponentModel": "4.3.0",
"System.Diagnostics.Contracts": "4.3.0",
"System.Dynamic.Runtime": "4.3.0",
"System.Runtime.Serialization.Primitives": "4.3.0"
}
},
"Avalonia.Angle.Windows.Natives": { "Avalonia.Angle.Windows.Natives": {
"type": "Transitive", "type": "Transitive",
"resolved": "2.1.0.2020091801", "resolved": "2.1.0.2020091801",
@ -191,8 +205,8 @@
}, },
"DynamicData": { "DynamicData": {
"type": "Transitive", "type": "Transitive",
"resolved": "7.3.1", "resolved": "7.4.3",
"contentHash": "E9oTvWlAgzct0MuWt6k+0s+nSDA3LkFVvDwkMUTklIZZnva314KZAEF2vG4XX9I98ia+EpMqjte67jWEBJlsRw==", "contentHash": "7eGyREbtzyaRutMa+iToi2e41JboEVK9c1ZBcTvJOfEoTRIZX3hChIsxIvV0ErzMXtGHAIS2O0I8jLDUIds5wg==",
"dependencies": { "dependencies": {
"System.Reactive": "5.0.0" "System.Reactive": "5.0.0"
} }
@ -522,15 +536,6 @@
"Ninject": "3.3.3" "Ninject": "3.3.3"
} }
}, },
"ReactiveUI": {
"type": "Transitive",
"resolved": "16.2.6",
"contentHash": "jf1RvD8HxHuA6CGQtheGHUCHzRrhpvo0z593Npsz7g8KJWXfGR45Dc9bILJHoymBxhdDD1L1WjUfh0fcucIPPg==",
"dependencies": {
"DynamicData": "7.3.1",
"Splat": "13.1.1"
}
},
"ReactiveUI.Validation": { "ReactiveUI.Validation": {
"type": "Transitive", "type": "Transitive",
"resolved": "2.2.1", "resolved": "2.2.1",
@ -918,6 +923,14 @@
"System.Text.Encoding": "4.3.0" "System.Text.Encoding": "4.3.0"
} }
}, },
"System.Diagnostics.Contracts": {
"type": "Transitive",
"resolved": "4.3.0",
"contentHash": "eelRRbnm+OloiQvp9CXS0ixjNQldjjkHO4iIkR5XH2VIP8sUB/SIpa1TdUW6/+HDcQ+MlhP3pNa1u5SbzYuWGA==",
"dependencies": {
"System.Runtime": "4.3.0"
}
},
"System.Diagnostics.Debug": { "System.Diagnostics.Debug": {
"type": "Transitive", "type": "Transitive",
"resolved": "4.3.0", "resolved": "4.3.0",
@ -1398,6 +1411,15 @@
"System.Runtime.Extensions": "4.3.0" "System.Runtime.Extensions": "4.3.0"
} }
}, },
"System.Runtime.Serialization.Primitives": {
"type": "Transitive",
"resolved": "4.3.0",
"contentHash": "Wz+0KOukJGAlXjtKr+5Xpuxf8+c8739RI1C+A2BoQZT+wMCCoMDDdO8/4IRHfaVINqL78GO8dW8G2lW/e45Mcw==",
"dependencies": {
"System.Resources.ResourceManager": "4.3.0",
"System.Runtime": "4.3.0"
}
},
"System.Security.AccessControl": { "System.Security.AccessControl": {
"type": "Transitive", "type": "Transitive",
"resolved": "5.0.0", "resolved": "5.0.0",
@ -1757,6 +1779,7 @@
"Material.Icons.Avalonia": "1.0.2", "Material.Icons.Avalonia": "1.0.2",
"RGB.NET.Core": "1.0.0-prerelease1", "RGB.NET.Core": "1.0.0-prerelease1",
"RGB.NET.Layout": "1.0.0-prerelease1", "RGB.NET.Layout": "1.0.0-prerelease1",
"ReactiveUI": "17.1.9",
"ReactiveUI.Validation": "2.2.1", "ReactiveUI.Validation": "2.2.1",
"Splat.Ninject": "14.1.17" "Splat.Ninject": "14.1.17"
} }
@ -1774,6 +1797,7 @@
"FluentAvaloniaUI": "1.1.8", "FluentAvaloniaUI": "1.1.8",
"Material.Icons.Avalonia": "1.0.2", "Material.Icons.Avalonia": "1.0.2",
"RGB.NET.Core": "1.0.0-prerelease1", "RGB.NET.Core": "1.0.0-prerelease1",
"ReactiveUI": "17.1.9",
"ReactiveUI.Validation": "2.2.1" "ReactiveUI.Validation": "2.2.1"
} }
} }

View File

@ -14,6 +14,7 @@
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.--> <!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="0.10.11" /> <PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="0.10.11" />
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.11" /> <PackageReference Include="Avalonia.ReactiveUI" Version="0.10.11" />
<PackageReference Include="ReactiveUI" Version="17.1.9" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\Artemis.Core\Artemis.Core.csproj" /> <ProjectReference Include="..\..\Artemis.Core\Artemis.Core.csproj" />

View File

@ -53,6 +53,20 @@
"System.Reactive": "5.0.0" "System.Reactive": "5.0.0"
} }
}, },
"ReactiveUI": {
"type": "Direct",
"requested": "[17.1.9, )",
"resolved": "17.1.9",
"contentHash": "bxH6uzEi1b6cfGoBBvCXWrdT18+Rleggi0R/vOaCYc+zvlSleJW6wlUtcWjrKzgQHD8EN3c02A4JBTt9oGSWWQ==",
"dependencies": {
"DynamicData": "7.4.3",
"Splat": "14.1.1",
"System.ComponentModel": "4.3.0",
"System.Diagnostics.Contracts": "4.3.0",
"System.Dynamic.Runtime": "4.3.0",
"System.Runtime.Serialization.Primitives": "4.3.0"
}
},
"Avalonia.Angle.Windows.Natives": { "Avalonia.Angle.Windows.Natives": {
"type": "Transitive", "type": "Transitive",
"resolved": "2.1.0.2020091801", "resolved": "2.1.0.2020091801",
@ -191,8 +205,8 @@
}, },
"DynamicData": { "DynamicData": {
"type": "Transitive", "type": "Transitive",
"resolved": "7.3.1", "resolved": "7.4.3",
"contentHash": "E9oTvWlAgzct0MuWt6k+0s+nSDA3LkFVvDwkMUTklIZZnva314KZAEF2vG4XX9I98ia+EpMqjte67jWEBJlsRw==", "contentHash": "7eGyREbtzyaRutMa+iToi2e41JboEVK9c1ZBcTvJOfEoTRIZX3hChIsxIvV0ErzMXtGHAIS2O0I8jLDUIds5wg==",
"dependencies": { "dependencies": {
"System.Reactive": "5.0.0" "System.Reactive": "5.0.0"
} }
@ -522,15 +536,6 @@
"Ninject": "3.3.3" "Ninject": "3.3.3"
} }
}, },
"ReactiveUI": {
"type": "Transitive",
"resolved": "16.2.6",
"contentHash": "jf1RvD8HxHuA6CGQtheGHUCHzRrhpvo0z593Npsz7g8KJWXfGR45Dc9bILJHoymBxhdDD1L1WjUfh0fcucIPPg==",
"dependencies": {
"DynamicData": "7.3.1",
"Splat": "13.1.1"
}
},
"ReactiveUI.Validation": { "ReactiveUI.Validation": {
"type": "Transitive", "type": "Transitive",
"resolved": "2.2.1", "resolved": "2.2.1",
@ -918,6 +923,14 @@
"System.Text.Encoding": "4.3.0" "System.Text.Encoding": "4.3.0"
} }
}, },
"System.Diagnostics.Contracts": {
"type": "Transitive",
"resolved": "4.3.0",
"contentHash": "eelRRbnm+OloiQvp9CXS0ixjNQldjjkHO4iIkR5XH2VIP8sUB/SIpa1TdUW6/+HDcQ+MlhP3pNa1u5SbzYuWGA==",
"dependencies": {
"System.Runtime": "4.3.0"
}
},
"System.Diagnostics.Debug": { "System.Diagnostics.Debug": {
"type": "Transitive", "type": "Transitive",
"resolved": "4.3.0", "resolved": "4.3.0",
@ -1398,6 +1411,15 @@
"System.Runtime.Extensions": "4.3.0" "System.Runtime.Extensions": "4.3.0"
} }
}, },
"System.Runtime.Serialization.Primitives": {
"type": "Transitive",
"resolved": "4.3.0",
"contentHash": "Wz+0KOukJGAlXjtKr+5Xpuxf8+c8739RI1C+A2BoQZT+wMCCoMDDdO8/4IRHfaVINqL78GO8dW8G2lW/e45Mcw==",
"dependencies": {
"System.Resources.ResourceManager": "4.3.0",
"System.Runtime": "4.3.0"
}
},
"System.Security.AccessControl": { "System.Security.AccessControl": {
"type": "Transitive", "type": "Transitive",
"resolved": "5.0.0", "resolved": "5.0.0",
@ -1757,6 +1779,7 @@
"Material.Icons.Avalonia": "1.0.2", "Material.Icons.Avalonia": "1.0.2",
"RGB.NET.Core": "1.0.0-prerelease1", "RGB.NET.Core": "1.0.0-prerelease1",
"RGB.NET.Layout": "1.0.0-prerelease1", "RGB.NET.Layout": "1.0.0-prerelease1",
"ReactiveUI": "17.1.9",
"ReactiveUI.Validation": "2.2.1", "ReactiveUI.Validation": "2.2.1",
"Splat.Ninject": "14.1.17" "Splat.Ninject": "14.1.17"
} }
@ -1774,6 +1797,7 @@
"FluentAvaloniaUI": "1.1.8", "FluentAvaloniaUI": "1.1.8",
"Material.Icons.Avalonia": "1.0.2", "Material.Icons.Avalonia": "1.0.2",
"RGB.NET.Core": "1.0.0-prerelease1", "RGB.NET.Core": "1.0.0-prerelease1",
"ReactiveUI": "17.1.9",
"ReactiveUI.Validation": "2.2.1" "ReactiveUI.Validation": "2.2.1"
} }
} }

View File

@ -24,6 +24,7 @@
<PackageReference Include="Avalonia.Xaml.Interactivity" Version="0.10.11.5" /> <PackageReference Include="Avalonia.Xaml.Interactivity" Version="0.10.11.5" />
<PackageReference Include="FluentAvaloniaUI" Version="1.1.8" /> <PackageReference Include="FluentAvaloniaUI" Version="1.1.8" />
<PackageReference Include="Material.Icons.Avalonia" Version="1.0.2" /> <PackageReference Include="Material.Icons.Avalonia" Version="1.0.2" />
<PackageReference Include="ReactiveUI" Version="17.1.9" />
<PackageReference Include="ReactiveUI.Validation" Version="2.2.1" /> <PackageReference Include="ReactiveUI.Validation" Version="2.2.1" />
<PackageReference Include="RGB.NET.Core" Version="1.0.0-prerelease1" /> <PackageReference Include="RGB.NET.Core" Version="1.0.0-prerelease1" />
</ItemGroup> </ItemGroup>

View File

@ -123,7 +123,7 @@ namespace Artemis.UI.Shared.Services.Builders
public IControl Build() public IControl Build()
{ {
return _action != null return _action != null
? new Button {Content = _text, Command = ReactiveCommand.Create(() => _action)} ? new Button {Content = _text, Command = ReactiveCommand.Create(() => _action())}
: new Button {Content = _text}; : new Button {Content = _text};
} }
} }

View File

@ -25,6 +25,10 @@
<Setter Property="Margin" Value="0 10 0 0"></Setter> <Setter Property="Margin" Value="0 10 0 0"></Setter>
<Setter Property="CornerRadius" Value="8 0 0 0" /> <Setter Property="CornerRadius" Value="8 0 0 0" />
<Setter Property="ClipToBounds" Value="True" /> <Setter Property="ClipToBounds" Value="True" />
</Style>
<Style Selector="Window:windows Border.router-container">
<Setter Property="Margin" Value="0 31 0 0"></Setter>
</Style> </Style>
<Style Selector="Border.card"> <Style Selector="Border.card">

View File

@ -10,8 +10,11 @@
<Button Classes="icon-button"> <Button Classes="icon-button">
<avalonia:MaterialIcon Kind="Cog" /> <avalonia:MaterialIcon Kind="Cog" />
</Button> </Button>
<Button Classes="icon-button" IsEnabled="False">
<avalonia:MaterialIcon Kind="Cog" />
</Button>
<TextBlock Margin="0 5 0 0">Button.icon-button icon-button-small</TextBlock> <TextBlock Margin="0 5 0 0">Button.icon-button icon-button-small</TextBlock>
<Button Classes="icon-button icon-button-small"> <Button Classes="icon-button icon-button-small">
<avalonia:MaterialIcon Kind="Cog" /> <avalonia:MaterialIcon Kind="Cog" />
</Button> </Button>
@ -40,9 +43,18 @@
<Setter Property="Background" Value="Transparent" /> <Setter Property="Background" Value="Transparent" />
<Setter Property="Padding" Value="5.5" /> <Setter Property="Padding" Value="5.5" />
</Style> </Style>
<Style Selector=":is(Button).icon-button:pointerover /template/ Border#BorderElement">
<Setter Property="BorderBrush" Value="Transparent" />
</Style>
<Style Selector=":is(Button).icon-button:disabled /template/ Border#BorderElement">
<Setter Property="BorderBrush" Value="Transparent" />
</Style>
<Style Selector=":is(Button).icon-button[IsEnabled=False] /template/ Border#BorderElement">
<Setter Property="BorderBrush" Value="Transparent" />
</Style>
<Style Selector=":is(Button).icon-button-small"> <Style Selector=":is(Button).icon-button-small">
<Setter Property="Padding" Value="2" /> <Setter Property="Padding" Value="4" />
</Style> </Style>
<Style Selector=":is(Button).icon-button-small avalonia|MaterialIcon"> <Style Selector=":is(Button).icon-button-small avalonia|MaterialIcon">
<Setter Property="Width" Value="14" /> <Setter Property="Width" Value="14" />

View File

@ -91,6 +91,20 @@
"Material.Icons": "1.0.2" "Material.Icons": "1.0.2"
} }
}, },
"ReactiveUI": {
"type": "Direct",
"requested": "[17.1.9, )",
"resolved": "17.1.9",
"contentHash": "bxH6uzEi1b6cfGoBBvCXWrdT18+Rleggi0R/vOaCYc+zvlSleJW6wlUtcWjrKzgQHD8EN3c02A4JBTt9oGSWWQ==",
"dependencies": {
"DynamicData": "7.4.3",
"Splat": "14.1.1",
"System.ComponentModel": "4.3.0",
"System.Diagnostics.Contracts": "4.3.0",
"System.Dynamic.Runtime": "4.3.0",
"System.Runtime.Serialization.Primitives": "4.3.0"
}
},
"ReactiveUI.Validation": { "ReactiveUI.Validation": {
"type": "Direct", "type": "Direct",
"requested": "[2.2.1, )", "requested": "[2.2.1, )",
@ -221,8 +235,8 @@
}, },
"DynamicData": { "DynamicData": {
"type": "Transitive", "type": "Transitive",
"resolved": "7.3.1", "resolved": "7.4.3",
"contentHash": "E9oTvWlAgzct0MuWt6k+0s+nSDA3LkFVvDwkMUTklIZZnva314KZAEF2vG4XX9I98ia+EpMqjte67jWEBJlsRw==", "contentHash": "7eGyREbtzyaRutMa+iToi2e41JboEVK9c1ZBcTvJOfEoTRIZX3hChIsxIvV0ErzMXtGHAIS2O0I8jLDUIds5wg==",
"dependencies": { "dependencies": {
"System.Reactive": "5.0.0" "System.Reactive": "5.0.0"
} }
@ -510,15 +524,6 @@
"Ninject": "3.3.3" "Ninject": "3.3.3"
} }
}, },
"ReactiveUI": {
"type": "Transitive",
"resolved": "16.2.6",
"contentHash": "jf1RvD8HxHuA6CGQtheGHUCHzRrhpvo0z593Npsz7g8KJWXfGR45Dc9bILJHoymBxhdDD1L1WjUfh0fcucIPPg==",
"dependencies": {
"DynamicData": "7.3.1",
"Splat": "13.1.1"
}
},
"RGB.NET.Layout": { "RGB.NET.Layout": {
"type": "Transitive", "type": "Transitive",
"resolved": "1.0.0-prerelease1", "resolved": "1.0.0-prerelease1",
@ -720,8 +725,8 @@
}, },
"Splat": { "Splat": {
"type": "Transitive", "type": "Transitive",
"resolved": "13.1.1", "resolved": "14.1.1",
"contentHash": "Hv41NNJwYEoxjpCrUw0BdzN17YLZrduhrnHgJyXatSgT6Dq/N/viVkPLfohCeoXf7MqA6ppB/AxtUGU3vJ7YEA==" "contentHash": "bKQtKu57w+iJ1T+WDyDdNq+LBNVdmNu2i0vGNyUdtmg4TEIEFiX2GfPusNEAW2QOfxyDE7i4+xTxbOKZr/4jhg=="
}, },
"Svg.Custom": { "Svg.Custom": {
"type": "Transitive", "type": "Transitive",
@ -884,6 +889,14 @@
"System.Text.Encoding": "4.3.0" "System.Text.Encoding": "4.3.0"
} }
}, },
"System.Diagnostics.Contracts": {
"type": "Transitive",
"resolved": "4.3.0",
"contentHash": "eelRRbnm+OloiQvp9CXS0ixjNQldjjkHO4iIkR5XH2VIP8sUB/SIpa1TdUW6/+HDcQ+MlhP3pNa1u5SbzYuWGA==",
"dependencies": {
"System.Runtime": "4.3.0"
}
},
"System.Diagnostics.Debug": { "System.Diagnostics.Debug": {
"type": "Transitive", "type": "Transitive",
"resolved": "4.3.0", "resolved": "4.3.0",
@ -1364,6 +1377,15 @@
"System.Runtime.Extensions": "4.3.0" "System.Runtime.Extensions": "4.3.0"
} }
}, },
"System.Runtime.Serialization.Primitives": {
"type": "Transitive",
"resolved": "4.3.0",
"contentHash": "Wz+0KOukJGAlXjtKr+5Xpuxf8+c8739RI1C+A2BoQZT+wMCCoMDDdO8/4IRHfaVINqL78GO8dW8G2lW/e45Mcw==",
"dependencies": {
"System.Resources.ResourceManager": "4.3.0",
"System.Runtime": "4.3.0"
}
},
"System.Security.AccessControl": { "System.Security.AccessControl": {
"type": "Transitive", "type": "Transitive",
"resolved": "5.0.0", "resolved": "5.0.0",

View File

@ -7,6 +7,7 @@ using System.Net.Http;
using System.Security.Principal; using System.Security.Principal;
using System.Threading; using System.Threading;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Shared.Services.Interfaces; using Artemis.UI.Shared.Services.Interfaces;
using Artemis.UI.Windows.Utilities; using Artemis.UI.Windows.Utilities;
using Avalonia; using Avalonia;
@ -28,10 +29,10 @@ namespace Artemis.UI.Windows
_windowService = kernel.Get<IWindowService>(); _windowService = kernel.Get<IWindowService>();
StartupArguments = startupArguments; StartupArguments = startupArguments;
IsElevated = new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator); IsElevated = new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);
Core.Utilities.ShutdownRequested += UtilitiesOnShutdownRequested; Core.Utilities.ShutdownRequested += UtilitiesOnShutdownRequested;
Core.Utilities.RestartRequested += UtilitiesOnRestartRequested; Core.Utilities.RestartRequested += UtilitiesOnRestartRequested;
// On Windows shutdown dispose the kernel just so device providers get a chance to clean up // On Windows shutdown dispose the kernel just so device providers get a chance to clean up
if (Application.Current.ApplicationLifetime is IControlledApplicationLifetime controlledApplicationLifetime) if (Application.Current.ApplicationLifetime is IControlledApplicationLifetime controlledApplicationLifetime)
{ {
@ -41,6 +42,9 @@ namespace Artemis.UI.Windows
kernel.Dispose(); kernel.Dispose();
}; };
} }
// Inform the Core about elevation status
kernel.Get<ICoreService>().IsElevated = IsElevated;
} }
public string[] StartupArguments { get; } public string[] StartupArguments { get; }

View File

@ -17,6 +17,7 @@
<PackageReference Include="Avalonia.Win32" Version="0.10.11" /> <PackageReference Include="Avalonia.Win32" Version="0.10.11" />
<PackageReference Include="Microsoft.Win32" Version="2.0.1" /> <PackageReference Include="Microsoft.Win32" Version="2.0.1" />
<PackageReference Include="RawInput.Sharp" Version="0.0.4" /> <PackageReference Include="RawInput.Sharp" Version="0.0.4" />
<PackageReference Include="ReactiveUI" Version="17.1.9" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\Artemis.Core\Artemis.Core.csproj" /> <ProjectReference Include="..\..\Artemis.Core\Artemis.Core.csproj" />

View File

@ -80,6 +80,20 @@
"NETStandard.Library": "1.6.1" "NETStandard.Library": "1.6.1"
} }
}, },
"ReactiveUI": {
"type": "Direct",
"requested": "[17.1.9, )",
"resolved": "17.1.9",
"contentHash": "bxH6uzEi1b6cfGoBBvCXWrdT18+Rleggi0R/vOaCYc+zvlSleJW6wlUtcWjrKzgQHD8EN3c02A4JBTt9oGSWWQ==",
"dependencies": {
"DynamicData": "7.4.3",
"Splat": "14.1.1",
"System.ComponentModel": "4.3.0",
"System.Diagnostics.Contracts": "4.3.0",
"System.Dynamic.Runtime": "4.3.0",
"System.Runtime.Serialization.Primitives": "4.3.0"
}
},
"Avalonia.Angle.Windows.Natives": { "Avalonia.Angle.Windows.Natives": {
"type": "Transitive", "type": "Transitive",
"resolved": "2.1.0.2020091801", "resolved": "2.1.0.2020091801",
@ -207,8 +221,8 @@
}, },
"DynamicData": { "DynamicData": {
"type": "Transitive", "type": "Transitive",
"resolved": "7.3.1", "resolved": "7.4.3",
"contentHash": "E9oTvWlAgzct0MuWt6k+0s+nSDA3LkFVvDwkMUTklIZZnva314KZAEF2vG4XX9I98ia+EpMqjte67jWEBJlsRw==", "contentHash": "7eGyREbtzyaRutMa+iToi2e41JboEVK9c1ZBcTvJOfEoTRIZX3hChIsxIvV0ErzMXtGHAIS2O0I8jLDUIds5wg==",
"dependencies": { "dependencies": {
"System.Reactive": "5.0.0" "System.Reactive": "5.0.0"
} }
@ -538,15 +552,6 @@
"Ninject": "3.3.3" "Ninject": "3.3.3"
} }
}, },
"ReactiveUI": {
"type": "Transitive",
"resolved": "16.2.6",
"contentHash": "jf1RvD8HxHuA6CGQtheGHUCHzRrhpvo0z593Npsz7g8KJWXfGR45Dc9bILJHoymBxhdDD1L1WjUfh0fcucIPPg==",
"dependencies": {
"DynamicData": "7.3.1",
"Splat": "13.1.1"
}
},
"ReactiveUI.Validation": { "ReactiveUI.Validation": {
"type": "Transitive", "type": "Transitive",
"resolved": "2.2.1", "resolved": "2.2.1",
@ -934,6 +939,14 @@
"System.Text.Encoding": "4.3.0" "System.Text.Encoding": "4.3.0"
} }
}, },
"System.Diagnostics.Contracts": {
"type": "Transitive",
"resolved": "4.3.0",
"contentHash": "eelRRbnm+OloiQvp9CXS0ixjNQldjjkHO4iIkR5XH2VIP8sUB/SIpa1TdUW6/+HDcQ+MlhP3pNa1u5SbzYuWGA==",
"dependencies": {
"System.Runtime": "4.3.0"
}
},
"System.Diagnostics.Debug": { "System.Diagnostics.Debug": {
"type": "Transitive", "type": "Transitive",
"resolved": "4.3.0", "resolved": "4.3.0",
@ -1414,6 +1427,15 @@
"System.Runtime.Extensions": "4.3.0" "System.Runtime.Extensions": "4.3.0"
} }
}, },
"System.Runtime.Serialization.Primitives": {
"type": "Transitive",
"resolved": "4.3.0",
"contentHash": "Wz+0KOukJGAlXjtKr+5Xpuxf8+c8739RI1C+A2BoQZT+wMCCoMDDdO8/4IRHfaVINqL78GO8dW8G2lW/e45Mcw==",
"dependencies": {
"System.Resources.ResourceManager": "4.3.0",
"System.Runtime": "4.3.0"
}
},
"System.Security.AccessControl": { "System.Security.AccessControl": {
"type": "Transitive", "type": "Transitive",
"resolved": "5.0.0", "resolved": "5.0.0",
@ -1773,6 +1795,7 @@
"Material.Icons.Avalonia": "1.0.2", "Material.Icons.Avalonia": "1.0.2",
"RGB.NET.Core": "1.0.0-prerelease1", "RGB.NET.Core": "1.0.0-prerelease1",
"RGB.NET.Layout": "1.0.0-prerelease1", "RGB.NET.Layout": "1.0.0-prerelease1",
"ReactiveUI": "17.1.9",
"ReactiveUI.Validation": "2.2.1", "ReactiveUI.Validation": "2.2.1",
"Splat.Ninject": "14.1.17" "Splat.Ninject": "14.1.17"
} }
@ -1790,6 +1813,7 @@
"FluentAvaloniaUI": "1.1.8", "FluentAvaloniaUI": "1.1.8",
"Material.Icons.Avalonia": "1.0.2", "Material.Icons.Avalonia": "1.0.2",
"RGB.NET.Core": "1.0.0-prerelease1", "RGB.NET.Core": "1.0.0-prerelease1",
"ReactiveUI": "17.1.9",
"ReactiveUI.Validation": "2.2.1" "ReactiveUI.Validation": "2.2.1"
} }
} }

View File

@ -23,6 +23,7 @@
<PackageReference Include="Flurl.Http" Version="3.2.0" /> <PackageReference Include="Flurl.Http" Version="3.2.0" />
<PackageReference Include="Live.Avalonia" Version="1.3.1" /> <PackageReference Include="Live.Avalonia" Version="1.3.1" />
<PackageReference Include="Material.Icons.Avalonia" Version="1.0.2" /> <PackageReference Include="Material.Icons.Avalonia" Version="1.0.2" />
<PackageReference Include="ReactiveUI" Version="17.1.9" />
<PackageReference Include="ReactiveUI.Validation" Version="2.2.1" /> <PackageReference Include="ReactiveUI.Validation" Version="2.2.1" />
<PackageReference Include="RGB.NET.Core" Version="1.0.0-prerelease1" /> <PackageReference Include="RGB.NET.Core" Version="1.0.0-prerelease1" />
<PackageReference Include="RGB.NET.Layout" Version="1.0.0-prerelease1" /> <PackageReference Include="RGB.NET.Layout" Version="1.0.0-prerelease1" />
@ -58,4 +59,9 @@
<ItemGroup> <ItemGroup>
<Folder Include="Screens\ProfileEditor\Tools\" /> <Folder Include="Screens\ProfileEditor\Tools\" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Reference Include="HistoricalReactiveCommand">
<HintPath>..\..\..\..\HistoricalReactiveCommand\HistoricalReactiveCommand\bin\Debug\netstandard2.1\HistoricalReactiveCommand.dll</HintPath>
</Reference>
</ItemGroup>
</Project> </Project>

View File

@ -1,22 +1,18 @@
<controls:CoreWindow xmlns="https://github.com/avaloniaui" <controls:CoreWindow xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.MainWindow" x:Class="Artemis.UI.MainWindow"
Icon="/Assets/Images/Logo/bow.ico" Icon="/Assets/Images/Logo/bow.ico"
Title="Artemis 2.0"> Title="Artemis 2.0">
<controls:CoreWindow.Resources>
<ResourceDictionary>
<Panel x:Key="RootWindowTitlebar" Height="31" HorizontalAlignment="Stretch" Background="Red">
<TextBlock>Test</TextBlock>
</Panel>
</ResourceDictionary>
</controls:CoreWindow.Resources>
<!-- Use a panel here so the main window can host ContentDialogs --> <!-- Use a panel here so the main window can host ContentDialogs -->
<Panel> <Panel>
<ContentControl Content="{Binding}" /> <ContentControl Content="{Binding}" />
<Border Background="Transparent" x:Name="TitleBar" VerticalAlignment="Top" MinHeight="31" >
<ContentControl Content="{Binding TitleBarViewModel}" Margin="240 0 150 0"/>
</Border>
</Panel> </Panel>
</controls:CoreWindow> </controls:CoreWindow>

View File

@ -1,6 +1,8 @@
using Artemis.UI.Screens.Root; using Artemis.UI.Screens.Root;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Shapes;
using Avalonia.Input;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using FluentAvalonia.Core.ApplicationModel; using FluentAvalonia.Core.ApplicationModel;
@ -20,9 +22,13 @@ namespace Artemis.UI
private void SetupTitlebar() private void SetupTitlebar()
{ {
//object? titleBar = this.FindResource("RootWindowTitlebar"); ICoreApplicationView coreAppTitleBar = this;
//if (titleBar != null) if (coreAppTitleBar.TitleBar != null)
// SetTitleBar((IControl) titleBar); {
coreAppTitleBar.TitleBar.ExtendViewIntoTitleBar = true;
SetTitleBar(this.Get<Border>("TitleBar"));
}
} }
private void InitializeComponent() private void InitializeComponent()

View File

@ -55,7 +55,7 @@ namespace Artemis.UI.Ninject.Factories
public interface IProfileEditorVmFactory : IVmFactory public interface IProfileEditorVmFactory : IVmFactory
{ {
ProfileEditorViewModel ProfileEditorViewModel(IScreen hostScreen); ProfileEditorViewModel ProfileEditorViewModel(IScreen hostScreen);
FolderTreeItemViewModel FolderTreeItemViewModel(Folder folder); FolderTreeItemViewModel FolderTreeItemViewModel(TreeItemViewModel? parent, Folder folder);
LayerTreeItemViewModel LayerTreeItemViewModel(Layer layer); LayerTreeItemViewModel LayerTreeItemViewModel(TreeItemViewModel? parent, Layer layer);
} }
} }

View File

@ -1,18 +1,16 @@
using System; using System;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.LogicalTree;
using FluentAvalonia.Interop;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using ReactiveUI; using ReactiveUI;
namespace Artemis.UI namespace Artemis.UI
{ {
/// <summary> /// <summary>
/// A ReactiveUI <see cref="Window"/> that implements the <see cref="IViewFor{TViewModel}"/> interface and will /// A ReactiveUI <see cref="Window" /> that implements the <see cref="IViewFor{TViewModel}" /> interface and will
/// activate your ViewModel automatically if the view model implements <see cref="IActivatableViewModel"/>. When /// activate your ViewModel automatically if the view model implements <see cref="IActivatableViewModel" />. When
/// the DataContext property changes, this class will update the ViewModel property with the new DataContext value, /// the DataContext property changes, this class will update the ViewModel property with the new DataContext value,
/// and vice versa. /// and vice versa.
/// </summary> /// </summary>
/// <typeparam name="TViewModel">ViewModel type.</typeparam> /// <typeparam name="TViewModel">ViewModel type.</typeparam>
public class ReactiveCoreWindow<TViewModel> : CoreWindow, IViewFor<TViewModel> where TViewModel : class public class ReactiveCoreWindow<TViewModel> : CoreWindow, IViewFor<TViewModel> where TViewModel : class
@ -21,7 +19,7 @@ namespace Artemis.UI
.Register<ReactiveCoreWindow<TViewModel>, TViewModel?>(nameof(ViewModel)); .Register<ReactiveCoreWindow<TViewModel>, TViewModel?>(nameof(ViewModel));
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ReactiveCoreWindow{TViewModel}"/> class. /// Initializes a new instance of the <see cref="ReactiveCoreWindow{TViewModel}" /> class.
/// </summary> /// </summary>
public ReactiveCoreWindow() public ReactiveCoreWindow()
{ {
@ -30,16 +28,25 @@ namespace Artemis.UI
this.WhenActivated(disposables => { }); this.WhenActivated(disposables => { });
this.GetObservable(DataContextProperty).Subscribe(OnDataContextChanged); this.GetObservable(DataContextProperty).Subscribe(OnDataContextChanged);
this.GetObservable(ViewModelProperty).Subscribe(OnViewModelChanged); this.GetObservable(ViewModelProperty).Subscribe(OnViewModelChanged);
// TODO Remove
Win32Interop.OSVERSIONINFOEX version = default;
Win32Interop.RtlGetVersion(ref version);
if (version.MajorVersion == 10)
PseudoClasses.Add(":windows10");
} }
private void OnDataContextChanged(object? value)
{
if (value is TViewModel viewModel)
ViewModel = viewModel;
else
ViewModel = null;
}
private void OnViewModelChanged(object? value)
{
if (value == null)
ClearValue(DataContextProperty);
else if (DataContext != value) DataContext = value;
}
/// <summary> /// <summary>
/// The ViewModel. /// The ViewModel.
/// </summary> /// </summary>
public TViewModel? ViewModel public TViewModel? ViewModel
{ {
@ -50,31 +57,7 @@ namespace Artemis.UI
object? IViewFor.ViewModel object? IViewFor.ViewModel
{ {
get => ViewModel; get => ViewModel;
set => ViewModel = (TViewModel?)value; set => ViewModel = (TViewModel?) value;
}
private void OnDataContextChanged(object? value)
{
if (value is TViewModel viewModel)
{
ViewModel = viewModel;
}
else
{
ViewModel = null;
}
}
private void OnViewModelChanged(object? value)
{
if (value == null)
{
ClearValue(DataContextProperty);
}
else if (DataContext != value)
{
DataContext = value;
}
} }
} }
} }

View File

@ -16,5 +16,7 @@ namespace Artemis.UI.Screens
/// <inheritdoc /> /// <inheritdoc />
public IScreen HostScreen { get; } public IScreen HostScreen { get; }
public ViewModelBase? TitleBarViewModel { get; protected set; }
} }
} }

View File

@ -7,6 +7,8 @@ using Artemis.Core.Services;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services.Builders; using Artemis.UI.Shared.Services.Builders;
using Artemis.UI.Shared.Services.Interfaces; using Artemis.UI.Shared.Services.Interfaces;
using Avalonia;
using Avalonia.Threading;
using ReactiveUI; using ReactiveUI;
namespace Artemis.UI.Screens.Plugins namespace Artemis.UI.Screens.Plugins
@ -56,7 +58,7 @@ namespace Artemis.UI.Screens.Plugins
public bool IsEnabled public bool IsEnabled
{ {
get => FeatureInfo.Instance != null && FeatureInfo.Instance.IsEnabled; get => FeatureInfo.Instance != null && FeatureInfo.Instance.IsEnabled;
set => Task.Run(() => UpdateEnabled(value)); set => Dispatcher.UIThread.InvokeAsync(() => UpdateEnabled(value));
} }
public bool CanToggleEnabled => FeatureInfo.Plugin.IsEnabled && !FeatureInfo.AlwaysEnabled; public bool CanToggleEnabled => FeatureInfo.Plugin.IsEnabled && !FeatureInfo.AlwaysEnabled;
@ -159,7 +161,7 @@ namespace Artemis.UI.Screens.Plugins
} }
} }
await Task.Run(() => _pluginManagementService.EnablePluginFeature(FeatureInfo.Instance!, true)); await Dispatcher.UIThread.InvokeAsync(() => _pluginManagementService.EnablePluginFeature(FeatureInfo.Instance!, true));
} }
catch (Exception e) catch (Exception e)
{ {

View File

@ -0,0 +1,198 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.ProfileEditor.Panels.MenuBar.MenuBarView">
<Menu Grid.Row="0" Grid.Column="0" Margin="0 2" VerticalAlignment="Top">
<MenuItem Header="_File">
<MenuItem Header="New">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="Plus" />
</MenuItem.Icon>
<MenuItem Header="Folder" Command="{Binding AddFolder}">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="Folder" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Layer" Command="{Binding AddLayer}">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="Layers" />
</MenuItem.Icon>
</MenuItem>
</MenuItem>
<Separator />
<MenuItem Header="View Properties" Command="{Binding ViewProperties}">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="Settings" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="_View Scripts" Command="{Binding ViewScripts}">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="BookEdit" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Adapt Profile" Command="{Binding AdaptProfile}">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="Magic" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Suspend Profile">
<MenuItem.Icon>
<CheckBox BorderThickness="0"
IsHitTestVisible="False"
IsChecked="{Binding ProfileConfiguration.IsSuspended}" />
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem Header="Export Profile" Command="{Binding ExportProfile}">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="Export" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Duplicate Profile" Command="{Binding DuplicateProfile}">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="ContentDuplicate" />
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem Header="Delete Profile" Command="{Binding DeleteProfile}">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="Trash" />
</MenuItem.Icon>
</MenuItem>
</MenuItem>
<MenuItem Header="_Edit" SubmenuOpened="MenuItem_OnSubmenuOpened">
<MenuItem Header="_Duplicate"
Command="{Binding Duplicate}"
HotKey="Ctrl+D"
IsEnabled="{Binding HasSelectedElement}">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="ContentDuplicate" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="_Copy"
Command="{Binding Copy}"
HotKey="Ctrl+C"
IsEnabled="{Binding HasSelectedElement}">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="ContentCopy" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="_Paste"
Command="{Binding Paste}"
HotKey="Ctrl+V">
<MenuItem.Icon>
<avalonia:MaterialIconExt Kind="ContentPaste" />
</MenuItem.Icon>
</MenuItem>
</MenuItem>
<MenuItem Header="_Run">
<MenuItem Header="_Switch run mode"
Command="{Binding ToggleSuspend}"
HotKey="F5">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="SwapHorizontal" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Run Profile on Focus Loss"
ToolTip.Tip="If enabled, run mode is set to normal on focus loss"
HotKey="Shift+F5">
<MenuItem.Icon>
<CheckBox BorderThickness="0"
IsHitTestVisible="False"
IsChecked="{Binding StopOnFocusLoss.Value}" />
</MenuItem.Icon>
</MenuItem>
</MenuItem>
<MenuItem Header="_Options">
<MenuItem Header="Focus Selected Layer"
ToolTip.Tip="If enabled, displays only the layer you currently have selected"
IsEnabled="False">
<MenuItem.Icon>
<CheckBox BorderThickness="0"
IsHitTestVisible="False"
IsChecked="{Binding FocusSelectedLayer.Value}" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Display Data Model Values">
<MenuItem.Icon>
<CheckBox BorderThickness="0"
IsHitTestVisible="False"
IsChecked="{Binding ShowDataModelValues.Value}" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Display Full Condition Paths">
<MenuItem.Icon>
<CheckBox BorderThickness="0"
IsHitTestVisible="False"
IsChecked="{Binding ShowFullPaths.Value}" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Always Display Cable Values" ToolTip.Tip="If enabled, cable values are always shown instead of only on hover">
<MenuItem.Icon>
<CheckBox BorderThickness="0"
IsHitTestVisible="False"
IsChecked="{Binding AlwaysShowValues.Value}" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Apply All Data Bindings During Edit" ToolTip.Tip="If enabled, updates all data bindings instead of only the one you are editing">
<MenuItem.Icon>
<CheckBox BorderThickness="0"
IsHitTestVisible="False"
IsChecked="{Binding AlwaysApplyDataBindings.Value}" />
</MenuItem.Icon>
</MenuItem>
</MenuItem>
<MenuItem Header="_Help">
<MenuItem Header="Artemis Wiki" Command="{Binding OpenUrl}" CommandParameter="https://wiki.artemis-rgb.com/">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="BookEdit" />
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem Header="Editor" Command="{Binding OpenUrl}" CommandParameter="https://wiki.artemis-rgb.com/en/guides/user/profiles">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="Edit" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Layers" Command="{Binding OpenUrl}" CommandParameter="https://wiki.artemis-rgb.com/guides/user/profiles/layers">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="Layers" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Display Conditions" Command="{Binding OpenUrl}" CommandParameter="https://wiki.artemis-rgb.com/guides/user/profiles/conditions">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="NotEqual" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Timeline" Command="{Binding OpenUrl}" CommandParameter="https://wiki.artemis-rgb.com/guides/user/profiles/timeline">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="Stopwatch" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Data Bindings" Command="{Binding OpenUrl}" CommandParameter="https://wiki.artemis-rgb.com/guides/user/profiles/data-bindings">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="VectorLink" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Scripting" Command="{Binding OpenUrl}" CommandParameter="https://wiki.artemis-rgb.com/guides/user/profiles/scripting">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="CodeJson" />
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem Header="Report a Bug" Command="{Binding OpenUrl}" CommandParameter="https://github.com/Artemis-RGB/Artemis/issues">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="Github" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Get Help on Discord" Command="{Binding OpenUrl}" CommandParameter="https://discord.gg/S3MVaC9">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="Discord" />
</MenuItem.Icon>
</MenuItem>
</MenuItem>
</Menu>
</UserControl>

View File

@ -0,0 +1,25 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
namespace Artemis.UI.Screens.ProfileEditor.Panels.MenuBar
{
public partial class MenuBarView : UserControl
{
public MenuBarView()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
private void MenuItem_OnSubmenuOpened(object? sender, RoutedEventArgs e)
{
}
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Artemis.UI.Shared;
namespace Artemis.UI.Screens.ProfileEditor.Panels.MenuBar
{
public class MenuBarViewModel : ViewModelBase
{
}
}

View File

@ -29,7 +29,8 @@
Classes="icon-button icon-button-small" Classes="icon-button icon-button-small"
ToolTip.Tip="Toggle suspended state" ToolTip.Tip="Toggle suspended state"
IsChecked="{Binding !Folder.Suspended}" IsChecked="{Binding !Folder.Suspended}"
VerticalAlignment="Center"> VerticalAlignment="Center"
Margin="4 0">
<avalonia:MaterialIcon Kind="Pause" /> <avalonia:MaterialIcon Kind="Pause" />
</ToggleButton> </ToggleButton>
</Grid> </Grid>

View File

@ -1,27 +1,23 @@
using System.Collections.Generic; using Artemis.Core;
using Artemis.Core;
using Artemis.UI.Ninject.Factories; using Artemis.UI.Ninject.Factories;
namespace Artemis.UI.Screens.ProfileEditor.ProfileTree namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
{ {
public class FolderTreeItemViewModel : TreeItemViewModel public class FolderTreeItemViewModel : TreeItemViewModel
{ {
public Folder Folder { get; } public FolderTreeItemViewModel(TreeItemViewModel? parent, Folder folder, IProfileEditorVmFactory profileEditorVmFactory) : base(parent, folder)
public FolderTreeItemViewModel(Folder folder, IProfileEditorVmFactory profileEditorVmFactory)
{ {
Folder = folder; Folder = folder;
Children = new List<TreeItemViewModel>();
foreach (ProfileElement profileElement in folder.Children) foreach (ProfileElement profileElement in folder.Children)
{ {
if (profileElement is Folder childFolder) if (profileElement is Folder childFolder)
Children.Add(profileEditorVmFactory.FolderTreeItemViewModel(childFolder)); Children.Add(profileEditorVmFactory.FolderTreeItemViewModel(this, childFolder));
else if (profileElement is Layer layer) else if (profileElement is Layer layer)
Children.Add(profileEditorVmFactory.LayerTreeItemViewModel(layer)); Children.Add(profileEditorVmFactory.LayerTreeItemViewModel(this, layer));
} }
} }
public List<TreeItemViewModel> Children { get; } public Folder Folder { get; }
} }
} }

View File

@ -10,9 +10,7 @@
ToolTip.Tip="{Binding BrokenState}" ToolTip.Tip="{Binding BrokenState}"
IsVisible="{Binding BrokenState, Converter={x:Static ObjectConverters.IsNotNull}}" IsVisible="{Binding BrokenState, Converter={x:Static ObjectConverters.IsNotNull}}"
Classes="icon-button icon-button-small" Classes="icon-button icon-button-small"
Foreground="White" Foreground="#E74C4C"
Background="#E74C4C"
BorderBrush="#E74C4C"
Command="{Binding ShowBrokenStateExceptions}"> Command="{Binding ShowBrokenStateExceptions}">
<avalonia:MaterialIcon Kind="AlertCircle" /> <avalonia:MaterialIcon Kind="AlertCircle" />
</Button> </Button>
@ -22,7 +20,8 @@
Classes="icon-button icon-button-small" Classes="icon-button icon-button-small"
ToolTip.Tip="Toggle suspended state" ToolTip.Tip="Toggle suspended state"
IsChecked="{Binding Layer.Suspended}" IsChecked="{Binding Layer.Suspended}"
VerticalAlignment="Center"> VerticalAlignment="Center"
Margin="4 0">
<avalonia:MaterialIcon Kind="Pause" /> <avalonia:MaterialIcon Kind="Pause" />
</ToggleButton> </ToggleButton>
</Grid> </Grid>

View File

@ -4,11 +4,11 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
{ {
public class LayerTreeItemViewModel : TreeItemViewModel public class LayerTreeItemViewModel : TreeItemViewModel
{ {
public Layer Layer { get; } public LayerTreeItemViewModel(TreeItemViewModel? parent, Layer layer) : base(parent, layer)
public LayerTreeItemViewModel(Layer layer)
{ {
Layer = layer; Layer = layer;
} }
public Layer Layer { get; }
} }
} }

View File

@ -2,14 +2,30 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:profileTree="clr-namespace:Artemis.UI.Screens.ProfileEditor.ProfileTree" xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.ProfileEditor.ProfileTree.ProfileTreeView"> x:Class="Artemis.UI.Screens.ProfileEditor.ProfileTree.ProfileTreeView">
<TreeView Items="{Binding TreeItems}" Classes="no-right-margin"> <Grid RowDefinitions="*,Auto">
<TreeView.ItemTemplate> <TreeView Classes="no-right-margin" Items="{Binding TreeItems}" SelectedItem="{Binding SelectedTreeItem}">
<TreeDataTemplate ItemsSource="{Binding Children}"> <TreeView.Styles>
<ContentControl Content="{Binding}"/> <Style Selector="TreeViewItem">
</TreeDataTemplate> <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
</TreeView.ItemTemplate> </Style>
</TreeView> </TreeView.Styles>
<TreeView.ItemTemplate>
<TreeDataTemplate ItemsSource="{Binding Children}">
<ContentControl Content="{Binding}" />
</TreeDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
<StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right">
<Button Classes="icon-button" ToolTip.Tip="Add new folder to root" Command="{Binding AddFolder}">
<avalonia:MaterialIcon Kind="FolderAdd" />
</Button>
<Button Classes="icon-button" ToolTip.Tip="Add new layer to root" Command="{Binding AddLayer}" Margin="2 0 0 0">
<avalonia:MaterialIcon Kind="LayersPlus" />
</Button>
</StackPanel>
</Grid>
</UserControl> </UserControl>

View File

@ -1,11 +1,14 @@
using System; using System;
using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Reactive;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using Artemis.Core; using Artemis.Core;
using Artemis.UI.Ninject.Factories; using Artemis.UI.Ninject.Factories;
using Artemis.UI.Services; using Artemis.UI.Services;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using HistoricalReactiveCommand;
using ReactiveUI; using ReactiveUI;
namespace Artemis.UI.Screens.ProfileEditor.ProfileTree namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
@ -13,15 +16,48 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
public class ProfileTreeViewModel : ActivatableViewModelBase public class ProfileTreeViewModel : ActivatableViewModelBase
{ {
private readonly IProfileEditorVmFactory _profileEditorVmFactory; private readonly IProfileEditorVmFactory _profileEditorVmFactory;
private TreeItemViewModel? _selectedTreeItem;
public ProfileTreeViewModel(IProfileEditorService profileEditorService, IProfileEditorVmFactory profileEditorVmFactory) public ProfileTreeViewModel(IProfileEditorService profileEditorService, IProfileEditorVmFactory profileEditorVmFactory)
{ {
_profileEditorVmFactory = profileEditorVmFactory; _profileEditorVmFactory = profileEditorVmFactory;
this.WhenActivated(d => profileEditorService.CurrentProfileConfiguration.WhereNotNull().Subscribe(Repopulate).DisposeWith(d));
this.WhenActivated(d =>
{
ProfileConfiguration profileConfiguration = null!;
profileEditorService.CurrentProfileConfiguration.WhereNotNull().Subscribe(p => profileConfiguration = p).DisposeWith(d);
profileEditorService.CurrentProfileConfiguration.WhereNotNull().Subscribe(Repopulate).DisposeWith(d);
profileEditorService.CurrentProfileElement.WhereNotNull().Subscribe(SelectCurrentProfileElement).DisposeWith(d);
Folder rootFolder = profileConfiguration.Profile!.GetRootFolder();
AddLayer = ReactiveCommandEx.CreateWithHistory("AddLayerAtRoot",
// ReSharper disable once ObjectCreationAsStatement
() => new Layer(rootFolder, "New layer", 0),
() => rootFolder.RemoveChild(rootFolder.Children[0]),
profileConfiguration.Profile.EntityId.ToString()
);
AddFolder = ReactiveCommandEx.CreateWithHistory("AddFolderAtRoot",
// ReSharper disable once ObjectCreationAsStatement
() => new Folder(rootFolder, "New folder", 0),
() => rootFolder.RemoveChild(rootFolder.Children[0]),
profileConfiguration.Profile.EntityId.ToString()
);
});
this.WhenAnyValue(vm => vm.SelectedTreeItem).Subscribe(model => profileEditorService.ChangeCurrentProfileElement(model?.ProfileElement));
} }
public ReactiveCommandWithHistory<Unit, Unit>? AddLayer { get; set; }
public ReactiveCommandWithHistory<Unit, Unit>? AddFolder { get; set; }
public ObservableCollection<TreeItemViewModel> TreeItems { get; } = new(); public ObservableCollection<TreeItemViewModel> TreeItems { get; } = new();
public TreeItemViewModel? SelectedTreeItem
{
get => _selectedTreeItem;
set => this.RaiseAndSetIfChanged(ref _selectedTreeItem, value);
}
private void Repopulate(ProfileConfiguration profileConfiguration) private void Repopulate(ProfileConfiguration profileConfiguration)
{ {
if (TreeItems.Any()) if (TreeItems.Any())
@ -33,11 +69,43 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
foreach (ProfileElement profileElement in profileConfiguration.Profile.GetRootFolder().Children) foreach (ProfileElement profileElement in profileConfiguration.Profile.GetRootFolder().Children)
{ {
if (profileElement is Folder folder) if (profileElement is Folder folder)
TreeItems.Add(_profileEditorVmFactory.FolderTreeItemViewModel(folder)); TreeItems.Add(_profileEditorVmFactory.FolderTreeItemViewModel(null, folder));
else if (profileElement is Layer layer) else if (profileElement is Layer layer)
TreeItems.Add(_profileEditorVmFactory.LayerTreeItemViewModel(layer)); TreeItems.Add(_profileEditorVmFactory.LayerTreeItemViewModel(null, layer));
} }
}
private void SelectCurrentProfileElement(RenderProfileElement element)
{
if (SelectedTreeItem?.ProfileElement == element)
return;
// Find the tree item belonging to the selected element
List<TreeItemViewModel> treeItems = GetAllTreeItems(TreeItems);
TreeItemViewModel? selected = treeItems.FirstOrDefault(e => e.ProfileElement == element);
// Walk up the tree, expanding parents
TreeItemViewModel? currentParent = selected?.Parent;
while (currentParent != null)
{
currentParent.IsExpanded = true;
currentParent = currentParent.Parent;
}
SelectedTreeItem = selected;
}
private List<TreeItemViewModel> GetAllTreeItems(ObservableCollection<TreeItemViewModel> treeItems)
{
List<TreeItemViewModel> result = new();
foreach (TreeItemViewModel treeItemViewModel in treeItems)
{
result.Add(treeItemViewModel);
if (treeItemViewModel.Children.Any())
result.AddRange(GetAllTreeItems(treeItemViewModel.Children));
}
return result;
} }
} }
} }

View File

@ -1,8 +1,55 @@
using Artemis.UI.Shared; using System.Collections.ObjectModel;
using System.Linq;
using Artemis.Core;
using Artemis.UI.Shared;
using HistoricalReactiveCommand;
using ReactiveUI;
namespace Artemis.UI.Screens.ProfileEditor.ProfileTree namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
{ {
public abstract class TreeItemViewModel : ActivatableViewModelBase public abstract class TreeItemViewModel : ActivatableViewModelBase
{ {
private bool _isExpanded;
protected TreeItemViewModel(TreeItemViewModel? parent, RenderProfileElement profileElement)
{
Parent = parent;
ProfileElement = profileElement;
AddLayerAtIndex = ReactiveCommandEx.CreateWithHistory<int, Layer>("AddLayerAtIndex",
(targetIndex, _) => new Layer(ProfileElement, "New folder", targetIndex),
(targetIndex, _) =>
{
Layer toRemove = (Layer) ProfileElement.Children.ElementAt(targetIndex);
ProfileElement.RemoveChild(toRemove);
return toRemove;
},
historyId: ProfileElement.Profile.EntityId.ToString()
);
AddFolderAtIndex = ReactiveCommandEx.CreateWithHistory<int, Folder>("AddFolderAtIndex",
(targetIndex, _) => new Folder(ProfileElement, "New folder", targetIndex),
(targetIndex, _) =>
{
Folder toRemove = (Folder) ProfileElement.Children.ElementAt(targetIndex);
ProfileElement.RemoveChild(toRemove);
return toRemove;
},
historyId: ProfileElement.Profile.EntityId.ToString()
);
}
public ReactiveCommandWithHistory<int, Layer> AddLayerAtIndex { get; set; }
public ReactiveCommandWithHistory<int, Folder> AddFolderAtIndex { get; set; }
public RenderProfileElement ProfileElement { get; }
public TreeItemViewModel? Parent { get; set; }
public ObservableCollection<TreeItemViewModel> Children { get; } = new();
public bool IsExpanded
{
get => _isExpanded;
set => this.RaiseAndSetIfChanged(ref _isExpanded, value);
}
} }
} }

View File

@ -0,0 +1,18 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.ProfileEditor.ProfileEditorTitleBarView">
<Grid ColumnDefinitions="Auto,*,Auto">
<ContentControl Grid.Row="0" Grid.Column="0" Content="{Binding MenuBarViewModel}"></ContentControl>
<!-- This border enables dragging the window in between the menu and the buttons-->
<Border Grid.Row="0" Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Background="Transparent" IsHitTestVisible="False" />
<Button Grid.Column="2" Classes="icon-button icon-button-small" Command="{Binding ShowDebugger}" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0 6 0 0">
<avalonia:MaterialIcon Kind="Bug"></avalonia:MaterialIcon>
</Button>
</Grid>
</UserControl>

View File

@ -0,0 +1,25 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
namespace Artemis.UI.Screens.ProfileEditor
{
public partial class ProfileEditorTitleBarView : UserControl
{
public ProfileEditorTitleBarView()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
private void MenuItem_OnSubmenuOpened(object? sender, RoutedEventArgs e)
{
}
}
}

View File

@ -0,0 +1,24 @@
using Artemis.UI.Screens.ProfileEditor.Panels.MenuBar;
using Artemis.UI.Services.Interfaces;
using Artemis.UI.Shared;
namespace Artemis.UI.Screens.ProfileEditor
{
public class ProfileEditorTitleBarViewModel : ViewModelBase
{
private readonly IDebugService _debugService;
public ProfileEditorTitleBarViewModel(IDebugService debugService, MenuBarViewModel menuBarViewModel)
{
MenuBarViewModel = menuBarViewModel;
_debugService = debugService;
}
public MenuBarViewModel MenuBarViewModel { get; }
public void ShowDebugger()
{
_debugService.ShowDebugger();
}
}
}

View File

@ -24,204 +24,19 @@
<Setter Property="Width" Value="18" /> <Setter Property="Width" Value="18" />
<Setter Property="Height" Value="18" /> <Setter Property="Height" Value="18" />
</Style> </Style>
<Style Selector="Window:windows Grid.editor-grid">
<Setter Property="Margin" Value="0 0 4 4"></Setter>
</Style>
<Style Selector="Window:windows Grid.editor-grid">
<Setter Property="Margin" Value="0 41 4 4"></Setter>
</Style>
</UserControl.Styles> </UserControl.Styles>
<Grid ColumnDefinitions="4*,Auto,*" RowDefinitions="Auto,*" Margin="0 0 4 4"> <Grid ColumnDefinitions="4*,Auto,*" Classes="editor-grid">
<Menu Grid.Row="0" Grid.ColumnSpan="3" VerticalAlignment="Top"> <ContentControl Content="{Binding MenuBarViewModel}"></ContentControl>
<MenuItem Header="_File"> <Grid Grid.Column="0" RowDefinitions="3*,Auto,*">
<MenuItem Header="New">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="Plus" />
</MenuItem.Icon>
<MenuItem Header="Folder" Command="{Binding AddFolder}">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="Folder" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Layer" Command="{Binding AddLayer}">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="Layers" />
</MenuItem.Icon>
</MenuItem>
</MenuItem>
<Separator />
<MenuItem Header="View Properties" Command="{Binding ViewProperties}">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="Settings" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="_View Scripts" Command="{Binding ViewScripts}">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="BookEdit" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Adapt Profile" Command="{Binding AdaptProfile}">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="Magic" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Suspend Profile">
<MenuItem.Icon>
<CheckBox BorderThickness="0"
IsHitTestVisible="False"
IsChecked="{Binding ProfileConfiguration.IsSuspended}" />
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem Header="Export Profile" Command="{Binding ExportProfile}">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="Export" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Duplicate Profile" Command="{Binding DuplicateProfile}">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="ContentDuplicate" />
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem Header="Delete Profile" Command="{Binding DeleteProfile}">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="Trash" />
</MenuItem.Icon>
</MenuItem>
</MenuItem>
<MenuItem Header="_Edit" SubmenuOpened="MenuItem_OnSubmenuOpened">
<MenuItem Header="_Duplicate"
Command="{Binding Duplicate}"
HotKey="Ctrl+D"
IsEnabled="{Binding HasSelectedElement}">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="ContentDuplicate" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="_Copy"
Command="{Binding Copy}"
HotKey="Ctrl+C"
IsEnabled="{Binding HasSelectedElement}">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="ContentCopy" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="_Paste"
Command="{Binding Paste}"
HotKey="Ctrl+V">
<MenuItem.Icon>
<avalonia:MaterialIconExt Kind="ContentPaste" />
</MenuItem.Icon>
</MenuItem>
</MenuItem>
<MenuItem Header="_Run">
<MenuItem Header="_Switch run mode"
Command="{Binding ToggleSuspend}"
HotKey="F5">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="SwapHorizontal" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Run Profile on Focus Loss"
ToolTip.Tip="If enabled, run mode is set to normal on focus loss"
HotKey="Shift+F5">
<MenuItem.Icon>
<CheckBox BorderThickness="0"
IsHitTestVisible="False"
IsChecked="{Binding StopOnFocusLoss.Value}" />
</MenuItem.Icon>
</MenuItem>
</MenuItem>
<MenuItem Header="_Options">
<MenuItem Header="Focus Selected Layer"
ToolTip.Tip="If enabled, displays only the layer you currently have selected"
IsEnabled="False">
<MenuItem.Icon>
<CheckBox BorderThickness="0"
IsHitTestVisible="False"
IsChecked="{Binding FocusSelectedLayer.Value}" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Display Data Model Values">
<MenuItem.Icon>
<CheckBox BorderThickness="0"
IsHitTestVisible="False"
IsChecked="{Binding ShowDataModelValues.Value}" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Display Full Condition Paths">
<MenuItem.Icon>
<CheckBox BorderThickness="0"
IsHitTestVisible="False"
IsChecked="{Binding ShowFullPaths.Value}" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Always Display Cable Values" ToolTip.Tip="If enabled, cable values are always shown instead of only on hover">
<MenuItem.Icon>
<CheckBox BorderThickness="0"
IsHitTestVisible="False"
IsChecked="{Binding AlwaysShowValues.Value}" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Apply All Data Bindings During Edit" ToolTip.Tip="If enabled, updates all data bindings instead of only the one you are editing">
<MenuItem.Icon>
<CheckBox BorderThickness="0"
IsHitTestVisible="False"
IsChecked="{Binding AlwaysApplyDataBindings.Value}" />
</MenuItem.Icon>
</MenuItem>
</MenuItem>
<MenuItem Header="_Help">
<MenuItem Header="Artemis Wiki" Command="{Binding OpenUrl}" CommandParameter="https://wiki.artemis-rgb.com/">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="BookEdit" />
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem Header="Editor" Command="{Binding OpenUrl}" CommandParameter="https://wiki.artemis-rgb.com/en/guides/user/profiles">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="Edit" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Layers" Command="{Binding OpenUrl}" CommandParameter="https://wiki.artemis-rgb.com/guides/user/profiles/layers">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="Layers" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Display Conditions" Command="{Binding OpenUrl}" CommandParameter="https://wiki.artemis-rgb.com/guides/user/profiles/conditions">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="NotEqual" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Timeline" Command="{Binding OpenUrl}" CommandParameter="https://wiki.artemis-rgb.com/guides/user/profiles/timeline">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="Stopwatch" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Data Bindings" Command="{Binding OpenUrl}" CommandParameter="https://wiki.artemis-rgb.com/guides/user/profiles/data-bindings">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="VectorLink" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Scripting" Command="{Binding OpenUrl}" CommandParameter="https://wiki.artemis-rgb.com/guides/user/profiles/scripting">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="CodeJson" />
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem Header="Report a Bug" Command="{Binding OpenUrl}" CommandParameter="https://github.com/Artemis-RGB/Artemis/issues">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="Github" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Get Help on Discord" Command="{Binding OpenUrl}" CommandParameter="https://discord.gg/S3MVaC9">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="Discord" />
</MenuItem.Icon>
</MenuItem>
</MenuItem>
</Menu>
<Grid Grid.Row="1" Grid.Column="0" RowDefinitions="3*,Auto,*">
<Border Grid.Row="0" Classes="card" Padding="0" Margin="4 0 4 4" ClipToBounds="True"> <Border Grid.Row="0" Classes="card" Padding="0" Margin="4 0 4 4" ClipToBounds="True">
<Grid ColumnDefinitions="Auto,*"> <Grid ColumnDefinitions="Auto,*">
<Border Grid.Column="0" Background="{DynamicResource CardStrokeColorDefaultSolidBrush}"> <Border Grid.Column="0" Background="{DynamicResource CardStrokeColorDefaultSolidBrush}">
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
<ToggleButton Classes="icon-button editor-sidebar-button"> <ToggleButton Classes="icon-button editor-sidebar-button">
<avalonia:MaterialIcon Kind="HandLeft" /> <avalonia:MaterialIcon Kind="HandLeft" />
@ -239,7 +54,6 @@
</Border> </Border>
<ContentControl Grid.Column="1" Content="{Binding VisualEditorViewModel}" /> <ContentControl Grid.Column="1" Content="{Binding VisualEditorViewModel}" />
</Grid> </Grid>
</Border> </Border>
<GridSplitter Grid.Row="1" Classes="editor-grid-splitter-horizontal" /> <GridSplitter Grid.Row="1" Classes="editor-grid-splitter-horizontal" />
@ -249,9 +63,9 @@
</Border> </Border>
</Grid> </Grid>
<GridSplitter Grid.Row="1" Grid.Column="1" Classes="editor-grid-splitter-vertical" /> <GridSplitter Grid.Row="0" Grid.Column="1" Classes="editor-grid-splitter-vertical" />
<Grid Grid.Row="1" Grid.Column="2" RowDefinitions="*,Auto,*"> <Grid Grid.Row="0" Grid.Column="2" RowDefinitions="*,Auto,*">
<Border Grid.Row="0" Classes="card card-condensed" Margin="4 0 4 4"> <Border Grid.Row="0" Classes="card card-condensed" Margin="4 0 4 4">
<ContentControl Content="{Binding ProfileTreeViewModel}" /> <ContentControl Content="{Binding ProfileTreeViewModel}" />
</Border> </Border>

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using Artemis.Core; using Artemis.Core;
using Artemis.UI.Screens.ProfileEditor.Panels.MenuBar;
using Artemis.UI.Screens.ProfileEditor.ProfileTree; using Artemis.UI.Screens.ProfileEditor.ProfileTree;
using Artemis.UI.Screens.ProfileEditor.VisualEditor; using Artemis.UI.Screens.ProfileEditor.VisualEditor;
using Artemis.UI.Services; using Artemis.UI.Services;
@ -13,16 +14,28 @@ namespace Artemis.UI.Screens.ProfileEditor
private ProfileConfiguration? _profile; private ProfileConfiguration? _profile;
/// <inheritdoc /> /// <inheritdoc />
public ProfileEditorViewModel(IScreen hostScreen, IProfileEditorService profileEditorService, VisualEditorViewModel visualEditorViewModel, ProfileTreeViewModel profileTreeViewModel) public ProfileEditorViewModel(IScreen hostScreen,
IProfileEditorService profileEditorService,
VisualEditorViewModel visualEditorViewModel,
ProfileTreeViewModel profileTreeViewModel,
ProfileEditorTitleBarViewModel profileEditorTitleBarViewModel,
MenuBarViewModel menuBarViewModel)
: base(hostScreen, "profile-editor") : base(hostScreen, "profile-editor")
{ {
VisualEditorViewModel = visualEditorViewModel; VisualEditorViewModel = visualEditorViewModel;
ProfileTreeViewModel = profileTreeViewModel; ProfileTreeViewModel = profileTreeViewModel;
this.WhenActivated(disposables => { profileEditorService.CurrentProfileConfiguration.WhereNotNull().Subscribe(p => Profile = p).DisposeWith(disposables); });
if (OperatingSystem.IsWindows())
TitleBarViewModel = profileEditorTitleBarViewModel;
else
MenuBarViewModel = menuBarViewModel;
this.WhenActivated(disposables => profileEditorService.CurrentProfileConfiguration.WhereNotNull().Subscribe(p => Profile = p).DisposeWith(disposables));
} }
public VisualEditorViewModel VisualEditorViewModel { get; } public VisualEditorViewModel VisualEditorViewModel { get; }
public ProfileTreeViewModel ProfileTreeViewModel { get; } public ProfileTreeViewModel ProfileTreeViewModel { get; }
public MenuBarViewModel? MenuBarViewModel { get; }
public ProfileConfiguration? Profile public ProfileConfiguration? Profile
{ {

View File

@ -0,0 +1,11 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.Root.DefaultTitleBarView">
<Button Classes="icon-button icon-button-small" Command="{Binding ShowDebugger}" HorizontalAlignment="Right">
<avalonia:MaterialIcon Kind="Bug"></avalonia:MaterialIcon>
</Button>
</UserControl>

View File

@ -0,0 +1,19 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace Artemis.UI.Screens.Root
{
public partial class DefaultTitleBarView : UserControl
{
public DefaultTitleBarView()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

View File

@ -0,0 +1,20 @@
using Artemis.UI.Services.Interfaces;
using Artemis.UI.Shared;
namespace Artemis.UI.Screens.Root
{
public class DefaultTitleBarViewModel : ViewModelBase
{
private readonly IDebugService _debugService;
public DefaultTitleBarViewModel(IDebugService debugService)
{
_debugService = debugService;
}
public void ShowDebugger()
{
_debugService.ShowDebugger();
}
}
}

View File

@ -11,7 +11,7 @@
<ColumnDefinition /> <ColumnDefinition />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<ContentControl Grid.Column="0" Content="{Binding SidebarViewModel}" /> <ContentControl x:Name="SidebarContentControl" Grid.Column="0" Content="{Binding SidebarViewModel}" />
<reactiveUi:RoutedViewHost Grid.Column="1" Router="{Binding Router}"> <reactiveUi:RoutedViewHost Grid.Column="1" Router="{Binding Router}">
<reactiveUi:RoutedViewHost.PageTransition> <reactiveUi:RoutedViewHost.PageTransition>
<CrossFade Duration="0.1" /> <CrossFade Duration="0.1" />

View File

@ -1,4 +1,5 @@
using System; 
using System;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
@ -21,6 +22,7 @@ namespace Artemis.UI.Screens.Root
public class RootViewModel : ActivatableViewModelBase, IScreen, IMainWindowProvider public class RootViewModel : ActivatableViewModelBase, IScreen, IMainWindowProvider
{ {
private readonly IAssetLoader _assetLoader; private readonly IAssetLoader _assetLoader;
private readonly DefaultTitleBarViewModel _defaultTitleBarViewModel;
private readonly ICoreService _coreService; private readonly ICoreService _coreService;
private readonly IDebugService _debugService; private readonly IDebugService _debugService;
private readonly IClassicDesktopStyleApplicationLifetime _lifeTime; private readonly IClassicDesktopStyleApplicationLifetime _lifeTime;
@ -30,6 +32,7 @@ namespace Artemis.UI.Screens.Root
private SidebarViewModel? _sidebarViewModel; private SidebarViewModel? _sidebarViewModel;
private TrayIcon? _trayIcon; private TrayIcon? _trayIcon;
private TrayIcons? _trayIcons; private TrayIcons? _trayIcons;
private ViewModelBase? _titleBarViewModel;
public RootViewModel(ICoreService coreService, public RootViewModel(ICoreService coreService,
ISettingsService settingsService, ISettingsService settingsService,
@ -38,6 +41,7 @@ namespace Artemis.UI.Screens.Root
IMainWindowService mainWindowService, IMainWindowService mainWindowService,
IDebugService debugService, IDebugService debugService,
IAssetLoader assetLoader, IAssetLoader assetLoader,
DefaultTitleBarViewModel defaultTitleBarViewModel,
ISidebarVmFactory sidebarVmFactory) ISidebarVmFactory sidebarVmFactory)
{ {
Router = new RoutingState(); Router = new RoutingState();
@ -47,6 +51,7 @@ namespace Artemis.UI.Screens.Root
_windowService = windowService; _windowService = windowService;
_debugService = debugService; _debugService = debugService;
_assetLoader = assetLoader; _assetLoader = assetLoader;
_defaultTitleBarViewModel = defaultTitleBarViewModel;
_sidebarVmFactory = sidebarVmFactory; _sidebarVmFactory = sidebarVmFactory;
_lifeTime = (IClassicDesktopStyleApplicationLifetime) Application.Current.ApplicationLifetime; _lifeTime = (IClassicDesktopStyleApplicationLifetime) Application.Current.ApplicationLifetime;
@ -54,15 +59,31 @@ namespace Artemis.UI.Screens.Root
mainWindowService.ConfigureMainWindowProvider(this); mainWindowService.ConfigureMainWindowProvider(this);
DisplayAccordingToSettings(); DisplayAccordingToSettings();
Router.CurrentViewModel.Subscribe(UpdateTitleBarViewModel);
Task.Run(coreService.Initialize); Task.Run(coreService.Initialize);
} }
private void UpdateTitleBarViewModel(IRoutableViewModel? viewModel)
{
if (viewModel is MainScreenViewModel mainScreenViewModel && mainScreenViewModel.TitleBarViewModel != null)
TitleBarViewModel = mainScreenViewModel.TitleBarViewModel;
else
TitleBarViewModel = _defaultTitleBarViewModel;
}
public SidebarViewModel? SidebarViewModel public SidebarViewModel? SidebarViewModel
{ {
get => _sidebarViewModel; get => _sidebarViewModel;
set => this.RaiseAndSetIfChanged(ref _sidebarViewModel, value); set => this.RaiseAndSetIfChanged(ref _sidebarViewModel, value);
} }
public ViewModelBase? TitleBarViewModel
{
get => _titleBarViewModel;
set => this.RaiseAndSetIfChanged(ref _titleBarViewModel, value);
}
private void CurrentMainWindowOnClosed(object? sender, EventArgs e) private void CurrentMainWindowOnClosed(object? sender, EventArgs e)
{ {
_lifeTime.MainWindow = null; _lifeTime.MainWindow = null;

View File

@ -22,8 +22,7 @@ namespace Artemis.UI.Services
CurrentProfileConfiguration = Observable.Defer(() => Observable.Return(_currentProfileConfiguration)).Concat(_changeCurrentProfileConfiguration); CurrentProfileConfiguration = Observable.Defer(() => Observable.Return(_currentProfileConfiguration)).Concat(_changeCurrentProfileConfiguration);
CurrentProfileElement = Observable.Defer(() => Observable.Return(_currentProfileElement)).Concat(_changeCurrentProfileElement); CurrentProfileElement = Observable.Defer(() => Observable.Return(_currentProfileElement)).Concat(_changeCurrentProfileElement);
} }
public IObservable<ProfileConfiguration?> CurrentProfileConfiguration { get; } public IObservable<ProfileConfiguration?> CurrentProfileConfiguration { get; }
public IObservable<RenderProfileElement?> CurrentProfileElement { get; } public IObservable<RenderProfileElement?> CurrentProfileElement { get; }

View File

@ -115,6 +115,20 @@
"Material.Icons": "1.0.2" "Material.Icons": "1.0.2"
} }
}, },
"ReactiveUI": {
"type": "Direct",
"requested": "[17.1.9, )",
"resolved": "17.1.9",
"contentHash": "bxH6uzEi1b6cfGoBBvCXWrdT18+Rleggi0R/vOaCYc+zvlSleJW6wlUtcWjrKzgQHD8EN3c02A4JBTt9oGSWWQ==",
"dependencies": {
"DynamicData": "7.4.3",
"Splat": "14.1.1",
"System.ComponentModel": "4.3.0",
"System.Diagnostics.Contracts": "4.3.0",
"System.Dynamic.Runtime": "4.3.0",
"System.Runtime.Serialization.Primitives": "4.3.0"
}
},
"ReactiveUI.Validation": { "ReactiveUI.Validation": {
"type": "Direct", "type": "Direct",
"requested": "[2.2.1, )", "requested": "[2.2.1, )",
@ -268,8 +282,8 @@
}, },
"DynamicData": { "DynamicData": {
"type": "Transitive", "type": "Transitive",
"resolved": "7.3.1", "resolved": "7.4.3",
"contentHash": "E9oTvWlAgzct0MuWt6k+0s+nSDA3LkFVvDwkMUTklIZZnva314KZAEF2vG4XX9I98ia+EpMqjte67jWEBJlsRw==", "contentHash": "7eGyREbtzyaRutMa+iToi2e41JboEVK9c1ZBcTvJOfEoTRIZX3hChIsxIvV0ErzMXtGHAIS2O0I8jLDUIds5wg==",
"dependencies": { "dependencies": {
"System.Reactive": "5.0.0" "System.Reactive": "5.0.0"
} }
@ -562,15 +576,6 @@
"Ninject": "3.3.3" "Ninject": "3.3.3"
} }
}, },
"ReactiveUI": {
"type": "Transitive",
"resolved": "16.2.6",
"contentHash": "jf1RvD8HxHuA6CGQtheGHUCHzRrhpvo0z593Npsz7g8KJWXfGR45Dc9bILJHoymBxhdDD1L1WjUfh0fcucIPPg==",
"dependencies": {
"DynamicData": "7.3.1",
"Splat": "13.1.1"
}
},
"RGB.NET.Presets": { "RGB.NET.Presets": {
"type": "Transitive", "type": "Transitive",
"resolved": "1.0.0-prerelease1", "resolved": "1.0.0-prerelease1",
@ -928,6 +933,14 @@
"System.Text.Encoding": "4.3.0" "System.Text.Encoding": "4.3.0"
} }
}, },
"System.Diagnostics.Contracts": {
"type": "Transitive",
"resolved": "4.3.0",
"contentHash": "eelRRbnm+OloiQvp9CXS0ixjNQldjjkHO4iIkR5XH2VIP8sUB/SIpa1TdUW6/+HDcQ+MlhP3pNa1u5SbzYuWGA==",
"dependencies": {
"System.Runtime": "4.3.0"
}
},
"System.Diagnostics.Debug": { "System.Diagnostics.Debug": {
"type": "Transitive", "type": "Transitive",
"resolved": "4.3.0", "resolved": "4.3.0",
@ -1408,6 +1421,15 @@
"System.Runtime.Extensions": "4.3.0" "System.Runtime.Extensions": "4.3.0"
} }
}, },
"System.Runtime.Serialization.Primitives": {
"type": "Transitive",
"resolved": "4.3.0",
"contentHash": "Wz+0KOukJGAlXjtKr+5Xpuxf8+c8739RI1C+A2BoQZT+wMCCoMDDdO8/4IRHfaVINqL78GO8dW8G2lW/e45Mcw==",
"dependencies": {
"System.Resources.ResourceManager": "4.3.0",
"System.Runtime": "4.3.0"
}
},
"System.Security.AccessControl": { "System.Security.AccessControl": {
"type": "Transitive", "type": "Transitive",
"resolved": "5.0.0", "resolved": "5.0.0",
@ -1763,6 +1785,7 @@
"FluentAvaloniaUI": "1.1.8", "FluentAvaloniaUI": "1.1.8",
"Material.Icons.Avalonia": "1.0.2", "Material.Icons.Avalonia": "1.0.2",
"RGB.NET.Core": "1.0.0-prerelease1", "RGB.NET.Core": "1.0.0-prerelease1",
"ReactiveUI": "17.1.9",
"ReactiveUI.Validation": "2.2.1" "ReactiveUI.Validation": "2.2.1"
} }
} }