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

Merge branch 'feature/avalonia-preview7' into development

This commit is contained in:
Robert 2023-05-12 23:41:22 +02:00
commit f2d51b8daf
153 changed files with 848 additions and 712 deletions

View File

@ -35,7 +35,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="DryIoc.dll" Version="5.3.4" /> <PackageReference Include="DryIoc.dll" Version="5.4.0" />
<PackageReference Include="EmbedIO" Version="3.5.2" /> <PackageReference Include="EmbedIO" Version="3.5.2" />
<PackageReference Include="HidSharp" Version="2.1.0" /> <PackageReference Include="HidSharp" Version="2.1.0" />
<PackageReference Include="Humanizer.Core" Version="2.14.1" /> <PackageReference Include="Humanizer.Core" Version="2.14.1" />

View File

@ -1,6 +1,8 @@
<Application xmlns="https://github.com/avaloniaui" <Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ui="clr-namespace:Artemis.UI;assembly=Artemis.UI" xmlns:ui="clr-namespace:Artemis.UI;assembly=Artemis.UI"
xmlns:root="clr-namespace:Artemis.UI.Screens.Root;assembly=Artemis.UI"
x:DataType="root:RootViewModel"
x:Class="Artemis.UI.Linux.App"> x:Class="Artemis.UI.Linux.App">
<Application.DataTemplates> <Application.DataTemplates>
<ui:ViewLocator /> <ui:ViewLocator />
@ -12,16 +14,16 @@
<TrayIcon.Icons> <TrayIcon.Icons>
<TrayIcons> <TrayIcons>
<TrayIcon Icon="avares://Artemis.UI/Assets/Images/Logo/application.ico" ToolTipText="Artemis" Command="{Binding OpenScreen}" CommandParameter="Home"> <TrayIcon Icon="avares://Artemis.UI/Assets/Images/Logo/application.ico" ToolTipText="Artemis" Command="{CompiledBinding OpenScreen}" CommandParameter="Home">
<TrayIcon.Menu> <TrayIcon.Menu>
<NativeMenu> <NativeMenu>
<NativeMenuItem Header="Home" Command="{Binding OpenScreen}" CommandParameter="Home" /> <NativeMenuItem Header="Home" Command="{CompiledBinding OpenScreen}" CommandParameter="Home" />
<!-- <NativeMenuItem Header="Workshop" Command="{Binding OpenScreen}" CommandParameter="Workshop" /> --> <!-- <NativeMenuItem Header="Workshop" Command="{CompiledBinding OpenScreen}" CommandParameter="Workshop" /> -->
<NativeMenuItem Header="Surface Editor" Command="{Binding OpenScreen}" CommandParameter="Surface Editor" /> <NativeMenuItem Header="Surface Editor" Command="{CompiledBinding OpenScreen}" CommandParameter="Surface Editor" />
<NativeMenuItem Header="Settings" Command="{Binding OpenScreen}" CommandParameter="Settings" /> <NativeMenuItem Header="Settings" Command="{CompiledBinding OpenScreen}" CommandParameter="Settings" />
<NativeMenuItemSeparator /> <NativeMenuItemSeparator />
<NativeMenuItem Header="Debugger" Command="{Binding OpenDebugger}" /> <NativeMenuItem Header="Debugger" Command="{CompiledBinding OpenDebugger}" />
<NativeMenuItem Header="Exit" Command="{Binding Exit}" /> <NativeMenuItem Header="Exit" Command="{CompiledBinding Exit}" />
</NativeMenu> </NativeMenu>
</TrayIcon.Menu> </TrayIcon.Menu>
</TrayIcon> </TrayIcon>

View File

@ -22,18 +22,20 @@ public class App : Application
Program.CreateLogger(_container); Program.CreateLogger(_container);
RxApp.MainThreadScheduler = AvaloniaScheduler.Instance; RxApp.MainThreadScheduler = AvaloniaScheduler.Instance;
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
RegisterProviders();
} }
public override void OnFrameworkInitializationCompleted() public override void OnFrameworkInitializationCompleted()
{ {
if (Design.IsDesignMode) if (Design.IsDesignMode)
return; return;
if (ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop)
return;
ArtemisBootstrapper.Initialize(); ArtemisBootstrapper.Initialize();
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
_applicationStateManager = new ApplicationStateManager(_container!, desktop.Args); _applicationStateManager = new ApplicationStateManager(_container!, desktop.Args);
RegisterProviders();
} }
private void RegisterProviders() private void RegisterProviders()

View File

@ -16,11 +16,11 @@
<None Remove=".gitignore" /> <None Remove=".gitignore" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Avalonia" Version="11.0.0-preview6" /> <PackageReference Include="Avalonia" Version="11.0.0-preview8" />
<PackageReference Include="Avalonia.Desktop" Version="11.0.0-preview6" /> <PackageReference Include="Avalonia.Desktop" Version="11.0.0-preview8" />
<!--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="11.0.0-preview6" /> <PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.0-preview8" />
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.0-preview6" /> <PackageReference Include="Avalonia.ReactiveUI" Version="11.0.0-preview8" />
<PackageReference Include="ReactiveUI" Version="18.4.26" /> <PackageReference Include="ReactiveUI" Version="18.4.26" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -2,6 +2,7 @@
public enum LinuxDeviceType public enum LinuxDeviceType
{ {
Unknown,
Keyboard, Keyboard,
Mouse, Mouse,
Gamepad Gamepad

View File

@ -19,7 +19,7 @@ public class LinuxInputDevice
switch (dataType) switch (dataType)
{ {
case 'I': case 'I':
InputId = new LinuxInputId(data); InputId = data;
break; break;
case 'N': case 'N':
Name = data.Replace("\"", "").Replace("Name=", ""); Name = data.Replace("\"", "").Replace("Name=", "");
@ -27,14 +27,16 @@ public class LinuxInputDevice
case 'H': case 'H':
Handlers = data.Replace("Handlers=", "").Split(" "); Handlers = data.Replace("Handlers=", "").Split(" ");
if (Handlers?.Any(h => h.Contains("mouse")) == true) if (Handlers.Any(h => h.Contains("mouse")))
DeviceType = LinuxDeviceType.Mouse; DeviceType = LinuxDeviceType.Mouse;
else if (Handlers?.Any(h => h.Contains("kbd")) == true) else if (Handlers.Any(h => h.Contains("kbd")))
DeviceType = LinuxDeviceType.Keyboard; DeviceType = LinuxDeviceType.Keyboard;
else if (Handlers?.Any(h => h.Contains("js")) == true) else if (Handlers.Any(h => h.Contains("js")))
DeviceType = LinuxDeviceType.Gamepad; DeviceType = LinuxDeviceType.Gamepad;
else
DeviceType = LinuxDeviceType.Unknown;
string evt = Handlers!.First(h => h.Contains("event")); string evt = Handlers.First(h => h.Contains("event"));
EventPath = $"/dev/input/{evt}"; EventPath = $"/dev/input/{evt}";
break; break;
@ -45,7 +47,7 @@ public class LinuxInputDevice
throw new ArtemisLinuxInputProviderException("Linux device definition did not contain necessary data"); throw new ArtemisLinuxInputProviderException("Linux device definition did not contain necessary data");
} }
public LinuxInputId InputId { get; } public string InputId { get; }
public string Name { get; } public string Name { get; }
public string[] Handlers { get; } public string[] Handlers { get; }
public string EventPath { get; } public string EventPath { get; }
@ -60,29 +62,4 @@ public class LinuxInputDevice
} }
#endregion #endregion
public class LinuxInputId
{
public LinuxInputId(string line)
{
Dictionary<string, string> components = line.Split(" ")
.Select(c => c.Split('='))
.ToDictionary(c => c[0], c => c[1]);
Bus = components["Bus"];
Vendor = components["Vendor"];
Product = components["Product"];
Version = components["Version"];
}
public string Bus { get; }
public string Vendor { get; }
public string Product { get; }
public string Version { get; }
public override string ToString()
{
return $"Bus={Bus} Vendor={Vendor} Product={Product} Version={Version}";
}
}
} }

View File

@ -11,9 +11,36 @@ public static class LinuxInputDeviceFinder
public static IEnumerable<LinuxInputDevice> Find() public static IEnumerable<LinuxInputDevice> Find()
{ {
return File.ReadAllLines(DEVICES_FILE) IEnumerable<IEnumerable<string>> lineGroups = File.ReadAllLines(DEVICES_FILE).PartitionBy(s => s?.Length == 0); //split on empty lines
.PartitionBy(s => s?.Length == 0) //split on empty lines
.Select(lineGroup => new LinuxInputDevice(lineGroup)); foreach (IEnumerable<string> lineGroup in lineGroups)
{
LinuxInputDevice device;
try
{
device = new LinuxInputDevice(lineGroup);
}
catch
{
continue;
//some devices don't have all the required data, we can ignore those
}
if (ShouldReadDevice(device))
{
yield return device;
}
}
}
private static bool ShouldReadDevice(LinuxInputDevice device)
{
if (device.DeviceType == LinuxDeviceType.Unknown)
return false;
//possibly add more checks here
return true;
} }
//https://stackoverflow.com/questions/56623354 //https://stackoverflow.com/questions/56623354
@ -34,7 +61,7 @@ public static class LinuxInputDeviceFinder
return groupNumber; return groupNumber;
}; };
return a return a
.Select(x => new {Value = x, GroupNumber = getGroupNumber(predicate(x))}) .Select(x => new { Value = x, GroupNumber = getGroupNumber(predicate(x)) })
.Where(x => x.GroupNumber != null) .Where(x => x.GroupNumber != null)
.GroupBy(x => x.GroupNumber) .GroupBy(x => x.GroupNumber)
.Select(g => g.Select(x => x.Value)); .Select(g => g.Select(x => x.Value));

View File

@ -75,7 +75,7 @@ public class LinuxInputProvider : InputProvider
//_logger.Verbose($"Keyboard Key: {(LinuxKeyboardKeyCodes)args.Code} | Down: {isDown}"); //_logger.Verbose($"Keyboard Key: {(LinuxKeyboardKeyCodes)args.Code} | Down: {isDown}");
LinuxInputDevice.LinuxInputId identifier = keyboard.InputId; string identifier = keyboard.InputId;
OnIdentifierReceived(identifier, InputDeviceType.Keyboard); OnIdentifierReceived(identifier, InputDeviceType.Keyboard);
ArtemisDevice? device = null; ArtemisDevice? device = null;
@ -93,7 +93,7 @@ public class LinuxInputProvider : InputProvider
private void HandleMouseData(LinuxInputDevice mouse, LinuxInputEventArgs args) private void HandleMouseData(LinuxInputDevice mouse, LinuxInputEventArgs args)
{ {
LinuxInputDevice.LinuxInputId identifier = mouse.InputId; string identifier = mouse.InputId;
OnIdentifierReceived(identifier, InputDeviceType.Mouse); OnIdentifierReceived(identifier, InputDeviceType.Mouse);
ArtemisDevice? device = null; ArtemisDevice? device = null;

View File

@ -52,7 +52,7 @@ public static class InputUtilities
LinuxKeyboardKeyCodes.KEY_APOSTROPHE => KeyboardKey.OemQuotes, LinuxKeyboardKeyCodes.KEY_APOSTROPHE => KeyboardKey.OemQuotes,
LinuxKeyboardKeyCodes.KEY_GRAVE => KeyboardKey.OemTilde, LinuxKeyboardKeyCodes.KEY_GRAVE => KeyboardKey.OemTilde,
LinuxKeyboardKeyCodes.KEY_LEFTSHIFT => KeyboardKey.LeftShift, LinuxKeyboardKeyCodes.KEY_LEFTSHIFT => KeyboardKey.LeftShift,
LinuxKeyboardKeyCodes.KEY_BACKSLASH => KeyboardKey.OemBackslash, LinuxKeyboardKeyCodes.KEY_BACKSLASH => KeyboardKey.OemPipe,
LinuxKeyboardKeyCodes.KEY_Z => KeyboardKey.Z, LinuxKeyboardKeyCodes.KEY_Z => KeyboardKey.Z,
LinuxKeyboardKeyCodes.KEY_X => KeyboardKey.X, LinuxKeyboardKeyCodes.KEY_X => KeyboardKey.X,
LinuxKeyboardKeyCodes.KEY_C => KeyboardKey.C, LinuxKeyboardKeyCodes.KEY_C => KeyboardKey.C,
@ -94,7 +94,7 @@ public static class InputUtilities
LinuxKeyboardKeyCodes.KEY_KP0 => KeyboardKey.NumPad0, LinuxKeyboardKeyCodes.KEY_KP0 => KeyboardKey.NumPad0,
LinuxKeyboardKeyCodes.KEY_KPDOT => KeyboardKey.NumPadDecimal, LinuxKeyboardKeyCodes.KEY_KPDOT => KeyboardKey.NumPadDecimal,
// LinuxKeyboardKeyCodes.KEY_ZENKAKUHANKAKU => expr, // LinuxKeyboardKeyCodes.KEY_ZENKAKUHANKAKU => expr,
// LinuxKeyboardKeyCodes.KEY_102ND => expr, LinuxKeyboardKeyCodes.KEY_102ND => KeyboardKey.OemBackslash,
LinuxKeyboardKeyCodes.KEY_F11 => KeyboardKey.F11, LinuxKeyboardKeyCodes.KEY_F11 => KeyboardKey.F11,
LinuxKeyboardKeyCodes.KEY_F12 => KeyboardKey.F12, LinuxKeyboardKeyCodes.KEY_F12 => KeyboardKey.F12,
//LinuxKeyboardKeyCodes.KEY_RO => expr, //LinuxKeyboardKeyCodes.KEY_RO => expr,

View File

@ -1,6 +1,8 @@
<Application xmlns="https://github.com/avaloniaui" <Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ui="clr-namespace:Artemis.UI;assembly=Artemis.UI" xmlns:ui="clr-namespace:Artemis.UI;assembly=Artemis.UI"
xmlns:root="clr-namespace:Artemis.UI.Screens.Root;assembly=Artemis.UI"
x:DataType="root:RootViewModel"
x:Class="Artemis.UI.MacOS.App"> x:Class="Artemis.UI.MacOS.App">
<Application.DataTemplates> <Application.DataTemplates>
<ui:ViewLocator /> <ui:ViewLocator />
@ -12,16 +14,16 @@
<TrayIcon.Icons> <TrayIcon.Icons>
<TrayIcons> <TrayIcons>
<TrayIcon Icon="avares://Artemis.UI/Assets/Images/Logo/application.ico" ToolTipText="Artemis" Command="{Binding OpenScreen}" CommandParameter="Home"> <TrayIcon Icon="avares://Artemis.UI/Assets/Images/Logo/application.ico" ToolTipText="Artemis" Command="{CompiledBinding OpenScreen}" CommandParameter="Home">
<TrayIcon.Menu> <TrayIcon.Menu>
<NativeMenu> <NativeMenu>
<NativeMenuItem Header="Home" Command="{Binding OpenScreen}" CommandParameter="Home" /> <NativeMenuItem Header="Home" Command="{CompiledBinding OpenScreen}" CommandParameter="Home" />
<!-- <NativeMenuItem Header="Workshop" Command="{Binding OpenScreen}" CommandParameter="Workshop" /> --> <!-- <NativeMenuItem Header="Workshop" Command="{CompiledBinding OpenScreen}" CommandParameter="Workshop" /> -->
<NativeMenuItem Header="Surface Editor" Command="{Binding OpenScreen}" CommandParameter="Surface Editor" /> <NativeMenuItem Header="Surface Editor" Command="{CompiledBinding OpenScreen}" CommandParameter="Surface Editor" />
<NativeMenuItem Header="Settings" Command="{Binding OpenScreen}" CommandParameter="Settings" /> <NativeMenuItem Header="Settings" Command="{CompiledBinding OpenScreen}" CommandParameter="Settings" />
<NativeMenuItemSeparator /> <NativeMenuItemSeparator />
<NativeMenuItem Header="Debugger" Command="{Binding OpenDebugger}" /> <NativeMenuItem Header="Debugger" Command="{CompiledBinding OpenDebugger}" />
<NativeMenuItem Header="Exit" Command="{Binding Exit}" /> <NativeMenuItem Header="Exit" Command="{CompiledBinding Exit}" />
</NativeMenu> </NativeMenu>
</TrayIcon.Menu> </TrayIcon.Menu>
</TrayIcon> </TrayIcon>

View File

@ -15,11 +15,11 @@
<None Remove=".gitignore" /> <None Remove=".gitignore" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Avalonia" Version="11.0.0-preview6" /> <PackageReference Include="Avalonia" Version="11.0.0-preview8" />
<PackageReference Include="Avalonia.Desktop" Version="11.0.0-preview6" /> <PackageReference Include="Avalonia.Desktop" Version="11.0.0-preview8" />
<!--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="11.0.0-preview6" /> <PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.0-preview8" />
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.0-preview6" /> <PackageReference Include="Avalonia.ReactiveUI" Version="11.0.0-preview8" />
<PackageReference Include="ReactiveUI" Version="18.4.26" /> <PackageReference Include="ReactiveUI" Version="18.4.26" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -10,13 +10,13 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Avalonia" Version="11.0.0-preview6" /> <PackageReference Include="Avalonia" Version="11.0.0-preview8" />
<!--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="11.0.0-preview6" /> <PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.0-preview8" />
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.0-preview6" /> <PackageReference Include="Avalonia.ReactiveUI" Version="11.0.0-preview8" />
<PackageReference Include="Avalonia.Xaml.Behaviors" Version="11.0.0-preview6" /> <PackageReference Include="Avalonia.Xaml.Behaviors" Version="11.0.0-preview8" />
<PackageReference Include="DynamicData" Version="7.13.1" /> <PackageReference Include="DynamicData" Version="7.13.1" />
<PackageReference Include="FluentAvaloniaUI" Version="2.0.0-preview6" /> <PackageReference Include="FluentAvaloniaUI" Version="2.0.0-preview8" />
<PackageReference Include="Material.Icons.Avalonia" Version="2.0.0-preview3" /> <PackageReference Include="Material.Icons.Avalonia" Version="2.0.0-preview3" />
<PackageReference Include="ReactiveUI" Version="18.4.26" /> <PackageReference Include="ReactiveUI" Version="18.4.26" />
<PackageReference Include="ReactiveUI.Validation" Version="3.1.7" /> <PackageReference Include="ReactiveUI.Validation" Version="3.1.7" />

View File

@ -64,7 +64,7 @@ public partial class ArtemisIcon : UserControl
Background = TextElement.GetForeground(this), Background = TextElement.GetForeground(this),
VerticalAlignment = VerticalAlignment.Stretch, VerticalAlignment = VerticalAlignment.Stretch,
HorizontalAlignment = HorizontalAlignment.Stretch, HorizontalAlignment = HorizontalAlignment.Stretch,
OpacityMask = new ImageBrush(new Bitmap(iconString)) {BitmapInterpolationMode = BitmapInterpolationMode.MediumQuality} OpacityMask = new ImageBrush(new Bitmap(iconString))
}; };
} }
else else

View File

@ -308,7 +308,7 @@ public class DataModelPicker : TemplatedControl
{ {
GetDataModel(); GetDataModel();
UpdateCurrentPath(true); UpdateCurrentPath(true);
_updateTimer = new DispatcherTimer(TimeSpan.FromMilliseconds(200), DispatcherPriority.Normal, Update); _updateTimer = new DispatcherTimer(TimeSpan.FromMilliseconds(200), DispatcherPriority.Background, Update);
_updateTimer.Start(); _updateTimer.Start();
} }

View File

