mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-11 19:36:28 +00:00
Meta - Replace WPF projects with Avalonia projects
This commit is contained in:
1 parent
fcf0376a9a
commit
d7f3ee1190
1110 files changed
+10872
-52240
No files matched your search
@@ -1,454 +0,0 @@
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Mono auto generated files
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Ww][Ii][Nn]32/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
[Ll]ogs/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUnit
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
nunit-*.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# Tye
|
||||
.tye/
|
||||
|
||||
# ASP.NET Scaffolding
|
||||
ScaffoldingReadMe.txt
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Coverlet is a free, cross platform Code Coverage Tool
|
||||
coverage*.json
|
||||
coverage*.xml
|
||||
coverage*.info
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# NuGet Symbol Packages
|
||||
*.snupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
*.appxbundle
|
||||
*.appxupload
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- [Bb]ackup.rdl
|
||||
*- [Bb]ackup ([0-9]).rdl
|
||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
|
||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||
MigrationBackup/
|
||||
|
||||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
.ionide/
|
||||
|
||||
# Fody - auto-generated XML schema
|
||||
FodyWeavers.xsd
|
||||
|
||||
##
|
||||
## Visual studio for Mac
|
||||
##
|
||||
|
||||
|
||||
# globs
|
||||
Makefile.in
|
||||
*.userprefs
|
||||
*.usertasks
|
||||
config.make
|
||||
config.status
|
||||
aclocal.m4
|
||||
install-sh
|
||||
autom4te.cache/
|
||||
*.tar.gz
|
||||
tarballs/
|
||||
test-results/
|
||||
|
||||
# Mac bundle stuff
|
||||
*.dmg
|
||||
*.app
|
||||
|
||||
# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
|
||||
# Windows thumbnail cache files
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
ehthumbs_vista.db
|
||||
|
||||
# Dump file
|
||||
*.stackdump
|
||||
|
||||
# Folder config file
|
||||
[Dd]esktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msix
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
# JetBrains Rider
|
||||
.idea/
|
||||
*.sln.iml
|
||||
|
||||
##
|
||||
## Visual Studio Code
|
||||
##
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
@@ -1,12 +0,0 @@
|
||||
<Application xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:ui="clr-namespace:Artemis.UI;assembly=Artemis.UI"
|
||||
x:Class="Artemis.UI.Linux.App">
|
||||
<Application.DataTemplates>
|
||||
<ui:ViewLocator/>
|
||||
</Application.DataTemplates>
|
||||
|
||||
<Application.Styles>
|
||||
<StyleInclude Source="avares://Artemis.UI/Styles/Artemis.axaml" />
|
||||
</Application.Styles>
|
||||
</Application>
|
||||
@@ -1,39 +0,0 @@
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Linux.Providers.Input;
|
||||
using Avalonia;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Threading;
|
||||
using ReactiveUI;
|
||||
using Ninject;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
|
||||
namespace Artemis.UI.Linux
|
||||
{
|
||||
public class App : Application
|
||||
{
|
||||
private StandardKernel? _kernel;
|
||||
private ApplicationStateManager? _applicationStateManager;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
_kernel = ArtemisBootstrapper.Bootstrap(this);
|
||||
RxApp.MainThreadScheduler = AvaloniaScheduler.Instance;
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
|
||||
RegisterProviders();
|
||||
}
|
||||
|
||||
public override void OnFrameworkInitializationCompleted()
|
||||
{
|
||||
ArtemisBootstrapper.Initialize();
|
||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
_applicationStateManager = new ApplicationStateManager(_kernel!, desktop.Args);
|
||||
}
|
||||
|
||||
private void RegisterProviders()
|
||||
{
|
||||
IInputService inputService = _kernel.Get<IInputService>();
|
||||
inputService.AddInputProvider(_kernel.Get<LinuxInputProvider>());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Shared.Services.Interfaces;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Threading;
|
||||
using Ninject;
|
||||
|
||||
namespace Artemis.UI.Linux
|
||||
{
|
||||
public class ApplicationStateManager
|
||||
{
|
||||
private readonly IWindowService _windowService;
|
||||
|
||||
// ReSharper disable once NotAccessedField.Local - Kept in scope to ensure it does not get released
|
||||
private Mutex? _artemisMutex;
|
||||
|
||||
public ApplicationStateManager(IKernel kernel, string[] startupArguments)
|
||||
{
|
||||
_windowService = kernel.Get<IWindowService>();
|
||||
StartupArguments = startupArguments;
|
||||
|
||||
Core.Utilities.ShutdownRequested += UtilitiesOnShutdownRequested;
|
||||
Core.Utilities.RestartRequested += UtilitiesOnRestartRequested;
|
||||
|
||||
// On OS shutdown dispose the kernel just so device providers get a chance to clean up
|
||||
if (Application.Current?.ApplicationLifetime is IControlledApplicationLifetime controlledApplicationLifetime)
|
||||
{
|
||||
controlledApplicationLifetime.Exit += (_, _) =>
|
||||
{
|
||||
RunForcedShutdownIfEnabled();
|
||||
|
||||
// Dispose plugins before disposing the kernel because plugins might access services during dispose
|
||||
kernel.Get<IPluginManagementService>().Dispose();
|
||||
kernel.Dispose();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public string[] StartupArguments { get; }
|
||||
|
||||
public bool FocusExistingInstance()
|
||||
{
|
||||
_artemisMutex = new Mutex(true, "Artemis-3c24b502-64e6-4587-84bf-9072970e535d", out bool createdNew);
|
||||
if (createdNew)
|
||||
return false;
|
||||
|
||||
return RemoteFocus();
|
||||
}
|
||||
|
||||
public void DisplayException(Exception e)
|
||||
{
|
||||
try
|
||||
{
|
||||
_windowService.ShowExceptionDialog("An unhandled exception occured", e);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored, we tried
|
||||
}
|
||||
}
|
||||
|
||||
private bool RemoteFocus()
|
||||
{
|
||||
// At this point we cannot read the database yet to retrieve the web server port.
|
||||
// Instead use the method external applications should use as well.
|
||||
if (!File.Exists(Path.Combine(Constants.DataFolder, "webserver.txt")))
|
||||
{
|
||||
KillOtherInstances();
|
||||
return false;
|
||||
}
|
||||
|
||||
string url = File.ReadAllText(Path.Combine(Constants.DataFolder, "webserver.txt"));
|
||||
using HttpClient client = new();
|
||||
try
|
||||
{
|
||||
CancellationTokenSource cts = new();
|
||||
cts.CancelAfter(2000);
|
||||
|
||||
HttpResponseMessage httpResponseMessage = client.Send(new HttpRequestMessage(HttpMethod.Post, url + "remote/bring-to-foreground"), cts.Token);
|
||||
httpResponseMessage.EnsureSuccessStatusCode();
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
KillOtherInstances();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void KillOtherInstances()
|
||||
{
|
||||
// Kill everything else heh
|
||||
List<Process> processes = Process.GetProcessesByName("Artemis.UI").Where(p => p.Id != Process.GetCurrentProcess().Id).ToList();
|
||||
foreach (Process process in processes)
|
||||
{
|
||||
try
|
||||
{
|
||||
process.Kill(true);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UtilitiesOnRestartRequested(object? sender, RestartEventArgs e)
|
||||
{
|
||||
List<string> argsList = new();
|
||||
argsList.AddRange(StartupArguments);
|
||||
if (e.ExtraArgs != null)
|
||||
argsList.AddRange(e.ExtraArgs.Except(argsList));
|
||||
|
||||
//TODO: start new instance with correct arguments
|
||||
|
||||
if (Application.Current?.ApplicationLifetime is IControlledApplicationLifetime controlledApplicationLifetime)
|
||||
Dispatcher.UIThread.Post(() => controlledApplicationLifetime.Shutdown());
|
||||
}
|
||||
|
||||
private void UtilitiesOnShutdownRequested(object? sender, EventArgs e)
|
||||
{
|
||||
RunForcedShutdownIfEnabled();
|
||||
|
||||
if (Application.Current?.ApplicationLifetime is IControlledApplicationLifetime controlledApplicationLifetime)
|
||||
Dispatcher.UIThread.Post(() => controlledApplicationLifetime.Shutdown());
|
||||
}
|
||||
|
||||
private void RunForcedShutdownIfEnabled()
|
||||
{
|
||||
if (StartupArguments.Contains("--disable-forced-shutdown"))
|
||||
return;
|
||||
|
||||
//todo
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<AvaloniaResource Include="Assets\**" />
|
||||
<None Remove=".gitignore" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="0.10.13" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="0.10.13" />
|
||||
<!--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.13" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.13" />
|
||||
<PackageReference Include="ReactiveUI" Version="17.1.50" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Artemis.Core\Artemis.Core.csproj" />
|
||||
<ProjectReference Include="..\Artemis.UI\Artemis.UI.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 172 KiB |
@@ -1,24 +0,0 @@
|
||||
using System;
|
||||
using Avalonia;
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Linux
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
// Initialization code. Don't use any Avalonia, third-party APIs or any
|
||||
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
|
||||
// yet and stuff might break.
|
||||
[STAThread]
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
|
||||
}
|
||||
|
||||
// Avalonia configuration, don't remove; also used by visual designer.
|
||||
public static AppBuilder BuildAvaloniaApp()
|
||||
{
|
||||
return AppBuilder.Configure<App>().UsePlatformDetect().LogToTrace().UseReactiveUI();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
namespace Artemis.UI.Linux.Providers.Input
|
||||
{
|
||||
public enum LinuxAbsoluteAxis
|
||||
{
|
||||
ABS_X = 0x00,
|
||||
ABS_Y = 0x01,
|
||||
ABS_Z = 0x02,
|
||||
ABS_RX = 0x03,
|
||||
ABS_RY = 0x04,
|
||||
ABS_RZ = 0x05,
|
||||
ABS_THROTTLE = 0x06,
|
||||
ABS_RUDDER = 0x07,
|
||||
ABS_WHEEL = 0x08,
|
||||
ABS_GAS = 0x09,
|
||||
ABS_BRAKE = 0x0a,
|
||||
ABS_HAT0X = 0x10,
|
||||
ABS_HAT0Y = 0x11,
|
||||
ABS_HAT1X = 0x12,
|
||||
ABS_HAT1Y = 0x13,
|
||||
ABS_HAT2X = 0x14,
|
||||
ABS_HAT2Y = 0x15,
|
||||
ABS_HAT3X = 0x16,
|
||||
ABS_HAT3Y = 0x17,
|
||||
ABS_PRESSURE = 0x18,
|
||||
ABS_DISTANCE = 0x19,
|
||||
ABS_TILT_X = 0x1a,
|
||||
ABS_TILT_Y = 0x1b,
|
||||
ABS_TOOL_WIDTH = 0x1c,
|
||||
|
||||
ABS_VOLUME = 0x20,
|
||||
|
||||
ABS_MISC = 0x28,
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection.Metadata;
|
||||
using Humanizer;
|
||||
|
||||
namespace Artemis.UI.Linux.Providers.Input
|
||||
{
|
||||
/// <summary>
|
||||
/// Data transfer object representing a device read from /proc/bus/input/devices
|
||||
/// </summary>
|
||||
public class LinuxInputDevice
|
||||
{
|
||||
public string InputId { get; }
|
||||
public string? Bus { get; }
|
||||
public string? Vendor { get; }
|
||||
public string? Product { get; }
|
||||
public string? Version { get; }
|
||||
public string? Name { get; }
|
||||
public string? Phys { get; }
|
||||
public string? Sysfs { get; }
|
||||
public string? Uniq { get; }
|
||||
public string[]? Handlers { get; }
|
||||
public bool IsMouse => Handlers.Any(h => h.Contains("mouse"));
|
||||
public bool IsKeyboard => Handlers.Any(h => h.Contains("kbd"));
|
||||
public bool IsGamePad => Handlers.Any(h => h.Contains("js"));
|
||||
public string EventPath => $"/dev/input/{Handlers.First(h => h.Contains("event"))}";
|
||||
|
||||
public LinuxInputDevice(IEnumerable<string> lines)
|
||||
{
|
||||
foreach (string line in lines)
|
||||
{
|
||||
char dataType = line.First();
|
||||
string data = line.Substring(3);
|
||||
//get the first character in each line and set the according property with relevant data
|
||||
switch (dataType)
|
||||
{
|
||||
case 'I':
|
||||
InputId = data;
|
||||
foreach (string component in data.Split(" "))
|
||||
{
|
||||
string?[] parts = component.Split('=');
|
||||
switch (parts[0])
|
||||
{
|
||||
case "Bus":
|
||||
Bus = parts[1];
|
||||
break;
|
||||
case "Vendor":
|
||||
Vendor = parts[1];
|
||||
break;
|
||||
case "Product":
|
||||
Product = parts[1];
|
||||
break;
|
||||
case "Version":
|
||||
Version = parts[1];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'N':
|
||||
Name = data.Replace("\"", "")
|
||||
.Replace("Name=", "");
|
||||
break;
|
||||
case 'P':
|
||||
Phys = data.Replace("Phys=", "");
|
||||
break;
|
||||
case 'S':
|
||||
Sysfs = data.Replace("Sysfs=", "");
|
||||
break;
|
||||
case 'H':
|
||||
Handlers = data.Replace("Handlers=", "").Split(" ");
|
||||
break;
|
||||
case 'U':
|
||||
Uniq = data.Replace("Uniq=", "");
|
||||
break;
|
||||
default:
|
||||
//do we need any more of this data?
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region Overrides of Object
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString() => $"{Name} - {EventPath}";
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Artemis.UI.Linux.Providers.Input
|
||||
{
|
||||
public static class LinuxInputDeviceFinder
|
||||
{
|
||||
private const string DEVICES_FILE = "/proc/bus/input/devices";
|
||||
|
||||
public static IEnumerable<LinuxInputDevice> Find()
|
||||
{
|
||||
return File.ReadAllLines(DEVICES_FILE)
|
||||
.PartitionBy(s => s == "") //split on empty lines
|
||||
.Select(lineGroup => new LinuxInputDevice(lineGroup));
|
||||
}
|
||||
|
||||
//https://stackoverflow.com/questions/56623354
|
||||
private static IEnumerable<IEnumerable<T>> PartitionBy<T>(this IEnumerable<T> a, Func<T, bool> predicate)
|
||||
{
|
||||
int groupNumber = 0;
|
||||
Func<bool, int?> getGroupNumber = skip =>
|
||||
{
|
||||
if (skip)
|
||||
{
|
||||
// prepare next group, we don't care if we increment more than once
|
||||
// we only want to split groups
|
||||
groupNumber++;
|
||||
// null, to be able to filter out group separators
|
||||
return null;
|
||||
}
|
||||
return groupNumber;
|
||||
};
|
||||
return a
|
||||
.Select(x => new { Value = x, GroupNumber = getGroupNumber(predicate(x))} )
|
||||
.Where(x => x.GroupNumber != null)
|
||||
.GroupBy(x => x.GroupNumber)
|
||||
.Select(g => g.Select(x => x.Value));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.UI.Linux.Utilities;
|
||||
|
||||
namespace Artemis.UI.Linux.Providers.Input
|
||||
{
|
||||
internal class LinuxInputDeviceReader
|
||||
{
|
||||
private readonly FileStream _stream;
|
||||
private readonly Task _task;
|
||||
private readonly CancellationTokenSource _cts;
|
||||
private readonly byte[] _buffer;
|
||||
internal event EventHandler<LinuxInputEventArgs>? InputEvent;
|
||||
|
||||
public LinuxInputDevice InputDevice { get; }
|
||||
|
||||
public LinuxInputDeviceReader(LinuxInputDevice inputDevice)
|
||||
{
|
||||
InputDevice = inputDevice;
|
||||
|
||||
_buffer = new byte[Marshal.SizeOf<LinuxInputEventArgs>()];
|
||||
_stream = new FileStream(InputDevice.EventPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 2048, FileOptions.Asynchronous);
|
||||
_cts = new CancellationTokenSource();
|
||||
_task = Task.Run(Read, _cts.Token);
|
||||
}
|
||||
|
||||
private async Task Read()
|
||||
{
|
||||
while (!_cts.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
int readBytes = await _stream.ReadAsync(_buffer, _cts.Token);
|
||||
if (readBytes == 0)
|
||||
continue;
|
||||
|
||||
InputEvent?.Invoke(this, MemoryMarshal.Read<LinuxInputEventArgs>(_buffer));
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_cts.Cancel();
|
||||
|
||||
_stream.Flush();
|
||||
_stream.Dispose();
|
||||
|
||||
//_task.Wait(); //TODO: fix this, it hangs
|
||||
_cts.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
// ReSharper disable UnusedMember.Global
|
||||
|
||||
#pragma warning disable CS0649
|
||||
namespace Artemis.UI.Linux.Providers.Input
|
||||
{
|
||||
// https://www.kernel.org/doc/Documentation/input/input.txt
|
||||
internal readonly struct LinuxInputEventArgs
|
||||
{
|
||||
internal readonly long TimeSeconds;
|
||||
internal readonly long TimeMicroseconds;
|
||||
internal readonly LinuxInputEventType Type;
|
||||
internal readonly short Code;
|
||||
internal readonly int Value;
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
namespace Artemis.UI.Linux.Providers.Input
|
||||
{
|
||||
/// <summary>
|
||||
/// https://www.kernel.org/doc/Documentation/input/event-codes.txt
|
||||
/// </summary>
|
||||
internal enum LinuxInputEventType : ushort
|
||||
{
|
||||
/// <summary>
|
||||
/// Used as markers to separate events. Events may be separated in time or in space, such as with the multitouch protocol.
|
||||
/// </summary>
|
||||
SYN = 0x00,
|
||||
|
||||
/// <summary>
|
||||
/// Used to describe state changes of keyboards, buttons, or other key-like devices.
|
||||
/// </summary>
|
||||
KEY = 0x01,
|
||||
|
||||
/// <summary>
|
||||
/// Used to describe relative axis value changes, e.g. moving the mouse 5 units to the left.
|
||||
/// </summary>
|
||||
REL = 0x02,
|
||||
|
||||
/// <summary>
|
||||
/// Used to describe absolute axis value changes, e.g. describing the coordinates of a touch on a touchscreen.
|
||||
/// </summary>
|
||||
ABS = 0x03,
|
||||
|
||||
/// <summary>
|
||||
/// Used to describe miscellaneous input data that do not fit into other types.
|
||||
/// </summary>
|
||||
MSC = 0x04,
|
||||
|
||||
/// <summary>
|
||||
/// Used to describe binary state input switches.
|
||||
/// </summary>
|
||||
SW = 0x05,
|
||||
|
||||
/// <summary>
|
||||
/// Used to turn LEDs on devices on and off.
|
||||
/// </summary>
|
||||
LED = 0x11,
|
||||
|
||||
/// <summary>
|
||||
/// Used to output sound to devices.
|
||||
/// </summary>
|
||||
SND = 0x12,
|
||||
|
||||
/// <summary>
|
||||
/// Used for autorepeating devices.
|
||||
/// </summary>
|
||||
REP = 0x14,
|
||||
|
||||
/// <summary>
|
||||
/// Used to send force feedback commands to an input device.
|
||||
/// </summary>
|
||||
FF = 0x15,
|
||||
|
||||
/// <summary>
|
||||
/// A special type for power button and switch input.
|
||||
/// </summary>
|
||||
PWR = 0x16,
|
||||
|
||||
/// <summary>
|
||||
/// Used to receive force feedback device status.
|
||||
/// </summary>
|
||||
FF_STATUS = 0x17,
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Linux.Utilities;
|
||||
using Serilog;
|
||||
|
||||
namespace Artemis.UI.Linux.Providers.Input
|
||||
{
|
||||
public class LinuxInputProvider : InputProvider
|
||||
{
|
||||
private readonly IInputService _inputService;
|
||||
private readonly ILogger _logger;
|
||||
private readonly List<LinuxInputDeviceReader> _readers;
|
||||
|
||||
public LinuxInputProvider(ILogger logger, IInputService inputService)
|
||||
{
|
||||
_logger = logger;
|
||||
_inputService = inputService;
|
||||
_readers = new List<LinuxInputDeviceReader>();
|
||||
|
||||
foreach (LinuxInputDevice deviceDefinition in LinuxInputDeviceFinder.Find())
|
||||
{
|
||||
LinuxInputDeviceReader? reader = new LinuxInputDeviceReader(deviceDefinition);
|
||||
reader.InputEvent += OnInputEvent;
|
||||
_readers.Add(reader);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnInputEvent(object? sender, LinuxInputEventArgs e)
|
||||
{
|
||||
if (sender is not LinuxInputDeviceReader reader)
|
||||
return;
|
||||
|
||||
if (reader.InputDevice.IsKeyboard)
|
||||
{
|
||||
HandleKeyboardData(reader.InputDevice, e);
|
||||
}
|
||||
else if (reader.InputDevice.IsMouse)
|
||||
{
|
||||
HandleMouseData(reader.InputDevice, e);
|
||||
}
|
||||
else if (reader.InputDevice.IsGamePad)
|
||||
{
|
||||
//TODO: handle game pad input?
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleKeyboardData(LinuxInputDevice keyboard, LinuxInputEventArgs e)
|
||||
{
|
||||
switch (e.Type)
|
||||
{
|
||||
case LinuxInputEventType.KEY:
|
||||
KeyboardKey key = InputUtilities.KeyFromKeyCode((LinuxKeyboardKeyCodes) e.Code);
|
||||
bool isDown = e.Value != 0;
|
||||
|
||||
_logger.Verbose($"Keyboard Key: {(LinuxKeyboardKeyCodes) e.Code} | Down: {isDown}");
|
||||
|
||||
//TODO: identify
|
||||
|
||||
OnKeyboardDataReceived(null, key, isDown);
|
||||
break;
|
||||
default:
|
||||
_logger.Verbose($"Unknown keyboard event type: {e.Type}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleMouseData(LinuxInputDevice mouse, LinuxInputEventArgs e)
|
||||
{
|
||||
switch (e.Type)
|
||||
{
|
||||
case LinuxInputEventType.KEY:
|
||||
bool isDown = e.Value != 0;
|
||||
MouseButton button = InputUtilities.MouseButtonFromButtonCode((LinuxKeyboardKeyCodes)e.Code);
|
||||
|
||||
_logger.Verbose($"Mouse Button: {(LinuxKeyboardKeyCodes) e.Code} | Down: {isDown}");
|
||||
|
||||
//TODO: identify
|
||||
|
||||
OnMouseButtonDataReceived(null, button, isDown);
|
||||
break;
|
||||
|
||||
case LinuxInputEventType.ABS:
|
||||
LinuxAbsoluteAxis absoluteAxis = (LinuxAbsoluteAxis) e.Code;
|
||||
_logger.Verbose($"Absolute mouse: axis={absoluteAxis} | value={e.Value}");
|
||||
break;
|
||||
case LinuxInputEventType.REL:
|
||||
LinuxRelativeAxis relativeAxis = (LinuxRelativeAxis) e.Code;
|
||||
_logger.Verbose($"Relative mouse: axis={relativeAxis} | value={e.Value}");
|
||||
|
||||
//TODO: handle mouse movement
|
||||
break;
|
||||
default:
|
||||
_logger.Verbose($"Unknown mouse event type: {e.Type}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#region Overrides of InputProvider
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
for (int i = _readers.Count - 1; i >= 0; i--)
|
||||
{
|
||||
_readers[i].InputEvent -= OnInputEvent;
|
||||
_readers[i].Dispose();
|
||||
_readers.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,620 +0,0 @@
|
||||
namespace Artemis.UI.Linux.Providers.Input
|
||||
{
|
||||
//https://github.com/torvalds/linux/blob/master/include/uapi/linux/input-event-codes.h
|
||||
public enum LinuxKeyboardKeyCodes
|
||||
{
|
||||
KEY_RESERVED = 0,
|
||||
KEY_ESC = 1,
|
||||
KEY_1 = 2,
|
||||
KEY_2 = 3,
|
||||
KEY_3 = 4,
|
||||
KEY_4 = 5,
|
||||
KEY_5 = 6,
|
||||
KEY_6 = 7,
|
||||
KEY_7 = 8,
|
||||
KEY_8 = 9,
|
||||
KEY_9 = 10,
|
||||
KEY_0 = 11,
|
||||
KEY_MINUS = 12,
|
||||
KEY_EQUAL = 13,
|
||||
KEY_BACKSPACE = 14,
|
||||
KEY_TAB = 15,
|
||||
KEY_Q = 16,
|
||||
KEY_W = 17,
|
||||
KEY_E = 18,
|
||||
KEY_R = 19,
|
||||
KEY_T = 20,
|
||||
KEY_Y = 21,
|
||||
KEY_U = 22,
|
||||
KEY_I = 23,
|
||||
KEY_O = 24,
|
||||
KEY_P = 25,
|
||||
KEY_LEFTBRACE = 26,
|
||||
KEY_RIGHTBRACE = 27,
|
||||
KEY_ENTER = 28,
|
||||
KEY_LEFTCTRL = 29,
|
||||
KEY_A = 30,
|
||||
KEY_S = 31,
|
||||
KEY_D = 32,
|
||||
KEY_F = 33,
|
||||
KEY_G = 34,
|
||||
KEY_H = 35,
|
||||
KEY_J = 36,
|
||||
KEY_K = 37,
|
||||
KEY_L = 38,
|
||||
KEY_SEMICOLON = 39,
|
||||
KEY_APOSTROPHE = 40,
|
||||
KEY_GRAVE = 41,
|
||||
KEY_LEFTSHIFT = 42,
|
||||
KEY_BACKSLASH = 43,
|
||||
KEY_Z = 44,
|
||||
KEY_X = 45,
|
||||
KEY_C = 46,
|
||||
KEY_V = 47,
|
||||
KEY_B = 48,
|
||||
KEY_N = 49,
|
||||
KEY_M = 50,
|
||||
KEY_COMMA = 51,
|
||||
KEY_DOT = 52,
|
||||
KEY_SLASH = 53,
|
||||
KEY_RIGHTSHIFT = 54,
|
||||
KEY_KPASTERISK = 55,
|
||||
KEY_LEFTALT = 56,
|
||||
KEY_SPACE = 57,
|
||||
KEY_CAPSLOCK = 58,
|
||||
KEY_F1 = 59,
|
||||
KEY_F2 = 60,
|
||||
KEY_F3 = 61,
|
||||
KEY_F4 = 62,
|
||||
KEY_F5 = 63,
|
||||
KEY_F6 = 64,
|
||||
KEY_F7 = 65,
|
||||
KEY_F8 = 66,
|
||||
KEY_F9 = 67,
|
||||
KEY_F10 = 68,
|
||||
KEY_NUMLOCK = 69,
|
||||
KEY_SCROLLLOCK = 70,
|
||||
KEY_KP7 = 71,
|
||||
KEY_KP8 = 72,
|
||||
KEY_KP9 = 73,
|
||||
KEY_KPMINUS = 74,
|
||||
KEY_KP4 = 75,
|
||||
KEY_KP5 = 76,
|
||||
KEY_KP6 = 77,
|
||||
KEY_KPPLUS = 78,
|
||||
KEY_KP1 = 79,
|
||||
KEY_KP2 = 80,
|
||||
KEY_KP3 = 81,
|
||||
KEY_KP0 = 82,
|
||||
KEY_KPDOT = 83,
|
||||
KEY_ZENKAKUHANKAKU = 85,
|
||||
KEY_102ND = 86,
|
||||
KEY_F11 = 87,
|
||||
KEY_F12 = 88,
|
||||
KEY_RO = 89,
|
||||
KEY_KATAKANA = 90,
|
||||
KEY_HIRAGANA = 91,
|
||||
KEY_HENKAN = 92,
|
||||
KEY_KATAKANAHIRAGANA = 93,
|
||||
KEY_MUHENKAN = 94,
|
||||
KEY_KPJPCOMMA = 95,
|
||||
KEY_KPENTER = 96,
|
||||
KEY_RIGHTCTRL = 97,
|
||||
KEY_KPSLASH = 98,
|
||||
KEY_SYSRQ = 99,
|
||||
KEY_RIGHTALT = 100,
|
||||
KEY_LINEFEED = 101,
|
||||
KEY_HOME = 102,
|
||||
KEY_UP = 103,
|
||||
KEY_PAGEUP = 104,
|
||||
KEY_LEFT = 105,
|
||||
KEY_RIGHT = 106,
|
||||
KEY_END = 107,
|
||||
KEY_DOWN = 108,
|
||||
KEY_PAGEDOWN = 109,
|
||||
KEY_INSERT = 110,
|
||||
KEY_DELETE = 111,
|
||||
KEY_MACRO = 112,
|
||||
KEY_MUTE = 113,
|
||||
KEY_VOLUMEDOWN = 114,
|
||||
KEY_VOLUMEUP = 115,
|
||||
KEY_POWER = 116, /* SC System Power Down */
|
||||
KEY_KPEQUAL = 117,
|
||||
KEY_KPPLUSMINUS = 118,
|
||||
KEY_PAUSE = 119,
|
||||
KEY_SCALE = 120, /* AL Compiz Scale (Expose) */
|
||||
KEY_KPCOMMA = 121,
|
||||
KEY_HANGEUL = 122,
|
||||
KEY_HANGUEL = KEY_HANGEUL,
|
||||
KEY_HANJA = 123,
|
||||
KEY_YEN = 124,
|
||||
KEY_LEFTMETA = 125,
|
||||
KEY_RIGHTMETA = 126,
|
||||
KEY_COMPOSE = 127,
|
||||
KEY_STOP = 128, /* AC Stop */
|
||||
KEY_AGAIN = 129,
|
||||
KEY_PROPS = 130, /* AC Properties */
|
||||
KEY_UNDO = 131, /* AC Undo */
|
||||
KEY_FRONT = 132,
|
||||
KEY_COPY = 133, /* AC Copy */
|
||||
KEY_OPEN = 134, /* AC Open */
|
||||
KEY_PASTE = 135, /* AC Paste */
|
||||
KEY_FIND = 136, /* AC Search */
|
||||
KEY_CUT = 137, /* AC Cut */
|
||||
KEY_HELP = 138, /* AL Integrated Help Center */
|
||||
KEY_MENU = 139, /* Menu (show menu) */
|
||||
KEY_CALC = 140, /* AL Calculator */
|
||||
KEY_SETUP = 141,
|
||||
KEY_SLEEP = 142, /* SC System Sleep */
|
||||
KEY_WAKEUP = 143, /* System Wake Up */
|
||||
KEY_FILE = 144, /* AL Local Machine Browser */
|
||||
KEY_SENDFILE = 145,
|
||||
KEY_DELETEFILE = 146,
|
||||
KEY_XFER = 147,
|
||||
KEY_PROG1 = 148,
|
||||
KEY_PROG2 = 149,
|
||||
KEY_WWW = 150, /* AL Internet Browser */
|
||||
KEY_MSDOS = 151,
|
||||
KEY_COFFEE = 152, /* AL Terminal Lock/Screensaver */
|
||||
KEY_SCREENLOCK = KEY_COFFEE,
|
||||
KEY_ROTATE_DISPLAY = 153, /* Display orientation for e.g. tablets */
|
||||
KEY_DIRECTION = KEY_ROTATE_DISPLAY,
|
||||
KEY_CYCLEWINDOWS = 154,
|
||||
KEY_MAIL = 155,
|
||||
KEY_BOOKMARKS = 156, /* AC Bookmarks */
|
||||
KEY_COMPUTER = 157,
|
||||
KEY_BACK = 158, /* AC Back */
|
||||
KEY_FORWARD = 159, /* AC Forward */
|
||||
KEY_CLOSECD = 160,
|
||||
KEY_EJECTCD = 161,
|
||||
KEY_EJECTCLOSECD = 162,
|
||||
KEY_NEXTSONG = 163,
|
||||
KEY_PLAYPAUSE = 164,
|
||||
KEY_PREVIOUSSONG = 165,
|
||||
KEY_STOPCD = 166,
|
||||
KEY_RECORD = 167,
|
||||
KEY_REWIND = 168,
|
||||
KEY_PHONE = 169, /* Media Select Telephone */
|
||||
KEY_ISO = 170,
|
||||
KEY_CONFIG = 171, /* AL Consumer Control Configuration */
|
||||
KEY_HOMEPAGE = 172, /* AC Home */
|
||||
KEY_REFRESH = 173, /* AC Refresh */
|
||||
KEY_EXIT = 174, /* AC Exit */
|
||||
KEY_MOVE = 175,
|
||||
KEY_EDIT = 176,
|
||||
KEY_SCROLLUP = 177,
|
||||
KEY_SCROLLDOWN = 178,
|
||||
KEY_KPLEFTPAREN = 179,
|
||||
KEY_KPRIGHTPAREN = 180,
|
||||
KEY_NEW = 181, /* AC New */
|
||||
KEY_REDO = 182, /* AC Redo/Repeat */
|
||||
KEY_F13 = 183,
|
||||
KEY_F14 = 184,
|
||||
KEY_F15 = 185,
|
||||
KEY_F16 = 186,
|
||||
KEY_F17 = 187,
|
||||
KEY_F18 = 188,
|
||||
KEY_F19 = 189,
|
||||
KEY_F20 = 190,
|
||||
KEY_F21 = 191,
|
||||
KEY_F22 = 192,
|
||||
KEY_F23 = 193,
|
||||
KEY_F24 = 194,
|
||||
KEY_PLAYCD = 200,
|
||||
KEY_PAUSECD = 201,
|
||||
KEY_PROG3 = 202,
|
||||
KEY_PROG4 = 203,
|
||||
KEY_DASHBOARD = 204, /* AL Dashboard */
|
||||
KEY_SUSPEND = 205,
|
||||
KEY_CLOSE = 206, /* AC Close */
|
||||
KEY_PLAY = 207,
|
||||
KEY_FASTFORWARD = 208,
|
||||
KEY_BASSBOOST = 209,
|
||||
KEY_PRINT = 210, /* AC Print */
|
||||
KEY_HP = 211,
|
||||
KEY_CAMERA = 212,
|
||||
KEY_SOUND = 213,
|
||||
KEY_QUESTION = 214,
|
||||
KEY_EMAIL = 215,
|
||||
KEY_CHAT = 216,
|
||||
KEY_SEARCH = 217,
|
||||
KEY_CONNECT = 218,
|
||||
KEY_FINANCE = 219, /* AL Checkbook/Finance */
|
||||
KEY_SPORT = 220,
|
||||
KEY_SHOP = 221,
|
||||
KEY_ALTERASE = 222,
|
||||
KEY_CANCEL = 223, /* AC Cancel */
|
||||
KEY_BRIGHTNESSDOWN = 224,
|
||||
KEY_BRIGHTNESSUP = 225,
|
||||
KEY_MEDIA = 226,
|
||||
KEY_SWITCHVIDEOMODE = 227,
|
||||
KEY_KBDILLUMTOGGLE = 228,
|
||||
KEY_KBDILLUMDOWN = 229,
|
||||
KEY_KBDILLUMUP = 230,
|
||||
KEY_SEND = 231, /* AC Send */
|
||||
KEY_REPLY = 232, /* AC Reply */
|
||||
KEY_FORWARDMAIL = 233, /* AC Forward Msg */
|
||||
KEY_SAVE = 234, /* AC Save */
|
||||
KEY_DOCUMENTS = 235,
|
||||
KEY_BATTERY = 236,
|
||||
KEY_BLUETOOTH = 237,
|
||||
KEY_WLAN = 238,
|
||||
KEY_UWB = 239,
|
||||
KEY_UNKNOWN = 240,
|
||||
KEY_VIDEO_NEXT = 241, /* drive next video source */
|
||||
KEY_VIDEO_PREV = 242, /* drive previous video source */
|
||||
KEY_BRIGHTNESS_CYCLE = 243, /* brightness up, after max is min */
|
||||
KEY_BRIGHTNESS_AUTO = 244, /* Set Auto Brightness: manual
|
||||
KEY_BRIGHTNESS_ZERO = KEY_BRIGHTNESS_AUTO,
|
||||
KEY_DISPLAY_OFF = 245, /* display device to off state */
|
||||
KEY_WWAN = 246, /* Wireless WAN (LTE, UMTS, GSM, etc.) */
|
||||
KEY_WIMAX = KEY_WWAN,
|
||||
KEY_RFKILL = 247, /* Key that controls all radios */
|
||||
KEY_MICMUTE = 248, /* Mute / unmute the microphone */
|
||||
BTN_MISC = 0x100,
|
||||
BTN_0 = 0x100,
|
||||
BTN_1 = 0x101,
|
||||
BTN_2 = 0x102,
|
||||
BTN_3 = 0x103,
|
||||
BTN_4 = 0x104,
|
||||
BTN_5 = 0x105,
|
||||
BTN_6 = 0x106,
|
||||
BTN_7 = 0x107,
|
||||
BTN_8 = 0x108,
|
||||
BTN_9 = 0x109,
|
||||
BTN_MOUSE = 0x110,
|
||||
BTN_LEFT = 0x110,
|
||||
BTN_RIGHT = 0x111,
|
||||
BTN_MIDDLE = 0x112,
|
||||
BTN_SIDE = 0x113,
|
||||
BTN_EXTRA = 0x114,
|
||||
BTN_FORWARD = 0x115,
|
||||
BTN_BACK = 0x116,
|
||||
BTN_TASK = 0x117,
|
||||
BTN_JOYSTICK = 0x120,
|
||||
BTN_TRIGGER = 0x120,
|
||||
BTN_THUMB = 0x121,
|
||||
BTN_THUMB2 = 0x122,
|
||||
BTN_TOP = 0x123,
|
||||
BTN_TOP2 = 0x124,
|
||||
BTN_PINKIE = 0x125,
|
||||
BTN_BASE = 0x126,
|
||||
BTN_BASE2 = 0x127,
|
||||
BTN_BASE3 = 0x128,
|
||||
BTN_BASE4 = 0x129,
|
||||
BTN_BASE5 = 0x12a,
|
||||
BTN_BASE6 = 0x12b,
|
||||
BTN_DEAD = 0x12f,
|
||||
BTN_GAMEPAD = 0x130,
|
||||
BTN_SOUTH = 0x130,
|
||||
BTN_A = BTN_SOUTH,
|
||||
BTN_EAST = 0x131,
|
||||
BTN_B = BTN_EAST,
|
||||
BTN_C = 0x132,
|
||||
BTN_NORTH = 0x133,
|
||||
BTN_X = BTN_NORTH,
|
||||
BTN_WEST = 0x134,
|
||||
BTN_Y = BTN_WEST,
|
||||
BTN_Z = 0x135,
|
||||
BTN_TL = 0x136,
|
||||
BTN_TR = 0x137,
|
||||
BTN_TL2 = 0x138,
|
||||
BTN_TR2 = 0x139,
|
||||
BTN_SELECT = 0x13a,
|
||||
BTN_START = 0x13b,
|
||||
BTN_MODE = 0x13c,
|
||||
BTN_THUMBL = 0x13d,
|
||||
BTN_THUMBR = 0x13e,
|
||||
BTN_DIGI = 0x140,
|
||||
BTN_TOOL_PEN = 0x140,
|
||||
BTN_TOOL_RUBBER = 0x141,
|
||||
BTN_TOOL_BRUSH = 0x142,
|
||||
BTN_TOOL_PENCIL = 0x143,
|
||||
BTN_TOOL_AIRBRUSH = 0x144,
|
||||
BTN_TOOL_FINGER = 0x145,
|
||||
BTN_TOOL_MOUSE = 0x146,
|
||||
BTN_TOOL_LENS = 0x147,
|
||||
BTN_TOOL_QUINTTAP = 0x148, /* Five fingers on trackpad */
|
||||
BTN_STYLUS3 = 0x149,
|
||||
BTN_TOUCH = 0x14a,
|
||||
BTN_STYLUS = 0x14b,
|
||||
BTN_STYLUS2 = 0x14c,
|
||||
BTN_TOOL_DOUBLETAP = 0x14d,
|
||||
BTN_TOOL_TRIPLETAP = 0x14e,
|
||||
BTN_TOOL_QUADTAP = 0x14f, /* Four fingers on trackpad */
|
||||
BTN_WHEEL = 0x150,
|
||||
BTN_GEAR_DOWN = 0x150,
|
||||
BTN_GEAR_UP = 0x151,
|
||||
KEY_OK = 0x160,
|
||||
KEY_SELECT = 0x161,
|
||||
KEY_GOTO = 0x162,
|
||||
KEY_CLEAR = 0x163,
|
||||
KEY_POWER2 = 0x164,
|
||||
KEY_OPTION = 0x165,
|
||||
KEY_INFO = 0x166, /* AL OEM Features/Tips/Tutorial */
|
||||
KEY_TIME = 0x167,
|
||||
KEY_VENDOR = 0x168,
|
||||
KEY_ARCHIVE = 0x169,
|
||||
KEY_PROGRAM = 0x16a, /* Media Select Program Guide */
|
||||
KEY_CHANNEL = 0x16b,
|
||||
KEY_FAVORITES = 0x16c,
|
||||
KEY_EPG = 0x16d,
|
||||
KEY_PVR = 0x16e, /* Media Select Home */
|
||||
KEY_MHP = 0x16f,
|
||||
KEY_LANGUAGE = 0x170,
|
||||
KEY_TITLE = 0x171,
|
||||
KEY_SUBTITLE = 0x172,
|
||||
KEY_ANGLE = 0x173,
|
||||
KEY_FULL_SCREEN = 0x174, /* AC View Toggle */
|
||||
KEY_ZOOM = KEY_FULL_SCREEN,
|
||||
KEY_MODE = 0x175,
|
||||
KEY_KEYBOARD = 0x176,
|
||||
KEY_ASPECT_RATIO = 0x177, /* HUTRR37: Aspect */
|
||||
KEY_SCREEN = KEY_ASPECT_RATIO,
|
||||
KEY_PC = 0x178, /* Media Select Computer */
|
||||
KEY_TV = 0x179, /* Media Select TV */
|
||||
KEY_TV2 = 0x17a, /* Media Select Cable */
|
||||
KEY_VCR = 0x17b, /* Media Select VCR */
|
||||
KEY_VCR2 = 0x17c, /* VCR Plus */
|
||||
KEY_SAT = 0x17d, /* Media Select Satellite */
|
||||
KEY_SAT2 = 0x17e,
|
||||
KEY_CD = 0x17f, /* Media Select CD */
|
||||
KEY_TAPE = 0x180, /* Media Select Tape */
|
||||
KEY_RADIO = 0x181,
|
||||
KEY_TUNER = 0x182, /* Media Select Tuner */
|
||||
KEY_PLAYER = 0x183,
|
||||
KEY_TEXT = 0x184,
|
||||
KEY_DVD = 0x185, /* Media Select DVD */
|
||||
KEY_AUX = 0x186,
|
||||
KEY_MP3 = 0x187,
|
||||
KEY_AUDIO = 0x188, /* AL Audio Browser */
|
||||
KEY_VIDEO = 0x189, /* AL Movie Browser */
|
||||
KEY_DIRECTORY = 0x18a,
|
||||
KEY_LIST = 0x18b,
|
||||
KEY_MEMO = 0x18c, /* Media Select Messages */
|
||||
KEY_CALENDAR = 0x18d,
|
||||
KEY_RED = 0x18e,
|
||||
KEY_GREEN = 0x18f,
|
||||
KEY_YELLOW = 0x190,
|
||||
KEY_BLUE = 0x191,
|
||||
KEY_CHANNELUP = 0x192, /* Channel Increment */
|
||||
KEY_CHANNELDOWN = 0x193, /* Channel Decrement */
|
||||
KEY_FIRST = 0x194,
|
||||
KEY_LAST = 0x195, /* Recall Last */
|
||||
KEY_AB = 0x196,
|
||||
KEY_NEXT = 0x197,
|
||||
KEY_RESTART = 0x198,
|
||||
KEY_SLOW = 0x199,
|
||||
KEY_SHUFFLE = 0x19a,
|
||||
KEY_BREAK = 0x19b,
|
||||
KEY_PREVIOUS = 0x19c,
|
||||
KEY_DIGITS = 0x19d,
|
||||
KEY_TEEN = 0x19e,
|
||||
KEY_TWEN = 0x19f,
|
||||
KEY_VIDEOPHONE = 0x1a0, /* Media Select Video Phone */
|
||||
KEY_GAMES = 0x1a1, /* Media Select Games */
|
||||
KEY_ZOOMIN = 0x1a2, /* AC Zoom In */
|
||||
KEY_ZOOMOUT = 0x1a3, /* AC Zoom Out */
|
||||
KEY_ZOOMRESET = 0x1a4, /* AC Zoom */
|
||||
KEY_WORDPROCESSOR = 0x1a5, /* AL Word Processor */
|
||||
KEY_EDITOR = 0x1a6, /* AL Text Editor */
|
||||
KEY_SPREADSHEET = 0x1a7, /* AL Spreadsheet */
|
||||
KEY_GRAPHICSEDITOR = 0x1a8, /* AL Graphics Editor */
|
||||
KEY_PRESENTATION = 0x1a9, /* AL Presentation App */
|
||||
KEY_DATABASE = 0x1aa, /* AL Database App */
|
||||
KEY_NEWS = 0x1ab, /* AL Newsreader */
|
||||
KEY_VOICEMAIL = 0x1ac, /* AL Voicemail */
|
||||
KEY_ADDRESSBOOK = 0x1ad, /* AL Contacts/Address Book */
|
||||
KEY_MESSENGER = 0x1ae, /* AL Instant Messaging */
|
||||
KEY_DISPLAYTOGGLE = 0x1af, /* Turn display (LCD) on and off */
|
||||
KEY_BRIGHTNESS_TOGGLE = KEY_DISPLAYTOGGLE,
|
||||
KEY_SPELLCHECK = 0x1b0, /* AL Spell Check */
|
||||
KEY_LOGOFF = 0x1b1, /* AL Logoff */
|
||||
KEY_DOLLAR = 0x1b2,
|
||||
KEY_EURO = 0x1b3,
|
||||
KEY_FRAMEBACK = 0x1b4, /* Consumer - transport controls */
|
||||
KEY_FRAMEFORWARD = 0x1b5,
|
||||
KEY_CONTEXT_MENU = 0x1b6, /* GenDesc - system context menu */
|
||||
KEY_MEDIA_REPEAT = 0x1b7, /* Consumer - transport control */
|
||||
KEY_10CHANNELSUP = 0x1b8, /* 10 channels up (10+) */
|
||||
KEY_10CHANNELSDOWN = 0x1b9, /* 10 channels down (10-) */
|
||||
KEY_IMAGES = 0x1ba, /* AL Image Browser */
|
||||
KEY_NOTIFICATION_CENTER = 0x1bc, /* Show/hide the notification center */
|
||||
KEY_PICKUP_PHONE = 0x1bd, /* Answer incoming call */
|
||||
KEY_HANGUP_PHONE = 0x1be, /* Decline incoming call */
|
||||
KEY_DEL_EOL = 0x1c0,
|
||||
KEY_DEL_EOS = 0x1c1,
|
||||
KEY_INS_LINE = 0x1c2,
|
||||
KEY_DEL_LINE = 0x1c3,
|
||||
KEY_FN = 0x1d0,
|
||||
KEY_FN_ESC = 0x1d1,
|
||||
KEY_FN_F1 = 0x1d2,
|
||||
KEY_FN_F2 = 0x1d3,
|
||||
KEY_FN_F3 = 0x1d4,
|
||||
KEY_FN_F4 = 0x1d5,
|
||||
KEY_FN_F5 = 0x1d6,
|
||||
KEY_FN_F6 = 0x1d7,
|
||||
KEY_FN_F7 = 0x1d8,
|
||||
KEY_FN_F8 = 0x1d9,
|
||||
KEY_FN_F9 = 0x1da,
|
||||
KEY_FN_F10 = 0x1db,
|
||||
KEY_FN_F11 = 0x1dc,
|
||||
KEY_FN_F12 = 0x1dd,
|
||||
KEY_FN_1 = 0x1de,
|
||||
KEY_FN_2 = 0x1df,
|
||||
KEY_FN_D = 0x1e0,
|
||||
KEY_FN_E = 0x1e1,
|
||||
KEY_FN_F = 0x1e2,
|
||||
KEY_FN_S = 0x1e3,
|
||||
KEY_FN_B = 0x1e4,
|
||||
KEY_FN_RIGHT_SHIFT = 0x1e5,
|
||||
KEY_BRL_DOT1 = 0x1f1,
|
||||
KEY_BRL_DOT2 = 0x1f2,
|
||||
KEY_BRL_DOT3 = 0x1f3,
|
||||
KEY_BRL_DOT4 = 0x1f4,
|
||||
KEY_BRL_DOT5 = 0x1f5,
|
||||
KEY_BRL_DOT6 = 0x1f6,
|
||||
KEY_BRL_DOT7 = 0x1f7,
|
||||
KEY_BRL_DOT8 = 0x1f8,
|
||||
KEY_BRL_DOT9 = 0x1f9,
|
||||
KEY_BRL_DOT10 = 0x1fa,
|
||||
KEY_NUMERIC_0 = 0x200, /* used by phones, remote controls, */
|
||||
KEY_NUMERIC_1 = 0x201, /* and other keypads */
|
||||
KEY_NUMERIC_2 = 0x202,
|
||||
KEY_NUMERIC_3 = 0x203,
|
||||
KEY_NUMERIC_4 = 0x204,
|
||||
KEY_NUMERIC_5 = 0x205,
|
||||
KEY_NUMERIC_6 = 0x206,
|
||||
KEY_NUMERIC_7 = 0x207,
|
||||
KEY_NUMERIC_8 = 0x208,
|
||||
KEY_NUMERIC_9 = 0x209,
|
||||
KEY_NUMERIC_STAR = 0x20a,
|
||||
KEY_NUMERIC_POUND = 0x20b,
|
||||
KEY_NUMERIC_A = 0x20c, /* Phone key A - HUT Telephony 0xb9 */
|
||||
KEY_NUMERIC_B = 0x20d,
|
||||
KEY_NUMERIC_C = 0x20e,
|
||||
KEY_NUMERIC_D = 0x20f,
|
||||
KEY_CAMERA_FOCUS = 0x210,
|
||||
KEY_WPS_BUTTON = 0x211, /* WiFi Protected Setup key */
|
||||
KEY_TOUCHPAD_TOGGLE = 0x212, /* Request switch touchpad on or off */
|
||||
KEY_TOUCHPAD_ON = 0x213,
|
||||
KEY_TOUCHPAD_OFF = 0x214,
|
||||
KEY_CAMERA_ZOOMIN = 0x215,
|
||||
KEY_CAMERA_ZOOMOUT = 0x216,
|
||||
KEY_CAMERA_UP = 0x217,
|
||||
KEY_CAMERA_DOWN = 0x218,
|
||||
KEY_CAMERA_LEFT = 0x219,
|
||||
KEY_CAMERA_RIGHT = 0x21a,
|
||||
KEY_ATTENDANT_ON = 0x21b,
|
||||
KEY_ATTENDANT_OFF = 0x21c,
|
||||
KEY_ATTENDANT_TOGGLE = 0x21d, /* Attendant call on or off */
|
||||
KEY_LIGHTS_TOGGLE = 0x21e, /* Reading light on or off */
|
||||
BTN_DPAD_UP = 0x220,
|
||||
BTN_DPAD_DOWN = 0x221,
|
||||
BTN_DPAD_LEFT = 0x222,
|
||||
BTN_DPAD_RIGHT = 0x223,
|
||||
KEY_ALS_TOGGLE = 0x230, /* Ambient light sensor */
|
||||
KEY_ROTATE_LOCK_TOGGLE = 0x231, /* Display rotation lock */
|
||||
KEY_BUTTONCONFIG = 0x240, /* AL Button Configuration */
|
||||
KEY_TASKMANAGER = 0x241, /* AL Task/Project Manager */
|
||||
KEY_JOURNAL = 0x242, /* AL Log/Journal/Timecard */
|
||||
KEY_CONTROLPANEL = 0x243, /* AL Control Panel */
|
||||
KEY_APPSELECT = 0x244, /* AL Select Task/Application */
|
||||
KEY_SCREENSAVER = 0x245, /* AL Screen Saver */
|
||||
KEY_VOICECOMMAND = 0x246, /* Listening Voice Command */
|
||||
KEY_ASSISTANT = 0x247, /* AL Context-aware desktop assistant */
|
||||
KEY_KBD_LAYOUT_NEXT = 0x248, /* AC Next Keyboard Layout Select */
|
||||
KEY_EMOJI_PICKER = 0x249, /* Show/hide emoji picker (HUTRR101) */
|
||||
KEY_BRIGHTNESS_MIN = 0x250, /* Set Brightness to Minimum */
|
||||
KEY_BRIGHTNESS_MAX = 0x251, /* Set Brightness to Maximum */
|
||||
KEY_KBDINPUTASSIST_PREV = 0x260,
|
||||
KEY_KBDINPUTASSIST_NEXT = 0x261,
|
||||
KEY_KBDINPUTASSIST_PREVGROUP = 0x262,
|
||||
KEY_KBDINPUTASSIST_NEXTGROUP = 0x263,
|
||||
KEY_KBDINPUTASSIST_ACCEPT = 0x264,
|
||||
KEY_KBDINPUTASSIST_CANCEL = 0x265,
|
||||
KEY_RIGHT_UP = 0x266,
|
||||
KEY_RIGHT_DOWN = 0x267,
|
||||
KEY_LEFT_UP = 0x268,
|
||||
KEY_LEFT_DOWN = 0x269,
|
||||
KEY_ROOT_MENU = 0x26a, /* Show Device's Root Menu */
|
||||
KEY_MEDIA_TOP_MENU = 0x26b,
|
||||
KEY_NUMERIC_11 = 0x26c,
|
||||
KEY_NUMERIC_12 = 0x26d,
|
||||
KEY_AUDIO_DESC = 0x26e,
|
||||
KEY_3D_MODE = 0x26f,
|
||||
KEY_NEXT_FAVORITE = 0x270,
|
||||
KEY_STOP_RECORD = 0x271,
|
||||
KEY_PAUSE_RECORD = 0x272,
|
||||
KEY_VOD = 0x273, /* Video on Demand */
|
||||
KEY_UNMUTE = 0x274,
|
||||
KEY_FASTREVERSE = 0x275,
|
||||
KEY_SLOWREVERSE = 0x276,
|
||||
KEY_DATA = 0x277,
|
||||
KEY_ONSCREEN_KEYBOARD = 0x278,
|
||||
KEY_PRIVACY_SCREEN_TOGGLE = 0x279,
|
||||
KEY_SELECTIVE_SCREENSHOT = 0x27a,
|
||||
KEY_MACRO1 = 0x290,
|
||||
KEY_MACRO2 = 0x291,
|
||||
KEY_MACRO3 = 0x292,
|
||||
KEY_MACRO4 = 0x293,
|
||||
KEY_MACRO5 = 0x294,
|
||||
KEY_MACRO6 = 0x295,
|
||||
KEY_MACRO7 = 0x296,
|
||||
KEY_MACRO8 = 0x297,
|
||||
KEY_MACRO9 = 0x298,
|
||||
KEY_MACRO10 = 0x299,
|
||||
KEY_MACRO11 = 0x29a,
|
||||
KEY_MACRO12 = 0x29b,
|
||||
KEY_MACRO13 = 0x29c,
|
||||
KEY_MACRO14 = 0x29d,
|
||||
KEY_MACRO15 = 0x29e,
|
||||
KEY_MACRO16 = 0x29f,
|
||||
KEY_MACRO17 = 0x2a0,
|
||||
KEY_MACRO18 = 0x2a1,
|
||||
KEY_MACRO19 = 0x2a2,
|
||||
KEY_MACRO20 = 0x2a3,
|
||||
KEY_MACRO21 = 0x2a4,
|
||||
KEY_MACRO22 = 0x2a5,
|
||||
KEY_MACRO23 = 0x2a6,
|
||||
KEY_MACRO24 = 0x2a7,
|
||||
KEY_MACRO25 = 0x2a8,
|
||||
KEY_MACRO26 = 0x2a9,
|
||||
KEY_MACRO27 = 0x2aa,
|
||||
KEY_MACRO28 = 0x2ab,
|
||||
KEY_MACRO29 = 0x2ac,
|
||||
KEY_MACRO30 = 0x2ad,
|
||||
KEY_MACRO_RECORD_START = 0x2b0,
|
||||
KEY_MACRO_RECORD_STOP = 0x2b1,
|
||||
KEY_MACRO_PRESET_CYCLE = 0x2b2,
|
||||
KEY_MACRO_PRESET1 = 0x2b3,
|
||||
KEY_MACRO_PRESET2 = 0x2b4,
|
||||
KEY_MACRO_PRESET3 = 0x2b5,
|
||||
KEY_KBD_LCD_MENU1 = 0x2b8,
|
||||
KEY_KBD_LCD_MENU2 = 0x2b9,
|
||||
KEY_KBD_LCD_MENU3 = 0x2ba,
|
||||
KEY_KBD_LCD_MENU4 = 0x2bb,
|
||||
KEY_KBD_LCD_MENU5 = 0x2bc,
|
||||
BTN_TRIGGER_HAPPY = 0x2c0,
|
||||
BTN_TRIGGER_HAPPY1 = 0x2c0,
|
||||
BTN_TRIGGER_HAPPY2 = 0x2c1,
|
||||
BTN_TRIGGER_HAPPY3 = 0x2c2,
|
||||
BTN_TRIGGER_HAPPY4 = 0x2c3,
|
||||
BTN_TRIGGER_HAPPY5 = 0x2c4,
|
||||
BTN_TRIGGER_HAPPY6 = 0x2c5,
|
||||
BTN_TRIGGER_HAPPY7 = 0x2c6,
|
||||
BTN_TRIGGER_HAPPY8 = 0x2c7,
|
||||
BTN_TRIGGER_HAPPY9 = 0x2c8,
|
||||
BTN_TRIGGER_HAPPY10 = 0x2c9,
|
||||
BTN_TRIGGER_HAPPY11 = 0x2ca,
|
||||
BTN_TRIGGER_HAPPY12 = 0x2cb,
|
||||
BTN_TRIGGER_HAPPY13 = 0x2cc,
|
||||
BTN_TRIGGER_HAPPY14 = 0x2cd,
|
||||
BTN_TRIGGER_HAPPY15 = 0x2ce,
|
||||
BTN_TRIGGER_HAPPY16 = 0x2cf,
|
||||
BTN_TRIGGER_HAPPY17 = 0x2d0,
|
||||
BTN_TRIGGER_HAPPY18 = 0x2d1,
|
||||
BTN_TRIGGER_HAPPY19 = 0x2d2,
|
||||
BTN_TRIGGER_HAPPY20 = 0x2d3,
|
||||
BTN_TRIGGER_HAPPY21 = 0x2d4,
|
||||
BTN_TRIGGER_HAPPY22 = 0x2d5,
|
||||
BTN_TRIGGER_HAPPY23 = 0x2d6,
|
||||
BTN_TRIGGER_HAPPY24 = 0x2d7,
|
||||
BTN_TRIGGER_HAPPY25 = 0x2d8,
|
||||
BTN_TRIGGER_HAPPY26 = 0x2d9,
|
||||
BTN_TRIGGER_HAPPY27 = 0x2da,
|
||||
BTN_TRIGGER_HAPPY28 = 0x2db,
|
||||
BTN_TRIGGER_HAPPY29 = 0x2dc,
|
||||
BTN_TRIGGER_HAPPY30 = 0x2dd,
|
||||
BTN_TRIGGER_HAPPY31 = 0x2de,
|
||||
BTN_TRIGGER_HAPPY32 = 0x2df,
|
||||
BTN_TRIGGER_HAPPY33 = 0x2e0,
|
||||
BTN_TRIGGER_HAPPY34 = 0x2e1,
|
||||
BTN_TRIGGER_HAPPY35 = 0x2e2,
|
||||
BTN_TRIGGER_HAPPY36 = 0x2e3,
|
||||
BTN_TRIGGER_HAPPY37 = 0x2e4,
|
||||
BTN_TRIGGER_HAPPY38 = 0x2e5,
|
||||
BTN_TRIGGER_HAPPY39 = 0x2e6,
|
||||
BTN_TRIGGER_HAPPY40 = 0x2e7,
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
namespace Artemis.UI.Linux.Providers.Input
|
||||
{
|
||||
public enum LinuxRelativeAxis
|
||||
{
|
||||
REL_X = 0x00,
|
||||
REL_Y = 0x01,
|
||||
REL_Z = 0x02,
|
||||
REL_RX = 0x03,
|
||||
REL_RY = 0x04,
|
||||
REL_RZ = 0x05,
|
||||
REL_HWHEEL = 0x06,
|
||||
REL_DIAL = 0x07,
|
||||
REL_WHEEL = 0x08,
|
||||
REL_MISC = 0x09,
|
||||
}
|
||||
}
|
||||
@@ -1,621 +0,0 @@
|
||||
using System;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Linux.Providers.Input;
|
||||
|
||||
namespace Artemis.UI.Linux.Utilities
|
||||
{
|
||||
public static class InputUtilities
|
||||
{
|
||||
public static KeyboardKey KeyFromKeyCode(LinuxKeyboardKeyCodes code) => code switch
|
||||
{
|
||||
LinuxKeyboardKeyCodes.KEY_ESC => KeyboardKey.Escape,
|
||||
LinuxKeyboardKeyCodes.KEY_1 => KeyboardKey.D1,
|
||||
LinuxKeyboardKeyCodes.KEY_2 => KeyboardKey.D2,
|
||||
LinuxKeyboardKeyCodes.KEY_3 => KeyboardKey.D3,
|
||||
LinuxKeyboardKeyCodes.KEY_4 => KeyboardKey.D4,
|
||||
LinuxKeyboardKeyCodes.KEY_5 => KeyboardKey.D5,
|
||||
LinuxKeyboardKeyCodes.KEY_6 => KeyboardKey.D6,
|
||||
LinuxKeyboardKeyCodes.KEY_7 => KeyboardKey.D7,
|
||||
LinuxKeyboardKeyCodes.KEY_8 => KeyboardKey.D8,
|
||||
LinuxKeyboardKeyCodes.KEY_9 => KeyboardKey.D9,
|
||||
LinuxKeyboardKeyCodes.KEY_0 => KeyboardKey.D0,
|
||||
LinuxKeyboardKeyCodes.KEY_MINUS => KeyboardKey.OemMinus,
|
||||
LinuxKeyboardKeyCodes.KEY_EQUAL => KeyboardKey.OemPlus,
|
||||
LinuxKeyboardKeyCodes.KEY_BACKSPACE => KeyboardKey.Back,
|
||||
LinuxKeyboardKeyCodes.KEY_TAB => KeyboardKey.Tab,
|
||||
LinuxKeyboardKeyCodes.KEY_Q => KeyboardKey.Q,
|
||||
LinuxKeyboardKeyCodes.KEY_W => KeyboardKey.W,
|
||||
LinuxKeyboardKeyCodes.KEY_E => KeyboardKey.E,
|
||||
LinuxKeyboardKeyCodes.KEY_R => KeyboardKey.R,
|
||||
LinuxKeyboardKeyCodes.KEY_T => KeyboardKey.T,
|
||||
LinuxKeyboardKeyCodes.KEY_Y => KeyboardKey.Y,
|
||||
LinuxKeyboardKeyCodes.KEY_U => KeyboardKey.U,
|
||||
LinuxKeyboardKeyCodes.KEY_I => KeyboardKey.I,
|
||||
LinuxKeyboardKeyCodes.KEY_O => KeyboardKey.O,
|
||||
LinuxKeyboardKeyCodes.KEY_P => KeyboardKey.P,
|
||||
LinuxKeyboardKeyCodes.KEY_LEFTBRACE => KeyboardKey.OemOpenBrackets,
|
||||
LinuxKeyboardKeyCodes.KEY_RIGHTBRACE => KeyboardKey.OemCloseBrackets,
|
||||
LinuxKeyboardKeyCodes.KEY_ENTER => KeyboardKey.Enter,
|
||||
LinuxKeyboardKeyCodes.KEY_LEFTCTRL => KeyboardKey.LeftCtrl,
|
||||
LinuxKeyboardKeyCodes.KEY_A => KeyboardKey.A,
|
||||
LinuxKeyboardKeyCodes.KEY_S => KeyboardKey.S,
|
||||
LinuxKeyboardKeyCodes.KEY_D => KeyboardKey.D,
|
||||
LinuxKeyboardKeyCodes.KEY_F => KeyboardKey.F,
|
||||
LinuxKeyboardKeyCodes.KEY_G => KeyboardKey.G,
|
||||
LinuxKeyboardKeyCodes.KEY_H => KeyboardKey.H,
|
||||
LinuxKeyboardKeyCodes.KEY_J => KeyboardKey.J,
|
||||
LinuxKeyboardKeyCodes.KEY_K => KeyboardKey.K,
|
||||
LinuxKeyboardKeyCodes.KEY_L => KeyboardKey.L,
|
||||
LinuxKeyboardKeyCodes.KEY_SEMICOLON => KeyboardKey.OemSemicolon,
|
||||
LinuxKeyboardKeyCodes.KEY_APOSTROPHE => KeyboardKey.OemQuotes,
|
||||
LinuxKeyboardKeyCodes.KEY_GRAVE => KeyboardKey.OemTilde,
|
||||
LinuxKeyboardKeyCodes.KEY_LEFTSHIFT => KeyboardKey.LeftShift,
|
||||
LinuxKeyboardKeyCodes.KEY_BACKSLASH => KeyboardKey.OemBackslash,
|
||||
LinuxKeyboardKeyCodes.KEY_Z => KeyboardKey.Z,
|
||||
LinuxKeyboardKeyCodes.KEY_X => KeyboardKey.X,
|
||||
LinuxKeyboardKeyCodes.KEY_C => KeyboardKey.C,
|
||||
LinuxKeyboardKeyCodes.KEY_V => KeyboardKey.V,
|
||||
LinuxKeyboardKeyCodes.KEY_B => KeyboardKey.B,
|
||||
LinuxKeyboardKeyCodes.KEY_N => KeyboardKey.N,
|
||||
LinuxKeyboardKeyCodes.KEY_M => KeyboardKey.M,
|
||||
LinuxKeyboardKeyCodes.KEY_COMMA => KeyboardKey.OemComma,
|
||||
LinuxKeyboardKeyCodes.KEY_DOT => KeyboardKey.OemPeriod,
|
||||
LinuxKeyboardKeyCodes.KEY_SLASH => KeyboardKey.OemQuestion,
|
||||
LinuxKeyboardKeyCodes.KEY_RIGHTSHIFT => KeyboardKey.RightShift,
|
||||
//LinuxKeyboardKeyCodes.KEY_KPASTERISK => ,
|
||||
LinuxKeyboardKeyCodes.KEY_LEFTALT => KeyboardKey.LeftAlt,
|
||||
LinuxKeyboardKeyCodes.KEY_SPACE => KeyboardKey.Space,
|
||||
LinuxKeyboardKeyCodes.KEY_CAPSLOCK => KeyboardKey.CapsLock,
|
||||
LinuxKeyboardKeyCodes.KEY_F1 => KeyboardKey.F1,
|
||||
LinuxKeyboardKeyCodes.KEY_F2 => KeyboardKey.F2,
|
||||
LinuxKeyboardKeyCodes.KEY_F3 => KeyboardKey.F3,
|
||||
LinuxKeyboardKeyCodes.KEY_F4 => KeyboardKey.F4,
|
||||
LinuxKeyboardKeyCodes.KEY_F5 => KeyboardKey.F5,
|
||||
LinuxKeyboardKeyCodes.KEY_F6 => KeyboardKey.F6,
|
||||
LinuxKeyboardKeyCodes.KEY_F7 => KeyboardKey.F7,
|
||||
LinuxKeyboardKeyCodes.KEY_F8 => KeyboardKey.F8,
|
||||
LinuxKeyboardKeyCodes.KEY_F9 => KeyboardKey.F9,
|
||||
LinuxKeyboardKeyCodes.KEY_F10 => KeyboardKey.F10,
|
||||
LinuxKeyboardKeyCodes.KEY_NUMLOCK => KeyboardKey.NumLock,
|
||||
LinuxKeyboardKeyCodes.KEY_SCROLLLOCK => KeyboardKey.Scroll,
|
||||
LinuxKeyboardKeyCodes.KEY_KP7 => KeyboardKey.NumPad7,
|
||||
LinuxKeyboardKeyCodes.KEY_KP8 => KeyboardKey.NumPad8,
|
||||
LinuxKeyboardKeyCodes.KEY_KP9 => KeyboardKey.NumPad9,
|
||||
LinuxKeyboardKeyCodes.KEY_KPMINUS => KeyboardKey.Subtract,
|
||||
LinuxKeyboardKeyCodes.KEY_KP4 => KeyboardKey.NumPad4,
|
||||
LinuxKeyboardKeyCodes.KEY_KP5 => KeyboardKey.NumPad5,
|
||||
LinuxKeyboardKeyCodes.KEY_KP6 => KeyboardKey.NumPad6,
|
||||
LinuxKeyboardKeyCodes.KEY_KPPLUS => KeyboardKey.Add,
|
||||
LinuxKeyboardKeyCodes.KEY_KP1 => KeyboardKey.NumPad1,
|
||||
LinuxKeyboardKeyCodes.KEY_KP2 => KeyboardKey.NumPad2,
|
||||
LinuxKeyboardKeyCodes.KEY_KP3 => KeyboardKey.NumPad3,
|
||||
LinuxKeyboardKeyCodes.KEY_KP0 => KeyboardKey.NumPad0,
|
||||
LinuxKeyboardKeyCodes.KEY_KPDOT => KeyboardKey.Decimal,
|
||||
// LinuxKeyboardKeyCodes.KEY_ZENKAKUHANKAKU => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_102ND => expr,
|
||||
LinuxKeyboardKeyCodes.KEY_F11 => KeyboardKey.F11,
|
||||
LinuxKeyboardKeyCodes.KEY_F12 => KeyboardKey.F12,
|
||||
//LinuxKeyboardKeyCodes.KEY_RO => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_KATAKANA => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_HIRAGANA => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_HENKAN => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_KATAKANAHIRAGANA => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MUHENKAN => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_KPJPCOMMA => expr,
|
||||
LinuxKeyboardKeyCodes.KEY_KPENTER => KeyboardKey.NumPadEnter,
|
||||
LinuxKeyboardKeyCodes.KEY_RIGHTCTRL => KeyboardKey.RightCtrl,
|
||||
LinuxKeyboardKeyCodes.KEY_KPSLASH => KeyboardKey.Divide,
|
||||
LinuxKeyboardKeyCodes.KEY_SYSRQ => KeyboardKey.System,//TODO:?
|
||||
LinuxKeyboardKeyCodes.KEY_RIGHTALT => KeyboardKey.RightAlt,
|
||||
LinuxKeyboardKeyCodes.KEY_LINEFEED => KeyboardKey.LineFeed,
|
||||
LinuxKeyboardKeyCodes.KEY_HOME => KeyboardKey.Home,
|
||||
LinuxKeyboardKeyCodes.KEY_UP => KeyboardKey.Up,
|
||||
LinuxKeyboardKeyCodes.KEY_PAGEUP => KeyboardKey.PageUp,
|
||||
LinuxKeyboardKeyCodes.KEY_LEFT => KeyboardKey.Left,
|
||||
LinuxKeyboardKeyCodes.KEY_RIGHT => KeyboardKey.Right,
|
||||
LinuxKeyboardKeyCodes.KEY_END => KeyboardKey.End,
|
||||
LinuxKeyboardKeyCodes.KEY_DOWN => KeyboardKey.Down,
|
||||
LinuxKeyboardKeyCodes.KEY_PAGEDOWN => KeyboardKey.PageDown,
|
||||
LinuxKeyboardKeyCodes.KEY_INSERT => KeyboardKey.Insert,
|
||||
LinuxKeyboardKeyCodes.KEY_DELETE => KeyboardKey.Delete,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO => KeyboardKey.macro,
|
||||
LinuxKeyboardKeyCodes.KEY_MUTE => KeyboardKey.VolumeMute,
|
||||
LinuxKeyboardKeyCodes.KEY_VOLUMEDOWN => KeyboardKey.VolumeDown,
|
||||
LinuxKeyboardKeyCodes.KEY_VOLUMEUP => KeyboardKey.VolumeUp,
|
||||
// LinuxKeyboardKeyCodes.KEY_POWER => KeyboardKey.power,
|
||||
LinuxKeyboardKeyCodes.KEY_KPEQUAL => KeyboardKey.NumPadEnter,//todo: ?
|
||||
// LinuxKeyboardKeyCodes.KEY_KPPLUSMINUS => KeyboardKey.numpad,
|
||||
LinuxKeyboardKeyCodes.KEY_PAUSE => KeyboardKey.Pause,
|
||||
// LinuxKeyboardKeyCodes.KEY_SCALE => KeyboardKey.scal,
|
||||
// LinuxKeyboardKeyCodes.KEY_KPCOMMA => KeyboardKey.ke,
|
||||
// LinuxKeyboardKeyCodes.KEY_HANGEUL => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_HANJA => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_YEN => expr,
|
||||
LinuxKeyboardKeyCodes.KEY_LEFTMETA => KeyboardKey.LWin,
|
||||
LinuxKeyboardKeyCodes.KEY_RIGHTMETA => KeyboardKey.RWin,
|
||||
// LinuxKeyboardKeyCodes.KEY_COMPOSE => ,
|
||||
LinuxKeyboardKeyCodes.KEY_STOP => KeyboardKey.MediaStop,
|
||||
// LinuxKeyboardKeyCodes.KEY_AGAIN => ,
|
||||
// LinuxKeyboardKeyCodes.KEY_PROPS => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_UNDO => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_FRONT => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_COPY => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_OPEN => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_PASTE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_FIND => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_CUT => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_HELP => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MENU => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_CALC => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_SETUP => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_SLEEP => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_WAKEUP => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_FILE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_SENDFILE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_DELETEFILE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_XFER => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_PROG1 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_PROG2 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_WWW => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MSDOS => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_COFFEE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_ROTATE_DISPLAY => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_CYCLEWINDOWS => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MAIL => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_BOOKMARKS => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_COMPUTER => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_BACK => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_FORWARD => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_CLOSECD => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_EJECTCD => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_EJECTCLOSECD => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_NEXTSONG => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_PLAYPAUSE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_PREVIOUSSONG => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_STOPCD => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_RECORD => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_REWIND => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_PHONE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_ISO => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_CONFIG => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_HOMEPAGE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_REFRESH => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_EXIT => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MOVE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_EDIT => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_SCROLLUP => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_SCROLLDOWN => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_KPLEFTPAREN => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_KPRIGHTPAREN => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_NEW => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_REDO => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_F13 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_F14 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_F15 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_F16 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_F17 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_F18 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_F19 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_F20 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_F21 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_F22 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_F23 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_F24 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_PLAYCD => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_PAUSECD => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_PROG3 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_PROG4 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_DASHBOARD => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_SUSPEND => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_CLOSE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_PLAY => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_FASTFORWARD => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_BASSBOOST => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_PRINT => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_HP => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_CAMERA => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_SOUND => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_QUESTION => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_EMAIL => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_CHAT => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_SEARCH => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_CONNECT => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_FINANCE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_SPORT => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_SHOP => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_ALTERASE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_CANCEL => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_BRIGHTNESSDOWN => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_BRIGHTNESSUP => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MEDIA => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_SWITCHVIDEOMODE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_KBDILLUMTOGGLE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_KBDILLUMDOWN => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_KBDILLUMUP => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_SEND => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_REPLY => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_FORWARDMAIL => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_SAVE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_DOCUMENTS => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_BATTERY => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_BLUETOOTH => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_WLAN => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_UWB => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_UNKNOWN => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_VIDEO_NEXT => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_VIDEO_PREV => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_BRIGHTNESS_CYCLE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_BRIGHTNESS_AUTO => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_WWAN => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_RFKILL => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MICMUTE => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_MISC => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_1 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_2 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_3 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_4 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_5 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_6 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_7 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_8 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_9 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_MOUSE => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_RIGHT => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_MIDDLE => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_SIDE => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_EXTRA => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_FORWARD => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_BACK => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TASK => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_JOYSTICK => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_THUMB => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_THUMB2 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TOP => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TOP2 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_PINKIE => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_BASE => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_BASE2 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_BASE3 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_BASE4 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_BASE5 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_BASE6 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_DEAD => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_GAMEPAD => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_EAST => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_C => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_NORTH => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_WEST => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_Z => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TL => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TR => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TL2 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TR2 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_SELECT => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_START => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_MODE => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_THUMBL => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_THUMBR => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_DIGI => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TOOL_RUBBER => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TOOL_BRUSH => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TOOL_PENCIL => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TOOL_AIRBRUSH => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TOOL_FINGER => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TOOL_MOUSE => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TOOL_LENS => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TOOL_QUINTTAP => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_STYLUS3 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TOUCH => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_STYLUS => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_STYLUS2 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TOOL_DOUBLETAP => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TOOL_TRIPLETAP => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TOOL_QUADTAP => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_WHEEL => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_GEAR_UP => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_OK => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_SELECT => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_GOTO => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_CLEAR => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_POWER2 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_OPTION => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_INFO => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_TIME => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_VENDOR => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_ARCHIVE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_PROGRAM => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_CHANNEL => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_FAVORITES => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_EPG => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_PVR => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MHP => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_LANGUAGE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_TITLE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_SUBTITLE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_ANGLE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_FULL_SCREEN => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MODE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_KEYBOARD => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_ASPECT_RATIO => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_PC => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_TV => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_TV2 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_VCR => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_VCR2 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_SAT => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_SAT2 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_CD => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_TAPE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_RADIO => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_TUNER => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_PLAYER => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_TEXT => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_DVD => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_AUX => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MP3 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_AUDIO => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_VIDEO => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_DIRECTORY => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_LIST => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MEMO => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_CALENDAR => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_RED => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_GREEN => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_YELLOW => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_BLUE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_CHANNELUP => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_CHANNELDOWN => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_FIRST => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_LAST => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_AB => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_NEXT => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_RESTART => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_SLOW => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_SHUFFLE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_BREAK => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_PREVIOUS => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_DIGITS => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_TEEN => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_TWEN => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_VIDEOPHONE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_GAMES => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_ZOOMIN => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_ZOOMOUT => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_ZOOMRESET => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_WORDPROCESSOR => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_EDITOR => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_SPREADSHEET => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_GRAPHICSEDITOR => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_PRESENTATION => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_DATABASE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_NEWS => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_VOICEMAIL => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_ADDRESSBOOK => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MESSENGER => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_DISPLAYTOGGLE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_SPELLCHECK => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_LOGOFF => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_DOLLAR => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_EURO => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_FRAMEBACK => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_FRAMEFORWARD => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_CONTEXT_MENU => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MEDIA_REPEAT => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_10CHANNELSUP => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_10CHANNELSDOWN => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_IMAGES => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_NOTIFICATION_CENTER => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_PICKUP_PHONE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_HANGUP_PHONE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_DEL_EOL => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_DEL_EOS => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_INS_LINE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_DEL_LINE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_FN => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_FN_ESC => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_FN_F1 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_FN_F2 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_FN_F3 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_FN_F4 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_FN_F5 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_FN_F6 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_FN_F7 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_FN_F8 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_FN_F9 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_FN_F10 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_FN_F11 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_FN_F12 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_FN_1 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_FN_2 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_FN_D => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_FN_E => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_FN_F => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_FN_S => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_FN_B => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_FN_RIGHT_SHIFT => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_BRL_DOT1 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_BRL_DOT2 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_BRL_DOT3 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_BRL_DOT4 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_BRL_DOT5 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_BRL_DOT6 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_BRL_DOT7 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_BRL_DOT8 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_BRL_DOT9 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_BRL_DOT10 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_NUMERIC_0 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_NUMERIC_1 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_NUMERIC_2 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_NUMERIC_3 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_NUMERIC_4 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_NUMERIC_5 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_NUMERIC_6 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_NUMERIC_7 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_NUMERIC_8 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_NUMERIC_9 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_NUMERIC_STAR => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_NUMERIC_POUND => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_NUMERIC_A => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_NUMERIC_B => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_NUMERIC_C => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_NUMERIC_D => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_CAMERA_FOCUS => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_WPS_BUTTON => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_TOUCHPAD_TOGGLE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_TOUCHPAD_ON => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_TOUCHPAD_OFF => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_CAMERA_ZOOMIN => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_CAMERA_ZOOMOUT => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_CAMERA_UP => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_CAMERA_DOWN => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_CAMERA_LEFT => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_CAMERA_RIGHT => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_ATTENDANT_ON => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_ATTENDANT_OFF => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_ATTENDANT_TOGGLE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_LIGHTS_TOGGLE => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_DPAD_UP => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_DPAD_DOWN => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_DPAD_LEFT => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_DPAD_RIGHT => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_ALS_TOGGLE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_ROTATE_LOCK_TOGGLE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_BUTTONCONFIG => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_TASKMANAGER => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_JOURNAL => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_CONTROLPANEL => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_APPSELECT => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_SCREENSAVER => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_VOICECOMMAND => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_ASSISTANT => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_KBD_LAYOUT_NEXT => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_EMOJI_PICKER => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_BRIGHTNESS_MIN => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_BRIGHTNESS_MAX => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_KBDINPUTASSIST_PREV => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_KBDINPUTASSIST_NEXT => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_KBDINPUTASSIST_PREVGROUP => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_KBDINPUTASSIST_NEXTGROUP => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_KBDINPUTASSIST_ACCEPT => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_KBDINPUTASSIST_CANCEL => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_RIGHT_UP => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_RIGHT_DOWN => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_LEFT_UP => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_LEFT_DOWN => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_ROOT_MENU => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MEDIA_TOP_MENU => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_NUMERIC_11 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_NUMERIC_12 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_AUDIO_DESC => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_3D_MODE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_NEXT_FAVORITE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_STOP_RECORD => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_PAUSE_RECORD => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_VOD => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_UNMUTE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_FASTREVERSE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_SLOWREVERSE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_DATA => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_ONSCREEN_KEYBOARD => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_PRIVACY_SCREEN_TOGGLE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_SELECTIVE_SCREENSHOT => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO1 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO2 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO3 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO4 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO5 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO6 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO7 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO8 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO9 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO10 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO11 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO12 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO13 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO14 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO15 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO16 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO17 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO18 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO19 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO20 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO21 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO22 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO23 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO24 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO25 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO26 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO27 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO28 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO29 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO30 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO_RECORD_START => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO_RECORD_STOP => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO_PRESET_CYCLE => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO_PRESET1 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO_PRESET2 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_MACRO_PRESET3 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_KBD_LCD_MENU1 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_KBD_LCD_MENU2 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_KBD_LCD_MENU3 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_KBD_LCD_MENU4 => expr,
|
||||
// LinuxKeyboardKeyCodes.KEY_KBD_LCD_MENU5 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY2 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY3 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY4 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY5 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY6 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY7 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY8 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY9 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY10 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY11 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY12 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY13 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY14 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY15 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY16 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY17 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY18 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY19 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY20 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY21 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY22 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY23 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY24 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY25 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY26 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY27 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY28 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY29 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY30 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY31 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY32 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY33 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY34 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY35 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY36 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY37 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY38 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY39 => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TRIGGER_HAPPY40 => expr,
|
||||
_ => KeyboardKey.None
|
||||
};
|
||||
|
||||
|
||||
public static MouseButton MouseButtonFromButtonCode(LinuxKeyboardKeyCodes eCode) => eCode switch
|
||||
{
|
||||
LinuxKeyboardKeyCodes.BTN_LEFT => MouseButton.Left,
|
||||
LinuxKeyboardKeyCodes.BTN_RIGHT => MouseButton.Right,
|
||||
LinuxKeyboardKeyCodes.BTN_MIDDLE => MouseButton.Middle,
|
||||
//todo: figure these out
|
||||
// LinuxKeyboardKeyCodes.BTN_SIDE => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_EXTRA => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_FORWARD => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_BACK => expr,
|
||||
// LinuxKeyboardKeyCodes.BTN_TASK => expr,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(eCode), eCode, null)
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,454 +0,0 @@
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Mono auto generated files
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Ww][Ii][Nn]32/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
[Ll]ogs/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUnit
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
nunit-*.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# Tye
|
||||
.tye/
|
||||
|
||||
# ASP.NET Scaffolding
|
||||
ScaffoldingReadMe.txt
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Coverlet is a free, cross platform Code Coverage Tool
|
||||
coverage*.json
|
||||
coverage*.xml
|
||||
coverage*.info
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# NuGet Symbol Packages
|
||||
*.snupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
*.appxbundle
|
||||
*.appxupload
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- [Bb]ackup.rdl
|
||||
*- [Bb]ackup ([0-9]).rdl
|
||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
|
||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||
MigrationBackup/
|
||||
|
||||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
.ionide/
|
||||
|
||||
# Fody - auto-generated XML schema
|
||||
FodyWeavers.xsd
|
||||
|
||||
##
|
||||
## Visual studio for Mac
|
||||
##
|
||||
|
||||
|
||||
# globs
|
||||
Makefile.in
|
||||
*.userprefs
|
||||
*.usertasks
|
||||
config.make
|
||||
config.status
|
||||
aclocal.m4
|
||||
install-sh
|
||||
autom4te.cache/
|
||||
*.tar.gz
|
||||
tarballs/
|
||||
test-results/
|
||||
|
||||
# Mac bundle stuff
|
||||
*.dmg
|
||||
*.app
|
||||
|
||||
# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
|
||||
# Windows thumbnail cache files
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
ehthumbs_vista.db
|
||||
|
||||
# Dump file
|
||||
*.stackdump
|
||||
|
||||
# Folder config file
|
||||
[Dd]esktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msix
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
# JetBrains Rider
|
||||
.idea/
|
||||
*.sln.iml
|
||||
|
||||
##
|
||||
## Visual Studio Code
|
||||
##
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
@@ -1,12 +0,0 @@
|
||||
<Application xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:ui="clr-namespace:Artemis.UI;assembly=Artemis.UI"
|
||||
x:Class="Artemis.UI.MacOS.App">
|
||||
<Application.DataTemplates>
|
||||
<ui:ViewLocator/>
|
||||
</Application.DataTemplates>
|
||||
|
||||
<Application.Styles>
|
||||
<StyleInclude Source="avares://Artemis.UI/Styles/Artemis.axaml" />
|
||||
</Application.Styles>
|
||||
</Application>
|
||||
@@ -1,22 +0,0 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Threading;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.MacOS
|
||||
{
|
||||
public class App : Application
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
ArtemisBootstrapper.Bootstrap(this);
|
||||
RxApp.MainThreadScheduler = AvaloniaScheduler.Instance;
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public override void OnFrameworkInitializationCompleted()
|
||||
{
|
||||
ArtemisBootstrapper.Initialize();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<AvaloniaResource Include="Assets\**" />
|
||||
<None Remove=".gitignore" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="0.10.13" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="0.10.13" />
|
||||
<!--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.13" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.13" />
|
||||
<PackageReference Include="ReactiveUI" Version="17.1.50" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Artemis.Core\Artemis.Core.csproj" />
|
||||
<ProjectReference Include="..\Artemis.UI\Artemis.UI.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 172 KiB |
@@ -1,24 +0,0 @@
|
||||
using System;
|
||||
using Avalonia;
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.MacOS
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
// Initialization code. Don't use any Avalonia, third-party APIs or any
|
||||
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
|
||||
// yet and stuff might break.
|
||||
[STAThread]
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
|
||||
}
|
||||
|
||||
// Avalonia configuration, don't remove; also used by visual designer.
|
||||
public static AppBuilder BuildAvaloniaApp()
|
||||
{
|
||||
return AppBuilder.Configure<App>().UsePlatformDetect().LogToTrace().UseReactiveUI();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
<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">
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Cwindowservice/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
@@ -1,506 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<doc>
|
||||
<assembly>
|
||||
<name>Artemis.UI.Shared</name>
|
||||
</assembly>
|
||||
<members>
|
||||
<member name="F:Artemis.UI.Avalonia.Shared.Controls.ArtemisIcon.IconProperty">
|
||||
<summary>
|
||||
Gets or sets the currently displayed icon as either a <see cref="T:Material.Icons.MaterialIconKind" /> or an <see cref="T:System.Uri" /> pointing
|
||||
to an SVG
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Avalonia.Shared.Controls.ArtemisIcon.Icon">
|
||||
<summary>
|
||||
Gets or sets the currently displayed icon as either a <see cref="T:Material.Icons.MaterialIconKind" /> or an <see cref="T:System.Uri" /> pointing
|
||||
to an SVG
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Avalonia.Shared.Controls.DeviceVisualizer">
|
||||
<summary>
|
||||
Visualizes an <see cref="T:Artemis.Core.ArtemisDevice" /> with optional per-LED colors
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Controls.DeviceVisualizer.#ctor">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Controls.DeviceVisualizer.Render(Avalonia.Media.DrawingContext)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="E:Artemis.UI.Avalonia.Shared.Controls.DeviceVisualizer.LedClicked">
|
||||
<summary>
|
||||
Occurs when a LED of the device has been clicked
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Controls.DeviceVisualizer.OnLedClicked(Artemis.UI.Avalonia.Shared.Events.LedClickedEventArgs)">
|
||||
<summary>
|
||||
Invokes the <see cref="E:Artemis.UI.Avalonia.Shared.Controls.DeviceVisualizer.LedClicked" /> event
|
||||
</summary>
|
||||
<param name="e"></param>
|
||||
</member>
|
||||
<member name="F:Artemis.UI.Avalonia.Shared.Controls.DeviceVisualizer.DeviceProperty">
|
||||
<summary>
|
||||
Gets or sets the <see cref="T:Artemis.Core.ArtemisDevice" /> to display
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Avalonia.Shared.Controls.DeviceVisualizer.Device">
|
||||
<summary>
|
||||
Gets or sets the <see cref="T:Artemis.Core.ArtemisDevice" /> to display
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:Artemis.UI.Avalonia.Shared.Controls.DeviceVisualizer.ShowColorsProperty">
|
||||
<summary>
|
||||
Gets or sets boolean indicating whether or not to show per-LED colors
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Avalonia.Shared.Controls.DeviceVisualizer.ShowColors">
|
||||
<summary>
|
||||
Gets or sets a boolean indicating whether or not to show per-LED colors
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:Artemis.UI.Avalonia.Shared.Controls.DeviceVisualizer.HighlightedLedsProperty">
|
||||
<summary>
|
||||
Gets or sets a list of LEDs to highlight
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Avalonia.Shared.Controls.DeviceVisualizer.HighlightedLeds">
|
||||
<summary>
|
||||
Gets or sets a list of LEDs to highlight
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Controls.DeviceVisualizer.OnAttachedToLogicalTree(Avalonia.LogicalTree.LogicalTreeAttachmentEventArgs)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Controls.DeviceVisualizer.OnDetachedFromLogicalTree(Avalonia.LogicalTree.LogicalTreeAttachmentEventArgs)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Controls.DeviceVisualizer.MeasureOverride(Avalonia.Size)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="F:Artemis.UI.Avalonia.Shared.Controls.EnumComboBox.ValueProperty">
|
||||
<summary>
|
||||
Gets or sets the currently selected value
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Avalonia.Shared.Controls.EnumComboBox.Value">
|
||||
<summary>
|
||||
Gets or sets the currently selected value
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Controls.EnumComboBox.OnAttachedToLogicalTree(Avalonia.LogicalTree.LogicalTreeAttachmentEventArgs)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Controls.EnumComboBox.OnDetachedFromLogicalTree(Avalonia.LogicalTree.LogicalTreeAttachmentEventArgs)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="F:Artemis.UI.Avalonia.Shared.Controls.ProfileConfigurationIcon.ConfigurationIconProperty">
|
||||
<summary>
|
||||
Gets or sets the <see cref="T:Artemis.Core.ProfileConfigurationIcon" /> to display
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Avalonia.Shared.Controls.ProfileConfigurationIcon.ConfigurationIcon">
|
||||
<summary>
|
||||
Gets or sets the <see cref="T:Artemis.Core.ProfileConfigurationIcon" /> to display
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Avalonia.Shared.Controls.SelectionRectangle">
|
||||
<summary>
|
||||
Visualizes an <see cref="T:Artemis.Core.ArtemisDevice" /> with optional per-LED colors
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:Artemis.UI.Avalonia.Shared.Controls.SelectionRectangle.BackgroundProperty">
|
||||
<summary>
|
||||
Defines the <see cref="P:Artemis.UI.Avalonia.Shared.Controls.SelectionRectangle.Background" /> property.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:Artemis.UI.Avalonia.Shared.Controls.SelectionRectangle.BorderBrushProperty">
|
||||
<summary>
|
||||
Defines the <see cref="P:Artemis.UI.Avalonia.Shared.Controls.SelectionRectangle.BorderBrush" /> property.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:Artemis.UI.Avalonia.Shared.Controls.SelectionRectangle.BorderThicknessProperty">
|
||||
<summary>
|
||||
Defines the <see cref="P:Artemis.UI.Avalonia.Shared.Controls.SelectionRectangle.BorderBrush" /> property.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:Artemis.UI.Avalonia.Shared.Controls.SelectionRectangle.InputElementProperty">
|
||||
<summary>
|
||||
Defines the <see cref="M:Artemis.UI.Avalonia.Shared.Controls.SelectionRectangle.get_InputElement" /> property.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Controls.SelectionRectangle.#ctor">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Avalonia.Shared.Controls.SelectionRectangle.Background">
|
||||
<summary>
|
||||
Gets or sets a brush used to paint the control's background.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Avalonia.Shared.Controls.SelectionRectangle.BorderBrush">
|
||||
<summary>
|
||||
Gets or sets a brush used to paint the control's border
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Avalonia.Shared.Controls.SelectionRectangle.BorderThickness">
|
||||
<summary>
|
||||
Gets or sets the width of the control's border
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Controls.SelectionRectangle.OnAttachedToVisualTree(Avalonia.VisualTreeAttachmentEventArgs)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Controls.SelectionRectangle.OnDetachedFromVisualTree(Avalonia.VisualTreeAttachmentEventArgs)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Avalonia.Shared.Converters.ColorToSKColorConverter">
|
||||
<summary>
|
||||
Converts <see cref="T:Avalonia.Media.Color" /> into <see cref="T:SkiaSharp.SKColor" />.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Converters.ColorToSKColorConverter.Convert(System.Object,System.Type,System.Object,System.Globalization.CultureInfo)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Converters.ColorToSKColorConverter.ConvertBack(System.Object,System.Type,System.Object,System.Globalization.CultureInfo)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Avalonia.Shared.Converters.SKColorToColorConverter">
|
||||
<summary>
|
||||
Converts <see cref="T:SkiaSharp.SKColor" /> into <see cref="T:Avalonia.Media.Color" />.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Converters.SKColorToColorConverter.Convert(System.Object,System.Type,System.Object,System.Globalization.CultureInfo)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Converters.SKColorToColorConverter.ConvertBack(System.Object,System.Type,System.Object,System.Globalization.CultureInfo)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Avalonia.Shared.Events.DataModelInputDynamicEventArgs">
|
||||
<summary>
|
||||
Provides data about selection events raised by <see cref="!:DataModelDynamicViewModel" />
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Avalonia.Shared.Events.DataModelInputDynamicEventArgs.DataModelPath">
|
||||
<summary>
|
||||
Gets the data model path that was selected
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Avalonia.Shared.Events.DataModelInputStaticEventArgs">
|
||||
<summary>
|
||||
Provides data about submit events raised by <see cref="!:DataModelStaticViewModel" />
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Avalonia.Shared.Events.DataModelInputStaticEventArgs.Value">
|
||||
<summary>
|
||||
The value that was submitted
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Avalonia.Shared.Events.LedClickedEventArgs">
|
||||
<summary>
|
||||
Provides data on LED click events raised by the device visualizer
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Avalonia.Shared.Events.LedClickedEventArgs.Device">
|
||||
<summary>
|
||||
The device that was clicked
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Avalonia.Shared.Events.LedClickedEventArgs.Led">
|
||||
<summary>
|
||||
The LED that was clicked
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Avalonia.Shared.Events.ProfileConfigurationEventArgs">
|
||||
<summary>
|
||||
Provides data on profile related events raised by the profile editor
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Avalonia.Shared.Events.ProfileConfigurationEventArgs.ProfileConfiguration">
|
||||
<summary>
|
||||
Gets the profile the event was raised for
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Avalonia.Shared.Events.ProfileConfigurationEventArgs.PreviousProfileConfiguration">
|
||||
<summary>
|
||||
If applicable, the previous active profile before the event was raised
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Avalonia.Shared.Events.RenderProfileElementEventArgs">
|
||||
<summary>
|
||||
Provides data on profile element related events raised by the profile editor
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Avalonia.Shared.Events.RenderProfileElementEventArgs.RenderProfileElement">
|
||||
<summary>
|
||||
Gets the profile element the event was raised for
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Avalonia.Shared.Events.RenderProfileElementEventArgs.PreviousRenderProfileElement">
|
||||
<summary>
|
||||
If applicable, the previous active profile element before the event was raised
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Avalonia.Shared.Exceptions.ArtemisSharedUIException">
|
||||
<summary>
|
||||
Represents errors that occur within the Artemis Shared UI library
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Avalonia.Shared.Ninject.SharedUIModule">
|
||||
<summary>
|
||||
The main <see cref="T:Ninject.Modules.NinjectModule" /> of the Artemis Shared UI toolkit that binds all services
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Ninject.SharedUIModule.Load">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Avalonia.Shared.PluginConfigurationDialog`1">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Avalonia.Shared.PluginConfigurationDialog`1.Type">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Avalonia.Shared.PluginConfigurationDialog">
|
||||
<summary>
|
||||
Describes a configuration dialog for a specific plugin
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Avalonia.Shared.PluginConfigurationDialog.Type">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Avalonia.Shared.PluginConfigurationViewModel">
|
||||
<summary>
|
||||
Represents a view model for a plugin configuration window
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.PluginConfigurationViewModel.#ctor(Artemis.Core.Plugin)">
|
||||
<summary>
|
||||
Creates a new instance of the <see cref="T:Artemis.UI.Avalonia.Shared.PluginConfigurationViewModel" /> class
|
||||
</summary>
|
||||
<param name="plugin"></param>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Avalonia.Shared.PluginConfigurationViewModel.Plugin">
|
||||
<summary>
|
||||
Gets the plugin this configuration view model is associated with
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.PluginConfigurationViewModel.Close">
|
||||
<summary>
|
||||
Closes the window hosting the view model
|
||||
</summary>
|
||||
</member>
|
||||
<member name="E:Artemis.UI.Avalonia.Shared.PluginConfigurationViewModel.CloseRequested">
|
||||
<summary>
|
||||
Occurs when the the window hosting the view model should close
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Avalonia.Shared.Services.Builders.FileDialogFilterBuilder">
|
||||
<summary>
|
||||
Represents a builder that can create a <see cref="T:Avalonia.Controls.FileDialogFilter" />.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Services.Builders.FileDialogFilterBuilder.WithName(System.String)">
|
||||
<summary>
|
||||
Sets the name of the filter
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Services.Builders.FileDialogFilterBuilder.WithExtension(System.String)">
|
||||
<summary>
|
||||
Adds the provided extension to the filter
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Services.Builders.NotificationBuilder.HavingButton(System.Action{Artemis.UI.Avalonia.Shared.Services.Builders.NotificationButtonBuilder})">
|
||||
<summary>
|
||||
Add a filter to the dialog
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Avalonia.Shared.Services.Builders.OpenFileDialogBuilder">
|
||||
<summary>
|
||||
Represents a builder that can create a <see cref="T:Avalonia.Controls.OpenFileDialog" />.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Services.Builders.OpenFileDialogBuilder.WithAllowMultiple">
|
||||
<summary>
|
||||
Indicate that the user can select multiple files.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Services.Builders.OpenFileDialogBuilder.WithTitle(System.String)">
|
||||
<summary>
|
||||
Set the title of the dialog
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Services.Builders.OpenFileDialogBuilder.WithDirectory(System.String)">
|
||||
<summary>
|
||||
Set the initial directory of the dialog
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Services.Builders.OpenFileDialogBuilder.WithInitialFileName(System.String)">
|
||||
<summary>
|
||||
Set the initial file name of the dialog
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Services.Builders.OpenFileDialogBuilder.HavingFilter(System.Action{Artemis.UI.Avalonia.Shared.Services.Builders.FileDialogFilterBuilder})">
|
||||
<summary>
|
||||
Add a filter to the dialog
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Services.Builders.OpenFileDialogBuilder.ShowAsync">
|
||||
<summary>
|
||||
Shows the file dialog
|
||||
</summary>
|
||||
<returns>
|
||||
A task that on completion returns an array containing the full path to the selected
|
||||
files, or null if the dialog was canceled.
|
||||
</returns>
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Avalonia.Shared.Services.Builders.SaveFileDialogBuilder">
|
||||
<summary>
|
||||
Represents a builder that can create a <see cref="T:Avalonia.Controls.SaveFileDialog" />.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Services.Builders.SaveFileDialogBuilder.WithTitle(System.String)">
|
||||
<summary>
|
||||
Set the title of the dialog
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Services.Builders.SaveFileDialogBuilder.WithDirectory(System.String)">
|
||||
<summary>
|
||||
Set the initial directory of the dialog
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Services.Builders.SaveFileDialogBuilder.WithInitialFileName(System.String)">
|
||||
<summary>
|
||||
Set the initial file name of the dialog
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Services.Builders.SaveFileDialogBuilder.WithDefaultExtension(System.String)">
|
||||
<summary>
|
||||
Set the default extension of the dialog
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Services.Builders.SaveFileDialogBuilder.HavingFilter(System.Action{Artemis.UI.Avalonia.Shared.Services.Builders.FileDialogFilterBuilder})">
|
||||
<summary>
|
||||
Add a filter to the dialog
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Services.Builders.SaveFileDialogBuilder.ShowAsync">
|
||||
<summary>
|
||||
Shows the save file dialog.
|
||||
</summary>
|
||||
<returns>
|
||||
A task that on completion contains the full path of the save location, or null if the
|
||||
dialog was canceled.
|
||||
</returns>
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Avalonia.Shared.Services.Interfaces.IArtemisSharedUIService">
|
||||
<summary>
|
||||
Represents a service provided by the Artemis Shared UI library
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Services.Interfaces.IWindowService.ShowWindow``1(System.ValueTuple{System.String,System.Object}[])">
|
||||
<summary>
|
||||
Creates a view model instance of type <typeparamref name="TViewModel" /> and shows its corresponding View as a window
|
||||
</summary>
|
||||
<typeparam name="TViewModel">The type of view model to create</typeparam>
|
||||
<returns>The created view model</returns>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Services.Interfaces.IWindowService.ShowWindow(System.Object)">
|
||||
<summary>
|
||||
Given a ViewModel, show its corresponding View as a window
|
||||
</summary>
|
||||
<param name="viewModel">ViewModel to show the View for</param>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Services.Interfaces.IWindowService.ShowExceptionDialog(System.String,System.Exception)">
|
||||
<summary>
|
||||
Shows a dialog displaying the given exception
|
||||
</summary>
|
||||
<param name="title">The title of the dialog</param>
|
||||
<param name="exception">The exception to display</param>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Services.Interfaces.IWindowService.ShowDialogAsync``1(Artemis.UI.Avalonia.Shared.DialogViewModelBase{``0})">
|
||||
<summary>
|
||||
Given an existing ViewModel, show its corresponding View as a Dialog
|
||||
</summary>
|
||||
<typeparam name="TResult">The return type</typeparam>
|
||||
<param name="viewModel">ViewModel to show the View for</param>
|
||||
<returns>A task containing the return value of type <typeparamref name="TResult" /></returns>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Services.Interfaces.IWindowService.ShowDialogAsync``2(System.ValueTuple{System.String,System.Object}[])">
|
||||
<summary>
|
||||
Creates a view model instance of type <typeparamref name="TViewModel"/> and shows its corresponding View as a Dialog
|
||||
</summary>
|
||||
<typeparam name="TViewModel">The view model type</typeparam>
|
||||
<typeparam name="TResult">The return type</typeparam>
|
||||
<returns>A task containing the return value of type <typeparamref name="TResult" /></returns>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Services.Interfaces.IWindowService.ShowConfirmContentDialog(System.String,System.String,System.String,System.String)">
|
||||
<summary>
|
||||
Shows a content dialog asking the user to confirm an action
|
||||
</summary>
|
||||
<param name="title">The title of the dialog</param>
|
||||
<param name="message">The message of the dialog</param>
|
||||
<param name="confirm">The text of the confirm button</param>
|
||||
<param name="cancel">The text of the cancel button, if <see langword="null"/> the cancel button will not be shown</param>
|
||||
<returns>A task containing the result of the dialog, <see langword="true"/> if confirmed; otherwise <see langword="false"/></returns>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Services.Interfaces.IWindowService.CreateOpenFileDialog">
|
||||
<summary>
|
||||
Creates an open file dialog, use the fluent API to configure it
|
||||
</summary>
|
||||
<returns>The builder that can be used to configure the dialog</returns>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.Services.Interfaces.IWindowService.CreateSaveFileDialog">
|
||||
<summary>
|
||||
Creates a save file dialog, use the fluent API to configure it
|
||||
</summary>
|
||||
<returns>The builder that can be used to configure the dialog</returns>
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Avalonia.Shared.ViewModelBase">
|
||||
<summary>
|
||||
Represents the base class for Artemis view models
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Avalonia.Shared.ViewModelBase.DisplayName">
|
||||
<summary>
|
||||
Gets or sets the display name of the view model
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Avalonia.Shared.ActivatableViewModelBase">
|
||||
<summary>
|
||||
Represents the base class for Artemis view models that are interested in the activated event
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.ActivatableViewModelBase.#ctor">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.ActivatableViewModelBase.Dispose(System.Boolean)">
|
||||
<summary>
|
||||
Releases the unmanaged resources used by the object and optionally releases the managed resources.
|
||||
</summary>
|
||||
<param name="disposing">
|
||||
<see langword="true" /> to release both managed and unmanaged resources;
|
||||
<see langword="false" /> to release only unmanaged resources.
|
||||
</param>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Avalonia.Shared.ActivatableViewModelBase.Activator">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.ActivatableViewModelBase.Dispose">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Avalonia.Shared.DialogViewModelBase`1">
|
||||
<summary>
|
||||
Represents the base class for Artemis view models used to drive dialogs
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.DialogViewModelBase`1.Close(`0)">
|
||||
<summary>
|
||||
Closes the dialog with the given <paramref name="result" />
|
||||
</summary>
|
||||
<param name="result">The result of the dialog</param>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Avalonia.Shared.DialogViewModelBase`1.Cancel">
|
||||
<summary>
|
||||
Closes the dialog without a result
|
||||
</summary>
|
||||
</member>
|
||||
</members>
|
||||
</doc>
|
||||
@@ -1,49 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ApplicationIcon />
|
||||
<StartupObject />
|
||||
<OutputPath>bin\</OutputPath>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DocumentationFile>bin\Artemis.UI.Avalonia.Shared.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Artemis.UI.Avalonia.Shared.csproj.DotSettings" />
|
||||
<None Remove="Artemis.UI.Shared.csproj.DotSettings" />
|
||||
<None Remove="DefaultTypes\DataModel\Display\DefaultDataModelDisplayView.xaml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="0.10.13" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.13" />
|
||||
<PackageReference Include="Avalonia.Svg.Skia" Version="0.10.12" />
|
||||
<PackageReference Include="Avalonia.Xaml.Behaviors" Version="0.10.13.2" />
|
||||
<PackageReference Include="DynamicData" Version="7.5.4" />
|
||||
<PackageReference Include="FluentAvaloniaUI" Version="1.3.0" />
|
||||
<PackageReference Include="Material.Icons.Avalonia" Version="1.0.2" />
|
||||
<PackageReference Include="ReactiveUI" Version="17.1.50" />
|
||||
<PackageReference Include="ReactiveUI.Validation" Version="2.2.1" />
|
||||
<PackageReference Include="RGB.NET.Core" Version="1.0.0-prerelease7" />
|
||||
<PackageReference Include="SkiaSharp" Version="2.88.0-preview.178" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Include="DefaultTypes\DataModel\Display\DefaultDataModelDisplayView.xaml">
|
||||
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Artemis.Core\Artemis.Core.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="Controls\HotkeyBox.axaml.cs">
|
||||
<DependentUpon>HotkeyBox.axaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Services\Window\ExceptionDialogView.axaml.cs">
|
||||
<DependentUpon>%(Filename)</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,6 +0,0 @@
|
||||
<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">
|
||||
<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>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Cwindows/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Cwindowservice/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
@@ -1,55 +0,0 @@
|
||||
using System.Windows.Input;
|
||||
using Avalonia;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
|
||||
namespace Artemis.UI.Shared.AttachedProperties;
|
||||
|
||||
/// <summary>
|
||||
/// Helper properties for working with NumberBoxes.
|
||||
/// </summary>
|
||||
public class NumberBoxAssist : AvaloniaObject
|
||||
{
|
||||
/// <summary>
|
||||
/// Identifies the <seealso cref="SuffixTextProperty" /> Avalonia attached property.
|
||||
/// </summary>
|
||||
/// <value>Provide an <see cref="ICommand" /> derived object or binding.</value>
|
||||
public static readonly AttachedProperty<string> SuffixTextProperty = AvaloniaProperty.RegisterAttached<NumberBoxAssist, string>("SuffixText", typeof(NumberBox));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <seealso cref="PrefixTextProperty" /> Avalonia attached property.
|
||||
/// </summary>
|
||||
/// <value>Provide an <see cref="ICommand" /> derived object or binding.</value>
|
||||
public static readonly AttachedProperty<string> PrefixTextProperty = AvaloniaProperty.RegisterAttached<NumberBoxAssist, string>("PrefixText", typeof(NumberBox));
|
||||
|
||||
/// <summary>
|
||||
/// Accessor for Attached property <see cref="SuffixTextProperty" />.
|
||||
/// </summary>
|
||||
public static void SetSuffixText(AvaloniaObject element, string value)
|
||||
{
|
||||
element.SetValue(SuffixTextProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Accessor for Attached property <see cref="SuffixTextProperty" />.
|
||||
/// </summary>
|
||||
public static string GetSuffixText(AvaloniaObject element)
|
||||
{
|
||||
return element.GetValue(SuffixTextProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Accessor for Attached property <see cref="PrefixTextProperty" />.
|
||||
/// </summary>
|
||||
public static void SetPrefixText(AvaloniaObject element, string value)
|
||||
{
|
||||
element.SetValue(PrefixTextProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Accessor for Attached property <see cref="PrefixTextProperty" />.
|
||||
/// </summary>
|
||||
public static string GetPrefixText(AvaloniaObject element)
|
||||
{
|
||||
return element.GetValue(PrefixTextProperty);
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
using System.Windows.Input;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
|
||||
namespace Artemis.UI.Shared.AttachedProperties;
|
||||
|
||||
/// <summary>
|
||||
/// Helper properties for working with TextBoxes.
|
||||
/// </summary>
|
||||
public class TextBoxAssist : AvaloniaObject
|
||||
{
|
||||
/// <summary>
|
||||
/// Identifies the <seealso cref="SuffixTextProperty" /> Avalonia attached property.
|
||||
/// </summary>
|
||||
/// <value>Provide an <see cref="ICommand" /> derived object or binding.</value>
|
||||
public static readonly AttachedProperty<string> SuffixTextProperty = AvaloniaProperty.RegisterAttached<TextBoxAssist, string>("SuffixText", typeof(TextBox));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <seealso cref="PrefixTextProperty" /> Avalonia attached property.
|
||||
/// </summary>
|
||||
/// <value>Provide an <see cref="ICommand" /> derived object or binding.</value>
|
||||
public static readonly AttachedProperty<string> PrefixTextProperty = AvaloniaProperty.RegisterAttached<TextBoxAssist, string>("PrefixText", typeof(TextBox));
|
||||
|
||||
/// <summary>
|
||||
/// Accessor for Attached property <see cref="SuffixTextProperty" />.
|
||||
/// </summary>
|
||||
public static void SetSuffixText(AvaloniaObject element, string value)
|
||||
{
|
||||
element.SetValue(SuffixTextProperty, value);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(value) && !((TextBox)element).Classes.Contains("suffixed"))
|
||||
((TextBox)element).Classes.Add("suffixed");
|
||||
else
|
||||
((TextBox)element).Classes.Remove("suffixed");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Accessor for Attached property <see cref="SuffixTextProperty" />.
|
||||
/// </summary>
|
||||
public static string GetSuffixText(AvaloniaObject element)
|
||||
{
|
||||
return element.GetValue(SuffixTextProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Accessor for Attached property <see cref="PrefixTextProperty" />.
|
||||
/// </summary>
|
||||
public static void SetPrefixText(AvaloniaObject element, string value)
|
||||
{
|
||||
element.SetValue(PrefixTextProperty, value);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(value) && !((TextBox)element).Classes.Contains("prefixed"))
|
||||
((TextBox)element).Classes.Add("prefixed");
|
||||
else
|
||||
((TextBox)element).Classes.Remove("prefixed");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Accessor for Attached property <see cref="PrefixTextProperty" />.
|
||||
/// </summary>
|
||||
public static string GetPrefixText(AvaloniaObject element)
|
||||
{
|
||||
return element.GetValue(PrefixTextProperty);
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
using System;
|
||||
using Avalonia;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Xaml.Interactivity;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
|
||||
namespace Artemis.UI.Shared.Behaviors;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a behavior that can be used to make a text box only update it's binding on focus loss.
|
||||
/// </summary>
|
||||
public class LostFocusNumberBoxBindingBehavior : Behavior<NumberBox>
|
||||
{
|
||||
public static readonly StyledProperty<double> ValueProperty = AvaloniaProperty.Register<LostFocusTextBoxBindingBehavior, double>(
|
||||
nameof(Value), defaultBindingMode: BindingMode.TwoWay);
|
||||
|
||||
static LostFocusNumberBoxBindingBehavior()
|
||||
{
|
||||
ValueProperty.Changed.Subscribe(e => ((LostFocusNumberBoxBindingBehavior) e.Sender).OnBindingValueChanged());
|
||||
}
|
||||
|
||||
public double Value
|
||||
{
|
||||
get => GetValue(ValueProperty);
|
||||
set => SetValue(ValueProperty, value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnAttached()
|
||||
{
|
||||
if (AssociatedObject != null)
|
||||
AssociatedObject.LostFocus += OnLostFocus;
|
||||
base.OnAttached();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnDetaching()
|
||||
{
|
||||
if (AssociatedObject != null)
|
||||
AssociatedObject.LostFocus -= OnLostFocus;
|
||||
base.OnDetaching();
|
||||
}
|
||||
|
||||
private void OnLostFocus(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
if (AssociatedObject != null)
|
||||
Value = AssociatedObject.Value;
|
||||
}
|
||||
|
||||
private void OnBindingValueChanged()
|
||||
{
|
||||
if (AssociatedObject != null)
|
||||
AssociatedObject.Value = Value;
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
using System;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Xaml.Interactivity;
|
||||
|
||||
namespace Artemis.UI.Shared.Behaviors;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a behavior that can be used to make a text box only update it's binding on focus loss.
|
||||
/// </summary>
|
||||
public class LostFocusTextBoxBindingBehavior : Behavior<TextBox>
|
||||
{
|
||||
public static readonly StyledProperty<string> TextProperty = AvaloniaProperty.Register<LostFocusTextBoxBindingBehavior, string>(
|
||||
"Text", defaultBindingMode: BindingMode.TwoWay);
|
||||
|
||||
static LostFocusTextBoxBindingBehavior()
|
||||
{
|
||||
TextProperty.Changed.Subscribe(e => ((LostFocusTextBoxBindingBehavior) e.Sender).OnBindingValueChanged());
|
||||
}
|
||||
|
||||
public string Text
|
||||
{
|
||||
get => GetValue(TextProperty);
|
||||
set => SetValue(TextProperty, value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnAttached()
|
||||
{
|
||||
if (AssociatedObject != null)
|
||||
AssociatedObject.LostFocus += OnLostFocus;
|
||||
base.OnAttached();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnDetaching()
|
||||
{
|
||||
if (AssociatedObject != null)
|
||||
AssociatedObject.LostFocus -= OnLostFocus;
|
||||
base.OnDetaching();
|
||||
}
|
||||
|
||||
private void OnLostFocus(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
if (AssociatedObject != null)
|
||||
Text = AssociatedObject.Text;
|
||||
}
|
||||
|
||||
private void OnBindingValueChanged()
|
||||
{
|
||||
if (AssociatedObject != null)
|
||||
AssociatedObject.Text = Text;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Shared.Controls.ArtemisIcon">
|
||||
Welcome to Avalonia!
|
||||
</UserControl>
|
||||
@@ -1,115 +0,0 @@
|
||||
using System;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.LogicalTree;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Svg.Skia;
|
||||
using Material.Icons;
|
||||
using Material.Icons.Avalonia;
|
||||
|
||||
namespace Artemis.UI.Shared.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a control that can display an arbitrary kind of icon.
|
||||
/// </summary>
|
||||
public class ArtemisIcon : UserControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="ArtemisIcon" /> class.
|
||||
/// </summary>
|
||||
public ArtemisIcon()
|
||||
{
|
||||
InitializeComponent();
|
||||
DetachedFromLogicalTree += OnDetachedFromLogicalTree;
|
||||
LayoutUpdated += OnLayoutUpdated;
|
||||
}
|
||||
|
||||
private static void IconChanging(IAvaloniaObject sender, bool before)
|
||||
{
|
||||
if (before)
|
||||
((ArtemisIcon) sender).Update();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
try
|
||||
{
|
||||
// First look for an enum value instead of a string
|
||||
if (Icon is MaterialIconKind materialIcon)
|
||||
{
|
||||
Content = new MaterialIcon {Kind = materialIcon, Width = Bounds.Width, Height = Bounds.Height};
|
||||
}
|
||||
// If it's a string there are several options
|
||||
else if (Icon is string iconString)
|
||||
{
|
||||
// An enum defined as a string
|
||||
if (Enum.TryParse(iconString, true, out MaterialIconKind parsedIcon))
|
||||
{
|
||||
Content = new MaterialIcon {Kind = parsedIcon, Width = Bounds.Width, Height = Bounds.Height};
|
||||
}
|
||||
// An URI pointing to an SVG
|
||||
else if (iconString.EndsWith(".svg"))
|
||||
{
|
||||
SvgSource source = new();
|
||||
source.Load(iconString);
|
||||
Content = new SvgImage {Source = source};
|
||||
}
|
||||
// An URI pointing to a different kind of image
|
||||
else
|
||||
{
|
||||
Content = new Image {Source = new Bitmap(iconString), Width = Bounds.Width, Height = Bounds.Height};
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
Content = new MaterialIcon {Kind = MaterialIconKind.QuestionMark, Width = Bounds.Width, Height = Bounds.Height};
|
||||
}
|
||||
}
|
||||
|
||||
private void OnLayoutUpdated(object? sender, EventArgs e)
|
||||
{
|
||||
if (Content is Control contentControl)
|
||||
{
|
||||
contentControl.Width = Bounds.Width;
|
||||
contentControl.Height = Bounds.Height;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDetachedFromLogicalTree(object? sender, LogicalTreeAttachmentEventArgs e)
|
||||
{
|
||||
if (Content is SvgImage svgImage)
|
||||
svgImage.Source?.Dispose();
|
||||
else if (Content is Image image)
|
||||
((Bitmap) image.Source).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);
|
||||
|
||||
|
||||
/// <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 object? Icon
|
||||
{
|
||||
get => GetValue(IconProperty);
|
||||
set => SetValue(IconProperty, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,300 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reactive;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Modules;
|
||||
using Artemis.UI.Shared.DataModelVisualization.Shared;
|
||||
using Artemis.UI.Shared.Events;
|
||||
using Artemis.UI.Shared.Extensions;
|
||||
using Artemis.UI.Shared.Services.Interfaces;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Data;
|
||||
using Material.Icons.Avalonia;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Shared.Controls.DataModelPicker;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a data model picker picker that can be used to select a data model path.
|
||||
/// </summary>
|
||||
public class DataModelPicker : TemplatedControl
|
||||
{
|
||||
/// <summary>
|
||||
/// The data model UI service this picker should use.
|
||||
/// </summary>
|
||||
public static IDataModelUIService? DataModelUIService;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets data model path.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<DataModelPath?> DataModelPathProperty =
|
||||
AvaloniaProperty.Register<DataModelPicker, DataModelPath?>(nameof(DataModelPath), defaultBindingMode: BindingMode.TwoWay);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether the data model picker should show current values when selecting a path.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<bool> ShowDataModelValuesProperty =
|
||||
AvaloniaProperty.Register<DataModelPicker, bool>(nameof(ShowDataModelValues));
|
||||
|
||||
/// <summary>
|
||||
/// A list of extra modules to show data models of.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<ObservableCollection<Module>?> ModulesProperty =
|
||||
AvaloniaProperty.Register<DataModelPicker, ObservableCollection<Module>?>(nameof(Modules), new ObservableCollection<Module>());
|
||||
|
||||
/// <summary>
|
||||
/// The data model view model to show, if not provided one will be retrieved by the control.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<DataModelPropertiesViewModel?> DataModelViewModelProperty =
|
||||
AvaloniaProperty.Register<DataModelPicker, DataModelPropertiesViewModel?>(nameof(DataModelViewModel));
|
||||
|
||||
/// <summary>
|
||||
/// A list of data model view models to show
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<ObservableCollection<DataModelPropertiesViewModel>?> ExtraDataModelViewModelsProperty =
|
||||
AvaloniaProperty.Register<DataModelPicker, ObservableCollection<DataModelPropertiesViewModel>?>(nameof(ExtraDataModelViewModels), new ObservableCollection<DataModelPropertiesViewModel>());
|
||||
|
||||
/// <summary>
|
||||
/// A list of types to filter the selectable paths on.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<ObservableCollection<Type>?> FilterTypesProperty =
|
||||
AvaloniaProperty.Register<DataModelPicker, ObservableCollection<Type>?>(nameof(FilterTypes), new ObservableCollection<Type>());
|
||||
|
||||
private MaterialIcon? _currentPathIcon;
|
||||
private TextBlock? _currentPathDisplay;
|
||||
private TextBlock? _currentPathDescription;
|
||||
private TreeView? _dataModelTreeView;
|
||||
|
||||
static DataModelPicker()
|
||||
{
|
||||
ModulesProperty.Changed.Subscribe(ModulesChanged);
|
||||
DataModelPathProperty.Changed.Subscribe(DataModelPathPropertyChanged);
|
||||
DataModelViewModelProperty.Changed.Subscribe(DataModelViewModelPropertyChanged);
|
||||
ExtraDataModelViewModelsProperty.Changed.Subscribe(ExtraDataModelViewModelsChanged);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="DataModelPicker" /> class.
|
||||
/// </summary>
|
||||
public DataModelPicker()
|
||||
{
|
||||
SelectPropertyCommand = ReactiveCommand.Create<DataModelVisualizationViewModel>(selected => ExecuteSelectPropertyCommand(selected));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a command that selects the path by it's view model.
|
||||
/// </summary>
|
||||
public ReactiveCommand<DataModelVisualizationViewModel, Unit> SelectPropertyCommand { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets data model path.
|
||||
/// </summary>
|
||||
public DataModelPath? DataModelPath
|
||||
{
|
||||
get => GetValue(DataModelPathProperty);
|
||||
set => SetValue(DataModelPathProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether the data model picker should show current values when selecting a path.
|
||||
/// </summary>
|
||||
public bool ShowDataModelValues
|
||||
{
|
||||
get => GetValue(ShowDataModelValuesProperty);
|
||||
set => SetValue(ShowDataModelValuesProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A list of extra modules to show data models of.
|
||||
/// </summary>
|
||||
public ObservableCollection<Module>? Modules
|
||||
{
|
||||
get => GetValue(ModulesProperty);
|
||||
set => SetValue(ModulesProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The data model view model to show, if not provided one will be retrieved by the control.
|
||||
/// </summary>
|
||||
public DataModelPropertiesViewModel? DataModelViewModel
|
||||
{
|
||||
get => GetValue(DataModelViewModelProperty);
|
||||
private set => SetValue(DataModelViewModelProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A list of data model view models to show.
|
||||
/// </summary>
|
||||
public ObservableCollection<DataModelPropertiesViewModel>? ExtraDataModelViewModels
|
||||
{
|
||||
get => GetValue(ExtraDataModelViewModelsProperty);
|
||||
set => SetValue(ExtraDataModelViewModelsProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A list of types to filter the selectable paths on.
|
||||
/// </summary>
|
||||
public ObservableCollection<Type>? FilterTypes
|
||||
{
|
||||
get => GetValue(FilterTypesProperty);
|
||||
set => SetValue(FilterTypesProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a new path has been selected
|
||||
/// </summary>
|
||||
public event EventHandler<DataModelSelectedEventArgs>? DataModelPathSelected;
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="DataModelPathSelected" /> event
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
protected virtual void OnDataModelPathSelected(DataModelSelectedEventArgs e)
|
||||
{
|
||||
DataModelPathSelected?.Invoke(this, e);
|
||||
}
|
||||
|
||||
#region Overrides of TemplatedControl
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
{
|
||||
if (_dataModelTreeView != null)
|
||||
_dataModelTreeView.SelectionChanged -= DataModelTreeViewOnSelectionChanged;
|
||||
|
||||
_currentPathIcon = e.NameScope.Find<MaterialIcon>("CurrentPathIcon");
|
||||
_currentPathDisplay = e.NameScope.Find<TextBlock>("CurrentPathDisplay");
|
||||
_currentPathDescription = e.NameScope.Find<TextBlock>("CurrentPathDescription");
|
||||
_dataModelTreeView = e.NameScope.Find<TreeView>("DataModelTreeView");
|
||||
|
||||
if (_dataModelTreeView != null)
|
||||
_dataModelTreeView.SelectionChanged += DataModelTreeViewOnSelectionChanged;
|
||||
}
|
||||
|
||||
#region Overrides of Visual
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
GetDataModel();
|
||||
UpdateCurrentPath(true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
DataModelViewModel?.Dispose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
private static void ModulesChanged(AvaloniaPropertyChangedEventArgs<ObservableCollection<Module>?> e)
|
||||
{
|
||||
if (e.Sender is DataModelPicker dataModelPicker)
|
||||
dataModelPicker.GetDataModel();
|
||||
}
|
||||
|
||||
private static void DataModelPathPropertyChanged(AvaloniaPropertyChangedEventArgs<DataModelPath?> e)
|
||||
{
|
||||
if (e.Sender is DataModelPicker dataModelPicker)
|
||||
dataModelPicker.UpdateCurrentPath(false);
|
||||
}
|
||||
|
||||
private static void DataModelViewModelPropertyChanged(AvaloniaPropertyChangedEventArgs<DataModelPropertiesViewModel?> e)
|
||||
{
|
||||
if (e.Sender is DataModelPicker && e.OldValue.Value != null)
|
||||
e.OldValue.Value.Dispose();
|
||||
}
|
||||
|
||||
private static void ExtraDataModelViewModelsChanged(AvaloniaPropertyChangedEventArgs<ObservableCollection<DataModelPropertiesViewModel>?> e)
|
||||
{
|
||||
// TODO, the original did nothing here either and I can't remember why
|
||||
}
|
||||
|
||||
private void ExecuteSelectPropertyCommand(DataModelVisualizationViewModel selected)
|
||||
{
|
||||
if (selected.DataModelPath == null)
|
||||
return;
|
||||
if (selected.DataModelPath.Equals(DataModelPath))
|
||||
return;
|
||||
|
||||
DataModelPath = new DataModelPath(selected.DataModelPath);
|
||||
OnDataModelPathSelected(new DataModelSelectedEventArgs(DataModelPath));
|
||||
}
|
||||
|
||||
private void GetDataModel()
|
||||
{
|
||||
if (DataModelUIService == null)
|
||||
return;
|
||||
|
||||
ChangeDataModel(DataModelUIService.GetPluginDataModelVisualization(Modules?.ToList() ?? new List<Module>(), true));
|
||||
}
|
||||
|
||||
private void ChangeDataModel(DataModelPropertiesViewModel? dataModel)
|
||||
{
|
||||
if (DataModelViewModel != null)
|
||||
{
|
||||
DataModelViewModel.Dispose();
|
||||
DataModelViewModel.UpdateRequested -= DataModelOnUpdateRequested;
|
||||
}
|
||||
|
||||
DataModelViewModel = dataModel;
|
||||
if (DataModelViewModel != null)
|
||||
DataModelViewModel.UpdateRequested += DataModelOnUpdateRequested;
|
||||
}
|
||||
|
||||
private void DataModelOnUpdateRequested(object? sender, EventArgs e)
|
||||
{
|
||||
DataModelViewModel?.ApplyTypeFilter(true, FilterTypes?.ToArray() ?? Type.EmptyTypes);
|
||||
if (ExtraDataModelViewModels == null) return;
|
||||
foreach (DataModelPropertiesViewModel extraDataModelViewModel in ExtraDataModelViewModels)
|
||||
extraDataModelViewModel.ApplyTypeFilter(true, FilterTypes?.ToArray() ?? Type.EmptyTypes);
|
||||
}
|
||||
|
||||
private void DataModelTreeViewOnSelectionChanged(object? sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
// Multi-select isn't a think so grab the first one
|
||||
object? selected = _dataModelTreeView?.SelectedItems[0];
|
||||
if (selected == null)
|
||||
return;
|
||||
|
||||
if (selected is DataModelPropertyViewModel property && property.DataModelPath != null)
|
||||
DataModelPath = new DataModelPath(property.DataModelPath);
|
||||
else if (selected is DataModelListViewModel list && list.DataModelPath != null)
|
||||
DataModelPath = new DataModelPath(list.DataModelPath);
|
||||
else if (selected is DataModelEventViewModel dataModelEvent && dataModelEvent.DataModelPath != null)
|
||||
DataModelPath = new DataModelPath(dataModelEvent.DataModelPath);
|
||||
}
|
||||
|
||||
private void UpdateCurrentPath(bool selectCurrentPath)
|
||||
{
|
||||
if (DataModelPath == null)
|
||||
return;
|
||||
|
||||
if (_dataModelTreeView != null && selectCurrentPath)
|
||||
{
|
||||
// Expand the path
|
||||
DataModel? start = DataModelPath.Target;
|
||||
DataModelVisualizationViewModel? root = DataModelViewModel?.Children.FirstOrDefault(c => c.DataModel == start);
|
||||
if (root != null)
|
||||
{
|
||||
root.ExpandToPath(DataModelPath);
|
||||
_dataModelTreeView.SelectedItem = root.GetViewModelForPath(DataModelPath);
|
||||
}
|
||||
}
|
||||
|
||||
if (_currentPathDisplay != null)
|
||||
_currentPathDisplay.Text = string.Join(" › ", DataModelPath.Segments.Where(s => s.GetPropertyDescription() != null).Select(s => s.GetPropertyDescription()!.Name));
|
||||
if (_currentPathDescription != null)
|
||||
_currentPathDescription.Text = DataModelPath.GetPropertyDescription()?.Description;
|
||||
if (_currentPathIcon != null)
|
||||
_currentPathIcon.Kind = DataModelPath.GetPropertyType().GetTypeIcon();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,338 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Modules;
|
||||
using Artemis.UI.Shared.Controls.Flyouts;
|
||||
using Artemis.UI.Shared.DataModelVisualization.Shared;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Threading;
|
||||
using FluentAvalonia.Core;
|
||||
|
||||
namespace Artemis.UI.Shared.Controls.DataModelPicker;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a button that can be used to pick a data model path in a flyout.
|
||||
/// </summary>
|
||||
public class DataModelPickerButton : TemplatedControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the placeholder to show when nothing is selected.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<string> PlaceholderProperty =
|
||||
AvaloniaProperty.Register<DataModelPicker, string>(nameof(Placeholder), "Click to select");
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether the data model picker should show the full path of the selected value.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<bool> ShowFullPathProperty =
|
||||
AvaloniaProperty.Register<DataModelPicker, bool>(nameof(ShowFullPath));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether the button should show the icon of the first provided filter type.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<bool> ShowTypeIconProperty =
|
||||
AvaloniaProperty.Register<DataModelPicker, bool>(nameof(ShowTypeIcon));
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether the data model picker has a value.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<bool> HasValueProperty =
|
||||
AvaloniaProperty.Register<DataModelPicker, bool>(nameof(HasValue));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the desired flyout placement.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<FlyoutPlacementMode> PlacementProperty =
|
||||
AvaloniaProperty.Register<FlyoutBase, FlyoutPlacementMode>(nameof(Placement));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets data model path.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<DataModelPath?> DataModelPathProperty =
|
||||
AvaloniaProperty.Register<DataModelPicker, DataModelPath?>(nameof(DataModelPath), defaultBindingMode: BindingMode.TwoWay);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether the data model picker should show current values when selecting a path.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<bool> ShowDataModelValuesProperty =
|
||||
AvaloniaProperty.Register<DataModelPicker, bool>(nameof(ShowDataModelValues));
|
||||
|
||||
/// <summary>
|
||||
/// A list of extra modules to show data models of.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<ObservableCollection<Module>?> ModulesProperty =
|
||||
AvaloniaProperty.Register<DataModelPicker, ObservableCollection<Module>?>(nameof(Modules), new ObservableCollection<Module>());
|
||||
|
||||
/// <summary>
|
||||
/// A list of data model view models to show
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<ObservableCollection<DataModelPropertiesViewModel>?> ExtraDataModelViewModelsProperty =
|
||||
AvaloniaProperty.Register<DataModelPicker, ObservableCollection<DataModelPropertiesViewModel>?>(nameof(ExtraDataModelViewModels), new ObservableCollection<DataModelPropertiesViewModel>());
|
||||
|
||||
/// <summary>
|
||||
/// A list of types to filter the selectable paths on.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<ObservableCollection<Type>?> FilterTypesProperty =
|
||||
AvaloniaProperty.Register<DataModelPicker, ObservableCollection<Type>?>(nameof(FilterTypes), new ObservableCollection<Type>());
|
||||
|
||||
private bool _attached;
|
||||
private bool _flyoutActive;
|
||||
private Button? _button;
|
||||
private TextBlock? _label;
|
||||
private DataModelPickerFlyout? _flyout;
|
||||
|
||||
static DataModelPickerButton()
|
||||
{
|
||||
ShowFullPathProperty.Changed.Subscribe(ShowFullPathChanged);
|
||||
DataModelPathProperty.Changed.Subscribe(DataModelPathChanged);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the placeholder to show when nothing is selected.
|
||||
/// </summary>
|
||||
public string Placeholder
|
||||
{
|
||||
get => GetValue(PlaceholderProperty);
|
||||
set => SetValue(PlaceholderProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether the data model picker should show the full path of the selected value.
|
||||
/// </summary>
|
||||
public bool ShowFullPath
|
||||
{
|
||||
get => GetValue(ShowFullPathProperty);
|
||||
set => SetValue(ShowFullPathProperty, value);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether the button should show the icon of the first provided filter type.
|
||||
/// </summary>
|
||||
public bool ShowTypeIcon
|
||||
{
|
||||
get => GetValue(ShowTypeIconProperty);
|
||||
set => SetValue(ShowTypeIconProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether the data model picker has a value.
|
||||
/// </summary>
|
||||
public bool HasValue
|
||||
{
|
||||
get => GetValue(HasValueProperty);
|
||||
private set => SetValue(HasValueProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the desired flyout placement.
|
||||
/// </summary>
|
||||
public FlyoutPlacementMode Placement
|
||||
{
|
||||
get => GetValue(PlacementProperty);
|
||||
set => SetValue(PlacementProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets data model path.
|
||||
/// </summary>
|
||||
public DataModelPath? DataModelPath
|
||||
{
|
||||
get => GetValue(DataModelPathProperty);
|
||||
set => SetValue(DataModelPathProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether the data model picker should show current values when selecting a path.
|
||||
/// </summary>
|
||||
public bool ShowDataModelValues
|
||||
{
|
||||
get => GetValue(ShowDataModelValuesProperty);
|
||||
set => SetValue(ShowDataModelValuesProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A list of extra modules to show data models of.
|
||||
/// </summary>
|
||||
public ObservableCollection<Module>? Modules
|
||||
{
|
||||
get => GetValue(ModulesProperty);
|
||||
set => SetValue(ModulesProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A list of data model view models to show.
|
||||
/// </summary>
|
||||
public ObservableCollection<DataModelPropertiesViewModel>? ExtraDataModelViewModels
|
||||
{
|
||||
get => GetValue(ExtraDataModelViewModelsProperty);
|
||||
set => SetValue(ExtraDataModelViewModelsProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A list of types to filter the selectable paths on.
|
||||
/// </summary>
|
||||
public ObservableCollection<Type>? FilterTypes
|
||||
{
|
||||
get => GetValue(FilterTypesProperty);
|
||||
set => SetValue(FilterTypesProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised when the flyout opens.
|
||||
/// </summary>
|
||||
public event TypedEventHandler<DataModelPickerButton, EventArgs>? FlyoutOpened;
|
||||
|
||||
/// <summary>
|
||||
/// Raised when the flyout closes.
|
||||
/// </summary>
|
||||
public event TypedEventHandler<DataModelPickerButton, EventArgs>? FlyoutClosed;
|
||||
|
||||
private static void DataModelPathChanged(AvaloniaPropertyChangedEventArgs<DataModelPath?> e)
|
||||
{
|
||||
if (e.Sender is not DataModelPickerButton self || !self._attached)
|
||||
return;
|
||||
|
||||
if (e.OldValue.Value != null)
|
||||
{
|
||||
e.OldValue.Value.PathInvalidated -= self.PathValidationChanged;
|
||||
e.OldValue.Value.PathValidated -= self.PathValidationChanged;
|
||||
e.OldValue.Value.Dispose();
|
||||
}
|
||||
|
||||
if (e.NewValue.Value != null)
|
||||
{
|
||||
e.NewValue.Value.PathInvalidated += self.PathValidationChanged;
|
||||
e.NewValue.Value.PathValidated += self.PathValidationChanged;
|
||||
}
|
||||
|
||||
self.UpdateValueDisplay();
|
||||
}
|
||||
|
||||
private static void ShowFullPathChanged(AvaloniaPropertyChangedEventArgs<bool> e)
|
||||
{
|
||||
if (e.Sender is DataModelPickerButton self)
|
||||
self.UpdateValueDisplay();
|
||||
}
|
||||
|
||||
private void PathValidationChanged(object? sender, EventArgs e)
|
||||
{
|
||||
Dispatcher.UIThread.InvokeAsync(UpdateValueDisplay, DispatcherPriority.DataBind);
|
||||
}
|
||||
|
||||
private void UpdateValueDisplay()
|
||||
{
|
||||
HasValue = DataModelPath != null && DataModelPath.IsValid;
|
||||
|
||||
if (_button == null || _label == null)
|
||||
return;
|
||||
|
||||
if (!HasValue)
|
||||
{
|
||||
ToolTip.SetTip(_button, null);
|
||||
_label.Text = Placeholder;
|
||||
}
|
||||
else
|
||||
{
|
||||
string? formattedPath = null;
|
||||
if (DataModelPath != null && DataModelPath.IsValid)
|
||||
formattedPath = string.Join(" › ", DataModelPath.Segments.Where(s => s.GetPropertyDescription() != null).Select(s => s.GetPropertyDescription()!.Name));
|
||||
|
||||
ToolTip.SetTip(_button, formattedPath);
|
||||
_label.Text = ShowFullPath
|
||||
? formattedPath
|
||||
: DataModelPath?.Segments.LastOrDefault()?.GetPropertyDescription()?.Name ?? DataModelPath?.Segments.LastOrDefault()?.Identifier;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnButtonClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_flyout == null)
|
||||
return;
|
||||
|
||||
// Logic here is taken from Fluent Avalonia's ColorPicker which also reuses the same control since it's large
|
||||
_flyout.DataModelPicker.DataModelPath = DataModelPath;
|
||||
_flyout.DataModelPicker.ExtraDataModelViewModels = ExtraDataModelViewModels;
|
||||
_flyout.DataModelPicker.FilterTypes = FilterTypes;
|
||||
_flyout.DataModelPicker.Modules = Modules;
|
||||
_flyout.DataModelPicker.ShowDataModelValues = ShowDataModelValues;
|
||||
|
||||
_flyout.Placement = Placement;
|
||||
_flyout.ShowAt(_button != null ? _button : this);
|
||||
_flyoutActive = true;
|
||||
|
||||
FlyoutOpened?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
#region Overrides of TemplatedControl
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
{
|
||||
if (_button != null)
|
||||
_button.Click -= OnButtonClick;
|
||||
base.OnApplyTemplate(e);
|
||||
_button = e.NameScope.Find<Button>("MainButton");
|
||||
_label = e.NameScope.Find<TextBlock>("MainButtonLabel");
|
||||
if (_button != null)
|
||||
_button.Click += OnButtonClick;
|
||||
UpdateValueDisplay();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
_attached = true;
|
||||
if (DataModelPath != null)
|
||||
{
|
||||
DataModelPath.PathInvalidated += PathValidationChanged;
|
||||
DataModelPath.PathValidated += PathValidationChanged;
|
||||
}
|
||||
|
||||
_flyout ??= new DataModelPickerFlyout();
|
||||
_flyout.Confirmed += FlyoutOnConfirmed;
|
||||
_flyout.Closed += OnFlyoutClosed;
|
||||
}
|
||||
|
||||
private void FlyoutOnConfirmed(DataModelPickerFlyout sender, object args)
|
||||
{
|
||||
if (_flyoutActive)
|
||||
{
|
||||
FlyoutClosed?.Invoke(this, EventArgs.Empty);
|
||||
_flyoutActive = false;
|
||||
|
||||
if (_flyout != null)
|
||||
DataModelPath = _flyout.DataModelPicker.DataModelPath;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnFlyoutClosed(object? sender, EventArgs e)
|
||||
{
|
||||
if (_flyoutActive)
|
||||
{
|
||||
FlyoutClosed?.Invoke(this, EventArgs.Empty);
|
||||
_flyoutActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
if (DataModelPath != null)
|
||||
{
|
||||
DataModelPath.PathInvalidated -= PathValidationChanged;
|
||||
DataModelPath.PathValidated -= PathValidationChanged;
|
||||
}
|
||||
|
||||
if (_flyout != null)
|
||||
_flyout.Closed -= OnFlyoutClosed;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -1,314 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Shared.Events;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
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.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Visualizes an <see cref="ArtemisDevice" /> with optional per-LED colors
|
||||
/// </summary>
|
||||
public class DeviceVisualizer : Control
|
||||
{
|
||||
private const double UpdateFrameRate = 25.0;
|
||||
private readonly List<DeviceVisualizerLed> _deviceVisualizerLeds;
|
||||
private readonly DispatcherTimer _timer;
|
||||
|
||||
private Rect _deviceBounds;
|
||||
private RenderTargetBitmap? _deviceImage;
|
||||
private List<DeviceVisualizerLed>? _dimmedLeds;
|
||||
private List<DeviceVisualizerLed>? _highlightedLeds;
|
||||
private ArtemisDevice? _oldDevice;
|
||||
|
||||
/// <inheritdoc />
|
||||
public DeviceVisualizer()
|
||||
{
|
||||
_timer = new DispatcherTimer(DispatcherPriority.Render) {Interval = TimeSpan.FromMilliseconds(1000.0 / UpdateFrameRate)};
|
||||
_deviceVisualizerLeds = new List<DeviceVisualizerLed>();
|
||||
|
||||
PointerReleased += OnPointerReleased;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Render(DrawingContext drawingContext)
|
||||
{
|
||||
if (Device == null)
|
||||
return;
|
||||
|
||||
// Determine the scale required to fit the desired size of the control
|
||||
double scale = Math.Min(Bounds.Width / _deviceBounds.Width, Bounds.Height / _deviceBounds.Height);
|
||||
|
||||
DrawingContext.PushedState? boundsPush = null;
|
||||
try
|
||||
{
|
||||
// Scale the visualization in the desired bounding box
|
||||
if (Bounds.Width > 0 && Bounds.Height > 0)
|
||||
boundsPush = drawingContext.PushPreTransform(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)));
|
||||
|
||||
// Apply device scale
|
||||
using DrawingContext.PushedState scalePush = drawingContext.PushPreTransform(Matrix.CreateScale(Device.Scale, Device.Scale));
|
||||
|
||||
// Render device and LED images
|
||||
if (_deviceImage != null)
|
||||
{
|
||||
drawingContext.DrawImage(
|
||||
_deviceImage,
|
||||
new Rect(_deviceImage.Size),
|
||||
new Rect(0, 0, Device.RgbDevice.ActualSize.Width, Device.RgbDevice.ActualSize.Height),
|
||||
RenderOptions.GetBitmapInterpolationMode(this)
|
||||
);
|
||||
}
|
||||
|
||||
if (!ShowColors)
|
||||
return;
|
||||
|
||||
foreach (DeviceVisualizerLed deviceVisualizerLed in _deviceVisualizerLeds)
|
||||
deviceVisualizerLed.RenderGeometry(drawingContext, false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
boundsPush?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a LED of the device has been clicked
|
||||
/// </summary>
|
||||
public event EventHandler<LedClickedEventArgs>? LedClicked;
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="LedClicked" /> event
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
protected virtual void OnLedClicked(LedClickedEventArgs e)
|
||||
{
|
||||
LedClicked?.Invoke(this, e);
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
InvalidateVisual();
|
||||
}
|
||||
|
||||
private Rect MeasureDevice()
|
||||
{
|
||||
if (Device == null)
|
||||
return Rect.Empty;
|
||||
|
||||
Rect deviceRect = new(0, 0, Device.RgbDevice.ActualSize.Width, Device.RgbDevice.ActualSize.Height);
|
||||
Geometry geometry = new RectangleGeometry(deviceRect);
|
||||
geometry.Transform = new RotateTransform(Device.Rotation);
|
||||
|
||||
return geometry.Bounds;
|
||||
}
|
||||
|
||||
private void TimerOnTick(object? sender, EventArgs e)
|
||||
{
|
||||
if (ShowColors && IsVisible && Opacity > 0)
|
||||
Update();
|
||||
}
|
||||
|
||||
private void OnPointerReleased(object? sender, PointerReleasedEventArgs e)
|
||||
{
|
||||
if (Device == null)
|
||||
return;
|
||||
|
||||
Point position = e.GetPosition(this);
|
||||
double x = position.X / Bounds.Width;
|
||||
double y = position.Y / Bounds.Height;
|
||||
|
||||
Point scaledPosition = new(x * Device.Rectangle.Width, y * Device.Rectangle.Height);
|
||||
DeviceVisualizerLed? deviceVisualizerLed = _deviceVisualizerLeds.FirstOrDefault(l => l.HitTest(scaledPosition));
|
||||
if (deviceVisualizerLed != null)
|
||||
OnLedClicked(new LedClickedEventArgs(deviceVisualizerLed.Led.Device, deviceVisualizerLed.Led));
|
||||
}
|
||||
|
||||
private void DevicePropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
Dispatcher.UIThread.Post(SetupForDevice, DispatcherPriority.Background);
|
||||
}
|
||||
|
||||
private void DeviceUpdated(object? sender, EventArgs e)
|
||||
{
|
||||
Dispatcher.UIThread.Post(SetupForDevice, DispatcherPriority.Background);
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// 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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="ArtemisDevice" /> to display
|
||||
/// </summary>
|
||||
public ArtemisDevice? Device
|
||||
{
|
||||
get => GetValue(DeviceProperty);
|
||||
set => SetValue(DeviceProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets boolean indicating whether or not to show per-LED colors
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<bool> ShowColorsProperty =
|
||||
AvaloniaProperty.Register<DeviceVisualizer, bool>(nameof(ShowColors));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether or not to show per-LED colors
|
||||
/// </summary>
|
||||
public bool ShowColors
|
||||
{
|
||||
get => GetValue(ShowColorsProperty);
|
||||
set => SetValue(ShowColorsProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a list of LEDs to highlight
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<ObservableCollection<ArtemisLed>?> HighlightedLedsProperty =
|
||||
AvaloniaProperty.Register<DeviceVisualizer, ObservableCollection<ArtemisLed>?>(nameof(HighlightedLeds));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a list of LEDs to highlight
|
||||
/// </summary>
|
||||
public ObservableCollection<ArtemisLed>? HighlightedLeds
|
||||
{
|
||||
get => GetValue(HighlightedLedsProperty);
|
||||
set => SetValue(HighlightedLedsProperty, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Lifetime management
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
_deviceImage?.Dispose();
|
||||
_deviceImage = null;
|
||||
|
||||
if (Device != null)
|
||||
{
|
||||
Device.RgbDevice.PropertyChanged -= DevicePropertyChanged;
|
||||
Device.DeviceUpdated -= DeviceUpdated;
|
||||
}
|
||||
|
||||
base.OnDetachedFromVisualTree(e);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
|
||||
{
|
||||
_timer.Start();
|
||||
_timer.Tick += TimerOnTick;
|
||||
base.OnAttachedToLogicalTree(e);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
|
||||
{
|
||||
_timer.Stop();
|
||||
_timer.Tick -= TimerOnTick;
|
||||
base.OnDetachedFromLogicalTree(e);
|
||||
}
|
||||
|
||||
private void SetupForDevice()
|
||||
{
|
||||
_deviceImage?.Dispose();
|
||||
_deviceImage = null;
|
||||
_deviceVisualizerLeds.Clear();
|
||||
_highlightedLeds = new List<DeviceVisualizerLed>();
|
||||
_dimmedLeds = new List<DeviceVisualizerLed>();
|
||||
|
||||
if (_oldDevice != null)
|
||||
{
|
||||
_oldDevice.RgbDevice.PropertyChanged -= DevicePropertyChanged;
|
||||
_oldDevice.DeviceUpdated -= DeviceUpdated;
|
||||
}
|
||||
|
||||
_oldDevice = Device;
|
||||
if (Device == null)
|
||||
return;
|
||||
|
||||
_deviceBounds = MeasureDevice();
|
||||
|
||||
Device.RgbDevice.PropertyChanged += DevicePropertyChanged;
|
||||
Device.DeviceUpdated += DeviceUpdated;
|
||||
|
||||
// Create all the LEDs
|
||||
foreach (ArtemisLed artemisLed in Device.Leds)
|
||||
_deviceVisualizerLeds.Add(new DeviceVisualizerLed(artemisLed));
|
||||
|
||||
// Load the device main image on a background thread
|
||||
ArtemisDevice? device = Device;
|
||||
Task.Run(() =>
|
||||
{
|
||||
if (device.Layout?.Image == null || !File.Exists(device.Layout.Image.LocalPath))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
// Create a bitmap that'll be used to render the device and LED images just once
|
||||
RenderTargetBitmap renderTargetBitmap = new(new PixelSize((int) device.RgbDevice.Size.Width * 4, (int) device.RgbDevice.Size.Height * 4));
|
||||
|
||||
using IDrawingContextImpl context = renderTargetBitmap.CreateDrawingContext(new ImmediateRenderer(this));
|
||||
using Bitmap bitmap = new(device.Layout.Image.LocalPath);
|
||||
context.DrawBitmap(bitmap.PlatformImpl, 1, new Rect(bitmap.Size), new Rect(renderTargetBitmap.Size), BitmapInterpolationMode.HighQuality);
|
||||
foreach (DeviceVisualizerLed deviceVisualizerLed in _deviceVisualizerLeds)
|
||||
deviceVisualizerLed.DrawBitmap(context);
|
||||
|
||||
_deviceImage = renderTargetBitmap;
|
||||
|
||||
Dispatcher.UIThread.Post(InvalidateMeasure);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
if (_deviceBounds.Width <= 0 || _deviceBounds.Height <= 0)
|
||||
return new Size(0, 0);
|
||||
|
||||
double availableWidth = double.IsInfinity(availableSize.Width) ? _deviceBounds.Width : availableSize.Width;
|
||||
double availableHeight = double.IsInfinity(availableSize.Height) ? _deviceBounds.Height : availableSize.Height;
|
||||
double bestRatio = Math.Min(availableWidth / _deviceBounds.Width, availableHeight / _deviceBounds.Height);
|
||||
|
||||
return new Size(_deviceBounds.Width * bestRatio, _deviceBounds.Height * bestRatio);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
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;
|
||||
using SolidColorBrush = Avalonia.Media.SolidColorBrush;
|
||||
|
||||
namespace Artemis.UI.Shared.Controls
|
||||
{
|
||||
internal class DeviceVisualizerLed
|
||||
{
|
||||
private readonly SolidColorBrush _penBrush;
|
||||
private readonly SolidColorBrush _fillBrush;
|
||||
private readonly Pen _pen;
|
||||
|
||||
public DeviceVisualizerLed(ArtemisLed led)
|
||||
{
|
||||
Led = led;
|
||||
LedRect = new Rect(
|
||||
Led.RgbLed.Location.X,
|
||||
Led.RgbLed.Location.Y,
|
||||
Led.RgbLed.Size.Width,
|
||||
Led.RgbLed.Size.Height
|
||||
);
|
||||
|
||||
_fillBrush = new SolidColorBrush();
|
||||
_penBrush = new SolidColorBrush();
|
||||
_pen = new Pen(_penBrush) {LineJoin = PenLineJoin.Round};
|
||||
|
||||
CreateLedGeometry();
|
||||
}
|
||||
|
||||
public ArtemisLed Led { get; }
|
||||
public Rect LedRect { get; set; }
|
||||
public Geometry? DisplayGeometry { get; private set; }
|
||||
|
||||
public void DrawBitmap(IDrawingContextImpl drawingContext)
|
||||
{
|
||||
if (Led.Layout?.Image == null || !File.Exists(Led.Layout.Image.LocalPath))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
using Bitmap bitmap = new(Led.Layout.Image.LocalPath);
|
||||
drawingContext.DrawBitmap(
|
||||
bitmap.PlatformImpl,
|
||||
1,
|
||||
new Rect(bitmap.Size),
|
||||
new Rect(Led.RgbLed.Location.X * 4, Led.RgbLed.Location.Y * 4, Led.RgbLed.Size.Width * 4, Led.RgbLed.Size.Height * 4),
|
||||
BitmapInterpolationMode.HighQuality
|
||||
);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
public void RenderGeometry(DrawingContext drawingContext, bool dimmed)
|
||||
{
|
||||
byte r = Led.RgbLed.Color.GetR();
|
||||
byte g = Led.RgbLed.Color.GetG();
|
||||
byte b = Led.RgbLed.Color.GetB();
|
||||
|
||||
if (dimmed)
|
||||
{
|
||||
_fillBrush.Color = new Color(50, r, g, b);
|
||||
_penBrush.Color = new Color(100, r, g, b);
|
||||
}
|
||||
else
|
||||
{
|
||||
_fillBrush.Color = new Color(100, r, g, b);
|
||||
_penBrush.Color = new Color(255, r, g, b);
|
||||
}
|
||||
|
||||
// Render the LED geometry
|
||||
drawingContext.DrawGeometry(_fillBrush, _pen, DisplayGeometry);
|
||||
}
|
||||
|
||||
public bool HitTest(Point position)
|
||||
{
|
||||
return DisplayGeometry != null && DisplayGeometry.FillContains(position);
|
||||
}
|
||||
|
||||
private void CreateLedGeometry()
|
||||
{
|
||||
// The minimum required size for geometry to be created
|
||||
if (Led.RgbLed.Size.Width < 2 || Led.RgbLed.Size.Height < 2)
|
||||
return;
|
||||
|
||||
switch (Led.RgbLed.Shape)
|
||||
{
|
||||
case Shape.Custom:
|
||||
if (Led.RgbLed.Device.DeviceInfo.DeviceType == RGBDeviceType.Keyboard || Led.RgbLed.Device.DeviceInfo.DeviceType == RGBDeviceType.Keypad)
|
||||
CreateCustomGeometry(2.0);
|
||||
else
|
||||
CreateCustomGeometry(1.0);
|
||||
break;
|
||||
case Shape.Rectangle:
|
||||
if (Led.RgbLed.Device.DeviceInfo.DeviceType == RGBDeviceType.Keyboard || Led.RgbLed.Device.DeviceInfo.DeviceType == RGBDeviceType.Keypad)
|
||||
CreateKeyCapGeometry();
|
||||
else
|
||||
CreateRectangleGeometry();
|
||||
break;
|
||||
case Shape.Circle:
|
||||
CreateCircleGeometry();
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateRectangleGeometry()
|
||||
{
|
||||
DisplayGeometry = new RectangleGeometry(new Rect(Led.RgbLed.Location.X + 0.5, Led.RgbLed.Location.Y + 0.5, Led.RgbLed.Size.Width - 1, Led.RgbLed.Size.Height - 1));
|
||||
}
|
||||
|
||||
private void CreateCircleGeometry()
|
||||
{
|
||||
DisplayGeometry = new EllipseGeometry(new Rect(Led.RgbLed.Location.X + 0.5, Led.RgbLed.Location.Y + 0.5, Led.RgbLed.Size.Width - 1, Led.RgbLed.Size.Height - 1));
|
||||
}
|
||||
|
||||
private void CreateKeyCapGeometry()
|
||||
{
|
||||
DisplayGeometry = new RectangleGeometry(new Rect(Led.RgbLed.Location.X + 1, Led.RgbLed.Location.Y + 1, Led.RgbLed.Size.Width - 2, Led.RgbLed.Size.Height - 2));
|
||||
}
|
||||
|
||||
private void CreateCustomGeometry(double deflateAmount)
|
||||
{
|
||||
try
|
||||
{
|
||||
double width = Led.RgbLed.Size.Width - deflateAmount;
|
||||
double height = Led.RgbLed.Size.Height - deflateAmount;
|
||||
|
||||
Geometry geometry = Geometry.Parse(Led.RgbLed.ShapeData);
|
||||
geometry.Transform = new TransformGroup
|
||||
{
|
||||
Children = new Transforms
|
||||
{
|
||||
new ScaleTransform(width, height),
|
||||
new TranslateTransform(Led.RgbLed.Location.X + deflateAmount / 2, Led.RgbLed.Location.Y + deflateAmount / 2)
|
||||
}
|
||||
};
|
||||
DisplayGeometry = geometry;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
CreateRectangleGeometry();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Shared.Controls.EnumComboBox">
|
||||
<ComboBox x:Name="EnumComboBox" HorizontalAlignment="Stretch">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding [0]}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</UserControl>
|
||||
@@ -1,115 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Artemis.Core;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.LogicalTree;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace Artemis.UI.Shared.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a combobox that can display the values of an enum.
|
||||
/// </summary>
|
||||
public 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);
|
||||
|
||||
private readonly ObservableCollection<(Enum, string)> _currentValues = new();
|
||||
|
||||
private ComboBox? _enumComboBox;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="EnumComboBox" /> class.
|
||||
/// </summary>
|
||||
public EnumComboBox()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the currently selected value
|
||||
/// </summary>
|
||||
public object? Value
|
||||
{
|
||||
get => GetValue(ValueProperty);
|
||||
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)
|
||||
return;
|
||||
|
||||
(Enum enumValue, _) = _currentValues[_enumComboBox.SelectedIndex];
|
||||
if (!Equals(Value, enumValue))
|
||||
Value = enumValue;
|
||||
}
|
||||
|
||||
private void UpdateValues()
|
||||
{
|
||||
Type? newType = Value?.GetType();
|
||||
if (_enumComboBox == null || _currentValues.Any() || newType is not {IsEnum: true})
|
||||
return;
|
||||
|
||||
foreach ((Enum, string) valueDesc in EnumUtilities.GetAllValuesAndDescriptions(newType))
|
||||
_currentValues.Add(valueDesc);
|
||||
}
|
||||
|
||||
private void UpdateSelection()
|
||||
{
|
||||
if (_enumComboBox == null || Value is not Enum)
|
||||
return;
|
||||
|
||||
(Enum, string) value = _currentValues.FirstOrDefault(v => v.Item1.Equals(Value));
|
||||
if (!Equals(value.Item1, _enumComboBox.SelectedItem))
|
||||
_enumComboBox.SelectedItem = value;
|
||||
}
|
||||
|
||||
#region Overrides of TemplatedControl
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
|
||||
{
|
||||
_enumComboBox = this.Get<ComboBox>("EnumComboBox");
|
||||
_enumComboBox.Items = _currentValues;
|
||||
|
||||
UpdateValues();
|
||||
UpdateSelection();
|
||||
_enumComboBox.SelectionChanged += OnSelectionChanged;
|
||||
|
||||
base.OnAttachedToLogicalTree(e);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
|
||||
{
|
||||
if (_enumComboBox != null)
|
||||
_enumComboBox.SelectionChanged -= OnSelectionChanged;
|
||||
|
||||
base.OnDetachedFromLogicalTree(e);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using Avalonia.Controls;
|
||||
using FluentAvalonia.Core;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using FluentAvalonia.UI.Controls.Primitives;
|
||||
|
||||
namespace Artemis.UI.Shared.Controls.Flyouts;
|
||||
|
||||
/// <summary>
|
||||
/// Defines a flyout that hosts a data model picker.
|
||||
/// </summary>
|
||||
public sealed class DataModelPickerFlyout : PickerFlyoutBase
|
||||
{
|
||||
private DataModelPicker.DataModelPicker? _picker;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data model picker that the flyout hosts.
|
||||
/// </summary>
|
||||
public DataModelPicker.DataModelPicker DataModelPicker => _picker ??= new DataModelPicker.DataModelPicker();
|
||||
|
||||
/// <summary>
|
||||
/// Raised when the Confirmed button is tapped indicating the new Color should be applied
|
||||
/// </summary>
|
||||
public event TypedEventHandler<DataModelPickerFlyout, object>? Confirmed;
|
||||
|
||||
/// <summary>
|
||||
/// Raised when the Dismiss button is tapped, indicating the new color should not be applied
|
||||
/// </summary>
|
||||
public event TypedEventHandler<DataModelPickerFlyout, object>? Dismissed;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Control CreatePresenter()
|
||||
{
|
||||
_picker ??= new DataModelPicker.DataModelPicker();
|
||||
PickerFlyoutPresenter presenter = new() {Content = DataModelPicker};
|
||||
presenter.Confirmed += OnFlyoutConfirmed;
|
||||
presenter.Dismissed += OnFlyoutDismissed;
|
||||
|
||||
return presenter;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnConfirmed()
|
||||
{
|
||||
Confirmed?.Invoke(this, EventArgs.Empty);
|
||||
Hide();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnOpening(CancelEventArgs args)
|
||||
{
|
||||
base.OnOpening(args);
|
||||
(Popup.Child as PickerFlyoutPresenter)?.Classes.Set(":acceptdismiss", true);
|
||||
}
|
||||
|
||||
private void OnFlyoutDismissed(PickerFlyoutPresenter sender, object args)
|
||||
{
|
||||
Dismissed?.Invoke(this, EventArgs.Empty);
|
||||
Hide();
|
||||
}
|
||||
|
||||
private void OnFlyoutConfirmed(PickerFlyoutPresenter sender, object args)
|
||||
{
|
||||
OnConfirmed();
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using Avalonia.Controls;
|
||||
|
||||
namespace Artemis.UI.Shared.Controls.Flyouts;
|
||||
|
||||
/// <summary>
|
||||
/// Defines a flyout that hosts a gradient picker.
|
||||
/// </summary>
|
||||
public sealed class GradientPickerFlyout : Flyout
|
||||
{
|
||||
private GradientPicker.GradientPicker? _picker;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the gradient picker that this flyout hosts.
|
||||
/// </summary>
|
||||
public GradientPicker.GradientPicker GradientPicker => _picker ??= new GradientPicker.GradientPicker();
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Control CreatePresenter()
|
||||
{
|
||||
_picker ??= new GradientPicker.GradientPicker();
|
||||
FlyoutPresenter presenter = new() {Content = GradientPicker};
|
||||
return presenter;
|
||||
}
|
||||
}
|
||||
@@ -1,325 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Windows.Input;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Shared.Providers;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Media;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using FluentAvalonia.UI.Media;
|
||||
using ReactiveUI;
|
||||
using Button = Avalonia.Controls.Button;
|
||||
|
||||
namespace Artemis.UI.Shared.Controls.GradientPicker;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a gradient picker that can be used to edit a gradient.
|
||||
/// </summary>
|
||||
public class GradientPicker : TemplatedControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the color gradient.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<ColorGradient> ColorGradientProperty =
|
||||
AvaloniaProperty.Register<GradientPicker, ColorGradient>(nameof(ColorGradient), notifying: ColorGradientChanged, defaultValue: ColorGradient.GetUnicornBarf());
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the currently selected color stop.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<ColorGradientStop?> SelectedColorStopProperty =
|
||||
AvaloniaProperty.Register<GradientPicker, ColorGradientStop?>(nameof(SelectedColorStop), defaultBindingMode: BindingMode.TwoWay);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether the gradient picker should be in compact mode or not.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<bool> IsCompactProperty =
|
||||
AvaloniaProperty.Register<GradientPicker, bool>(nameof(IsCompact), defaultBindingMode: BindingMode.TwoWay);
|
||||
|
||||
/// <summary>
|
||||
/// 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);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the linear gradient brush representing the color gradient.
|
||||
/// </summary>
|
||||
public static readonly DirectProperty<GradientPicker, LinearGradientBrush> LinearGradientBrushProperty =
|
||||
AvaloniaProperty.RegisterDirect<GradientPicker, LinearGradientBrush>(nameof(LinearGradientBrush), g => g.LinearGradientBrush);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the command to execute when deleting stops.
|
||||
/// </summary>
|
||||
public static readonly DirectProperty<GradientPicker, ICommand> DeleteStopProperty =
|
||||
AvaloniaProperty.RegisterDirect<GradientPicker, ICommand>(nameof(DeleteStop), g => g.DeleteStop);
|
||||
|
||||
private readonly ICommand _deleteStop;
|
||||
private bool _shiftDown;
|
||||
private Button? _flipStops;
|
||||
private Border? _gradient;
|
||||
private Button? _rotateStops;
|
||||
private Button? _spreadStops;
|
||||
private Button? _toggleSeamless;
|
||||
private Button? _randomize;
|
||||
private ColorGradient? _lastColorGradient;
|
||||
private ColorPicker? _colorPicker;
|
||||
|
||||
public GradientPicker()
|
||||
{
|
||||
_deleteStop = ReactiveCommand.Create<ColorGradientStop>(s =>
|
||||
{
|
||||
if (ColorGradient.Count <= 2)
|
||||
return;
|
||||
|
||||
int index = ColorGradient.IndexOf(s);
|
||||
ColorGradient.Remove(s);
|
||||
if (index > ColorGradient.Count - 1)
|
||||
index--;
|
||||
|
||||
SelectedColorStop = ColorGradient.ElementAtOrDefault(index);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color gradient.
|
||||
/// </summary>
|
||||
public ColorGradient ColorGradient
|
||||
{
|
||||
get => GetValue(ColorGradientProperty);
|
||||
set => SetValue(ColorGradientProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the currently selected color stop.
|
||||
/// </summary>
|
||||
public ColorGradientStop? SelectedColorStop
|
||||
{
|
||||
get => GetValue(SelectedColorStopProperty);
|
||||
set
|
||||
{
|
||||
if (_colorPicker != null && SelectedColorStop != null)
|
||||
_colorPicker.PreviousColor = new Color2(SelectedColorStop.Color.Red, SelectedColorStop.Color.Green, SelectedColorStop.Color.Blue, SelectedColorStop.Color.Alpha);
|
||||
SetValue(SelectedColorStopProperty, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether the gradient picker should be in compact mode or not.
|
||||
/// </summary>
|
||||
public bool IsCompact
|
||||
{
|
||||
get => GetValue(IsCompactProperty);
|
||||
set => SetValue(IsCompactProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a storage provider to use for storing and loading gradients.
|
||||
/// </summary>
|
||||
public IColorGradientStorageProvider? StorageProvider
|
||||
{
|
||||
get => GetValue(StorageProviderProperty);
|
||||
set => SetValue(StorageProviderProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the linear gradient brush representing the color gradient.
|
||||
/// </summary>
|
||||
public LinearGradientBrush LinearGradientBrush { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the command to execute when deleting stops.
|
||||
/// </summary>
|
||||
public ICommand DeleteStop
|
||||
{
|
||||
get => _deleteStop;
|
||||
private init => SetAndRaise(DeleteStopProperty, ref _deleteStop, value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
{
|
||||
if (_gradient != null)
|
||||
_gradient.PointerPressed -= GradientOnPointerPressed;
|
||||
if (_spreadStops != null)
|
||||
_spreadStops.Click -= SpreadStopsOnClick;
|
||||
if (_toggleSeamless != null)
|
||||
_toggleSeamless.Click -= ToggleSeamlessOnClick;
|
||||
if (_flipStops != null)
|
||||
_flipStops.Click -= FlipStopsOnClick;
|
||||
if (_rotateStops != null)
|
||||
_rotateStops.Click -= RotateStopsOnClick;
|
||||
if (_randomize != null)
|
||||
_randomize.Click -= RandomizeOnClick;
|
||||
|
||||
_colorPicker = e.NameScope.Find<ColorPicker>("ColorPicker");
|
||||
_gradient = e.NameScope.Find<Border>("Gradient");
|
||||
_spreadStops = e.NameScope.Find<Button>("SpreadStops");
|
||||
_toggleSeamless = e.NameScope.Find<Button>("ToggleSeamless");
|
||||
_flipStops = e.NameScope.Find<Button>("FlipStops");
|
||||
_rotateStops = e.NameScope.Find<Button>("RotateStops");
|
||||
_randomize = e.NameScope.Find<Button>("Randomize");
|
||||
|
||||
if (_gradient != null)
|
||||
_gradient.PointerPressed += GradientOnPointerPressed;
|
||||
if (_spreadStops != null)
|
||||
_spreadStops.Click += SpreadStopsOnClick;
|
||||
if (_toggleSeamless != null)
|
||||
_toggleSeamless.Click += ToggleSeamlessOnClick;
|
||||
if (_flipStops != null)
|
||||
_flipStops.Click += FlipStopsOnClick;
|
||||
if (_rotateStops != null)
|
||||
_rotateStops.Click += RotateStopsOnClick;
|
||||
if (_randomize != null)
|
||||
_randomize.Click += RandomizeOnClick;
|
||||
|
||||
base.OnApplyTemplate(e);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
Subscribe();
|
||||
|
||||
KeyUp += OnKeyUp;
|
||||
KeyDown += OnKeyDown;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
Unsubscribe();
|
||||
KeyUp -= OnKeyUp;
|
||||
KeyDown -= OnKeyDown;
|
||||
|
||||
_shiftDown = false;
|
||||
}
|
||||
|
||||
private static void ColorGradientChanged(IAvaloniaObject sender, bool before)
|
||||
{
|
||||
(sender as GradientPicker)?.Subscribe();
|
||||
}
|
||||
|
||||
private static void StorageProviderChanged(IAvaloniaObject sender, bool before)
|
||||
{
|
||||
}
|
||||
|
||||
private void Subscribe()
|
||||
{
|
||||
Unsubscribe();
|
||||
|
||||
ColorGradient.CollectionChanged += ColorGradientOnCollectionChanged;
|
||||
ColorGradient.StopChanged += ColorGradientOnStopChanged;
|
||||
SelectedColorStop = ColorGradient.FirstOrDefault();
|
||||
|
||||
UpdateGradient();
|
||||
|
||||
_lastColorGradient = ColorGradient;
|
||||
}
|
||||
|
||||
private void Unsubscribe()
|
||||
{
|
||||
if (_lastColorGradient == null)
|
||||
return;
|
||||
|
||||
_lastColorGradient.CollectionChanged -= ColorGradientOnCollectionChanged;
|
||||
_lastColorGradient.StopChanged -= ColorGradientOnStopChanged;
|
||||
_lastColorGradient = null;
|
||||
}
|
||||
|
||||
private void ColorGradientOnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
UpdateGradient();
|
||||
}
|
||||
|
||||
private void ColorGradientOnStopChanged(object? sender, EventArgs e)
|
||||
{
|
||||
UpdateGradient();
|
||||
}
|
||||
|
||||
private void UpdateGradient()
|
||||
{
|
||||
// Update the display gradient
|
||||
GradientStops collection = new();
|
||||
foreach (ColorGradientStop c in ColorGradient.OrderBy(s => s.Position))
|
||||
collection.Add(new GradientStop(Color.FromArgb(c.Color.Alpha, c.Color.Red, c.Color.Green, c.Color.Blue), c.Position));
|
||||
|
||||
LinearGradientBrush.GradientStops = collection;
|
||||
}
|
||||
|
||||
private void GradientOnPointerPressed(object? sender, PointerPressedEventArgs e)
|
||||
{
|
||||
if (_gradient == null)
|
||||
return;
|
||||
|
||||
float position = (float) (e.GetPosition(_gradient).X / _gradient.Bounds.Width);
|
||||
|
||||
ColorGradientStop newStop = new(ColorGradient.GetColor(position), position);
|
||||
ColorGradient.Add(newStop);
|
||||
SelectedColorStop = newStop;
|
||||
}
|
||||
|
||||
private void OnKeyDown(object? sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key is Key.LeftShift or Key.RightShift)
|
||||
_shiftDown = true;
|
||||
}
|
||||
|
||||
private void OnKeyUp(object? sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key is Key.LeftShift or Key.RightShift)
|
||||
_shiftDown = false;
|
||||
|
||||
if (e.Key != Key.Delete || SelectedColorStop == null || ColorGradient.Count <= 2)
|
||||
return;
|
||||
|
||||
int index = ColorGradient.IndexOf(SelectedColorStop);
|
||||
ColorGradient.Remove(SelectedColorStop);
|
||||
if (index > ColorGradient.Count - 1)
|
||||
index--;
|
||||
|
||||
SelectedColorStop = ColorGradient.ElementAtOrDefault(index);
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void SpreadStopsOnClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
ColorGradient.SpreadStops();
|
||||
}
|
||||
|
||||
private void ToggleSeamlessOnClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
if (SelectedColorStop == null || ColorGradient.Count < 2)
|
||||
return;
|
||||
|
||||
ColorGradient.ToggleSeamless();
|
||||
}
|
||||
|
||||
private void FlipStopsOnClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
if (SelectedColorStop == null || ColorGradient.Count < 2)
|
||||
return;
|
||||
|
||||
ColorGradient.FlipStops();
|
||||
}
|
||||
|
||||
private void RotateStopsOnClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
if (SelectedColorStop == null || ColorGradient.Count < 2)
|
||||
return;
|
||||
|
||||
ColorGradient.RotateStops(_shiftDown);
|
||||
}
|
||||
|
||||
private void RandomizeOnClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
ColorGradient.Randomize(6);
|
||||
SelectedColorStop = ColorGradient.First();
|
||||
}
|
||||
}
|
||||
@@ -1,212 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Shared.Controls.Flyouts;
|
||||
using Artemis.UI.Shared.Providers;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Media;
|
||||
using FluentAvalonia.Core;
|
||||
using Button = FluentAvalonia.UI.Controls.Button;
|
||||
|
||||
namespace Artemis.UI.Shared.Controls.GradientPicker;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a gradient picker box that can be used to edit a gradient
|
||||
/// </summary>
|
||||
public class GradientPickerButton : TemplatedControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the color gradient.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<ColorGradient?> ColorGradientProperty =
|
||||
AvaloniaProperty.Register<GradientPickerButton, ColorGradient?>(nameof(ColorGradient), notifying: ColorGradientChanged, defaultValue: ColorGradient.GetUnicornBarf());
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether the gradient picker should be in compact mode or not.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<bool> IsCompactProperty =
|
||||
AvaloniaProperty.Register<GradientPickerButton, bool>(nameof(IsCompact), defaultBindingMode: BindingMode.TwoWay);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a storage provider to use for storing and loading gradients.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<IColorGradientStorageProvider?> StorageProviderProperty =
|
||||
AvaloniaProperty.Register<GradientPickerButton, IColorGradientStorageProvider?>(nameof(StorageProvider));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the linear gradient brush representing the color gradient.
|
||||
/// </summary>
|
||||
public static readonly DirectProperty<GradientPickerButton, LinearGradientBrush> LinearGradientBrushProperty =
|
||||
AvaloniaProperty.RegisterDirect<GradientPickerButton, LinearGradientBrush>(nameof(LinearGradientBrush), g => g.LinearGradientBrush);
|
||||
|
||||
private ColorGradient? _lastColorGradient;
|
||||
private Button? _button;
|
||||
private GradientPickerFlyout? _flyout;
|
||||
private bool _flyoutActive;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color gradient.
|
||||
/// </summary>
|
||||
public ColorGradient? ColorGradient
|
||||
{
|
||||
get => GetValue(ColorGradientProperty);
|
||||
set => SetValue(ColorGradientProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether the gradient picker should be in compact mode or not.
|
||||
/// </summary>
|
||||
public bool IsCompact
|
||||
{
|
||||
get => GetValue(IsCompactProperty);
|
||||
set => SetValue(IsCompactProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a storage provider to use for storing and loading gradients.
|
||||
/// </summary>
|
||||
public IColorGradientStorageProvider? StorageProvider
|
||||
{
|
||||
get => GetValue(StorageProviderProperty);
|
||||
set => SetValue(StorageProviderProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the linear gradient brush representing the color gradient.
|
||||
/// </summary>
|
||||
public LinearGradientBrush LinearGradientBrush { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Raised when the flyout opens.
|
||||
/// </summary>
|
||||
public event TypedEventHandler<GradientPickerButton, EventArgs>? FlyoutOpened;
|
||||
|
||||
/// <summary>
|
||||
/// Raised when the flyout closes.
|
||||
/// </summary>
|
||||
public event TypedEventHandler<GradientPickerButton, EventArgs>? FlyoutClosed;
|
||||
|
||||
#region Overrides of TemplatedControl
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
{
|
||||
if (_button != null)
|
||||
_button.Click -= OnButtonClick;
|
||||
base.OnApplyTemplate(e);
|
||||
_button = e.NameScope.Find<Button>("MainButton");
|
||||
if (_button != null)
|
||||
_button.Click += OnButtonClick;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private static void ColorGradientChanged(IAvaloniaObject sender, bool before)
|
||||
{
|
||||
(sender as GradientPickerButton)?.Subscribe();
|
||||
}
|
||||
|
||||
private void Subscribe()
|
||||
{
|
||||
Unsubscribe();
|
||||
|
||||
if (ColorGradient != null)
|
||||
{
|
||||
ColorGradient.CollectionChanged += ColorGradientOnCollectionChanged;
|
||||
ColorGradient.StopChanged += ColorGradientOnStopChanged;
|
||||
}
|
||||
|
||||
UpdateGradient();
|
||||
_lastColorGradient = ColorGradient;
|
||||
}
|
||||
|
||||
private void Unsubscribe()
|
||||
{
|
||||
if (_lastColorGradient == null)
|
||||
return;
|
||||
|
||||
_lastColorGradient.CollectionChanged -= ColorGradientOnCollectionChanged;
|
||||
_lastColorGradient.StopChanged -= ColorGradientOnStopChanged;
|
||||
_lastColorGradient = null;
|
||||
}
|
||||
|
||||
private void ColorGradientOnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
UpdateGradient();
|
||||
}
|
||||
|
||||
private void ColorGradientOnStopChanged(object? sender, EventArgs e)
|
||||
{
|
||||
UpdateGradient();
|
||||
}
|
||||
|
||||
private void OnButtonClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_flyout == null || ColorGradient == null)
|
||||
return;
|
||||
|
||||
// Logic here is taken from Fluent Avalonia's ColorPicker which also reuses the same control since it's large
|
||||
_flyout.GradientPicker.ColorGradient = ColorGradient;
|
||||
_flyout.GradientPicker.IsCompact = IsCompact;
|
||||
_flyout.GradientPicker.StorageProvider = StorageProvider;
|
||||
|
||||
_flyout.ShowAt(this);
|
||||
_flyoutActive = true;
|
||||
|
||||
FlyoutOpened?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
|
||||
private void OnFlyoutClosed(object? sender, EventArgs e)
|
||||
{
|
||||
if (_flyoutActive)
|
||||
{
|
||||
FlyoutClosed?.Invoke(this, EventArgs.Empty);
|
||||
_flyoutActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateGradient()
|
||||
{
|
||||
// Update the display gradient
|
||||
GradientStops collection = new();
|
||||
if (ColorGradient != null)
|
||||
{
|
||||
foreach (ColorGradientStop c in ColorGradient.OrderBy(s => s.Position))
|
||||
collection.Add(new GradientStop(Color.FromArgb(c.Color.Alpha, c.Color.Red, c.Color.Green, c.Color.Blue), c.Position));
|
||||
}
|
||||
|
||||
LinearGradientBrush.GradientStops = collection;
|
||||
}
|
||||
|
||||
#region Overrides of Visual
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
Subscribe();
|
||||
|
||||
if (_flyout == null)
|
||||
{
|
||||
_flyout = new GradientPickerFlyout();
|
||||
_flyout.FlyoutPresenterClasses.Add("gradient-picker-presenter");
|
||||
}
|
||||
|
||||
_flyout.Closed += OnFlyoutClosed;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
Unsubscribe();
|
||||
if (_flyout != null)
|
||||
_flyout.Closed -= OnFlyoutClosed;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -1,199 +0,0 @@
|
||||
using System;
|
||||
using Artemis.Core;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Metadata;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Input;
|
||||
|
||||
namespace Artemis.UI.Shared.Controls.GradientPicker;
|
||||
|
||||
public class GradientPickerColorStop : TemplatedControl
|
||||
{
|
||||
private static ColorGradientStop? _draggingStop;
|
||||
private static IPointer? _dragPointer;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the gradient picker.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<GradientPicker?> GradientPickerProperty =
|
||||
AvaloniaProperty.Register<GradientPickerColorStop, GradientPicker?>(nameof(GradientPicker), notifying: Notifying);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color stop.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<ColorGradientStop> ColorStopProperty =
|
||||
AvaloniaProperty.Register<GradientPickerColorStop, ColorGradientStop>(nameof(ColorStop));
|
||||
|
||||
/// <summary>
|
||||
/// 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));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the linear gradient brush representing the color gradient.
|
||||
/// </summary>
|
||||
public static readonly DirectProperty<GradientPickerColorStop, bool> IsSelectedProperty =
|
||||
AvaloniaProperty.RegisterDirect<GradientPickerColorStop, bool>(nameof(IsSelected), g => g.IsSelected);
|
||||
|
||||
private bool _isSelected;
|
||||
private double _dragOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the gradient picker.
|
||||
/// </summary>
|
||||
public GradientPicker? GradientPicker
|
||||
{
|
||||
get => GetValue(GradientPickerProperty);
|
||||
set => SetValue(GradientPickerProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color stop.
|
||||
/// </summary>
|
||||
public ColorGradientStop ColorStop
|
||||
{
|
||||
get => GetValue(ColorStopProperty);
|
||||
set => SetValue(ColorStopProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
{
|
||||
get => GetValue(PositionReferenceProperty);
|
||||
set => SetValue(PositionReferenceProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the linear gradient brush representing the color gradient.
|
||||
/// </summary>
|
||||
public bool IsSelected
|
||||
{
|
||||
get => _isSelected;
|
||||
private set
|
||||
{
|
||||
SetAndRaise(IsSelectedProperty, ref _isSelected, value);
|
||||
if (IsSelected)
|
||||
PseudoClasses.Add(":selected");
|
||||
else
|
||||
PseudoClasses.Remove(":selected");
|
||||
}
|
||||
}
|
||||
|
||||
private void GradientPickerOnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (GradientPicker != null && e.Property == GradientPicker.SelectedColorStopProperty)
|
||||
{
|
||||
IsSelected = GradientPicker.SelectedColorStop == ColorStop;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPointerPressed(object? sender, PointerPressedEventArgs e)
|
||||
{
|
||||
if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed || PositionReference == null)
|
||||
return;
|
||||
|
||||
_dragOffset = e.GetCurrentPoint(PositionReference).Position.X - GetPixelPosition();
|
||||
e.Pointer.Capture(this);
|
||||
|
||||
// Store these in case the control is being recreated due to an array resort
|
||||
// it's a bit ugly but it gives us a way to pick up dragging again with the new control
|
||||
_dragPointer = e.Pointer;
|
||||
_draggingStop = ColorStop;
|
||||
e.Handled = true;
|
||||
|
||||
if (GradientPicker != null)
|
||||
GradientPicker.SelectedColorStop = ColorStop;
|
||||
}
|
||||
|
||||
private void OnPointerMoved(object? sender, PointerEventArgs e)
|
||||
{
|
||||
if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed || !ReferenceEquals(e.Pointer.Captured, this) || PositionReference == null)
|
||||
{
|
||||
if (_draggingStop != ColorStop)
|
||||
return;
|
||||
|
||||
_dragOffset = e.GetCurrentPoint(PositionReference).Position.X - GetPixelPosition();
|
||||
}
|
||||
|
||||
double position = e.GetCurrentPoint(PositionReference).Position.X - _dragOffset;
|
||||
ColorStop.Position = MathF.Round((float) Math.Clamp(position / PositionReference.Bounds.Width, 0, 1), 3, MidpointRounding.AwayFromZero);
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void OnPointerReleased(object? sender, PointerReleasedEventArgs e)
|
||||
{
|
||||
if (e.InitialPressMouseButton != MouseButton.Left)
|
||||
return;
|
||||
|
||||
e.Pointer.Capture(null);
|
||||
e.Handled = true;
|
||||
_draggingStop = null;
|
||||
}
|
||||
|
||||
private double GetPixelPosition()
|
||||
{
|
||||
if (PositionReference == null)
|
||||
return 0;
|
||||
|
||||
return PositionReference.Bounds.Width * ColorStop.Position;
|
||||
}
|
||||
|
||||
#region Overrides of Visual
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
if (GradientPicker != null)
|
||||
GradientPicker.PropertyChanged += GradientPickerOnPropertyChanged;
|
||||
|
||||
if (PositionReference != null)
|
||||
{
|
||||
PointerPressed += OnPointerPressed;
|
||||
PointerMoved += OnPointerMoved;
|
||||
PointerReleased += OnPointerReleased;
|
||||
|
||||
// If this stop was previously being dragged, carry on dragging again
|
||||
// This can happen if the control was recreated due to an array sort
|
||||
if (_draggingStop == ColorStop && _dragPointer != null)
|
||||
{
|
||||
_dragPointer.Capture(this);
|
||||
IsSelected = true;
|
||||
}
|
||||
}
|
||||
|
||||
base.OnAttachedToVisualTree(e);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
if (GradientPicker != null)
|
||||
GradientPicker.PropertyChanged -= GradientPickerOnPropertyChanged;
|
||||
PointerPressed -= OnPointerPressed;
|
||||
PointerMoved -= OnPointerMoved;
|
||||
PointerReleased -= OnPointerReleased;
|
||||
|
||||
base.OnDetachedFromVisualTree(e);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:controls="clr-namespace:Artemis.UI.Shared.Controls"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Shared.Controls.HotkeyBox">
|
||||
|
||||
<UserControl.Styles>
|
||||
<Style Selector="TextBox#DisplayTextBox:focus:not(TextBox:empty)">
|
||||
<Setter Property="InnerRightContent">
|
||||
<Template>
|
||||
<Button Classes="textBoxClearButton"
|
||||
Click="Button_OnClick" />
|
||||
</Template>
|
||||
</Setter>
|
||||
</Style>
|
||||
</UserControl.Styles>
|
||||
|
||||
<controls:NoInputTextBox x:Name="DisplayTextBox"
|
||||
Watermark="{Binding $parent.Watermark}"
|
||||
UseFloatingWatermark="{Binding $parent.UseFloatingWatermark}"
|
||||
Classes="clearButton"
|
||||
IsReadOnly="True"
|
||||
HorizontalAlignment="Stretch" />
|
||||
</UserControl>
|
||||
@@ -1,138 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Humanizer;
|
||||
using Material.Icons;
|
||||
|
||||
namespace Artemis.UI.Shared.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a control that can be used to display or edit <see cref="Core.Hotkey"/> instances.
|
||||
/// </summary>
|
||||
public class HotkeyBox : UserControl
|
||||
{
|
||||
private readonly TextBox _displayTextBox;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="HotkeyBox"/> class
|
||||
/// </summary>
|
||||
public HotkeyBox()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
_displayTextBox = this.Find<TextBox>("DisplayTextBox");
|
||||
_displayTextBox.KeyDown += DisplayTextBoxOnKeyDown;
|
||||
_displayTextBox.KeyUp += DisplayTextBoxOnKeyUp;
|
||||
UpdateDisplayTextBox();
|
||||
}
|
||||
|
||||
private static void HotkeyChanging(IAvaloniaObject sender, bool before)
|
||||
{
|
||||
((HotkeyBox) sender).UpdateDisplayTextBox();
|
||||
}
|
||||
|
||||
private void DisplayTextBoxOnKeyDown(object? sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key >= Key.LeftShift && e.Key <= Key.RightAlt)
|
||||
return;
|
||||
|
||||
Hotkey ??= new Hotkey();
|
||||
Hotkey.Key = (KeyboardKey?) e.Key;
|
||||
Hotkey.Modifiers = (KeyboardModifierKey?) e.KeyModifiers;
|
||||
UpdateDisplayTextBox();
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void DisplayTextBoxOnKeyUp(object? sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.KeyModifiers == KeyModifiers.None)
|
||||
FocusManager.Instance?.Focus(null);
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void UpdateDisplayTextBox()
|
||||
{
|
||||
string? display = null;
|
||||
if (Hotkey?.Modifiers != null)
|
||||
display = string.Join("+", Enum.GetValues<KeyboardModifierKey>().Skip(1).Where(m => Hotkey.Modifiers.Value.HasFlag(m)).Select(v => v.Humanize()));
|
||||
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);
|
||||
}
|
||||
|
||||
private void Button_OnClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
Hotkey = null;
|
||||
FocusManager.Instance?.Focus(null);
|
||||
|
||||
UpdateDisplayTextBox();
|
||||
}
|
||||
|
||||
#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<Hotkey?> HotkeyProperty =
|
||||
AvaloniaProperty.Register<HotkeyBox, Hotkey?>(nameof(Hotkey), defaultBindingMode: BindingMode.TwoWay, notifying: HotkeyChanging);
|
||||
|
||||
/// <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));
|
||||
|
||||
/// <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));
|
||||
|
||||
/// <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 Hotkey? Hotkey
|
||||
{
|
||||
get => GetValue(HotkeyProperty);
|
||||
set => SetValue(HotkeyProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the watermark of the hotkey box when it is empty.
|
||||
/// </summary>
|
||||
public string? Watermark
|
||||
{
|
||||
get => GetValue(WatermarkProperty);
|
||||
set => SetValue(WatermarkProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether the watermark should float above the hotkey box when it is not empty.
|
||||
/// </summary>
|
||||
public bool UseFloatingWatermark
|
||||
{
|
||||
get => GetValue(UseFloatingWatermarkProperty);
|
||||
set => SetValue(UseFloatingWatermarkProperty, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using System;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Styling;
|
||||
|
||||
namespace Artemis.UI.Shared.Controls
|
||||
{
|
||||
internal class NoInputTextBox : TextBox, IStyleable
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void OnKeyDown(KeyEventArgs e)
|
||||
{
|
||||
// Don't call the base method on purpose
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnKeyUp(KeyEventArgs e)
|
||||
{
|
||||
// Don't call the base method on purpose
|
||||
}
|
||||
|
||||
Type IStyleable.StyleKey => typeof(TextBox);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Shared.Controls.ProfileConfigurationIcon">
|
||||
</UserControl>
|
||||
@@ -1,125 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Artemis.Core;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.LogicalTree;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Svg.Skia;
|
||||
using Material.Icons;
|
||||
using Material.Icons.Avalonia;
|
||||
|
||||
namespace Artemis.UI.Shared.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a control that can display the icon of a specific <see cref="ProfileConfiguration"/>.
|
||||
/// </summary>
|
||||
public class ProfileConfigurationIcon : UserControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="ProfileConfigurationIcon" /> class.
|
||||
/// </summary>
|
||||
public ProfileConfigurationIcon()
|
||||
{
|
||||
InitializeComponent();
|
||||
DetachedFromLogicalTree += OnDetachedFromLogicalTree;
|
||||
PropertyChanged += OnPropertyChanged;
|
||||
}
|
||||
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (ConfigurationIcon == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
if (ConfigurationIcon.IconType == ProfileConfigurationIconType.MaterialIcon)
|
||||
{
|
||||
Content = Enum.TryParse(ConfigurationIcon.IconName, true, out MaterialIconKind parsedIcon)
|
||||
? new MaterialIcon {Kind = parsedIcon!}
|
||||
: new MaterialIcon {Kind = MaterialIconKind.QuestionMark};
|
||||
return;
|
||||
}
|
||||
|
||||
Stream? stream = ConfigurationIcon.GetIconStream();
|
||||
if (stream == null)
|
||||
{
|
||||
Content = new MaterialIcon {Kind = MaterialIconKind.QuestionMark};
|
||||
return;
|
||||
}
|
||||
|
||||
if (ConfigurationIcon.IconType == ProfileConfigurationIconType.SvgImage)
|
||||
{
|
||||
SvgSource source = new();
|
||||
source.Load(stream);
|
||||
Content = new Image {Source = new SvgImage {Source = source}};
|
||||
}
|
||||
else if (ConfigurationIcon.IconType == ProfileConfigurationIconType.BitmapImage)
|
||||
{
|
||||
Content = new Image {Source = new Bitmap(ConfigurationIcon.GetIconStream())};
|
||||
}
|
||||
else
|
||||
{
|
||||
Content = new MaterialIcon {Kind = MaterialIconKind.QuestionMark};
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
Content = new MaterialIcon {Kind = MaterialIconKind.QuestionMark};
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
private void OnDetachedFromLogicalTree(object? sender, LogicalTreeAttachmentEventArgs e)
|
||||
{
|
||||
if (ConfigurationIcon != null)
|
||||
ConfigurationIcon.IconUpdated -= ConfigurationIconOnIconUpdated;
|
||||
|
||||
if (Content is Image image && image.Source is IDisposable disposable)
|
||||
disposable.Dispose();
|
||||
}
|
||||
|
||||
private void OnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.Property != ConfigurationIconProperty)
|
||||
return;
|
||||
|
||||
if (e.OldValue is Core.ProfileConfigurationIcon oldIcon)
|
||||
oldIcon.IconUpdated -= ConfigurationIconOnIconUpdated;
|
||||
if (e.NewValue is Core.ProfileConfigurationIcon newIcon)
|
||||
newIcon.IconUpdated += ConfigurationIconOnIconUpdated;
|
||||
|
||||
Update();
|
||||
}
|
||||
|
||||
private void ConfigurationIconOnIconUpdated(object? sender, EventArgs e)
|
||||
{
|
||||
Update();
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Core.ProfileConfigurationIcon" /> to display
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<Core.ProfileConfigurationIcon?> ConfigurationIconProperty =
|
||||
AvaloniaProperty.Register<ProfileConfigurationIcon, Core.ProfileConfigurationIcon?>(nameof(ConfigurationIcon));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Core.ProfileConfigurationIcon" /> to display
|
||||
/// </summary>
|
||||
public Core.ProfileConfigurationIcon? ConfigurationIcon
|
||||
{
|
||||
get => GetValue(ConfigurationIconProperty);
|
||||
set => SetValue(ConfigurationIconProperty, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,275 +0,0 @@
|
||||
using System;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Shared.Events;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Media;
|
||||
|
||||
namespace Artemis.UI.Shared.Controls;
|
||||
|
||||
/// <summary>
|
||||
/// Visualizes an <see cref="ArtemisDevice" /> with optional per-LED colors
|
||||
/// </summary>
|
||||
public class SelectionRectangle : Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the <see cref="Background" /> property.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<IBrush> BackgroundProperty =
|
||||
AvaloniaProperty.Register<SelectionRectangle, IBrush>(nameof(Background), new SolidColorBrush(Colors.CadetBlue, 0.25));
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="BorderBrush" /> property.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<IBrush> BorderBrushProperty =
|
||||
AvaloniaProperty.Register<SelectionRectangle, IBrush>(nameof(BorderBrush), new SolidColorBrush(Colors.CadetBlue));
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="BorderThickness" /> property.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<double> BorderThicknessProperty =
|
||||
AvaloniaProperty.Register<SelectionRectangle, double>(nameof(BorderThickness), 1);
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="BorderRadius" /> property.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<double> BorderRadiusProperty =
|
||||
AvaloniaProperty.Register<SelectionRectangle, double>(nameof(BorderRadius));
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="InputElement" /> property.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<IControl?> InputElementProperty =
|
||||
AvaloniaProperty.Register<SelectionRectangle, IControl?>(nameof(InputElement), notifying: OnInputElementChanged);
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="ZoomRatio" /> property.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<double> ZoomRatioProperty =
|
||||
AvaloniaProperty.Register<SelectionRectangle, double>(nameof(ZoomRatio), 1);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Defines the read-only <see cref="IsSelecting" /> property.
|
||||
/// </summary>
|
||||
public static readonly DirectProperty<SelectionRectangle, bool> IsSelectingProperty = AvaloniaProperty.RegisterDirect<SelectionRectangle, bool>(nameof(IsSelecting), o => o.IsSelecting);
|
||||
|
||||
private Rect? _absoluteRect;
|
||||
private Point _absoluteStartPosition;
|
||||
|
||||
private Rect? _displayRect;
|
||||
private bool _isSelecting;
|
||||
private IControl? _oldInputElement;
|
||||
private Point _startPosition;
|
||||
private Point _lastPosition;
|
||||
|
||||
/// <inheritdoc />
|
||||
public SelectionRectangle()
|
||||
{
|
||||
AffectsRender<TextBlock>(BackgroundProperty, BorderBrushProperty, BorderThicknessProperty);
|
||||
IsHitTestVisible = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a brush used to paint the control's background.
|
||||
/// </summary>
|
||||
public IBrush Background
|
||||
{
|
||||
get => GetValue(BackgroundProperty);
|
||||
set => SetValue(BackgroundProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a brush used to paint the control's border
|
||||
/// </summary>
|
||||
public IBrush BorderBrush
|
||||
{
|
||||
get => GetValue(BorderBrushProperty);
|
||||
set => SetValue(BorderBrushProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the width of the control's border
|
||||
/// </summary>
|
||||
public double BorderThickness
|
||||
{
|
||||
get => GetValue(BorderThicknessProperty);
|
||||
set => SetValue(BorderThicknessProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the radius of the control's border
|
||||
/// </summary>
|
||||
public double BorderRadius
|
||||
{
|
||||
get => GetValue(BorderRadiusProperty);
|
||||
set => SetValue(BorderRadiusProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the element that captures input for the selection rectangle.
|
||||
/// </summary>
|
||||
public IControl? InputElement
|
||||
{
|
||||
get => GetValue(InputElementProperty);
|
||||
set => SetValue(InputElementProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the zoom ratio to counteract when drawing
|
||||
/// </summary>
|
||||
public double ZoomRatio
|
||||
{
|
||||
get => GetValue(ZoomRatioProperty);
|
||||
set => SetValue(ZoomRatioProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether the selection rectangle is currently performing a selection.
|
||||
/// </summary>
|
||||
public bool IsSelecting
|
||||
{
|
||||
get => _isSelecting;
|
||||
private set => SetAndRaise(IsSelectingProperty, ref _isSelecting, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the selection rect is being updated, indicating the user is dragging.
|
||||
/// </summary>
|
||||
public event EventHandler<SelectionRectangleEventArgs>? SelectionUpdated;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the selection has finished, indicating the user stopped dragging.
|
||||
/// </summary>
|
||||
public event EventHandler<SelectionRectangleEventArgs>? SelectionFinished;
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="SelectionUpdated" /> event
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
protected virtual void OnSelectionUpdated(SelectionRectangleEventArgs e)
|
||||
{
|
||||
SelectionUpdated?.Invoke(this, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="SelectionFinished" /> event
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
protected virtual void OnSelectionFinished(SelectionRectangleEventArgs e)
|
||||
{
|
||||
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?
|
||||
// I'm not sure why this is needed but this check makes sure the position really hasn't changed.
|
||||
Point position = e.GetCurrentPoint(null).Position;
|
||||
if (position == _lastPosition)
|
||||
return;
|
||||
|
||||
_lastPosition = position;
|
||||
|
||||
if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
|
||||
return;
|
||||
|
||||
// Capture the pointer and initialize dragging the first time it moves
|
||||
if (!ReferenceEquals(e.Pointer.Captured, this))
|
||||
{
|
||||
e.Pointer.Capture(this);
|
||||
|
||||
_startPosition = e.GetPosition(Parent);
|
||||
_absoluteStartPosition = e.GetPosition(VisualRoot);
|
||||
_displayRect = null;
|
||||
}
|
||||
|
||||
Point currentPosition = e.GetPosition(Parent);
|
||||
Point absoluteCurrentPosition = e.GetPosition(VisualRoot);
|
||||
|
||||
_displayRect = new Rect(
|
||||
new Point(Math.Min(_startPosition.X, currentPosition.X), Math.Min(_startPosition.Y, currentPosition.Y)),
|
||||
new Point(Math.Max(_startPosition.X, currentPosition.X), Math.Max(_startPosition.Y, currentPosition.Y))
|
||||
);
|
||||
_absoluteRect = new Rect(
|
||||
new Point(Math.Min(_absoluteStartPosition.X, absoluteCurrentPosition.X), Math.Min(_absoluteStartPosition.Y, absoluteCurrentPosition.Y)),
|
||||
new Point(Math.Max(_absoluteStartPosition.X, absoluteCurrentPosition.X), Math.Max(_absoluteStartPosition.Y, absoluteCurrentPosition.Y))
|
||||
);
|
||||
|
||||
OnSelectionUpdated(new SelectionRectangleEventArgs(_displayRect.Value, _absoluteRect.Value, e.KeyModifiers));
|
||||
InvalidateVisual();
|
||||
IsSelecting = true;
|
||||
}
|
||||
|
||||
private void ParentOnPointerReleased(object? sender, PointerReleasedEventArgs e)
|
||||
{
|
||||
if (!ReferenceEquals(e.Pointer.Captured, this))
|
||||
return;
|
||||
|
||||
e.Pointer.Capture(null);
|
||||
|
||||
if (_displayRect != null && _absoluteRect != null)
|
||||
{
|
||||
OnSelectionFinished(new SelectionRectangleEventArgs(_displayRect.Value, _absoluteRect.Value, e.KeyModifiers));
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
_displayRect = null;
|
||||
InvalidateVisual();
|
||||
IsSelecting = false;
|
||||
}
|
||||
|
||||
private void SubscribeToInputElement()
|
||||
{
|
||||
if (_oldInputElement != null)
|
||||
{
|
||||
_oldInputElement.PointerMoved -= ParentOnPointerMoved;
|
||||
_oldInputElement.PointerReleased -= ParentOnPointerReleased;
|
||||
}
|
||||
|
||||
_oldInputElement = InputElement;
|
||||
|
||||
if (InputElement != null)
|
||||
{
|
||||
InputElement.PointerMoved += ParentOnPointerMoved;
|
||||
InputElement.PointerReleased += ParentOnPointerReleased;
|
||||
}
|
||||
}
|
||||
|
||||
#region Overrides of Visual
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Render(DrawingContext drawingContext)
|
||||
{
|
||||
if (_displayRect != null)
|
||||
drawingContext.DrawRectangle(Background, new Pen(BorderBrush, BorderThickness / ZoomRatio), _displayRect.Value, BorderRadius / ZoomRatio, BorderRadius / ZoomRatio);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
SubscribeToInputElement();
|
||||
base.OnAttachedToVisualTree(e);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
if (_oldInputElement != null)
|
||||
{
|
||||
_oldInputElement.PointerMoved -= ParentOnPointerMoved;
|
||||
_oldInputElement.PointerReleased -= ParentOnPointerReleased;
|
||||
_oldInputElement = null;
|
||||
}
|
||||
|
||||
base.OnDetachedFromVisualTree(e);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Artemis.Core;
|
||||
using Avalonia.Data.Converters;
|
||||
using Avalonia.Media;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.UI.Shared.Converters;
|
||||
|
||||
/// <summary>
|
||||
/// Converts <see cref="T:Artemis.Core.ColorGradient" /> into a <see cref="T:Avalonia.Media.GradientStops" />.
|
||||
/// </summary>
|
||||
public class ColorGradientToGradientStopsConverter : IValueConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
ColorGradient? colorGradient = value as ColorGradient;
|
||||
GradientStops collection = new();
|
||||
if (colorGradient == null)
|
||||
return collection;
|
||||
|
||||
foreach (ColorGradientStop c in colorGradient.OrderBy(s => s.Position))
|
||||
collection.Add(new GradientStop(Color.FromArgb(c.Color.Alpha, c.Color.Red, c.Color.Green, c.Color.Blue), c.Position));
|
||||
return collection;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
GradientStops? collection = value as GradientStops;
|
||||
ColorGradient colorGradients = new();
|
||||
if (collection == null)
|
||||
return colorGradients;
|
||||
|
||||
foreach (GradientStop c in collection.OrderBy(s => s.Offset))
|
||||
colorGradients.Add(new ColorGradientStop(new SKColor(c.Color.R, c.Color.G, c.Color.B, c.Color.A), (float) c.Offset));
|
||||
return colorGradients;
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Avalonia.Data.Converters;
|
||||
using Avalonia.Media;
|
||||
using FluentAvalonia.UI.Media;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.UI.Shared.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts <see cref="T:Avalonia.Media.Color" /> into <see cref="T:SkiaSharp.SKColor" />.
|
||||
/// </summary>
|
||||
public class ColorToSKColorConverter : IValueConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is Color avaloniaColor)
|
||||
return new SKColor(avaloniaColor.R, avaloniaColor.G, avaloniaColor.B, avaloniaColor.A);
|
||||
if (value is Color2 fluentAvaloniaColor)
|
||||
return new SKColor(fluentAvaloniaColor.R, fluentAvaloniaColor.G, fluentAvaloniaColor.B, fluentAvaloniaColor.A);
|
||||
|
||||
return SKColor.Empty;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
Color result = new(0, 0, 0, 0);
|
||||
if (value is SKColor skColor)
|
||||
result = new Color(skColor.Alpha, skColor.Red, skColor.Green, skColor.Blue);
|
||||
|
||||
if (targetType == typeof(Color2))
|
||||
return (Color2) result;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Data.Converters;
|
||||
|
||||
namespace Artemis.UI.Shared.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts an enum into a boolean.
|
||||
/// </summary>
|
||||
public class EnumToBooleanConverter : IValueConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
return Equals(value, parameter);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
return value?.Equals(true) == true ? parameter : BindingOperations.DoNothing;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Data.Converters;
|
||||
|
||||
namespace Artemis.UI.Shared.Converters;
|
||||
|
||||
/// <summary>
|
||||
/// Converts the width in percentage to a real number based on the width of the given parent
|
||||
/// </summary>
|
||||
public class ParentWidthPercentageConverter : IValueConverter
|
||||
{
|
||||
#region Implementation of IValueConverter
|
||||
|
||||
/// <inheritdoc />
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
if (parameter is not IControl parent || value is not double percentage)
|
||||
return value;
|
||||
|
||||
return parent.Width / 100.0 * percentage;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
if (parameter is not IControl parent || value is not double real)
|
||||
return value;
|
||||
|
||||
return real / parent.Width * 100.0;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Avalonia.Data.Converters;
|
||||
using Avalonia.Media;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.UI.Shared.Converters;
|
||||
|
||||
/// <summary>
|
||||
/// Converts <see cref="T:SkiaSharp.SKColor" /> into <see cref="T:Avalonia.Media.SolidColorBrush" />.
|
||||
/// </summary>
|
||||
public class SKColorToBrushConverter : IValueConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is SKColor skColor)
|
||||
return new SolidColorBrush(new Color(skColor.Alpha, skColor.Red, skColor.Green, skColor.Blue));
|
||||
return new SolidColorBrush(Colors.Transparent);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is SolidColorBrush brush)
|
||||
return new SKColor(brush.Color.R, brush.Color.G, brush.Color.B, brush.Color.A);
|
||||
return SKColor.Empty;
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Avalonia.Data.Converters;
|
||||
using Avalonia.Media;
|
||||
using FluentAvalonia.UI.Media;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.UI.Shared.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts <see cref="T:SkiaSharp.SKColor" /> into <see cref="T:Avalonia.Media.Color" />.
|
||||
/// </summary>
|
||||
public class SKColorToColorConverter : IValueConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
Color result = new(0, 0, 0, 0);
|
||||
if (value is SKColor skColor)
|
||||
result = new Color(skColor.Alpha, skColor.Red, skColor.Green, skColor.Blue);
|
||||
|
||||
if (targetType == typeof(Color2))
|
||||
return (Color2) result;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is Color avaloniaColor)
|
||||
return new SKColor(avaloniaColor.R, avaloniaColor.G, avaloniaColor.B, avaloniaColor.A);
|
||||
if (value is Color2 fluentAvaloniaColor)
|
||||
return new SKColor(fluentAvaloniaColor.R, fluentAvaloniaColor.G, fluentAvaloniaColor.B, fluentAvaloniaColor.A);
|
||||
|
||||
return SKColor.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Avalonia.Data.Converters;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.UI.Shared.Converters;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Converts <see cref="SKColor" />into <see cref="T:System.String" />.
|
||||
/// </summary>
|
||||
public class SKColorToStringConverter : IValueConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
return value?.ToString();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value as string))
|
||||
return SKColor.Empty;
|
||||
|
||||
return SKColor.TryParse((string) value!, out SKColor color) ? color : SKColor.Empty;
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Artemis.Core;
|
||||
using Avalonia.Data.Converters;
|
||||
|
||||
namespace Artemis.UI.Shared.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts <see cref="T:System.Type" /> into <see cref="T:System.String" />.
|
||||
/// </summary>
|
||||
public class TypeToStringConverter : IValueConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
bool humanizeProvided = bool.TryParse(parameter?.ToString(), out bool humanize);
|
||||
if (value is Type type)
|
||||
return type.GetDisplayName(humanizeProvided && humanize);
|
||||
|
||||
return value?.ToString();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Data.Converters;
|
||||
|
||||
namespace Artemis.UI.Shared.Converters;
|
||||
|
||||
/// <summary>
|
||||
/// Converts the width in percentage to a real number based on the width of the given parent
|
||||
/// </summary>
|
||||
public class WidthNormalizedConverter : IMultiValueConverter
|
||||
{
|
||||
#region Implementation of IMultiValueConverter
|
||||
|
||||
/// <inheritdoc />
|
||||
public object? Convert(IList<object?> values, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
object? first = values.FirstOrDefault();
|
||||
object? second = values.Skip(1).FirstOrDefault();
|
||||
if (first is float value && second is double totalWidth)
|
||||
return (totalWidth / 1.0) * value;
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Artemis.Core.Modules;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Shared.DataModelVisualization
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a <see cref="DataModel" /> display view model
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the data model</typeparam>
|
||||
public abstract class DataModelDisplayViewModel<T> : DataModelDisplayViewModel
|
||||
{
|
||||
[AllowNull] private T _displayValue = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets value that the view model must display
|
||||
/// </summary>
|
||||
[AllowNull]
|
||||
public T DisplayValue
|
||||
{
|
||||
get => _displayValue;
|
||||
set
|
||||
{
|
||||
if (Equals(value, _displayValue)) return;
|
||||
RaiseAndSetIfChanged(ref _displayValue, value);
|
||||
OnDisplayValueUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
internal override object InternalGuard => new();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void UpdateValue(object? model)
|
||||
{
|
||||
DisplayValue = model is T value ? value : default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the display value is updated
|
||||
/// </summary>
|
||||
protected virtual void OnDisplayValueUpdated()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For internal use only, implement <see cref="DataModelDisplayViewModel{T}" /> instead.
|
||||
/// </summary>
|
||||
public abstract class DataModelDisplayViewModel : ViewModelBase
|
||||
{
|
||||
private DataModelPropertyAttribute? _propertyDescription;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property description of this value
|
||||
/// </summary>
|
||||
public DataModelPropertyAttribute? PropertyDescription
|
||||
{
|
||||
get => _propertyDescription;
|
||||
internal set => RaiseAndSetIfChanged(ref _propertyDescription, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prevents this type being implemented directly, implement <see cref="DataModelDisplayViewModel{T}" /> instead.
|
||||
/// </summary>
|
||||
internal abstract object InternalGuard { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Updates the display value
|
||||
/// </summary>
|
||||
/// <param name="model">The value to set</param>
|
||||
public abstract void UpdateValue(object? model);
|
||||
}
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Artemis.Core.Modules;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Shared.DataModelVisualization
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a <see cref="DataModel" /> input view model
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the data model</typeparam>
|
||||
public abstract class DataModelInputViewModel<T> : DataModelInputViewModel
|
||||
{
|
||||
private bool _closed;
|
||||
[AllowNull] private T _inputValue = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="DataModelInputViewModel{T}" /> class
|
||||
/// </summary>
|
||||
/// <param name="targetDescription">The description of the property this input VM is representing</param>
|
||||
/// <param name="initialValue">The initial value to set the input value to</param>
|
||||
protected DataModelInputViewModel(DataModelPropertyAttribute targetDescription, T initialValue)
|
||||
{
|
||||
TargetDescription = targetDescription;
|
||||
InputValue = initialValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value shown in the input
|
||||
/// </summary>
|
||||
[AllowNull]
|
||||
public T InputValue
|
||||
{
|
||||
get => _inputValue;
|
||||
set => this.RaiseAndSetIfChanged(ref _inputValue, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description of the property this input VM is representing
|
||||
/// </summary>
|
||||
public DataModelPropertyAttribute TargetDescription { get; }
|
||||
|
||||
internal override object InternalGuard { get; } = new();
|
||||
|
||||
/// <inheritdoc />
|
||||
public sealed override void Submit()
|
||||
{
|
||||
if (_closed)
|
||||
return;
|
||||
_closed = true;
|
||||
|
||||
OnSubmit();
|
||||
UpdateCallback(InputValue, true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public sealed override void Cancel()
|
||||
{
|
||||
if (_closed)
|
||||
return;
|
||||
_closed = true;
|
||||
|
||||
OnCancel();
|
||||
UpdateCallback(InputValue, false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For internal use only, implement <see cref="DataModelInputViewModel{T}" /> instead.
|
||||
/// </summary>
|
||||
public abstract class DataModelInputViewModel : ReactiveObject
|
||||
{
|
||||
/// <summary>
|
||||
/// Prevents this type being implemented directly, implement <see cref="DataModelInputViewModel{T}" /> instead.
|
||||
/// </summary>
|
||||
// ReSharper disable once UnusedMember.Global
|
||||
internal abstract object InternalGuard { get; }
|
||||
|
||||
internal Action<object?, bool> UpdateCallback { get; set; } = null!; // Set right after construction
|
||||
|
||||
/// <summary>
|
||||
/// Gets the types this input view model can support through type conversion. This list is defined when registering the
|
||||
/// view model.
|
||||
/// </summary>
|
||||
internal IReadOnlyCollection<Type>? CompatibleConversionTypes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Submits the input value and removes this view model.
|
||||
/// <para>This is called automatically when the user presses enter or clicks outside the view</para>
|
||||
/// </summary>
|
||||
public abstract void Submit();
|
||||
|
||||
/// <summary>
|
||||
/// Discards changes to the input value and removes this view model.
|
||||
/// <para>This is called automatically when the user presses escape</para>
|
||||
/// </summary>
|
||||
public abstract void Cancel();
|
||||
|
||||
/// <summary>
|
||||
/// Called before the current value is submitted
|
||||
/// </summary>
|
||||
protected virtual void OnSubmit()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called before the current value is discarded
|
||||
/// </summary>
|
||||
protected virtual void OnCancel()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
-89
@@ -1,89 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Artemis.UI.Shared.Services.Interfaces;
|
||||
|
||||
namespace Artemis.UI.Shared.DataModelVisualization
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a layer brush registered through
|
||||
/// <see cref="IDataModelUIService.RegisterDataModelInput{T}" /> or
|
||||
/// <see cref="IDataModelUIService.RegisterDataModelDisplay{T}" />
|
||||
/// </summary>
|
||||
public class DataModelVisualizationRegistration
|
||||
{
|
||||
private readonly IDataModelUIService _dataModelUIService;
|
||||
|
||||
internal DataModelVisualizationRegistration(IDataModelUIService dataModelUIService,
|
||||
RegistrationType registrationType,
|
||||
Plugin plugin,
|
||||
Type supportedType,
|
||||
Type viewModelType)
|
||||
{
|
||||
_dataModelUIService = dataModelUIService;
|
||||
RegistrationType = registrationType;
|
||||
Plugin = plugin;
|
||||
SupportedType = supportedType;
|
||||
ViewModelType = viewModelType;
|
||||
|
||||
if (Plugin != Constants.CorePlugin)
|
||||
Plugin.Disabled += InstanceOnDisabled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of registration, either a display or an input
|
||||
/// </summary>
|
||||
public RegistrationType RegistrationType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin that registered the visualization
|
||||
/// </summary>
|
||||
public Plugin Plugin { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type supported by the visualization
|
||||
/// </summary>
|
||||
public Type SupportedType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the view model type of the visualization
|
||||
/// </summary>
|
||||
public Type ViewModelType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read only collection of types this visualization can convert to and from
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<Type>? CompatibleConversionTypes { get; internal set; }
|
||||
|
||||
internal void Unsubscribe()
|
||||
{
|
||||
if (Plugin != Constants.CorePlugin)
|
||||
Plugin.Disabled -= InstanceOnDisabled;
|
||||
}
|
||||
|
||||
private void InstanceOnDisabled(object? sender, EventArgs e)
|
||||
{
|
||||
if (RegistrationType == RegistrationType.Input)
|
||||
_dataModelUIService.RemoveDataModelInput(this);
|
||||
else if (RegistrationType == RegistrationType.Display)
|
||||
_dataModelUIService.RemoveDataModelDisplay(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a type of data model visualization registration
|
||||
/// </summary>
|
||||
public enum RegistrationType
|
||||
{
|
||||
/// <summary>
|
||||
/// A visualization used for displaying values
|
||||
/// </summary>
|
||||
Display,
|
||||
|
||||
/// <summary>
|
||||
/// A visualization used for inputting values
|
||||
/// </summary>
|
||||
Input
|
||||
}
|
||||
}
|
||||
-71
@@ -1,71 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Modules;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Artemis.UI.Shared.Services.Interfaces;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Shared.DataModelVisualization.Shared
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a view model that visualizes an event data model property
|
||||
/// </summary>
|
||||
public class DataModelEventViewModel : DataModelVisualizationViewModel
|
||||
{
|
||||
private Type? _displayValueType;
|
||||
|
||||
internal DataModelEventViewModel(DataModel dataModel, DataModelVisualizationViewModel parent, DataModelPath dataModelPath) : base(dataModel, parent, dataModelPath)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of event arguments this event triggers and that must be displayed as children
|
||||
/// </summary>
|
||||
public Type? DisplayValueType
|
||||
{
|
||||
get => _displayValueType;
|
||||
set => this.RaiseAndSetIfChanged(ref _displayValueType, value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(IDataModelUIService dataModelUIService, DataModelUpdateConfiguration? configuration)
|
||||
{
|
||||
DisplayValueType = DataModelPath?.GetPropertyType();
|
||||
|
||||
if (configuration != null)
|
||||
{
|
||||
if (configuration.CreateEventChildren)
|
||||
PopulateProperties(dataModelUIService, configuration);
|
||||
else if (Children.Any())
|
||||
Children.Clear();
|
||||
}
|
||||
|
||||
// Only update children if the parent is expanded
|
||||
if (Parent != null && !Parent.IsRootViewModel && !Parent.IsVisualizationExpanded)
|
||||
return;
|
||||
|
||||
foreach (DataModelVisualizationViewModel dataModelVisualizationViewModel in Children)
|
||||
dataModelVisualizationViewModel.Update(dataModelUIService, configuration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Always returns <see langword="null"/> for data model events
|
||||
/// </summary>
|
||||
public override object? GetCurrentValue()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string? ToString()
|
||||
{
|
||||
return DisplayPath ?? Path;
|
||||
}
|
||||
|
||||
internal override int GetChildDepth()
|
||||
{
|
||||
return PropertyDescription != null && !PropertyDescription.ResetsDepth ? Depth + 1 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
-92
@@ -1,92 +0,0 @@
|
||||
using System;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Artemis.UI.Shared.Services.Interfaces;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Shared.DataModelVisualization.Shared
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a view model that visualizes a single data model property contained in a
|
||||
/// <see cref="DataModelListViewModel" />
|
||||
/// </summary>
|
||||
public class DataModelListItemViewModel : DataModelVisualizationViewModel
|
||||
{
|
||||
private int _index;
|
||||
private Type? _listType;
|
||||
private object? _displayValue;
|
||||
private DataModelDisplayViewModel? _displayViewModel;
|
||||
|
||||
internal DataModelListItemViewModel(Type listType, DataModelDisplayViewModel displayViewModel, string? name) : base(null, null, null)
|
||||
{
|
||||
ListType = listType;
|
||||
DisplayViewModel = displayViewModel;
|
||||
}
|
||||
|
||||
internal DataModelListItemViewModel(Type listType, string? name) : base(null, null, null)
|
||||
{
|
||||
ListType = listType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the view model used to display the display value
|
||||
/// </summary>
|
||||
public DataModelDisplayViewModel? DisplayViewModel
|
||||
{
|
||||
get => _displayViewModel;
|
||||
internal set => this.RaiseAndSetIfChanged(ref _displayViewModel, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of the element within the list
|
||||
/// </summary>
|
||||
public int Index
|
||||
{
|
||||
get => _index;
|
||||
internal set => this.RaiseAndSetIfChanged(ref _index, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of elements contained in the list
|
||||
/// </summary>
|
||||
public Type? ListType
|
||||
{
|
||||
get => _listType;
|
||||
private set => this.RaiseAndSetIfChanged(ref _listType, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the property that is being visualized
|
||||
/// </summary>
|
||||
public object? DisplayValue
|
||||
{
|
||||
get => _displayValue;
|
||||
internal set => this.RaiseAndSetIfChanged(ref _displayValue, value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object? GetCurrentValue()
|
||||
{
|
||||
return DisplayValue;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(IDataModelUIService dataModelUIService, DataModelUpdateConfiguration? configuration)
|
||||
{
|
||||
// Display value gets updated by parent, don't do anything if it is null
|
||||
if (DisplayValue == null)
|
||||
return;
|
||||
|
||||
if (DisplayViewModel == null)
|
||||
DisplayViewModel = dataModelUIService.GetDataModelDisplayViewModel(DisplayValue.GetType(), PropertyDescription, true);
|
||||
|
||||
ListType = DisplayValue.GetType();
|
||||
DisplayViewModel?.UpdateValue(DisplayValue);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return $"[List item {Index}]";
|
||||
}
|
||||
}
|
||||
}
|
||||
-137
@@ -1,137 +0,0 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.ObjectModel;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Modules;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Artemis.UI.Shared.Services.Interfaces;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Shared.DataModelVisualization.Shared
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a view model that visualizes a list data model property
|
||||
/// </summary>
|
||||
public class DataModelListViewModel : DataModelVisualizationViewModel
|
||||
{
|
||||
private string _countDisplay;
|
||||
private Type? _displayValueType;
|
||||
private IEnumerable? _list;
|
||||
private ObservableCollection<DataModelVisualizationViewModel> _listChildren;
|
||||
private int _listCount;
|
||||
|
||||
internal DataModelListViewModel(DataModel dataModel, DataModelVisualizationViewModel parent, DataModelPath dataModelPath)
|
||||
: base(dataModel, parent, dataModelPath)
|
||||
{
|
||||
_countDisplay = "0 items";
|
||||
_listChildren = new ObservableCollection<DataModelVisualizationViewModel>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the instance of the list that is being visualized
|
||||
/// </summary>
|
||||
public IEnumerable? List
|
||||
{
|
||||
get => _list;
|
||||
private set => this.RaiseAndSetIfChanged(ref _list, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets amount of elements in the list that is being visualized
|
||||
/// </summary>
|
||||
public int ListCount
|
||||
{
|
||||
get => _listCount;
|
||||
private set => this.RaiseAndSetIfChanged(ref _listCount, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of elements this list contains and that must be displayed as children
|
||||
/// </summary>
|
||||
public Type? DisplayValueType
|
||||
{
|
||||
get => _displayValueType;
|
||||
set => this.RaiseAndSetIfChanged(ref _displayValueType, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a human readable display count
|
||||
/// </summary>
|
||||
public string CountDisplay
|
||||
{
|
||||
get => _countDisplay;
|
||||
set => this.RaiseAndSetIfChanged(ref _countDisplay, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of child view models that visualize the elements in the list
|
||||
/// </summary>
|
||||
public ObservableCollection<DataModelVisualizationViewModel> ListChildren
|
||||
{
|
||||
get => _listChildren;
|
||||
private set => this.RaiseAndSetIfChanged(ref _listChildren, value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(IDataModelUIService dataModelUIService, DataModelUpdateConfiguration? configuration)
|
||||
{
|
||||
if (Parent != null && !Parent.IsVisualizationExpanded)
|
||||
return;
|
||||
|
||||
List = GetCurrentValue() as IEnumerable;
|
||||
DisplayValueType = List?.GetType();
|
||||
if (List == null)
|
||||
return;
|
||||
|
||||
int index = 0;
|
||||
foreach (object item in List)
|
||||
{
|
||||
if (item == null)
|
||||
continue;
|
||||
|
||||
DataModelVisualizationViewModel? child;
|
||||
if (ListChildren.Count <= index)
|
||||
{
|
||||
child = CreateListChild(dataModelUIService, item.GetType(), DataModelPath?.GetPropertyDescription()?.ListItemName);
|
||||
if (child == null)
|
||||
continue;
|
||||
ListChildren.Add(child);
|
||||
}
|
||||
else
|
||||
child = ListChildren[index];
|
||||
|
||||
if (child is DataModelListItemViewModel dataModelListPropertyViewModel)
|
||||
{
|
||||
dataModelListPropertyViewModel.DisplayValue = item;
|
||||
dataModelListPropertyViewModel.Index = index;
|
||||
dataModelListPropertyViewModel.Update(dataModelUIService, configuration);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
ListCount = index;
|
||||
|
||||
while (ListChildren.Count > ListCount)
|
||||
ListChildren.RemoveAt(ListChildren.Count - 1);
|
||||
|
||||
CountDisplay = $"{ListChildren.Count} {(ListChildren.Count == 1 ? "item" : "items")}";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return $"[List] {DisplayPath ?? Path} - {ListCount} item(s)";
|
||||
}
|
||||
|
||||
private DataModelVisualizationViewModel? CreateListChild(IDataModelUIService dataModelUIService, Type listType, string? name)
|
||||
{
|
||||
// If a display VM was found, prefer to use that in any case
|
||||
DataModelDisplayViewModel? typeViewModel = dataModelUIService.GetDataModelDisplayViewModel(listType, PropertyDescription);
|
||||
|
||||
return typeViewModel != null
|
||||
? new DataModelListItemViewModel(listType, typeViewModel, name)
|
||||
: new DataModelListItemViewModel(listType, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
-83
@@ -1,83 +0,0 @@
|
||||
using System;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Modules;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Artemis.UI.Shared.Services.Interfaces;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Shared.DataModelVisualization.Shared
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a view model that visualizes a class (POCO) data model property containing child properties
|
||||
/// </summary>
|
||||
public class DataModelPropertiesViewModel : DataModelVisualizationViewModel
|
||||
{
|
||||
private object? _displayValue;
|
||||
private Type? _displayValueType;
|
||||
|
||||
internal DataModelPropertiesViewModel(DataModel? dataModel, DataModelVisualizationViewModel? parent, DataModelPath? dataModelPath)
|
||||
: base(dataModel, parent, dataModelPath)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the property that is being visualized
|
||||
/// </summary>
|
||||
public Type? DisplayValueType
|
||||
{
|
||||
get => _displayValueType;
|
||||
private set => this.RaiseAndSetIfChanged(ref _displayValueType, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the property that is being visualized
|
||||
/// </summary>
|
||||
public object? DisplayValue
|
||||
{
|
||||
get => _displayValue;
|
||||
private set => this.RaiseAndSetIfChanged(ref _displayValue, value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(IDataModelUIService dataModelUIService, DataModelUpdateConfiguration? configuration)
|
||||
{
|
||||
DisplayValueType = DataModelPath?.GetPropertyType();
|
||||
|
||||
// Only set a display value if ToString returns useful information and not just the type name
|
||||
object? currentValue = GetCurrentValue();
|
||||
if (currentValue != null && currentValue.ToString() != currentValue.GetType().ToString())
|
||||
DisplayValue = currentValue.ToString();
|
||||
else
|
||||
DisplayValue = null;
|
||||
|
||||
// Always populate properties
|
||||
PopulateProperties(dataModelUIService, configuration);
|
||||
|
||||
// Only update children if the parent is expanded
|
||||
if (Parent != null && !Parent.IsRootViewModel && !Parent.IsVisualizationExpanded)
|
||||
return;
|
||||
|
||||
foreach (DataModelVisualizationViewModel dataModelVisualizationViewModel in Children)
|
||||
dataModelVisualizationViewModel.Update(dataModelUIService, configuration);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object? GetCurrentValue()
|
||||
{
|
||||
if (Parent == null || Parent.IsRootViewModel || IsRootViewModel)
|
||||
return DataModel;
|
||||
return base.GetCurrentValue();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string? ToString()
|
||||
{
|
||||
return DisplayPath ?? Path;
|
||||
}
|
||||
|
||||
internal override int GetChildDepth()
|
||||
{
|
||||
return PropertyDescription != null && !PropertyDescription.ResetsDepth ? Depth + 1 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
-83
@@ -1,83 +0,0 @@
|
||||
using System;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Modules;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Artemis.UI.Shared.Services.Interfaces;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Shared.DataModelVisualization.Shared
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a view model that visualizes a single data model property contained in a
|
||||
/// <see cref="DataModelPropertiesViewModel" />
|
||||
/// </summary>
|
||||
public class DataModelPropertyViewModel : DataModelVisualizationViewModel
|
||||
{
|
||||
private object? _displayValue;
|
||||
private Type? _displayValueType;
|
||||
private DataModelDisplayViewModel? _displayViewModel;
|
||||
|
||||
internal DataModelPropertyViewModel(DataModel? dataModel, DataModelVisualizationViewModel? parent, DataModelPath? dataModelPath)
|
||||
: base(dataModel, parent, dataModelPath)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the property that is being visualized
|
||||
/// </summary>
|
||||
public object? DisplayValue
|
||||
{
|
||||
get => _displayValue;
|
||||
internal set => this.RaiseAndSetIfChanged(ref _displayValue, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the property that is being visualized
|
||||
/// </summary>
|
||||
public Type? DisplayValueType
|
||||
{
|
||||
get => _displayValueType;
|
||||
protected set => this.RaiseAndSetIfChanged(ref _displayValueType, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the view model used to display the display value
|
||||
/// </summary>
|
||||
public DataModelDisplayViewModel? DisplayViewModel
|
||||
{
|
||||
get => _displayViewModel;
|
||||
internal set => this.RaiseAndSetIfChanged(ref _displayViewModel, value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(IDataModelUIService dataModelUIService, DataModelUpdateConfiguration? configuration)
|
||||
{
|
||||
if (Parent != null && !Parent.IsVisualizationExpanded && !Parent.IsRootViewModel)
|
||||
return;
|
||||
|
||||
if (DisplayViewModel == null)
|
||||
{
|
||||
Type? propertyType = DataModelPath?.GetPropertyType();
|
||||
if (propertyType != null)
|
||||
{
|
||||
DisplayViewModel = dataModelUIService.GetDataModelDisplayViewModel(propertyType, PropertyDescription, true);
|
||||
if (DisplayViewModel != null)
|
||||
DisplayViewModel.PropertyDescription = DataModelPath?.GetPropertyDescription();
|
||||
}
|
||||
}
|
||||
|
||||
DisplayValue = GetCurrentValue();
|
||||
DisplayValueType = DisplayValue != null ? DisplayValue.GetType() : DataModelPath?.GetPropertyType();
|
||||
|
||||
DisplayViewModel?.UpdateValue(DisplayValue);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
if (DisplayValueType != null)
|
||||
return $"[{DisplayValueType.Name}] {DisplayPath ?? Path} - {DisplayValue}";
|
||||
return $"{DisplayPath ?? Path} - {DisplayValue}";
|
||||
}
|
||||
}
|
||||
}
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
namespace Artemis.UI.Shared.DataModelVisualization.Shared
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a configuration to use while updating a <see cref="DataModelVisualizationViewModel" />
|
||||
/// </summary>
|
||||
public class DataModelUpdateConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="DataModelUpdateConfiguration" /> class
|
||||
/// </summary>
|
||||
/// <param name="createEventChildren">A boolean indicating whether or not event children should be created</param>
|
||||
public DataModelUpdateConfiguration(bool createEventChildren)
|
||||
{
|
||||
CreateEventChildren = createEventChildren;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether or not event children should be created
|
||||
/// </summary>
|
||||
public bool CreateEventChildren { get; }
|
||||
}
|
||||
}
|
||||
-401
@@ -1,401 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Modules;
|
||||
using Artemis.UI.Shared.Services.Interfaces;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Shared.DataModelVisualization.Shared;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a base class for a view model that visualizes a part of the data model
|
||||
/// </summary>
|
||||
public abstract class DataModelVisualizationViewModel : ReactiveObject, IDisposable
|
||||
{
|
||||
private const int MaxDepth = 4;
|
||||
private ObservableCollection<DataModelVisualizationViewModel> _children;
|
||||
private DataModel? _dataModel;
|
||||
private bool _isMatchingFilteredTypes;
|
||||
private bool _isVisualizationExpanded;
|
||||
private DataModelVisualizationViewModel? _parent;
|
||||
private bool _populatedStaticChildren;
|
||||
private DataModelPropertyAttribute? _propertyDescription;
|
||||
|
||||
internal DataModelVisualizationViewModel(DataModel? dataModel, DataModelVisualizationViewModel? parent, DataModelPath? dataModelPath)
|
||||
{
|
||||
_dataModel = dataModel;
|
||||
_children = new ObservableCollection<DataModelVisualizationViewModel>();
|
||||
_parent = parent;
|
||||
DataModelPath = dataModelPath;
|
||||
IsMatchingFilteredTypes = true;
|
||||
|
||||
if (parent == null)
|
||||
IsRootViewModel = true;
|
||||
else
|
||||
PropertyDescription = DataModelPath?.GetPropertyDescription() ?? DataModel?.DataModelDescription;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether this view model is at the root of the data model
|
||||
/// </summary>
|
||||
public bool IsRootViewModel { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data model path to the property this view model is visualizing
|
||||
/// </summary>
|
||||
public DataModelPath? DataModelPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a string representation of the path backing this model
|
||||
/// </summary>
|
||||
public string? Path => DataModelPath?.Path;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property depth of the view model
|
||||
/// </summary>
|
||||
public int Depth { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data model backing this view model
|
||||
/// </summary>
|
||||
public DataModel? DataModel
|
||||
{
|
||||
get => _dataModel;
|
||||
protected set => this.RaiseAndSetIfChanged(ref _dataModel, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property description of the property this view model is visualizing
|
||||
/// </summary>
|
||||
public DataModelPropertyAttribute? PropertyDescription
|
||||
{
|
||||
get => _propertyDescription;
|
||||
protected set => this.RaiseAndSetIfChanged(ref _propertyDescription, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent of this view model
|
||||
/// </summary>
|
||||
public DataModelVisualizationViewModel? Parent
|
||||
{
|
||||
get => _parent;
|
||||
protected set => this.RaiseAndSetIfChanged(ref _parent, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an observable collection containing the children of this view model
|
||||
/// </summary>
|
||||
public ObservableCollection<DataModelVisualizationViewModel> Children
|
||||
{
|
||||
get => _children;
|
||||
set => this.RaiseAndSetIfChanged(ref _children, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether the property being visualized matches the types last provided to
|
||||
/// <see cref="ApplyTypeFilter" />
|
||||
/// </summary>
|
||||
public bool IsMatchingFilteredTypes
|
||||
{
|
||||
get => _isMatchingFilteredTypes;
|
||||
private set => this.RaiseAndSetIfChanged(ref _isMatchingFilteredTypes, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether the visualization is expanded, exposing the <see cref="Children" />
|
||||
/// </summary>
|
||||
public bool IsVisualizationExpanded
|
||||
{
|
||||
get => _isVisualizationExpanded;
|
||||
set
|
||||
{
|
||||
if (!this.RaiseAndSetIfChanged(ref _isVisualizationExpanded, value)) return;
|
||||
RequestUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a user-friendly representation of the <see cref="DataModelPath" />
|
||||
/// </summary>
|
||||
public virtual string? DisplayPath => DataModelPath != null
|
||||
? string.Join(" › ", DataModelPath.Segments.Select(s => s.GetPropertyDescription()?.Name ?? s.Identifier))
|
||||
: null;
|
||||
|
||||
/// <summary>
|
||||
/// Updates the datamodel and if in an parent, any children
|
||||
/// </summary>
|
||||
/// <param name="dataModelUIService">The data model UI service used during update</param>
|
||||
/// <param name="configuration">The configuration to apply while updating</param>
|
||||
public abstract void Update(IDataModelUIService dataModelUIService, DataModelUpdateConfiguration? configuration);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current value of the property being visualized
|
||||
/// </summary>
|
||||
/// <returns>The current value of the property being visualized</returns>
|
||||
public virtual object? GetCurrentValue()
|
||||
{
|
||||
if (IsRootViewModel)
|
||||
return null;
|
||||
|
||||
return DataModelPath?.GetValue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the provided types match the type of the property being visualized and sets the result in
|
||||
/// <see cref="IsMatchingFilteredTypes" />
|
||||
/// </summary>
|
||||
/// <param name="looseMatch">Whether the type may be a loose match, meaning it can be cast or converted</param>
|
||||
/// <param name="filteredTypes">The types to filter</param>
|
||||
public void ApplyTypeFilter(bool looseMatch, params Type[]? filteredTypes)
|
||||
{
|
||||
if (filteredTypes != null)
|
||||
{
|
||||
if (filteredTypes.All(t => t == null))
|
||||
filteredTypes = null;
|
||||
else
|
||||
filteredTypes = filteredTypes.Where(t => t != null).ToArray();
|
||||
}
|
||||
|
||||
// If the VM has children, its own type is not relevant
|
||||
if (Children.Any())
|
||||
{
|
||||
foreach (DataModelVisualizationViewModel child in Children)
|
||||
child?.ApplyTypeFilter(looseMatch, filteredTypes);
|
||||
|
||||
IsMatchingFilteredTypes = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// If null is passed, clear the type filter
|
||||
if (filteredTypes == null || filteredTypes.Length == 0)
|
||||
{
|
||||
IsMatchingFilteredTypes = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// If the type couldn't be retrieved either way, assume false
|
||||
Type? type = DataModelPath?.GetPropertyType();
|
||||
if (type == null)
|
||||
{
|
||||
IsMatchingFilteredTypes = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (looseMatch)
|
||||
IsMatchingFilteredTypes = filteredTypes.Any(t => t.IsCastableFrom(type) ||
|
||||
t == typeof(Enum) && type.IsEnum ||
|
||||
t == typeof(IEnumerable<>) && type.IsGenericEnumerable() ||
|
||||
type.IsGenericType && t == type.GetGenericTypeDefinition());
|
||||
else
|
||||
IsMatchingFilteredTypes = filteredTypes.Any(t => t == type || t == typeof(Enum) && type.IsEnum);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when an update to the property this view model visualizes is requested
|
||||
/// </summary>
|
||||
public event EventHandler? UpdateRequested;
|
||||
|
||||
/// <summary>
|
||||
/// Expands this view model and any children to expose the provided <paramref name="dataModelPath" />.
|
||||
/// </summary>
|
||||
/// <param name="dataModelPath">The data model path to expose.</param>
|
||||
public void ExpandToPath(DataModelPath dataModelPath)
|
||||
{
|
||||
if (dataModelPath.Target != DataModel)
|
||||
throw new ArtemisSharedUIException("Can't expand to a path that doesn't belong to this data model.");
|
||||
|
||||
IsVisualizationExpanded = true;
|
||||
DataModelPathSegment current = dataModelPath.Segments.Skip(1).First();
|
||||
Children.FirstOrDefault(c => c.Path == current.Path)?.ExpandToPath(current.Next);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the view model that hosts the given path.
|
||||
/// </summary>
|
||||
/// <param name="dataModelPath">The path to find</param>
|
||||
/// <returns>The matching view model, may be null if the path doesn't exist or isn't expanded</returns>
|
||||
public DataModelVisualizationViewModel? GetViewModelForPath(DataModelPath dataModelPath)
|
||||
{
|
||||
if (dataModelPath.Target != DataModel)
|
||||
throw new ArtemisSharedUIException("Can't expand to a path that doesn't belong to this data model.");
|
||||
|
||||
if (DataModelPath?.Path == dataModelPath.Path)
|
||||
return this;
|
||||
|
||||
return Children.Select(c => c.GetViewModelForPath(dataModelPath)).FirstOrDefault(match => match != null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="UpdateRequested" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnUpdateRequested()
|
||||
{
|
||||
UpdateRequested?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing">
|
||||
/// <see langword="true" /> to release both managed and unmanaged resources;
|
||||
/// <see langword="false" /> to release only unmanaged resources.
|
||||
/// </param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
DataModelPath?.Dispose();
|
||||
foreach (DataModelVisualizationViewModel dataModelVisualizationViewModel in Children)
|
||||
dataModelVisualizationViewModel.Dispose(true);
|
||||
}
|
||||
}
|
||||
|
||||
internal virtual int GetChildDepth()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
internal void PopulateProperties(IDataModelUIService dataModelUIService, DataModelUpdateConfiguration? dataModelUpdateConfiguration)
|
||||
{
|
||||
if (IsRootViewModel && DataModel == null)
|
||||
return;
|
||||
|
||||
Type? modelType = IsRootViewModel ? DataModel?.GetType() : DataModelPath?.GetPropertyType();
|
||||
if (modelType == null)
|
||||
throw new ArtemisSharedUIException("Failed to populate data model visualization properties, couldn't get a property type");
|
||||
|
||||
// Add missing static children only once, they're static after all
|
||||
if (!_populatedStaticChildren)
|
||||
{
|
||||
foreach (PropertyInfo propertyInfo in modelType.GetProperties(BindingFlags.Public | BindingFlags.Instance).OrderBy(t => t.MetadataToken))
|
||||
{
|
||||
string childPath = AppendToPath(propertyInfo.Name);
|
||||
if (Children.Any(c => c?.Path != null && c.Path.Equals(childPath)))
|
||||
continue;
|
||||
if (propertyInfo.GetCustomAttribute<DataModelIgnoreAttribute>() != null)
|
||||
continue;
|
||||
MethodInfo? getMethod = propertyInfo.GetGetMethod();
|
||||
if (getMethod == null || getMethod.GetParameters().Any())
|
||||
continue;
|
||||
|
||||
DataModelVisualizationViewModel? child = CreateChild(dataModelUIService, childPath, GetChildDepth());
|
||||
if (child != null)
|
||||
Children.Add(child);
|
||||
}
|
||||
|
||||
_populatedStaticChildren = true;
|
||||
}
|
||||
|
||||
// Remove static children that should be hidden
|
||||
if (DataModel != null)
|
||||
{
|
||||
ReadOnlyCollection<PropertyInfo> hiddenProperties = DataModel.GetHiddenProperties();
|
||||
foreach (PropertyInfo hiddenProperty in hiddenProperties)
|
||||
{
|
||||
string childPath = AppendToPath(hiddenProperty.Name);
|
||||
DataModelVisualizationViewModel? toRemove = Children.FirstOrDefault(c => c.Path != null && c.Path == childPath);
|
||||
if (toRemove != null)
|
||||
Children.Remove(toRemove);
|
||||
}
|
||||
}
|
||||
|
||||
// Add missing dynamic children
|
||||
object? value = Parent == null || Parent.IsRootViewModel ? DataModel : DataModelPath?.GetValue();
|
||||
if (value is DataModel dataModel)
|
||||
foreach (string key in dataModel.DynamicChildren.Keys.ToList())
|
||||
{
|
||||
string childPath = AppendToPath(key);
|
||||
if (Children.Any(c => c.Path != null && c.Path.Equals(childPath)))
|
||||
continue;
|
||||
|
||||
DataModelVisualizationViewModel? child = CreateChild(dataModelUIService, childPath, GetChildDepth());
|
||||
if (child != null)
|
||||
Children.Add(child);
|
||||
}
|
||||
|
||||
// Remove dynamic children that have been removed from the data model
|
||||
List<DataModelVisualizationViewModel> toRemoveDynamic = Children.Where(c => c.DataModelPath != null && !c.DataModelPath.IsValid).ToList();
|
||||
foreach (DataModelVisualizationViewModel dataModelVisualizationViewModel in toRemoveDynamic)
|
||||
Children.Remove(dataModelVisualizationViewModel);
|
||||
}
|
||||
|
||||
private DataModelVisualizationViewModel? CreateChild(IDataModelUIService dataModelUIService, string path, int depth)
|
||||
{
|
||||
if (DataModel == null)
|
||||
throw new ArtemisSharedUIException("Cannot create a data model visualization child VM for a parent without a data model");
|
||||
if (depth > MaxDepth)
|
||||
return null;
|
||||
|
||||
DataModelPath dataModelPath = new(DataModel, path);
|
||||
if (!dataModelPath.IsValid)
|
||||
return null;
|
||||
|
||||
PropertyInfo? propertyInfo = dataModelPath.GetPropertyInfo();
|
||||
Type? propertyType = dataModelPath.GetPropertyType();
|
||||
|
||||
// Skip properties decorated with DataModelIgnore
|
||||
if (propertyInfo != null && Attribute.IsDefined(propertyInfo, typeof(DataModelIgnoreAttribute)))
|
||||
return null;
|
||||
// Skip properties that are in the ignored properties list of the respective profile module/data model expansion
|
||||
if (DataModel.GetHiddenProperties().Any(p => p.Equals(propertyInfo)))
|
||||
return null;
|
||||
|
||||
if (propertyType == null)
|
||||
return null;
|
||||
|
||||
// If a display VM was found, prefer to use that in any case
|
||||
DataModelDisplayViewModel? typeViewModel = dataModelUIService.GetDataModelDisplayViewModel(propertyType, PropertyDescription);
|
||||
if (typeViewModel != null)
|
||||
return new DataModelPropertyViewModel(DataModel, this, dataModelPath) {DisplayViewModel = typeViewModel, Depth = depth};
|
||||
// For primitives, create a property view model, it may be null that is fine
|
||||
if (propertyType.IsPrimitive || propertyType.IsEnum || propertyType == typeof(string) || propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
|
||||
return new DataModelPropertyViewModel(DataModel, this, dataModelPath) {Depth = depth};
|
||||
if (propertyType.IsGenericEnumerable())
|
||||
return new DataModelListViewModel(DataModel, this, dataModelPath) {Depth = depth};
|
||||
if (propertyType == typeof(DataModelEvent) || propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(DataModelEvent<>))
|
||||
return new DataModelEventViewModel(DataModel, this, dataModelPath) {Depth = depth};
|
||||
// For other value types create a child view model
|
||||
if (propertyType.IsClass || propertyType.IsStruct())
|
||||
return new DataModelPropertiesViewModel(DataModel, this, dataModelPath) {Depth = depth};
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string AppendToPath(string toAppend)
|
||||
{
|
||||
if (string.IsNullOrEmpty(Path))
|
||||
return toAppend;
|
||||
|
||||
StringBuilder builder = new();
|
||||
builder.Append(Path);
|
||||
builder.Append(".");
|
||||
builder.Append(toAppend);
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private void RequestUpdate()
|
||||
{
|
||||
Parent?.RequestUpdate();
|
||||
OnUpdateRequested();
|
||||
}
|
||||
|
||||
private void ExpandToPath(DataModelPathSegment? segment)
|
||||
{
|
||||
if (segment == null)
|
||||
return;
|
||||
|
||||
IsVisualizationExpanded = true;
|
||||
Children.FirstOrDefault(c => c.Path == segment.Path)?.ExpandToPath(segment.Next);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Shared.DefaultTypes.DataModel.Display.DefaultDataModelDisplayView">
|
||||
<Grid ColumnDefinitions="*,Auto,Auto">
|
||||
<!-- Prefix -->
|
||||
<TextBlock Grid.Column="0"
|
||||
Text="{Binding PropertyDescription.Prefix}"
|
||||
IsVisible="{Binding PropertyDescription.Prefix, Converter={x:Static ObjectConverters.IsNotNull}}"
|
||||
TextAlignment="Right"
|
||||
Margin="0 0 5 0" />
|
||||
|
||||
<!-- Value -->
|
||||
<TextBlock Grid.Column="1" Text="{Binding Display, Mode=OneWay}" HorizontalAlignment="Right"/>
|
||||
|
||||
<!-- Affix -->
|
||||
<TextBlock Grid.Column="2"
|
||||
Text="{Binding PropertyDescription.Affix}"
|
||||
IsVisible="{Binding PropertyDescription.Affix, Converter={x:Static ObjectConverters.IsNotNull}}"
|
||||
Margin="5 0 0 0" />
|
||||
</Grid>
|
||||
</UserControl>
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace Artemis.UI.Shared.DefaultTypes.DataModel.Display
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a default data model display view.
|
||||
/// </summary>
|
||||
public class DefaultDataModelDisplayView : UserControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="DefaultDataModelDisplayView" /> class.
|
||||
/// </summary>
|
||||
public DefaultDataModelDisplayView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
<UserControl x:Class="Artemis.UI.Shared.DefaultTypes.DataModel.Display.DefaultDataModelDisplayView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Artemis.UI.Shared.DefaultTypes.DataModel.Display"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
xmlns:shared="clr-namespace:Artemis.UI.Shared"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance local:DefaultDataModelDisplayViewModel}">
|
||||
<UserControl.Resources>
|
||||
<shared:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Prefix -->
|
||||
<TextBlock Grid.Column="0"
|
||||
Text="{Binding PropertyDescription.Prefix}"
|
||||
Visibility="{Binding PropertyDescription.Prefix, Converter={StaticResource NullToVisibilityConverter}}"
|
||||
TextAlignment="Right"
|
||||
Margin="0 0 5 0" />
|
||||
|
||||
<!-- Value -->
|
||||
<TextBlock Grid.Column="1"
|
||||
Text="{Binding DisplayValue, Mode=OneWay}"
|
||||
HorizontalAlignment="Right"
|
||||
Visibility="{Binding ShowToString, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}" />
|
||||
<TextBlock Grid.Column="1"
|
||||
Text="null"
|
||||
FontFamily="Consolas"
|
||||
HorizontalAlignment="Right"
|
||||
Foreground="{DynamicResource MaterialDesignCheckBoxDisabled}"
|
||||
Visibility="{Binding ShowNull, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}" />
|
||||
|
||||
<!-- Affix -->
|
||||
<TextBlock Grid.Column="2"
|
||||
Text="{Binding PropertyDescription.Affix}"
|
||||
Visibility="{Binding PropertyDescription.Affix, Converter={StaticResource NullToVisibilityConverter}}"
|
||||
Margin="5 0 0 0" />
|
||||
</Grid>
|
||||
</UserControl>
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
using System;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Shared.DataModelVisualization;
|
||||
using Newtonsoft.Json;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Shared.DefaultTypes.DataModel.Display
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the default data model display view model that is used when no display viewmodel specific for the type
|
||||
/// is registered
|
||||
/// </summary>
|
||||
internal class DefaultDataModelDisplayViewModel : DataModelDisplayViewModel<object>
|
||||
{
|
||||
private readonly JsonSerializerSettings _serializerSettings;
|
||||
private string _display;
|
||||
|
||||
public DefaultDataModelDisplayViewModel()
|
||||
{
|
||||
_serializerSettings = new JsonSerializerSettings()
|
||||
{
|
||||
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
|
||||
PreserveReferencesHandling = PreserveReferencesHandling.None
|
||||
};
|
||||
_display = "null";
|
||||
}
|
||||
|
||||
public string Display
|
||||
{
|
||||
get => _display;
|
||||
set => RaiseAndSetIfChanged(ref _display, value);
|
||||
}
|
||||
|
||||
protected override void OnDisplayValueUpdated()
|
||||
{
|
||||
if (DisplayValue is Enum enumDisplayValue)
|
||||
Display = EnumUtilities.HumanizeValue(enumDisplayValue);
|
||||
else if (DisplayValue is not string)
|
||||
Display = JsonConvert.SerializeObject(DisplayValue, _serializerSettings);
|
||||
else
|
||||
Display = DisplayValue?.ToString() ?? "null";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
using System;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Shared.Controls;
|
||||
|
||||
namespace Artemis.UI.Shared.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides data about selection events raised by <see cref="DataModelPicker" />
|
||||
/// </summary>
|
||||
public class DataModelSelectedEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the data model path that was selected
|
||||
/// </summary>
|
||||
public DataModelPath? Path { get; }
|
||||
|
||||
internal DataModelSelectedEventArgs(DataModelPath? path)
|
||||
{
|
||||
Path = path;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.UI.Shared.Events
|
||||
{
|
||||
internal class DialogClosedEventArgs<TResult> : EventArgs
|
||||
{
|
||||
public TResult Result { get; }
|
||||
|
||||
public DialogClosedEventArgs(TResult result)
|
||||
{
|
||||
Result = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
using System;
|
||||
using Artemis.Core;
|
||||
|
||||
namespace Artemis.UI.Shared.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides data on LED click events raised by the device visualizer
|
||||
/// </summary>
|
||||
public class LedClickedEventArgs : EventArgs
|
||||
{
|
||||
internal LedClickedEventArgs(ArtemisDevice device, ArtemisLed led)
|
||||
{
|
||||
Device = device;
|
||||
Led = led;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The device that was clicked
|
||||
/// </summary>
|
||||
public ArtemisDevice Device { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The LED that was clicked
|
||||
/// </summary>
|
||||
public ArtemisLed Led { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
using System;
|
||||
using Artemis.Core;
|
||||
|
||||
namespace Artemis.UI.Shared.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides data on profile related events raised by the profile editor
|
||||
/// </summary>
|
||||
public class ProfileConfigurationEventArgs : EventArgs
|
||||
{
|
||||
internal ProfileConfigurationEventArgs(ProfileConfiguration? profileConfiguration)
|
||||
{
|
||||
ProfileConfiguration = profileConfiguration;
|
||||
}
|
||||
|
||||
internal ProfileConfigurationEventArgs(ProfileConfiguration? profileConfiguration, ProfileConfiguration? previousProfileConfiguration)
|
||||
{
|
||||
ProfileConfiguration = profileConfiguration;
|
||||
PreviousProfileConfiguration = previousProfileConfiguration;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the profile the event was raised for
|
||||
/// </summary>
|
||||
public ProfileConfiguration? ProfileConfiguration { get; }
|
||||
|
||||
/// <summary>
|
||||
/// If applicable, the previous active profile before the event was raised
|
||||
/// </summary>
|
||||
public ProfileConfiguration? PreviousProfileConfiguration { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
using System;
|
||||
using Artemis.Core;
|
||||
|
||||
namespace Artemis.UI.Shared.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides data on profile element related events raised by the profile editor
|
||||
/// </summary>
|
||||
public class RenderProfileElementEventArgs : EventArgs
|
||||
{
|
||||
internal RenderProfileElementEventArgs(RenderProfileElement? renderProfileElement)
|
||||
{
|
||||
RenderProfileElement = renderProfileElement;
|
||||
}
|
||||
|
||||
internal RenderProfileElementEventArgs(RenderProfileElement? renderProfileElement, RenderProfileElement? previousRenderProfileElement)
|
||||
{
|
||||
RenderProfileElement = renderProfileElement;
|
||||
PreviousRenderProfileElement = previousRenderProfileElement;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the profile element the event was raised for
|
||||
/// </summary>
|
||||
public RenderProfileElement? RenderProfileElement { get; }
|
||||
|
||||
/// <summary>
|
||||
/// If applicable, the previous active profile element before the event was raised
|
||||
/// </summary>
|
||||
public RenderProfileElement? PreviousRenderProfileElement { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
using System;
|
||||
using Artemis.UI.Shared.Controls;
|
||||
using Avalonia;
|
||||
using Avalonia.Input;
|
||||
|
||||
namespace Artemis.UI.Shared.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides data on selection events raised by the <see cref="SelectionRectangle" />.
|
||||
/// </summary>
|
||||
public class SelectionRectangleEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="SelectionRectangleEventArgs" /> class.
|
||||
/// </summary>
|
||||
public SelectionRectangleEventArgs(Rect rectangle, Rect absoluteRectangle, KeyModifiers keyModifiers)
|
||||
{
|
||||
KeyModifiers = keyModifiers;
|
||||
Rectangle = rectangle;
|
||||
AbsoluteRectangle = absoluteRectangle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the rectangle relative to the parent that was selected when the event occurred.
|
||||
/// </summary>
|
||||
public Rect Rectangle { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the rectangle relative to the window that was selected when the event occurred.
|
||||
/// </summary>
|
||||
public Rect AbsoluteRectangle { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the key modifiers that where pressed when the event occurred.
|
||||
/// </summary>
|
||||
public KeyModifiers KeyModifiers { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.UI.Shared
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents errors that occur within the Artemis Shared UI library
|
||||
/// </summary>
|
||||
public class ArtemisSharedUIException : Exception
|
||||
{
|
||||
internal ArtemisSharedUIException()
|
||||
{
|
||||
}
|
||||
|
||||
internal ArtemisSharedUIException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
internal ArtemisSharedUIException(string message, Exception inner) : base(message, inner)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.LogicalTree;
|
||||
|
||||
namespace Artemis.UI.Shared.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extension methods for Avalonia's <see cref="Control" /> type
|
||||
/// </summary>
|
||||
public static class ControlExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Clears all data validation errors on the given control and any of it's logical siblings
|
||||
/// </summary>
|
||||
/// <param name="target">The target control</param>
|
||||
public static void ClearAllDataValidationErrors(this Control target)
|
||||
{
|
||||
DataValidationErrors.ClearErrors(target);
|
||||
foreach (ILogical logicalChild in target.GetLogicalChildren())
|
||||
{
|
||||
if (logicalChild is Control childControl)
|
||||
childControl.ClearAllDataValidationErrors();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
using System.Linq;
|
||||
using Artemis.Core;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.UI.Shared.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Provides utilities when working with layers in UI elements.
|
||||
/// </summary>
|
||||
public static class LayerExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the layer's bounds in real coordinates.
|
||||
/// </summary>
|
||||
public static SKRect GetLayerBounds(this Layer layer)
|
||||
{
|
||||
if (!layer.Leds.Any())
|
||||
return SKRect.Empty;
|
||||
|
||||
return new SKRect(
|
||||
layer.Leds.Min(l => l.RgbLed.AbsoluteBoundary.Location.X),
|
||||
layer.Leds.Min(l => l.RgbLed.AbsoluteBoundary.Location.Y),
|
||||
layer.Leds.Max(l => l.RgbLed.AbsoluteBoundary.Location.X + l.RgbLed.AbsoluteBoundary.Size.Width),
|
||||
layer.Leds.Max(l => l.RgbLed.AbsoluteBoundary.Location.Y + l.RgbLed.AbsoluteBoundary.Size.Height)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the layer's anchor in real coordinates.
|
||||
/// </summary>
|
||||
public static SKPoint GetLayerAnchorPosition(this Layer layer, SKPoint? positionOverride = null)
|
||||
{
|
||||
SKRect layerBounds = GetLayerBounds(layer);
|
||||
SKPoint positionProperty = layer.Transform.Position.CurrentValue;
|
||||
if (positionOverride != null)
|
||||
positionProperty = positionOverride.Value;
|
||||
|
||||
// Start at the top left of the shape
|
||||
SKPoint position = new(layerBounds.Left, layerBounds.Top);
|
||||
|
||||
// Apply translation
|
||||
position.X += positionProperty.X * layerBounds.Width;
|
||||
position.Y += positionProperty.Y * layerBounds.Height;
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an absolute and scaled rectangular path for the given layer in real coordinates.
|
||||
/// </summary>
|
||||
public static SKPath GetLayerPath(this Layer layer, bool includeTranslation, bool includeScale, bool includeRotation)
|
||||
{
|
||||
SKRect layerBounds = GetLayerBounds(layer);
|
||||
|
||||
SKMatrix transform = layer.GetTransformMatrix(false, includeTranslation, includeScale, includeRotation, layerBounds);
|
||||
SKPath path = new();
|
||||
path.AddRect(layerBounds);
|
||||
path.Transform(transform);
|
||||
return path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new point normalized to 0.0-1.0
|
||||
/// </summary>
|
||||
public static SKPoint GetNormalizedPoint(this Layer layer, SKPoint point, bool absolute)
|
||||
{
|
||||
SKRect bounds = GetLayerBounds(layer);
|
||||
if (absolute)
|
||||
return new SKPoint(
|
||||
100 / bounds.Width * (point.X - bounds.Left) / 100,
|
||||
100 / bounds.Height * (point.Y - bounds.Top) / 100
|
||||
);
|
||||
|
||||
return new SKPoint(
|
||||
100 / bounds.Width * point.X / 100,
|
||||
100 / bounds.Height * point.Y / 100
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the offset from the given point to the closest sides of the layer's shape bounds
|
||||
/// </summary>
|
||||
public static SKPoint GetDragOffset(this Layer layer, SKPoint dragStart)
|
||||
{
|
||||
SKRect bounds = layer.GetLayerPath(true, true, false).Bounds;
|
||||
SKPoint anchor = layer.GetLayerAnchorPosition();
|
||||
|
||||
float xOffset = 0f, yOffset = 0f;
|
||||
|
||||
// X offset
|
||||
if (dragStart.X < anchor.X)
|
||||
xOffset = bounds.Left - dragStart.X;
|
||||
else if (dragStart.X > anchor.X)
|
||||
xOffset = bounds.Right - dragStart.X;
|
||||
|
||||
// Y offset
|
||||
if (dragStart.Y < anchor.Y)
|
||||
yOffset = bounds.Top - dragStart.Y;
|
||||
else if (dragStart.Y > anchor.Y)
|
||||
yOffset = bounds.Bottom - dragStart.Y;
|
||||
|
||||
return new SKPoint(xOffset, yOffset);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia;
|
||||
|
||||
namespace Artemis.UI.Shared.Extensions
|
||||
{
|
||||
public static class PointExtensions
|
||||
{
|
||||
public static Point Empty = new(0, 0);
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
using Avalonia.Media;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.UI.Shared.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for the <see cref="SKColor"/> type.
|
||||
/// </summary>
|
||||
public static class SKColorExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a SkiaSharp <see cref="SKColor"/> to an Avalonia <see cref="Color"/>.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to convert.</param>
|
||||
/// <returns>The resulting color.</returns>
|
||||
public static Color ToColor(this SKColor color)
|
||||
{
|
||||
return new Color(color.Alpha, color.Red, color.Green, color.Blue);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
using Avalonia;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.UI.Shared.Extensions;
|
||||
|
||||
public static class SKMatrixExtensions
|
||||
{
|
||||
public static Matrix ToMatrix(this SKMatrix matrix)
|
||||
{
|
||||
return new Matrix(
|
||||
matrix.ScaleX,
|
||||
matrix.SkewY,
|
||||
matrix.SkewX,
|
||||
matrix.ScaleY,
|
||||
matrix.TransX,
|
||||
matrix.TransY
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
using Avalonia;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.UI.Shared.Extensions;
|
||||
|
||||
public static class SKRectExtensions
|
||||
{
|
||||
public static Rect ToRect(this SKRect rect)
|
||||
{
|
||||
return new Rect(rect.Left, rect.Top, rect.Width, rect.Height);
|
||||
}
|
||||
|
||||
public static Rect ToRect(this SKRectI rect)
|
||||
{
|
||||
return new Rect(rect.Left, rect.Top, rect.Width, rect.Height);
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
using Artemis.Core;
|
||||
using Material.Icons;
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
|
||||
namespace Artemis.UI.Shared.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides utilities when working with types in UI elements.
|
||||
/// </summary>
|
||||
public static class TypeExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Finds an appropriate Material Design icon for the given <paramref name="type"/>.
|
||||
/// </summary>
|
||||
/// <param name="type">The type to retrieve an icon for.</param>
|
||||
/// <returns>An appropriate Material Design icon for the given <paramref name="type"/>.</returns>
|
||||
public static MaterialIconKind GetTypeIcon(this Type? type)
|
||||
{
|
||||
if (type == null)
|
||||
return MaterialIconKind.QuestionMarkCircle;
|
||||
if (type.TypeIsNumber())
|
||||
return MaterialIconKind.CalculatorVariantOutline;
|
||||
if (type.IsEnum)
|
||||
return MaterialIconKind.FormatListBulletedSquare;
|
||||
if (type == typeof(bool))
|
||||
return MaterialIconKind.CircleHalfFull;
|
||||
if (type == typeof(string))
|
||||
return MaterialIconKind.Text;
|
||||
if (type == typeof(SKColor))
|
||||
return MaterialIconKind.Palette;
|
||||
if (type.IsAssignableTo(typeof(IDataModelEvent)))
|
||||
return MaterialIconKind.LightningBolt;
|
||||
return MaterialIconKind.Matrix;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Avalonia;
|
||||
using Avalonia.VisualTree;
|
||||
|
||||
namespace Artemis.UI.Shared.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for Avalonia's <see cref="IVisual" /> type
|
||||
/// </summary>
|
||||
public static class VisualExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a recursive list of all visual children of type <typeparamref name="T" />.
|
||||
/// </summary>
|
||||
/// <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)
|
||||
{
|
||||
List<T> result = new();
|
||||
|
||||
List<IVisual>? visualChildren = root.GetVisualChildren()?.ToList();
|
||||
if (visualChildren == null || !visualChildren.Any())
|
||||
return result;
|
||||
|
||||
foreach (IVisual visualChild in visualChildren)
|
||||
{
|
||||
if (visualChild is T toFind)
|
||||
result.Add(toFind);
|
||||
|
||||
result.AddRange(GetVisualChildrenOfType<T>(visualChild));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a recursive list of all visual children with a data context of type <typeparamref name="T" />.
|
||||
/// </summary>
|
||||
/// <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)
|
||||
{
|
||||
List<T> result = new();
|
||||
|
||||
List<IVisual>? visualChildren = root.GetVisualChildren()?.ToList();
|
||||
if (visualChildren == null || !visualChildren.Any())
|
||||
return result;
|
||||
|
||||
foreach (IVisual visualChild in visualChildren)
|
||||
{
|
||||
if (visualChild is IDataContextProvider dataContextProvider && dataContextProvider.DataContext is T toFind)
|
||||
result.Add(toFind);
|
||||
|
||||
result.AddRange(GetVisualChildrenOfType<T>(visualChild));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
using System;
|
||||
using Artemis.UI.Shared.Services.Interfaces;
|
||||
using Ninject.Extensions.Conventions;
|
||||
using Ninject.Modules;
|
||||
|
||||
namespace Artemis.UI.Shared.Ninject
|
||||
{
|
||||
/// <summary>
|
||||
/// The main <see cref="NinjectModule" /> of the Artemis Shared UI toolkit that binds all services
|
||||
/// </summary>
|
||||
public class SharedUIModule : NinjectModule
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void Load()
|
||||
{
|
||||
if (Kernel == null)
|
||||
throw new ArgumentNullException("Kernel shouldn't be null here.");
|
||||
|
||||
// Bind all shared UI services as singletons
|
||||
Kernel.Bind(x =>
|
||||
{
|
||||
x.FromAssemblyContaining<IArtemisSharedUIService>()
|
||||
.IncludingNonPublicTypes()
|
||||
.SelectAllClasses()
|
||||
.InheritedFrom<IArtemisSharedUIService>()
|
||||
.BindAllInterfaces()
|
||||
.Configure(c => c.InSingletonScope());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core.LayerBrushes;
|
||||
|
||||
namespace Artemis.UI.Shared.LayerBrushes
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a view model for a brush configuration window
|
||||
/// </summary>
|
||||
public abstract class BrushConfigurationViewModel : ActivatableViewModelBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="BrushConfigurationViewModel" /> class
|
||||
/// </summary>
|
||||
/// <param name="layerBrush"></param>
|
||||
protected BrushConfigurationViewModel(BaseLayerBrush layerBrush)
|
||||
{
|
||||
LayerBrush = layerBrush;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the layer brush this view model is associated with
|
||||
/// </summary>
|
||||
public BaseLayerBrush LayerBrush { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Closes the dialog
|
||||
/// </summary>
|
||||
public void RequestClose()
|
||||
{
|
||||
CloseRequested?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the window wants to close, returning <see langword="false" /> will cause the window to stay open.
|
||||
/// </summary>
|
||||
/// <returns><see langword="true" /> if the window may close; otherwise <see langword="false" />.</returns>
|
||||
public virtual bool CanClose()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the window wants to close, returning <see langword="false" /> will cause the window to stay open.
|
||||
/// </summary>
|
||||
/// <returns>A task <see langword="true" /> if the window may close; otherwise <see langword="false" />.</returns>
|
||||
public virtual Task<bool> CanCloseAsync()
|
||||
{
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a close was requested
|
||||
/// </summary>
|
||||
public event EventHandler? CloseRequested;
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
using System;
|
||||
using Artemis.Core.LayerBrushes;
|
||||
|
||||
namespace Artemis.UI.Shared.LayerBrushes
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public class LayerBrushConfigurationDialog<T> : LayerBrushConfigurationDialog where T : BrushConfigurationViewModel
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public LayerBrushConfigurationDialog()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public LayerBrushConfigurationDialog(int dialogWidth, int dialogHeight)
|
||||
{
|
||||
DialogWidth = dialogWidth;
|
||||
DialogHeight = dialogHeight;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type Type => typeof(T);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describes a UI tab for a layer brush
|
||||
/// </summary>
|
||||
public abstract class LayerBrushConfigurationDialog : ILayerBrushConfigurationDialog
|
||||
{
|
||||
/// <summary>
|
||||
/// The default width of the dialog
|
||||
/// </summary>
|
||||
public int DialogWidth { get; set; } = 800;
|
||||
|
||||
/// <summary>
|
||||
/// The default height of the dialog
|
||||
/// </summary>
|
||||
public int DialogHeight { get; set; } = 800;
|
||||
|
||||
/// <summary>
|
||||
/// The type of view model the dialog contains
|
||||
/// </summary>
|
||||
public abstract Type Type { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core.LayerEffects;
|
||||
using Avalonia.Threading;
|
||||
|
||||
namespace Artemis.UI.Shared.LayerEffects;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a view model for an effect configuration window
|
||||
/// </summary>
|
||||
public abstract class EffectConfigurationViewModel : ActivatableViewModelBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="EffectConfigurationViewModel" /> class
|
||||
/// </summary>
|
||||
/// <param name="layerEffect"></param>
|
||||
protected EffectConfigurationViewModel(BaseLayerEffect layerEffect)
|
||||
{
|
||||
LayerEffect = layerEffect;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the layer effect this view model is associated with
|
||||
/// </summary>
|
||||
public BaseLayerEffect LayerEffect { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Closes the dialog
|
||||
/// </summary>
|
||||
public void RequestClose()
|
||||
{
|
||||
CloseRequested?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the window wants to close, returning <see langword="false" /> will cause the window to stay open.
|
||||
/// </summary>
|
||||
/// <returns><see langword="true" /> if the window may close; otherwise <see langword="false" />.</returns>
|
||||
public virtual bool CanClose()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the window wants to close, returning <see langword="false" /> will cause the window to stay open.
|
||||
/// </summary>
|
||||
/// <returns>A task <see langword="true" /> if the window may close; otherwise <see langword="false" />.</returns>
|
||||
public virtual Task<bool> CanCloseAsync()
|
||||
{
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a close was requested
|
||||
/// </summary>
|
||||
public event EventHandler? CloseRequested;
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
using System;
|
||||
using Artemis.Core.LayerEffects;
|
||||
|
||||
namespace Artemis.UI.Shared.LayerEffects
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public class LayerEffectConfigurationDialog<T> : LayerEffectConfigurationDialog where T : EffectConfigurationViewModel
|
||||
{
|
||||
|
||||
/// <inheritdoc />
|
||||
public LayerEffectConfigurationDialog()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public LayerEffectConfigurationDialog(int dialogWidth, int dialogHeight)
|
||||
{
|
||||
DialogWidth = dialogWidth;
|
||||
DialogHeight = dialogHeight;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type Type => typeof(T);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describes a UI tab for a specific layer effect
|
||||
/// </summary>
|
||||
public abstract class LayerEffectConfigurationDialog : ILayerEffectConfigurationDialog
|
||||
{
|
||||
/// <summary>
|
||||
/// The default width of the dialog
|
||||
/// </summary>
|
||||
public int DialogWidth { get; set; } = 800;
|
||||
|
||||
/// <summary>
|
||||
/// The default height of the dialog
|
||||
/// </summary>
|
||||
public int DialogHeight { get; set; } = 800;
|
||||
|
||||
/// <summary>
|
||||
/// The type of view model the dialog contains
|
||||
/// </summary>
|
||||
public abstract Type Type { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
using System;
|
||||
using Artemis.Core;
|
||||
|
||||
namespace Artemis.UI.Shared
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public class PluginConfigurationDialog<T> : PluginConfigurationDialog where T : PluginConfigurationViewModel
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override Type Type => typeof(T);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describes a configuration dialog for a specific plugin
|
||||
/// </summary>
|
||||
public abstract class PluginConfigurationDialog : IPluginConfigurationDialog
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public abstract Type Type { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
using System;
|
||||
using Artemis.Core;
|
||||
|
||||
namespace Artemis.UI.Shared
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a view model for a plugin configuration window
|
||||
/// </summary>
|
||||
public abstract class PluginConfigurationViewModel : ViewModelValidationBase, IPluginConfigurationViewModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="PluginConfigurationViewModel" /> class
|
||||
/// </summary>
|
||||
/// <param name="plugin"></param>
|
||||
protected PluginConfigurationViewModel(Plugin plugin)
|
||||
{
|
||||
Plugin = plugin;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin this configuration view model is associated with
|
||||
/// </summary>
|
||||
public Plugin Plugin { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Closes the window hosting the view model
|
||||
/// </summary>
|
||||
public void Close()
|
||||
{
|
||||
CloseRequested?.Invoke(this, EventArgs.Empty);
|
||||
OnCloseRequested();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the the window hosting the view model should close
|
||||
/// </summary>
|
||||
public virtual void OnCloseRequested()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the the window hosting the view model should close
|
||||
/// </summary>
|
||||
public event EventHandler? CloseRequested;
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
using Artemis.Core.ScriptingProviders;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Shared.ScriptingProviders
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Stylet view model containing a script editor
|
||||
/// </summary>
|
||||
public class ScriptEditorViewModel : ActivatableViewModelBase, IScriptEditorViewModel
|
||||
{
|
||||
private Script? _script;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="ScriptEditorViewModel" />
|
||||
/// </summary>
|
||||
/// <param name="scriptType">The script type this view model was created for</param>
|
||||
public ScriptEditorViewModel(ScriptType scriptType)
|
||||
{
|
||||
ScriptType = scriptType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called just before the script is changed to a different one
|
||||
/// </summary>
|
||||
/// <param name="script">The script to display or <see langword="null" /> if no script is to be displayed</param>
|
||||
protected virtual void OnScriptChanging(Script? script)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called after the script was changed to a different one
|
||||
/// </summary>
|
||||
/// <param name="script">The script to display or <see langword="null" /> if no script is to be displayed</param>
|
||||
protected virtual void OnScriptChanged(Script? script)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ScriptType ScriptType { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Script? Script
|
||||
{
|
||||
get => _script;
|
||||
internal set => RaiseAndSetIfChanged(ref _script, value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ChangeScript(Script? script)
|
||||
{
|
||||
OnScriptChanging(script);
|
||||
Script = script;
|
||||
OnScriptChanged(script);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Artemis.Core;
|
||||
|
||||
namespace Artemis.UI.Shared.Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a provider for color gradient storage.
|
||||
/// </summary>
|
||||
public interface IColorGradientStorageProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a list containing a copy of all stored color gradients.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="List{T}" /> of <see cref="ColorGradient" /> containing a copy of all stored color gradients.</returns>
|
||||
public List<ColorGradient> GetColorGradients();
|
||||
|
||||
/// <summary>
|
||||
/// Saves the provided color gradient to storage.
|
||||
/// </summary>
|
||||
/// <param name="colorGradient">The color gradient to save.</param>
|
||||
public void SaveColorGradient(ColorGradient colorGradient);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the provided color gradient from storage.
|
||||
/// </summary>
|
||||
/// <param name="colorGradient">The color gradient to delete.</param>
|
||||
public void DeleteColorGradient(ColorGradient colorGradient);
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using Avalonia.Input;
|
||||
|
||||
namespace Artemis.UI.Shared.Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a provider for custom cursors.
|
||||
/// </summary>
|
||||
public interface ICursorProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// A cursor that indicates a rotating.
|
||||
/// </summary>
|
||||
public Cursor Rotate { get; }
|
||||
|
||||
/// <summary>
|
||||
/// A cursor that indicates dragging.
|
||||
/// </summary>
|
||||
public Cursor Drag { get; }
|
||||
|
||||
/// <summary>
|
||||
/// A cursor that indicates dragging horizontally.
|
||||
/// </summary>
|
||||
public Cursor DragHorizontal { get; }
|
||||
}
|
||||
@@ -1,258 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Avalonia.Controls;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Ninject;
|
||||
using Ninject.Parameters;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Shared.Services.Builders
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a builder that can be used to create Fluent UI dialogs.
|
||||
/// </summary>
|
||||
public class ContentDialogBuilder
|
||||
{
|
||||
private readonly ContentDialog _contentDialog;
|
||||
private readonly IKernel _kernel;
|
||||
private readonly Window _parent;
|
||||
private ContentDialogViewModelBase? _viewModel;
|
||||
|
||||
internal ContentDialogBuilder(IKernel kernel, Window parent)
|
||||
{
|
||||
_kernel = kernel;
|
||||
_parent = parent;
|
||||
_contentDialog = new ContentDialog
|
||||
{
|
||||
CloseButtonText = "Close"
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the title of the dialog.
|
||||
/// </summary>
|
||||
/// <param name="title">The new title.</param>
|
||||
/// <returns>The builder that can be used to further build the dialog.</returns>
|
||||
public ContentDialogBuilder WithTitle(string? title)
|
||||
{
|
||||
_contentDialog.Title = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the content of the dialog.
|
||||
/// </summary>
|
||||
/// <param name="content">The new content.</param>
|
||||
/// <returns>The builder that can be used to further build the dialog.</returns>
|
||||
public ContentDialogBuilder WithContent(string? content)
|
||||
{
|
||||
_contentDialog.Content = content;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the default button of the dialog that is pressed on enter.
|
||||
/// </summary>
|
||||
/// <param name="defaultButton">The default button.</param>
|
||||
/// <returns>The builder that can be used to further build the dialog.</returns>
|
||||
public ContentDialogBuilder WithDefaultButton(ContentDialogButton defaultButton)
|
||||
{
|
||||
_contentDialog.DefaultButton = (FluentAvalonia.UI.Controls.ContentDialogButton) defaultButton;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the primary button of the dialog.
|
||||
/// </summary>
|
||||
/// <param name="configure">An action to configure the button.</param>
|
||||
/// <returns>The builder that can be used to further build the dialog.</returns>
|
||||
public ContentDialogBuilder HavingPrimaryButton(Action<ContentDialogButtonBuilder> configure)
|
||||
{
|
||||
ContentDialogButtonBuilder builder = new();
|
||||
configure(builder);
|
||||
|
||||
_contentDialog.IsPrimaryButtonEnabled = true;
|
||||
_contentDialog.PrimaryButtonText = builder.Text;
|
||||
_contentDialog.PrimaryButtonCommand = builder.Command;
|
||||
_contentDialog.PrimaryButtonCommandParameter = builder.CommandParameter;
|
||||
|
||||
// I feel like this isn't my responsibility...
|
||||
if (builder.Command != null)
|
||||
{
|
||||
_contentDialog.IsPrimaryButtonEnabled = builder.Command.CanExecute(builder.CommandParameter);
|
||||
builder.Command.CanExecuteChanged += (_, _) => _contentDialog.IsPrimaryButtonEnabled = builder.Command.CanExecute(builder.CommandParameter);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the secondary button of the dialog.
|
||||
/// </summary>
|
||||
/// <param name="configure">An action to configure the button.</param>
|
||||
/// <returns>The builder that can be used to further build the dialog.</returns>
|
||||
public ContentDialogBuilder HavingSecondaryButton(Action<ContentDialogButtonBuilder> configure)
|
||||
{
|
||||
ContentDialogButtonBuilder builder = new();
|
||||
configure(builder);
|
||||
|
||||
_contentDialog.IsSecondaryButtonEnabled = true;
|
||||
_contentDialog.SecondaryButtonText = builder.Text;
|
||||
_contentDialog.SecondaryButtonCommand = builder.Command;
|
||||
_contentDialog.SecondaryButtonCommandParameter = builder.CommandParameter;
|
||||
|
||||
// I feel like this isn't my responsibility...
|
||||
if (builder.Command != null)
|
||||
{
|
||||
_contentDialog.IsSecondaryButtonEnabled = builder.Command.CanExecute(builder.CommandParameter);
|
||||
builder.Command.CanExecuteChanged += (_, _) => _contentDialog.IsSecondaryButtonEnabled = builder.Command.CanExecute(builder.CommandParameter);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the text of the close button of the dialog.
|
||||
/// </summary>
|
||||
/// <param name="text">The new text.</param>
|
||||
/// <returns>The builder that can be used to further build the dialog.</returns>
|
||||
public ContentDialogBuilder WithCloseButtonText(string? text)
|
||||
{
|
||||
_contentDialog.CloseButtonText = text;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the view model of the content dialog, hosting it inside the dialog.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the view model to host.</typeparam>
|
||||
/// <param name="viewModel">The resulting view model.</param>
|
||||
/// <param name="parameters">Optional parameters to pass to the constructor of the view model, case and order sensitive.</param>
|
||||
/// <returns>The builder that can be used to further build the dialog.</returns>
|
||||
public ContentDialogBuilder WithViewModel<T>(out T viewModel, params (string name, object? value)[] parameters) where T : ContentDialogViewModelBase
|
||||
{
|
||||
IParameter[] paramsArray = parameters.Select(kv => new ConstructorArgument(kv.name, kv.value)).Cast<IParameter>().ToArray();
|
||||
viewModel = _kernel.Get<T>(paramsArray);
|
||||
viewModel.ContentDialog = _contentDialog;
|
||||
_contentDialog.Content = viewModel;
|
||||
|
||||
_viewModel = viewModel;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously shows the content dialog.
|
||||
/// </summary>
|
||||
/// <returns>A task containing the result of the content dialog.</returns>
|
||||
/// <exception cref="ArtemisSharedUIException">Thrown when the parent window does not contain a panel at its root.</exception>
|
||||
public async Task<ContentDialogResult> ShowAsync()
|
||||
{
|
||||
if (_parent.Content is not Panel panel)
|
||||
throw new ArtemisSharedUIException($"The parent window {_parent.GetType().FullName} should contain a panel at its root");
|
||||
|
||||
try
|
||||
{
|
||||
panel.Children.Add(_contentDialog);
|
||||
ContentDialogResult result = await _contentDialog.ShowAsync();
|
||||
|
||||
// Take the dialog away from the VM in case it's going to try to hide it again or whatever...
|
||||
if (_viewModel != null)
|
||||
_viewModel.ContentDialog = null;
|
||||
|
||||
return result;
|
||||
}
|
||||
finally
|
||||
{
|
||||
panel.Children.Remove(_contentDialog);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a content dialog button.
|
||||
/// </summary>
|
||||
public enum ContentDialogButton
|
||||
{
|
||||
/// <summary>
|
||||
/// No button.
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// The primary button.
|
||||
/// </summary>
|
||||
Primary,
|
||||
|
||||
/// <summary>
|
||||
/// The secondary button.
|
||||
/// </summary>
|
||||
Secondary,
|
||||
|
||||
/// <summary>
|
||||
/// The close button.
|
||||
/// </summary>
|
||||
Close,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a builder that can be used to create buttons inside content dialogs.
|
||||
/// </summary>
|
||||
public class ContentDialogButtonBuilder
|
||||
{
|
||||
internal ContentDialogButtonBuilder()
|
||||
{
|
||||
}
|
||||
|
||||
internal string? Text { get; set; }
|
||||
internal ICommand? Command { get; set; }
|
||||
internal Action? Action { get; set; }
|
||||
internal object? CommandParameter { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Changes text message of the button.
|
||||
/// </summary>
|
||||
/// <param name="text">The new text.</param>
|
||||
/// <returns>The notification builder that can be used to further build the button.</returns>
|
||||
public ContentDialogButtonBuilder WithText(string? text)
|
||||
{
|
||||
Text = text;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes action that is called when the button is clicked.
|
||||
/// </summary>
|
||||
/// <param name="action">The action to call when the button is clicked.</param>
|
||||
/// <returns>The builder that can be used to further build the button.</returns>
|
||||
public ContentDialogButtonBuilder WithAction(Action action)
|
||||
{
|
||||
Action = action;
|
||||
Command = ReactiveCommand.Create(() => Action());
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes command that is called when the button is clicked.
|
||||
/// </summary>
|
||||
/// <param name="command">The command to call when the button is clicked.</param>
|
||||
/// <returns>The builder that can be used to further build the button.</returns>
|
||||
public ContentDialogButtonBuilder WithCommand(ICommand? command)
|
||||
{
|
||||
Command = command;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes parameter of the command that is called when the button is clicked.
|
||||
/// </summary>
|
||||
/// <param name="commandParameter">The parameter of the command to call when the button is clicked.</param>
|
||||
/// <returns>The builder that can be used to further build the button.</returns>
|
||||
public ContentDialogButtonBuilder WithCommandParameter(object? commandParameter)
|
||||
{
|
||||
CommandParameter = commandParameter;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
using Avalonia.Controls;
|
||||
|
||||
namespace Artemis.UI.Shared.Services.Builders
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a builder that can create a <see cref="FileDialogFilter" />.
|
||||
/// </summary>
|
||||
public class FileDialogFilterBuilder
|
||||
{
|
||||
private readonly FileDialogFilter _filter;
|
||||
|
||||
internal FileDialogFilterBuilder()
|
||||
{
|
||||
_filter = new FileDialogFilter();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the name of the filter
|
||||
/// </summary>
|
||||
public FileDialogFilterBuilder WithName(string name)
|
||||
{
|
||||
_filter.Name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the provided extension to the filter
|
||||
/// </summary>
|
||||
public FileDialogFilterBuilder WithExtension(string extension)
|
||||
{
|
||||
_filter.Extensions.Add(extension);
|
||||
return this;
|
||||
}
|
||||
|
||||
internal FileDialogFilter Build()
|
||||
{
|
||||
return _filter;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loaded 100 of 1110 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user