mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-12 21:38:38 +00:00
Merge branch 'development'
This commit is contained in:
commit
4e700a7ced
2
.github/workflows/master.yml
vendored
2
.github/workflows/master.yml
vendored
@ -58,7 +58,7 @@ jobs:
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v2
|
||||
with:
|
||||
dotnet-version: '6.0.x'
|
||||
dotnet-version: '7.0.x'
|
||||
- name: Publish Artemis
|
||||
run: dotnet publish --configuration Release -p:Version=${{ needs.version.outputs.version-number }} --runtime ${{ matrix.rid }} --output build/${{ matrix.rid }} --self-contained Artemis/src/Artemis.UI.${{ matrix.csproj }}/Artemis.UI.${{ matrix.csproj }}.csproj
|
||||
- name: Publish Plugins
|
||||
|
||||
4
.github/workflows/nuget.yml
vendored
4
.github/workflows/nuget.yml
vendored
@ -3,8 +3,6 @@ name: Publish Nuget packages
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
version:
|
||||
@ -37,7 +35,7 @@ jobs:
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v2
|
||||
with:
|
||||
dotnet-version: '6.0.x'
|
||||
dotnet-version: '7.0.x'
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Pack Artemis.Core
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<PreserveCompilationContext>false</PreserveCompilationContext>
|
||||
<ShouldIncludeNativeSkiaSharp>false</ShouldIncludeNativeSkiaSharp>
|
||||
<AssemblyTitle>Artemis.Core</AssemblyTitle>
|
||||
@ -35,21 +35,22 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DryIoc.dll" Version="5.3.1" />
|
||||
<PackageReference Include="EmbedIO" Version="3.5.0" />
|
||||
<PackageReference Include="DryIoc.dll" Version="5.3.4" />
|
||||
<PackageReference Include="EmbedIO" Version="3.5.2" />
|
||||
<PackageReference Include="HidSharp" Version="2.1.0" />
|
||||
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
|
||||
<PackageReference Include="LiteDB" Version="5.0.12" />
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2022.3.1" />
|
||||
<PackageReference Include="LiteDB" Version="5.0.16" />
|
||||
<PackageReference Include="McMaster.NETCore.Plugins" Version="1.4.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="RGB.NET.Core" Version="2.0.0-prerelease.17" />
|
||||
<PackageReference Include="RGB.NET.Layout" Version="2.0.0-prerelease.17" />
|
||||
<PackageReference Include="RGB.NET.Presets" Version="2.0.0-prerelease.17" />
|
||||
<PackageReference Include="Serilog" Version="2.11.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
|
||||
<PackageReference Include="RGB.NET.Core" Version="2.0.0-prerelease.29" />
|
||||
<PackageReference Include="RGB.NET.Layout" Version="2.0.0-prerelease.29" />
|
||||
<PackageReference Include="RGB.NET.Presets" Version="2.0.0-prerelease.29" />
|
||||
<PackageReference Include="Serilog" Version="2.12.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Debug" Version="2.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||
<PackageReference Include="SkiaSharp" Version="2.88.1-preview.108" />
|
||||
<PackageReference Include="SkiaSharp" Version="2.88.3" />
|
||||
<PackageReference Include="System.Buffers" Version="4.5.1" />
|
||||
<PackageReference Include="System.IO.FileSystem.AccessControl" Version="5.0.0" />
|
||||
<PackageReference Include="System.Numerics.Vectors" Version="4.5.0" />
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib"
|
||||
xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=colorscience_005Cquantization/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=colorscience_005Csorting/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=defaulttypes/@EntryIndexedValue">True</s:Boolean>
|
||||
|
||||
@ -6,7 +6,6 @@ using System.Linq;
|
||||
using System.Reflection;
|
||||
using Artemis.Core.JsonConverters;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.Core.Services.Core;
|
||||
using Artemis.Core.SkiaSharp;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
|
||||
@ -11,12 +11,12 @@ using DryIoc;
|
||||
namespace Artemis.Core.DryIoc;
|
||||
|
||||
/// <summary>
|
||||
/// Provides an extension method to register services onto a DryIoc <see cref="IContainer"/>.
|
||||
/// Provides an extension method to register services onto a DryIoc <see cref="IContainer" />.
|
||||
/// </summary>
|
||||
public static class ContainerExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Registers core services into the container.
|
||||
/// Registers core services into the container.
|
||||
/// </summary>
|
||||
/// <param name="container">The builder building the current container</param>
|
||||
public static void RegisterCore(this IContainer container)
|
||||
@ -37,13 +37,13 @@ public static class ContainerExtensions
|
||||
container.RegisterMany(storageAssembly, type => type.IsAssignableTo<IStorageMigration>(), Reuse.Singleton, nonPublicServiceTypes: true);
|
||||
|
||||
container.Register<IPluginSettingsFactory, PluginSettingsFactory>(Reuse.Singleton);
|
||||
container.Register(made: Made.Of(_ => ServiceInfo.Of<IPluginSettingsFactory>(), f => f.CreatePluginSettings(Arg.Index<Type>(0)), r => r.Parent.ImplementationType));
|
||||
container.Register(Made.Of(_ => ServiceInfo.Of<IPluginSettingsFactory>(), f => f.CreatePluginSettings(Arg.Index<Type>(0)), r => r.Parent.ImplementationType));
|
||||
container.Register<ILoggerFactory, LoggerFactory>(Reuse.Singleton);
|
||||
container.Register(made: Made.Of(_ => ServiceInfo.Of<ILoggerFactory>(), f => f.CreateLogger(Arg.Index<Type>(0)), r => r.Parent.ImplementationType));
|
||||
container.Register(Made.Of(_ => ServiceInfo.Of<ILoggerFactory>(), f => f.CreateLogger(Arg.Index<Type>(0)), r => r.Parent.ImplementationType));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers plugin services into the container, this is typically a child container.
|
||||
/// Registers plugin services into the container, this is typically a child container.
|
||||
/// </summary>
|
||||
/// <param name="container">The builder building the current container</param>
|
||||
/// <param name="plugin">The plugin to register</param>
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Artemis.Core;
|
||||
|
||||
@ -14,7 +13,7 @@ public class UpdateEventArgs : EventArgs
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether to silently update or not.
|
||||
/// Gets a boolean indicating whether to silently update or not.
|
||||
/// </summary>
|
||||
public bool Silent { get; }
|
||||
}
|
||||
@ -10,7 +10,7 @@ public class ArtemisPluginException : Exception
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="ArtemisPluginException" /> class
|
||||
/// </summary>
|
||||
public ArtemisPluginException(Plugin plugin)
|
||||
internal ArtemisPluginException(Plugin plugin)
|
||||
{
|
||||
Plugin = plugin;
|
||||
}
|
||||
@ -18,7 +18,7 @@ public class ArtemisPluginException : Exception
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="ArtemisPluginException" /> class
|
||||
/// </summary>
|
||||
public ArtemisPluginException(Plugin plugin, string message) : base(message)
|
||||
internal ArtemisPluginException(Plugin plugin, string message) : base(message)
|
||||
{
|
||||
Plugin = plugin;
|
||||
}
|
||||
@ -26,7 +26,7 @@ public class ArtemisPluginException : Exception
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="ArtemisPluginException" /> class
|
||||
/// </summary>
|
||||
public ArtemisPluginException(Plugin plugin, string message, Exception inner) : base(message, inner)
|
||||
internal ArtemisPluginException(Plugin plugin, string message, Exception inner) : base(message, inner)
|
||||
{
|
||||
Plugin = plugin;
|
||||
}
|
||||
@ -44,9 +44,31 @@ public class ArtemisPluginException : Exception
|
||||
public ArtemisPluginException(string message, Exception inner) : base(message, inner)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="ArtemisPluginException" /> class
|
||||
/// </summary>
|
||||
public ArtemisPluginException(string message, string helpDocument) : base(message)
|
||||
{
|
||||
HelpDocument = helpDocument;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="ArtemisPluginException" /> class
|
||||
/// </summary>
|
||||
public ArtemisPluginException(string message, Exception inner, string helpDocument) : base(message, inner)
|
||||
{
|
||||
HelpDocument = helpDocument;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin the error is related to
|
||||
/// </summary>
|
||||
public Plugin? Plugin { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the help document related to this exception.
|
||||
/// </summary>
|
||||
public string? HelpDocument { get; }
|
||||
|
||||
}
|
||||
@ -21,17 +21,17 @@ public static class ProcessExtensions
|
||||
{
|
||||
int capacity = 2000;
|
||||
StringBuilder builder = new(capacity);
|
||||
IntPtr ptr = OpenProcess(ProcessAccessFlags.QueryLimitedInformation, false, p.Id);
|
||||
nint ptr = OpenProcess(ProcessAccessFlags.QueryLimitedInformation, false, p.Id);
|
||||
if (!QueryFullProcessImageName(ptr, 0, builder, ref capacity)) return string.Empty;
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
|
||||
private static extern bool QueryFullProcessImageName([In] IntPtr hProcess, [In] int dwFlags, [Out] StringBuilder lpExeName, ref int lpdwSize);
|
||||
private static extern bool QueryFullProcessImageName([In] nint hProcess, [In] int dwFlags, [Out] StringBuilder lpExeName, ref int lpdwSize);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern IntPtr OpenProcess(ProcessAccessFlags processAccess, bool bInheritHandle, int processId);
|
||||
private static extern nint OpenProcess(ProcessAccessFlags processAccess, bool bInheritHandle, int processId);
|
||||
|
||||
[Flags]
|
||||
private enum ProcessAccessFlags : uint
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Artemis.Core.Properties;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Artemis.Core;
|
||||
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Artemis.Core.Properties;
|
||||
using Artemis.Storage.Entities.Plugins;
|
||||
using Artemis.Storage.Repositories.Interfaces;
|
||||
using Newtonsoft.Json;
|
||||
@ -36,9 +34,7 @@ public class PluginSetting<T> : CorePropertyChanged, IPluginSetting
|
||||
/// <summary>
|
||||
/// The value of the setting
|
||||
/// </summary>
|
||||
[AllowNull]
|
||||
[CanBeNull]
|
||||
public T Value
|
||||
public T? Value
|
||||
{
|
||||
get => _value;
|
||||
set
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -127,6 +127,13 @@ public interface IPluginManagementService : IArtemisService, IDisposable
|
||||
/// <returns>If the current call stack contains a plugin, the plugin. Otherwise null</returns>
|
||||
Plugin? GetCallingPlugin();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the plugin that threw the provided exception.
|
||||
/// </summary>
|
||||
/// <param name="exception"></param>
|
||||
/// <returns>If the exception was thrown by a plugin, the plugin. Otherwise null</returns>
|
||||
Plugin? GetPluginFromException(Exception exception);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin that defined the specified device
|
||||
/// </summary>
|
||||
|
||||
@ -192,7 +192,19 @@ internal class PluginManagementService : IPluginManagementService
|
||||
|
||||
public Plugin? GetCallingPlugin()
|
||||
{
|
||||
StackTrace stackTrace = new(); // get call stack
|
||||
return GetPluginFromStackTrace(new StackTrace());
|
||||
}
|
||||
|
||||
public Plugin? GetPluginFromException(Exception exception)
|
||||
{
|
||||
if (exception is ArtemisPluginException pluginException && pluginException.Plugin != null)
|
||||
return pluginException.Plugin;
|
||||
|
||||
return GetPluginFromStackTrace(new StackTrace(exception));
|
||||
}
|
||||
|
||||
private Plugin? GetPluginFromStackTrace(StackTrace stackTrace)
|
||||
{
|
||||
StackFrame[] stackFrames = stackTrace.GetFrames(); // get method calls (frames)
|
||||
|
||||
foreach (StackFrame stackFrame in stackFrames)
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using EmbedIO;
|
||||
using DryIoc;
|
||||
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
|
||||
@ -4,8 +4,8 @@ using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Artemis.Core.Events;
|
||||
using Artemis.Core.Internal;
|
||||
using Artemis.Core.Properties;
|
||||
using Artemis.Storage.Entities.Profile.Nodes;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Artemis.Core;
|
||||
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<PreserveCompilationContext>false</PreserveCompilationContext>
|
||||
<Platforms>x64</Platforms>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="LiteDB" Version="5.0.12" />
|
||||
<PackageReference Include="Serilog" Version="2.11.0" />
|
||||
<PackageReference Include="LiteDB" Version="5.0.16" />
|
||||
<PackageReference Include="Serilog" Version="2.12.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@ -5,7 +5,7 @@ namespace Artemis.Storage.Entities.General;
|
||||
public class ReleaseEntity
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
|
||||
|
||||
public string Version { get; set; }
|
||||
public DateTimeOffset? InstalledAt { get; set; }
|
||||
}
|
||||
@ -5,6 +5,11 @@ namespace Artemis.Storage.Entities.Profile;
|
||||
|
||||
public class LedEntity
|
||||
{
|
||||
public string LedName { get; set; }
|
||||
public string DeviceIdentifier { get; set; }
|
||||
|
||||
public int? PhysicalLayout { get; set; }
|
||||
|
||||
#region LedEntityEqualityComparer
|
||||
|
||||
private sealed class LedEntityEqualityComparer : IEqualityComparer<LedEntity>
|
||||
@ -31,9 +36,4 @@ public class LedEntity
|
||||
public static IEqualityComparer<LedEntity> LedEntityComparer { get; } = new LedEntityEqualityComparer();
|
||||
|
||||
#endregion
|
||||
|
||||
public string LedName { get; set; }
|
||||
public string DeviceIdentifier { get; set; }
|
||||
|
||||
public int? PhysicalLayout { get; set; }
|
||||
}
|
||||
@ -51,7 +51,7 @@ public class M0021GradientNodes : IStorageMigration
|
||||
TargetType = "ColorGradient",
|
||||
TargetNode = gradientNode.Id,
|
||||
TargetPinCollectionId = -1,
|
||||
TargetPinId = 0,
|
||||
TargetPinId = 0
|
||||
});
|
||||
|
||||
// Move the exit node to the right
|
||||
@ -59,6 +59,18 @@ public class M0021GradientNodes : IStorageMigration
|
||||
exitNode.Y += 30;
|
||||
}
|
||||
|
||||
private void MigrateDataBinding(PropertyGroupEntity propertyGroup)
|
||||
{
|
||||
foreach (PropertyGroupEntity propertyGroupPropertyGroup in propertyGroup.PropertyGroups)
|
||||
MigrateDataBinding(propertyGroupPropertyGroup);
|
||||
|
||||
foreach (PropertyEntity property in propertyGroup.Properties)
|
||||
{
|
||||
if (property.Value.StartsWith("[{\"Color\":\"") && property.DataBinding?.NodeScript != null && property.DataBinding.IsEnabled)
|
||||
MigrateDataBinding(property);
|
||||
}
|
||||
}
|
||||
|
||||
public int UserVersion => 21;
|
||||
|
||||
public void Apply(LiteRepository repository)
|
||||
@ -73,16 +85,4 @@ public class M0021GradientNodes : IStorageMigration
|
||||
repository.Update(profileEntity);
|
||||
}
|
||||
}
|
||||
|
||||
private void MigrateDataBinding(PropertyGroupEntity propertyGroup)
|
||||
{
|
||||
foreach (PropertyGroupEntity propertyGroupPropertyGroup in propertyGroup.PropertyGroups)
|
||||
MigrateDataBinding(propertyGroupPropertyGroup);
|
||||
|
||||
foreach (PropertyEntity property in propertyGroup.Properties)
|
||||
{
|
||||
if (property.Value.StartsWith("[{\"Color\":\"") && property.DataBinding?.NodeScript != null && property.DataBinding.IsEnabled)
|
||||
MigrateDataBinding(property);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -22,7 +22,7 @@ public class M0022TransitionNodes : IStorageMigration
|
||||
else if (node.Type == "ColorGradientEasingNode")
|
||||
node.Type = "ColorGradientTransitionNode";
|
||||
else if (node.Type == "SKColorEasingNode")
|
||||
node.Type = "SKColorTransitionNode";
|
||||
node.Type = "SKColorTransitionNode";
|
||||
else if (node.Type == "EasingTypeNode")
|
||||
node.Type = "EasingFunctionNode";
|
||||
}
|
||||
|
||||
@ -46,7 +46,7 @@ public class QueuedActionRepository : IQueuedActionRepository
|
||||
{
|
||||
return _repository.Query<QueuedActionEntity>().Where(q => q.Type == type).Count() > 0;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ClearByType(string type)
|
||||
{
|
||||
|
||||
@ -5,7 +5,7 @@ using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Threading;
|
||||
using Avalonia.ReactiveUI;
|
||||
using DryIoc;
|
||||
using ReactiveUI;
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<Platforms>x64</Platforms>
|
||||
@ -16,12 +16,12 @@
|
||||
<None Remove=".gitignore" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="0.10.18" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="0.10.18" />
|
||||
<PackageReference Include="Avalonia" Version="11.0.0-preview6" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="11.0.0-preview6" />
|
||||
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="0.10.18" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.18" />
|
||||
<PackageReference Include="ReactiveUI" Version="17.1.50" />
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.0-preview6" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.0-preview6" />
|
||||
<PackageReference Include="ReactiveUI" Version="18.4.26" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj" />
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Threading;
|
||||
using Avalonia.ReactiveUI;
|
||||
using DryIoc;
|
||||
using ReactiveUI;
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<Platforms>x64</Platforms>
|
||||
@ -15,12 +15,12 @@
|
||||
<None Remove=".gitignore" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="0.10.18" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="0.10.18" />
|
||||
<PackageReference Include="Avalonia" Version="11.0.0-preview6" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="11.0.0-preview6" />
|
||||
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="0.10.18" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.18" />
|
||||
<PackageReference Include="ReactiveUI" Version="17.1.50" />
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.0-preview6" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.0-preview6" />
|
||||
<PackageReference Include="ReactiveUI" Version="18.4.26" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj" />
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<OutputPath>bin\</OutputPath>
|
||||
<Platforms>x64</Platforms>
|
||||
@ -10,18 +10,18 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="0.10.18" />
|
||||
<PackageReference Include="Avalonia" Version="11.0.0-preview6" />
|
||||
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="0.10.18" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.18" />
|
||||
<PackageReference Include="Avalonia.Xaml.Behaviors" Version="0.10.18" />
|
||||
<PackageReference Include="DynamicData" Version="7.9.14" />
|
||||
<PackageReference Include="FluentAvaloniaUI" Version="1.4.1" />
|
||||
<PackageReference Include="Material.Icons.Avalonia" Version="1.1.10" />
|
||||
<PackageReference Include="ReactiveUI" Version="17.1.50" />
|
||||
<PackageReference Include="ReactiveUI.Validation" Version="2.2.1" />
|
||||
<PackageReference Include="RGB.NET.Core" Version="2.0.0-prerelease.17" />
|
||||
<PackageReference Include="SkiaSharp" Version="2.88.1-preview.108" />
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.0-preview6" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.0-preview6" />
|
||||
<PackageReference Include="Avalonia.Xaml.Behaviors" Version="11.0.0-preview6" />
|
||||
<PackageReference Include="DynamicData" Version="7.13.1" />
|
||||
<PackageReference Include="FluentAvaloniaUI" Version="2.0.0-preview6" />
|
||||
<PackageReference Include="Material.Icons.Avalonia" Version="2.0.0-preview3" />
|
||||
<PackageReference Include="ReactiveUI" Version="18.4.26" />
|
||||
<PackageReference Include="ReactiveUI.Validation" Version="3.1.7" />
|
||||
<PackageReference Include="RGB.NET.Core" Version="2.0.0-prerelease.29" />
|
||||
<PackageReference Include="SkiaSharp" Version="2.88.3" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj" />
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib"
|
||||
xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=exceptions/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Cwindow/@EntryIndexedValue">True</s:Boolean>
|
||||
|
||||
@ -15,7 +15,7 @@ public class LostFocusNumericUpDownBindingBehavior : Behavior<NumericUpDown>
|
||||
/// <summary>
|
||||
/// Gets or sets the value of the binding.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<double> ValueProperty = AvaloniaProperty.Register<LostFocusNumericUpDownBindingBehavior, double>(
|
||||
public static readonly StyledProperty<decimal?> ValueProperty = AvaloniaProperty.Register<LostFocusNumericUpDownBindingBehavior, decimal?>(
|
||||
nameof(Value), defaultBindingMode: BindingMode.TwoWay);
|
||||
|
||||
static LostFocusNumericUpDownBindingBehavior()
|
||||
@ -26,7 +26,7 @@ public class LostFocusNumericUpDownBindingBehavior : Behavior<NumericUpDown>
|
||||
/// <summary>
|
||||
/// Gets or sets the value of the binding.
|
||||
/// </summary>
|
||||
public double Value
|
||||
public decimal? Value
|
||||
{
|
||||
get => GetValue(ValueProperty);
|
||||
set => SetValue(ValueProperty, value);
|
||||
|
||||
@ -2,12 +2,12 @@ using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Documents;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.LogicalTree;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Visuals.Media.Imaging;
|
||||
using Material.Icons;
|
||||
using Material.Icons.Avalonia;
|
||||
|
||||
@ -16,9 +16,9 @@ namespace Artemis.UI.Shared;
|
||||
/// <summary>
|
||||
/// Represents a control that can display an arbitrary kind of icon.
|
||||
/// </summary>
|
||||
public class ArtemisIcon : UserControl
|
||||
public partial class ArtemisIcon : UserControl
|
||||
{
|
||||
private static readonly Regex _imageRegex = new(@"[\/.](gif|jpg|jpeg|tiff|png)$", RegexOptions.Compiled);
|
||||
private static readonly Regex ImageRegex = new(@"[\/.](gif|jpg|jpeg|tiff|png)$", RegexOptions.Compiled);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="ArtemisIcon" /> class.
|
||||
@ -28,12 +28,7 @@ public class ArtemisIcon : UserControl
|
||||
InitializeComponent();
|
||||
DetachedFromLogicalTree += OnDetachedFromLogicalTree;
|
||||
LayoutUpdated += OnLayoutUpdated;
|
||||
}
|
||||
|
||||
private static void IconChanging(IAvaloniaObject sender, bool before)
|
||||
{
|
||||
if (before)
|
||||
((ArtemisIcon) sender).Update();
|
||||
PropertyChanged += OnPropertyChanged;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
@ -54,7 +49,7 @@ public class ArtemisIcon : UserControl
|
||||
Content = new MaterialIcon {Kind = parsedIcon, Width = Bounds.Width, Height = Bounds.Height};
|
||||
}
|
||||
// An URI pointing to an image
|
||||
else if (_imageRegex.IsMatch(iconString))
|
||||
else if (ImageRegex.IsMatch(iconString))
|
||||
{
|
||||
if (!Fill)
|
||||
Content = new Image
|
||||
@ -66,7 +61,7 @@ public class ArtemisIcon : UserControl
|
||||
else
|
||||
Content = new Border
|
||||
{
|
||||
Background = TextBlock.GetForeground(this),
|
||||
Background = TextElement.GetForeground(this),
|
||||
VerticalAlignment = VerticalAlignment.Stretch,
|
||||
HorizontalAlignment = HorizontalAlignment.Stretch,
|
||||
OpacityMask = new ImageBrush(new Bitmap(iconString)) {BitmapInterpolationMode = BitmapInterpolationMode.MediumQuality}
|
||||
@ -92,6 +87,12 @@ public class ArtemisIcon : UserControl
|
||||
contentControl.Height = Bounds.Height;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.Property == IconProperty || e.Property == FillProperty)
|
||||
Update();
|
||||
}
|
||||
|
||||
private void OnDetachedFromLogicalTree(object? sender, LogicalTreeAttachmentEventArgs e)
|
||||
{
|
||||
@ -99,19 +100,13 @@ public class ArtemisIcon : UserControl
|
||||
disposable.Dispose();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the currently displayed icon as either a <see cref="MaterialIconKind" /> or an <see cref="Uri" />
|
||||
/// pointing to an SVG
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<object?> IconProperty =
|
||||
AvaloniaProperty.Register<ArtemisIcon, object?>(nameof(Icon), notifying: IconChanging);
|
||||
public static readonly StyledProperty<object?> IconProperty = AvaloniaProperty.Register<ArtemisIcon, object?>(nameof(Icon));
|
||||
|
||||
|
||||
/// <summary>
|
||||
@ -128,8 +123,7 @@ public class ArtemisIcon : UserControl
|
||||
/// Gets or sets a boolean indicating whether or not the icon should be filled in with the primary text color of the
|
||||
/// theme
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<bool> FillProperty =
|
||||
AvaloniaProperty.Register<ArtemisIcon, bool>(nameof(Icon), false, notifying: IconChanging);
|
||||
public static readonly StyledProperty<bool> FillProperty = AvaloniaProperty.Register<ArtemisIcon, bool>(nameof(Icon));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether or not the icon should be filled in with the primary text color of the
|
||||
|
||||
@ -46,8 +46,8 @@ public class DataModelPickerButton : TemplatedControl
|
||||
/// <summary>
|
||||
/// Gets or sets the desired flyout placement.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<FlyoutPlacementMode> PlacementProperty =
|
||||
AvaloniaProperty.Register<FlyoutBase, FlyoutPlacementMode>(nameof(Placement));
|
||||
public static readonly StyledProperty<PlacementMode> PlacementProperty =
|
||||
AvaloniaProperty.Register<FlyoutBase, PlacementMode>(nameof(Placement));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets data model path.
|
||||
@ -133,7 +133,7 @@ public class DataModelPickerButton : TemplatedControl
|
||||
/// <summary>
|
||||
/// Gets or sets the desired flyout placement.
|
||||
/// </summary>
|
||||
public FlyoutPlacementMode Placement
|
||||
public PlacementMode Placement
|
||||
{
|
||||
get => GetValue(PlacementProperty);
|
||||
set => SetValue(PlacementProperty, value);
|
||||
|
||||
@ -12,10 +12,7 @@ using Avalonia.Input;
|
||||
using Avalonia.LogicalTree;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Platform;
|
||||
using Avalonia.Rendering;
|
||||
using Avalonia.Threading;
|
||||
using Avalonia.Visuals.Media.Imaging;
|
||||
|
||||
namespace Artemis.UI.Shared;
|
||||
|
||||
@ -41,12 +38,14 @@ public class DeviceVisualizer : Control
|
||||
_deviceVisualizerLeds = new List<DeviceVisualizerLed>();
|
||||
|
||||
PointerReleased += OnPointerReleased;
|
||||
PropertyChanged += OnPropertyChanged;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Render(DrawingContext drawingContext)
|
||||
{
|
||||
if (Device == null)
|
||||
if (Device == null || _deviceBounds.Width == 0 || _deviceBounds.Height == 0 || _loading)
|
||||
return;
|
||||
|
||||
// Determine the scale required to fit the desired size of the control
|
||||
@ -57,11 +56,11 @@ public class DeviceVisualizer : Control
|
||||
{
|
||||
// Scale the visualization in the desired bounding box
|
||||
if (Bounds.Width > 0 && Bounds.Height > 0)
|
||||
boundsPush = drawingContext.PushPreTransform(Matrix.CreateScale(scale, scale));
|
||||
boundsPush = drawingContext.PushTransform(Matrix.CreateScale(scale, scale));
|
||||
|
||||
// Apply device rotation
|
||||
using DrawingContext.PushedState translationPush = drawingContext.PushPreTransform(Matrix.CreateTranslation(0 - _deviceBounds.Left, 0 - _deviceBounds.Top));
|
||||
using DrawingContext.PushedState rotationPush = drawingContext.PushPreTransform(Matrix.CreateRotation(Matrix.ToRadians(Device.Rotation)));
|
||||
using DrawingContext.PushedState translationPush = drawingContext.PushTransform(Matrix.CreateTranslation(0 - _deviceBounds.Left, 0 - _deviceBounds.Top));
|
||||
using DrawingContext.PushedState rotationPush = drawingContext.PushTransform(Matrix.CreateRotation(Matrix.ToRadians(Device.Rotation)));
|
||||
|
||||
// Render device and LED images
|
||||
if (_deviceImage != null)
|
||||
@ -78,7 +77,7 @@ public class DeviceVisualizer : Control
|
||||
lock (_deviceVisualizerLeds)
|
||||
{
|
||||
// Apply device scale
|
||||
using DrawingContext.PushedState scalePush = drawingContext.PushPreTransform(Matrix.CreateScale(Device.Scale, Device.Scale));
|
||||
using DrawingContext.PushedState scalePush = drawingContext.PushTransform(Matrix.CreateScale(Device.Scale, Device.Scale));
|
||||
foreach (DeviceVisualizerLed deviceVisualizerLed in _deviceVisualizerLeds)
|
||||
deviceVisualizerLed.RenderGeometry(drawingContext, false);
|
||||
}
|
||||
@ -123,7 +122,7 @@ public class DeviceVisualizer : Control
|
||||
private Rect MeasureDevice()
|
||||
{
|
||||
if (Device == null)
|
||||
return Rect.Empty;
|
||||
return new Rect();
|
||||
|
||||
Rect deviceRect = new(0, 0, Device.RgbDevice.ActualSize.Width, Device.RgbDevice.ActualSize.Height);
|
||||
Geometry geometry = new RectangleGeometry(deviceRect);
|
||||
@ -155,6 +154,12 @@ public class DeviceVisualizer : Control
|
||||
OnClicked(e);
|
||||
}
|
||||
|
||||
private void OnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.Property == DeviceProperty)
|
||||
SetupForDevice();
|
||||
}
|
||||
|
||||
private void DevicePropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
Dispatcher.UIThread.Post(SetupForDevice, DispatcherPriority.Background);
|
||||
@ -171,13 +176,7 @@ public class DeviceVisualizer : Control
|
||||
/// Gets or sets the <see cref="ArtemisDevice" /> to display
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<ArtemisDevice?> DeviceProperty =
|
||||
AvaloniaProperty.Register<DeviceVisualizer, ArtemisDevice?>(nameof(Device), notifying: DeviceUpdated);
|
||||
|
||||
private static void DeviceUpdated(IAvaloniaObject sender, bool before)
|
||||
{
|
||||
if (!before)
|
||||
((DeviceVisualizer) sender).SetupForDevice();
|
||||
}
|
||||
AvaloniaProperty.Register<DeviceVisualizer, ArtemisDevice?>(nameof(Device));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="ArtemisDevice" /> to display
|
||||
@ -209,6 +208,8 @@ public class DeviceVisualizer : Control
|
||||
public static readonly StyledProperty<ObservableCollection<ArtemisLed>?> HighlightedLedsProperty =
|
||||
AvaloniaProperty.Register<DeviceVisualizer, ObservableCollection<ArtemisLed>?>(nameof(HighlightedLeds));
|
||||
|
||||
private bool _loading;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a list of LEDs to highlight
|
||||
/// </summary>
|
||||
@ -274,6 +275,7 @@ public class DeviceVisualizer : Control
|
||||
return;
|
||||
|
||||
_deviceBounds = MeasureDevice();
|
||||
_loading = true;
|
||||
|
||||
Device.RgbDevice.PropertyChanged += DevicePropertyChanged;
|
||||
Device.DeviceUpdated += DeviceUpdated;
|
||||
@ -289,22 +291,22 @@ public class DeviceVisualizer : Control
|
||||
ArtemisDevice? device = Device;
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
if (device.Layout?.Image == null || !File.Exists(device.Layout.Image.LocalPath))
|
||||
{
|
||||
_deviceImage?.Dispose();
|
||||
_deviceImage = null;
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (device.Layout?.Image == null || !File.Exists(device.Layout.Image.LocalPath))
|
||||
{
|
||||
_deviceImage?.Dispose();
|
||||
_deviceImage = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a bitmap that'll be used to render the device and LED images just once
|
||||
// Render 4 times the actual size of the device to make sure things look sharp when zoomed in
|
||||
RenderTargetBitmap renderTargetBitmap = new(new PixelSize((int) device.RgbDevice.ActualSize.Width * 2, (int) device.RgbDevice.ActualSize.Height * 2));
|
||||
|
||||
using IDrawingContextImpl context = renderTargetBitmap.CreateDrawingContext(new ImmediateRenderer(this));
|
||||
using DrawingContext context = renderTargetBitmap.CreateDrawingContext();
|
||||
using Bitmap bitmap = new(device.Layout.Image.LocalPath);
|
||||
context.DrawBitmap(bitmap.PlatformImpl, 1, new Rect(bitmap.Size), new Rect(renderTargetBitmap.Size), BitmapInterpolationMode.HighQuality);
|
||||
context.DrawImage(bitmap, new Rect(bitmap.Size), new Rect(renderTargetBitmap.Size), BitmapInterpolationMode.HighQuality);
|
||||
|
||||
lock (_deviceVisualizerLeds)
|
||||
{
|
||||
@ -315,12 +317,16 @@ public class DeviceVisualizer : Control
|
||||
_deviceImage?.Dispose();
|
||||
_deviceImage = renderTargetBitmap;
|
||||
|
||||
Dispatcher.UIThread.Post(InvalidateMeasure);
|
||||
InvalidateMeasure();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
finally
|
||||
{
|
||||
_loading = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -4,8 +4,6 @@ using Artemis.Core;
|
||||
using Avalonia;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Platform;
|
||||
using Avalonia.Visuals.Media.Imaging;
|
||||
using RGB.NET.Core;
|
||||
using Color = Avalonia.Media.Color;
|
||||
using Point = Avalonia.Point;
|
||||
@ -40,7 +38,7 @@ internal class DeviceVisualizerLed
|
||||
public Rect LedRect { get; set; }
|
||||
public Geometry? DisplayGeometry { get; private set; }
|
||||
|
||||
public void DrawBitmap(IDrawingContextImpl drawingContext, double scale)
|
||||
public void DrawBitmap(DrawingContext drawingContext, double scale)
|
||||
{
|
||||
if (Led.Layout?.Image == null || !File.Exists(Led.Layout.Image.LocalPath))
|
||||
return;
|
||||
@ -48,9 +46,8 @@ internal class DeviceVisualizerLed
|
||||
try
|
||||
{
|
||||
using Bitmap bitmap = new(Led.Layout.Image.LocalPath);
|
||||
drawingContext.DrawBitmap(
|
||||
bitmap.PlatformImpl,
|
||||
1,
|
||||
drawingContext.DrawImage(
|
||||
bitmap,
|
||||
new Rect(bitmap.Size),
|
||||
new Rect(Led.RgbLed.Location.X * scale, Led.RgbLed.Location.Y * scale, Led.RgbLed.Size.Width * scale, Led.RgbLed.Size.Height * scale),
|
||||
BitmapInterpolationMode.HighQuality
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
</UserControl.Styles>
|
||||
|
||||
<Panel>
|
||||
<controls:NumberBox Name="NumberBox"
|
||||
<controls:NumberBox Name="InnerNumberBox"
|
||||
AcceptsExpression="True"
|
||||
LargeChange="{Binding $parent[sharedControls:DraggableNumberBox].LargeChange}"
|
||||
SmallChange="{Binding $parent[sharedControls:DraggableNumberBox].SmallChange}"
|
||||
|
||||
@ -14,12 +14,12 @@ namespace Artemis.UI.Shared.Controls;
|
||||
/// <summary>
|
||||
/// Represents a number box that can be mutated by dragging over it horizontally
|
||||
/// </summary>
|
||||
public class DraggableNumberBox : UserControl
|
||||
public partial class DraggableNumberBox : UserControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the <see cref="Value" /> property.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<double> ValueProperty = AvaloniaProperty.Register<DraggableNumberBox, double>(nameof(Value), defaultBindingMode: BindingMode.TwoWay, notifying: ValueChanged);
|
||||
public static readonly StyledProperty<double> ValueProperty = AvaloniaProperty.Register<DraggableNumberBox, double>(nameof(Value), defaultBindingMode: BindingMode.TwoWay);
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="Minimum" /> property.
|
||||
@ -56,7 +56,6 @@ public class DraggableNumberBox : UserControl
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<string?> SuffixProperty = AvaloniaProperty.Register<DraggableNumberBox, string?>(nameof(Suffix));
|
||||
|
||||
private readonly NumberBox _numberBox;
|
||||
private TextBox? _inputTextBox;
|
||||
private double _lastX;
|
||||
private bool _moved;
|
||||
@ -69,13 +68,13 @@ public class DraggableNumberBox : UserControl
|
||||
public DraggableNumberBox()
|
||||
{
|
||||
InitializeComponent();
|
||||
_numberBox = this.Get<NumberBox>("NumberBox");
|
||||
_numberBox.Value = Value;
|
||||
InnerNumberBox.Value = Value;
|
||||
|
||||
PointerPressed += OnPointerPressed;
|
||||
PointerMoved += OnPointerMoved;
|
||||
PointerReleased += OnPointerReleased;
|
||||
|
||||
PropertyChanged += OnPropertyChanged;
|
||||
|
||||
AddHandler(KeyUpEvent, HandleKeyUp, RoutingStrategies.Direct | RoutingStrategies.Tunnel | RoutingStrategies.Bubble, true);
|
||||
}
|
||||
|
||||
@ -160,36 +159,27 @@ public class DraggableNumberBox : UserControl
|
||||
/// Occurs when the user finishes dragging over the control.
|
||||
/// </summary>
|
||||
public event TypedEventHandler<DraggableNumberBox, EventArgs>? DragFinished;
|
||||
|
||||
private static void ValueChanged(IAvaloniaObject sender, bool before)
|
||||
|
||||
private void SetNumberBoxValue(double value)
|
||||
{
|
||||
if (before)
|
||||
if (!(Math.Abs(InnerNumberBox.Value - Value) > 0.00001))
|
||||
return;
|
||||
|
||||
DraggableNumberBox draggable = (DraggableNumberBox) sender;
|
||||
if (!(Math.Abs(draggable._numberBox.Value - draggable.Value) > 0.00001))
|
||||
return;
|
||||
|
||||
draggable._updating = true;
|
||||
draggable._numberBox.Value = draggable.Value;
|
||||
draggable._updating = false;
|
||||
_updating = true;
|
||||
InnerNumberBox.Value = Value;
|
||||
_updating = false;
|
||||
}
|
||||
|
||||
private void HandleKeyUp(object? sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == Key.Enter || e.Key == Key.Escape)
|
||||
Parent?.Focus();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
FocusManager.Instance?.Focus(Parent as IInputElement);
|
||||
}
|
||||
|
||||
private void OnPointerPressed(object? sender, PointerPressedEventArgs e)
|
||||
{
|
||||
PointerPoint point = e.GetCurrentPoint(this);
|
||||
_inputTextBox = _numberBox.FindDescendantOfType<TextBox>();
|
||||
_inputTextBox = InnerNumberBox.FindDescendantOfType<TextBox>();
|
||||
_moved = false;
|
||||
_startX = point.Position.X;
|
||||
_lastX = point.Position.X;
|
||||
@ -211,7 +201,7 @@ public class DraggableNumberBox : UserControl
|
||||
if (!_moved)
|
||||
{
|
||||
// Let our parent take focus, it would make more sense to take focus ourselves but that hides the collider
|
||||
Parent?.Focus();
|
||||
FocusManager.Instance?.Focus(Parent as IInputElement);
|
||||
_moved = true;
|
||||
e.Pointer.Capture(this);
|
||||
DragStarted?.Invoke(this, EventArgs.Empty);
|
||||
@ -253,6 +243,12 @@ public class DraggableNumberBox : UserControl
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void OnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.Property == ValueProperty)
|
||||
SetNumberBoxValue(Value);
|
||||
}
|
||||
|
||||
private void NumberBox_OnValueChanged(NumberBox sender, NumberBoxValueChangedEventArgs args)
|
||||
{
|
||||
@ -261,17 +257,17 @@ public class DraggableNumberBox : UserControl
|
||||
|
||||
if (args.NewValue < Minimum)
|
||||
{
|
||||
_numberBox.Value = Minimum;
|
||||
InnerNumberBox.Value = Minimum;
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.NewValue > Maximum)
|
||||
{
|
||||
_numberBox.Value = Maximum;
|
||||
InnerNumberBox.Value = Maximum;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Math.Abs(Value - _numberBox.Value) > 0.00001)
|
||||
Value = _numberBox.Value;
|
||||
if (Math.Abs(Value - InnerNumberBox.Value) > 0.00001)
|
||||
Value = InnerNumberBox.Value;
|
||||
}
|
||||
}
|
||||
@ -4,7 +4,7 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Shared.EnumComboBox">
|
||||
<ComboBox x:Name="EnumComboBox" HorizontalAlignment="Stretch">
|
||||
<ComboBox x:Name="ChildEnumComboBox" HorizontalAlignment="Stretch">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding [1]}" />
|
||||
|
||||
@ -13,13 +13,12 @@ namespace Artemis.UI.Shared;
|
||||
/// <summary>
|
||||
/// Represents a combobox that can display the values of an enum.
|
||||
/// </summary>
|
||||
public class EnumComboBox : UserControl
|
||||
public partial class EnumComboBox : UserControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the currently selected value
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<object?> ValueProperty =
|
||||
AvaloniaProperty.Register<EnumComboBox, object?>(nameof(Value), defaultBindingMode: BindingMode.TwoWay, notifying: ValueChanged);
|
||||
public static readonly StyledProperty<object?> ValueProperty = AvaloniaProperty.Register<EnumComboBox, object?>(nameof(Value), defaultBindingMode: BindingMode.TwoWay);
|
||||
|
||||
private readonly ObservableCollection<(Enum, string)> _currentValues = new();
|
||||
private Type? _currentType;
|
||||
@ -31,9 +30,19 @@ public class EnumComboBox : UserControl
|
||||
/// </summary>
|
||||
public EnumComboBox()
|
||||
{
|
||||
PropertyChanged += OnPropertyChanged;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void OnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.Property == ValueProperty)
|
||||
{
|
||||
UpdateValues();
|
||||
UpdateSelection();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the currently selected value
|
||||
/// </summary>
|
||||
@ -43,20 +52,6 @@ public class EnumComboBox : UserControl
|
||||
set => SetValue(ValueProperty, value);
|
||||
}
|
||||
|
||||
private static void ValueChanged(IAvaloniaObject sender, bool before)
|
||||
{
|
||||
if (sender is EnumComboBox enumCombo && !before)
|
||||
{
|
||||
enumCombo.UpdateValues();
|
||||
enumCombo.UpdateSelection();
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
private void OnSelectionChanged(object? sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (_enumComboBox == null || _enumComboBox.SelectedIndex == -1)
|
||||
@ -95,7 +90,7 @@ public class EnumComboBox : UserControl
|
||||
/// <inheritdoc />
|
||||
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
|
||||
{
|
||||
_enumComboBox = this.Get<ComboBox>("EnumComboBox");
|
||||
_enumComboBox = this.Get<ComboBox>("ChildEnumComboBox");
|
||||
_enumComboBox.Items = _currentValues;
|
||||
|
||||
UpdateValues();
|
||||
|
||||
@ -27,7 +27,7 @@ public class GradientPicker : TemplatedControl
|
||||
/// Gets or sets the color gradient.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<ColorGradient> ColorGradientProperty =
|
||||
AvaloniaProperty.Register<GradientPicker, ColorGradient>(nameof(ColorGradient), notifying: ColorGradientChanged, defaultValue: ColorGradient.GetUnicornBarf());
|
||||
AvaloniaProperty.Register<GradientPicker, ColorGradient>(nameof(ColorGradient), defaultValue: ColorGradient.GetUnicornBarf());
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the currently selected color stop.
|
||||
@ -45,7 +45,7 @@ public class GradientPicker : TemplatedControl
|
||||
/// Gets or sets a storage provider to use for storing and loading gradients.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<IColorGradientStorageProvider?> StorageProviderProperty =
|
||||
AvaloniaProperty.Register<GradientPicker, IColorGradientStorageProvider?>(nameof(StorageProvider), notifying: StorageProviderChanged);
|
||||
AvaloniaProperty.Register<GradientPicker, IColorGradientStorageProvider?>(nameof(StorageProvider));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the linear gradient brush representing the color gradient.
|
||||
@ -66,7 +66,7 @@ public class GradientPicker : TemplatedControl
|
||||
AvaloniaProperty.RegisterDirect<GradientPicker, ColorGradient>(nameof(EditingColorGradient), g => g.EditingColorGradient);
|
||||
|
||||
private readonly ICommand _deleteStop;
|
||||
private ColorPicker? _colorPicker;
|
||||
private FAColorPicker? _colorPicker;
|
||||
private Button? _flipStops;
|
||||
private Border? _gradient;
|
||||
private Button? _randomize;
|
||||
@ -94,6 +94,8 @@ public class GradientPicker : TemplatedControl
|
||||
|
||||
SelectedColorStop = EditingColorGradient.ElementAtOrDefault(index);
|
||||
});
|
||||
|
||||
PropertyChanged += OnPropertyChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -176,7 +178,7 @@ public class GradientPicker : TemplatedControl
|
||||
if (_randomize != null)
|
||||
_randomize.Click -= RandomizeOnClick;
|
||||
|
||||
_colorPicker = e.NameScope.Find<ColorPicker>("ColorPicker");
|
||||
_colorPicker = e.NameScope.Find<FAColorPicker>("ColorPicker");
|
||||
_gradient = e.NameScope.Find<Border>("Gradient");
|
||||
_spreadStops = e.NameScope.Find<Button>("SpreadStops");
|
||||
_toggleSeamless = e.NameScope.Find<Button>("ToggleSeamless");
|
||||
@ -220,16 +222,6 @@ public class GradientPicker : TemplatedControl
|
||||
_shiftDown = false;
|
||||
}
|
||||
|
||||
|
||||
private static void ColorGradientChanged(IAvaloniaObject sender, bool before)
|
||||
{
|
||||
(sender as GradientPicker)?.ApplyToField();
|
||||
}
|
||||
|
||||
private static void StorageProviderChanged(IAvaloniaObject sender, bool before)
|
||||
{
|
||||
}
|
||||
|
||||
private void ApplyToField()
|
||||
{
|
||||
EditingColorGradient = new ColorGradient(ColorGradient);
|
||||
@ -348,4 +340,10 @@ public class GradientPicker : TemplatedControl
|
||||
EditingColorGradient.Randomize(6);
|
||||
SelectedColorStop = EditingColorGradient.First();
|
||||
}
|
||||
|
||||
private void OnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.Property == ColorGradientProperty)
|
||||
ApplyToField();
|
||||
}
|
||||
}
|
||||
@ -12,7 +12,6 @@ using Avalonia.Interactivity;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Threading;
|
||||
using FluentAvalonia.Core;
|
||||
using Button = FluentAvalonia.UI.Controls.Button;
|
||||
|
||||
namespace Artemis.UI.Shared.Controls.GradientPicker;
|
||||
|
||||
@ -25,7 +24,7 @@ public class GradientPickerButton : TemplatedControl
|
||||
/// Gets or sets the color gradient.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<ColorGradient?> ColorGradientProperty =
|
||||
AvaloniaProperty.Register<GradientPickerButton, ColorGradient?>(nameof(ColorGradient), notifying: ColorGradientChanged);
|
||||
AvaloniaProperty.Register<GradientPickerButton, ColorGradient?>(nameof(ColorGradient));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether the gradient picker should be in compact mode or not.
|
||||
@ -48,6 +47,12 @@ public class GradientPickerButton : TemplatedControl
|
||||
private Button? _button;
|
||||
private ColorGradient? _lastColorGradient;
|
||||
|
||||
/// <inheritdoc />
|
||||
public GradientPickerButton()
|
||||
{
|
||||
PropertyChanged += OnPropertyChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color gradient.
|
||||
/// </summary>
|
||||
@ -105,12 +110,6 @@ public class GradientPickerButton : TemplatedControl
|
||||
|
||||
#endregion
|
||||
|
||||
private static void ColorGradientChanged(IAvaloniaObject sender, bool before)
|
||||
{
|
||||
if (!before)
|
||||
(sender as GradientPickerButton)?.Subscribe();
|
||||
}
|
||||
|
||||
private void Subscribe()
|
||||
{
|
||||
Unsubscribe();
|
||||
@ -178,6 +177,12 @@ public class GradientPickerButton : TemplatedControl
|
||||
LinearGradientBrush.GradientStops = collection;
|
||||
}
|
||||
|
||||
private void OnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.Property == ColorGradientProperty)
|
||||
Subscribe();
|
||||
}
|
||||
|
||||
#region Overrides of Visual
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@ -16,7 +16,7 @@ internal class GradientPickerColorStop : TemplatedControl
|
||||
/// Gets or sets the gradient picker.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<GradientPicker?> GradientPickerProperty =
|
||||
AvaloniaProperty.Register<GradientPickerColorStop, GradientPicker?>(nameof(GradientPicker), notifying: Notifying);
|
||||
AvaloniaProperty.Register<GradientPickerColorStop, GradientPicker?>(nameof(GradientPicker));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color stop.
|
||||
@ -28,8 +28,8 @@ internal class GradientPickerColorStop : TemplatedControl
|
||||
/// Gets or sets the position reference to use when positioning and dragging this color stop.
|
||||
/// <para>If <see langword="null" /> then dragging is not enabled.</para>
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<IControl?> PositionReferenceProperty =
|
||||
AvaloniaProperty.Register<GradientPickerColorStop, IControl?>(nameof(PositionReference));
|
||||
public static readonly StyledProperty<Control?> PositionReferenceProperty =
|
||||
AvaloniaProperty.Register<GradientPickerColorStop, Control?>(nameof(PositionReference));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the linear gradient brush representing the color gradient.
|
||||
@ -47,7 +47,16 @@ internal class GradientPickerColorStop : TemplatedControl
|
||||
public GradientPicker? GradientPicker
|
||||
{
|
||||
get => GetValue(GradientPickerProperty);
|
||||
set => SetValue(GradientPickerProperty, value);
|
||||
set
|
||||
{
|
||||
if (GradientPicker != null)
|
||||
GradientPicker.PropertyChanged -= GradientPickerOnPropertyChanged;
|
||||
SetValue(GradientPickerProperty, value);
|
||||
if (GradientPicker != null)
|
||||
GradientPicker.PropertyChanged += GradientPickerOnPropertyChanged;
|
||||
|
||||
IsSelected = ReferenceEquals(GradientPicker?.SelectedColorStop, ColorStop);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -63,7 +72,7 @@ internal class GradientPickerColorStop : TemplatedControl
|
||||
/// Gets or sets the position reference to use when positioning and dragging this color stop.
|
||||
/// <para>If <see langword="null" /> then dragging is not enabled.</para>
|
||||
/// </summary>
|
||||
public IControl? PositionReference
|
||||
public Control? PositionReference
|
||||
{
|
||||
get => GetValue(PositionReferenceProperty);
|
||||
set => SetValue(PositionReferenceProperty, value);
|
||||
@ -85,19 +94,6 @@ internal class GradientPickerColorStop : TemplatedControl
|
||||
}
|
||||
}
|
||||
|
||||
private static void Notifying(IAvaloniaObject sender, bool before)
|
||||
{
|
||||
if (sender is not GradientPickerColorStop self)
|
||||
return;
|
||||
|
||||
if (before && self.GradientPicker != null)
|
||||
self.GradientPicker.PropertyChanged -= self.GradientPickerOnPropertyChanged;
|
||||
else if (self.GradientPicker != null)
|
||||
self.GradientPicker.PropertyChanged += self.GradientPickerOnPropertyChanged;
|
||||
|
||||
self.IsSelected = ReferenceEquals(self.GradientPicker?.SelectedColorStop, self.ColorStop);
|
||||
}
|
||||
|
||||
private void GradientPickerOnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (GradientPicker != null && e.Property == GradientPicker.SelectedColorStopProperty)
|
||||
|
||||
@ -10,8 +10,16 @@
|
||||
<Style Selector="TextBox#DisplayTextBox:focus:not(TextBox:empty)">
|
||||
<Setter Property="InnerRightContent">
|
||||
<Template>
|
||||
<Button Classes="textBoxClearButton"
|
||||
Click="Button_OnClick" />
|
||||
<Button Theme="{StaticResource TextBoxDeleteButtonStyle}"
|
||||
Click="Button_OnClick"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="{DynamicResource ControlCornerRadius}"
|
||||
Padding="{StaticResource HelperButtonThemePadding}"
|
||||
IsTabStop="False"
|
||||
Focusable="False"
|
||||
FontSize="{TemplateBinding FontSize}"
|
||||
Width="30"
|
||||
VerticalAlignment="Stretch" />
|
||||
</Template>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
@ -8,6 +8,8 @@ using Avalonia.Data;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Threading;
|
||||
using DryIoc;
|
||||
using FluentAvalonia.Core;
|
||||
using Humanizer;
|
||||
using Material.Icons;
|
||||
@ -17,48 +19,64 @@ namespace Artemis.UI.Shared;
|
||||
/// <summary>
|
||||
/// Represents a control that can be used to display or edit <see cref="Core.Hotkey" /> instances.
|
||||
/// </summary>
|
||||
public class HotkeyBox : UserControl
|
||||
public partial class HotkeyBox : UserControl
|
||||
{
|
||||
private readonly TextBox _displayTextBox;
|
||||
private readonly IInputService _inputService;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="HotkeyBox" /> class
|
||||
/// </summary>
|
||||
public HotkeyBox()
|
||||
{
|
||||
InitializeComponent();
|
||||
_inputService = UI.Locator.Resolve<IInputService>();
|
||||
|
||||
_displayTextBox = this.Find<TextBox>("DisplayTextBox");
|
||||
_displayTextBox.KeyDown += DisplayTextBoxOnKeyDown;
|
||||
_displayTextBox.KeyUp += DisplayTextBoxOnKeyUp;
|
||||
InitializeComponent();
|
||||
PropertyChanged += OnPropertyChanged;
|
||||
UpdateDisplayTextBox();
|
||||
}
|
||||
|
||||
private static void HotkeyChanging(IAvaloniaObject sender, bool before)
|
||||
protected override void OnGotFocus(GotFocusEventArgs e)
|
||||
{
|
||||
((HotkeyBox) sender).UpdateDisplayTextBox();
|
||||
_inputService.KeyboardKeyDown += InputServiceOnKeyboardKeyDown;
|
||||
_inputService.KeyboardKeyUp += InputServiceOnKeyboardKeyUp;
|
||||
|
||||
base.OnGotFocus(e);
|
||||
}
|
||||
|
||||
private void DisplayTextBoxOnKeyDown(object? sender, KeyEventArgs e)
|
||||
protected override void OnLostFocus(RoutedEventArgs e)
|
||||
{
|
||||
if (e.Key >= Key.LeftShift && e.Key <= Key.RightAlt)
|
||||
_inputService.KeyboardKeyDown -= InputServiceOnKeyboardKeyDown;
|
||||
_inputService.KeyboardKeyUp -= InputServiceOnKeyboardKeyUp;
|
||||
|
||||
base.OnLostFocus(e);
|
||||
}
|
||||
|
||||
private void OnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.Property == HotkeyProperty)
|
||||
UpdateDisplayTextBox();
|
||||
}
|
||||
|
||||
private void InputServiceOnKeyboardKeyDown(object? sender, ArtemisKeyboardKeyEventArgs e)
|
||||
{
|
||||
if (e.Key >= KeyboardKey.LeftShift && e.Key <= KeyboardKey.RightAlt)
|
||||
return;
|
||||
|
||||
Hotkey ??= new Hotkey();
|
||||
Hotkey.Key = (KeyboardKey?) e.Key;
|
||||
Hotkey.Modifiers = (KeyboardModifierKey?) e.KeyModifiers;
|
||||
UpdateDisplayTextBox();
|
||||
HotkeyChanged?.Invoke(this, EventArgs.Empty);
|
||||
Hotkey.Key = e.Key;
|
||||
Hotkey.Modifiers = e.Modifiers;
|
||||
|
||||
e.Handled = true;
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
UpdateDisplayTextBox();
|
||||
HotkeyChanged?.Invoke(this, EventArgs.Empty);
|
||||
});
|
||||
}
|
||||
|
||||
private void DisplayTextBoxOnKeyUp(object? sender, KeyEventArgs e)
|
||||
private void InputServiceOnKeyboardKeyUp(object? sender, ArtemisKeyboardKeyEventArgs e)
|
||||
{
|
||||
if (e.KeyModifiers == KeyModifiers.None)
|
||||
FocusManager.Instance?.Focus(null);
|
||||
|
||||
e.Handled = true;
|
||||
if (e.Modifiers == KeyboardModifierKey.None)
|
||||
Dispatcher.UIThread.Post(() => FocusManager.Instance?.Focus(null));
|
||||
}
|
||||
|
||||
private void UpdateDisplayTextBox()
|
||||
@ -69,13 +87,8 @@ public class HotkeyBox : UserControl
|
||||
if (Hotkey?.Key != null)
|
||||
display = string.IsNullOrEmpty(display) ? Hotkey.Key.ToString() : $"{display}+{Hotkey.Key}";
|
||||
|
||||
_displayTextBox.Text = display;
|
||||
_displayTextBox.CaretIndex = _displayTextBox.Text?.Length ?? 0;
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
DisplayTextBox.Text = display;
|
||||
DisplayTextBox.CaretIndex = DisplayTextBox.Text?.Length ?? 0;
|
||||
}
|
||||
|
||||
private void Button_OnClick(object? sender, RoutedEventArgs e)
|
||||
@ -92,20 +105,17 @@ public class HotkeyBox : UserControl
|
||||
/// Gets or sets the currently displayed icon as either a <see cref="MaterialIconKind" /> or an <see cref="Uri" />
|
||||
/// pointing to an SVG
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<Hotkey?> HotkeyProperty =
|
||||
AvaloniaProperty.Register<HotkeyBox, Hotkey?>(nameof(Hotkey), defaultBindingMode: BindingMode.TwoWay, notifying: HotkeyChanging);
|
||||
public static readonly StyledProperty<Hotkey?> HotkeyProperty = AvaloniaProperty.Register<HotkeyBox, Hotkey?>(nameof(Hotkey), defaultBindingMode: BindingMode.TwoWay);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the watermark of the hotkey box when it is empty.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<string?> WatermarkProperty =
|
||||
AvaloniaProperty.Register<HotkeyBox, string?>(nameof(Watermark));
|
||||
public static readonly StyledProperty<string?> WatermarkProperty = AvaloniaProperty.Register<HotkeyBox, string?>(nameof(Watermark));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether the watermark should float above the hotkey box when it is not empty.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<bool> UseFloatingWatermarkProperty =
|
||||
AvaloniaProperty.Register<HotkeyBox, bool>(nameof(UseFloatingWatermark));
|
||||
public static readonly StyledProperty<bool> UseFloatingWatermarkProperty = AvaloniaProperty.Register<HotkeyBox, bool>(nameof(UseFloatingWatermark));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the currently displayed icon as either a <see cref="MaterialIconKind" /> or an <see cref="Uri" />
|
||||
|
||||
@ -3,13 +3,13 @@ using System.IO;
|
||||
using Artemis.Core;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Documents;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.LogicalTree;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Threading;
|
||||
using Avalonia.Visuals.Media.Imaging;
|
||||
using Material.Icons;
|
||||
using Material.Icons.Avalonia;
|
||||
|
||||
@ -18,7 +18,7 @@ namespace Artemis.UI.Shared;
|
||||
/// <summary>
|
||||
/// Represents a control that can display the icon of a specific <see cref="ProfileConfiguration" />.
|
||||
/// </summary>
|
||||
public class ProfileConfigurationIcon : UserControl, IDisposable
|
||||
public partial class ProfileConfigurationIcon : UserControl, IDisposable
|
||||
{
|
||||
private Stream? _stream;
|
||||
|
||||
@ -72,18 +72,13 @@ public class ProfileConfigurationIcon : UserControl, IDisposable
|
||||
|
||||
Content = new Border
|
||||
{
|
||||
Background = TextBlock.GetForeground(this),
|
||||
Background = TextElement.GetForeground(this),
|
||||
VerticalAlignment = VerticalAlignment.Stretch,
|
||||
HorizontalAlignment = HorizontalAlignment.Stretch,
|
||||
OpacityMask = new ImageBrush(new Bitmap(stream)) {BitmapInterpolationMode = BitmapInterpolationMode.MediumQuality}
|
||||
};
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
private void OnDetachedFromLogicalTree(object? sender, LogicalTreeAttachmentEventArgs e)
|
||||
{
|
||||
if (ConfigurationIcon != null)
|
||||
|
||||
@ -40,8 +40,8 @@ public class SelectionRectangle : Control
|
||||
/// <summary>
|
||||
/// Defines the <see cref="InputElement" /> property.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<IControl?> InputElementProperty =
|
||||
AvaloniaProperty.Register<SelectionRectangle, IControl?>(nameof(InputElement), notifying: OnInputElementChanged);
|
||||
public static readonly StyledProperty<InputElement?> InputElementProperty =
|
||||
AvaloniaProperty.Register<SelectionRectangle, InputElement?>(nameof(InputElement));
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="ZoomRatio" /> property.
|
||||
@ -61,7 +61,7 @@ public class SelectionRectangle : Control
|
||||
private Rect? _displayRect;
|
||||
private bool _isSelecting;
|
||||
private Point _lastPosition;
|
||||
private IControl? _oldInputElement;
|
||||
private InputElement? _oldInputElement;
|
||||
private Point _startPosition;
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -69,6 +69,14 @@ public class SelectionRectangle : Control
|
||||
{
|
||||
AffectsRender<TextBlock>(BackgroundProperty, BorderBrushProperty, BorderThicknessProperty);
|
||||
IsHitTestVisible = false;
|
||||
|
||||
PropertyChanged += OnPropertyChanged;
|
||||
}
|
||||
|
||||
private void OnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.Property == InputElementProperty)
|
||||
SubscribeToInputElement();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -110,7 +118,7 @@ public class SelectionRectangle : Control
|
||||
/// <summary>
|
||||
/// Gets or sets the element that captures input for the selection rectangle.
|
||||
/// </summary>
|
||||
public IControl? InputElement
|
||||
public InputElement? InputElement
|
||||
{
|
||||
get => GetValue(InputElementProperty);
|
||||
set => SetValue(InputElementProperty, value);
|
||||
@ -162,11 +170,6 @@ public class SelectionRectangle : Control
|
||||
SelectionFinished?.Invoke(this, e);
|
||||
}
|
||||
|
||||
private static void OnInputElementChanged(IAvaloniaObject sender, bool before)
|
||||
{
|
||||
((SelectionRectangle) sender).SubscribeToInputElement();
|
||||
}
|
||||
|
||||
private void ParentOnPointerMoved(object? sender, PointerEventArgs e)
|
||||
{
|
||||
// Point moved seems to trigger when the element under the mouse changes?
|
||||
@ -185,13 +188,13 @@ public class SelectionRectangle : Control
|
||||
{
|
||||
e.Pointer.Capture(this);
|
||||
|
||||
_startPosition = e.GetPosition(Parent);
|
||||
_absoluteStartPosition = e.GetPosition(VisualRoot);
|
||||
_startPosition = e.GetPosition(Parent as Visual);
|
||||
_absoluteStartPosition = e.GetPosition(VisualRoot as Visual);
|
||||
_displayRect = null;
|
||||
}
|
||||
|
||||
Point currentPosition = e.GetPosition(Parent);
|
||||
Point absoluteCurrentPosition = e.GetPosition(VisualRoot);
|
||||
Point currentPosition = e.GetPosition(Parent as Visual);
|
||||
Point absoluteCurrentPosition = e.GetPosition(VisualRoot as Visual);
|
||||
|
||||
_displayRect = new Rect(
|
||||
new Point(Math.Min(_startPosition.X, currentPosition.X), Math.Min(_startPosition.Y, currentPosition.Y)),
|
||||
|
||||
@ -15,7 +15,7 @@ public class ParentWidthPercentageConverter : IValueConverter
|
||||
/// <inheritdoc />
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
if (parameter is not IControl parent || value is not double percentage)
|
||||
if (parameter is not Control parent || value is not double percentage)
|
||||
return value;
|
||||
|
||||
return parent.Width / 100.0 * percentage;
|
||||
@ -24,7 +24,7 @@ public class ParentWidthPercentageConverter : IValueConverter
|
||||
/// <inheritdoc />
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
if (parameter is not IControl parent || value is not double real)
|
||||
if (parameter is not Control parent || value is not double real)
|
||||
return value;
|
||||
|
||||
return real / parent.Width * 100.0;
|
||||
|
||||
@ -6,7 +6,7 @@ namespace Artemis.UI.Shared.DefaultTypes.DataModel.Display;
|
||||
/// <summary>
|
||||
/// Represents a default data model display view.
|
||||
/// </summary>
|
||||
public class DefaultDataModelDisplayView : UserControl
|
||||
public partial class DefaultDataModelDisplayView : UserControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="DefaultDataModelDisplayView" /> class.
|
||||
@ -15,9 +15,4 @@ public class DefaultDataModelDisplayView : UserControl
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
@ -7,7 +7,7 @@ namespace Artemis.UI.Shared.DefaultTypes.DataModel.Display;
|
||||
/// <summary>
|
||||
/// Represents a data model display view used to display <see cref="SKColor" /> values.
|
||||
/// </summary>
|
||||
public class SKColorDataModelDisplayView : UserControl
|
||||
public partial class SKColorDataModelDisplayView : UserControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="SKColorDataModelDisplayView" /> class.
|
||||
@ -17,8 +17,4 @@ public class SKColorDataModelDisplayView : UserControl
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
@ -17,5 +17,7 @@ public static class ContainerExtensions
|
||||
{
|
||||
Assembly artemisShared = typeof(IArtemisSharedUIService).GetAssembly();
|
||||
container.RegisterMany(new[] { artemisShared }, type => type.IsAssignableTo<IArtemisSharedUIService>(), Reuse.Singleton);
|
||||
|
||||
UI.Locator = container;
|
||||
}
|
||||
}
|
||||
@ -6,7 +6,7 @@ using Avalonia.VisualTree;
|
||||
namespace Artemis.UI.Shared.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for Avalonia's <see cref="IVisual" /> type
|
||||
/// Provides extension methods for Avalonia's <see cref="Visual" /> type
|
||||
/// </summary>
|
||||
public static class VisualExtensions
|
||||
{
|
||||
@ -16,15 +16,15 @@ public static class VisualExtensions
|
||||
/// <typeparam name="T">The type the children should have.</typeparam>
|
||||
/// <param name="root">The root visual at which to start searching.</param>
|
||||
/// <returns>A recursive list of all visual children of type <typeparamref name="T" />.</returns>
|
||||
public static List<T> GetVisualChildrenOfType<T>(this IVisual root)
|
||||
public static List<T> GetVisualChildrenOfType<T>(this Visual root)
|
||||
{
|
||||
List<T> result = new();
|
||||
|
||||
List<IVisual>? visualChildren = root.GetVisualChildren()?.ToList();
|
||||
List<Visual>? visualChildren = root.GetVisualChildren()?.ToList();
|
||||
if (visualChildren == null || !visualChildren.Any())
|
||||
return result;
|
||||
|
||||
foreach (IVisual visualChild in visualChildren)
|
||||
foreach (Visual visualChild in visualChildren)
|
||||
{
|
||||
if (visualChild is T toFind)
|
||||
result.Add(toFind);
|
||||
@ -41,15 +41,15 @@ public static class VisualExtensions
|
||||
/// <typeparam name="T">The type of data context the children should have.</typeparam>
|
||||
/// <param name="root">The root visual at which to start searching.</param>
|
||||
/// <returns>A recursive list of all visual children with a data context of type <typeparamref name="T" />.</returns>
|
||||
public static List<T> GetVisualChildrenOfDataContextType<T>(this IVisual root)
|
||||
public static List<T> GetVisualChildrenOfDataContextType<T>(this Visual root)
|
||||
{
|
||||
List<T> result = new();
|
||||
|
||||
List<IVisual>? visualChildren = root.GetVisualChildren()?.ToList();
|
||||
List<Visual>? visualChildren = root.GetVisualChildren()?.ToList();
|
||||
if (visualChildren == null || !visualChildren.Any())
|
||||
return result;
|
||||
|
||||
foreach (IVisual visualChild in visualChildren)
|
||||
foreach (Visual visualChild in visualChildren)
|
||||
{
|
||||
if (visualChild is IDataContextProvider dataContextProvider && dataContextProvider.DataContext is T toFind)
|
||||
result.Add(toFind);
|
||||
|
||||
@ -4,8 +4,9 @@ using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Media.Immutable;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Avalonia.Styling;
|
||||
using FluentAvalonia.UI.Media;
|
||||
using FluentAvalonia.UI.Windowing;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Shared;
|
||||
@ -17,18 +18,18 @@ namespace Artemis.UI.Shared;
|
||||
/// and vice versa.
|
||||
/// </summary>
|
||||
/// <typeparam name="TViewModel">ViewModel type.</typeparam>
|
||||
public class ReactiveCoreWindow<TViewModel> : CoreWindow, IViewFor<TViewModel> where TViewModel : class
|
||||
public class ReactiveAppWindow<TViewModel> : AppWindow, IViewFor<TViewModel> where TViewModel : class
|
||||
{
|
||||
/// <summary>
|
||||
/// The ViewModel.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<TViewModel?> ViewModelProperty = AvaloniaProperty
|
||||
.Register<ReactiveCoreWindow<TViewModel>, TViewModel?>(nameof(ViewModel));
|
||||
.Register<ReactiveAppWindow<TViewModel>, TViewModel?>(nameof(ViewModel));
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ReactiveCoreWindow{TViewModel}" /> class.
|
||||
/// Initializes a new instance of the <see cref="ReactiveAppWindow{TViewModel}" /> class.
|
||||
/// </summary>
|
||||
public ReactiveCoreWindow()
|
||||
public ReactiveAppWindow()
|
||||
{
|
||||
// This WhenActivated block calls ViewModel's WhenActivated
|
||||
// block if the ViewModel implements IActivatableViewModel.
|
||||
@ -46,13 +47,9 @@ public class ReactiveCoreWindow<TViewModel> : CoreWindow, IViewFor<TViewModel> w
|
||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || !IsWindows11)
|
||||
return;
|
||||
|
||||
// Enable Mica on Windows 11, based on the FluentAvalonia sample application
|
||||
TransparencyBackgroundFallback = Brushes.Transparent;
|
||||
TransparencyLevelHint = WindowTransparencyLevel.Mica;
|
||||
|
||||
Color2 color = this.TryFindResource("SolidBackgroundFillColorBase", out object? value) ? (Color) value! : new Color2(32, 32, 32);
|
||||
color = color.LightenPercent(-0.5f);
|
||||
Background = new ImmutableSolidColorBrush(color, 0.82);
|
||||
TryEnableMicaEffect();
|
||||
}
|
||||
|
||||
private void OnDataContextChanged(object? value)
|
||||
@ -70,6 +67,31 @@ public class ReactiveCoreWindow<TViewModel> : CoreWindow, IViewFor<TViewModel> w
|
||||
else if (DataContext != value) DataContext = value;
|
||||
}
|
||||
|
||||
private void TryEnableMicaEffect()
|
||||
{
|
||||
// The background colors for the Mica brush are still based around SolidBackgroundFillColorBase resource
|
||||
// BUT since we can't control the actual Mica brush color, we have to use the window background to create
|
||||
// the same effect. However, we can't use SolidBackgroundFillColorBase directly since its opaque, and if
|
||||
// we set the opacity the color become lighter than we want. So we take the normal color, darken it and
|
||||
// apply the opacity until we get the roughly the correct color
|
||||
// NOTE that the effect still doesn't look right, but it suffices. Ideally we need access to the Mica
|
||||
// CompositionBrush to properly change the color but I don't know if we can do that or not
|
||||
if (ActualThemeVariant == ThemeVariant.Dark)
|
||||
{
|
||||
Color2 color = this.TryFindResource("SolidBackgroundFillColorBase", ThemeVariant.Dark, out object? value) ? (Color) value : new Color2(32, 32, 32);
|
||||
color = color.LightenPercent(-0.5f);
|
||||
|
||||
Background = new ImmutableSolidColorBrush(color, 0.78);
|
||||
}
|
||||
else if (ActualThemeVariant == ThemeVariant.Light)
|
||||
{
|
||||
// Similar effect here
|
||||
Color2 color = this.TryFindResource("SolidBackgroundFillColorBase", ThemeVariant.Light, out object? value) ? (Color) value : new Color2(243, 243, 243);
|
||||
color = color.LightenPercent(0.5f);
|
||||
|
||||
Background = new ImmutableSolidColorBrush(color, 0.9);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The ViewModel.
|
||||
@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Avalonia.Controls;
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
using Avalonia.Controls;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Platform.Storage;
|
||||
|
||||
namespace Artemis.UI.Shared.Services.Builders;
|
||||
|
||||
@ -7,11 +10,12 @@ namespace Artemis.UI.Shared.Services.Builders;
|
||||
/// </summary>
|
||||
public class FileDialogFilterBuilder
|
||||
{
|
||||
private readonly FileDialogFilter _filter;
|
||||
private string _name;
|
||||
private readonly List<string> _extensions = new();
|
||||
|
||||
internal FileDialogFilterBuilder()
|
||||
{
|
||||
_filter = new FileDialogFilter();
|
||||
_name = "Unknown";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -19,7 +23,7 @@ public class FileDialogFilterBuilder
|
||||
/// </summary>
|
||||
public FileDialogFilterBuilder WithName(string name)
|
||||
{
|
||||
_filter.Name = name;
|
||||
_name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -28,12 +32,16 @@ public class FileDialogFilterBuilder
|
||||
/// </summary>
|
||||
public FileDialogFilterBuilder WithExtension(string extension)
|
||||
{
|
||||
_filter.Extensions.Add(extension);
|
||||
if (!_extensions.Contains(extension))
|
||||
_extensions.Add(extension);
|
||||
return this;
|
||||
}
|
||||
|
||||
internal FileDialogFilter Build()
|
||||
internal FilePickerFileType Build()
|
||||
{
|
||||
return _filter;
|
||||
return new FilePickerFileType(_name)
|
||||
{
|
||||
Patterns = _extensions.Select(e => "*." + e).ToList()
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -116,7 +116,7 @@ public class NotificationBuilder
|
||||
/// <exception cref="ArtemisSharedUIException" />
|
||||
public Action Show()
|
||||
{
|
||||
IPanel? panel = _parent.Find<IPanel>("NotificationContainer");
|
||||
Panel? panel = _parent.Find<Panel>("NotificationContainer");
|
||||
if (panel == null)
|
||||
throw new ArtemisSharedUIException("Can't display a notification on a window without a NotificationContainer.");
|
||||
|
||||
@ -202,7 +202,7 @@ public class NotificationButtonBuilder
|
||||
return this;
|
||||
}
|
||||
|
||||
internal IControl Build()
|
||||
internal Control Build()
|
||||
{
|
||||
if (_action != null)
|
||||
return new Button {Content = _text, Command = ReactiveCommand.Create(() => _action()), Classes = new Classes("AppBarButton")};
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Platform.Storage;
|
||||
|
||||
namespace Artemis.UI.Shared.Services.Builders;
|
||||
|
||||
@ -10,8 +12,9 @@ namespace Artemis.UI.Shared.Services.Builders;
|
||||
/// </summary>
|
||||
public class OpenFileDialogBuilder
|
||||
{
|
||||
private readonly OpenFileDialog _openFileDialog;
|
||||
private readonly Window _parent;
|
||||
private readonly FilePickerOpenOptions _options;
|
||||
private List<FilePickerFileType>? _fileTypeFilters;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="OpenFileDialogBuilder" /> class.
|
||||
@ -20,7 +23,7 @@ public class OpenFileDialogBuilder
|
||||
internal OpenFileDialogBuilder(Window parent)
|
||||
{
|
||||
_parent = parent;
|
||||
_openFileDialog = new OpenFileDialog();
|
||||
_options = new FilePickerOpenOptions();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -28,7 +31,7 @@ public class OpenFileDialogBuilder
|
||||
/// </summary>
|
||||
public OpenFileDialogBuilder WithAllowMultiple()
|
||||
{
|
||||
_openFileDialog.AllowMultiple = true;
|
||||
_options.AllowMultiple = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -37,7 +40,7 @@ public class OpenFileDialogBuilder
|
||||
/// </summary>
|
||||
public OpenFileDialogBuilder WithTitle(string? title)
|
||||
{
|
||||
_openFileDialog.Title = title;
|
||||
_options.Title = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -46,16 +49,7 @@ public class OpenFileDialogBuilder
|
||||
/// </summary>
|
||||
public OpenFileDialogBuilder WithDirectory(string? directory)
|
||||
{
|
||||
_openFileDialog.Directory = directory;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the initial file name of the dialog
|
||||
/// </summary>
|
||||
public OpenFileDialogBuilder WithInitialFileName(string? initialFileName)
|
||||
{
|
||||
_openFileDialog.InitialFileName = initialFileName;
|
||||
_options.SuggestedStartLocation = directory != null ? _parent.StorageProvider.TryGetFolderFromPathAsync(directory).GetAwaiter().GetResult() : null;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -67,8 +61,9 @@ public class OpenFileDialogBuilder
|
||||
FileDialogFilterBuilder builder = new();
|
||||
configure(builder);
|
||||
|
||||
_openFileDialog.Filters ??= new List<FileDialogFilter>();
|
||||
_openFileDialog.Filters.Add(builder.Build());
|
||||
_fileTypeFilters ??= new List<FilePickerFileType>();
|
||||
_fileTypeFilters.Add(builder.Build());
|
||||
_options.FileTypeFilter = _fileTypeFilters;
|
||||
|
||||
return this;
|
||||
}
|
||||
@ -82,6 +77,7 @@ public class OpenFileDialogBuilder
|
||||
/// </returns>
|
||||
public async Task<string[]?> ShowAsync()
|
||||
{
|
||||
return await _openFileDialog.ShowAsync(_parent);
|
||||
IReadOnlyList<IStorageFile> files = await _parent.StorageProvider.OpenFilePickerAsync(_options);
|
||||
return files.Select(f => f.Path.AbsolutePath).ToArray();
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,8 @@
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Platform.Storage;
|
||||
|
||||
namespace Artemis.UI.Shared.Services.Builders;
|
||||
|
||||
@ -8,8 +11,8 @@ namespace Artemis.UI.Shared.Services.Builders;
|
||||
/// </summary>
|
||||
public class OpenFolderDialogBuilder
|
||||
{
|
||||
private readonly OpenFolderDialog _openFolderDialog;
|
||||
private readonly Window _parent;
|
||||
private readonly FolderPickerOpenOptions _options;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="OpenFolderDialogBuilder" /> class.
|
||||
@ -18,16 +21,15 @@ public class OpenFolderDialogBuilder
|
||||
internal OpenFolderDialogBuilder(Window parent)
|
||||
{
|
||||
_parent = parent;
|
||||
_openFolderDialog = new OpenFolderDialog();
|
||||
_options = new FolderPickerOpenOptions {AllowMultiple = false};
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Set the title of the dialog
|
||||
/// </summary>
|
||||
public OpenFolderDialogBuilder WithTitle(string? title)
|
||||
{
|
||||
_openFolderDialog.Title = title;
|
||||
_options.Title = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -36,7 +38,7 @@ public class OpenFolderDialogBuilder
|
||||
/// </summary>
|
||||
public OpenFolderDialogBuilder WithDirectory(string? directory)
|
||||
{
|
||||
_openFolderDialog.Directory = directory;
|
||||
_options.SuggestedStartLocation = directory != null ? _parent.StorageProvider.TryGetFolderFromPathAsync(directory).GetAwaiter().GetResult() : null;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -49,6 +51,7 @@ public class OpenFolderDialogBuilder
|
||||
/// </returns>
|
||||
public async Task<string?> ShowAsync()
|
||||
{
|
||||
return await _openFolderDialog.ShowAsync(_parent);
|
||||
IReadOnlyList<IStorageFolder> folder = await _parent.StorageProvider.OpenFolderPickerAsync(_options);
|
||||
return folder.FirstOrDefault()?.Path.AbsolutePath;
|
||||
}
|
||||
}
|
||||
@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Platform.Storage;
|
||||
|
||||
namespace Artemis.UI.Shared.Services.Builders;
|
||||
|
||||
@ -11,8 +12,9 @@ namespace Artemis.UI.Shared.Services.Builders;
|
||||
public class SaveFileDialogBuilder
|
||||
{
|
||||
private readonly Window _parent;
|
||||
private readonly SaveFileDialog _saveFileDialog;
|
||||
|
||||
private readonly FilePickerSaveOptions _options;
|
||||
private List<FilePickerFileType>? _fileTypeFilters;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="SaveFileDialogBuilder" /> class.
|
||||
/// </summary>
|
||||
@ -20,7 +22,7 @@ public class SaveFileDialogBuilder
|
||||
internal SaveFileDialogBuilder(Window parent)
|
||||
{
|
||||
_parent = parent;
|
||||
_saveFileDialog = new SaveFileDialog();
|
||||
_options = new FilePickerSaveOptions();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -28,7 +30,7 @@ public class SaveFileDialogBuilder
|
||||
/// </summary>
|
||||
public SaveFileDialogBuilder WithTitle(string? title)
|
||||
{
|
||||
_saveFileDialog.Title = title;
|
||||
_options.Title = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -37,7 +39,7 @@ public class SaveFileDialogBuilder
|
||||
/// </summary>
|
||||
public SaveFileDialogBuilder WithDirectory(string? directory)
|
||||
{
|
||||
_saveFileDialog.Directory = directory;
|
||||
_options.SuggestedStartLocation = directory != null ? _parent.StorageProvider.TryGetFolderFromPathAsync(directory).GetAwaiter().GetResult() : null;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -46,16 +48,7 @@ public class SaveFileDialogBuilder
|
||||
/// </summary>
|
||||
public SaveFileDialogBuilder WithInitialFileName(string? initialFileName)
|
||||
{
|
||||
_saveFileDialog.InitialFileName = initialFileName;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the default extension of the dialog
|
||||
/// </summary>
|
||||
public SaveFileDialogBuilder WithDefaultExtension(string? defaultExtension)
|
||||
{
|
||||
_saveFileDialog.DefaultExtension = defaultExtension;
|
||||
_options.SuggestedFileName = initialFileName;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -67,8 +60,9 @@ public class SaveFileDialogBuilder
|
||||
FileDialogFilterBuilder builder = new();
|
||||
configure(builder);
|
||||
|
||||
_saveFileDialog.Filters ??= new List<FileDialogFilter>();
|
||||
_saveFileDialog.Filters.Add(builder.Build());
|
||||
_fileTypeFilters ??= new List<FilePickerFileType>();
|
||||
_fileTypeFilters.Add(builder.Build());
|
||||
_options.FileTypeChoices = _fileTypeFilters;
|
||||
|
||||
return this;
|
||||
}
|
||||
@ -82,6 +76,7 @@ public class SaveFileDialogBuilder
|
||||
/// </returns>
|
||||
public async Task<string?> ShowAsync()
|
||||
{
|
||||
return await _saveFileDialog.ShowAsync(_parent);
|
||||
IStorageFile? path = await _parent.StorageProvider.SaveFilePickerAsync(_options);
|
||||
return path?.Path.AbsolutePath;
|
||||
}
|
||||
}
|
||||
@ -4,7 +4,7 @@ using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Shared.Services;
|
||||
|
||||
internal class ExceptionDialogView : ReactiveWindow<ExceptionDialogViewModel>
|
||||
internal partial class ExceptionDialogView : ReactiveWindow<ExceptionDialogViewModel>
|
||||
{
|
||||
public ExceptionDialogView()
|
||||
{
|
||||
@ -14,8 +14,4 @@ internal class ExceptionDialogView : ReactiveWindow<ExceptionDialogViewModel>
|
||||
#endif
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
@ -32,15 +32,4 @@
|
||||
<StyleInclude Source="/Styles/Notifications.axaml" />
|
||||
<StyleInclude Source="/Styles/NumberBox.axaml" />
|
||||
<StyleInclude Source="/Styles/TreeView.axaml" />
|
||||
|
||||
<!-- <Style Selector="Window:windows:windows10 /template/ Border#RootBorder"> -->
|
||||
<!-- ~1~ This will show if custom accent color setting is used in Settings page@1@ -->
|
||||
<!-- <Setter Property="BorderBrush" Value="{DynamicResource SystemAccentColor}" /> -->
|
||||
<!-- <Setter Property="BorderThickness" Value="0 1 0 0" /> -->
|
||||
<!-- </Style> -->
|
||||
<!-- -->
|
||||
<!-- <Style Selector="Window[IsActive=False]:windows:windows10 /template/ Border#RootBorder"> -->
|
||||
<!-- <Setter Property="BorderBrush" Value="#3d3d3d" /> -->
|
||||
<!-- <Setter Property="BorderThickness" Value="0 1 0 0" /> -->
|
||||
<!-- </Style> -->
|
||||
</Styles>
|
||||
@ -8,7 +8,7 @@
|
||||
<Border Classes="card" Margin="20">
|
||||
<StackPanel>
|
||||
<TextBlock>I'm in a panel yo!</TextBlock>
|
||||
<Separator Classes="card-separator" />
|
||||
<Border Classes="card-separator" />
|
||||
<TextBlock>I'm in a panel yo!</TextBlock>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
@ -27,14 +27,7 @@
|
||||
<Setter Property="CornerRadius" Value="8 0 0 0" />
|
||||
<Setter Property="ClipToBounds" Value="True" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="Border#TitleBar">
|
||||
<Setter Property="Height" Value="40"></Setter>
|
||||
</Style>
|
||||
<Style Selector="Window:windows Border#TitleBar">
|
||||
<Setter Property="Margin" Value="0 0 138 0"></Setter>
|
||||
</Style>
|
||||
|
||||
|
||||
<Style Selector="Border.card">
|
||||
<Setter Property="Padding" Value="16" />
|
||||
<Setter Property="Background" Value="{DynamicResource ControlFillColorDefaultBrush}" />
|
||||
@ -51,7 +44,7 @@
|
||||
<Setter Property="CornerRadius" Value="{DynamicResource CardCornerRadius}" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="Separator.card-separator">
|
||||
<Style Selector="Border.card-separator">
|
||||
<Setter Property="Background" Value="{DynamicResource ButtonBorderBrush}" />
|
||||
<Setter Property="Margin" Value="-12 15" />
|
||||
<Setter Property="Height" Value="1" />
|
||||
|
||||
@ -2,14 +2,17 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia">
|
||||
<Design.PreviewWith>
|
||||
<Border Padding="20">
|
||||
<Grid Height="24" ColumnDefinitions="*">
|
||||
<Border Padding="20" Width="600" Height="600">
|
||||
<Grid Height="24" ColumnDefinitions="*" Width="200">
|
||||
<TextBox Classes="condensed"
|
||||
Text="#FFFF0000"
|
||||
Padding="2 2 30 2">
|
||||
Padding="2 2 30 2"
|
||||
>
|
||||
</TextBox>
|
||||
<controls:ColorPickerButton Classes="contained-color-picker-button"
|
||||
ShowAcceptDismissButtons="False"/>
|
||||
Color="Red"
|
||||
ShowAcceptDismissButtons="False"
|
||||
/>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Design.PreviewWith>
|
||||
@ -25,27 +28,25 @@
|
||||
<Setter Property="HorizontalAlignment" Value="Right" />
|
||||
<Setter Property="Cursor" Value="Hand" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate>
|
||||
<controls:Button Name="MainButton"
|
||||
Padding="0"
|
||||
BorderThickness="0"
|
||||
CornerRadius="{TemplateBinding CornerRadius}">
|
||||
<ControlTemplate>
|
||||
<Button Name="ShowFlyoutButton"
|
||||
CornerRadius="{TemplateBinding CornerRadius}"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
Padding="0">
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<Border BorderBrush="{DynamicResource ColorPickerButtonOutline}"
|
||||
BorderThickness="1"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
|
||||
MinWidth="18" MinHeight="18"
|
||||
Background="{TemplateBinding Color, Converter={StaticResource ColorBrushConv}}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}">
|
||||
<controls:ColorPicker Name="ColorPicker" IsVisible="False" />
|
||||
</Border>
|
||||
</controls:Button>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
CornerRadius="{TemplateBinding CornerRadius}"/>
|
||||
</Grid>
|
||||
</Button>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style Selector="controls|ColorPickerButton.contained-color-picker-button /template/ Viewbox">
|
||||
<Setter Property="IsVisible" Value="False"></Setter>
|
||||
</Style>
|
||||
</Styles>
|
||||
</Styles>
|
||||
@ -102,7 +102,7 @@
|
||||
<Setter Property="MinHeight" Value="24" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="dataModelPicker|DataModelPickerButton.condensed /template/ controls|Button">
|
||||
<Style Selector="dataModelPicker|DataModelPickerButton.condensed /template/ Button">
|
||||
<Setter Property="Padding" Value="6 3 11 3" />
|
||||
<Setter Property="Height" Value="24" />
|
||||
</Style>
|
||||
|
||||
@ -27,20 +27,20 @@
|
||||
<Setter Property="CornerRadius" Value="{DynamicResource ControlCornerRadius}" />
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
<controls:Button Name="MainButton"
|
||||
CornerRadius="{TemplateBinding CornerRadius}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
VerticalAlignment="{TemplateBinding VerticalAlignment}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
|
||||
Width="{TemplateBinding Width}"
|
||||
Height="{TemplateBinding Height}"
|
||||
HorizontalContentAlignment="Stretch">
|
||||
<Button Name="MainButton"
|
||||
CornerRadius="{TemplateBinding CornerRadius}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
VerticalAlignment="{TemplateBinding VerticalAlignment}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
|
||||
Width="{TemplateBinding Width}"
|
||||
Height="{TemplateBinding Height}"
|
||||
HorizontalContentAlignment="Stretch">
|
||||
<Grid ColumnDefinitions="*,Auto" HorizontalAlignment="Stretch">
|
||||
<TextBlock Name="MainButtonLabel"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
TextAlignment="Left"
|
||||
TextTrimming="CharacterEllipsis"/>
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
<TextBlock Name="ChevronTextBlock"
|
||||
Grid.Column="1"
|
||||
FontFamily="{DynamicResource SymbolThemeFontFamily}"
|
||||
@ -51,7 +51,7 @@
|
||||
Padding="2 2 2 0"
|
||||
Margin="5 0" />
|
||||
</Grid>
|
||||
</controls:Button>
|
||||
</Button>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
@ -168,14 +168,14 @@
|
||||
BorderBrush="{DynamicResource ButtonBorderBrush}"
|
||||
BorderThickness="0 0 1 0"
|
||||
Padding="0 0 10 0">
|
||||
<fluent:ColorPicker Name="ColorPicker"
|
||||
ColorTextType="HexAlpha"
|
||||
UseColorWheel="True"
|
||||
UseColorTriangle="True"
|
||||
IsMoreButtonVisible="True"
|
||||
IsVisible="{TemplateBinding SelectedColorStop, Converter={x:Static ObjectConverters.IsNotNull}}"
|
||||
IsCompact="{Binding IsCompact, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
Color="{Binding SelectedColorStop.Color, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource SKColorToColorConverter}}" />
|
||||
<fluent:FAColorPicker Name="ColorPicker"
|
||||
ColorTextType="HexAlpha"
|
||||
UseColorWheel="True"
|
||||
UseColorTriangle="True"
|
||||
IsMoreButtonVisible="True"
|
||||
IsVisible="{TemplateBinding SelectedColorStop, Converter={x:Static ObjectConverters.IsNotNull}}"
|
||||
IsCompact="{Binding IsCompact, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
Color="{Binding SelectedColorStop.Color, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource SKColorToColorConverter}}" />
|
||||
</Border>
|
||||
|
||||
<Grid Grid.Row="2" Grid.Column="1" RowDefinitions="*,Auto">
|
||||
|
||||
@ -1,15 +1,13 @@
|
||||
<Styles xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:gradientPicker="clr-namespace:Artemis.UI.Shared.Controls.GradientPicker">
|
||||
<Design.PreviewWith>
|
||||
<Border Padding="20" Width="200">
|
||||
<StackPanel Spacing="5">
|
||||
<gradientPicker:GradientPickerButton IsCompact="True" />
|
||||
<ComboBox HorizontalAlignment="Stretch"></ComboBox>
|
||||
<controls:DropDownButton HorizontalAlignment="Stretch"></controls:DropDownButton>
|
||||
<DropDownButton HorizontalAlignment="Stretch"></DropDownButton>
|
||||
</StackPanel>
|
||||
|
||||
</Border>
|
||||
</Design.PreviewWith>
|
||||
|
||||
@ -31,14 +29,14 @@
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
|
||||
<controls:Button
|
||||
<Button
|
||||
Name="MainButton"
|
||||
Padding="0 0 12 0"
|
||||
CornerRadius="{TemplateBinding CornerRadius}"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch">
|
||||
Padding="0 0 12 0"
|
||||
CornerRadius="{TemplateBinding CornerRadius}"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch">
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<Border Classes="gradient-display"
|
||||
HorizontalAlignment="Stretch"
|
||||
@ -54,7 +52,7 @@
|
||||
Margin="4,0,0,0" />
|
||||
|
||||
</Grid>
|
||||
</controls:Button>
|
||||
</Button>
|
||||
|
||||
|
||||
</ControlTemplate>
|
||||
|
||||
@ -35,22 +35,20 @@
|
||||
<ControlTemplate>
|
||||
<DataValidationErrors>
|
||||
<Panel>
|
||||
<!-- This is flipped (scaleY(-1)) for the elevation brush effect
|
||||
-->
|
||||
<Border
|
||||
Name="PART_BorderElement"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
MinWidth="{TemplateBinding MinWidth}"
|
||||
MinHeight="{TemplateBinding MinHeight}"
|
||||
RenderTransform="scaleY(-1)"
|
||||
CornerRadius="{TemplateBinding CornerRadius}" />
|
||||
<Border Name="PART_BorderElement"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}"
|
||||
MinWidth="{TemplateBinding MinWidth}"
|
||||
MinHeight="{TemplateBinding MinHeight}">
|
||||
</Border>
|
||||
|
||||
<Border
|
||||
Margin="{TemplateBinding BorderThickness}">
|
||||
<Grid ColumnDefinitions="Auto,*,Auto">
|
||||
<ContentPresenter Grid.Column="0" Content="{TemplateBinding InnerLeftContent}" />
|
||||
<Border Margin="{TemplateBinding BorderThickness}">
|
||||
<Grid ColumnDefinitions="Auto,*,Auto" >
|
||||
<ContentPresenter Grid.Column="0"
|
||||
Grid.ColumnSpan="1"
|
||||
Content="{TemplateBinding InnerLeftContent}"/>
|
||||
<Grid x:Name="PART_InnerGrid"
|
||||
Grid.Column="1"
|
||||
RowDefinitions="Auto,Auto"
|
||||
@ -62,6 +60,7 @@
|
||||
Name="PART_FloatingWatermark"
|
||||
Foreground="{DynamicResource SystemAccentColor}"
|
||||
FontSize="{TemplateBinding FontSize}"
|
||||
IsVisible="False"
|
||||
Text="{TemplateBinding Watermark}" />
|
||||
|
||||
<TextBlock Grid.Row="1"
|
||||
@ -75,30 +74,33 @@
|
||||
<ScrollViewer Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
|
||||
VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}">
|
||||
VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}"
|
||||
IsScrollChainingEnabled="{TemplateBinding (ScrollViewer.IsScrollChainingEnabled)}"
|
||||
AllowAutoHide="{TemplateBinding (ScrollViewer.AllowAutoHide)}">
|
||||
<Panel>
|
||||
<TextBlock Name="PART_Watermark"
|
||||
Foreground="{DynamicResource TextControlPlaceholderForeground}"
|
||||
Text="{TemplateBinding Watermark}"
|
||||
TextAlignment="{TemplateBinding TextAlignment}"
|
||||
TextWrapping="{TemplateBinding TextWrapping}"
|
||||
IsVisible="{TemplateBinding Text, Converter={x:Static StringConverters.IsNullOrEmpty}}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
IsHitTestVisible="False" />
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
|
||||
<TextPresenter Name="PART_TextPresenter"
|
||||
Text="{TemplateBinding Text, Mode=TwoWay}"
|
||||
CaretIndex="{TemplateBinding CaretIndex}"
|
||||
SelectionStart="{TemplateBinding SelectionStart}"
|
||||
SelectionEnd="{TemplateBinding SelectionEnd}"
|
||||
TextAlignment="{TemplateBinding TextAlignment}"
|
||||
TextWrapping="{TemplateBinding TextWrapping}"
|
||||
PasswordChar="{TemplateBinding PasswordChar}"
|
||||
RevealPassword="{TemplateBinding RevealPassword}"
|
||||
SelectionBrush="{TemplateBinding SelectionBrush}"
|
||||
SelectionForegroundBrush="{TemplateBinding SelectionForegroundBrush}"
|
||||
CaretBrush="{TemplateBinding CaretBrush}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
|
||||
Text="{TemplateBinding Text, Mode=TwoWay}"
|
||||
CaretIndex="{TemplateBinding CaretIndex}"
|
||||
SelectionStart="{TemplateBinding SelectionStart}"
|
||||
SelectionEnd="{TemplateBinding SelectionEnd}"
|
||||
TextAlignment="{TemplateBinding TextAlignment}"
|
||||
TextWrapping="{TemplateBinding TextWrapping}"
|
||||
LineHeight="{TemplateBinding LineHeight}"
|
||||
PasswordChar="{TemplateBinding PasswordChar}"
|
||||
RevealPassword="{TemplateBinding RevealPassword}"
|
||||
SelectionBrush="{TemplateBinding SelectionBrush}"
|
||||
SelectionForegroundBrush="{TemplateBinding SelectionForegroundBrush}"
|
||||
CaretBrush="{TemplateBinding CaretBrush}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
|
||||
</Panel>
|
||||
</ScrollViewer>
|
||||
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
<Styles xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Design.PreviewWith>
|
||||
<Design.PreviewWith>
|
||||
<Border Padding="30" MinWidth="350" Height="600">
|
||||
<TreeView Classes="no-right-margin">
|
||||
<TreeViewItem>
|
||||
<TreeViewItem.Header>
|
||||
<Border BorderThickness="2" BorderBrush="Red">
|
||||
<TextBlock>Test</TextBlock>
|
||||
</Border>
|
||||
</Border>
|
||||
|
||||
</TreeViewItem.Header>
|
||||
</TreeViewItem.Header>
|
||||
</TreeViewItem>
|
||||
<TreeViewItem Header="Item 2" IsExpanded="True">
|
||||
<TreeViewItem>
|
||||
@ -21,7 +21,7 @@
|
||||
</TreeViewItem>
|
||||
<TreeViewItem Header="SubItem2" />
|
||||
<TreeViewItem Header="SubItem3" IsExpanded="True">
|
||||
<TreeViewItem>
|
||||
<TreeViewItem>
|
||||
<TreeViewItem.Header>
|
||||
<Border BorderThickness="2" BorderBrush="Red">
|
||||
<TextBlock>Test</TextBlock>
|
||||
@ -32,17 +32,17 @@
|
||||
<TreeViewItem Header="SubItem Item3" />
|
||||
</TreeViewItem>
|
||||
</TreeViewItem>
|
||||
<TreeViewItem Header="Item3" IsEnabled="False"/>
|
||||
<TreeViewItem Header="Item3" IsEnabled="False" />
|
||||
<TreeViewItem Header="Item4" />
|
||||
</TreeView>
|
||||
</Border>
|
||||
</Design.PreviewWith>
|
||||
|
||||
<Style Selector="TreeView.no-right-margin TreeViewItem">
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
<StackPanel>
|
||||
<Border Name="PART_LayoutRoot"
|
||||
<Style Selector="TreeView.no-right-margin TreeViewItem">
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
<StackPanel>
|
||||
<Border Name="PART_LayoutRoot"
|
||||
Classes="TreeViewItemLayoutRoot"
|
||||
Focusable="True"
|
||||
Background="{TemplateBinding Background}"
|
||||
@ -52,56 +52,59 @@
|
||||
CornerRadius="{TemplateBinding CornerRadius}"
|
||||
TemplatedControl.IsTemplateFocusTarget="True"
|
||||
Margin="2 2 0 2">
|
||||
<Panel>
|
||||
<Border Name="SelectionIndicator"
|
||||
Width="3"
|
||||
Height="16"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
CornerRadius="2" />
|
||||
|
||||
<Grid Name="PART_Header"
|
||||
ColumnDefinitions="Auto, *"
|
||||
Margin="{TemplateBinding Level, Mode=OneWay, Converter={StaticResource TreeViewItemLeftMarginConverter}}">
|
||||
<Panel Name="PART_ExpandCollapseChevronContainer"
|
||||
Margin="8 0 4 0">
|
||||
<ToggleButton Name="PART_ExpandCollapseChevron"
|
||||
Classes="ExpandCollapseChevron"
|
||||
<Panel>
|
||||
<Rectangle Name="SelectionIndicator"
|
||||
Width="3"
|
||||
Height="16"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
RadiusX="2"
|
||||
RadiusY="2"
|
||||
IsVisible="False"
|
||||
Fill="{DynamicResource TreeViewItemSelectionIndicatorForeground}" />
|
||||
|
||||
<Grid Name="PART_Header"
|
||||
ColumnDefinitions="Auto, *"
|
||||
Margin="{TemplateBinding Level, Mode=OneWay, Converter={StaticResource TreeViewItemLeftMarginConverter}}">
|
||||
<Panel Name="PART_ExpandCollapseChevronContainer"
|
||||
Margin="{StaticResource TreeViewItemExpandCollapseChevronMargin}">
|
||||
<ToggleButton Name="PART_ExpandCollapseChevron"
|
||||
Theme="{StaticResource TreeViewChevronButton}"
|
||||
Focusable="False"
|
||||
IsChecked="{TemplateBinding IsExpanded, Mode=TwoWay}" />
|
||||
</Panel>
|
||||
<ContentPresenter Name="PART_HeaderPresenter"
|
||||
</Panel>
|
||||
<ContentPresenter Name="PART_HeaderPresenter"
|
||||
Grid.Column="1"
|
||||
Focusable="False"
|
||||
Content="{TemplateBinding Header}"
|
||||
ContentTemplate="{TemplateBinding HeaderTemplate}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalAlignment}"
|
||||
Margin="{TemplateBinding Padding}" />
|
||||
</Grid>
|
||||
</Panel>
|
||||
</Border>
|
||||
<ItemsPresenter Name="PART_ItemsPresenter"
|
||||
</Grid>
|
||||
</Panel>
|
||||
</Border>
|
||||
<ItemsPresenter Name="PART_ItemsPresenter"
|
||||
IsVisible="{TemplateBinding IsExpanded}"
|
||||
Items="{TemplateBinding Items}"
|
||||
ItemsPanel="{TemplateBinding ItemsPanel}"
|
||||
Margin="2 2 0 2"/>
|
||||
</StackPanel>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
</Style>
|
||||
</StackPanel>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style Selector="Border#TreeViewItemLayoutRoot Panel.PART_ExpandCollapseChevronContainer">
|
||||
<Style Selector="Border#TreeViewItemLayoutRoot Panel.PART_ExpandCollapseChevronContainer">
|
||||
<Setter Property="Margin" Value="0"></Setter>
|
||||
</Style>
|
||||
|
||||
<Style Selector="TreeView TreeViewItem[IsEnabled=False]">
|
||||
<Setter Property="Foreground" Value="{DynamicResource ButtonDisabledForegroundThemeBrush}"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource ButtonDisabledForegroundThemeBrush}" />
|
||||
</Style>
|
||||
|
||||
<!-- <Style Selector="TreeView.no-right-margin TreeViewItem /template/ Border#TreeViewItemLayoutRoot"> -->
|
||||
<!-- <Setter Property="Margin" Value="2 2 0 2"/> -->
|
||||
<!-- </Style> -->
|
||||
<!-- <Style Selector="TreeView.no-right-margin TreeViewItem /template/ StackPanel ItemsPresenter#PART_ItemsPresenter"> -->
|
||||
<!-- <Setter Property="Margin" Value="2 2 0 2"/> -->
|
||||
<!-- </Style> -->
|
||||
</Styles>
|
||||
<!-- <Style Selector="TreeView.no-right-margin TreeViewItem /template/ Border#TreeViewItemLayoutRoot"> -->
|
||||
<!-- <Setter Property="Margin" Value="2 2 0 2"/> -->
|
||||
<!-- </Style> -->
|
||||
<!-- <Style Selector="TreeView.no-right-margin TreeViewItem /template/ StackPanel ItemsPresenter#PART_ItemsPresenter"> -->
|
||||
<!-- <Setter Property="Margin" Value="2 2 0 2"/> -->
|
||||
<!-- </Style> -->
|
||||
</Styles>
|
||||
8
src/Artemis.UI.Shared/Utilities.cs
Normal file
8
src/Artemis.UI.Shared/Utilities.cs
Normal file
@ -0,0 +1,8 @@
|
||||
using DryIoc;
|
||||
|
||||
namespace Artemis.UI.Shared;
|
||||
|
||||
internal static class UI
|
||||
{
|
||||
public static IContainer Locator { get; set; } = null!;
|
||||
}
|
||||
@ -13,7 +13,7 @@ using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Threading;
|
||||
using Avalonia.ReactiveUI;
|
||||
using DryIoc;
|
||||
using ReactiveUI;
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net6.0-windows10.0.17763.0</TargetFramework>
|
||||
<TargetFramework>net7.0-windows10.0.17763.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<Platforms>x64</Platforms>
|
||||
<OutputPath>bin</OutputPath>
|
||||
@ -21,18 +21,18 @@
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="0.10.18" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="0.10.18" />
|
||||
<PackageReference Include="Avalonia" Version="11.0.0-preview6" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="11.0.0-preview6" />
|
||||
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="0.10.18" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.18" />
|
||||
<PackageReference Include="Avalonia.Win32" Version="0.10.18" />
|
||||
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.2" />
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.0-preview6" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.0-preview6" />
|
||||
<PackageReference Include="Avalonia.Win32" Version="11.0.0-preview6" />
|
||||
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.3" />
|
||||
<PackageReference Include="Microsoft.Win32" Version="2.0.1" />
|
||||
<PackageReference Include="Microsoft.Windows.Compatibility" Version="6.0.0" />
|
||||
<PackageReference Include="RawInput.Sharp" Version="0.0.4" />
|
||||
<PackageReference Include="ReactiveUI" Version="17.1.50" />
|
||||
<PackageReference Include="SkiaSharp.Vulkan.SharpVk" Version="2.88.1-preview.108" />
|
||||
<PackageReference Include="Microsoft.Windows.Compatibility" Version="7.0.0" />
|
||||
<PackageReference Include="RawInput.Sharp" Version="0.1.1" />
|
||||
<PackageReference Include="ReactiveUI" Version="18.4.26" />
|
||||
<PackageReference Include="SkiaSharp.Vulkan.SharpVk" Version="2.88.3" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj" />
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
"profiles": {
|
||||
"Artemis.UI.Windows": {
|
||||
"commandName": "Project",
|
||||
"commandLineArgs": "--force-elevation --disable-forced-shutdown --pcmr"
|
||||
"commandLineArgs": "--disable-forced-shutdown --pcmr"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
using System;
|
||||
using Avalonia.Win32;
|
||||
|
||||
namespace Artemis.UI.Windows.Providers.Input;
|
||||
|
||||
public class SpongeWindow : WindowImpl
|
||||
{
|
||||
public event EventHandler<SpongeWindowEventArgs>? WndProcCalled;
|
||||
|
||||
#region Overrides of WindowImpl
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
|
||||
{
|
||||
OnWndProcCalled(new SpongeWindowEventArgs(hWnd, msg, wParam, lParam));
|
||||
return base.WndProc(hWnd, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
protected virtual void OnWndProcCalled(SpongeWindowEventArgs e)
|
||||
{
|
||||
WndProcCalled?.Invoke(this, e);
|
||||
}
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.UI.Windows.Providers.Input;
|
||||
|
||||
public class SpongeWindowEventArgs : EventArgs
|
||||
{
|
||||
public SpongeWindowEventArgs(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
|
||||
{
|
||||
HWnd = hWnd;
|
||||
Msg = msg;
|
||||
WParam = wParam;
|
||||
LParam = lParam;
|
||||
}
|
||||
|
||||
public IntPtr HWnd { get; }
|
||||
public uint Msg { get; }
|
||||
public IntPtr WParam { get; }
|
||||
public IntPtr LParam { get; }
|
||||
}
|
||||
@ -5,6 +5,8 @@ using System.Timers;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Windows.Utilities;
|
||||
using Avalonia.Controls.Platform;
|
||||
using Avalonia.Platform;
|
||||
using Linearstar.Windows.RawInput;
|
||||
using Linearstar.Windows.RawInput.Native;
|
||||
using Serilog;
|
||||
@ -13,28 +15,43 @@ namespace Artemis.UI.Windows.Providers.Input;
|
||||
|
||||
public class WindowsInputProvider : InputProvider
|
||||
{
|
||||
private const int GWL_WNDPROC = -4;
|
||||
private const int WM_INPUT = 0x00FF;
|
||||
|
||||
private readonly IWindowImpl _window;
|
||||
private readonly nint _hWndProcHook;
|
||||
private readonly WndProc? _fnWndProcHook;
|
||||
private readonly IInputService _inputService;
|
||||
private readonly ILogger _logger;
|
||||
private readonly SpongeWindow _sponge;
|
||||
private readonly Timer _taskManagerTimer;
|
||||
|
||||
private int _lastProcessId;
|
||||
delegate nint WndProc(nint hWnd, uint msg, nint wParam, nint lParam);
|
||||
|
||||
private nint CustomWndProc(nint hWnd, uint msg, nint wParam, nint lParam)
|
||||
{
|
||||
OnWndProcCalled(hWnd, msg, wParam, lParam);
|
||||
return CallWindowProc(_hWndProcHook, hWnd, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
public WindowsInputProvider(ILogger logger, IInputService inputService)
|
||||
{
|
||||
_logger = logger;
|
||||
_inputService = inputService;
|
||||
|
||||
_sponge = new SpongeWindow();
|
||||
_sponge.WndProcCalled += SpongeOnWndProcCalled;
|
||||
|
||||
_taskManagerTimer = new Timer(500);
|
||||
_taskManagerTimer.Elapsed += TaskManagerTimerOnElapsed;
|
||||
_taskManagerTimer.Start();
|
||||
|
||||
RawInputDevice.RegisterDevice(HidUsageAndPage.Keyboard, RawInputDeviceFlags.InputSink, _sponge.Handle.Handle);
|
||||
RawInputDevice.RegisterDevice(HidUsageAndPage.Mouse, RawInputDeviceFlags.InputSink, _sponge.Handle.Handle);
|
||||
_window = PlatformManager.CreateWindow();
|
||||
|
||||
_hWndProcHook = GetWindowLongPtr(_window.Handle.Handle, GWL_WNDPROC);
|
||||
_fnWndProcHook = CustomWndProc;
|
||||
nint newLong = Marshal.GetFunctionPointerForDelegate(_fnWndProcHook);
|
||||
SetWindowLongPtr(_window.Handle.Handle, GWL_WNDPROC, newLong);
|
||||
|
||||
RawInputDevice.RegisterDevice(HidUsageAndPage.Keyboard, RawInputDeviceFlags.InputSink, _window.Handle.Handle);
|
||||
RawInputDevice.RegisterDevice(HidUsageAndPage.Mouse, RawInputDeviceFlags.InputSink, _window.Handle.Handle);
|
||||
}
|
||||
|
||||
public static Guid Id { get; } = new("6737b204-ffb1-4cd9-8776-9fb851db303a");
|
||||
@ -55,19 +72,18 @@ public class WindowsInputProvider : InputProvider
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_sponge.Dispose();
|
||||
_taskManagerTimer.Dispose();
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private void SpongeOnWndProcCalled(object? sender, SpongeWindowEventArgs message)
|
||||
private void OnWndProcCalled(nint hWnd, uint msg, nint wParam, nint lParam)
|
||||
{
|
||||
if (message.Msg != WM_INPUT)
|
||||
if (msg != WM_INPUT)
|
||||
return;
|
||||
|
||||
RawInputData data = RawInputData.FromHandle(message.LParam);
|
||||
RawInputData data = RawInputData.FromHandle(lParam);
|
||||
switch (data)
|
||||
{
|
||||
case RawInputMouseData mouse:
|
||||
@ -221,6 +237,15 @@ public class WindowsInputProvider : InputProvider
|
||||
|
||||
#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);
|
||||
|
||||
@ -9,7 +9,6 @@ using Artemis.UI.Screens.Settings;
|
||||
using Artemis.UI.Services.Updating;
|
||||
using Artemis.UI.Shared.Services.MainWindow;
|
||||
using Avalonia.Threading;
|
||||
using DryIoc.ImTools;
|
||||
using Microsoft.Toolkit.Uwp.Notifications;
|
||||
using ReactiveUI;
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Avalonia.Win32;
|
||||
using Avalonia.Controls.Platform;
|
||||
using Avalonia.Platform;
|
||||
using SharpVk;
|
||||
using SharpVk.Khronos;
|
||||
|
||||
@ -10,7 +11,7 @@ internal sealed class Win32VkContext : VkContext
|
||||
{
|
||||
public Win32VkContext()
|
||||
{
|
||||
Window = new WindowImpl();
|
||||
Window = PlatformManager.CreateWindow();
|
||||
Instance = Instance.Create(null, new[] {"VK_KHR_surface", "VK_KHR_win32_surface"});
|
||||
PhysicalDevice = Instance.EnumeratePhysicalDevices().First();
|
||||
Surface = Instance.CreateWin32Surface(Kernel32.CurrentModuleHandle, Window.Handle.Handle);
|
||||
@ -43,7 +44,7 @@ internal sealed class Win32VkContext : VkContext
|
||||
};
|
||||
}
|
||||
|
||||
public WindowImpl Window { get; }
|
||||
public IWindowImpl Window { get; }
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<OutputPath>bin/</OutputPath>
|
||||
<Platforms>x64</Platforms>
|
||||
@ -15,29 +15,28 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="0.10.18" />
|
||||
<PackageReference Include="Avalonia.AvaloniaEdit" Version="0.10.12.2" />
|
||||
<PackageReference Include="Avalonia.Controls.PanAndZoom" Version="10.14.0" />
|
||||
<PackageReference Include="Avalonia.Controls.Skia" Version="0.10.16" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="0.10.18" />
|
||||
<PackageReference Include="Avalonia" Version="11.0.0-preview6" />
|
||||
<PackageReference Include="Avalonia.Controls.PanAndZoom" Version="11.0.0-preview6" />
|
||||
<PackageReference Include="Avalonia.Controls.Skia" Version="11.0.0-preview6" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="11.0.0-preview6" />
|
||||
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="0.10.18" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.18" />
|
||||
<PackageReference Include="Avalonia.Xaml.Behaviors" Version="0.10.18" />
|
||||
<PackageReference Include="DryIoc.dll" Version="5.3.1" />
|
||||
<PackageReference Include="DynamicData" Version="7.9.14" />
|
||||
<PackageReference Include="FluentAvaloniaUI" Version="1.4.1" />
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.0-preview6" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.0-preview6" />
|
||||
<PackageReference Include="Avalonia.Xaml.Behaviors" Version="11.0.0-preview6" />
|
||||
<PackageReference Include="DryIoc.dll" Version="5.3.4" />
|
||||
<PackageReference Include="DynamicData" Version="7.13.1" />
|
||||
<PackageReference Include="FluentAvaloniaUI" Version="2.0.0-preview6" />
|
||||
<PackageReference Include="Flurl.Http" Version="3.2.4" />
|
||||
<PackageReference Include="Live.Avalonia" Version="1.3.1" />
|
||||
<PackageReference Include="Markdown.Avalonia.Tight" Version="0.10.13" />
|
||||
<PackageReference Include="Material.Icons.Avalonia" Version="1.1.10" />
|
||||
<PackageReference Include="Octopus.Octodiff" Version="2.0.100" />
|
||||
<PackageReference Include="ReactiveUI" Version="17.1.50" />
|
||||
<PackageReference Include="ReactiveUI.Validation" Version="2.2.1" />
|
||||
<PackageReference Include="RGB.NET.Core" Version="2.0.0-prerelease.17" />
|
||||
<PackageReference Include="RGB.NET.Layout" Version="2.0.0-prerelease.17" />
|
||||
<PackageReference Include="SkiaSharp" Version="2.88.1-preview.108" />
|
||||
<PackageReference Include="Splat.DryIoc" Version="14.6.1" />
|
||||
<PackageReference Include="Markdown.Avalonia.Tight" Version="11.0.0-b1" />
|
||||
<PackageReference Include="Material.Icons.Avalonia" Version="2.0.0-preview3" />
|
||||
<PackageReference Include="Octopus.Octodiff" Version="2.0.261" />
|
||||
<PackageReference Include="ReactiveUI" Version="18.4.26" />
|
||||
<PackageReference Include="ReactiveUI.Validation" Version="3.1.7" />
|
||||
<PackageReference Include="RGB.NET.Core" Version="2.0.0-prerelease.29" />
|
||||
<PackageReference Include="RGB.NET.Layout" Version="2.0.0-prerelease.29" />
|
||||
<PackageReference Include="SkiaSharp" Version="2.88.3" />
|
||||
<PackageReference Include="Splat.DryIoc" Version="14.6.8" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib"
|
||||
xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=events/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=screens_005Cdebugger_005Ctabs/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=screens_005Cdevice_005Ctabs/@EntryIndexedValue">True</s:Boolean>
|
||||
|
||||
@ -106,7 +106,7 @@ public class SimpleContextDragBehavior : Behavior<Control>
|
||||
PointerPointProperties properties = e.GetCurrentPoint(AssociatedObject).Properties;
|
||||
if (!properties.IsLeftButtonPressed || FocusManager.Instance?.Current is TextBox)
|
||||
return;
|
||||
if (e.Source is not IControl control || AssociatedObject?.DataContext != control.DataContext)
|
||||
if (e.Source is not Control control || AssociatedObject?.DataContext != control.DataContext)
|
||||
return;
|
||||
|
||||
_dragStartPoint = e.GetPosition(null);
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
using System.Collections;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Generators;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
@ -14,7 +13,7 @@ namespace Artemis.UI.Behaviors;
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
public class TreeItemDragBehavior : Behavior<IControl>
|
||||
public class TreeItemDragBehavior : Behavior<Control>
|
||||
{
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
@ -31,7 +30,7 @@ public class TreeItemDragBehavior : Behavior<IControl>
|
||||
public static readonly StyledProperty<double> VerticalDragThresholdProperty =
|
||||
AvaloniaProperty.Register<TreeItemDragBehavior, double>(nameof(VerticalDragThreshold), 3);
|
||||
|
||||
private IControl? _draggedContainer;
|
||||
private Control? _draggedContainer;
|
||||
private int _draggedIndex;
|
||||
private bool _dragStarted;
|
||||
private bool _enableDrag;
|
||||
@ -69,12 +68,12 @@ public class TreeItemDragBehavior : Behavior<IControl>
|
||||
{
|
||||
base.OnAttached();
|
||||
|
||||
if (AssociatedObject is { })
|
||||
if (AssociatedObject is not null)
|
||||
{
|
||||
AssociatedObject.AddHandler(InputElement.PointerReleasedEvent, Released, RoutingStrategies.Tunnel);
|
||||
AssociatedObject.AddHandler(InputElement.PointerPressedEvent, Pressed, RoutingStrategies.Tunnel);
|
||||
AssociatedObject.AddHandler(InputElement.PointerMovedEvent, Moved, RoutingStrategies.Tunnel);
|
||||
AssociatedObject.AddHandler(InputElement.PointerCaptureLostEvent, CaptureLost, RoutingStrategies.Tunnel);
|
||||
AssociatedObject.AddHandler(InputElement.PointerReleasedEvent, ReleasedHandler, RoutingStrategies.Tunnel);
|
||||
AssociatedObject.AddHandler(InputElement.PointerPressedEvent, PressedHandler, RoutingStrategies.Tunnel);
|
||||
AssociatedObject.AddHandler(InputElement.PointerMovedEvent, MovedHandler, RoutingStrategies.Tunnel);
|
||||
AssociatedObject.AddHandler(InputElement.PointerCaptureLostEvent, CaptureLostHandler, RoutingStrategies.Tunnel);
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,16 +83,16 @@ public class TreeItemDragBehavior : Behavior<IControl>
|
||||
{
|
||||
base.OnDetaching();
|
||||
|
||||
if (AssociatedObject is { })
|
||||
if (AssociatedObject is not null)
|
||||
{
|
||||
AssociatedObject.RemoveHandler(InputElement.PointerReleasedEvent, Released);
|
||||
AssociatedObject.RemoveHandler(InputElement.PointerPressedEvent, Pressed);
|
||||
AssociatedObject.RemoveHandler(InputElement.PointerMovedEvent, Moved);
|
||||
AssociatedObject.RemoveHandler(InputElement.PointerCaptureLostEvent, CaptureLost);
|
||||
AssociatedObject.RemoveHandler(InputElement.PointerReleasedEvent, ReleasedHandler);
|
||||
AssociatedObject.RemoveHandler(InputElement.PointerPressedEvent, PressedHandler);
|
||||
AssociatedObject.RemoveHandler(InputElement.PointerMovedEvent, MovedHandler);
|
||||
AssociatedObject.RemoveHandler(InputElement.PointerCaptureLostEvent, CaptureLostHandler);
|
||||
}
|
||||
}
|
||||
|
||||
private void Pressed(object? sender, PointerPressedEventArgs e)
|
||||
private void PressedHandler(object? sender, PointerPressedEventArgs e)
|
||||
{
|
||||
PointerPointProperties properties = e.GetCurrentPoint(AssociatedObject).Properties;
|
||||
if (properties.IsLeftButtonPressed
|
||||
@ -101,20 +100,20 @@ public class TreeItemDragBehavior : Behavior<IControl>
|
||||
{
|
||||
_enableDrag = true;
|
||||
_dragStarted = false;
|
||||
_start = e.GetPosition(AssociatedObject.Parent);
|
||||
_start = e.GetPosition(AssociatedObject.Parent as Visual);
|
||||
_draggedIndex = -1;
|
||||
_targetIndex = -1;
|
||||
_itemsControl = itemsControl;
|
||||
_draggedContainer = AssociatedObject;
|
||||
|
||||
if (_draggedContainer is { })
|
||||
if (_draggedContainer is not null)
|
||||
SetDraggingPseudoClasses(_draggedContainer, true);
|
||||
|
||||
AddTransforms(_itemsControl);
|
||||
}
|
||||
}
|
||||
|
||||
private void Released(object? sender, PointerReleasedEventArgs e)
|
||||
private void ReleasedHandler(object? sender, PointerReleasedEventArgs e)
|
||||
{
|
||||
if (_dragStarted)
|
||||
{
|
||||
@ -125,7 +124,7 @@ public class TreeItemDragBehavior : Behavior<IControl>
|
||||
}
|
||||
}
|
||||
|
||||
private void CaptureLost(object? sender, PointerCaptureLostEventArgs e)
|
||||
private void CaptureLostHandler(object? sender, PointerCaptureLostEventArgs e)
|
||||
{
|
||||
Released();
|
||||
}
|
||||
@ -137,19 +136,23 @@ public class TreeItemDragBehavior : Behavior<IControl>
|
||||
|
||||
RemoveTransforms(_itemsControl);
|
||||
|
||||
if (_itemsControl is { })
|
||||
foreach (ItemContainerInfo? container in _itemsControl.ItemContainerGenerator.Containers)
|
||||
SetDraggingPseudoClasses(container.ContainerControl, true);
|
||||
if (_itemsControl is not null)
|
||||
{
|
||||
foreach (Control realizedContainer in _itemsControl.GetRealizedContainers())
|
||||
SetDraggingPseudoClasses(realizedContainer, true);
|
||||
}
|
||||
|
||||
if (_dragStarted)
|
||||
if (_draggedIndex >= 0 && _targetIndex >= 0 && _draggedIndex != _targetIndex)
|
||||
MoveDraggedItem(_itemsControl, _draggedIndex, _targetIndex);
|
||||
|
||||
if (_itemsControl is { })
|
||||
foreach (ItemContainerInfo? container in _itemsControl.ItemContainerGenerator.Containers)
|
||||
SetDraggingPseudoClasses(container.ContainerControl, false);
|
||||
if (_itemsControl is not null)
|
||||
{
|
||||
foreach (Control realizedContainer in _itemsControl.GetRealizedContainers())
|
||||
SetDraggingPseudoClasses(realizedContainer, false);
|
||||
}
|
||||
|
||||
if (_draggedContainer is { })
|
||||
if (_draggedContainer is not null)
|
||||
SetDraggingPseudoClasses(_draggedContainer, false);
|
||||
|
||||
_draggedIndex = -1;
|
||||
@ -165,17 +168,9 @@ public class TreeItemDragBehavior : Behavior<IControl>
|
||||
{
|
||||
if (itemsControl?.Items is null)
|
||||
return;
|
||||
|
||||
int i = 0;
|
||||
|
||||
foreach (object? _ in itemsControl.Items)
|
||||
{
|
||||
IControl? container = itemsControl.ItemContainerGenerator.ContainerFromIndex(i);
|
||||
if (container is not null)
|
||||
SetTranslateTransform(container, 0, 0);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
foreach (Control container in itemsControl.GetRealizedContainers())
|
||||
SetTranslateTransform(container, 0, 0);
|
||||
}
|
||||
|
||||
private void RemoveTransforms(ItemsControl? itemsControl)
|
||||
@ -183,16 +178,8 @@ public class TreeItemDragBehavior : Behavior<IControl>
|
||||
if (itemsControl?.Items is null)
|
||||
return;
|
||||
|
||||
int i = 0;
|
||||
|
||||
foreach (object? _ in itemsControl.Items)
|
||||
{
|
||||
IControl? container = itemsControl.ItemContainerGenerator.ContainerFromIndex(i);
|
||||
if (container is not null)
|
||||
SetTranslateTransform(container, 0, 0);
|
||||
|
||||
i++;
|
||||
}
|
||||
foreach (Control container in itemsControl.GetRealizedContainers())
|
||||
SetTranslateTransform(container, 0, 0);
|
||||
}
|
||||
|
||||
private void MoveDraggedItem(ItemsControl? itemsControl, int draggedIndex, int targetIndex)
|
||||
@ -208,7 +195,7 @@ public class TreeItemDragBehavior : Behavior<IControl>
|
||||
selectingItemsControl.SelectedIndex = targetIndex;
|
||||
}
|
||||
|
||||
private void Moved(object? sender, PointerEventArgs e)
|
||||
private void MovedHandler(object? sender, PointerEventArgs e)
|
||||
{
|
||||
PointerPointProperties properties = e.GetCurrentPoint(AssociatedObject).Properties;
|
||||
if (properties.IsLeftButtonPressed)
|
||||
@ -249,7 +236,7 @@ public class TreeItemDragBehavior : Behavior<IControl>
|
||||
else
|
||||
SetTranslateTransform(_draggedContainer, 0, delta);
|
||||
|
||||
_draggedIndex = _itemsControl.ItemContainerGenerator.IndexFromContainer(_draggedContainer);
|
||||
_draggedIndex = _itemsControl.IndexFromContainer(_draggedContainer);
|
||||
_targetIndex = -1;
|
||||
|
||||
Rect draggedBounds = _draggedContainer.Bounds;
|
||||
@ -263,17 +250,11 @@ public class TreeItemDragBehavior : Behavior<IControl>
|
||||
double draggedDeltaEnd = orientation == Orientation.Horizontal
|
||||
? draggedBounds.X + delta + draggedBounds.Width
|
||||
: draggedBounds.Y + delta + draggedBounds.Height;
|
||||
|
||||
int i = 0;
|
||||
|
||||
foreach (object? _ in _itemsControl.Items)
|
||||
|
||||
foreach (Control targetContainer in _itemsControl.GetRealizedContainers())
|
||||
{
|
||||
IControl? targetContainer = _itemsControl.ItemContainerGenerator.ContainerFromIndex(i);
|
||||
if (targetContainer?.RenderTransform is null || ReferenceEquals(targetContainer, _draggedContainer))
|
||||
{
|
||||
i++;
|
||||
if (targetContainer.RenderTransform is null || ReferenceEquals(targetContainer, _draggedContainer))
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the target container has children, there are two options
|
||||
// Move into the top of the container
|
||||
@ -286,7 +267,7 @@ public class TreeItemDragBehavior : Behavior<IControl>
|
||||
? targetBounds.X + targetBounds.Width / 2
|
||||
: targetBounds.Y + targetBounds.Height / 2;
|
||||
|
||||
int targetIndex = _itemsControl.ItemContainerGenerator.IndexFromContainer(targetContainer);
|
||||
int targetIndex = _itemsControl.IndexFromContainer(targetContainer);
|
||||
|
||||
if (targetStart > draggedStart && draggedDeltaEnd >= targetMid)
|
||||
{
|
||||
@ -315,22 +296,24 @@ public class TreeItemDragBehavior : Behavior<IControl>
|
||||
else
|
||||
SetTranslateTransform(targetContainer, 0, 0);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetDraggingPseudoClasses(IControl control, bool isDragging)
|
||||
private void SetDraggingPseudoClasses(Control? control, bool isDragging)
|
||||
{
|
||||
if (control == null)
|
||||
return;
|
||||
if (isDragging)
|
||||
((IPseudoClasses) control.Classes).Add(":dragging");
|
||||
else
|
||||
((IPseudoClasses) control.Classes).Remove(":dragging");
|
||||
}
|
||||
|
||||
private void SetTranslateTransform(IControl control, double x, double y)
|
||||
private void SetTranslateTransform(Control? control, double x, double y)
|
||||
{
|
||||
if (control == null)
|
||||
return;
|
||||
TransformOperations.Builder transformBuilder = new(1);
|
||||
transformBuilder.AppendTranslate(x, y);
|
||||
control.RenderTransform = transformBuilder.Build();
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Media;
|
||||
@ -137,47 +138,47 @@ public class TimelineHeader : Control
|
||||
private void RenderLabel(DrawingContext drawingContext, string text, double x)
|
||||
{
|
||||
Typeface typeFace = new(FontFamily);
|
||||
FormattedText formattedText = new(text, typeFace, 9, TextAlignment.Left, TextWrapping.NoWrap, Bounds.Size);
|
||||
FormattedText formattedText = new(text, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, typeFace, 9, Foreground);
|
||||
if (x == 0 && OffsetFirstValue)
|
||||
drawingContext.DrawText(Foreground, new Point(2, 5), formattedText);
|
||||
drawingContext.DrawText(formattedText, new Point(2, 5));
|
||||
else
|
||||
drawingContext.DrawText(Foreground, new Point(x - formattedText.Bounds.Width / 2, 5), formattedText);
|
||||
drawingContext.DrawText(formattedText, new Point(x - formattedText.Width / 2, 5));
|
||||
}
|
||||
|
||||
private void UpdateTimeScale()
|
||||
{
|
||||
object[] subds;
|
||||
double[] subds;
|
||||
if (PixelsPerSecond > 350)
|
||||
subds = new object[] {12d, 12d, 60d};
|
||||
subds = new[] {12d, 12d, 60d};
|
||||
else if (PixelsPerSecond > 250)
|
||||
subds = new object[] {6d, 12d, 60d};
|
||||
subds = new[] {6d, 12d, 60d};
|
||||
else if (PixelsPerSecond > 200)
|
||||
subds = new object[] {6d, 6d, 30d};
|
||||
subds = new[] {6d, 6d, 30d};
|
||||
else if (PixelsPerSecond > 150)
|
||||
subds = new object[] {4d, 4d, 20d};
|
||||
subds = new[] {4d, 4d, 20d};
|
||||
else if (PixelsPerSecond > 140)
|
||||
subds = new object[] {4d, 4d, 20d};
|
||||
subds = new[] {4d, 4d, 20d};
|
||||
else if (PixelsPerSecond > 90)
|
||||
subds = new object[] {2d, 4d, 20d};
|
||||
subds = new[] {2d, 4d, 20d};
|
||||
else if (PixelsPerSecond > 60)
|
||||
subds = new object[] {2d, 4d, 8d};
|
||||
subds = new[] {2d, 4d, 8d};
|
||||
else if (PixelsPerSecond > 40)
|
||||
subds = new object[] {1d, 2d, 10d};
|
||||
subds = new[] {1d, 2d, 10d};
|
||||
else if (PixelsPerSecond > 30)
|
||||
subds = new object[] {1d, 2d, 10d};
|
||||
subds = new[] {1d, 2d, 10d};
|
||||
else if (PixelsPerSecond > 10)
|
||||
subds = new object[] {1d / 2d, 1d / 2d, 1d / 2d};
|
||||
subds = new[] {1d / 2d, 1d / 2d, 1d / 2d};
|
||||
else if (PixelsPerSecond > 4)
|
||||
subds = new object[] {1d / 5d, 1d / 5d, 1d / 5d};
|
||||
subds = new[] {1d / 5d, 1d / 5d, 1d / 5d};
|
||||
else if (PixelsPerSecond > 3)
|
||||
subds = new object[] {1d / 10d, 1d / 10d, 1d / 5d};
|
||||
subds = new[] {1d / 10d, 1d / 10d, 1d / 5d};
|
||||
else if (PixelsPerSecond > 1)
|
||||
subds = new object[] {1d / 20d, 1d / 20d, 1d / 10d};
|
||||
subds = new[] {1d / 20d, 1d / 20d, 1d / 10d};
|
||||
else if (PixelsPerSecond >= 1)
|
||||
subds = new object[] {1d / 30d, 1d / 30d, 1d / 15d};
|
||||
subds = new[] {1d / 30d, 1d / 30d, 1d / 15d};
|
||||
else
|
||||
// 1s per pixel
|
||||
subds = new object[] {1d / 60d, 1d / 60d, 1d / 15d};
|
||||
subds = new[] {1d / 60d, 1d / 60d, 1d / 15d};
|
||||
|
||||
_subd1 = (double) subds[0]; // big ticks / labels
|
||||
_subd2 = (double) subds[1]; // medium ticks
|
||||
|
||||
@ -3,15 +3,11 @@ using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.DefaultTypes.PropertyInput;
|
||||
|
||||
public class BoolPropertyInputView : ReactiveUserControl<BoolPropertyInputViewModel>
|
||||
public partial class BoolPropertyInputView : ReactiveUserControl<BoolPropertyInputViewModel>
|
||||
{
|
||||
public BoolPropertyInputView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
@ -3,15 +3,11 @@ using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.DefaultTypes.PropertyInput;
|
||||
|
||||
public class BrushPropertyInputView : ReactiveUserControl<BrushPropertyInputViewModel>
|
||||
public partial class BrushPropertyInputView : ReactiveUserControl<BrushPropertyInputViewModel>
|
||||
{
|
||||
public BrushPropertyInputView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.LayerBrushes;
|
||||
@ -11,7 +12,6 @@ using Artemis.UI.Shared.Services.Builders;
|
||||
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||
using Artemis.UI.Shared.Services.ProfileEditor.Commands;
|
||||
using Artemis.UI.Shared.Services.PropertyInput;
|
||||
using Avalonia.Controls.Mixins;
|
||||
using Avalonia.Threading;
|
||||
using ReactiveUI;
|
||||
|
||||
|
||||
@ -5,17 +5,13 @@ using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.DefaultTypes.PropertyInput;
|
||||
|
||||
public class ColorGradientPropertyInputView : ReactiveUserControl<ColorGradientPropertyInputViewModel>
|
||||
public partial class ColorGradientPropertyInputView : ReactiveUserControl<ColorGradientPropertyInputViewModel>
|
||||
{
|
||||
public ColorGradientPropertyInputView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
private void GradientPickerButton_OnFlyoutOpened(GradientPickerButton sender, EventArgs args)
|
||||
{
|
||||
|
||||
@ -4,15 +4,11 @@ using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.DefaultTypes.PropertyInput;
|
||||
|
||||
public class EnumPropertyInputView : ReactiveUserControl<PropertyInputViewModel>
|
||||
public partial class EnumPropertyInputView : ReactiveUserControl<PropertyInputViewModel>
|
||||
{
|
||||
public EnumPropertyInputView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
@ -5,17 +5,13 @@ using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.DefaultTypes.PropertyInput;
|
||||
|
||||
public class FloatPropertyInputView : ReactiveUserControl<FloatPropertyInputViewModel>
|
||||
public partial class FloatPropertyInputView : ReactiveUserControl<FloatPropertyInputViewModel>
|
||||
{
|
||||
public FloatPropertyInputView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
private void DraggableNumberBox_OnDragStarted(DraggableNumberBox sender, EventArgs args)
|
||||
{
|
||||
|
||||
@ -5,17 +5,13 @@ using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.DefaultTypes.PropertyInput;
|
||||
|
||||
public class FloatRangePropertyInputView : ReactiveUserControl<FloatRangePropertyInputViewModel>
|
||||
public partial class FloatRangePropertyInputView : ReactiveUserControl<FloatRangePropertyInputViewModel>
|
||||
{
|
||||
public FloatRangePropertyInputView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
private void DraggableNumberBox_OnDragStarted(DraggableNumberBox sender, EventArgs args)
|
||||
{
|
||||
|
||||
@ -5,17 +5,13 @@ using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.DefaultTypes.PropertyInput;
|
||||
|
||||
public class IntPropertyInputView : ReactiveUserControl<IntPropertyInputViewModel>
|
||||
public partial class IntPropertyInputView : ReactiveUserControl<IntPropertyInputViewModel>
|
||||
{
|
||||
public IntPropertyInputView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
private void DraggableNumberBox_OnDragStarted(DraggableNumberBox sender, EventArgs args)
|
||||
{
|
||||
|
||||
@ -5,17 +5,13 @@ using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.DefaultTypes.PropertyInput;
|
||||
|
||||
public class IntRangePropertyInputView : ReactiveUserControl<IntRangePropertyInputViewModel>
|
||||
public partial class IntRangePropertyInputView : ReactiveUserControl<IntRangePropertyInputViewModel>
|
||||
{
|
||||
public IntRangePropertyInputView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
private void DraggableNumberBox_OnDragStarted(DraggableNumberBox sender, EventArgs args)
|
||||
{
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:propertyInput="clr-namespace:Artemis.UI.DefaultTypes.PropertyInput"
|
||||
xmlns:shared="clr-namespace:Artemis.UI.Shared.Converters;assembly=Artemis.UI.Shared"
|
||||
xmlns:behaviors="clr-namespace:Artemis.UI.Shared.Behaviors;assembly=Artemis.UI.Shared"
|
||||
mc:Ignorable="d" d:DesignWidth="200" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.DefaultTypes.PropertyInput.SKColorPropertyInputView"
|
||||
x:DataType="propertyInput:SKColorPropertyInputViewModel">
|
||||
@ -13,13 +14,15 @@
|
||||
<shared:SKColorToColorConverter x:Key="SKColorToColorConverter" />
|
||||
</UserControl.Resources>
|
||||
<Grid Height="24" ColumnDefinitions="*">
|
||||
<TextBox Classes="condensed"
|
||||
Text="{CompiledBinding InputValue, Converter={StaticResource SKColorToStringConverter}}"
|
||||
Padding="2 2 30 2">
|
||||
<TextBox Classes="condensed" Padding="2 2 30 2">
|
||||
<Interaction.Behaviors>
|
||||
<behaviors:LostFocusTextBoxBindingBehavior Text="{CompiledBinding InputValue, Converter={StaticResource SKColorToStringConverter}}" />
|
||||
</Interaction.Behaviors>
|
||||
</TextBox>
|
||||
<controls:ColorPickerButton Classes="contained-color-picker-button"
|
||||
Color="{CompiledBinding InputValue, Converter={StaticResource SKColorToColorConverter}}"
|
||||
ShowAcceptDismissButtons="False"
|
||||
FlyoutPlacement="Right"
|
||||
FlyoutOpened="ColorPickerButton_OnFlyoutOpened"
|
||||
FlyoutClosed="ColorPickerButton_OnFlyoutClosed" />
|
||||
</Grid>
|
||||
|
||||
@ -5,17 +5,13 @@ using FluentAvalonia.UI.Controls;
|
||||
|
||||
namespace Artemis.UI.DefaultTypes.PropertyInput;
|
||||
|
||||
public class SKColorPropertyInputView : ReactiveUserControl<SKColorPropertyInputViewModel>
|
||||
public partial class SKColorPropertyInputView : ReactiveUserControl<SKColorPropertyInputViewModel>
|
||||
{
|
||||
public SKColorPropertyInputView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
private void ColorPickerButton_OnFlyoutOpened(ColorPickerButton sender, EventArgs args)
|
||||
{
|
||||
|
||||
@ -5,17 +5,13 @@ using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.DefaultTypes.PropertyInput;
|
||||
|
||||
public class SKPointPropertyInputView : ReactiveUserControl<SKPointPropertyInputViewModel>
|
||||
public partial class SKPointPropertyInputView : ReactiveUserControl<SKPointPropertyInputViewModel>
|
||||
{
|
||||
public SKPointPropertyInputView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
private void DraggableNumberBox_OnDragStarted(DraggableNumberBox sender, EventArgs args)
|
||||
{
|
||||
|
||||
@ -5,17 +5,13 @@ using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.DefaultTypes.PropertyInput;
|
||||
|
||||
public class SKSizePropertyInputView : ReactiveUserControl<SKSizePropertyInputViewModel>
|
||||
public partial class SKSizePropertyInputView : ReactiveUserControl<SKSizePropertyInputViewModel>
|
||||
{
|
||||
public SKSizePropertyInputView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
private void DraggableNumberBox_OnDragStarted(DraggableNumberBox sender, EventArgs args)
|
||||
{
|
||||
|
||||
@ -4,7 +4,7 @@ using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.DefaultTypes.PropertyInput;
|
||||
|
||||
public class StringPropertyInputView : ReactiveUserControl<FloatPropertyInputViewModel>
|
||||
public partial class StringPropertyInputView : ReactiveUserControl<FloatPropertyInputViewModel>
|
||||
{
|
||||
public StringPropertyInputView()
|
||||
{
|
||||
@ -12,10 +12,6 @@ public class StringPropertyInputView : ReactiveUserControl<FloatPropertyInputVie
|
||||
AddHandler(KeyUpEvent, OnRoutedKeyUp, handledEventsToo: true);
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
private void OnRoutedKeyUp(object? sender, KeyEventArgs e)
|
||||
{
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
using System.Reflection;
|
||||
using Artemis.UI.DryIoc.Factories;
|
||||
using Artemis.UI.DryIoc.InstanceProviders;
|
||||
using Artemis.UI.Screens;
|
||||
using Artemis.UI.Screens.VisualScripting;
|
||||
using Artemis.UI.Services.Interfaces;
|
||||
using Artemis.UI.Services.Updating;
|
||||
@ -9,7 +8,6 @@ using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Services.NodeEditor;
|
||||
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||
using Avalonia.Platform;
|
||||
using Avalonia.Shared.PlatformSupport;
|
||||
using DryIoc;
|
||||
|
||||
namespace Artemis.UI.DryIoc;
|
||||
|
||||
@ -1,16 +1,26 @@
|
||||
<controls:CoreWindow xmlns="https://github.com/avaloniaui"
|
||||
<windowing:AppWindow xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:windowing="clr-namespace:FluentAvalonia.UI.Windowing;assembly=FluentAvalonia"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.MainWindow"
|
||||
Icon="/Assets/Images/Logo/application.ico"
|
||||
Title="Artemis 2.0"
|
||||
MinWidth="600"
|
||||
MinHeight="400">
|
||||
<windowing:AppWindow.Styles>
|
||||
<Styles>
|
||||
<Style Selector="Border#TitleBarContainer">
|
||||
<Setter Property="Height" Value="40"></Setter>
|
||||
</Style>
|
||||
<Style Selector="windowing|AppWindow:windows Border#TitleBarContainer">
|
||||
<Setter Property="Margin" Value="0 0 138 0"></Setter>
|
||||
</Style>
|
||||
</Styles>
|
||||
</windowing:AppWindow.Styles>
|
||||
<Panel Name="RootPanel">
|
||||
<Border Name="DragHandle" Background="Transparent" Height="40" HorizontalAlignment="Stretch" VerticalAlignment="Top"/>
|
||||
<DockPanel>
|
||||
<ContentControl Name="SidebarContentControl" Content="{Binding SidebarViewModel}" DockPanel.Dock="Left" Width="240">
|
||||
<ContentControl.Transitions>
|
||||
@ -19,11 +29,11 @@
|
||||
</Transitions>
|
||||
</ContentControl.Transitions>
|
||||
</ContentControl>
|
||||
<Border Name="TitleBar" DockPanel.Dock="Top">
|
||||
<ContentControl Content="{Binding TitleBarViewModel}" />
|
||||
<Border Name="TitleBarContainer" DockPanel.Dock="Top">
|
||||
<ContentControl Content="{Binding TitleBarViewModel}"/>
|
||||
</Border>
|
||||
<ContentControl Content="{Binding}" />
|
||||
</DockPanel>
|
||||
<StackPanel Classes="notification-container" Name="NotificationContainer" VerticalAlignment="Bottom" HorizontalAlignment="Right"/>
|
||||
</Panel>
|
||||
</controls:CoreWindow>
|
||||
</windowing:AppWindow>
|
||||
@ -7,16 +7,13 @@ using Artemis.UI.Shared;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Threading;
|
||||
using FluentAvalonia.Core.ApplicationModel;
|
||||
using Avalonia.ReactiveUI;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI;
|
||||
|
||||
public class MainWindow : ReactiveCoreWindow<RootViewModel>
|
||||
public partial class MainWindow : ReactiveAppWindow<RootViewModel>
|
||||
{
|
||||
private readonly Panel _rootPanel;
|
||||
private readonly ContentControl _sidebarContentControl;
|
||||
private bool _activated;
|
||||
|
||||
public MainWindow()
|
||||
@ -25,12 +22,10 @@ public class MainWindow : ReactiveCoreWindow<RootViewModel>
|
||||
Activated += OnActivated;
|
||||
Deactivated += OnDeactivated;
|
||||
|
||||
ApplyWindowSize();
|
||||
InitializeComponent();
|
||||
|
||||
_rootPanel = this.Get<Panel>("RootPanel");
|
||||
_sidebarContentControl = this.Get<ContentControl>("SidebarContentControl");
|
||||
_rootPanel.LayoutUpdated += OnLayoutUpdated;
|
||||
ApplyWindowSize();
|
||||
|
||||
RootPanel.LayoutUpdated += OnLayoutUpdated;
|
||||
|
||||
#if DEBUG
|
||||
this.AttachDevTools();
|
||||
@ -57,22 +52,16 @@ public class MainWindow : ReactiveCoreWindow<RootViewModel>
|
||||
RootViewModel.WindowSizeSetting.Value ??= new WindowSize();
|
||||
RootViewModel.WindowSizeSetting.Value.ApplyFromWindow(this);
|
||||
}
|
||||
|
||||
// TODO: Replace with a media query once https://github.com/AvaloniaUI/Avalonia/pull/7938 is implemented
|
||||
|
||||
private void OnLayoutUpdated(object? sender, EventArgs e)
|
||||
{
|
||||
_sidebarContentControl.Width = _rootPanel.Bounds.Width >= 1800 ? 300 : 240;
|
||||
SidebarContentControl.Width = RootPanel.Bounds.Width >= 1800 ? 300 : 240;
|
||||
}
|
||||
|
||||
private void OnOpened(object? sender, EventArgs e)
|
||||
{
|
||||
Opened -= OnOpened;
|
||||
ICoreApplicationView coreAppTitleBar = this;
|
||||
if (coreAppTitleBar.TitleBar != null)
|
||||
{
|
||||
coreAppTitleBar.TitleBar.ExtendViewIntoTitleBar = true;
|
||||
SetTitleBar(this.Get<Border>("DragHandle"));
|
||||
}
|
||||
TitleBar.ExtendsContentIntoTitleBar = true;
|
||||
}
|
||||
|
||||
private void OnActivated(object? sender, EventArgs e)
|
||||
@ -85,8 +74,4 @@ public class MainWindow : ReactiveCoreWindow<RootViewModel>
|
||||
ViewModel?.Unfocused();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
<controls:CoreWindow xmlns="https://github.com/avaloniaui"
|
||||
<windowing:AppWindow xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
@ -6,6 +6,7 @@
|
||||
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||
xmlns:debugger="clr-namespace:Artemis.UI.Screens.Debugger"
|
||||
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
||||
xmlns:windowing="clr-namespace:FluentAvalonia.UI.Windowing;assembly=FluentAvalonia"
|
||||
mc:Ignorable="d" d:DesignWidth="1200" d:DesignHeight="800"
|
||||
x:Class="Artemis.UI.Screens.Debugger.DebugView"
|
||||
x:DataType="debugger:DebugViewModel"
|
||||
@ -15,7 +16,6 @@
|
||||
Height="800">
|
||||
|
||||
<Window.Styles>
|
||||
<StyleInclude Source="avares://AvaloniaEdit/AvaloniaEdit.xaml" />
|
||||
<Style Selector="StackPanel.sidebar-stackpanel avalonia|MaterialIcon">
|
||||
<Setter Property="Margin" Value="-7 0 0 0" />
|
||||
</Style>
|
||||
@ -38,4 +38,4 @@
|
||||
|
||||
<StackPanel Grid.Column="0" Grid.ColumnSpan="2" Classes="notification-container" Name="NotificationContainer" VerticalAlignment="Bottom" HorizontalAlignment="Right" />
|
||||
</Grid>
|
||||
</controls:CoreWindow>
|
||||
</windowing:AppWindow>
|
||||
@ -10,7 +10,7 @@ using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Debugger;
|
||||
|
||||
public class DebugView : ReactiveCoreWindow<DebugViewModel>
|
||||
public partial class DebugView : ReactiveAppWindow<DebugViewModel>
|
||||
{
|
||||
public DebugView()
|
||||
{
|
||||
@ -29,10 +29,6 @@ public class DebugView : ReactiveCoreWindow<DebugViewModel>
|
||||
});
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
private void DeviceVisualizer_OnLedClicked(object? sender, LedClickedEventArgs e)
|
||||
{
|
||||
|
||||
@ -3,15 +3,11 @@ using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Debugger.DataModel;
|
||||
|
||||
public class DataModelDebugView : ReactiveUserControl<DataModelDebugViewModel>
|
||||
public partial class DataModelDebugView : ReactiveUserControl<DataModelDebugViewModel>
|
||||
{
|
||||
public DataModelDebugView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user