@ -41,7 +41,6 @@ public class DeviceVisualizer : Control
PropertyChanged += OnPropertyChanged; PropertyChanged += OnPropertyChanged;
} }
/// <inheritdoc /> /// <inheritdoc />
public override void Render(DrawingContext drawingContext) public override void Render(DrawingContext drawingContext)
{ {
@ -67,8 +66,7 @@ public class DeviceVisualizer : Control
drawingContext.DrawImage( drawingContext.DrawImage(
_deviceImage, _deviceImage,
new Rect(_deviceImage.Size), new Rect(_deviceImage.Size),
new Rect(0, 0, Device.RgbDevice.ActualSize.Width, Device.RgbDevice.ActualSize.Height), new Rect(0, 0, Device.RgbDevice.ActualSize.Width, Device.RgbDevice.ActualSize.Height)
RenderOptions.GetBitmapInterpolationMode(this)
); );
if (!ShowColors) if (!ShowColors)
@ -306,8 +304,9 @@ public class DeviceVisualizer : Control
using DrawingContext context = renderTargetBitmap.CreateDrawingContext(); using DrawingContext context = renderTargetBitmap.CreateDrawingContext();
using Bitmap bitmap = new(device.Layout.Image.LocalPath); using Bitmap bitmap = new(device.Layout.Image.LocalPath);
context.DrawImage(bitmap, new Rect(bitmap.Size), new Rect(renderTargetBitmap.Size), BitmapInterpolationMode.HighQuality); using Bitmap scaledBitmap = bitmap.CreateScaledBitmap(renderTargetBitmap.PixelSize);
context.DrawImage(scaledBitmap, new Rect(scaledBitmap.Size));
lock (_deviceVisualizerLeds) lock (_deviceVisualizerLeds)
{ {
foreach (DeviceVisualizerLed deviceVisualizerLed in _deviceVisualizerLeds) foreach (DeviceVisualizerLed deviceVisualizerLed in _deviceVisualizerLeds)

View File

@ -46,11 +46,10 @@ internal class DeviceVisualizerLed
try try
{ {
using Bitmap bitmap = new(Led.Layout.Image.LocalPath); using Bitmap bitmap = new(Led.Layout.Image.LocalPath);
using Bitmap scaledBitmap = bitmap.CreateScaledBitmap(new PixelSize((Led.RgbLed.Size.Width * scale).RoundToInt(), (Led.RgbLed.Size.Height * scale).RoundToInt()));
drawingContext.DrawImage( drawingContext.DrawImage(
bitmap, scaledBitmap,
new Rect(bitmap.Size), new Rect(Led.RgbLed.Location.X * scale, Led.RgbLed.Location.Y * scale, scaledBitmap.Size.Width, scaledBitmap.Size.Height)
new Rect(Led.RgbLed.Location.X * scale, Led.RgbLed.Location.Y * scale, Led.RgbLed.Size.Width * scale, Led.RgbLed.Size.Height * scale),
BitmapInterpolationMode.HighQuality
); );
} }
catch catch

View File

@ -1,4 +1,4 @@
<UserControl xmlns="https://github.com/avaloniaui" <UserControl 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"
@ -25,12 +25,14 @@
<Panel> <Panel>
<controls:NumberBox Name="InnerNumberBox" <controls:NumberBox Name="InnerNumberBox"
AcceptsExpression="True" AcceptsExpression="True"
LargeChange="{Binding $parent[sharedControls:DraggableNumberBox].LargeChange}" LargeChange="{CompiledBinding $parent[sharedControls:DraggableNumberBox].LargeChange}"
SmallChange="{Binding $parent[sharedControls:DraggableNumberBox].SmallChange}" SmallChange="{CompiledBinding $parent[sharedControls:DraggableNumberBox].SmallChange}"
SimpleNumberFormat="{Binding $parent[sharedControls:DraggableNumberBox].SimpleNumberFormat}" Minimum="{CompiledBinding $parent[sharedControls:DraggableNumberBox].Minimum}"
attachedProperties:NumberBoxAssist.PrefixText="{Binding $parent[sharedControls:DraggableNumberBox].Prefix}" Maximum="{CompiledBinding $parent[sharedControls:DraggableNumberBox].Maximum}"
attachedProperties:NumberBoxAssist.SuffixText="{Binding $parent[sharedControls:DraggableNumberBox].Suffix}" SimpleNumberFormat="{CompiledBinding $parent[sharedControls:DraggableNumberBox].SimpleNumberFormat}"
HorizontalAlignment="{Binding $parent[sharedControls:DraggableNumberBox].HorizontalAlignment}" attachedProperties:NumberBoxAssist.PrefixText="{CompiledBinding $parent[sharedControls:DraggableNumberBox].Prefix}"
attachedProperties:NumberBoxAssist.SuffixText="{CompiledBinding $parent[sharedControls:DraggableNumberBox].Suffix}"
HorizontalAlignment="{CompiledBinding $parent[sharedControls:DraggableNumberBox].HorizontalAlignment}"
ValueChanged="NumberBox_OnValueChanged"/> ValueChanged="NumberBox_OnValueChanged"/>
<Rectangle Name="DragCollider" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Fill="Transparent"></Rectangle> <Rectangle Name="DragCollider" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Fill="Transparent"></Rectangle>
</Panel> </Panel>

View File

@ -3,11 +3,12 @@
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"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
xmlns:local="clr-namespace:Artemis.UI.Shared"
x:Class="Artemis.UI.Shared.EnumComboBox"> x:Class="Artemis.UI.Shared.EnumComboBox">
<ComboBox x:Name="ChildEnumComboBox" HorizontalAlignment="Stretch"> <ComboBox x:Name="ChildEnumComboBox" HorizontalAlignment="Stretch">
<ComboBox.ItemTemplate> <ComboBox.ItemTemplate>
<DataTemplate> <DataTemplate x:DataType="local:EnumComboBoxItem">
<TextBlock Text="{Binding [1]}" /> <TextBlock Text="{CompiledBinding Value}" />
</DataTemplate> </DataTemplate>
</ComboBox.ItemTemplate> </ComboBox.ItemTemplate>
</ComboBox> </ComboBox>

View File

@ -20,11 +20,9 @@ public partial class EnumComboBox : UserControl
/// </summary> /// </summary>
public static readonly StyledProperty<object?> ValueProperty = AvaloniaProperty.Register<EnumComboBox, object?>(nameof(Value), defaultBindingMode: BindingMode.TwoWay); public static readonly StyledProperty<object?> ValueProperty = AvaloniaProperty.Register<EnumComboBox, object?>(nameof(Value), defaultBindingMode: BindingMode.TwoWay);
private readonly ObservableCollection<(Enum, string)> _currentValues = new(); private readonly ObservableCollection<EnumComboBoxItem> _currentValues = new();
private Type? _currentType; private Type? _currentType;
private ComboBox? _enumComboBox;
/// <summary> /// <summary>
/// Creates a new instance of the <see cref="EnumComboBox" /> class. /// Creates a new instance of the <see cref="EnumComboBox" /> class.
/// </summary> /// </summary>
@ -54,35 +52,35 @@ public partial class EnumComboBox : UserControl
private void OnSelectionChanged(object? sender, SelectionChangedEventArgs e) private void OnSelectionChanged(object? sender, SelectionChangedEventArgs e)
{ {
if (_enumComboBox == null || _enumComboBox.SelectedIndex == -1) if (ChildEnumComboBox == null || ChildEnumComboBox.SelectedIndex == -1)
return; return;
(Enum enumValue, _) = _currentValues[_enumComboBox.SelectedIndex]; EnumComboBoxItem v = _currentValues[ChildEnumComboBox.SelectedIndex];
if (!Equals(Value, enumValue)) if (!Equals(Value, v.Value))
Value = enumValue; Value = v.Value;
} }
private void UpdateValues() private void UpdateValues()
{ {
Type? newType = Value?.GetType(); Type? newType = Value?.GetType();
if (_enumComboBox == null || newType == null || _currentType == newType) if (ChildEnumComboBox == null || newType == null || _currentType == newType)
return; return;
_currentValues.Clear(); _currentValues.Clear();
foreach ((Enum, string) valueDesc in EnumUtilities.GetAllValuesAndDescriptions(newType)) foreach ((Enum, string) valueDesc in EnumUtilities.GetAllValuesAndDescriptions(newType))
_currentValues.Add(valueDesc); _currentValues.Add(new EnumComboBoxItem(value: valueDesc.Item1, description: valueDesc.Item2));
_currentType = newType; _currentType = newType;
} }
private void UpdateSelection() private void UpdateSelection()
{ {
if (_enumComboBox == null || Value is not Enum) if (ChildEnumComboBox == null || Value is not Enum)
return; return;
(Enum, string) value = _currentValues.FirstOrDefault(v => v.Item1.Equals(Value)); EnumComboBoxItem? value = _currentValues.FirstOrDefault(v => v.Value.Equals(Value));
if (!Equals(value.Item1, _enumComboBox.SelectedItem)) if (!Equals(value?.Value, ChildEnumComboBox.SelectedItem))
_enumComboBox.SelectedItem = value; ChildEnumComboBox.SelectedItem = value;
} }
#region Overrides of TemplatedControl #region Overrides of TemplatedControl
@ -90,12 +88,11 @@ public partial class EnumComboBox : UserControl
/// <inheritdoc /> /// <inheritdoc />
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{ {
_enumComboBox = this.Get<ComboBox>("ChildEnumComboBox"); ChildEnumComboBox.ItemsSource = _currentValues;
_enumComboBox.Items = _currentValues;
UpdateValues(); UpdateValues();
UpdateSelection(); UpdateSelection();
_enumComboBox.SelectionChanged += OnSelectionChanged; ChildEnumComboBox.SelectionChanged += OnSelectionChanged;
base.OnAttachedToLogicalTree(e); base.OnAttachedToLogicalTree(e);
} }
@ -103,11 +100,36 @@ public partial class EnumComboBox : UserControl
/// <inheritdoc /> /// <inheritdoc />
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e) protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{ {
if (_enumComboBox != null) if (ChildEnumComboBox != null)
_enumComboBox.SelectionChanged -= OnSelectionChanged; ChildEnumComboBox.SelectionChanged -= OnSelectionChanged;
base.OnDetachedFromLogicalTree(e); base.OnDetachedFromLogicalTree(e);
} }
#endregion #endregion
}
/// <summary>
/// Represents an item in the <see cref="EnumComboBox" />
/// </summary>
public class EnumComboBoxItem
{
/// <summary>
/// Creates a new instance of the <see cref="EnumComboBoxItem" /> class.
/// </summary>
public EnumComboBoxItem(Enum value, string description)
{
Value = value;
Description = description;
}
/// <summary>
/// Gets or sets the value of the item
/// </summary>
public Enum Value { get; set; }
/// <summary>
/// Gets or sets the description of the item
/// </summary>
public string Description { get; set; }
} }

View File

@ -26,8 +26,8 @@
</UserControl.Styles> </UserControl.Styles>
<shared:NoInputTextBox x:Name="DisplayTextBox" <shared:NoInputTextBox x:Name="DisplayTextBox"
Watermark="{Binding $parent.Watermark}" Watermark="{CompiledBinding $parent.Watermark}"
UseFloatingWatermark="{Binding $parent.UseFloatingWatermark}" UseFloatingWatermark="{CompiledBinding $parent.UseFloatingWatermark}"
Classes="clearButton" Classes="clearButton"
IsReadOnly="True" IsReadOnly="True"
HorizontalAlignment="Stretch" /> HorizontalAlignment="Stretch" />

View File

@ -35,6 +35,7 @@ public partial class HotkeyBox : UserControl
UpdateDisplayTextBox(); UpdateDisplayTextBox();
} }
/// <inheritdoc />
protected override void OnGotFocus(GotFocusEventArgs e) protected override void OnGotFocus(GotFocusEventArgs e)
{ {
_inputService.KeyboardKeyDown += InputServiceOnKeyboardKeyDown; _inputService.KeyboardKeyDown += InputServiceOnKeyboardKeyDown;
@ -43,6 +44,7 @@ public partial class HotkeyBox : UserControl
base.OnGotFocus(e); base.OnGotFocus(e);
} }
/// <inheritdoc />
protected override void OnLostFocus(RoutedEventArgs e) protected override void OnLostFocus(RoutedEventArgs e)
{ {
_inputService.KeyboardKeyDown -= InputServiceOnKeyboardKeyDown; _inputService.KeyboardKeyDown -= InputServiceOnKeyboardKeyDown;

View File

@ -75,7 +75,7 @@ public partial class ProfileConfigurationIcon : UserControl, IDisposable
Background = TextElement.GetForeground(this), Background = TextElement.GetForeground(this),
VerticalAlignment = VerticalAlignment.Stretch, VerticalAlignment = VerticalAlignment.Stretch,
HorizontalAlignment = HorizontalAlignment.Stretch, HorizontalAlignment = HorizontalAlignment.Stretch,
OpacityMask = new ImageBrush(new Bitmap(stream)) {BitmapInterpolationMode = BitmapInterpolationMode.MediumQuality} OpacityMask = new ImageBrush(new Bitmap(stream))
}; };
} }

View File

@ -37,8 +37,8 @@ public abstract class DataModelVisualizationViewModel : ReactiveObject, IDisposa
CopyPath = ReactiveCommand.CreateFromTask(async () => CopyPath = ReactiveCommand.CreateFromTask(async () =>
{ {
if (Application.Current?.Clipboard != null && Path != null) if (Path != null)
await Application.Current.Clipboard.SetTextAsync(Path); await UI.Clipboard.SetTextAsync(Path);
}); });
if (parent == null) if (parent == null)

View File

@ -3,22 +3,24 @@
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"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
xmlns:local="clr-namespace:Artemis.UI.Shared.DefaultTypes.DataModel.Display"
x:DataType="local:DefaultDataModelDisplayViewModel"
x:Class="Artemis.UI.Shared.DefaultTypes.DataModel.Display.DefaultDataModelDisplayView"> x:Class="Artemis.UI.Shared.DefaultTypes.DataModel.Display.DefaultDataModelDisplayView">
<Grid ColumnDefinitions="*,Auto,Auto"> <Grid ColumnDefinitions="*,Auto,Auto">
<!-- Prefix --> <!-- Prefix -->
<TextBlock Grid.Column="0" <TextBlock Grid.Column="0"
Text="{Binding PropertyDescription.Prefix}" Text="{CompiledBinding PropertyDescription.Prefix}"
IsVisible="{Binding PropertyDescription.Prefix, Converter={x:Static ObjectConverters.IsNotNull}}" IsVisible="{CompiledBinding PropertyDescription.Prefix, Converter={x:Static ObjectConverters.IsNotNull}}"
TextAlignment="Right" TextAlignment="Right"
Margin="0 0 5 0" /> Margin="0 0 5 0" />
<!-- Value --> <!-- Value -->
<TextBlock Grid.Column="1" Text="{Binding Display, Mode=OneWay}" HorizontalAlignment="Right"/> <TextBlock Grid.Column="1" Text="{CompiledBinding Display, Mode=OneWay}" HorizontalAlignment="Right"/>
<!-- Affix --> <!-- Affix -->
<TextBlock Grid.Column="2" <TextBlock Grid.Column="2"
Text="{Binding PropertyDescription.Affix}" Text="{CompiledBinding PropertyDescription.Affix}"
IsVisible="{Binding PropertyDescription.Affix, Converter={x:Static ObjectConverters.IsNotNull}}" IsVisible="{CompiledBinding PropertyDescription.Affix, Converter={x:Static ObjectConverters.IsNotNull}}"
Margin="5 0 0 0" /> Margin="5 0 0 0" />
</Grid> </Grid>
</UserControl> </UserControl>

View File

@ -16,7 +16,7 @@ public static class ContainerExtensions
public static void RegisterSharedUI(this IContainer container) public static void RegisterSharedUI(this IContainer container)
{ {
Assembly artemisShared = typeof(IArtemisSharedUIService).GetAssembly(); Assembly artemisShared = typeof(IArtemisSharedUIService).GetAssembly();
container.RegisterMany(new[] { artemisShared }, type => type.IsAssignableTo<IArtemisSharedUIService>(), Reuse.Singleton); container.RegisterMany(new[] {artemisShared}, type => type.IsAssignableTo<IArtemisSharedUIService>(), Reuse.Singleton);
UI.Locator = container; UI.Locator = container;
} }

View File

@ -26,7 +26,8 @@ public class NotificationBuilder
public NotificationBuilder(Window parent) public NotificationBuilder(Window parent)
{ {
_parent = parent; _parent = parent;
_infoBar = new InfoBar {Classes = Classes.Parse("notification-info-bar")}; _infoBar = new InfoBar();
_infoBar.Classes.Add("notification-info-bar");
} }
/// <summary> /// <summary>
@ -204,11 +205,18 @@ public class NotificationButtonBuilder
internal Control Build() internal Control Build()
{ {
Button button = new() {Content = _text};
button.Classes.Add("AppBarButton");
if (_action != null) if (_action != null)
return new Button {Content = _text, Command = ReactiveCommand.Create(() => _action()), Classes = new Classes("AppBarButton")}; button.Command = ReactiveCommand.Create(() => _action());
if (_command != null) else if (_command != null)
return new Button {Content = _text, Command = _command, CommandParameter = _commandParameter, Classes = new Classes("AppBarButton")}; {
return new Button {Content = _text, Classes = new Classes("AppBarButton")}; button.Command = _command;
button.CommandParameter = _commandParameter;
}
return button;
} }
} }

View File

@ -54,7 +54,7 @@ internal class PropertyInputService : IPropertyInputService
return existing; return existing;
} }
_container.Register(viewModelType); _container.Register(viewModelType, setup: Setup.With(preventDisposal: true));
PropertyInputRegistration registration = new(this, plugin, supportedType, viewModelType); PropertyInputRegistration registration = new(this, plugin, supportedType, viewModelType);
_registeredPropertyEditors.Add(registration); _registeredPropertyEditors.Add(registration);
return registration; return registration;

View File

@ -220,7 +220,7 @@ public abstract class PropertyInputViewModel<T> : PropertyInputViewModel
/// <summary> /// <summary>
/// For internal use only, implement <see cref="PropertyInputViewModel" /> instead. /// For internal use only, implement <see cref="PropertyInputViewModel" /> instead.
/// </summary> /// </summary>
public abstract class PropertyInputViewModel : ReactiveValidationObject, IActivatableViewModel, IDisposable public abstract class PropertyInputViewModel : ReactiveValidationObject, IActivatableViewModel
{ {
/// <summary> /// <summary>
/// Prevents this type being implemented directly, implement /// Prevents this type being implemented directly, implement
@ -228,29 +228,7 @@ public abstract class PropertyInputViewModel : ReactiveValidationObject, IActiva
/// </summary> /// </summary>
// ReSharper disable once UnusedMember.Global // ReSharper disable once UnusedMember.Global
internal abstract object InternalGuard { get; } internal abstract object InternalGuard { get; }
/// <summary>
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
/// </summary>
/// <param name="disposing">
/// <see langword="true" /> to release both managed and unmanaged resources;
/// <see langword="false" /> to release only unmanaged resources.
/// </param>
protected virtual void Dispose(bool disposing)
{
}
#region Implementation of IActivatableViewModel
/// <inheritdoc /> /// <inheritdoc />
public ViewModelActivator Activator { get; } = new(); public ViewModelActivator Activator { get; } = new();
#endregion
/// <inheritdoc />
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
} }

View File

@ -2,9 +2,11 @@
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:local="clr-namespace:Artemis.UI.Shared.Services"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="800" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="800"
x:Class="Artemis.UI.Shared.Services.ExceptionDialogView" x:Class="Artemis.UI.Shared.Services.ExceptionDialogView"
Title="{Binding Title}" x:DataType="local:ExceptionDialogViewModel"
Title="{CompiledBinding Title}"
ExtendClientAreaToDecorationsHint="True" ExtendClientAreaToDecorationsHint="True"
Width="800" Width="800"
Height="800" Height="800"
@ -16,7 +18,7 @@
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<TextBlock Margin="10" Grid.Row="1" IsHitTestVisible="False" Text="{Binding Title}" /> <TextBlock Margin="10" Grid.Row="1" IsHitTestVisible="False" Text="{CompiledBinding Title}" />
<StackPanel Grid.Row="2" Margin="20"> <StackPanel Grid.Row="2" Margin="20">
<TextBlock Classes="h3">Awww :(</TextBlock> <TextBlock Classes="h3">Awww :(</TextBlock>
@ -27,7 +29,7 @@
<Grid Grid.Row="3" ColumnDefinitions="*,Auto" RowDefinitions="*,Auto"> <Grid Grid.Row="3" ColumnDefinitions="*,Auto" RowDefinitions="*,Auto">
<ScrollViewer Grid.Row="0" Grid.ColumnSpan="2" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Margin="20 0"> <ScrollViewer Grid.Row="0" Grid.ColumnSpan="2" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Margin="20 0">
<TextBox Text="{Binding Exception, Mode=OneTime}" <TextBox Text="{CompiledBinding Exception, Mode=OneTime}"
AcceptsReturn="True" AcceptsReturn="True"
IsReadOnly="True" IsReadOnly="True"
FontFamily="Consolas" FontFamily="Consolas"
@ -39,10 +41,10 @@
When reporting errors please don't take a screenshot of the error, instead copy the text, thanks! When reporting errors please don't take a screenshot of the error, instead copy the text, thanks!
</TextBlock> </TextBlock>
<StackPanel Grid.Row="1" Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right" Margin="15"> <StackPanel Grid.Row="1" Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right" Margin="15">
<Button Command="{Binding CopyException}" Classes="AppBarButton" Width="150" Margin="0 0 5 0"> <Button Command="{CompiledBinding CopyException}" Classes="AppBarButton" Width="150" Margin="0 0 5 0">
Copy exception Copy exception
</Button> </Button>
<Button Command="{Binding Close}" Width="150" Margin="5 0 0 0"> <Button Command="{CompiledBinding Close}" Width="150" Margin="5 0 0 0">
Close Close
</Button> </Button>
</StackPanel> </StackPanel>

View File

@ -23,10 +23,7 @@ internal class ExceptionDialogViewModel : DialogViewModelBase<object>
public async Task CopyException() public async Task CopyException()
{ {
if (Application.Current?.Clipboard == null) await UI.Clipboard.SetTextAsync(Exception.ToString());
return;
await Application.Current.Clipboard.SetTextAsync(Exception.ToString());
_notificationService.CreateNotification() _notificationService.CreateNotification()
.WithMessage("Copied stack trace to clipboard.") .WithMessage("Copied stack trace to clipboard.")
.WithSeverity(NotificationSeverity.Success) .WithSeverity(NotificationSeverity.Success)

View File

@ -19,7 +19,7 @@
<Grid ColumnDefinitions="Auto,*" <Grid ColumnDefinitions="Auto,*"
RowDefinitions="*" RowDefinitions="*"
MinHeight="38" MinHeight="38"
IsVisible="{Binding DataModelPath, RelativeSource={RelativeSource TemplatedParent}, Converter={x:Static ObjectConverters.IsNotNull}}"> IsVisible="{CompiledBinding DataModelPath, RelativeSource={RelativeSource TemplatedParent}, Converter={x:Static ObjectConverters.IsNotNull}}">
<avalonia:MaterialIcon Grid.Column="0" <avalonia:MaterialIcon Grid.Column="0"
Grid.Row="0" Grid.Row="0"
Name="CurrentPathIcon" Name="CurrentPathIcon"
@ -27,7 +27,7 @@
Height="22" Height="22"
Width="22" Width="22"
Margin="5 0 15 0" Margin="5 0 15 0"
IsVisible="{Binding !IsEventPicker, RelativeSource={RelativeSource TemplatedParent}}"/> IsVisible="{CompiledBinding !IsEventPicker, RelativeSource={RelativeSource TemplatedParent}}"/>
<avalonia:MaterialIcon Grid.Column="0" <avalonia:MaterialIcon Grid.Column="0"
Grid.Row="0" Grid.Row="0"
Name="EventIcon" Name="EventIcon"
@ -35,14 +35,14 @@
Height="22" Height="22"
Width="22" Width="22"
Margin="5 0 15 0" Margin="5 0 15 0"
IsVisible="{Binding IsEventPicker, RelativeSource={RelativeSource TemplatedParent}}"/> IsVisible="{CompiledBinding IsEventPicker, RelativeSource={RelativeSource TemplatedParent}}"/>
<StackPanel Grid.Column="1" Grid.Row="0" VerticalAlignment="Center"> <StackPanel Grid.Column="1" Grid.Row="0" VerticalAlignment="Center">
<TextBlock Name="CurrentPathDisplay" Classes="BodyStrongTextBlockStyle" MaxHeight="50" /> <TextBlock Name="CurrentPathDisplay" Classes="BodyStrongTextBlockStyle" MaxHeight="50" />
<TextBlock Name="CurrentPathDescription" Classes="BodyTextBlockStyle" Foreground="{DynamicResource TextFillColorSecondary}" MaxHeight="50" /> <TextBlock Name="CurrentPathDescription" Classes="BodyTextBlockStyle" Foreground="{DynamicResource TextFillColorSecondary}" MaxHeight="50" />
</StackPanel> </StackPanel>
</Grid> </Grid>
<Grid MinHeight="38" <Grid MinHeight="38"
IsVisible="{Binding DataModelPath, RelativeSource={RelativeSource TemplatedParent}, Converter={x:Static ObjectConverters.IsNull}}" ColumnDefinitions="*,Auto" IsVisible="{CompiledBinding DataModelPath, RelativeSource={RelativeSource TemplatedParent}, Converter={x:Static ObjectConverters.IsNull}}" ColumnDefinitions="*,Auto"
RowDefinitions="*,*"> RowDefinitions="*,*">
<TextBlock Grid.Column="0" Grid.Row="0" Classes="BodyStrongTextBlockStyle">Welcome to the data model picker</TextBlock> <TextBlock Grid.Column="0" Grid.Row="0" Classes="BodyStrongTextBlockStyle">Welcome to the data model picker</TextBlock>
<TextBlock Grid.Column="0" Grid.Row="1" Foreground="{DynamicResource TextFillColorSecondary}">Select a value from the data model below</TextBlock> <TextBlock Grid.Column="0" Grid.Row="1" Foreground="{DynamicResource TextFillColorSecondary}">Select a value from the data model below</TextBlock>
@ -53,19 +53,19 @@
<TreeView Grid.Row="2" <TreeView Grid.Row="2"
Name="DataModelTreeView" Name="DataModelTreeView"
Items="{Binding DataModelViewModel.Children, RelativeSource={RelativeSource TemplatedParent}}"> ItemsSource="{CompiledBinding DataModelViewModel.Children, RelativeSource={RelativeSource TemplatedParent}}">
<TreeView.Styles> <TreeView.Styles>
<Style Selector="TreeViewItem"> <Style Selector="TreeViewItem">
<Setter Property="IsExpanded" Value="{Binding IsVisualizationExpanded, Mode=TwoWay}" /> <Setter Property="IsExpanded" Value="{CompiledBinding IsVisualizationExpanded, Mode=TwoWay, DataType=dataModel:DataModelVisualizationViewModel}" />
<Setter Property="IsEnabled" Value="{Binding IsMatchingFilteredTypes, Mode=OneWay}" /> <Setter Property="IsEnabled" Value="{CompiledBinding IsMatchingFilteredTypes, Mode=OneWay, DataType=dataModel:DataModelVisualizationViewModel}" />
</Style> </Style>
</TreeView.Styles> </TreeView.Styles>
<TreeView.DataTemplates> <TreeView.DataTemplates>
<TreeDataTemplate DataType="{x:Type dataModel:DataModelPropertiesViewModel}" ItemsSource="{Binding Children}"> <TreeDataTemplate DataType="{x:Type dataModel:DataModelPropertiesViewModel}" ItemsSource="{CompiledBinding Children}">
<Grid ColumnDefinitions="Auto,*"> <Grid ColumnDefinitions="Auto,*">
<TextBlock Grid.Column="0" Text="{Binding PropertyDescription.Name}" ToolTip.Tip="{Binding PropertyDescription.Description}" /> <TextBlock Grid.Column="0" Text="{CompiledBinding PropertyDescription.Name}" ToolTip.Tip="{CompiledBinding PropertyDescription.Description}" />
<TextBlock Grid.Column="1" <TextBlock Grid.Column="1"
Text="{Binding DisplayValue}" Text="{CompiledBinding DisplayValue}"
FontFamily="Consolas" FontFamily="Consolas"
HorizontalAlignment="Right" HorizontalAlignment="Right"
Margin="0 0 10 0" /> Margin="0 0 10 0" />
@ -75,29 +75,29 @@
<TreeDataTemplate DataType="{x:Type dataModel:DataModelPropertyViewModel}"> <TreeDataTemplate DataType="{x:Type dataModel:DataModelPropertyViewModel}">
<Grid ColumnDefinitions="Auto,*"> <Grid ColumnDefinitions="Auto,*">
<StackPanel Grid.Column="0" Orientation="Horizontal"> <StackPanel Grid.Column="0" Orientation="Horizontal">
<TextBlock Text="{Binding PropertyDescription.Name}" ToolTip.Tip="{Binding PropertyDescription.Description}" /> <TextBlock Text="{CompiledBinding PropertyDescription.Name}" ToolTip.Tip="{CompiledBinding PropertyDescription.Description}" />
<TextBlock Text=" changed" <TextBlock Text=" changed"
ToolTip.Tip="{Binding PropertyDescription.Description}" ToolTip.Tip="{CompiledBinding PropertyDescription.Description}"
IsVisible="{Binding IsEventPicker, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type dataModelPicker:DataModelPicker}}}"/> IsVisible="{CompiledBinding IsEventPicker, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type dataModelPicker:DataModelPicker}}}"/>
</StackPanel> </StackPanel>
<ContentControl Grid.Column="1" Content="{Binding DisplayViewModel}" FontFamily="Consolas" Margin="0 0 10 0" /> <ContentControl Grid.Column="1" Content="{CompiledBinding DisplayViewModel}" FontFamily="Consolas" Margin="0 0 10 0" />
</Grid> </Grid>
</TreeDataTemplate> </TreeDataTemplate>
<TreeDataTemplate DataType="{x:Type dataModel:DataModelListViewModel}"> <TreeDataTemplate DataType="{x:Type dataModel:DataModelListViewModel}">
<Grid ColumnDefinitions="Auto,*"> <Grid ColumnDefinitions="Auto,*">
<TextBlock Grid.Column="0" Text="{Binding PropertyDescription.Name}" ToolTip.Tip="{Binding PropertyDescription.Description}" /> <TextBlock Grid.Column="0" Text="{CompiledBinding PropertyDescription.Name}" ToolTip.Tip="{CompiledBinding PropertyDescription.Description}" />
<TextBlock Grid.Column="1" <TextBlock Grid.Column="1"
Text="{Binding CountDisplay, Mode=OneWay}" Text="{CompiledBinding CountDisplay, Mode=OneWay}"
FontFamily="Consolas" FontFamily="Consolas"
HorizontalAlignment="Right" HorizontalAlignment="Right"
Margin="0 0 10 0" /> Margin="0 0 10 0" />
</Grid> </Grid>
</TreeDataTemplate> </TreeDataTemplate>
<TreeDataTemplate DataType="{x:Type dataModel:DataModelEventViewModel}" ItemsSource="{Binding Children}"> <TreeDataTemplate DataType="{x:Type dataModel:DataModelEventViewModel}" ItemsSource="{CompiledBinding Children}">
<TextBlock Text="{Binding PropertyDescription.Name}" ToolTip.Tip="{Binding PropertyDescription.Description}" /> <TextBlock Text="{CompiledBinding PropertyDescription.Name}" ToolTip.Tip="{CompiledBinding PropertyDescription.Description}" />
</TreeDataTemplate> </TreeDataTemplate>
</TreeView.DataTemplates> </TreeView.DataTemplates>
</TreeView> </TreeView>

View File

@ -88,7 +88,7 @@
Background="{DynamicResource LightCheckerboardBrush}" Background="{DynamicResource LightCheckerboardBrush}"
Margin="5 0"> Margin="5 0">
<Border Background="{TemplateBinding LinearGradientBrush}"> <Border Background="{TemplateBinding LinearGradientBrush}">
<ItemsControl Name="GradientStops" Items="{TemplateBinding EditingColorGradient}" ClipToBounds="False"> <ItemsControl Name="GradientStops" ItemsSource="{TemplateBinding EditingColorGradient}" ClipToBounds="False">
<ItemsControl.Styles> <ItemsControl.Styles>
<Style Selector="ItemsControl#GradientStops > ContentPresenter"> <Style Selector="ItemsControl#GradientStops > ContentPresenter">
<Setter Property="Canvas.Left"> <Setter Property="Canvas.Left">
@ -103,10 +103,10 @@
</ItemsControl.Styles> </ItemsControl.Styles>
<ItemsControl.ItemTemplate> <ItemsControl.ItemTemplate>
<DataTemplate DataType="core:ColorGradientStop"> <DataTemplate DataType="core:ColorGradientStop">
<gradientPicker:GradientPickerColorStop ColorStop="{Binding}" <gradientPicker:GradientPickerColorStop ColorStop="{CompiledBinding}"
PositionReference="{Binding $parent[Border]}" PositionReference="{CompiledBinding $parent[Border]}"
Classes="gradient-handle" Classes="gradient-handle"
GradientPicker="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type gradientPicker:GradientPicker}}}"> GradientPicker="{CompiledBinding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type gradientPicker:GradientPicker}}}">
</gradientPicker:GradientPickerColorStop> </gradientPicker:GradientPickerColorStop>
</DataTemplate> </DataTemplate>
</ItemsControl.ItemTemplate> </ItemsControl.ItemTemplate>
@ -134,7 +134,7 @@
Grid.Column="0" Grid.Column="0"
Grid.ColumnSpan="2" Grid.ColumnSpan="2"
VerticalAlignment="Center" VerticalAlignment="Center"
Items="{TemplateBinding EditingColorGradient}" ItemsSource="{TemplateBinding EditingColorGradient}"
ClipToBounds="False" ClipToBounds="False"
Margin="5 0"> Margin="5 0">
<ItemsControl.Styles> <ItemsControl.Styles>
@ -152,7 +152,7 @@
<ItemsControl.ItemTemplate> <ItemsControl.ItemTemplate>
<DataTemplate DataType="core:ColorGradientStop"> <DataTemplate DataType="core:ColorGradientStop">
<Border Classes="stop-position"> <Border Classes="stop-position">
<TextBlock Text="{Binding Position}"></TextBlock> <TextBlock Text="{CompiledBinding Position}"></TextBlock>
</Border> </Border>
</DataTemplate> </DataTemplate>
</ItemsControl.ItemTemplate> </ItemsControl.ItemTemplate>
@ -174,15 +174,15 @@
UseColorTriangle="True" UseColorTriangle="True"
IsMoreButtonVisible="True" IsMoreButtonVisible="True"
IsVisible="{TemplateBinding SelectedColorStop, Converter={x:Static ObjectConverters.IsNotNull}}" IsVisible="{TemplateBinding SelectedColorStop, Converter={x:Static ObjectConverters.IsNotNull}}"
IsCompact="{Binding IsCompact, RelativeSource={RelativeSource TemplatedParent}}" IsCompact="{CompiledBinding IsCompact, RelativeSource={RelativeSource TemplatedParent}}"
Color="{Binding SelectedColorStop.Color, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource SKColorToColorConverter}}" /> Color="{CompiledBinding SelectedColorStop.Color, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource SKColorToColorConverter}}" />
</Border> </Border>
<Grid Grid.Row="2" Grid.Column="1" RowDefinitions="*,Auto"> <Grid Grid.Row="2" Grid.Column="1" RowDefinitions="*,Auto">
<ListBox Name="GradientColors" <ListBox Name="GradientColors"
Grid.Row="0" Grid.Row="0"
MaxHeight="280" MaxHeight="280"
Items="{TemplateBinding EditingColorGradient}" ItemsSource="{TemplateBinding EditingColorGradient}"
SelectedItem="{TemplateBinding SelectedColorStop, Mode=TwoWay}" SelectedItem="{TemplateBinding SelectedColorStop, Mode=TwoWay}"
Padding="10 0 15 0"> Padding="10 0 15 0">
<ListBox.ItemTemplate> <ListBox.ItemTemplate>
@ -196,19 +196,19 @@
BorderBrush="{DynamicResource ButtonBorderBrush}" BorderBrush="{DynamicResource ButtonBorderBrush}"
ClipToBounds="True" ClipToBounds="True"
Background="{DynamicResource LightCheckerboardBrush}"> Background="{DynamicResource LightCheckerboardBrush}">
<Border CornerRadius="4" Margin="-2" Background="{Binding Color, Converter={StaticResource SKColorToBrushConverter}}" /> <Border CornerRadius="4" Margin="-2" Background="{CompiledBinding Color, Converter={StaticResource SKColorToBrushConverter}}" />
</Border> </Border>
<TextBox Grid.Column="1" Text="{Binding Color, Converter={StaticResource SKColorToStringConverter}}" /> <TextBox Grid.Column="1" Text="{CompiledBinding Color, Converter={StaticResource SKColorToStringConverter}}" />
<NumericUpDown Grid.Column="2" FormatString="F3" ShowButtonSpinner="False" Margin="5 0" Minimum="0" Maximum="1" Increment="0.01"> <NumericUpDown Grid.Column="2" FormatString="F3" ShowButtonSpinner="False" Margin="5 0" Minimum="0" Maximum="1" Increment="0.01">
<Interaction.Behaviors> <Interaction.Behaviors>
<behaviors:LostFocusNumericUpDownBindingBehavior Value="{Binding Position}" /> <behaviors:LostFocusNumericUpDownBindingBehavior Value="{CompiledBinding Position}" />
</Interaction.Behaviors> </Interaction.Behaviors>
</NumericUpDown> </NumericUpDown>
<Button Name="DeleteButton" <Button Name="DeleteButton"
Grid.Column="3" Grid.Column="3"
Classes="icon-button" Classes="icon-button"
Command="{Binding DeleteStop, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type gradientPicker:GradientPicker}}}" Command="{CompiledBinding DeleteStop, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type gradientPicker:GradientPicker}}}"
CommandParameter="{Binding}"> CommandParameter="{CompiledBinding}">
<avalonia:MaterialIcon Kind="Close" /> <avalonia:MaterialIcon Kind="Close" />
</Button> </Button>
</Grid> </Grid>

View File

@ -4,6 +4,7 @@ using System.Reactive.Linq;
using System.Reactive.Subjects; using System.Reactive.Subjects;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Input.Platform;
using IContainer = DryIoc.IContainer; using IContainer = DryIoc.IContainer;
namespace Artemis.UI.Shared; namespace Artemis.UI.Shared;
@ -26,6 +27,11 @@ public static class UI
/// </summary> /// </summary>
public static IContainer Locator { get; set; } = null!; public static IContainer Locator { get; set; } = null!;
/// <summary>
/// Gets the clipboard.
/// </summary>
public static IClipboard Clipboard { get; set; } = null!;
/// <summary> /// <summary>
/// Gets a boolean indicating whether hotkeys are to be disabled. /// Gets a boolean indicating whether hotkeys are to be disabled.
/// </summary> /// </summary>

View File

@ -1,6 +1,8 @@
<Application xmlns="https://github.com/avaloniaui" <Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ui="clr-namespace:Artemis.UI;assembly=Artemis.UI" xmlns:ui="clr-namespace:Artemis.UI;assembly=Artemis.UI"
xmlns:root="clr-namespace:Artemis.UI.Screens.Root;assembly=Artemis.UI"
x:DataType="root:RootViewModel"
x:Class="Artemis.UI.Windows.App"> x:Class="Artemis.UI.Windows.App">
<Application.DataTemplates> <Application.DataTemplates>
<ui:ViewLocator /> <ui:ViewLocator />
@ -12,16 +14,16 @@
<TrayIcon.Icons> <TrayIcon.Icons>
<TrayIcons> <TrayIcons>
<TrayIcon Icon="avares://Artemis.UI/Assets/Images/Logo/application.ico" ToolTipText="Artemis" Command="{Binding OpenScreen}" CommandParameter="Home"> <TrayIcon Icon="avares://Artemis.UI/Assets/Images/Logo/application.ico" ToolTipText="Artemis" Command="{CompiledBinding OpenScreen}" CommandParameter="Home">
<TrayIcon.Menu> <TrayIcon.Menu>
<NativeMenu> <NativeMenu>
<NativeMenuItem Header="Home" Command="{Binding OpenScreen}" CommandParameter="Home" /> <NativeMenuItem Header="Home" Command="{CompiledBinding OpenScreen}" CommandParameter="Home" />
<!-- <NativeMenuItem Header="Workshop" Command="{Binding OpenScreen}" CommandParameter="Workshop" /> --> <!-- <NativeMenuItem Header="Workshop" Command="{CompiledBinding OpenScreen}" CommandParameter="Workshop" /> -->
<NativeMenuItem Header="Surface Editor" Command="{Binding OpenScreen}" CommandParameter="Surface Editor" /> <NativeMenuItem Header="Surface Editor" Command="{CompiledBinding OpenScreen}" CommandParameter="Surface Editor" />
<NativeMenuItem Header="Settings" Command="{Binding OpenScreen}" CommandParameter="Settings" /> <NativeMenuItem Header="Settings" Command="{CompiledBinding OpenScreen}" CommandParameter="Settings" />
<NativeMenuItemSeparator /> <NativeMenuItemSeparator />
<NativeMenuItem Header="Debugger" Command="{Binding OpenDebugger}" /> <NativeMenuItem Header="Debugger" Command="{CompiledBinding OpenDebugger}" />
<NativeMenuItem Header="Exit" Command="{Binding Exit}" /> <NativeMenuItem Header="Exit" Command="{CompiledBinding Exit}" />
</NativeMenu> </NativeMenu>
</TrayIcon.Menu> </TrayIcon.Menu>
</TrayIcon> </TrayIcon>

View File

@ -21,12 +21,12 @@
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Avalonia" Version="11.0.0-preview6" /> <PackageReference Include="Avalonia" Version="11.0.0-preview8" />
<PackageReference Include="Avalonia.Desktop" Version="11.0.0-preview6" /> <PackageReference Include="Avalonia.Desktop" Version="11.0.0-preview8" />
<!--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="11.0.0-preview6" /> <PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.0-preview8" />
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.0-preview6" /> <PackageReference Include="Avalonia.ReactiveUI" Version="11.0.0-preview8" />
<PackageReference Include="Avalonia.Win32" Version="11.0.0-preview6" /> <PackageReference Include="Avalonia.Win32" Version="11.0.0-preview8" />
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.3" /> <PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.3" />
<PackageReference Include="Microsoft.Win32" Version="2.0.1" /> <PackageReference Include="Microsoft.Win32" Version="2.0.1" />
<PackageReference Include="Microsoft.Windows.Compatibility" Version="7.0.0" /> <PackageReference Include="Microsoft.Windows.Compatibility" Version="7.0.0" />

View File

@ -16,12 +16,6 @@ public class AutoRunProvider : IAutoRunProvider
{ {
private readonly string _autorunName = $"Artemis 2 autorun {Environment.UserName}"; private readonly string _autorunName = $"Artemis 2 autorun {Environment.UserName}";
private readonly string _oldAutorunName = "Artemis 2 autorun"; private readonly string _oldAutorunName = "Artemis 2 autorun";
private readonly IAssetLoader _assetLoader;
public AutoRunProvider(IAssetLoader assetLoader)
{
_assetLoader = assetLoader;
}
private async Task<bool> IsAutoRunTaskCreated(string autorunName) private async Task<bool> IsAutoRunTaskCreated(string autorunName)
{ {
@ -43,7 +37,7 @@ public class AutoRunProvider : IAutoRunProvider
private async Task CreateAutoRunTask(TimeSpan autoRunDelay, string autorunName) private async Task CreateAutoRunTask(TimeSpan autoRunDelay, string autorunName)
{ {
await using Stream taskFile = _assetLoader.Open(new Uri("avares://Artemis.UI.Windows/Assets/autorun.xml")); await using Stream taskFile = AssetLoader.Open(new Uri("avares://Artemis.UI.Windows/Assets/autorun.xml"));
XDocument document = await XDocument.LoadAsync(taskFile, LoadOptions.None, CancellationToken.None); XDocument document = await XDocument.LoadAsync(taskFile, LoadOptions.None, CancellationToken.None);
XElement task = document.Descendants().First(); XElement task = document.Descendants().First();

View File

@ -9,11 +9,11 @@ namespace Artemis.UI.Windows.Providers;
public class CursorProvider : ICursorProvider public class CursorProvider : ICursorProvider
{ {
public CursorProvider(IAssetLoader assetLoader) public CursorProvider()
{ {
Rotate = new Cursor(new Bitmap(assetLoader.Open(new Uri("avares://Artemis.UI.Windows/Assets/Cursors/aero_rotate.png"))), new PixelPoint(21, 10)); Rotate = new Cursor(new Bitmap(AssetLoader.Open(new Uri("avares://Artemis.UI.Windows/Assets/Cursors/aero_rotate.png"))), new PixelPoint(21, 10));
Drag = new Cursor(new Bitmap(assetLoader.Open(new Uri("avares://Artemis.UI.Windows/Assets/Cursors/aero_drag.png"))), new PixelPoint(11, 3)); Drag = new Cursor(new Bitmap(AssetLoader.Open(new Uri("avares://Artemis.UI.Windows/Assets/Cursors/aero_drag.png"))), new PixelPoint(11, 3));
DragHorizontal = new Cursor(new Bitmap(assetLoader.Open(new Uri("avares://Artemis.UI.Windows/Assets/Cursors/aero_drag_horizontal.png"))), new PixelPoint(16, 5)); DragHorizontal = new Cursor(new Bitmap(AssetLoader.Open(new Uri("avares://Artemis.UI.Windows/Assets/Cursors/aero_drag_horizontal.png"))), new PixelPoint(16, 5));
} }
public Cursor Rotate { get; } public Cursor Rotate { get; }

View File

@ -5,8 +5,6 @@ using System.Timers;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Windows.Utilities; using Artemis.UI.Windows.Utilities;
using Avalonia.Controls.Platform;
using Avalonia.Platform;
using Linearstar.Windows.RawInput; using Linearstar.Windows.RawInput;
using Linearstar.Windows.RawInput.Native; using Linearstar.Windows.RawInput.Native;
using Serilog; using Serilog;
@ -18,7 +16,7 @@ public class WindowsInputProvider : InputProvider
private const int GWL_WNDPROC = -4; private const int GWL_WNDPROC = -4;
private const int WM_INPUT = 0x00FF; private const int WM_INPUT = 0x00FF;
private readonly IWindowImpl _window; private readonly nint _hWnd;
private readonly nint _hWndProcHook; private readonly nint _hWndProcHook;
private readonly WndProc? _fnWndProcHook; private readonly WndProc? _fnWndProcHook;
private readonly IInputService _inputService; private readonly IInputService _inputService;
@ -31,7 +29,7 @@ public class WindowsInputProvider : InputProvider
private nint CustomWndProc(nint hWnd, uint msg, nint wParam, nint lParam) private nint CustomWndProc(nint hWnd, uint msg, nint wParam, nint lParam)
{ {
OnWndProcCalled(hWnd, msg, wParam, lParam); OnWndProcCalled(hWnd, msg, wParam, lParam);
return CallWindowProc(_hWndProcHook, hWnd, msg, wParam, lParam); return User32.CallWindowProc(_hWndProcHook, hWnd, msg, wParam, lParam);
} }
public WindowsInputProvider(ILogger logger, IInputService inputService) public WindowsInputProvider(ILogger logger, IInputService inputService)
@ -43,15 +41,14 @@ public class WindowsInputProvider : InputProvider
_taskManagerTimer.Elapsed += TaskManagerTimerOnElapsed; _taskManagerTimer.Elapsed += TaskManagerTimerOnElapsed;
_taskManagerTimer.Start(); _taskManagerTimer.Start();
_window = PlatformManager.CreateWindow(); _hWnd = User32.CreateWindowEx(0, "STATIC", "", 0x80000000, 0, 0, 0, 0, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
_hWndProcHook = User32.GetWindowLongPtr(_hWnd, GWL_WNDPROC);
_hWndProcHook = GetWindowLongPtr(_window.Handle.Handle, GWL_WNDPROC);
_fnWndProcHook = CustomWndProc; _fnWndProcHook = CustomWndProc;
nint newLong = Marshal.GetFunctionPointerForDelegate(_fnWndProcHook); nint newLong = Marshal.GetFunctionPointerForDelegate(_fnWndProcHook);
SetWindowLongPtr(_window.Handle.Handle, GWL_WNDPROC, newLong); User32.SetWindowLongPtr(_hWnd, GWL_WNDPROC, newLong);
RawInputDevice.RegisterDevice(HidUsageAndPage.Keyboard, RawInputDeviceFlags.InputSink, _window.Handle.Handle); RawInputDevice.RegisterDevice(HidUsageAndPage.Keyboard, RawInputDeviceFlags.InputSink, _hWnd);
RawInputDevice.RegisterDevice(HidUsageAndPage.Mouse, RawInputDeviceFlags.InputSink, _window.Handle.Handle); RawInputDevice.RegisterDevice(HidUsageAndPage.Mouse, RawInputDeviceFlags.InputSink, _hWnd);
} }
public static Guid Id { get; } = new("6737b204-ffb1-4cd9-8776-9fb851db303a"); public static Guid Id { get; } = new("6737b204-ffb1-4cd9-8776-9fb851db303a");
@ -236,31 +233,11 @@ public class WindowsInputProvider : InputProvider
#endregion #endregion
#region Native #region Native
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
static extern IntPtr CallWindowProc(nint lpPrevWndFunc, IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", EntryPoint = "GetWindowLongPtr", CharSet = CharSet.Unicode)]
private static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr", CharSet = CharSet.Unicode)]
private static extern IntPtr SetWindowLongPtr(nint hWnd, int nIndex, IntPtr dwNewLong);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetCursorPos(ref Win32Point pt);
[StructLayout(LayoutKind.Sequential)]
private struct Win32Point
{
public readonly int X;
public readonly int Y;
}
private static Win32Point GetCursorPosition() private static Win32Point GetCursorPosition()
{ {
Win32Point w32Mouse = new(); Win32Point w32Mouse = new();
GetCursorPos(ref w32Mouse); User32.GetCursorPos(ref w32Mouse);
return w32Mouse; return w32Mouse;
} }

View File

@ -1,7 +1,5 @@
using System; using System;
using System.Linq; using System.Linq;
using Avalonia.Controls.Platform;
using Avalonia.Platform;
using SharpVk; using SharpVk;
using SharpVk.Khronos; using SharpVk.Khronos;
@ -9,12 +7,16 @@ namespace Artemis.UI.Windows.SkiaSharp.Vulkan;
internal sealed class Win32VkContext : VkContext internal sealed class Win32VkContext : VkContext
{ {
private readonly nint _hWnd;
public Win32VkContext() public Win32VkContext()
{ {
Window = PlatformManager.CreateWindow(); // Use WS_CHILD and WS_VISIBLE with WS_VISIBLE set to false
_hWnd = User32.CreateWindowEx(0, "STATIC", "", 0x80000000, 0, 0, 0, 0, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
Instance = Instance.Create(null, new[] {"VK_KHR_surface", "VK_KHR_win32_surface"}); Instance = Instance.Create(null, new[] {"VK_KHR_surface", "VK_KHR_win32_surface"});
PhysicalDevice = Instance.EnumeratePhysicalDevices().First(); PhysicalDevice = Instance.EnumeratePhysicalDevices().First();
Surface = Instance.CreateWin32Surface(Kernel32.CurrentModuleHandle, Window.Handle.Handle); Surface = Instance.CreateWin32Surface(Kernel32.CurrentModuleHandle, _hWnd);
(GraphicsFamily, PresentFamily) = FindQueueFamilies(); (GraphicsFamily, PresentFamily) = FindQueueFamilies();
@ -44,12 +46,10 @@ internal sealed class Win32VkContext : VkContext
}; };
} }
public IWindowImpl Window { get; }
public override void Dispose() public override void Dispose()
{ {
base.Dispose(); base.Dispose();
Window.Dispose(); User32.DestroyWindow(_hWnd);
} }
private IntPtr Proc(string name, IntPtr instanceHandle, IntPtr deviceHandle) private IntPtr Proc(string name, IntPtr instanceHandle, IntPtr deviceHandle)

View File

@ -0,0 +1,51 @@
using System;
using System.Runtime.InteropServices;
namespace Artemis.UI.Windows;
static class User32
{
[DllImport("user32.dll")]
public static extern IntPtr CreateWindowEx(
uint dwExStyle,
string lpClassName,
string lpWindowName,
uint dwStyle,
int x,
int y,
int nWidth,
int nHeight,
IntPtr hWndParent,
IntPtr hMenu,
IntPtr hInstance,
IntPtr lpParam);
[DllImport("user32.dll")]
public static extern bool DestroyWindow(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr CallWindowProc(nint lpPrevWndFunc, IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", EntryPoint = "GetWindowLongPtr", CharSet = CharSet.Unicode)]
public static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr", CharSet = CharSet.Unicode)]
public static extern IntPtr SetWindowLongPtr(nint hWnd, int nIndex, IntPtr dwNewLong);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetCursorPos(ref Win32Point pt);
}
[StructLayout(LayoutKind.Sequential)]
public struct Win32Point
{
public readonly int X;
public readonly int Y;
}

View File

@ -1,5 +1,4 @@
using System; using System;
using System.Runtime.InteropServices;
namespace Artemis.UI.Windows.Utilities; namespace Artemis.UI.Windows.Utilities;
@ -8,15 +7,9 @@ public static class WindowUtilities
public static int GetActiveProcessId() public static int GetActiveProcessId()
{ {
// Get foreground window handle // Get foreground window handle
IntPtr hWnd = GetForegroundWindow(); IntPtr hWnd = User32.GetForegroundWindow();
GetWindowThreadProcessId(hWnd, out uint processId); User32.GetWindowThreadProcessId(hWnd, out uint processId);
return (int) processId; return (int) processId;
} }
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
} }

View File

@ -15,17 +15,17 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Avalonia" Version="11.0.0-preview6" /> <PackageReference Include="Avalonia" Version="11.0.0-preview8" />
<PackageReference Include="Avalonia.Controls.PanAndZoom" Version="11.0.0-preview6" /> <PackageReference Include="Avalonia.Controls.PanAndZoom" Version="11.0.0-preview8" />
<PackageReference Include="Avalonia.Controls.Skia" Version="11.0.0-preview6" /> <PackageReference Include="Avalonia.Controls.Skia" Version="11.0.0-preview8.1" />
<PackageReference Include="Avalonia.Desktop" Version="11.0.0-preview6" /> <PackageReference Include="Avalonia.Desktop" Version="11.0.0-preview8" />
<!--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="11.0.0-preview6" /> <PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.0-preview8" />
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.0-preview6" /> <PackageReference Include="Avalonia.ReactiveUI" Version="11.0.0-preview8" />
<PackageReference Include="Avalonia.Xaml.Behaviors" Version="11.0.0-preview6" /> <PackageReference Include="Avalonia.Xaml.Behaviors" Version="11.0.0-preview8" />
<PackageReference Include="DryIoc.dll" Version="5.3.4" /> <PackageReference Include="DryIoc.dll" Version="5.4.0" />
<PackageReference Include="DynamicData" Version="7.13.1" /> <PackageReference Include="DynamicData" Version="7.13.1" />
<PackageReference Include="FluentAvaloniaUI" Version="2.0.0-preview6" /> <PackageReference Include="FluentAvaloniaUI" Version="2.0.0-preview8" />
<PackageReference Include="Flurl.Http" Version="3.2.4" /> <PackageReference Include="Flurl.Http" Version="3.2.4" />
<PackageReference Include="Live.Avalonia" Version="1.3.1" /> <PackageReference Include="Live.Avalonia" Version="1.3.1" />
<PackageReference Include="Markdown.Avalonia.Tight" Version="11.0.0-b1" /> <PackageReference Include="Markdown.Avalonia.Tight" Version="11.0.0-b1" />

View File

@ -15,6 +15,7 @@ using Artemis.WebClient.Updating.DryIoc;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Styling;
using DryIoc; using DryIoc;
using ReactiveUI; using ReactiveUI;
using Splat.DryIoc; using Splat.DryIoc;
@ -33,6 +34,7 @@ public static class ArtemisBootstrapper
Utilities.PrepareFirstLaunch(); Utilities.PrepareFirstLaunch();
application.RequestedThemeVariant = ThemeVariant.Dark;
_application = application; _application = application;
_container = new Container(rules => rules _container = new Container(rules => rules
.WithMicrosoftDependencyInjectionRules() .WithMicrosoftDependencyInjectionRules()

View File

@ -26,7 +26,7 @@
<ComboBox Classes="brush condensed" <ComboBox Classes="brush condensed"
Width="200" Width="200"
VerticalAlignment="Center" VerticalAlignment="Center"
Items="{CompiledBinding Descriptors}" ItemsSource="{CompiledBinding Descriptors}"
SelectedItem="{CompiledBinding SelectedDescriptor}" SelectedItem="{CompiledBinding SelectedDescriptor}"
PlaceholderText="Please select a brush"> PlaceholderText="Please select a brush">
<ComboBox.ItemTemplate> <ComboBox.ItemTemplate>
@ -34,7 +34,7 @@
<Grid ColumnDefinitions="30,*" RowDefinitions="Auto,Auto"> <Grid ColumnDefinitions="30,*" RowDefinitions="Auto,Auto">
<avalonia:MaterialIcon Grid.Row="0" <avalonia:MaterialIcon Grid.Row="0"
Grid.RowSpan="2" Grid.RowSpan="2"
Kind="{Binding Icon}" Kind="{CompiledBinding Icon}"
Height="20" Height="20"
Width="20" Width="20"
VerticalAlignment="Center" VerticalAlignment="Center"

View File

@ -23,11 +23,11 @@
<Style.Resources> <Style.Resources>
<ControlTemplate x:Key="TooltipDataValidationLeftContentTemplate" TargetType="DataValidationErrors"> <ControlTemplate x:Key="TooltipDataValidationLeftContentTemplate" TargetType="DataValidationErrors">
<DockPanel LastChildFill="True"> <DockPanel LastChildFill="True">
<ContentControl Content="{Binding (DataValidationErrors.Errors)}" <ContentControl Content="{CompiledBinding (DataValidationErrors.Errors)}"
ContentTemplate="{TemplateBinding ErrorTemplate}" ContentTemplate="{TemplateBinding ErrorTemplate}"
DataContext="{TemplateBinding Owner}" DataContext="{TemplateBinding Owner}"
DockPanel.Dock="Left" DockPanel.Dock="Left"
IsVisible="{Binding (DataValidationErrors.HasErrors)}" /> IsVisible="{CompiledBinding (DataValidationErrors.HasErrors)}" />
<ContentPresenter Name="PART_ContentPresenter" <ContentPresenter Name="PART_ContentPresenter"
Padding="{TemplateBinding Padding}" Padding="{TemplateBinding Padding}"
Background="{TemplateBinding Background}" Background="{TemplateBinding Background}"

View File

@ -7,7 +7,6 @@ using Artemis.UI.Services.Updating;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services.NodeEditor; using Artemis.UI.Shared.Services.NodeEditor;
using Artemis.UI.Shared.Services.ProfileEditor; using Artemis.UI.Shared.Services.ProfileEditor;
using Avalonia.Platform;
using DryIoc; using DryIoc;
namespace Artemis.UI.DryIoc; namespace Artemis.UI.DryIoc;
@ -24,12 +23,9 @@ public static class ContainerExtensions
public static void RegisterUI(this IContainer container) public static void RegisterUI(this IContainer container)
{ {
Assembly[] thisAssembly = {typeof(ContainerExtensions).Assembly}; Assembly[] thisAssembly = {typeof(ContainerExtensions).Assembly};
container.RegisterInstance(new AssetLoader(), IfAlreadyRegistered.Throw);
container.Register<IAssetLoader, AssetLoader>(Reuse.Singleton);
container.RegisterMany(thisAssembly, type => type.IsAssignableTo<ViewModelBase>()); container.RegisterMany(thisAssembly, type => type.IsAssignableTo<ViewModelBase>(), setup: Setup.With(preventDisposal: true));
container.RegisterMany(thisAssembly, type => type.IsAssignableTo<IToolViewModel>() && type.IsInterface); container.RegisterMany(thisAssembly, type => type.IsAssignableTo<IToolViewModel>() && type.IsInterface, setup: Setup.With(preventDisposal: true));
container.RegisterMany(thisAssembly, type => type.IsAssignableTo<IVmFactory>() && type != typeof(PropertyVmFactory)); container.RegisterMany(thisAssembly, type => type.IsAssignableTo<IVmFactory>() && type != typeof(PropertyVmFactory));
container.Register<NodeScriptWindowViewModelBase, NodeScriptWindowViewModel>(Reuse.Singleton); container.Register<NodeScriptWindowViewModelBase, NodeScriptWindowViewModel>(Reuse.Singleton);

View File

@ -18,33 +18,24 @@ public static class ProfileElementExtensions
public static async Task CopyToClipboard(this Folder folder) public static async Task CopyToClipboard(this Folder folder)
{ {
if (Application.Current?.Clipboard == null)
return;
DataObject dataObject = new(); DataObject dataObject = new();
string copy = CoreJson.SerializeObject(new FolderClipboardModel(folder), true); string copy = CoreJson.SerializeObject(new FolderClipboardModel(folder), true);
dataObject.Set(ClipboardDataFormat, copy); dataObject.Set(ClipboardDataFormat, copy);
await Application.Current.Clipboard.SetDataObjectAsync(dataObject); await Shared.UI.Clipboard.SetDataObjectAsync(dataObject);
} }
public static async Task CopyToClipboard(this Layer layer) public static async Task CopyToClipboard(this Layer layer)
{ {
if (Application.Current?.Clipboard == null)
return;
DataObject dataObject = new(); DataObject dataObject = new();
string copy = CoreJson.SerializeObject(layer.LayerEntity, true); string copy = CoreJson.SerializeObject(layer.LayerEntity, true);
dataObject.Set(ClipboardDataFormat, copy); dataObject.Set(ClipboardDataFormat, copy);
await Application.Current.Clipboard.SetDataObjectAsync(dataObject); await Shared.UI.Clipboard.SetDataObjectAsync(dataObject);
} }
public static async Task<RenderProfileElement?> PasteChildFromClipboard(this Folder parent) public static async Task<RenderProfileElement?> PasteChildFromClipboard(this Folder parent)
{ {
if (Application.Current?.Clipboard == null) byte[]? bytes = (byte[]?) await Shared.UI.Clipboard.GetDataAsync(ClipboardDataFormat);
return null;
byte[]? bytes = (byte[]?) await Application.Current.Clipboard.GetDataAsync(ClipboardDataFormat);
if (bytes == null!) if (bytes == null!)
return null; return null;

View File

@ -2,10 +2,11 @@
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:windowing="clr-namespace:FluentAvalonia.UI.Windowing;assembly=FluentAvalonia" xmlns:windowing="clr-namespace:FluentAvalonia.UI.Windowing;assembly=FluentAvalonia"
xmlns:vm="clr-namespace:Artemis.UI.Screens.Root"
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"
x:DataType="vm:RootViewModel"
Icon="/Assets/Images/Logo/application.ico" Icon="/Assets/Images/Logo/application.ico"
Title="Artemis 2.0" Title="Artemis 2.0"
MinWidth="600" MinWidth="600"
@ -22,7 +23,7 @@
</windowing:AppWindow.Styles> </windowing:AppWindow.Styles>
<Panel Name="RootPanel"> <Panel Name="RootPanel">
<DockPanel> <DockPanel>
<ContentControl Name="SidebarContentControl" Content="{Binding SidebarViewModel}" DockPanel.Dock="Left" Width="240"> <ContentControl Name="SidebarContentControl" Content="{CompiledBinding SidebarViewModel}" DockPanel.Dock="Left" Width="240">
<ContentControl.Transitions> <ContentControl.Transitions>
<Transitions> <Transitions>
<DoubleTransition Property="Width" Duration="0:0:0.2" Easing="CubicEaseOut"></DoubleTransition> <DoubleTransition Property="Width" Duration="0:0:0.2" Easing="CubicEaseOut"></DoubleTransition>
@ -30,9 +31,9 @@
</ContentControl.Transitions> </ContentControl.Transitions>
</ContentControl> </ContentControl>
<Border Name="TitleBarContainer" DockPanel.Dock="Top"> <Border Name="TitleBarContainer" DockPanel.Dock="Top">
<ContentControl Content="{Binding TitleBarViewModel}"/> <ContentControl Content="{CompiledBinding TitleBarViewModel}"/>
</Border> </Border>
<ContentControl Content="{Binding}" /> <ContentControl Content="{CompiledBinding}" />
</DockPanel> </DockPanel>
<StackPanel Classes="notification-container" Name="NotificationContainer" VerticalAlignment="Bottom" HorizontalAlignment="Right"/> <StackPanel Classes="notification-container" Name="NotificationContainer" VerticalAlignment="Bottom" HorizontalAlignment="Right"/>
</Panel> </Panel>

View File

@ -6,7 +6,6 @@ using Artemis.UI.Screens.Root;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using ReactiveUI; using ReactiveUI;
@ -15,27 +14,24 @@ namespace Artemis.UI;
public partial class MainWindow : ReactiveAppWindow<RootViewModel> public partial class MainWindow : ReactiveAppWindow<RootViewModel>
{ {
private bool _activated; private bool _activated;
private IDisposable? _positionObserver;
public MainWindow() public MainWindow()
{ {
Opened += OnOpened; Opened += OnOpened;
Closed += OnClosed;
Activated += OnActivated; Activated += OnActivated;
Deactivated += OnDeactivated; Deactivated += OnDeactivated;
InitializeComponent(); InitializeComponent();
ApplyWindowSize(); ApplyWindowSize();
Shared.UI.Clipboard = GetTopLevel(this)!.Clipboard!;
RootPanel.LayoutUpdated += OnLayoutUpdated; RootPanel.LayoutUpdated += OnLayoutUpdated;
#if DEBUG #if DEBUG
this.AttachDevTools(); this.AttachDevTools();
#endif #endif
Observable.FromEventPattern<PixelPointEventArgs>(x => PositionChanged += x, x => PositionChanged -= x)
.Select(_ => Unit.Default)
.Merge(this.WhenAnyValue(vm => vm.WindowState, vm => vm.Width, vm => vm.Width, vm => vm.Height).Select(_ => Unit.Default))
.Throttle(TimeSpan.FromMilliseconds(200), AvaloniaScheduler.Instance)
.Subscribe(_ => SaveWindowSize());
} }
private void ApplyWindowSize() private void ApplyWindowSize()
@ -52,7 +48,7 @@ public partial class MainWindow : ReactiveAppWindow<RootViewModel>
RootViewModel.WindowSizeSetting.Value ??= new WindowSize(); RootViewModel.WindowSizeSetting.Value ??= new WindowSize();
RootViewModel.WindowSizeSetting.Value.ApplyFromWindow(this); RootViewModel.WindowSizeSetting.Value.ApplyFromWindow(this);
} }
private void OnLayoutUpdated(object? sender, EventArgs e) private void OnLayoutUpdated(object? sender, EventArgs e)
{ {
SidebarContentControl.Width = RootPanel.Bounds.Width >= 1800 ? 300 : 240; SidebarContentControl.Width = RootPanel.Bounds.Width >= 1800 ? 300 : 240;
@ -60,8 +56,18 @@ public partial class MainWindow : ReactiveAppWindow<RootViewModel>
private void OnOpened(object? sender, EventArgs e) private void OnOpened(object? sender, EventArgs e)
{ {
Opened -= OnOpened;
TitleBar.ExtendsContentIntoTitleBar = true; TitleBar.ExtendsContentIntoTitleBar = true;
_positionObserver = Observable.FromEventPattern<PixelPointEventArgs>(x => PositionChanged += x, x => PositionChanged -= x)
.Select(_ => Unit.Default)
.Merge(this.WhenAnyValue(vm => vm.WindowState, vm => vm.Width, vm => vm.Width, vm => vm.Height).Select(_ => Unit.Default))
.Throttle(TimeSpan.FromMilliseconds(200), AvaloniaScheduler.Instance)
.Subscribe(_ => SaveWindowSize());
}
private void OnClosed(object? sender, EventArgs e)
{
_positionObserver?.Dispose();
} }
private void OnActivated(object? sender, EventArgs e) private void OnActivated(object? sender, EventArgs e)
@ -73,5 +79,4 @@ public partial class MainWindow : ReactiveAppWindow<RootViewModel>
{ {
ViewModel?.Unfocused(); ViewModel?.Unfocused();
} }
} }

View File

@ -25,7 +25,7 @@
</Window.Styles> </Window.Styles>
<Grid ColumnDefinitions="240,*"> <Grid ColumnDefinitions="240,*">
<ListBox Items="{CompiledBinding Items}" SelectedItem="{CompiledBinding SelectedItem}" Grid.Column="0" Margin="10"> <ListBox ItemsSource="{CompiledBinding Items}" SelectedItem="{CompiledBinding SelectedItem}" Grid.Column="0" Margin="10">
<ListBox.ItemTemplate> <ListBox.ItemTemplate>
<DataTemplate x:DataType="shared:ViewModelBase"> <DataTemplate x:DataType="shared:ViewModelBase">
<TextBlock Text="{CompiledBinding DisplayName}" VerticalAlignment="Center" /> <TextBlock Text="{CompiledBinding DisplayName}" VerticalAlignment="Center" />

View File

@ -5,7 +5,9 @@
xmlns:dataModel="clr-namespace:Artemis.UI.Shared.DataModelVisualization.Shared;assembly=Artemis.UI.Shared" xmlns:dataModel="clr-namespace:Artemis.UI.Shared.DataModelVisualization.Shared;assembly=Artemis.UI.Shared"
xmlns:converters="clr-namespace:Artemis.UI.Shared.Converters;assembly=Artemis.UI.Shared" xmlns:converters="clr-namespace:Artemis.UI.Shared.Converters;assembly=Artemis.UI.Shared"
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia" xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
xmlns:local="clr-namespace:Artemis.UI.Screens.Debugger.DataModel"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:DataType="local:DataModelDebugViewModel"
x:Class="Artemis.UI.Screens.Debugger.DataModel.DataModelDebugView"> x:Class="Artemis.UI.Screens.Debugger.DataModel.DataModelDebugView">
<UserControl.Resources> <UserControl.Resources>
<converters:TypeToStringConverter x:Key="TypeToStringConverter" /> <converters:TypeToStringConverter x:Key="TypeToStringConverter" />
@ -21,14 +23,14 @@
</TextBlock> </TextBlock>
</StackPanel> </StackPanel>
<TreeView Grid.Row="1" Items="{Binding MainDataModel.Children}"> <TreeView Grid.Row="1" ItemsSource="{CompiledBinding MainDataModel.Children}">
<TreeView.Styles> <TreeView.Styles>
<Style Selector="TreeViewItem"> <Style Selector="TreeViewItem">
<Setter Property="IsExpanded" Value="{Binding IsVisualizationExpanded, Mode=TwoWay}" /> <Setter Property="IsExpanded" Value="{CompiledBinding IsVisualizationExpanded, Mode=TwoWay,DataType=dataModel:DataModelVisualizationViewModel}" />
<Setter Property="ContextFlyout"> <Setter Property="ContextFlyout">
<Setter.Value> <Setter.Value>
<MenuFlyout> <MenuFlyout>
<MenuItem Header="Copy path" Command="{Binding CopyPath}"> <MenuItem Header="Copy path" Command="{CompiledBinding CopyPath, DataType=dataModel:DataModelVisualizationViewModel}">
<MenuItem.Icon> <MenuItem.Icon>
<avalonia:MaterialIcon Kind="ContentCopy" /> <avalonia:MaterialIcon Kind="ContentCopy" />
</MenuItem.Icon> </MenuItem.Icon>
@ -39,32 +41,32 @@
</Style> </Style>
</TreeView.Styles> </TreeView.Styles>
<TreeView.DataTemplates> <TreeView.DataTemplates>
<TreeDataTemplate DataType="{x:Type dataModel:DataModelPropertiesViewModel}" ItemsSource="{Binding Children}"> <TreeDataTemplate DataType="{x:Type dataModel:DataModelPropertiesViewModel}" ItemsSource="{CompiledBinding Children}">
<Grid ColumnDefinitions="Auto,Auto,*"> <Grid ColumnDefinitions="Auto,Auto,*">
<StackPanel Grid.Column="0" Orientation="Horizontal" Margin="0 0 5 0"> <StackPanel Grid.Column="0" Orientation="Horizontal" Margin="0 0 5 0">
<TextBlock FontWeight="Bold">[</TextBlock> <TextBlock FontWeight="Bold">[</TextBlock>
<TextBlock FontWeight="Bold" Text="{Binding DisplayValueType, Converter={StaticResource TypeToStringConverter}, Mode=OneWay}" /> <TextBlock FontWeight="Bold" Text="{CompiledBinding DisplayValueType, Converter={StaticResource TypeToStringConverter}, Mode=OneWay}" />
<TextBlock FontWeight="Bold">]</TextBlock> <TextBlock FontWeight="Bold">]</TextBlock>
</StackPanel> </StackPanel>
<TextBlock Grid.Column="1" Text="{Binding PropertyDescription.Name}" ToolTip.Tip="{Binding PropertyDescription.Description}" /> <TextBlock Grid.Column="1" Text="{CompiledBinding PropertyDescription.Name}" ToolTip.Tip="{CompiledBinding PropertyDescription.Description}" />
<TextBlock Grid.Column="2" <TextBlock Grid.Column="2"
Text="{Binding DisplayValue}" Text="{CompiledBinding DisplayValue}"
FontFamily="Consolas" FontFamily="Consolas"
HorizontalAlignment="Right" /> HorizontalAlignment="Right" />
</Grid> </Grid>
</TreeDataTemplate> </TreeDataTemplate>
<TreeDataTemplate DataType="{x:Type dataModel:DataModelListViewModel}" ItemsSource="{Binding ListChildren}"> <TreeDataTemplate DataType="{x:Type dataModel:DataModelListViewModel}" ItemsSource="{CompiledBinding ListChildren}">
<Grid ColumnDefinitions="Auto,Auto,*"> <Grid ColumnDefinitions="Auto,Auto,*">
<StackPanel Grid.Column="0" Orientation="Horizontal" Margin="0 0 5 0"> <StackPanel Grid.Column="0" Orientation="Horizontal" Margin="0 0 5 0">
<TextBlock FontWeight="Bold">[</TextBlock> <TextBlock FontWeight="Bold">[</TextBlock>
<TextBlock FontWeight="Bold" Text="{Binding DisplayValueType, Converter={StaticResource TypeToStringConverter}, Mode=OneWay}" /> <TextBlock FontWeight="Bold" Text="{CompiledBinding DisplayValueType, Converter={StaticResource TypeToStringConverter}, Mode=OneWay}" />
<TextBlock FontWeight="Bold">]</TextBlock> <TextBlock FontWeight="Bold">]</TextBlock>
</StackPanel> </StackPanel>
<TextBlock Grid.Column="1" Text="{Binding PropertyDescription.Name}" ToolTip.Tip="{Binding PropertyDescription.Description}" /> <TextBlock Grid.Column="1" Text="{CompiledBinding PropertyDescription.Name}" ToolTip.Tip="{CompiledBinding PropertyDescription.Description}" />
<TextBlock Grid.Column="2" <TextBlock Grid.Column="2"
Text="{Binding CountDisplay, Mode=OneWay}" Text="{CompiledBinding CountDisplay, Mode=OneWay}"
FontFamily="Consolas" FontFamily="Consolas"
HorizontalAlignment="Right" /> HorizontalAlignment="Right" />
</Grid> </Grid>
@ -74,13 +76,13 @@
<!-- Value description --> <!-- Value description -->
<StackPanel Grid.Column="0" Orientation="Horizontal" Margin="0 0 5 0"> <StackPanel Grid.Column="0" Orientation="Horizontal" Margin="0 0 5 0">
<TextBlock FontWeight="Bold">[</TextBlock> <TextBlock FontWeight="Bold">[</TextBlock>
<TextBlock FontWeight="Bold" Text="{Binding DisplayValueType, Converter={StaticResource TypeToStringConverter}, Mode=OneWay}" /> <TextBlock FontWeight="Bold" Text="{CompiledBinding DisplayValueType, Converter={StaticResource TypeToStringConverter}, Mode=OneWay}" />
<TextBlock FontWeight="Bold">]</TextBlock> <TextBlock FontWeight="Bold">]</TextBlock>
</StackPanel> </StackPanel>
<TextBlock Grid.Column="1" Text="{Binding PropertyDescription.Name}" ToolTip.Tip="{Binding PropertyDescription.Description}" /> <TextBlock Grid.Column="1" Text="{CompiledBinding PropertyDescription.Name}" ToolTip.Tip="{CompiledBinding PropertyDescription.Description}" />
<!-- Value display --> <!-- Value display -->
<ContentControl Grid.Column="2" Content="{Binding DisplayViewModel}" FontFamily="Consolas" /> <ContentControl Grid.Column="2" Content="{CompiledBinding DisplayViewModel}" FontFamily="Consolas" />
</Grid> </Grid>
</TreeDataTemplate> </TreeDataTemplate>
<TreeDataTemplate DataType="{x:Type dataModel:DataModelListItemViewModel}"> <TreeDataTemplate DataType="{x:Type dataModel:DataModelListItemViewModel}">
@ -88,29 +90,29 @@
<!-- Value description --> <!-- Value description -->
<StackPanel Grid.Column="0" Orientation="Horizontal" Margin="0 0 5 0"> <StackPanel Grid.Column="0" Orientation="Horizontal" Margin="0 0 5 0">
<TextBlock FontWeight="Bold">[</TextBlock> <TextBlock FontWeight="Bold">[</TextBlock>
<TextBlock FontWeight="Bold" Text="{Binding ListType, Converter={StaticResource TypeToStringConverter}}" /> <TextBlock FontWeight="Bold" Text="{CompiledBinding ListType, Converter={StaticResource TypeToStringConverter}}" />
<TextBlock FontWeight="Bold">]</TextBlock> <TextBlock FontWeight="Bold">]</TextBlock>
</StackPanel> </StackPanel>
<StackPanel Grid.Column="1" Orientation="Horizontal" Margin="0 0 5 0"> <StackPanel Grid.Column="1" Orientation="Horizontal" Margin="0 0 5 0">
<TextBlock>List item #</TextBlock> <TextBlock>List item #</TextBlock>
<TextBlock Text="{Binding Index, Mode=OneWay}" /> <TextBlock Text="{CompiledBinding Index, Mode=OneWay}" />
</StackPanel> </StackPanel>
<!-- Value display --> <!-- Value display -->
<ContentControl Grid.Column="2" Content="{Binding DisplayViewModel}" FontFamily="Consolas" /> <ContentControl Grid.Column="2" Content="{CompiledBinding DisplayViewModel}" FontFamily="Consolas" />
</Grid> </Grid>
</TreeDataTemplate> </TreeDataTemplate>
<TreeDataTemplate DataType="{x:Type dataModel:DataModelEventViewModel}" ItemsSource="{Binding Children}"> <TreeDataTemplate DataType="{x:Type dataModel:DataModelEventViewModel}" ItemsSource="{CompiledBinding Children}">
<Grid ColumnDefinitions="Auto,Auto,*"> <Grid ColumnDefinitions="Auto,Auto,*">
<StackPanel Grid.Column="0" Orientation="Horizontal" Margin="0 0 5 0"> <StackPanel Grid.Column="0" Orientation="Horizontal" Margin="0 0 5 0">
<TextBlock FontWeight="Bold">[</TextBlock> <TextBlock FontWeight="Bold">[</TextBlock>
<TextBlock FontWeight="Bold" Text="{Binding DisplayValueType, Converter={StaticResource TypeToStringConverter}, Mode=OneWay}" /> <TextBlock FontWeight="Bold" Text="{CompiledBinding DisplayValueType, Converter={StaticResource TypeToStringConverter}, Mode=OneWay}" />
<TextBlock FontWeight="Bold">]</TextBlock> <TextBlock FontWeight="Bold">]</TextBlock>
</StackPanel> </StackPanel>
<TextBlock Grid.Column="1" Text="{Binding PropertyDescription.Name}" ToolTip.Tip="{Binding PropertyDescription.Description}" /> <TextBlock Grid.Column="1" Text="{CompiledBinding PropertyDescription.Name}" ToolTip.Tip="{CompiledBinding PropertyDescription.Description}" />
<TextBlock Grid.Column="2" <TextBlock Grid.Column="2"
Text="{Binding CountDisplay, Mode=OneWay}" Text="{CompiledBinding Path=CountDisplay, DataType=dataModel:DataModelListViewModel ,Mode=OneWay}"
FontFamily="Consolas" FontFamily="Consolas"
HorizontalAlignment="Right" /> HorizontalAlignment="Right" />
</Grid> </Grid>

View File

@ -4,15 +4,17 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared" xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
xmlns:vm="clr-namespace:Artemis.UI.Screens.Debugger.Performance"
x:DataType="vm:PerformanceDebugPluginViewModel"
x:Class="Artemis.UI.Screens.Debugger.Performance.PerformanceDebugPluginView"> x:Class="Artemis.UI.Screens.Debugger.Performance.PerformanceDebugPluginView">
<Border Classes="card-condensed" Margin="0 5"> <Border Classes="card-condensed" Margin="0 5">
<StackPanel> <StackPanel>
<Grid ColumnDefinitions="40,*"> <Grid ColumnDefinitions="40,*">
<shared:ArtemisIcon Grid.Column="0" Icon="{Binding Plugin.Info.ResolvedIcon}" Width="24" Height="24" /> <shared:ArtemisIcon Grid.Column="0" Icon="{CompiledBinding Plugin.Info.ResolvedIcon}" Width="24" Height="24" />
<TextBlock Grid.Column="1" VerticalAlignment="Center" Classes="h5" Text="{Binding Plugin.Info.Name}" /> <TextBlock Grid.Column="1" VerticalAlignment="Center" Classes="h5" Text="{CompiledBinding Plugin.Info.Name}" />
</Grid> </Grid>
<ItemsControl Items="{Binding Profilers}" /> <ItemsControl ItemsSource="{CompiledBinding Profilers}" />
</StackPanel> </StackPanel>
</Border> </Border>
</UserControl> </UserControl>

View File

@ -4,11 +4,12 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Artemis.UI.Screens.Debugger.Performance" xmlns:local="clr-namespace:Artemis.UI.Screens.Debugger.Performance"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:DataType="local:PerformanceDebugProfilerViewModel"
x:Class="Artemis.UI.Screens.Debugger.Performance.PerformanceDebugProfilerView"> x:Class="Artemis.UI.Screens.Debugger.Performance.PerformanceDebugProfilerView">
<StackPanel> <StackPanel>
<TextBlock Classes="subtitle" Text="{Binding Profiler.Name}" Margin="10 10 0 0" /> <TextBlock Classes="subtitle" Text="{CompiledBinding Profiler.Name}" Margin="10 10 0 0" />
<DataGrid Items="{Binding Measurements}" <DataGrid ItemsSource="{CompiledBinding Measurements}"
CanUserSortColumns="True" CanUserSortColumns="True"
IsReadOnly="True" IsReadOnly="True"
AutoGenerateColumns="False" AutoGenerateColumns="False"
@ -16,12 +17,12 @@
CanUserResizeColumns="False" CanUserResizeColumns="False"
Margin="10 5 10 10"> Margin="10 5 10 10">
<DataGrid.Columns> <DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Measurement.Identifier}" Header="Identifier" /> <DataGridTextColumn Binding="{CompiledBinding Measurement.Identifier}" Header="Identifier" />
<DataGridTextColumn Binding="{Binding Last}" Header="Last" /> <DataGridTextColumn Binding="{CompiledBinding Last}" Header="Last" />
<DataGridTextColumn Binding="{Binding Min}" Header="Min" /> <DataGridTextColumn Binding="{CompiledBinding Min}" Header="Min" />
<DataGridTextColumn Binding="{Binding Max}" Header="Max" /> <DataGridTextColumn Binding="{CompiledBinding Max}" Header="Max" />
<DataGridTextColumn Binding="{Binding Average}" Header="Average" /> <DataGridTextColumn Binding="{CompiledBinding Average}" Header="Average" />
<DataGridTextColumn Binding="{Binding Percentile}" Header="95th percentile" /> <DataGridTextColumn Binding="{CompiledBinding Percentile}" Header="95th percentile" />
</DataGrid.Columns> </DataGrid.Columns>
</DataGrid> </DataGrid>
</StackPanel> </StackPanel>

View File

@ -3,7 +3,9 @@
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"
xmlns:debugger="clr-namespace:Artemis.UI.Screens.Debugger.Performance;assembly=Artemis.UI"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:DataType="debugger:PerformanceDebugViewModel"
x:Class="Artemis.UI.Screens.Debugger.Performance.PerformanceDebugView"> x:Class="Artemis.UI.Screens.Debugger.Performance.PerformanceDebugView">
<Grid RowDefinitions="Auto,Auto,*"> <Grid RowDefinitions="Auto,Auto,*">
<StackPanel Grid.Row="0"> <StackPanel Grid.Row="0">
@ -23,18 +25,18 @@
<StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right" Margin="10 0"> <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right" Margin="10 0">
<TextBlock Text="FPS: " /> <TextBlock Text="FPS: " />
<TextBlock FontWeight="Bold" Text="{Binding CurrentFps}" /> <TextBlock FontWeight="Bold" Text="{CompiledBinding CurrentFps}" />
<TextBlock Text=" at " /> <TextBlock Text=" at " />
<TextBlock Text="{Binding RenderWidth}" /> <TextBlock Text="{CompiledBinding RenderWidth}" />
<TextBlock Text="x" /> <TextBlock Text="x" />
<TextBlock Text="{Binding RenderHeight}" /> <TextBlock Text="{CompiledBinding RenderHeight}" />
<TextBlock Text=" - Renderer: " /> <TextBlock Text=" - Renderer: " />
<TextBlock Text="{Binding Renderer}" /> <TextBlock Text="{CompiledBinding Renderer}" />
</StackPanel> </StackPanel>
<ScrollViewer Grid.Row="2" VerticalScrollBarVisibility="Auto"> <ScrollViewer Grid.Row="2" VerticalScrollBarVisibility="Auto">
<ItemsControl Items="{Binding Items}" Margin="0 0 10 0" /> <ItemsControl ItemsSource="{CompiledBinding Items}" Margin="0 0 10 0" />
</ScrollViewer> </ScrollViewer>
</Grid> </Grid>

View File

@ -26,7 +26,7 @@ public class PerformanceDebugViewModel : ActivatableViewModelBase
{ {
_coreService = coreService; _coreService = coreService;
_pluginManagementService = pluginManagementService; _pluginManagementService = pluginManagementService;
_updateTimer = new DispatcherTimer(TimeSpan.FromMilliseconds(500), DispatcherPriority.Normal, (_, _) => Update()); _updateTimer = new DispatcherTimer(TimeSpan.FromMilliseconds(500), DispatcherPriority.Background, (_, _) => Update());
DisplayName = "Performance"; DisplayName = "Performance";

View File

@ -2,7 +2,9 @@
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:local="clr-namespace:Artemis.UI.Screens.Debugger.Render"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:DataType="local:RenderDebugViewModel"
x:Class="Artemis.UI.Screens.Debugger.Render.RenderDebugView"> x:Class="Artemis.UI.Screens.Debugger.Render.RenderDebugView">
<StackPanel> <StackPanel>
<TextBlock Classes="h3">Render</TextBlock> <TextBlock Classes="h3">Render</TextBlock>
@ -15,17 +17,17 @@
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<TextBlock Text="FPS: " /> <TextBlock Text="FPS: " />
<TextBlock FontWeight="Bold" Text="{Binding CurrentFps}" /> <TextBlock FontWeight="Bold" Text="{CompiledBinding CurrentFps}" />
<TextBlock Text=" at " /> <TextBlock Text=" at " />
<TextBlock Text="{Binding RenderWidth}" /> <TextBlock Text="{CompiledBinding RenderWidth}" />
<TextBlock Text="x" /> <TextBlock Text="x" />
<TextBlock Text="{Binding RenderHeight}" /> <TextBlock Text="{CompiledBinding RenderHeight}" />
<TextBlock Text=" - Renderer: " /> <TextBlock Text=" - Renderer: " />
<TextBlock Text="{Binding Renderer}" /> <TextBlock Text="{CompiledBinding Renderer}" />
</StackPanel> </StackPanel>
<Border Classes="card" Padding="10"> <Border Classes="card" Padding="10">
<Image Name="Visualization" Source="{Binding CurrentFrame}"> <Image Name="Visualization" Source="{CompiledBinding CurrentFrame}">
<Image.Transitions> <Image.Transitions>
<Transitions> <Transitions>
<TransformOperationsTransition Property="RenderTransform" Duration="0:0:0.2" Easing="CubicEaseOut" /> <TransformOperationsTransition Property="RenderTransform" Duration="0:0:0.2" Easing="CubicEaseOut" />

View File

@ -4,6 +4,8 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia" xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="1050" mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="1050"
xmlns:devicedetectinput="clr-namespace:Artemis.UI.Screens.Device;assembly=Artemis.UI"
x:DataType="devicedetectinput:DeviceDetectInputViewModel"
x:Class="Artemis.UI.Screens.Device.DeviceDetectInputView"> x:Class="Artemis.UI.Screens.Device.DeviceDetectInputView">
<StackPanel Width="500"> <StackPanel Width="500">
<!-- TODO: Replace with Text.Run stuff once available --> <!-- TODO: Replace with Text.Run stuff once available -->
@ -15,12 +17,12 @@
Width="300" Width="300"
Height="300" Height="300"
HorizontalAlignment="Center" HorizontalAlignment="Center"
IsVisible="{Binding !IsMouse}" /> IsVisible="{CompiledBinding !IsMouse}" />
<avalonia:MaterialIcon Kind="Mouse" <avalonia:MaterialIcon Kind="Mouse"
Width="300" Width="300"
Height="300" Height="300"
HorizontalAlignment="Center" HorizontalAlignment="Center"
IsVisible="{Binding IsMouse}" /> IsVisible="{CompiledBinding IsMouse}" />
<TextBlock TextWrapping="Wrap" Margin="0 10"> <TextBlock TextWrapping="Wrap" Margin="0 10">
This will teach Artemis to associate button/key presses with this specific device. This will teach Artemis to associate button/key presses with this specific device.

View File

@ -7,6 +7,7 @@ using Artemis.Core.Services;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.Builders; using Artemis.UI.Shared.Services.Builders;
using Avalonia.Threading;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using ReactiveUI; using ReactiveUI;
using RGB.NET.Core; using RGB.NET.Core;
@ -57,10 +58,13 @@ public class DeviceDetectInputViewModel : ContentDialogViewModelBase
private void InputServiceOnDeviceIdentified() private void InputServiceOnDeviceIdentified()
{ {
ContentDialog?.Hide(ContentDialogResult.Primary); Dispatcher.UIThread.Post(() =>
_notificationService.CreateNotification() {
.WithMessage($"{Device.RgbDevice.DeviceInfo.DeviceName} identified 😁") ContentDialog?.Hide(ContentDialogResult.Primary);
.WithSeverity(NotificationSeverity.Success) _notificationService.CreateNotification()
.Show(); .WithMessage($"{Device.RgbDevice.DeviceInfo.DeviceName} identified 😁")
.WithSeverity(NotificationSeverity.Success)
.Show();
});
} }
} }

View File

@ -54,9 +54,9 @@
<Border Grid.Column="2" Classes="card-condensed" CornerRadius="10 0 0 0" Margin="0 10 0 0" Background="#ff323232"> <Border Grid.Column="2" Classes="card-condensed" CornerRadius="10 0 0 0" Margin="0 10 0 0" Background="#ff323232">
<Panel> <Panel>
<TabControl Items="{CompiledBinding Tabs}" IsVisible="{CompiledBinding Tabs.Count}" Padding="12"> <TabControl ItemsSource="{CompiledBinding Tabs}" IsVisible="{CompiledBinding Tabs.Count}" Padding="12">
<TabControl.ItemTemplate> <TabControl.ItemTemplate>
<DataTemplate> <DataTemplate x:DataType="shared:ActivatableViewModelBase">
<TextBlock Text="{CompiledBinding DisplayName}" /> <TextBlock Text="{CompiledBinding DisplayName}" />
</DataTemplate> </DataTemplate>
</TabControl.ItemTemplate> </TabControl.ItemTemplate>

View File

@ -5,53 +5,56 @@
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia" xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared" xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
xmlns:local="clr-namespace:Artemis.UI.Screens.Device"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:DataType="local:DeviceSettingsViewModel"
x:Class="Artemis.UI.Screens.Device.DeviceSettingsView"> x:Class="Artemis.UI.Screens.Device.DeviceSettingsView">
<Border Classes="card" Padding="0" Width="200" ClipToBounds="True" Margin="5"> <Border Classes="card" Padding="0" Width="200" ClipToBounds="True" Margin="5">
<Grid RowDefinitions="140,*,Auto"> <Grid RowDefinitions="140,*,Auto">
<Rectangle Grid.Row="0" Fill="{DynamicResource CheckerboardBrush}"/> <Rectangle Grid.Row="0" Fill="{DynamicResource CheckerboardBrush}" />
<shared:DeviceVisualizer VerticalAlignment="Center" <shared:DeviceVisualizer Grid.Row="0"
VerticalAlignment="Center"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Margin="5" Margin="5"
ShowColors="False" ShowColors="False"
Device="{Binding Device}" Device="{CompiledBinding Device}"
Grid.Row="0" /> RenderOptions.BitmapInterpolationMode="HighQuality" />
<Button Grid.Row="0" <Button Grid.Row="0"
Classes="icon-button icon-button-large" Classes="icon-button icon-button-large"
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
HorizontalAlignment="Right" HorizontalAlignment="Right"
Margin="5" Margin="5"
ToolTip.Tip="Identify" ToolTip.Tip="Identify"
Command="{Binding IdentifyDevice}"> Command="{CompiledBinding IdentifyDevice}">
<avalonia:MaterialIcon Kind="AlarmLight" /> <avalonia:MaterialIcon Kind="AlarmLight" />
</Button> </Button>
<StackPanel Grid.Row="1" Margin="8 24 8 0"> <StackPanel Grid.Row="1" Margin="8 24 8 0">
<TextBlock Text="{Binding Device.RgbDevice.DeviceInfo.Model}" /> <TextBlock Text="{CompiledBinding Device.RgbDevice.DeviceInfo.Model}" />
<StackPanel> <StackPanel>
<TextBlock TextWrapping="Wrap" Classes="subtitle" Text="{Binding Device.RgbDevice.DeviceInfo.Manufacturer, Mode=OneWay}" /> <TextBlock TextWrapping="Wrap" Classes="subtitle" Text="{CompiledBinding Device.RgbDevice.DeviceInfo.Manufacturer, Mode=OneWay}" />
<TextBlock TextWrapping="Wrap" Classes="subtitle" Text="{Binding Device.DeviceType, Mode=OneWay}" /> <TextBlock TextWrapping="Wrap" Classes="subtitle" Text="{CompiledBinding Device.DeviceType, Mode=OneWay}" />
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
<Grid Grid.Row="2" Margin="10" ColumnDefinitions="25,*"> <Grid Grid.Row="2" Margin="10" ColumnDefinitions="25,*">
<CheckBox IsChecked="{Binding IsDeviceEnabled}" /> <CheckBox IsChecked="{CompiledBinding IsDeviceEnabled}" />
<SplitButton Grid.Column="1" Content="Properties" Command="{Binding ViewProperties}" HorizontalAlignment="Right"> <SplitButton Grid.Column="1" Content="Properties" Command="{CompiledBinding ViewProperties}" HorizontalAlignment="Right">
<SplitButton.Flyout> <SplitButton.Flyout>
<MenuFlyout Placement="Bottom"> <MenuFlyout Placement="Bottom">
<MenuItem Header="Open plugin directory" Command="{Binding OpenPluginDirectory}"> <MenuItem Header="Open plugin directory" Command="{CompiledBinding OpenPluginDirectory}">
<MenuItem.Icon> <MenuItem.Icon>
<avalonia:MaterialIcon Kind="FolderOpen" /> <avalonia:MaterialIcon Kind="FolderOpen" />
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<MenuItem Header="Identity input" Command="{Binding DetectInput}"> <MenuItem Header="Identity input" Command="{CompiledBinding DetectInput}">
<MenuItem.Icon> <MenuItem.Icon>
<avalonia:MaterialIcon Kind="GestureDoubleTap" /> <avalonia:MaterialIcon Kind="GestureDoubleTap" />
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
</MenuFlyout> </MenuFlyout>
</SplitButton.Flyout> </SplitButton.Flyout>
</SplitButton> </SplitButton>
</Grid> </Grid>
</Grid> </Grid>
</Border> </Border>

View File

@ -3,29 +3,31 @@
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:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia" xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
xmlns:device="clr-namespace:Artemis.UI.Screens.Device"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.Device.DeviceInfoTabView"> x:Class="Artemis.UI.Screens.Device.DeviceInfoTabView"
x:DataType="device:DeviceInfoTabViewModel">
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto"> <ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<Grid RowDefinitions="Auto,*" ColumnDefinitions="*,*" Margin="-5"> <Grid RowDefinitions="Auto,*" ColumnDefinitions="*,*" Margin="-5">
<!-- First row --> <!-- First row -->
<Border Classes="card" Grid.Column="0" Grid.Row="0" Margin="5"> <Border Classes="card" Grid.Column="0" Grid.Row="0" Margin="5">
<StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Top"> <StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Top">
<TextBlock FontWeight="Bold">Device name</TextBlock> <TextBlock FontWeight="Bold">Device name</TextBlock>
<TextBlock TextWrapping="Wrap" Text="{Binding Device.RgbDevice.DeviceInfo.DeviceName}" /> <TextBlock TextWrapping="Wrap" Text="{CompiledBinding Device.RgbDevice.DeviceInfo.DeviceName}" />
<Border Classes="card-separator" /> <Border Classes="card-separator" />
<TextBlock FontWeight="Bold">Manufacturer</TextBlock> <TextBlock FontWeight="Bold">Manufacturer</TextBlock>
<TextBlock TextWrapping="Wrap" Text="{Binding Device.RgbDevice.DeviceInfo.Manufacturer}" /> <TextBlock TextWrapping="Wrap" Text="{CompiledBinding Device.RgbDevice.DeviceInfo.Manufacturer}" />
<Border Classes="card-separator" /> <Border Classes="card-separator" />
<TextBlock FontWeight="Bold">Device type</TextBlock> <TextBlock FontWeight="Bold">Device type</TextBlock>
<TextBlock TextWrapping="Wrap" Text="{Binding Device.DeviceType}" /> <TextBlock TextWrapping="Wrap" Text="{CompiledBinding Device.DeviceType}" />
<Border Classes="card-separator" IsVisible="{Binding IsKeyboard}" /> <Border Classes="card-separator" IsVisible="{CompiledBinding IsKeyboard}" />
<StackPanel IsVisible="{Binding IsKeyboard}"> <StackPanel IsVisible="{CompiledBinding IsKeyboard}">
<TextBlock FontWeight="Bold">Physical layout</TextBlock> <TextBlock FontWeight="Bold">Physical layout</TextBlock>
<TextBlock TextWrapping="Wrap" Text="{Binding Device.PhysicalLayout}" /> <TextBlock TextWrapping="Wrap" Text="{CompiledBinding Device.PhysicalLayout}" />
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</Border> </Border>
@ -33,20 +35,20 @@
<Border Classes="card" Grid.Row="0" Grid.Column="1" Margin="5"> <Border Classes="card" Grid.Row="0" Grid.Column="1" Margin="5">
<StackPanel VerticalAlignment="Top"> <StackPanel VerticalAlignment="Top">
<TextBlock FontWeight="Bold">Size (1px = 1mm)</TextBlock> <TextBlock FontWeight="Bold">Size (1px = 1mm)</TextBlock>
<TextBlock TextWrapping="Wrap" Text="{Binding Device.RgbDevice.Size}" /> <TextBlock TextWrapping="Wrap" Text="{CompiledBinding Device.RgbDevice.Size}" />
<Border Classes="card-separator" /> <Border Classes="card-separator" />
<TextBlock FontWeight="Bold">Location (1px = 1mm)</TextBlock> <TextBlock FontWeight="Bold">Location (1px = 1mm)</TextBlock>
<TextBlock TextWrapping="Wrap" Text="{Binding Device.RgbDevice.Location}" /> <TextBlock TextWrapping="Wrap" Text="{CompiledBinding Device.RgbDevice.Location}" />
<Border Classes="card-separator" /> <Border Classes="card-separator" />
<TextBlock FontWeight="Bold">Rotation (degrees)</TextBlock> <TextBlock FontWeight="Bold">Rotation (degrees)</TextBlock>
<TextBlock TextWrapping="Wrap" Text="{Binding Device.RgbDevice.Rotation.Degrees}" /> <TextBlock TextWrapping="Wrap" Text="{CompiledBinding Device.RgbDevice.Rotation.Degrees}" />
<Border Classes="card-separator" IsVisible="{Binding IsKeyboard}" /> <Border Classes="card-separator" IsVisible="{CompiledBinding IsKeyboard}" />
<StackPanel IsVisible="{Binding IsKeyboard}"> <StackPanel IsVisible="{CompiledBinding IsKeyboard}">
<TextBlock FontWeight="Bold">Logical layout</TextBlock> <TextBlock FontWeight="Bold">Logical layout</TextBlock>
<TextBlock TextWrapping="Wrap" Text="{Binding Device.LogicalLayout}" /> <TextBlock TextWrapping="Wrap" Text="{CompiledBinding Device.LogicalLayout}" />
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</Border> </Border>
@ -59,14 +61,13 @@
<Button Grid.Column="1" <Button Grid.Column="1"
Classes="icon-button" Classes="icon-button"
ToolTip.Tip="Copy path to clipboard" ToolTip.Tip="Copy path to clipboard"
Command="{Binding CopyToClipboard}" Click="LayoutPathButton_OnClick">
CommandParameter="{Binding DefaultLayoutPath}">
<avalonia:MaterialIcon Kind="ContentCopy" Width="18" Height="18" /> <avalonia:MaterialIcon Kind="ContentCopy" Width="18" Height="18" />
</Button> </Button>
</Grid> </Grid>
<TextBlock <TextBlock
TextWrapping="Wrap" TextWrapping="Wrap"
Text="{Binding DefaultLayoutPath}" /> Text="{CompiledBinding DefaultLayoutPath}" />
<Border Classes="card-separator" /> <Border Classes="card-separator" />
<Grid ColumnDefinitions="*,Auto"> <Grid ColumnDefinitions="*,Auto">
@ -74,12 +75,11 @@
<Button Grid.Column="1" <Button Grid.Column="1"
Classes="icon-button" Classes="icon-button"
ToolTip.Tip="Copy path to clipboard" ToolTip.Tip="Copy path to clipboard"
Command="{Binding CopyToClipboard}" Click="ImagePathButton_OnClick">
CommandParameter="{Binding Device.Layout.Image.LocalPath}">
<avalonia:MaterialIcon Kind="ContentCopy" Width="18" Height="18" /> <avalonia:MaterialIcon Kind="ContentCopy" Width="18" Height="18" />
</Button> </Button>
</Grid> </Grid>
<TextBlock TextWrapping="Wrap" Text="{Binding Device.Layout.Image.LocalPath}" /> <TextBlock TextWrapping="Wrap" Text="{CompiledBinding Device.Layout.Image.LocalPath}" />
</StackPanel> </StackPanel>
</Border> </Border>
</Grid> </Grid>

View File

@ -1,3 +1,5 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
@ -10,4 +12,13 @@ public partial class DeviceInfoTabView : ReactiveUserControl<DeviceInfoTabViewMo
InitializeComponent(); InitializeComponent();
} }
private void LayoutPathButton_OnClick(object? sender, RoutedEventArgs e)
{
TopLevel.GetTopLevel(this).Clipboard.SetTextAsync(ViewModel.DefaultLayoutPath);
}
private void ImagePathButton_OnClick(object? sender, RoutedEventArgs e)
{
TopLevel.GetTopLevel(this).Clipboard.SetTextAsync(ViewModel.Device.Layout.Image.LocalPath);
}
} }

View File

@ -3,6 +3,7 @@ using Artemis.Core;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using Avalonia; using Avalonia;
using Avalonia.Controls;
using RGB.NET.Core; using RGB.NET.Core;
namespace Artemis.UI.Screens.Device; namespace Artemis.UI.Screens.Device;
@ -25,13 +26,4 @@ public class DeviceInfoTabViewModel : ActivatableViewModelBase
public ArtemisDevice Device { get; } public ArtemisDevice Device { get; }
public string DefaultLayoutPath { get; } public string DefaultLayoutPath { get; }
public async Task CopyToClipboard(string content)
{
if (Application.Current?.Clipboard == null)
return;
await Application.Current.Clipboard.SetTextAsync(content);
_notificationService.CreateNotification().WithMessage("Copied path to clipboard.").Show();
}
} }

View File

@ -13,22 +13,22 @@
<converters:LedIdToStringConverter x:Key="LedIdToStringConverter" /> <converters:LedIdToStringConverter x:Key="LedIdToStringConverter" />
<shared:ToStringConverter x:Key="ToStringConverter" /> <shared:ToStringConverter x:Key="ToStringConverter" />
</UserControl.Resources> </UserControl.Resources>
<DataGrid Items="{CompiledBinding LedViewModels}" CanUserSortColumns="True" AutoGenerateColumns="False"> <DataGrid ItemsSource="{CompiledBinding LedViewModels}" CanUserSortColumns="True" AutoGenerateColumns="False">
<DataGrid.Columns > <DataGrid.Columns>
<DataGridTemplateColumn CanUserSort="False" CanUserReorder="False" Header="Highlight"> <DataGridTemplateColumn CanUserSort="False" CanUserReorder="False" Header="Highlight">
<DataGridTemplateColumn.CellTemplate> <DataGridTemplateColumn.CellTemplate>
<DataTemplate> <DataTemplate>
<CheckBox IsChecked="{Binding IsSelected, Mode=TwoWay}" /> <CheckBox IsChecked="{CompiledBinding IsSelected, Mode=TwoWay}" />
</DataTemplate> </DataTemplate>
</DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn> </DataGridTemplateColumn>
<DataGridTextColumn Binding="{Binding ArtemisLed.RgbLed.Id, Converter={StaticResource LedIdToStringConverter}, Mode=OneWay}" Header="LED ID" Width="Auto" /> <DataGridTextColumn Binding="{CompiledBinding ArtemisLed.RgbLed.Id, Converter={StaticResource LedIdToStringConverter}, Mode=OneWay}" Header="LED ID" Width="Auto" />
<DataGridTextColumn Binding="{Binding ArtemisLed.RgbLed.Color, Converter={StaticResource ToStringConverter}, Mode=OneWay}" Header="Color (ARGB)" Width="Auto" CanUserSort="False" /> <DataGridTextColumn Binding="{CompiledBinding ArtemisLed.RgbLed.Color, Converter={StaticResource ToStringConverter}, Mode=OneWay}" Header="Color (ARGB)" Width="Auto" CanUserSort="False" />
<DataGridTextColumn Binding="{Binding ArtemisLed.Layout.Image, Converter={StaticResource UriToFileNameConverter}, Mode=OneWay}" Header="Image file" CanUserSort="False" /> <DataGridTextColumn Binding="{CompiledBinding ArtemisLed.Layout.Image, Converter={StaticResource UriToFileNameConverter}, Mode=OneWay}" Header="Image file" CanUserSort="False" />
<DataGridTextColumn Binding="{Binding ArtemisLed.RgbLed.Shape}" Header="Shape" /> <DataGridTextColumn Binding="{CompiledBinding ArtemisLed.RgbLed.Shape}" Header="Shape" />
<DataGridTextColumn Binding="{Binding ArtemisLed.RgbLed.Location, Converter={StaticResource ToStringConverter}, Mode=OneWay}" Header="Location" Width="Auto" CanUserSort="False" /> <DataGridTextColumn Binding="{CompiledBinding ArtemisLed.RgbLed.Location, Converter={StaticResource ToStringConverter}, Mode=OneWay}" Header="Location" Width="Auto" CanUserSort="False" />
<DataGridTextColumn Binding="{Binding ArtemisLed.RgbLed.Size, Converter={StaticResource ToStringConverter}, Mode=OneWay}" Header="Size" Width="Auto" CanUserSort="False" /> <DataGridTextColumn Binding="{CompiledBinding ArtemisLed.RgbLed.Size, Converter={StaticResource ToStringConverter}, Mode=OneWay}" Header="Size" Width="Auto" CanUserSort="False" />
<DataGridTextColumn Binding="{Binding ArtemisLed.RgbLed.CustomData}" Header="LED data" Width="Auto" /> <DataGridTextColumn Binding="{CompiledBinding ArtemisLed.RgbLed.CustomData}" Header="LED data" Width="Auto" />
</DataGrid.Columns> </DataGrid.Columns>
</DataGrid> </DataGrid>
</UserControl> </UserControl>

View File

@ -16,9 +16,9 @@
</TextBlock> </TextBlock>
<AutoCompleteBox HorizontalAlignment="Stretch" <AutoCompleteBox HorizontalAlignment="Stretch"
Items="{CompiledBinding Regions}" ItemsSource="{CompiledBinding Regions}"
SelectedItem="{CompiledBinding SelectedRegion}" SelectedItem="{CompiledBinding SelectedRegion}"
ValueMemberBinding="{Binding EnglishName}" ValueMemberBinding="{CompiledBinding EnglishName, DataType=globalization:RegionInfo}"
Watermark="Enter keyboard country name" Watermark="Enter keyboard country name"
FilterMode="Custom" FilterMode="Custom"
MaxDropDownHeight="200" MaxDropDownHeight="200"

View File

@ -157,7 +157,7 @@
The device layout is used to determine the position of LEDs and to create the visual representation of the device you see on the left side of this window. The device layout is used to determine the position of LEDs and to create the visual representation of the device you see on the left side of this window.
</TextBlock> </TextBlock>
<CheckBox Margin="0 0 0 5" IsChecked="{Binding Device.DisableDefaultLayout}"> <CheckBox Margin="0 0 0 5" IsChecked="{CompiledBinding Device.DisableDefaultLayout}">
<CheckBox.Content> <CheckBox.Content>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock>Don't load default layout</TextBlock> <TextBlock>Don't load default layout</TextBlock>
@ -169,13 +169,13 @@
</CheckBox> </CheckBox>
<Grid> <Grid>
<TextBox Text="{Binding Device.CustomLayoutPath}" <TextBox Text="{CompiledBinding Device.CustomLayoutPath}"
Watermark="Custom layout path" Watermark="Custom layout path"
UseFloatingWatermark="True" UseFloatingWatermark="True"
IsReadOnly="True" IsReadOnly="True"
PointerReleased="InputElement_OnPointerReleased" PointerReleased="InputElement_OnPointerReleased"
Padding="10 5 36 6" /> Padding="10 5 36 6" />
<Button Classes="AppBarButton" HorizontalAlignment="Right" Command="{Binding ClearCustomLayout}" Padding="6" Margin="5 0"> <Button Classes="AppBarButton" HorizontalAlignment="Right" Command="{CompiledBinding ClearCustomLayout}" Padding="6" Margin="5 0">
<avalonia:MaterialIcon Kind="CloseCircle" /> <avalonia:MaterialIcon Kind="CloseCircle" />
</Button> </Button>
</Grid> </Grid>
@ -189,14 +189,14 @@
<Grid Grid.Row="1" ColumnDefinitions="Auto,*"> <Grid Grid.Row="1" ColumnDefinitions="Auto,*">
<Button Grid.Column="0" <Button Grid.Column="0"
IsVisible="{CompiledBinding RequiresManualSetup}" IsVisible="{CompiledBinding RequiresManualSetup}"
Command="{Binding RestartSetup}" Command="{CompiledBinding RestartSetup}"
ToolTip.Tip="Restart device setup, allowing you to select a new physical and logical layout"> ToolTip.Tip="Restart device setup, allowing you to select a new physical and logical layout">
Restart setup Restart setup
</Button> </Button>
<StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right"> <StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right">
<Button IsCancel="True" Command="{Binding Reset}" Margin="0 0 5 0">Reset</Button> <Button IsCancel="True" Command="{CompiledBinding Reset}" Margin="0 0 5 0">Reset</Button>
<Button Classes="accent" IsDefault="True" Command="{Binding Apply}">Apply</Button> <Button Classes="accent" IsDefault="True" Command="{CompiledBinding Apply}">Apply</Button>
</StackPanel> </StackPanel>
</Grid> </Grid>
</Grid> </Grid>

View File

@ -42,14 +42,14 @@
</StackPanel> </StackPanel>
<DataGrid Grid.Row="2" <DataGrid Grid.Row="2"
Items="{CompiledBinding InputMappings}" ItemsSource="{CompiledBinding InputMappings}"
CanUserSortColumns="True" CanUserSortColumns="True"
IsReadOnly="True" IsReadOnly="True"
AutoGenerateColumns="False" AutoGenerateColumns="False"
Margin="10"> Margin="10">
<DataGrid.Columns> <DataGrid.Columns>
<DataGridTextColumn Binding="{Binding [0].RgbLed.Id}" Header="Original LED ID" Width="*" /> <DataGridTextColumn Binding="{CompiledBinding Path=Original.RgbLed.Id}" Header="Original LED ID" Width="*" />
<DataGridTextColumn Binding="{Binding [1].RgbLed.Id}" Header="Remapped LED ID" Width="*" /> <DataGridTextColumn Binding="{CompiledBinding Path=Replacement.RgbLed.Id}" Header="Remapped LED ID" Width="*" />
<DataGridTemplateColumn Width="Auto" IsReadOnly="True"> <DataGridTemplateColumn Width="Auto" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate> <DataGridTemplateColumn.CellTemplate>

View File

@ -1,4 +1,4 @@
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.Linq; using System.Linq;
using System.Reactive.Disposables; using System.Reactive.Disposables;
@ -6,8 +6,10 @@ using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Exceptions; using Artemis.UI.Exceptions;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using HidSharp.Reports.Units;
using ReactiveUI; using ReactiveUI;
using RGB.NET.Core; using RGB.NET.Core;
using Unit = System.Reactive.Unit;
namespace Artemis.UI.Screens.Device; namespace Artemis.UI.Screens.Device;
@ -29,14 +31,14 @@ public class InputMappingsTabViewModel : ActivatableViewModelBase
Device = device; Device = device;
DisplayName = "Input Mappings"; DisplayName = "Input Mappings";
InputMappings = new ObservableCollection<(ArtemisLed, ArtemisLed)>(); InputMappings = new ObservableCollection<ArtemisInputMapping>();
DeleteMapping = ReactiveCommand.Create<ArtemisInputMapping>(ExecuteDeleteMapping);
this.WhenActivated(d => this.WhenActivated(d =>
{ {
_selectedLeds.CollectionChanged += SelectedLedsOnCollectionChanged; _selectedLeds.CollectionChanged += SelectedLedsOnCollectionChanged;
_inputService.KeyboardKeyUp += InputServiceOnKeyboardKeyUp; _inputService.KeyboardKeyUp += InputServiceOnKeyboardKeyUp;
UpdateInputMappings(); UpdateInputMappings();
Disposable.Create(() => Disposable.Create(() =>
{ {
_selectedLeds.CollectionChanged -= SelectedLedsOnCollectionChanged; _selectedLeds.CollectionChanged -= SelectedLedsOnCollectionChanged;
@ -46,6 +48,8 @@ public class InputMappingsTabViewModel : ActivatableViewModelBase
}); });
} }
public ReactiveCommand<ArtemisInputMapping, Unit> DeleteMapping { get; }
public ArtemisDevice Device { get; } public ArtemisDevice Device { get; }
public ArtemisLed? SelectedLed public ArtemisLed? SelectedLed
@ -54,11 +58,11 @@ public class InputMappingsTabViewModel : ActivatableViewModelBase
set => RaiseAndSetIfChanged(ref _selectedLed, value); set => RaiseAndSetIfChanged(ref _selectedLed, value);
} }
public ObservableCollection<(ArtemisLed, ArtemisLed)> InputMappings { get; } public ObservableCollection<ArtemisInputMapping> InputMappings { get; }
public void DeleteMapping((ArtemisLed, ArtemisLed) inputMapping) private void ExecuteDeleteMapping(ArtemisInputMapping inputMapping)
{ {
Device.InputMappings.Remove(inputMapping.Item1); Device.InputMappings.Remove(inputMapping.Original);
UpdateInputMappings(); UpdateInputMappings();
} }
@ -88,7 +92,7 @@ public class InputMappingsTabViewModel : ActivatableViewModelBase
if (InputMappings.Any()) if (InputMappings.Any())
InputMappings.Clear(); InputMappings.Clear();
foreach ((ArtemisLed, ArtemisLed) tuple in Device.InputMappings.Select(m => (m.Key, m.Value))) foreach (ArtemisInputMapping tuple in Device.InputMappings.Select(m => new ArtemisInputMapping(m.Key, m.Value)))
InputMappings.Add(tuple); InputMappings.Add(tuple);
} }
@ -96,4 +100,29 @@ public class InputMappingsTabViewModel : ActivatableViewModelBase
{ {
SelectedLed = _selectedLeds.FirstOrDefault(); SelectedLed = _selectedLeds.FirstOrDefault();
} }
}
/// <summary>
/// Represents a pair of LEDs, the original and the replacement
/// </summary>
public class ArtemisInputMapping
{
/// <summary>
/// Creates a new instance of the <see cref="ArtemisInputMapping" /> class
/// </summary>
public ArtemisInputMapping(ArtemisLed original, ArtemisLed replacement)
{
Original = original;
Replacement = replacement;
}
/// <summary>
/// The original LED
/// </summary>
public ArtemisLed Original { get; set; }
/// <summary>
/// The replacement LED
/// </summary>
public ArtemisLed Replacement { get; set; }
} }

View File

@ -13,7 +13,8 @@
VerticalAlignment="Top" VerticalAlignment="Top"
Source="/Assets/Images/home-banner.png" Source="/Assets/Images/home-banner.png"
Height="200" Height="200"
Stretch="UniformToFill" /> Stretch="UniformToFill"
RenderOptions.BitmapInterpolationMode="HighQuality"/>
<!-- TODO: Replace with a shadow when available --> <!-- TODO: Replace with a shadow when available -->
<TextBlock Grid.Row="0" <TextBlock Grid.Row="0"

View File

@ -31,7 +31,7 @@
<ListBox Grid.Row="0" <ListBox Grid.Row="0"
Grid.Column="0" Grid.Column="0"
Items="{CompiledBinding Prerequisites}" ItemsSource="{CompiledBinding Prerequisites}"
SelectedItem="{CompiledBinding ActivePrerequisite, Mode=OneWay}" SelectedItem="{CompiledBinding ActivePrerequisite, Mode=OneWay}"
IsHitTestVisible="False"> IsHitTestVisible="False">
<ListBox.ItemTemplate> <ListBox.ItemTemplate>

View File

@ -31,7 +31,7 @@
<ListBox Grid.Row="0" <ListBox Grid.Row="0"
Grid.Column="0" Grid.Column="0"
Items="{CompiledBinding Prerequisites}" ItemsSource="{CompiledBinding Prerequisites}"
SelectedItem="{CompiledBinding ActivePrerequisite, Mode=OneWay}" SelectedItem="{CompiledBinding ActivePrerequisite, Mode=OneWay}"
IsHitTestVisible="False"> IsHitTestVisible="False">
<ListBox.ItemTemplate> <ListBox.ItemTemplate>

View File

@ -38,7 +38,7 @@
IsVisible="{CompiledBinding LoadException, Converter={x:Static ObjectConverters.IsNotNull}}" IsVisible="{CompiledBinding LoadException, Converter={x:Static ObjectConverters.IsNotNull}}"
Foreground="#E74C4C" Foreground="#E74C4C"
ToolTip.Tip="An exception occurred while enabling this feature, click to view" ToolTip.Tip="An exception occurred while enabling this feature, click to view"
Command="{Binding ViewLoadException}"> Command="{CompiledBinding ViewLoadException}">
<avalonia:MaterialIcon Kind="AlertCircle" /> <avalonia:MaterialIcon Kind="AlertCircle" />
</Button> </Button>

View File

@ -13,7 +13,7 @@
<Border Grid.Column="1" BorderBrush="{DynamicResource ButtonBorderBrush}" BorderThickness="1 0 0 0" Margin="10 0 0 0" Padding="10 0 0 0"> <Border Grid.Column="1" BorderBrush="{DynamicResource ButtonBorderBrush}" BorderThickness="1 0 0 0" Margin="10 0 0 0" Padding="10 0 0 0">
<Grid RowDefinitions="Auto,*"> <Grid RowDefinitions="Auto,*">
<TextBlock Classes="h5">Plugin features</TextBlock> <TextBlock Classes="h5">Plugin features</TextBlock>
<ListBox Grid.Row="1" MaxHeight="135" Items="{CompiledBinding PluginFeatures}" /> <ListBox Grid.Row="1" MaxHeight="135" ItemsSource="{CompiledBinding PluginFeatures}" />
</Grid> </Grid>
</Border> </Border>
</Grid> </Grid>

View File

@ -13,7 +13,7 @@
Height="800" Height="800"
WindowStartupLocation="CenterOwner"> WindowStartupLocation="CenterOwner">
<Panel> <Panel>
<ContentControl Content="{Binding ConfigurationViewModel}"></ContentControl> <ContentControl Content="{CompiledBinding ConfigurationViewModel}"></ContentControl>
<StackPanel Classes="notification-container" Name="NotificationContainer" VerticalAlignment="Bottom" HorizontalAlignment="Right" /> <StackPanel Classes="notification-container" Name="NotificationContainer" VerticalAlignment="Bottom" HorizontalAlignment="Right" />
</Panel> </Panel>
</windowing:AppWindow> </windowing:AppWindow>

View File

@ -22,7 +22,7 @@
<TextBlock Grid.Column="1" Grid.Row="0" Classes="h5 no-margin" Text="{CompiledBinding Plugin.Info.Name}" /> <TextBlock Grid.Column="1" Grid.Row="0" Classes="h5 no-margin" Text="{CompiledBinding Plugin.Info.Name}" />
<ItemsControl Grid.Column="2" Grid.Row="0" IsVisible="{CompiledBinding Platforms.Count}" Items="{CompiledBinding Platforms}" HorizontalAlignment="Right"> <ItemsControl Grid.Column="2" Grid.Row="0" IsVisible="{CompiledBinding Platforms.Count}" ItemsSource="{CompiledBinding Platforms}" HorizontalAlignment="Right">
<ItemsControl.ItemsPanel> <ItemsControl.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
<StackPanel Spacing="5" Orientation="Horizontal" /> <StackPanel Spacing="5" Orientation="Horizontal" />

View File

@ -24,7 +24,7 @@
Grid.Row="1" Grid.Row="1"
Classes="condition-type" Classes="condition-type"
PlaceholderText="Select an activation type" PlaceholderText="Select an activation type"
Items="{CompiledBinding ConditionTypeViewModels}" ItemsSource="{CompiledBinding ConditionTypeViewModels}"
IsEnabled="{CompiledBinding ProfileElement, Converter={x:Static ObjectConverters.IsNotNull}}" IsEnabled="{CompiledBinding ProfileElement, Converter={x:Static ObjectConverters.IsNotNull}}"
SelectedItem="{CompiledBinding SelectedConditionTypeViewModel}" SelectedItem="{CompiledBinding SelectedConditionTypeViewModel}"
HorizontalAlignment="Stretch"> HorizontalAlignment="Stretch">

View File

@ -55,7 +55,7 @@ public class PlaybackViewModel : ActivatableViewModelBase
_keyBindingsEnabled = Shared.UI.KeyBindingsEnabled.ToProperty(this, vm => vm.KeyBindingsEnabled).DisposeWith(d); _keyBindingsEnabled = Shared.UI.KeyBindingsEnabled.ToProperty(this, vm => vm.KeyBindingsEnabled).DisposeWith(d);
_lastUpdate = DateTime.MinValue; _lastUpdate = DateTime.MinValue;
DispatcherTimer updateTimer = new(TimeSpan.FromMilliseconds(60.0 / 1000), DispatcherPriority.Render, Update); DispatcherTimer updateTimer = new(TimeSpan.FromMilliseconds(60.0 / 1000), DispatcherPriority.Background, Update);
updateTimer.Start(); updateTimer.Start();
Disposable.Create(() => Disposable.Create(() =>
{ {

View File

@ -37,7 +37,7 @@
<Border Grid.Row="1" Classes="card" Margin="0 15"> <Border Grid.Row="1" Classes="card" Margin="0 15">
<Panel> <Panel>
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Hidden" IsVisible="{CompiledBinding AdaptionHints.Count}"> <ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Hidden" IsVisible="{CompiledBinding AdaptionHints.Count}">
<ItemsControl Items="{CompiledBinding AdaptionHints}" Classes="adaption-hints"> <ItemsControl ItemsSource="{CompiledBinding AdaptionHints}" Classes="adaption-hints">
<ItemsControl.ItemsPanel> <ItemsControl.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
<StackPanel Spacing="10" /> <StackPanel Spacing="10" />
@ -46,7 +46,7 @@
<ItemsControl.ItemTemplate> <ItemsControl.ItemTemplate>
<DataTemplate> <DataTemplate>
<Border Classes="card-condensed"> <Border Classes="card-condensed">
<ContentControl Content="{Binding}"></ContentControl> <ContentControl Content="{CompiledBinding}"></ContentControl>
</Border> </Border>
</DataTemplate> </DataTemplate>
</ItemsControl.ItemTemplate> </ItemsControl.ItemTemplate>
@ -67,22 +67,22 @@
</Border> </Border>
<Grid Grid.Row="2" ColumnDefinitions="*,Auto"> <Grid Grid.Row="2" ColumnDefinitions="*,Auto">
<Button Grid.Row="0" Grid.Column="0" Command="{Binding AutoDetermineHints}">Auto-determine hints</Button> <Button Grid.Row="0" Grid.Column="0" Command="{CompiledBinding AutoDetermineHints}">Auto-determine hints</Button>
<StackPanel Grid.Row="0" Grid.Column="1" Orientation="Horizontal" Spacing="5"> <StackPanel Grid.Row="0" Grid.Column="1" Orientation="Horizontal" Spacing="5">
<DropDownButton> <DropDownButton>
<DropDownButton.Flyout> <DropDownButton.Flyout>
<MenuFlyout Placement="Top"> <MenuFlyout Placement="Top">
<MenuItem Header="Category hint" Command="{Binding AddCategoryHint}"> <MenuItem Header="Category hint" Command="{CompiledBinding AddCategoryHint}">
<MenuItem.Icon> <MenuItem.Icon>
<avalonia:MaterialIcon Kind="Desk" /> <avalonia:MaterialIcon Kind="Desk" />
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<MenuItem Header="Device type hint" Command="{Binding AddDeviceHint}"> <MenuItem Header="Device type hint" Command="{CompiledBinding AddDeviceHint}">
<MenuItem.Icon> <MenuItem.Icon>
<avalonia:MaterialIcon Kind="Devices" /> <avalonia:MaterialIcon Kind="Devices" />
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<MenuItem Header="Keyboard-section hint" Command="{Binding AddKeyboardSectionHint}"> <MenuItem Header="Keyboard-section hint" Command="{CompiledBinding AddKeyboardSectionHint}">
<MenuItem.Icon> <MenuItem.Icon>
<avalonia:MaterialIcon Kind="Keyboard" /> <avalonia:MaterialIcon Kind="Keyboard" />
</MenuItem.Icon> </MenuItem.Icon>
@ -91,7 +91,7 @@
</DropDownButton.Flyout> </DropDownButton.Flyout>
Add hint Add hint
</DropDownButton> </DropDownButton>
<Button Command="{Binding Finish}">Close</Button> <Button Command="{CompiledBinding Finish}">Close</Button>
</StackPanel> </StackPanel>
</Grid> </Grid>

View File

@ -20,11 +20,11 @@
<avalonia:MaterialIcon Grid.Column="1" <avalonia:MaterialIcon Grid.Column="1"
Kind="Folder" Kind="Folder"
Margin="0 0 5 0" Margin="0 0 5 0"
IsVisible="{Binding !IsExpanded, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TreeViewItem}}}" /> IsVisible="{CompiledBinding !IsExpanded, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TreeViewItem}}}" />
<avalonia:MaterialIcon Grid.Column="1" <avalonia:MaterialIcon Grid.Column="1"
Kind="FolderOpen" Kind="FolderOpen"
Margin="0 0 5 0" Margin="0 0 5 0"
IsVisible="{Binding IsExpanded, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TreeViewItem}}}" /> IsVisible="{CompiledBinding IsExpanded, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TreeViewItem}}}" />
<TextBlock Grid.Column="2" Text="{CompiledBinding Folder.Name}" VerticalAlignment="Center" /> <TextBlock Grid.Column="2" Text="{CompiledBinding Folder.Name}" VerticalAlignment="Center" />

View File

@ -54,8 +54,8 @@
<LinearGradientBrush StartPoint="0,0" EndPoint="0,28"> <LinearGradientBrush StartPoint="0,0" EndPoint="0,28">
<GradientStop Color="{StaticResource SystemAccentColorLight3}" Offset="0.0" /> <GradientStop Color="{StaticResource SystemAccentColorLight3}" Offset="0.0" />
<GradientStop Color="{StaticResource SystemAccentColorLight3}" Offset="0.05" /> <GradientStop Color="{StaticResource SystemAccentColorLight3}" Offset="0.05" />
<GradientStop Color="{Binding Source={StaticResource SystemAccentColorLight3}, Converter={StaticResource ColorOpacityConverter}, ConverterParameter=0.25}" Offset="0.05" /> <GradientStop Color="{CompiledBinding Source={StaticResource SystemAccentColorLight3}, Converter={StaticResource ColorOpacityConverter}, ConverterParameter=0.25}" Offset="0.05" />
<GradientStop Color="{Binding Source={StaticResource SystemAccentColorLight3}, Converter={StaticResource ColorOpacityConverter}, ConverterParameter=0}" Offset="0.25" /> <GradientStop Color="{CompiledBinding Source={StaticResource SystemAccentColorLight3}, Converter={StaticResource ColorOpacityConverter}, ConverterParameter=0}" Offset="0.25" />
</LinearGradientBrush> </LinearGradientBrush>
</Setter.Value> </Setter.Value>
</Setter> </Setter>
@ -66,10 +66,10 @@
<LinearGradientBrush StartPoint="0,0" EndPoint="0,28"> <LinearGradientBrush StartPoint="0,0" EndPoint="0,28">
<GradientStop Color="{StaticResource SystemAccentColorLight3}" Offset="0.0" /> <GradientStop Color="{StaticResource SystemAccentColorLight3}" Offset="0.0" />
<GradientStop Color="{StaticResource SystemAccentColorLight3}" Offset="0.05" /> <GradientStop Color="{StaticResource SystemAccentColorLight3}" Offset="0.05" />
<GradientStop Color="{Binding Source={StaticResource SystemAccentColorLight3}, Converter={StaticResource ColorOpacityConverter}, ConverterParameter=0.25}" Offset="0.05" /> <GradientStop Color="{CompiledBinding Source={StaticResource SystemAccentColorLight3}, Converter={StaticResource ColorOpacityConverter}, ConverterParameter=0.25}" Offset="0.05" />
<GradientStop Color="{Binding Source={StaticResource SystemAccentColorLight3}, Converter={StaticResource ColorOpacityConverter}, ConverterParameter=0}" Offset="0.25" /> <GradientStop Color="{CompiledBinding Source={StaticResource SystemAccentColorLight3}, Converter={StaticResource ColorOpacityConverter}, ConverterParameter=0}" Offset="0.25" />
<GradientStop Color="{Binding Source={StaticResource SystemAccentColorLight3}, Converter={StaticResource ColorOpacityConverter}, ConverterParameter=0}" Offset="0.75" /> <GradientStop Color="{CompiledBinding Source={StaticResource SystemAccentColorLight3}, Converter={StaticResource ColorOpacityConverter}, ConverterParameter=0}" Offset="0.75" />
<GradientStop Color="{Binding Source={StaticResource SystemAccentColorLight3}, Converter={StaticResource ColorOpacityConverter}, ConverterParameter=0.25}" Offset="0.95" /> <GradientStop Color="{CompiledBinding Source={StaticResource SystemAccentColorLight3}, Converter={StaticResource ColorOpacityConverter}, ConverterParameter=0.25}" Offset="0.95" />
<GradientStop Color="{StaticResource SystemAccentColorLight3}" Offset="0.95" /> <GradientStop Color="{StaticResource SystemAccentColorLight3}" Offset="0.95" />
<GradientStop Color="{StaticResource SystemAccentColorLight3}" Offset="1" /> <GradientStop Color="{StaticResource SystemAccentColorLight3}" Offset="1" />
</LinearGradientBrush> </LinearGradientBrush>
@ -80,8 +80,8 @@
<Setter Property="Background"> <Setter Property="Background">
<Setter.Value> <Setter.Value>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,28"> <LinearGradientBrush StartPoint="0,0" EndPoint="0,28">
<GradientStop Color="{Binding Source={StaticResource SystemAccentColorLight3}, Converter={StaticResource ColorOpacityConverter}, ConverterParameter=0}" Offset="0.75" /> <GradientStop Color="{CompiledBinding Source={StaticResource SystemAccentColorLight3}, Converter={StaticResource ColorOpacityConverter}, ConverterParameter=0}" Offset="0.75" />
<GradientStop Color="{Binding Source={StaticResource SystemAccentColorLight3}, Converter={StaticResource ColorOpacityConverter}, ConverterParameter=0.25}" Offset="0.95" /> <GradientStop Color="{CompiledBinding Source={StaticResource SystemAccentColorLight3}, Converter={StaticResource ColorOpacityConverter}, ConverterParameter=0.25}" Offset="0.95" />
<GradientStop Color="{StaticResource SystemAccentColorLight3}" Offset="0.95" /> <GradientStop Color="{StaticResource SystemAccentColorLight3}" Offset="0.95" />
<GradientStop Color="{StaticResource SystemAccentColorLight3}" Offset="1" /> <GradientStop Color="{StaticResource SystemAccentColorLight3}" Offset="1" />
</LinearGradientBrush> </LinearGradientBrush>
@ -91,17 +91,17 @@
</UserControl.Styles> </UserControl.Styles>
<Grid RowDefinitions="*,Auto"> <Grid RowDefinitions="*,Auto">
<TreeView Name="Profile" Classes="no-right-margin draggable" <TreeView Name="Profile" Classes="no-right-margin draggable"
Items="{CompiledBinding Children}" ItemsSource="{CompiledBinding Children}"
SelectedItem="{CompiledBinding SelectedChild}" SelectedItem="{CompiledBinding SelectedChild}"
SelectionChanged="ProfileTreeView_OnSelectionChanged"> SelectionChanged="ProfileTreeView_OnSelectionChanged">
<TreeView.Styles> <TreeView.Styles>
<Style Selector="TreeViewItem"> <Style Selector="TreeViewItem">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" /> <Setter Property="IsExpanded" Value="{CompiledBinding IsExpanded, Mode=TwoWay}" />
</Style> </Style>
</TreeView.Styles> </TreeView.Styles>
<TreeView.ItemTemplate> <TreeView.ItemTemplate>
<TreeDataTemplate ItemsSource="{Binding Children}"> <TreeDataTemplate ItemsSource="{CompiledBinding Children}">
<ContentControl Content="{Binding}" x:DataType="profileTree:TreeItemViewModel" Background="Transparent"> <ContentControl Content="{CompiledBinding}" x:DataType="profileTree:TreeItemViewModel" Background="Transparent">
<ContentControl.ContextFlyout> <ContentControl.ContextFlyout>
<MenuFlyout IsOpen="{CompiledBinding IsFlyoutOpen, Mode=OneWayToSource}"> <MenuFlyout IsOpen="{CompiledBinding IsFlyoutOpen, Mode=OneWayToSource}">
<MenuItem Header="Add new folder" Command="{CompiledBinding AddFolder}"> <MenuItem Header="Add new folder" Command="{CompiledBinding AddFolder}">

View File

@ -256,15 +256,9 @@ public abstract class TreeItemViewModel : ActivatableViewModelBase
private async void UpdateCanPaste(bool isFlyoutOpen) private async void UpdateCanPaste(bool isFlyoutOpen)
{ {
if (Application.Current?.Clipboard == null) string[] formats = await Shared.UI.Clipboard.GetFormatsAsync();
{
CanPaste = false;
return;
}
string[] formats = await Application.Current.Clipboard.GetFormatsAsync();
//diogotr7: This can be null on Linux sometimes. I'm not sure why. //diogotr7: This can be null on Linux sometimes. I'm not sure why.
if (formats == null) if (formats == null!)
{ {
CanPaste = false; CanPaste = false;
return; return;

View File

@ -26,7 +26,7 @@
VerticalAlignment="Center" VerticalAlignment="Center"
HorizontalAlignment="Right" HorizontalAlignment="Right"
Margin="10" Margin="10"
Command="{Binding OpenEditor}"> Command="{CompiledBinding OpenEditor}">
<avalonia:MaterialIcon Kind="OpenInNew" /> <avalonia:MaterialIcon Kind="OpenInNew" />
</Button> </Button>
</Grid> </Grid>

View File

@ -50,7 +50,7 @@ public class DataBindingViewModel : ActivatableViewModelBase
.DisposeWith(d); .DisposeWith(d);
_profileEditorService.Playing.CombineLatest(_profileEditorService.SuspendedEditing).Subscribe(tuple => _playing = tuple.First || tuple.Second).DisposeWith(d); _profileEditorService.Playing.CombineLatest(_profileEditorService.SuspendedEditing).Subscribe(tuple => _playing = tuple.First || tuple.Second).DisposeWith(d);
DispatcherTimer updateTimer = new(TimeSpan.FromMilliseconds(25.0 / 1000), DispatcherPriority.Render, Update); DispatcherTimer updateTimer = new(TimeSpan.FromMilliseconds(25.0 / 1000), DispatcherPriority.Background, Update);
// TODO: Remove in favor of saving each time a node editor command is executed // TODO: Remove in favor of saving each time a node editor command is executed
DispatcherTimer saveTimer = new(TimeSpan.FromMinutes(2), DispatcherPriority.Normal, Save); DispatcherTimer saveTimer = new(TimeSpan.FromMinutes(2), DispatcherPriority.Normal, Save);

View File

@ -14,7 +14,7 @@
<TextBox Name="SearchBox" Text="{CompiledBinding SearchText}" Margin="0 0 0 15" Watermark="Search" /> <TextBox Name="SearchBox" Text="{CompiledBinding SearchText}" Margin="0 0 0 15" Watermark="Search" />
<ListBox Name="EffectDescriptorsList" <ListBox Name="EffectDescriptorsList"
Grid.Row="1" Grid.Row="1"
Items="{CompiledBinding LayerEffectDescriptors}" ItemsSource="{CompiledBinding LayerEffectDescriptors}"
IsVisible="{CompiledBinding LayerEffectDescriptors.Count}" IsVisible="{CompiledBinding LayerEffectDescriptors.Count}"
Height="300"> Height="300">

View File

@ -28,7 +28,7 @@
Name="TreeScrollViewer" Name="TreeScrollViewer"
Offset="{CompiledBinding #TimelineScrollViewer.Offset, Mode=OneWay}"> Offset="{CompiledBinding #TimelineScrollViewer.Offset, Mode=OneWay}">
<Grid RowDefinitions="*,Auto"> <Grid RowDefinitions="*,Auto">
<ItemsControl Items="{CompiledBinding PropertyGroupViewModels}" Padding="0 0 8 0"> <ItemsControl ItemsSource="{CompiledBinding PropertyGroupViewModels}" Padding="0 0 8 0">
<ItemsControl.ItemTemplate> <ItemsControl.ItemTemplate>
<TreeDataTemplate DataType="{x:Type local:PropertyGroupViewModel}" ItemsSource="{CompiledBinding Children}"> <TreeDataTemplate DataType="{x:Type local:PropertyGroupViewModel}" ItemsSource="{CompiledBinding Children}">
<ContentControl Content="{CompiledBinding TreeGroupViewModel}" /> <ContentControl Content="{CompiledBinding TreeGroupViewModel}" />

View File

@ -179,12 +179,12 @@ public class PropertyGroupViewModel : PropertyViewModelBase, IDisposable
public void Dispose() public void Dispose()
{ {
LayerPropertyGroup.VisibilityChanged -= LayerPropertyGroupOnVisibilityChanged; LayerPropertyGroup.VisibilityChanged -= LayerPropertyGroupOnVisibilityChanged;
foreach (ViewModelBase viewModelBase in Children) while (Children.Any())
{ {
if (viewModelBase is IDisposable disposable) if (Children[0] is IDisposable disposable)
disposable.Dispose(); disposable.Dispose();
Children.RemoveAt(0);
} }
_keyframeSubscription.Dispose(); _keyframeSubscription.Dispose();
} }
} }

View File

@ -32,7 +32,7 @@
</Ellipse.Transitions> </Ellipse.Transitions>
<Ellipse.ContextFlyout> <Ellipse.ContextFlyout>
<MenuFlyout Opening="FlyoutBase_OnOpening"> <MenuFlyout Opening="FlyoutBase_OnOpening">
<MenuItem Header="Easing" Items="{Binding EasingViewModels}"> <MenuItem Header="Easing" ItemsSource="{Binding EasingViewModels}">
<MenuItem.Styles> <MenuItem.Styles>
<Style Selector="MenuItem > MenuItem"> <Style Selector="MenuItem > MenuItem">
<Setter Property="Icon"> <Setter Property="Icon">

View File

@ -150,9 +150,6 @@ public class TimelineKeyframeViewModel<T> : ActivatableViewModelBase, ITimelineK
private async Task ExecuteCopy() private async Task ExecuteCopy()
{ {
if (Application.Current?.Clipboard == null)
return;
List<KeyframeClipboardModel> keyframes = new(); List<KeyframeClipboardModel> keyframes = new();
if (!IsSelected) if (!IsSelected)
keyframes.Add(new KeyframeClipboardModel(Keyframe)); keyframes.Add(new KeyframeClipboardModel(Keyframe));
@ -162,15 +159,12 @@ public class TimelineKeyframeViewModel<T> : ActivatableViewModelBase, ITimelineK
string copy = CoreJson.SerializeObject(keyframes, true); string copy = CoreJson.SerializeObject(keyframes, true);
DataObject dataObject = new(); DataObject dataObject = new();
dataObject.Set(KeyframeClipboardModel.ClipboardDataFormat, copy); dataObject.Set(KeyframeClipboardModel.ClipboardDataFormat, copy);
await Application.Current.Clipboard.SetDataObjectAsync(dataObject); await Shared.UI.Clipboard.SetDataObjectAsync(dataObject);
} }
private async Task ExecutePaste() private async Task ExecutePaste()
{ {
if (Application.Current?.Clipboard == null) List<KeyframeClipboardModel>? keyframes = await Shared.UI.Clipboard.GetJsonAsync<List<KeyframeClipboardModel>>(KeyframeClipboardModel.ClipboardDataFormat);
return;
List<KeyframeClipboardModel>? keyframes = await Application.Current.Clipboard.GetJsonAsync<List<KeyframeClipboardModel>>(KeyframeClipboardModel.ClipboardDataFormat);
if (keyframes == null) if (keyframes == null)
return; return;
@ -201,13 +195,7 @@ public class TimelineKeyframeViewModel<T> : ActivatableViewModelBase, ITimelineK
private async void UpdateCanPaste(bool isFlyoutOpen) private async void UpdateCanPaste(bool isFlyoutOpen)
{ {
if (Application.Current?.Clipboard == null) string[] formats = await Shared.UI.Clipboard.GetFormatsAsync();
{
CanPaste = false;
return;
}
string[] formats = await Application.Current.Clipboard.GetFormatsAsync();
CanPaste = formats.Contains("Artemis.Keyframes"); CanPaste = formats.Contains("Artemis.Keyframes");
} }

View File

@ -22,7 +22,7 @@
<avalonia:MaterialIcon Kind="Edit" /> <avalonia:MaterialIcon Kind="Edit" />
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<MenuItem Header="Remove segment" Command="{Binding RemoveSegment}"> <MenuItem Header="Remove segment" Command="{CompiledBinding RemoveSegment}">
<MenuItem.Icon> <MenuItem.Icon>
<avalonia:MaterialIcon Kind="Remove" /> <avalonia:MaterialIcon Kind="Remove" />
</MenuItem.Icon> </MenuItem.Icon>
@ -38,7 +38,7 @@
Name="AddMainSegment" Name="AddMainSegment"
Classes="AppBarButton icon-button icon-button-small" Classes="AppBarButton icon-button icon-button-small"
ToolTip.Tip="Add main segment" ToolTip.Tip="Add main segment"
Command="{Binding AddMainSegment}" Command="{CompiledBinding AddMainSegment}"
IsVisible="{CompiledBinding ShowAddMain}"> IsVisible="{CompiledBinding ShowAddMain}">
<avalonia:MaterialIcon Kind="PlusCircle" /> <avalonia:MaterialIcon Kind="PlusCircle" />
</Button> </Button>
@ -52,7 +52,7 @@
<Button Name="SegmentClose" <Button Name="SegmentClose"
Classes="AppBarButton icon-button icon-button-small" Classes="AppBarButton icon-button icon-button-small"
ToolTip.Tip="Remove this segment" ToolTip.Tip="Remove this segment"
Command="{Binding RemoveSegment}"> Command="{CompiledBinding RemoveSegment}">
<avalonia:MaterialIcon Kind="CloseCircle" /> <avalonia:MaterialIcon Kind="CloseCircle" />
</Button> </Button>
</StackPanel> </StackPanel>

View File

@ -22,7 +22,7 @@
<avalonia:MaterialIcon Kind="Edit" /> <avalonia:MaterialIcon Kind="Edit" />
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<MenuItem Header="Remove segment" Command="{Binding RemoveSegment}"> <MenuItem Header="Remove segment" Command="{CompiledBinding RemoveSegment}">
<MenuItem.Icon> <MenuItem.Icon>
<avalonia:MaterialIcon Kind="Remove" /> <avalonia:MaterialIcon Kind="Remove" />
</MenuItem.Icon> </MenuItem.Icon>
@ -39,7 +39,7 @@
Classes="AppBarButton icon-button icon-button-small" Classes="AppBarButton icon-button icon-button-small"
VerticalAlignment="Center" VerticalAlignment="Center"
ToolTip.Tip="Add a start segment" ToolTip.Tip="Add a start segment"
Command="{Binding AddStartSegment}" Command="{CompiledBinding AddStartSegment}"
IsVisible="{CompiledBinding ShowAddStart}"> IsVisible="{CompiledBinding ShowAddStart}">
<avalonia:MaterialIcon Kind="PlusCircle" /> <avalonia:MaterialIcon Kind="PlusCircle" />
</Button> </Button>
@ -56,7 +56,7 @@
<Button Name="SegmentClose" <Button Name="SegmentClose"
Classes="AppBarButton icon-button icon-button-small" Classes="AppBarButton icon-button icon-button-small"
ToolTip.Tip="Remove this segment" ToolTip.Tip="Remove this segment"
Command="{Binding RemoveSegment}"> Command="{CompiledBinding RemoveSegment}">
<avalonia:MaterialIcon Kind="CloseCircle" /> <avalonia:MaterialIcon Kind="CloseCircle" />
</Button> </Button>
</StackPanel> </StackPanel>
@ -66,7 +66,7 @@
Classes="AppBarButton icon-button icon-button-small" Classes="AppBarButton icon-button icon-button-small"
VerticalAlignment="Center" VerticalAlignment="Center"
ToolTip.Tip="Add an end segment" ToolTip.Tip="Add an end segment"
Command="{Binding AddEndSegment}" Command="{CompiledBinding AddEndSegment}"
IsVisible="{CompiledBinding ShowAddEnd}"> IsVisible="{CompiledBinding ShowAddEnd}">
<avalonia:MaterialIcon Kind="PlusCircle" /> <avalonia:MaterialIcon Kind="PlusCircle" />
</Button> </Button>

View File

@ -22,7 +22,7 @@
<avalonia:MaterialIcon Kind="Edit" /> <avalonia:MaterialIcon Kind="Edit" />
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<MenuItem Header="Remove segment" Command="{Binding RemoveSegment}"> <MenuItem Header="Remove segment" Command="{CompiledBinding RemoveSegment}">
<MenuItem.Icon> <MenuItem.Icon>
<avalonia:MaterialIcon Kind="Remove" /> <avalonia:MaterialIcon Kind="Remove" />
</MenuItem.Icon> </MenuItem.Icon>
@ -39,7 +39,7 @@
<Button Name="SegmentClose" <Button Name="SegmentClose"
Classes="AppBarButton icon-button icon-button-small" Classes="AppBarButton icon-button icon-button-small"
ToolTip.Tip="Remove this segment" ToolTip.Tip="Remove this segment"
Command="{Binding RemoveSegment}"> Command="{CompiledBinding RemoveSegment}">
<avalonia:MaterialIcon Kind="CloseCircle" /> <avalonia:MaterialIcon Kind="CloseCircle" />
</Button> </Button>
</StackPanel> </StackPanel>
@ -48,7 +48,7 @@
Name="AddMainSegment" Name="AddMainSegment"
Classes="AppBarButton icon-button icon-button-small" Classes="AppBarButton icon-button icon-button-small"
ToolTip.Tip="Add main segment" ToolTip.Tip="Add main segment"
Command="{Binding AddMainSegment}" Command="{CompiledBinding AddMainSegment}"
IsVisible="{CompiledBinding ShowAddMain}"> IsVisible="{CompiledBinding ShowAddMain}">
<avalonia:MaterialIcon Kind="PlusCircle" /> <avalonia:MaterialIcon Kind="PlusCircle" />
</Button> </Button>

View File

@ -4,6 +4,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:properties="clr-namespace:Artemis.UI.Screens.ProfileEditor.Properties" xmlns:properties="clr-namespace:Artemis.UI.Screens.ProfileEditor.Properties"
xmlns:timeline="clr-namespace:Artemis.UI.Screens.ProfileEditor.Properties.Timeline" xmlns:timeline="clr-namespace:Artemis.UI.Screens.ProfileEditor.Properties.Timeline"
xmlns:system="clr-namespace:System;assembly=netstandard"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.ProfileEditor.Properties.Timeline.TimelineGroupView" x:Class="Artemis.UI.Screens.ProfileEditor.Properties.Timeline.TimelineGroupView"
x:DataType="timeline:TimelineGroupViewModel"> x:DataType="timeline:TimelineGroupViewModel">
@ -11,7 +12,7 @@
<ItemsControl Grid.Row="0" <ItemsControl Grid.Row="0"
Height="28" Height="28"
IsVisible="{CompiledBinding !PropertyGroupViewModel.IsExpanded}" IsVisible="{CompiledBinding !PropertyGroupViewModel.IsExpanded}"
Items="{CompiledBinding KeyframePositions}" ItemsSource="{CompiledBinding KeyframePositions}"
HorizontalAlignment="Stretch"> HorizontalAlignment="Stretch">
<ItemsControl.ItemsPanel> <ItemsControl.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
@ -19,8 +20,8 @@
</ItemsPanelTemplate> </ItemsPanelTemplate>
</ItemsControl.ItemsPanel> </ItemsControl.ItemsPanel>
<ItemsControl.Styles> <ItemsControl.Styles>
<Style Selector="ContentPresenter"> <Style Selector="ContentPresenter" x:DataType="system:Double">
<Setter Property="Canvas.Left" Value="{Binding}" /> <Setter Property="Canvas.Left" Value="{CompiledBinding}" />
<Setter Property="Canvas.Top" Value="9" /> <Setter Property="Canvas.Top" Value="9" />
</Style> </Style>
</ItemsControl.Styles> </ItemsControl.Styles>
@ -39,7 +40,7 @@
<Rectangle Grid.Row="1" HorizontalAlignment="Stretch" Fill="{DynamicResource ButtonBorderBrush}" Height="1" /> <Rectangle Grid.Row="1" HorizontalAlignment="Stretch" Fill="{DynamicResource ButtonBorderBrush}" Height="1" />
<ItemsControl Grid.Row="2" <ItemsControl Grid.Row="2"
Items="{CompiledBinding Children}" ItemsSource="{CompiledBinding Children}"
IsVisible="{CompiledBinding PropertyGroupViewModel.IsExpanded}" IsVisible="{CompiledBinding PropertyGroupViewModel.IsExpanded}"
HorizontalAlignment="Stretch"> HorizontalAlignment="Stretch">
<ItemsControl.DataTemplates> <ItemsControl.DataTemplates>

View File

@ -5,7 +5,7 @@
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.ProfileEditor.Properties.Timeline.TimelinePropertyView"> x:Class="Artemis.UI.Screens.ProfileEditor.Properties.Timeline.TimelinePropertyView">
<Border Height="29" BorderThickness="0,0,0,1" BorderBrush="{DynamicResource ButtonBorderBrush}"> <Border Height="29" BorderThickness="0,0,0,1" BorderBrush="{DynamicResource ButtonBorderBrush}">
<ItemsControl Items="{Binding KeyframeViewModels}"> <ItemsControl ItemsSource="{Binding KeyframeViewModels}">
<ItemsControl.ItemsPanel> <ItemsControl.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
<Canvas /> <Canvas />

View File

@ -10,14 +10,14 @@
x:DataType="timeline:TimelineViewModel"> x:DataType="timeline:TimelineViewModel">
<UserControl.Resources> <UserControl.Resources>
</UserControl.Resources> </UserControl.Resources>
<Grid Background="Transparent" PointerReleased="InputElement_OnPointerReleased" Focusable="True" MinWidth="{Binding MinWidth}"> <Grid Background="Transparent" PointerReleased="InputElement_OnPointerReleased" Focusable="True" MinWidth="{CompiledBinding MinWidth}">
<Grid.KeyBindings> <Grid.KeyBindings>
<KeyBinding Command="{CompiledBinding CopySelectedKeyframes}" Gesture="Ctrl+C" /> <KeyBinding Command="{CompiledBinding CopySelectedKeyframes}" Gesture="Ctrl+C" />
<KeyBinding Command="{CompiledBinding DuplicateSelectedKeyframes}" Gesture="Ctrl+D" /> <KeyBinding Command="{CompiledBinding DuplicateSelectedKeyframes}" Gesture="Ctrl+D" />
<KeyBinding Command="{CompiledBinding PasteKeyframes}" Gesture="Ctrl+V" /> <KeyBinding Command="{CompiledBinding PasteKeyframes}" Gesture="Ctrl+V" />
<KeyBinding Command="{CompiledBinding DeleteSelectedKeyframes}" Gesture="Delete" /> <KeyBinding Command="{CompiledBinding DeleteSelectedKeyframes}" Gesture="Delete" />
</Grid.KeyBindings> </Grid.KeyBindings>
<ItemsControl Items="{CompiledBinding PropertyGroupViewModels}"> <ItemsControl ItemsSource="{CompiledBinding PropertyGroupViewModels}">
<ItemsControl.ItemTemplate> <ItemsControl.ItemTemplate>
<TreeDataTemplate DataType="{x:Type local:PropertyGroupViewModel}" ItemsSource="{CompiledBinding Children}"> <TreeDataTemplate DataType="{x:Type local:PropertyGroupViewModel}" ItemsSource="{CompiledBinding Children}">
<ContentControl Content="{CompiledBinding TimelineGroupViewModel}" /> <ContentControl Content="{CompiledBinding TimelineGroupViewModel}" />

View File

@ -169,10 +169,10 @@ public class TimelineViewModel : ActivatableViewModelBase
private async Task ExecutePasteKeyframes() private async Task ExecutePasteKeyframes()
{ {
if (_profileElement == null || Application.Current?.Clipboard == null) if (_profileElement == null)
return; return;
List<KeyframeClipboardModel>? keyframes = await Application.Current.Clipboard.GetJsonAsync<List<KeyframeClipboardModel>>(KeyframeClipboardModel.ClipboardDataFormat); List<KeyframeClipboardModel>? keyframes = await Shared.UI.Clipboard.GetJsonAsync<List<KeyframeClipboardModel>>(KeyframeClipboardModel.ClipboardDataFormat);
if (keyframes == null) if (keyframes == null)
return; return;

View File

@ -14,7 +14,7 @@
<TextBox Name="SearchBox" Text="{CompiledBinding SearchText}" Margin="0 0 0 15" Watermark="Search" /> <TextBox Name="SearchBox" Text="{CompiledBinding SearchText}" Margin="0 0 0 15" Watermark="Search" />
<ListBox Name="EffectDescriptorsList" <ListBox Name="EffectDescriptorsList"
Grid.Row="1" Grid.Row="1"
Items="{CompiledBinding Presets}" ItemsSource="{CompiledBinding Presets}"
IsVisible="{CompiledBinding Presets.Count}" IsVisible="{CompiledBinding Presets.Count}"
Height="300"> Height="300">

View File

@ -25,7 +25,7 @@
BorderBrush="{DynamicResource ButtonBorderBrush}" BorderBrush="{DynamicResource ButtonBorderBrush}"
BorderThickness="0,0,0,1" BorderThickness="0,0,0,1"
Height="29"> Height="29">
<Grid Margin="{Binding Converter={StaticResource PropertyTreeMarginConverter}}" ColumnDefinitions="19,*"> <Grid Margin="{CompiledBinding Converter={StaticResource PropertyTreeMarginConverter}}" ColumnDefinitions="19,*">
<avalonia:MaterialIcon Classes.chevron-collapsed="{CompiledBinding !PropertyGroupViewModel.IsExpanded}" <avalonia:MaterialIcon Classes.chevron-collapsed="{CompiledBinding !PropertyGroupViewModel.IsExpanded}"
IsVisible="{CompiledBinding PropertyGroupViewModel.HasChildren}" IsVisible="{CompiledBinding PropertyGroupViewModel.HasChildren}"
@ -171,7 +171,7 @@
Do not bind directly to the PropertyGroupViewModel.Children collection Do not bind directly to the PropertyGroupViewModel.Children collection
Instead use a reference provided by the VM that is null when collapsed, virtualization for noobs Instead use a reference provided by the VM that is null when collapsed, virtualization for noobs
--> -->
<ItemsControl Items="{CompiledBinding Children}" <ItemsControl ItemsSource="{CompiledBinding Children}"
IsVisible="{CompiledBinding PropertyGroupViewModel.IsExpanded}" IsVisible="{CompiledBinding PropertyGroupViewModel.IsExpanded}"
HorizontalAlignment="Stretch"> HorizontalAlignment="Stretch">
<ItemsControl.DataTemplates> <ItemsControl.DataTemplates>

View File

@ -15,7 +15,7 @@
BorderBrush="{DynamicResource ButtonBorderBrush}" BorderBrush="{DynamicResource ButtonBorderBrush}"
BorderThickness="0,0,0,1" BorderThickness="0,0,0,1"
Height="29"> Height="29">
<Grid Margin="{Binding Converter={StaticResource PropertyTreeMarginConverter}}" ColumnDefinitions="Auto,*,Auto,Auto,Auto"> <Grid Margin="{CompiledBinding Converter={StaticResource PropertyTreeMarginConverter}}" ColumnDefinitions="Auto,*,Auto,Auto,Auto">
<ToggleButton Grid.Column="0" <ToggleButton Grid.Column="0"
Classes="icon-button" Classes="icon-button"
ToolTip.Tip="Toggle key-framing" ToolTip.Tip="Toggle key-framing"

View File

@ -17,9 +17,13 @@ public partial class TreePropertyView : ReactiveUserControl<ITreePropertyViewMod
InitializeComponent(); InitializeComponent();
this.WhenActivated(d => this.WhenActivated(d =>
{ {
Observable.FromEventPattern<LayerPropertyEventArgs>(e => ViewModel!.BaseLayerProperty.CurrentValueSet += e, e => ViewModel!.BaseLayerProperty.CurrentValueSet -= e) ITreePropertyViewModel? viewModel = ViewModel;
.Subscribe(_ => this.BringIntoView()) if (viewModel != null)
.DisposeWith(d); {
Observable.FromEventPattern<LayerPropertyEventArgs>(e => viewModel.BaseLayerProperty.CurrentValueSet += e, e => viewModel.BaseLayerProperty.CurrentValueSet -= e)
.Subscribe(_ => this.BringIntoView())
.DisposeWith(d);
}
}); });
} }

View File

@ -32,7 +32,6 @@ internal class TreePropertyViewModel<T> : ActivatableViewModelBase, ITreePropert
_profileEditorService.Time.Subscribe(t => _time = t).DisposeWith(d); _profileEditorService.Time.Subscribe(t => _time = t).DisposeWith(d);
_isCurrentlySelected = _profileEditorService.LayerProperty.Select(l => l == LayerProperty).ToProperty(this, vm => vm.IsCurrentlySelected).DisposeWith(d); _isCurrentlySelected = _profileEditorService.LayerProperty.Select(l => l == LayerProperty).ToProperty(this, vm => vm.IsCurrentlySelected).DisposeWith(d);
_dataBindingEnabled = LayerProperty.BaseDataBinding.AsObservable().Select(b => b.IsEnabled).ToProperty(this, vm => vm.DataBindingEnabled).DisposeWith(d); _dataBindingEnabled = LayerProperty.BaseDataBinding.AsObservable().Select(b => b.IsEnabled).ToProperty(this, vm => vm.DataBindingEnabled).DisposeWith(d);
this.WhenAnyValue(vm => vm.LayerProperty.KeyframesEnabled).Subscribe(_ => this.RaisePropertyChanged(nameof(KeyframesEnabled))).DisposeWith(d); this.WhenAnyValue(vm => vm.LayerProperty.KeyframesEnabled).Subscribe(_ => this.RaisePropertyChanged(nameof(KeyframesEnabled))).DisposeWith(d);
}); });

Some files were not shown because too many files have changed in this diff Show More