Rename Avalonia projects
Add Avalonia platform projects
No files matched your search
@@ -0,0 +1,454 @@
|
||||
## 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
|
||||
@@ -0,0 +1,13 @@
|
||||
<Application xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:Artemis.UI.Linux"
|
||||
xmlns:ui="clr-namespace:Artemis.UI;assembly=Artemis.UI"
|
||||
x:Class="Artemis.UI.Linux.App">
|
||||
<Application.DataTemplates>
|
||||
<ui:ViewLocator/>
|
||||
</Application.DataTemplates>
|
||||
|
||||
<Application.Styles>
|
||||
<FluentTheme Mode="Light"/>
|
||||
</Application.Styles>
|
||||
</Application>
|
||||
@@ -0,0 +1,50 @@
|
||||
using Artemis.Core.Ninject;
|
||||
using Artemis.UI.Ninject;
|
||||
using Artemis.UI.Screens.Root.ViewModels;
|
||||
using Artemis.UI.Shared.Ninject;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Threading;
|
||||
using Ninject;
|
||||
using ReactiveUI;
|
||||
using Splat.Ninject;
|
||||
|
||||
namespace Artemis.UI.Linux
|
||||
{
|
||||
public class App : Application
|
||||
{
|
||||
private StandardKernel _kernel = null!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
InitializeNinject();
|
||||
RxApp.MainThreadScheduler = AvaloniaScheduler.Instance;
|
||||
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public override void OnFrameworkInitializationCompleted()
|
||||
{
|
||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
desktop.MainWindow = new MainWindow
|
||||
{
|
||||
DataContext = _kernel.Get<RootViewModel>()
|
||||
};
|
||||
|
||||
base.OnFrameworkInitializationCompleted();
|
||||
}
|
||||
|
||||
private void InitializeNinject()
|
||||
{
|
||||
_kernel = new StandardKernel();
|
||||
_kernel.Settings.InjectNonPublic = true;
|
||||
|
||||
_kernel.Load<CoreModule>();
|
||||
_kernel.Load<UIModule>();
|
||||
_kernel.Load<SharedUIModule>();
|
||||
|
||||
_kernel.UseNinjectDependencyResolver();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<AvaloniaResource Include="Assets\**" />
|
||||
<None Remove=".gitignore" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="0.10.10" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="0.10.10" />
|
||||
<!--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.10" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.10" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Artemis.Core\Artemis.Core.csproj" />
|
||||
<ProjectReference Include="..\Artemis.UI\Artemis.UI.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
After Width: | Height: | Size: 172 KiB |
@@ -0,0 +1,24 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,454 @@
|
||||
## 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
|
||||
@@ -0,0 +1,13 @@
|
||||
<Application xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:Artemis.UI.MacOS"
|
||||
xmlns:ui="clr-namespace:Artemis.UI;assembly=Artemis.UI"
|
||||
x:Class="Artemis.UI.MacOS.App">
|
||||
<Application.DataTemplates>
|
||||
<ui:ViewLocator/>
|
||||
</Application.DataTemplates>
|
||||
|
||||
<Application.Styles>
|
||||
<FluentTheme Mode="Light"/>
|
||||
</Application.Styles>
|
||||
</Application>
|
||||
@@ -0,0 +1,50 @@
|
||||
using Artemis.Core.Ninject;
|
||||
using Artemis.UI.Ninject;
|
||||
using Artemis.UI.Screens.Root.ViewModels;
|
||||
using Artemis.UI.Shared.Ninject;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Threading;
|
||||
using Ninject;
|
||||
using ReactiveUI;
|
||||
using Splat.Ninject;
|
||||
|
||||
namespace Artemis.UI.MacOS
|
||||
{
|
||||
public class App : Application
|
||||
{
|
||||
private StandardKernel _kernel = null!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
InitializeNinject();
|
||||
RxApp.MainThreadScheduler = AvaloniaScheduler.Instance;
|
||||
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public override void OnFrameworkInitializationCompleted()
|
||||
{
|
||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
desktop.MainWindow = new MainWindow
|
||||
{
|
||||
DataContext = _kernel.Get<RootViewModel>()
|
||||
};
|
||||
|
||||
base.OnFrameworkInitializationCompleted();
|
||||
}
|
||||
|
||||
private void InitializeNinject()
|
||||
{
|
||||
_kernel = new StandardKernel();
|
||||
_kernel.Settings.InjectNonPublic = true;
|
||||
|
||||
_kernel.Load<CoreModule>();
|
||||
_kernel.Load<UIModule>();
|
||||
_kernel.Load<SharedUIModule>();
|
||||
|
||||
_kernel.UseNinjectDependencyResolver();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<AvaloniaResource Include="Assets\**" />
|
||||
<None Remove=".gitignore" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="0.10.10" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="0.10.10" />
|
||||
<!--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.10" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.10" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Artemis.Core\Artemis.Core.csproj" />
|
||||
<ProjectReference Include="..\Artemis.UI\Artemis.UI.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
After Width: | Height: | Size: 172 KiB |
@@ -0,0 +1,24 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<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>
|
||||
@@ -0,0 +1,506 @@
|
||||
<?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>
|
||||
@@ -0,0 +1,39 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ApplicationIcon />
|
||||
<StartupObject />
|
||||
<OutputPath>bin\</OutputPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DocumentationFile>C:\Repos\Artemis\src\Artemis.UI.Avalonia.Shared\Artemis.UI.Avalonia.Shared.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="0.10.10" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.10" />
|
||||
<PackageReference Include="Avalonia.Svg.Skia" Version="0.10.8.3" />
|
||||
<PackageReference Include="Avalonia.Xaml.Behaviors" Version="0.10.10" />
|
||||
<PackageReference Include="Avalonia.Xaml.Interactions" Version="0.10.10" />
|
||||
<PackageReference Include="Avalonia.Xaml.Interactivity" Version="0.10.10" />
|
||||
<PackageReference Include="FluentAvaloniaUI" Version="1.1.5" />
|
||||
<PackageReference Include="Material.Icons.Avalonia" Version="1.0.2" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Artemis.Core\Artemis.Core.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Material.Icons.Avalonia">
|
||||
<HintPath>..\..\..\..\Users\Robert\.nuget\packages\material.icons.avalonia\1.0.2\lib\netstandard2.0\Material.Icons.Avalonia.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RGB.NET.Core">
|
||||
<HintPath>..\..\..\..\RGB.NET\bin\net5.0\RGB.NET.Core.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="Services\WindowService\ExceptionDialogView.axaml.cs">
|
||||
<DependentUpon>%(Filename)</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,3 @@
|
||||
<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>
|
||||
@@ -0,0 +1,8 @@
|
||||
<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>
|
||||
@@ -0,0 +1,102 @@
|
||||
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
|
||||
{
|
||||
public partial class ArtemisIcon : UserControl
|
||||
{
|
||||
#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);
|
||||
|
||||
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 };
|
||||
}
|
||||
}
|
||||
|
||||
/// <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
|
||||
|
||||
public ArtemisIcon()
|
||||
{
|
||||
InitializeComponent();
|
||||
DetachedFromLogicalTree += OnDetachedFromLogicalTree;
|
||||
LayoutUpdated += OnLayoutUpdated;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,306 @@
|
||||
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(0, 0, Device.RgbDevice.ActualSize.Width, Device.RgbDevice.ActualSize.Height));
|
||||
|
||||
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;
|
||||
|
||||
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
_deviceImage?.Dispose();
|
||||
_deviceImage = null;
|
||||
|
||||
if (Device != null)
|
||||
{
|
||||
Device.RgbDevice.PropertyChanged -= DevicePropertyChanged;
|
||||
Device.DeviceUpdated -= DeviceUpdated;
|
||||
}
|
||||
|
||||
base.OnDetachedFromVisualTree(e);
|
||||
}
|
||||
|
||||
/// <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 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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<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>
|
||||
@@ -0,0 +1,108 @@
|
||||
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
|
||||
{
|
||||
public partial class EnumComboBox : UserControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the currently selected value
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<object?> ValueProperty =
|
||||
AvaloniaProperty.Register<EnumComboBox, object?>(nameof(Value), defaultBindingMode: BindingMode.TwoWay, notifying: ValueChanged);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the currently selected value
|
||||
/// </summary>
|
||||
public object? Value
|
||||
{
|
||||
get => GetValue(ValueProperty);
|
||||
set => SetValue(ValueProperty, value);
|
||||
}
|
||||
|
||||
private ComboBox? _enumComboBox;
|
||||
private readonly ObservableCollection<(Enum, string)> _currentValues = new();
|
||||
|
||||
private static void ValueChanged(IAvaloniaObject sender, bool before)
|
||||
{
|
||||
if (sender is EnumComboBox enumCombo && !before)
|
||||
{
|
||||
enumCombo.UpdateValues();
|
||||
enumCombo.UpdateSelection();
|
||||
}
|
||||
}
|
||||
|
||||
public EnumComboBox()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
private void OnSelectionChanged(object? sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (_enumComboBox == null)
|
||||
return;
|
||||
|
||||
var (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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<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>
|
||||
@@ -0,0 +1,111 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
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
|
||||
{
|
||||
public class ProfileConfigurationIcon : UserControl
|
||||
{
|
||||
#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
|
||||
|
||||
public ProfileConfigurationIcon()
|
||||
{
|
||||
InitializeComponent();
|
||||
DetachedFromLogicalTree += OnDetachedFromLogicalTree;
|
||||
PropertyChanged += OnPropertyChanged;
|
||||
}
|
||||
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (ConfigurationIcon == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
if (ConfigurationIcon.IconType == ProfileConfigurationIconType.SvgImage && ConfigurationIcon.FileIcon != null)
|
||||
{
|
||||
SvgSource source = new();
|
||||
source.Load(ConfigurationIcon.FileIcon);
|
||||
Content = new SvgImage {Source = source};
|
||||
}
|
||||
else if (ConfigurationIcon.IconType == ProfileConfigurationIconType.MaterialIcon && ConfigurationIcon.MaterialIcon != null)
|
||||
{
|
||||
Content = Enum.TryParse(ConfigurationIcon.MaterialIcon, true, out MaterialIconKind parsedIcon)
|
||||
? new MaterialIcon {Kind = parsedIcon!}
|
||||
: new MaterialIcon {Kind = MaterialIconKind.QuestionMark};
|
||||
}
|
||||
else if (ConfigurationIcon.IconType == ProfileConfigurationIconType.BitmapImage && ConfigurationIcon.FileIcon != null)
|
||||
Content = new Image {Source = new Bitmap(ConfigurationIcon.FileIcon)};
|
||||
else
|
||||
Content = new MaterialIcon {Kind = MaterialIconKind.QuestionMark};
|
||||
}
|
||||
catch
|
||||
{
|
||||
Content = new MaterialIcon {Kind = MaterialIconKind.QuestionMark};
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
#region Event handlers
|
||||
|
||||
private void OnDetachedFromLogicalTree(object? sender, LogicalTreeAttachmentEventArgs e)
|
||||
{
|
||||
if (ConfigurationIcon != null)
|
||||
ConfigurationIcon.PropertyChanged -= IconOnPropertyChanged;
|
||||
|
||||
if (Content is SvgImage svgImage)
|
||||
svgImage.Source?.Dispose();
|
||||
else if (Content is Image image)
|
||||
((Bitmap) image.Source).Dispose();
|
||||
}
|
||||
|
||||
private void OnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.Property == ConfigurationIconProperty)
|
||||
{
|
||||
if (e.OldValue is Core.ProfileConfigurationIcon oldIcon)
|
||||
oldIcon.PropertyChanged -= IconOnPropertyChanged;
|
||||
if (e.NewValue is Core.ProfileConfigurationIcon newIcon)
|
||||
newIcon.PropertyChanged += IconOnPropertyChanged;
|
||||
Update();
|
||||
}
|
||||
}
|
||||
|
||||
private void IconOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
Update();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
using System;
|
||||
using System.Windows.Input;
|
||||
using Artemis.Core;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Media;
|
||||
using FluentAvalonia.Styling;
|
||||
|
||||
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(AvaloniaLocator.Current.GetService<FluentAvaloniaTheme>().CustomAccentColor ?? Colors.Transparent, 0.25));
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="BorderBrush" /> property.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<IBrush> BorderBrushProperty =
|
||||
AvaloniaProperty.Register<SelectionRectangle, IBrush>(nameof(BorderBrush),
|
||||
new SolidColorBrush(AvaloniaLocator.Current.GetService<FluentAvaloniaTheme>().CustomAccentColor ?? Colors.Transparent));
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="BorderBrush" /> property.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<double> BorderThicknessProperty =
|
||||
AvaloniaProperty.Register<SelectionRectangle, double>(nameof(BorderThickness), 1);
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="get_InputElement" /> property.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<IControl?> InputElementProperty =
|
||||
AvaloniaProperty.Register<SelectionRectangle, IControl?>(nameof(InputElement), notifying: OnInputElementChanged);
|
||||
|
||||
public static readonly StyledProperty<ICommand?> SelectionUpdatedProperty
|
||||
= AvaloniaProperty.Register<SelectionRectangle, ICommand?>(nameof(SelectionUpdated));
|
||||
|
||||
public static readonly StyledProperty<ICommand?> SelectionFinishedProperty
|
||||
= AvaloniaProperty.Register<SelectionRectangle, ICommand?>(nameof(SelectionUpdated));
|
||||
|
||||
private Rect? _displayRect;
|
||||
private IControl? _oldInputElement;
|
||||
private Point _startPosition;
|
||||
|
||||
/// <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);
|
||||
}
|
||||
|
||||
public IControl? InputElement
|
||||
{
|
||||
get => GetValue(InputElementProperty);
|
||||
set => SetValue(InputElementProperty, value);
|
||||
}
|
||||
|
||||
public ICommand? SelectionUpdated
|
||||
{
|
||||
get => GetValue(SelectionUpdatedProperty);
|
||||
set => SetValue(SelectionUpdatedProperty, value);
|
||||
}
|
||||
|
||||
public ICommand? SelectionFinished
|
||||
{
|
||||
get => GetValue(SelectionFinishedProperty);
|
||||
set => SetValue(SelectionFinishedProperty, value);
|
||||
}
|
||||
|
||||
private static void OnInputElementChanged(IAvaloniaObject sender, bool before)
|
||||
{
|
||||
((SelectionRectangle) sender).SubscribeToInputElement();
|
||||
}
|
||||
|
||||
private void ParentOnPointerPressed(object? sender, PointerPressedEventArgs e)
|
||||
{
|
||||
if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
|
||||
return;
|
||||
|
||||
e.Pointer.Capture(this);
|
||||
|
||||
_startPosition = e.GetPosition(Parent);
|
||||
_displayRect = null;
|
||||
}
|
||||
|
||||
private void ParentOnPointerMoved(object? sender, PointerEventArgs e)
|
||||
{
|
||||
if (!ReferenceEquals(e.Pointer.Captured, this))
|
||||
return;
|
||||
|
||||
Point currentPosition = e.GetPosition(Parent);
|
||||
_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))
|
||||
);
|
||||
SelectionUpdated?.Execute(_displayRect.Value);
|
||||
|
||||
InvalidateVisual();
|
||||
}
|
||||
|
||||
private void ParentOnPointerReleased(object? sender, PointerReleasedEventArgs e)
|
||||
{
|
||||
if (!ReferenceEquals(e.Pointer.Captured, this))
|
||||
return;
|
||||
|
||||
e.Pointer.Capture(null);
|
||||
|
||||
if (_displayRect != null)
|
||||
SelectionFinished?.Execute(_displayRect.Value);
|
||||
|
||||
_displayRect = null;
|
||||
InvalidateVisual();
|
||||
}
|
||||
|
||||
private void SubscribeToInputElement()
|
||||
{
|
||||
if (_oldInputElement != null)
|
||||
{
|
||||
_oldInputElement.PointerPressed -= ParentOnPointerPressed;
|
||||
_oldInputElement.PointerMoved -= ParentOnPointerMoved;
|
||||
_oldInputElement.PointerReleased -= ParentOnPointerReleased;
|
||||
}
|
||||
|
||||
_oldInputElement = InputElement;
|
||||
|
||||
if (InputElement != null)
|
||||
{
|
||||
InputElement.PointerPressed += ParentOnPointerPressed;
|
||||
InputElement.PointerMoved += ParentOnPointerMoved;
|
||||
InputElement.PointerReleased += ParentOnPointerReleased;
|
||||
}
|
||||
}
|
||||
|
||||
#region Overrides of Visual
|
||||
|
||||
public override void Render(DrawingContext drawingContext)
|
||||
{
|
||||
if (_displayRect != null)
|
||||
drawingContext.DrawRectangle(Background, new Pen(BorderBrush, BorderThickness), _displayRect.Value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
SubscribeToInputElement();
|
||||
base.OnAttachedToVisualTree(e);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
if (_oldInputElement != null)
|
||||
{
|
||||
_oldInputElement.PointerPressed -= ParentOnPointerPressed;
|
||||
_oldInputElement.PointerMoved -= ParentOnPointerMoved;
|
||||
_oldInputElement.PointerReleased -= ParentOnPointerReleased;
|
||||
_oldInputElement = null;
|
||||
}
|
||||
|
||||
base.OnDetachedFromVisualTree(e);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using Artemis.Core;
|
||||
|
||||
namespace Artemis.UI.Shared.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides data about selection events raised by <see cref="DataModelDynamicViewModel" />
|
||||
/// </summary>
|
||||
public class DataModelInputDynamicEventArgs : EventArgs
|
||||
{
|
||||
internal DataModelInputDynamicEventArgs(DataModelPath? dataModelPath)
|
||||
{
|
||||
DataModelPath = dataModelPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data model path that was selected
|
||||
/// </summary>
|
||||
public DataModelPath? DataModelPath { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.UI.Shared.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides data about submit events raised by <see cref="DataModelStaticViewModel" />
|
||||
/// </summary>
|
||||
public class DataModelInputStaticEventArgs : EventArgs
|
||||
{
|
||||
internal DataModelInputStaticEventArgs(object? value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The value that was submitted
|
||||
/// </summary>
|
||||
public object? Value { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.UI.Shared.Events
|
||||
{
|
||||
internal class DialogClosedEventArgs<TResult> : EventArgs
|
||||
{
|
||||
public TResult Result { get; }
|
||||
|
||||
public DialogClosedEventArgs(TResult result)
|
||||
{
|
||||
Result = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
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; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
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; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
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; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using Avalonia;
|
||||
|
||||
namespace Artemis.UI.Shared.Events
|
||||
{
|
||||
public class SelectionRectangleEventArgs : EventArgs
|
||||
{
|
||||
public SelectionRectangleEventArgs(Rect rect)
|
||||
{
|
||||
Rect = rect;
|
||||
}
|
||||
|
||||
public Rect Rect { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.UI.Shared.Exceptions
|
||||
{
|
||||
/// <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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
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());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
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; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using Artemis.Core;
|
||||
|
||||
namespace Artemis.UI.Shared
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a view model for a plugin configuration window
|
||||
/// </summary>
|
||||
public abstract class PluginConfigurationViewModel : ViewModelBase, 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the the window hosting the view model should close
|
||||
/// </summary>
|
||||
public event EventHandler? CloseRequested;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
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;
|
||||
|
||||
namespace Artemis.UI.Shared.Services.Builders
|
||||
{
|
||||
public class ContentDialogBuilder
|
||||
{
|
||||
private readonly ContentDialog _contentDialog;
|
||||
private readonly IKernel _kernel;
|
||||
private readonly Window _parent;
|
||||
|
||||
internal ContentDialogBuilder(IKernel kernel, Window parent)
|
||||
{
|
||||
_kernel = kernel;
|
||||
_parent = parent;
|
||||
_contentDialog = new ContentDialog
|
||||
{
|
||||
CloseButtonText = "Close"
|
||||
};
|
||||
}
|
||||
|
||||
public ContentDialogBuilder WithTitle(string? title)
|
||||
{
|
||||
_contentDialog.Title = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ContentDialogBuilder WithContent(string? content)
|
||||
{
|
||||
_contentDialog.Content = content;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ContentDialogBuilder WithDefaultButton(ContentDialogButton defaultButton)
|
||||
{
|
||||
_contentDialog.DefaultButton = defaultButton;
|
||||
return this;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public ContentDialogBuilder WithCloseButtonText(string? text)
|
||||
{
|
||||
_contentDialog.CloseButtonText = text;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ContentDialogBuilder WithViewModel<T>(out T viewModel, params (string name, object value)[] parameters) where T : ViewModelBase
|
||||
{
|
||||
IParameter[] paramsArray = parameters.Select(kv => new ConstructorArgument(kv.name, kv.value)).Cast<IParameter>().ToArray();
|
||||
viewModel = _kernel.Get<T>(paramsArray);
|
||||
_contentDialog.Content = viewModel;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public async Task<ContentDialogResult> ShowAsync()
|
||||
{
|
||||
if (_parent.Content is not Panel panel)
|
||||
return ContentDialogResult.None;
|
||||
|
||||
try
|
||||
{
|
||||
panel.Children.Add(_contentDialog);
|
||||
return await _contentDialog.ShowAsync();
|
||||
}
|
||||
finally
|
||||
{
|
||||
panel.Children.Remove(_contentDialog);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ContentDialogButtonBuilder
|
||||
{
|
||||
internal ContentDialogButtonBuilder()
|
||||
{
|
||||
}
|
||||
|
||||
internal string? Text { get; set; }
|
||||
internal ICommand? Command { get; set; }
|
||||
internal object? CommandParameter { get; set; }
|
||||
|
||||
public ContentDialogButtonBuilder WithText(string? text)
|
||||
{
|
||||
Text = text;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ContentDialogButtonBuilder WithCommand(ICommand? command)
|
||||
{
|
||||
Command = command;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ContentDialogButtonBuilder WithCommandParameter(object? commandParameter)
|
||||
{
|
||||
CommandParameter = commandParameter;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.Threading;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using ReactiveUI;
|
||||
using Button = Avalonia.Controls.Button;
|
||||
|
||||
namespace Artemis.UI.Shared.Services.Builders
|
||||
{
|
||||
public class NotificationBuilder
|
||||
{
|
||||
private readonly InfoBar _infoBar;
|
||||
private readonly Window _parent;
|
||||
private TimeSpan _timeout = TimeSpan.FromSeconds(5);
|
||||
|
||||
public NotificationBuilder(Window parent)
|
||||
{
|
||||
_parent = parent;
|
||||
_infoBar = new InfoBar
|
||||
{
|
||||
Classes = Classes.Parse("notification-info-bar"),
|
||||
VerticalAlignment = VerticalAlignment.Bottom,
|
||||
HorizontalAlignment = HorizontalAlignment.Right
|
||||
};
|
||||
}
|
||||
|
||||
public NotificationBuilder WithTitle(string? title)
|
||||
{
|
||||
_infoBar.Title = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
public NotificationBuilder WithMessage(string? content)
|
||||
{
|
||||
_infoBar.Message = content;
|
||||
return this;
|
||||
}
|
||||
|
||||
public NotificationBuilder WithTimeout(TimeSpan timeout)
|
||||
{
|
||||
_timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
public NotificationBuilder WithVerticalPosition(VerticalAlignment position)
|
||||
{
|
||||
_infoBar.VerticalAlignment = position;
|
||||
return this;
|
||||
}
|
||||
|
||||
public NotificationBuilder WithHorizontalPosition(HorizontalAlignment position)
|
||||
{
|
||||
_infoBar.HorizontalAlignment = position;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a filter to the dialog
|
||||
/// </summary>
|
||||
public NotificationBuilder HavingButton(Action<NotificationButtonBuilder> configure)
|
||||
{
|
||||
NotificationButtonBuilder builder = new();
|
||||
configure(builder);
|
||||
_infoBar.ActionButton = builder.Build();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public NotificationBuilder WithSeverity(NotificationSeverity severity)
|
||||
{
|
||||
_infoBar.Severity = (InfoBarSeverity) severity;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void Show()
|
||||
{
|
||||
if (_parent.Content is not Panel panel)
|
||||
return;
|
||||
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
panel.Children.Add(_infoBar);
|
||||
_infoBar.Closed += InfoBarOnClosed;
|
||||
_infoBar.IsOpen = true;
|
||||
});
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(_timeout);
|
||||
Dispatcher.UIThread.Post(() => _infoBar.IsOpen = false);
|
||||
});
|
||||
}
|
||||
|
||||
private void InfoBarOnClosed(InfoBar sender, InfoBarClosedEventArgs args)
|
||||
{
|
||||
_infoBar.Closed -= InfoBarOnClosed;
|
||||
if (_parent.Content is not Panel panel)
|
||||
return;
|
||||
|
||||
panel.Children.Remove(_infoBar);
|
||||
}
|
||||
}
|
||||
|
||||
public class NotificationButtonBuilder
|
||||
{
|
||||
private string _text = "Text";
|
||||
private Action? _action;
|
||||
|
||||
public NotificationButtonBuilder WithText(string text)
|
||||
{
|
||||
_text = text;
|
||||
return this;
|
||||
}
|
||||
|
||||
public NotificationButtonBuilder WithAction(Action action)
|
||||
{
|
||||
_action = action;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IControl Build()
|
||||
{
|
||||
return _action != null
|
||||
? new Button {Content = _text, Command = ReactiveCommand.Create(() => _action)}
|
||||
: new Button {Content = _text};
|
||||
}
|
||||
}
|
||||
|
||||
public enum NotificationSeverity
|
||||
{
|
||||
Informational,
|
||||
Success,
|
||||
Warning,
|
||||
Error
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Controls;
|
||||
|
||||
namespace Artemis.UI.Shared.Services.Builders
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a builder that can create a <see cref="OpenFileDialog" />.
|
||||
/// </summary>
|
||||
public class OpenFileDialogBuilder
|
||||
{
|
||||
private readonly OpenFileDialog _openFileDialog;
|
||||
private readonly Window _parent;
|
||||
|
||||
public OpenFileDialogBuilder(Window parent)
|
||||
{
|
||||
_parent = parent;
|
||||
_openFileDialog = new OpenFileDialog();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicate that the user can select multiple files.
|
||||
/// </summary>
|
||||
public OpenFileDialogBuilder WithAllowMultiple()
|
||||
{
|
||||
_openFileDialog.AllowMultiple = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the title of the dialog
|
||||
/// </summary>
|
||||
public OpenFileDialogBuilder WithTitle(string? title)
|
||||
{
|
||||
_openFileDialog.Title = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the initial directory of the dialog
|
||||
/// </summary>
|
||||
public OpenFileDialogBuilder WithDirectory(string? directory)
|
||||
{
|
||||
_openFileDialog.Directory = directory;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the initial file name of the dialog
|
||||
/// </summary>
|
||||
public OpenFileDialogBuilder WithInitialFileName(string? initialFileName)
|
||||
{
|
||||
_openFileDialog.InitialFileName = initialFileName;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a filter to the dialog
|
||||
/// </summary>
|
||||
public OpenFileDialogBuilder HavingFilter(Action<FileDialogFilterBuilder> configure)
|
||||
{
|
||||
FileDialogFilterBuilder builder = new();
|
||||
configure(builder);
|
||||
_openFileDialog.Filters.Add(builder.Build());
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
public async Task<string[]?> ShowAsync()
|
||||
{
|
||||
return await _openFileDialog.ShowAsync(_parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Controls;
|
||||
|
||||
namespace Artemis.UI.Shared.Services.Builders
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a builder that can create a <see cref="SaveFileDialog" />.
|
||||
/// </summary>
|
||||
public class SaveFileDialogBuilder
|
||||
{
|
||||
private readonly Window _parent;
|
||||
private readonly SaveFileDialog _saveFileDialog;
|
||||
|
||||
public SaveFileDialogBuilder(Window parent)
|
||||
{
|
||||
_parent = parent;
|
||||
_saveFileDialog = new SaveFileDialog();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the title of the dialog
|
||||
/// </summary>
|
||||
public SaveFileDialogBuilder WithTitle(string? title)
|
||||
{
|
||||
_saveFileDialog.Title = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the initial directory of the dialog
|
||||
/// </summary>
|
||||
public SaveFileDialogBuilder WithDirectory(string? directory)
|
||||
{
|
||||
_saveFileDialog.Directory = directory;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the initial file name of the dialog
|
||||
/// </summary>
|
||||
public SaveFileDialogBuilder WithInitialFileName(string? initialFileName)
|
||||
{
|
||||
_saveFileDialog.InitialFileName = initialFileName;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the default extension of the dialog
|
||||
/// </summary>
|
||||
public SaveFileDialogBuilder WithDefaultExtension(string? defaultExtension)
|
||||
{
|
||||
_saveFileDialog.DefaultExtension = defaultExtension;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a filter to the dialog
|
||||
/// </summary>
|
||||
public SaveFileDialogBuilder HavingFilter(Action<FileDialogFilterBuilder> configure)
|
||||
{
|
||||
FileDialogFilterBuilder builder = new();
|
||||
configure(builder);
|
||||
_saveFileDialog.Filters.Add(builder.Build());
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
public async Task<string?> ShowAsync()
|
||||
{
|
||||
return await _saveFileDialog.ShowAsync(_parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Artemis.UI.Shared.Services.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a service provided by the Artemis Shared UI library
|
||||
/// </summary>
|
||||
public interface IArtemisSharedUIService
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using Artemis.UI.Shared.Services.Builders;
|
||||
|
||||
namespace Artemis.UI.Shared.Services.Interfaces
|
||||
{
|
||||
public interface INotificationService : IArtemisSharedUIService
|
||||
{
|
||||
NotificationBuilder CreateNotification();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.UI.Shared.Services.Builders;
|
||||
using Avalonia.Controls;
|
||||
|
||||
namespace Artemis.UI.Shared.Services.Interfaces
|
||||
{
|
||||
public interface IWindowService : IArtemisSharedUIService
|
||||
{
|
||||
/// <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>
|
||||
TViewModel ShowWindow<TViewModel>(params (string name, object value)[] parameters);
|
||||
|
||||
/// <summary>
|
||||
/// Given a ViewModel, show its corresponding View as a window
|
||||
/// </summary>
|
||||
/// <param name="viewModel">ViewModel to show the View for</param>
|
||||
void ShowWindow(object viewModel);
|
||||
|
||||
/// <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>
|
||||
void ShowExceptionDialog(string title, Exception exception);
|
||||
|
||||
/// <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>
|
||||
Task<TResult> ShowDialogAsync<TResult>(DialogViewModelBase<TResult> viewModel);
|
||||
|
||||
/// <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>
|
||||
Task<TResult> ShowDialogAsync<TViewModel, TResult>(params (string name, object value)[] parameters) where TViewModel : DialogViewModelBase<TResult>;
|
||||
|
||||
/// <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>
|
||||
Task<bool> ShowConfirmContentDialog(string title, string message, string confirm = "Confirm", string? cancel = "Cancel");
|
||||
|
||||
/// <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>
|
||||
OpenFileDialogBuilder CreateOpenFileDialog();
|
||||
|
||||
/// <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>
|
||||
SaveFileDialogBuilder CreateSaveFileDialog();
|
||||
|
||||
ContentDialogBuilder CreateContentDialog();
|
||||
|
||||
Window GetCurrentWindow();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using Artemis.UI.Shared.Services.Builders;
|
||||
using Artemis.UI.Shared.Services.Interfaces;
|
||||
|
||||
namespace Artemis.UI.Shared.Services
|
||||
{
|
||||
public class NotificationService : INotificationService
|
||||
{
|
||||
private readonly IWindowService _windowService;
|
||||
|
||||
public NotificationService(IWindowService windowService)
|
||||
{
|
||||
_windowService = windowService;
|
||||
}
|
||||
|
||||
public NotificationBuilder CreateNotification()
|
||||
{
|
||||
return new NotificationBuilder(_windowService.GetCurrentWindow());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<Window 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="800"
|
||||
x:Class="Artemis.UI.Shared.Services.ExceptionDialogView"
|
||||
Title="{Binding Title}"
|
||||
ExtendClientAreaToDecorationsHint="True"
|
||||
Width="800"
|
||||
Height="800"
|
||||
WindowStartupLocation="CenterOwner">
|
||||
<Grid>
|
||||
<TextBlock Margin="10" IsHitTestVisible="False" Text="{Binding Title}" />
|
||||
<Grid Margin="0 32 0 0" ColumnDefinitions="*,Auto" RowDefinitions="*,Auto">
|
||||
|
||||
<Border Classes="card" Grid.Row="0" Grid.ColumnSpan="2" Margin="10">
|
||||
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Margin="0 5">
|
||||
<Grid RowDefinitions="Auto,Auto,*">
|
||||
<TextBlock Grid.Row="0" Classes="h3">Awww :(</TextBlock>
|
||||
<TextBlock Grid.Row="1">
|
||||
It looks like Artemis ran into an unhandled exception. If this keeps happening feel free to hit us up on Discord.
|
||||
</TextBlock>
|
||||
|
||||
<TextBox Grid.Row="2" Text="{Binding Exception, Mode=OneTime}"
|
||||
Margin="0 10"
|
||||
AcceptsReturn="True"
|
||||
IsReadOnly="True"
|
||||
FontFamily="Consolas"
|
||||
FontSize="12"
|
||||
BorderThickness="0" />
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" TextWrapping="Wrap" Margin="15" VerticalAlignment="Center">
|
||||
When reporting errors please don't take a screenshot of the error, instead copy the text, thanks!
|
||||
</TextBlock>
|
||||
<StackPanel Grid.Row="1" Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right" Margin="15">
|
||||
<Button Command="{Binding CopyException}" Classes="AppBarButton" Width="150" Margin="0 0 5 0">
|
||||
Copy exception
|
||||
</Button>
|
||||
<Button Command="{Binding Close}" Width="150" Margin="5 0 0 0">
|
||||
Close
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -0,0 +1,22 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Shared.Services
|
||||
{
|
||||
internal class ExceptionDialogView : ReactiveWindow<ExceptionDialogViewModel>
|
||||
{
|
||||
public ExceptionDialogView()
|
||||
{
|
||||
InitializeComponent();
|
||||
#if DEBUG
|
||||
this.AttachDevTools();
|
||||
#endif
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.UI.Shared.Services.Builders;
|
||||
using Artemis.UI.Shared.Services.Interfaces;
|
||||
using Avalonia;
|
||||
using Avalonia.Layout;
|
||||
|
||||
namespace Artemis.UI.Shared.Services
|
||||
{
|
||||
internal class ExceptionDialogViewModel : DialogViewModelBase<object>
|
||||
{
|
||||
private readonly INotificationService _notificationService;
|
||||
|
||||
public ExceptionDialogViewModel(string title, Exception exception, INotificationService notificationService)
|
||||
{
|
||||
_notificationService = notificationService;
|
||||
|
||||
Title = $"Artemis | {title}";
|
||||
Exception = exception;
|
||||
}
|
||||
|
||||
public string Title { get; }
|
||||
public Exception Exception { get; }
|
||||
|
||||
public async Task CopyException()
|
||||
{
|
||||
await Application.Current.Clipboard.SetTextAsync(Exception.ToString());
|
||||
_notificationService.CreateNotification()
|
||||
.WithMessage("Copied stack trace to clipboard.")
|
||||
.WithSeverity(NotificationSeverity.Success)
|
||||
.WithHorizontalPosition(HorizontalAlignment.Center)
|
||||
.Show();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.UI.Shared.Exceptions;
|
||||
using Artemis.UI.Shared.Services.Builders;
|
||||
using Artemis.UI.Shared.Services.Interfaces;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Threading;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Ninject;
|
||||
using Ninject.Parameters;
|
||||
|
||||
namespace Artemis.UI.Shared.Services
|
||||
{
|
||||
internal class WindowService : IWindowService
|
||||
{
|
||||
private readonly IKernel _kernel;
|
||||
private bool _exceptionDialogOpen;
|
||||
|
||||
public WindowService(IKernel kernel)
|
||||
{
|
||||
_kernel = kernel;
|
||||
}
|
||||
|
||||
public T ShowWindow<T>(params (string name, object value)[] parameters)
|
||||
{
|
||||
IParameter[] paramsArray = parameters.Select(kv => new ConstructorArgument(kv.name, kv.value)).Cast<IParameter>().ToArray();
|
||||
T viewModel = _kernel.Get<T>(paramsArray)!;
|
||||
ShowWindow(viewModel);
|
||||
return viewModel;
|
||||
}
|
||||
|
||||
public void ShowWindow(object viewModel)
|
||||
{
|
||||
Window parent = GetCurrentWindow();
|
||||
|
||||
string name = viewModel.GetType().FullName!.Split('`')[0].Replace("ViewModel", "View");
|
||||
Type? type = viewModel.GetType().Assembly.GetType(name);
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
throw new ArtemisSharedUIException($"Failed to find a window named {name}.");
|
||||
}
|
||||
|
||||
if (!type.IsAssignableTo(typeof(Window)))
|
||||
{
|
||||
throw new ArtemisSharedUIException($"Type {name} is not a window.");
|
||||
}
|
||||
|
||||
Window window = (Window) Activator.CreateInstance(type)!;
|
||||
window.DataContext = viewModel;
|
||||
window.Show(parent);
|
||||
}
|
||||
|
||||
public async Task<TResult> ShowDialogAsync<TViewModel, TResult>(params (string name, object value)[] parameters) where TViewModel : DialogViewModelBase<TResult>
|
||||
{
|
||||
IParameter[] paramsArray = parameters.Select(kv => new ConstructorArgument(kv.name, kv.value)).Cast<IParameter>().ToArray();
|
||||
TViewModel viewModel = _kernel.Get<TViewModel>(paramsArray)!;
|
||||
return await ShowDialogAsync(viewModel);
|
||||
}
|
||||
|
||||
public async Task<bool> ShowConfirmContentDialog(string title, string message, string confirm = "Confirm", string? cancel = "Cancel")
|
||||
{
|
||||
ContentDialogResult contentDialogResult = await CreateContentDialog()
|
||||
.WithTitle(title)
|
||||
.WithContent(message)
|
||||
.HavingPrimaryButton(b => b.WithText(confirm))
|
||||
.WithCloseButtonText(cancel)
|
||||
.ShowAsync();
|
||||
|
||||
return contentDialogResult == ContentDialogResult.Primary;
|
||||
}
|
||||
|
||||
public async Task<TResult> ShowDialogAsync<TResult>(DialogViewModelBase<TResult> viewModel)
|
||||
{
|
||||
Window parent = GetCurrentWindow();
|
||||
|
||||
string name = viewModel.GetType().FullName!.Split('`')[0].Replace("ViewModel", "View");
|
||||
Type? type = viewModel.GetType().Assembly.GetType(name);
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
throw new ArtemisSharedUIException($"Failed to find a window named {name}.");
|
||||
}
|
||||
|
||||
if (!type.IsAssignableTo(typeof(Window)))
|
||||
{
|
||||
throw new ArtemisSharedUIException($"Type {name} is not a window.");
|
||||
}
|
||||
|
||||
Window window = (Window) Activator.CreateInstance(type)!;
|
||||
window.DataContext = viewModel;
|
||||
viewModel.CloseRequested += (_, args) => window.Close(args.Result);
|
||||
viewModel.CancelRequested += (_, _) => window.Close();
|
||||
|
||||
return await window.ShowDialog<TResult>(parent);
|
||||
}
|
||||
|
||||
public void ShowExceptionDialog(string title, Exception exception)
|
||||
{
|
||||
if (_exceptionDialogOpen)
|
||||
return;
|
||||
|
||||
_exceptionDialogOpen = true;
|
||||
// Fire and forget the dialog
|
||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await ShowDialogAsync(new ExceptionDialogViewModel(title, exception, _kernel.Get<INotificationService>()));
|
||||
}
|
||||
finally
|
||||
{
|
||||
_exceptionDialogOpen = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public ContentDialogBuilder CreateContentDialog()
|
||||
{
|
||||
return new ContentDialogBuilder(_kernel, GetCurrentWindow());
|
||||
}
|
||||
|
||||
public OpenFileDialogBuilder CreateOpenFileDialog()
|
||||
{
|
||||
return new OpenFileDialogBuilder(GetCurrentWindow());
|
||||
}
|
||||
|
||||
public SaveFileDialogBuilder CreateSaveFileDialog()
|
||||
{
|
||||
return new SaveFileDialogBuilder(GetCurrentWindow());
|
||||
}
|
||||
|
||||
public Window GetCurrentWindow()
|
||||
{
|
||||
if (Application.Current.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime classic)
|
||||
{
|
||||
throw new ArtemisSharedUIException("Can't show a dialog when application lifetime is not IClassicDesktopStyleApplicationLifetime.");
|
||||
}
|
||||
|
||||
Window parent = classic.Windows.FirstOrDefault(w => w.IsActive) ?? classic.MainWindow;
|
||||
return parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<Styles xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia">
|
||||
<Design.PreviewWith>
|
||||
<Border Padding="20">
|
||||
<StackPanel >
|
||||
<controls:InfoBar Classes="notification-info-bar" Title="Test title" Message="Test message" IsOpen="True" Severity="Informational"/>
|
||||
<controls:InfoBar Classes="notification-info-bar" Title="Test title" Message="Test message" IsOpen="True" Severity="Success"/>
|
||||
<controls:InfoBar Classes="notification-info-bar" Title="Test title" Message="Test message" IsOpen="True" Severity="Warning"/>
|
||||
<controls:InfoBar Classes="notification-info-bar" Title="Test title" Message="Test message" IsOpen="True" Severity="Error"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Design.PreviewWith>
|
||||
|
||||
<!-- Add Styles Here -->
|
||||
<Style Selector="controls|InfoBar.notification-info-bar">
|
||||
<Setter Property="Opacity" Value="0" />
|
||||
<Setter Property="MaxWidth" Value="600" />
|
||||
<Setter Property="Margin" Value="15"/>
|
||||
<Setter Property="Transitions">
|
||||
<Transitions>
|
||||
<DoubleTransition Property="Opacity" Duration="0:0:0.2"/>
|
||||
</Transitions>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style Selector="controls|InfoBar.notification-info-bar[IsOpen=True]">
|
||||
<Setter Property="Opacity" Value="1" />
|
||||
</Style>
|
||||
<Style Selector="controls|InfoBar.notification-info-bar:informational /template/ Border#ContentRoot">
|
||||
<Setter Property="Background" Value="#ff3c3c3c" />
|
||||
</Style>
|
||||
</Styles>
|
||||
@@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using System.Reactive.Disposables;
|
||||
using Artemis.UI.Shared.Events;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Shared
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the base class for Artemis view models
|
||||
/// </summary>
|
||||
public abstract class ViewModelBase : ReactiveObject
|
||||
{
|
||||
private string? _displayName;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the display name of the view model
|
||||
/// </summary>
|
||||
public string? DisplayName
|
||||
{
|
||||
get => _displayName;
|
||||
set => this.RaiseAndSetIfChanged(ref _displayName, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the base class for Artemis view models that are interested in the activated event
|
||||
/// </summary>
|
||||
public abstract class ActivatableViewModelBase : ViewModelBase, IActivatableViewModel, IDisposable
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected ActivatableViewModelBase()
|
||||
{
|
||||
this.WhenActivated(disposables => Disposable.Create(Dispose).DisposeWith(disposables));
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ViewModelActivator Activator { get; } = new();
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the base class for Artemis view models used to drive dialogs
|
||||
/// </summary>
|
||||
public abstract class DialogViewModelBase<TResult> : ActivatableViewModelBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Closes the dialog with the given <paramref name="result" />
|
||||
/// </summary>
|
||||
/// <param name="result">The result of the dialog</param>
|
||||
public void Close(TResult result)
|
||||
{
|
||||
CloseRequested?.Invoke(this, new DialogClosedEventArgs<TResult>(result));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the dialog without a result
|
||||
/// </summary>
|
||||
public void Cancel()
|
||||
{
|
||||
CancelRequested?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
internal event EventHandler<DialogClosedEventArgs<TResult>>? CloseRequested;
|
||||
internal event EventHandler? CancelRequested;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!--
|
||||
To use the Avalonia CI feed to get unstable packages, move this file to the root of your solution.
|
||||
-->
|
||||
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<add key="AvaloniaCI" value="https://www.myget.org/F/avalonia-ci/api/v2" />
|
||||
</packageSources>
|
||||
</configuration>
|
||||
@@ -0,0 +1,454 @@
|
||||
## 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
|
||||
@@ -0,0 +1,31 @@
|
||||
<Application xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:Artemis.UI.Windows"
|
||||
xmlns:ui="clr-namespace:Artemis.UI;assembly=Artemis.UI"
|
||||
xmlns:styling="clr-namespace:FluentAvalonia.Styling;assembly=FluentAvalonia"
|
||||
x:Class="Artemis.UI.Windows.App">
|
||||
<Application.DataTemplates>
|
||||
<ui:ViewLocator/>
|
||||
</Application.DataTemplates>
|
||||
|
||||
<Application.Styles>
|
||||
<!-- Third party styles -->
|
||||
<styling:FluentAvaloniaTheme RequestedTheme="Dark" CustomAccentColor="#4db6ac" />
|
||||
<StyleInclude Source="avares://Material.Icons.Avalonia/App.xaml" />
|
||||
|
||||
<!-- Grab the window styling from Avalonia -->
|
||||
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/Window.xaml" />
|
||||
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/TitleBar.xaml" />
|
||||
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml" />
|
||||
|
||||
<!-- Global styles -->
|
||||
<!-- <StyleInclude Source="/Styles/Border.axaml" /> -->
|
||||
<!-- <StyleInclude Source="/Styles/Button.axaml" /> -->
|
||||
<!-- <StyleInclude Source="/Styles/TextBlock.axaml" /> -->
|
||||
<!-- <StyleInclude Source="/Styles/Sidebar.axaml" /> -->
|
||||
|
||||
<!-- Shared styles -->
|
||||
<!-- TODO: Make a single file-->
|
||||
<StyleInclude Source="avares://Artemis.UI.Shared/Styles/InfoBar.axaml" />
|
||||
</Application.Styles>
|
||||
</Application>
|
||||
@@ -0,0 +1,54 @@
|
||||
using Artemis.Core.Ninject;
|
||||
using Artemis.UI.Ninject;
|
||||
using Artemis.UI.Screens.Root.ViewModels;
|
||||
using Artemis.UI.Shared.Ninject;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Threading;
|
||||
using FluentAvalonia.Styling;
|
||||
using Ninject;
|
||||
using ReactiveUI;
|
||||
using Splat.Ninject;
|
||||
|
||||
namespace Artemis.UI.Windows
|
||||
{
|
||||
public class App : Application
|
||||
{
|
||||
private StandardKernel _kernel = null!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
InitializeNinject();
|
||||
RxApp.MainThreadScheduler = AvaloniaScheduler.Instance;
|
||||
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public override void OnFrameworkInitializationCompleted()
|
||||
{
|
||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
desktop.MainWindow = new MainWindow
|
||||
{
|
||||
DataContext = _kernel.Get<RootViewModel>()
|
||||
};
|
||||
AvaloniaLocator.Current.GetService<FluentAvaloniaTheme>().ForceNativeTitleBarToTheme(desktop.MainWindow, "Dark");
|
||||
}
|
||||
|
||||
base.OnFrameworkInitializationCompleted();
|
||||
}
|
||||
|
||||
private void InitializeNinject()
|
||||
{
|
||||
_kernel = new StandardKernel();
|
||||
_kernel.Settings.InjectNonPublic = true;
|
||||
|
||||
_kernel.Load<CoreModule>();
|
||||
_kernel.Load<UIModule>();
|
||||
_kernel.Load<SharedUIModule>();
|
||||
|
||||
_kernel.UseNinjectDependencyResolver();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net5.0-windows</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<AvaloniaResource Include="Assets\**" />
|
||||
<None Remove=".gitignore" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="0.10.10" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="0.10.10" />
|
||||
<!--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.10" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.10" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Artemis.Core\Artemis.Core.csproj" />
|
||||
<ProjectReference Include="..\Artemis.UI\Artemis.UI.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
After Width: | Height: | Size: 172 KiB |
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using Avalonia;
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Windows
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<Application xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:sty="using:FluentAvalonia.Styling"
|
||||
xmlns:ui="clr-namespace:Artemis.UI"
|
||||
x:Class="Artemis.UI.App">
|
||||
<Application.DataTemplates>
|
||||
<ui:ViewLocator />
|
||||
</Application.DataTemplates>
|
||||
|
||||
<Application.Styles>
|
||||
<!-- Third party styles -->
|
||||
<sty:FluentAvaloniaTheme RequestedTheme="Dark" CustomAccentColor="#4db6ac" />
|
||||
<StyleInclude Source="avares://Material.Icons.Avalonia/App.xaml" />
|
||||
|
||||
<!-- Grab the window styling from Avalonia -->
|
||||
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/Window.xaml" />
|
||||
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/TitleBar.xaml" />
|
||||
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml" />
|
||||
|
||||
<!-- Global styles -->
|
||||
<StyleInclude Source="/Styles/Border.axaml" />
|
||||
<StyleInclude Source="/Styles/Button.axaml" />
|
||||
<StyleInclude Source="/Styles/TextBlock.axaml" />
|
||||
<StyleInclude Source="/Styles/Sidebar.axaml" />
|
||||
|
||||
<!-- Shared styles -->
|
||||
<!-- TODO: Make a single file-->
|
||||
<StyleInclude Source="avares://Artemis.UI.Avalonia.Shared/Styles/InfoBar.axaml" />
|
||||
</Application.Styles>
|
||||
</Application>
|
||||
@@ -0,0 +1,54 @@
|
||||
using Artemis.Core.Ninject;
|
||||
using Artemis.UI.Ninject;
|
||||
using Artemis.UI.Screens.Root.ViewModels;
|
||||
using Artemis.UI.Shared.Ninject;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Threading;
|
||||
using FluentAvalonia.Styling;
|
||||
using Ninject;
|
||||
using ReactiveUI;
|
||||
using Splat.Ninject;
|
||||
|
||||
namespace Artemis.UI
|
||||
{
|
||||
public class App : Application
|
||||
{
|
||||
private StandardKernel _kernel = null!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
InitializeNinject();
|
||||
RxApp.MainThreadScheduler = AvaloniaScheduler.Instance;
|
||||
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public override void OnFrameworkInitializationCompleted()
|
||||
{
|
||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
desktop.MainWindow = new MainWindow
|
||||
{
|
||||
DataContext = _kernel.Get<RootViewModel>()
|
||||
};
|
||||
AvaloniaLocator.Current.GetService<FluentAvaloniaTheme>().ForceNativeTitleBarToTheme(desktop.MainWindow, "Dark");
|
||||
}
|
||||
|
||||
base.OnFrameworkInitializationCompleted();
|
||||
}
|
||||
|
||||
private void InitializeNinject()
|
||||
{
|
||||
_kernel = new StandardKernel();
|
||||
_kernel.Settings.InjectNonPublic = true;
|
||||
|
||||
_kernel.Load<CoreModule>();
|
||||
_kernel.Load<UIModule>();
|
||||
_kernel.Load<SharedUIModule>();
|
||||
|
||||
_kernel.UseNinjectDependencyResolver();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
<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/=events/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
@@ -0,0 +1,45 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<OutputPath>bin\</OutputPath>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<AvaloniaResource Include="Assets\**" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="0.10.10" />
|
||||
<PackageReference Include="Avalonia.Controls.PanAndZoom" Version="4.2.0" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="0.10.10" />
|
||||
<PackageReference Include="Avalonia.Diagnostics" Version="0.10.10" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.10" />
|
||||
<PackageReference Include="Avalonia.Svg.Skia" Version="0.10.8.3" />
|
||||
<PackageReference Include="FluentAvaloniaUI" Version="1.1.5" />
|
||||
<PackageReference Include="Flurl.Http" Version="3.2.0" />
|
||||
<PackageReference Include="Live.Avalonia" Version="1.3.1" />
|
||||
<PackageReference Include="Material.Icons.Avalonia" Version="1.0.2" />
|
||||
<PackageReference Include="Splat.Ninject" Version="13.1.30" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Artemis.Core\Artemis.Core.csproj" />
|
||||
<ProjectReference Include="..\Artemis.UI.Shared\Artemis.UI.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="RGB.NET.Core">
|
||||
<HintPath>..\..\..\..\RGB.NET\bin\net5.0\RGB.NET.Core.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Assets\Images\Logo\bow-black.ico" />
|
||||
<Content Include="Assets\Images\Logo\bow-white.ico" />
|
||||
<Content Include="Assets\Images\Logo\bow.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="Assets\Images\Logo\bow-black.ico" />
|
||||
<Resource Include="Assets\Images\Logo\bow-white.ico" />
|
||||
<Resource Include="Assets\Images\Logo\bow-white.svg" />
|
||||
<Resource Include="Assets\Images\Logo\bow.ico" />
|
||||
<Resource Include="Assets\Images\Logo\bow.svg" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,2 @@
|
||||
<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/=events/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 20 KiB |
@@ -0,0 +1,12 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<defs>
|
||||
<style>.cls-1,.cls-2{fill:#fff;}.cls-2{fill-rule:evenodd;}</style>
|
||||
</defs>
|
||||
<g id="Layer_10" data-name="Layer 10">
|
||||
<path class="cls-1" d="M52.32,17.68l-2.83,5a1.16,1.16,0,0,1-2,0l-.57-1-.54.32c-.22-.44-.46-.88-.71-1.31s-.66-1.1-1-1.63l.53-.3-.63-1.06a1.16,1.16,0,0,1,1-1.74l5.75,0A1.15,1.15,0,0,1,52.32,17.68Z"/>
|
||||
<path class="cls-1" d="M24.71,25.63l-4.84,2.58-.53.28a38.15,38.15,0,0,1-5.54-2.18c-4.51-1.49-1.48-7,1.93-4.31.34.22.69.44,1,.64.59.35,1.21.67,1.82,1A25.88,25.88,0,0,0,24.71,25.63Z"/>
|
||||
<path class="cls-1" d="M30.83,36.22c-.15.43-.28.86-.41,1.29a25.74,25.74,0,0,0-.81,4.09,26.72,26.72,0,0,0-.17,3.1c0,.37,0,.75,0,1.12A2.45,2.45,0,0,1,25,47.72c-.56-1-.22-2-.18-3.08s.21-2,.39-3c.12-.74.27-1.47.43-2.2l.53-.33,3.63-2.26Z"/>
|
||||
<path class="cls-1" d="M35,28.71l-.91.57L31.3,31,24,35.59l-4.45,2.78-2.22,1.37a2.6,2.6,0,0,1-1.26.34,2.45,2.45,0,0,1-.8-4.72l.58-.31,1.3-.69,4.67-2.5,7.6-4.05,2.94-1.57.94-.5a17.91,17.91,0,0,1,1,1.55C34.57,27.75,34.82,28.23,35,28.71Z"/>
|
||||
<path class="cls-2" d="M39.29,53.89a2.56,2.56,0,0,1-1.09.74l-.34.08a.13.13,0,0,1-.09,0,1.84,1.84,0,0,1-.33,0,2.41,2.41,0,0,1-1.84-4,22.32,22.32,0,0,0,5-18.09c0-.24-.08-.48-.13-.72s-.1-.48-.16-.73-.11-.48-.18-.72-.12-.45-.2-.68a20.49,20.49,0,0,0-.7-1.94c-.06-.18-.14-.34-.21-.51a21.83,21.83,0,0,0-1.09-2.16c-.14-.22-.27-.45-.4-.66-.25-.4-.51-.78-.77-1.16s-.63-.85-1-1.26l-.48-.56c-.35-.4-.72-.78-1.09-1.14a6.51,6.51,0,0,0-.54-.51l-.45-.4a22.08,22.08,0,0,0-3-2.2c-.17-.11-.36-.21-.54-.31s-.42-.24-.63-.35l-.46-.23a19.7,19.7,0,0,0-2.31-1l-.44-.15-.1,0c-.53-.18-1.07-.34-1.63-.48l-.25-.06a19.61,19.61,0,0,0-2-.39c-.35-.06-.7-.1-1-.13s-.8-.07-1.2-.08-.65,0-1,0h0a22.18,22.18,0,0,0-4,.36,3.28,3.28,0,0,1-.43,0,2.42,2.42,0,0,1-.42-4.8A26,26,0,0,1,18,9.26h.62c.43,0,.86,0,1.28,0l1,.07,1.07.11c.52.07,1,.14,1.53.24.23,0,.46.08.7.13l.14,0c.35.08.69.15,1,.25a20.61,20.61,0,0,1,2.16.65c.48.16.94.33,1.4.52h0c.33.14.67.28,1,.44s.58.27.86.42l.27.13c.28.14.56.29.82.45s.64.36,1,.55c.49.31,1,.62,1.45,1l.15.11c.31.22.62.46.93.7l.11.08c.36.28.71.58,1.06.89l0,0c.33.28.64.57,1,.88s.64.62.94.95c.1.1.19.21.29.32.26.29.52.58.77.88,0,0,0,.05.06.09.28.34.55.68.81,1s.55.75.82,1.15l.28.44c.21.33.42.67.62,1A27.14,27.14,0,0,1,39.29,53.89Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 46 KiB |
@@ -0,0 +1,43 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 512 512">
|
||||
<defs>
|
||||
<style>.cls-1{fill:url(#linear-gradient);}.cls-2{fill:url(#linear-gradient-2);}.cls-3{fill:url(#linear-gradient-3);}.cls-4{fill:url(#linear-gradient-4);}.cls-5{fill:url(#linear-gradient-5);}.cls-6{fill:url(#linear-gradient-6);}.cls-7{fill:url(#linear-gradient-7);}.cls-8{fill-rule:evenodd;fill:url(#linear-gradient-8);}</style>
|
||||
<linearGradient id="linear-gradient" x1="151.25" y1="94.74" x2="810.05" y2="369.54" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#f19d25"/>
|
||||
<stop offset="0.12" stop-color="#f63d3d"/>
|
||||
<stop offset="0.28" stop-color="#c93cec"/>
|
||||
<stop offset="0.44" stop-color="#2667f4"/>
|
||||
<stop offset="0.56" stop-color="#1cb6e7"/>
|
||||
<stop offset="0.7" stop-color="#2df4b5"/>
|
||||
<stop offset="0.87" stop-color="#70ea37"/>
|
||||
<stop offset="1" stop-color="#cfe726"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="linear-gradient-2" x1="132.17" y1="204.4" x2="234.7" y2="230.88" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#ef3d3d"/>
|
||||
<stop offset="1" stop-color="#b72222"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="linear-gradient-3" x1="217.07" y1="354.42" x2="253.03" y2="258.52" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#2b75f6"/>
|
||||
<stop offset="1" stop-color="#1452aa"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="linear-gradient-4" x1="105.18" y1="320.77" x2="311.18" y2="203.81" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#22f15e"/>
|
||||
<stop offset="1" stop-color="#29af4d"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="linear-gradient-5" x1="112.68" y1="187.21" x2="771.48" y2="462.01" xlink:href="#linear-gradient"/>
|
||||
<linearGradient id="linear-gradient-6" x1="88.75" y1="244.59" x2="747.54" y2="519.39" xlink:href="#linear-gradient"/>
|
||||
<linearGradient id="linear-gradient-7" x1="102.97" y1="210.49" x2="761.77" y2="485.29" xlink:href="#linear-gradient"/>
|
||||
<linearGradient id="linear-gradient-8" x1="141.26" y1="116.54" x2="347.22" y2="356.89" gradientTransform="translate(11.74 6.94)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#009385"/>
|
||||
<stop offset="1" stop-color="#045e53"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g id="Teal_Bow" data-name="Teal Bow">
|
||||
<g id="TealRainbow">
|
||||
<path class="cls-1" d="M375.54,156.66,340,157a7.14,7.14,0,0,0-6.1,10.76l3.85,6.55-16.8,9.84c2.18,3.26,4.26,6.61,6.24,10,1.54,2.67,3,5.37,4.39,8.09l16.82-9.85,3.53,6a7.14,7.14,0,0,0,12.37-.1l17.53-30.94A7.15,7.15,0,0,0,375.54,156.66Z"/>
|
||||
<path class="cls-5" d="M235.3,221.27l-34.52,18.41c-6.08-1-12.11-2.36-18.05-4a230.91,230.91,0,0,1-39.4-14.9c-27.91-9.26-9.21-43.22,11.93-26.76,2.11,1.41,4.28,2.76,6.48,4,3.65,2.15,7.42,4.09,11.24,5.91a156.68,156.68,0,0,0,42.24,13.31A130.42,130.42,0,0,0,239.63,219Z"/>
|
||||
<path class="cls-6" d="M257,258.59l4.09-2.55a135.41,135.41,0,0,0-14.8,34.07,160.44,160.44,0,0,0-5,25.32,158,158,0,0,0-1.06,19.21c0,2.32.11,4.63.26,6.94,4.06,16.9-18.44,27-27.64,11.76-3.51-6-1.35-12.66-1.14-19.08.58-6.21,1.35-12.39,2.39-18.55a200.68,200.68,0,0,1,9.63-36.38l10.53-6.56Z"/>
|
||||
<path class="cls-7" d="M284,219.06c-1.92-3.33-4-6.55-6.21-9.68h0l-9.46,5.54a17.12,17.12,0,0,0-2.69,1.52l-7.34,3.92L240,230.1q-23.52,12.53-47,25.08L164,270.62l-8,4.27c-1.21.65-2.43,1.29-3.64,1.95C132.81,285.25,146.31,314,165.2,304l13.72-8.55,27.53-17.15,45.22-28.18,17.55-10.94,7-4.39a15.73,15.73,0,0,0,3.58-2.29h0l8.56-5Q286.34,223.21,284,219.06Z"/>
|
||||
<path class="cls-8" d="M169.2,115.38A169,169,0,0,0,143.28,118a15,15,0,0,0,5.26,29.44A137.81,137.81,0,0,1,278.31,372.15a14.95,14.95,0,0,0,22.82,19.31A167.69,167.69,0,0,0,169.2,115.38Z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 566 KiB |
|
After Width: | Height: | Size: 172 KiB |
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Avalonia.Data.Converters;
|
||||
using Avalonia.Media;
|
||||
using RGBColor = RGB.NET.Core.Color;
|
||||
|
||||
namespace Artemis.UI.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts <see cref="T:RGB.NET.Core.Color" /> into <see cref="T:Avalonia.Media.Color" />.
|
||||
/// </summary>
|
||||
public class ColorToSolidColorBrushConverter : IValueConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return new SolidColorBrush(!(value is RGBColor color)
|
||||
? new Color(0, 0, 0, 0)
|
||||
: new Color((byte) color.A, (byte) color.R, (byte) color.G, (byte) color.B));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return !(value is SolidColorBrush brush)
|
||||
? RGBColor.Transparent
|
||||
: new RGBColor(brush.Color.A, brush.Color.R, brush.Color.G, brush.Color.B);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Avalonia.Data.Converters;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace Artemis.UI.Converters
|
||||
{
|
||||
public class EnumToCollectionConverter : MarkupExtension, IValueConverter
|
||||
{
|
||||
public override object ProvideValue(IServiceProvider serviceProvider)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
private static string Description(Enum value)
|
||||
{
|
||||
object[] attributes = value.GetType().GetField(value.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);
|
||||
if (attributes.Any())
|
||||
return (attributes.First() as DescriptionAttribute)?.Description;
|
||||
|
||||
// If no description is found, the least we can do is replace underscores with spaces
|
||||
TextInfo ti = CultureInfo.CurrentCulture.TextInfo;
|
||||
return ti.ToTitleCase(ti.ToLower(value.ToString().Replace("_", " ")));
|
||||
}
|
||||
|
||||
private static IEnumerable<Tuple<object, object>> GetAllValuesAndDescriptions(Type t)
|
||||
{
|
||||
if (!t.IsEnum)
|
||||
throw new ArgumentException($"{nameof(t)} must be an enum type");
|
||||
|
||||
return Enum.GetValues(t).Cast<Enum>().Select(e => new Tuple<object, object>(e, Description(e))).ToList();
|
||||
}
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return GetAllValuesAndDescriptions(value.GetType());
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Avalonia.Data.Converters;
|
||||
using RGB.NET.Core;
|
||||
|
||||
namespace Artemis.UI.Converters
|
||||
{
|
||||
public class LedIdToStringConverter : IValueConverter
|
||||
{
|
||||
#region Implementation of 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 (Enum.TryParse(typeof(LedId), value?.ToString(), true, out object parsedLedId))
|
||||
return parsedLedId;
|
||||
return LedId.Unknown1;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Avalonia.Data.Converters;
|
||||
|
||||
namespace Artemis.UI.Converters
|
||||
{
|
||||
public class NormalizedPercentageConverter : IValueConverter
|
||||
{
|
||||
#region IValueConverter Members
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is double number)
|
||||
return number * 100.0;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is double number)
|
||||
return number / 100.0;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using Avalonia.Data.Converters;
|
||||
|
||||
namespace Artemis.UI.Converters
|
||||
{
|
||||
public class UriToFileNameConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is Uri uri && uri.IsFile)
|
||||
return Path.GetFileName(uri.LocalPath);
|
||||
return null;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Avalonia.Data.Converters;
|
||||
|
||||
namespace Artemis.UI.Converters
|
||||
{
|
||||
public class ValuesAdditionConverter : IMultiValueConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public object Convert(IList<object> values, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return values.Where(v => v is double).Cast<double>().Sum();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.UI.Exceptions
|
||||
{
|
||||
public class ArtemisGraphicsContextException : Exception
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public ArtemisGraphicsContextException()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ArtemisGraphicsContextException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ArtemisGraphicsContextException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.UI.Exceptions
|
||||
{
|
||||
public class ArtemisUIException : Exception
|
||||
{
|
||||
public ArtemisUIException()
|
||||
{
|
||||
}
|
||||
|
||||
public ArtemisUIException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public ArtemisUIException(string message, Exception inner) : base(message, inner)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
|
||||
namespace Artemis.UI.Extensions
|
||||
{
|
||||
public static class ObservableCollectionExtensions
|
||||
{
|
||||
public static void Sort<T>(this ObservableCollection<T> collection, Func<T, object> order)
|
||||
{
|
||||
List<T> ordered = collection.OrderBy(order).ToList();
|
||||
for (int index = 0; index < ordered.Count; index++)
|
||||
{
|
||||
T dataBindingConditionViewModel = ordered[index];
|
||||
if (collection.IndexOf(dataBindingConditionViewModel) != index)
|
||||
collection.Move(collection.IndexOf(dataBindingConditionViewModel), index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<Window 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.MainWindow"
|
||||
Icon="/Assets/Images/Logo/bow.ico"
|
||||
Title="Artemis.UI.Avalonia"
|
||||
ExtendClientAreaToDecorationsHint="True"
|
||||
TransparencyLevelHint="AcrylicBlur"
|
||||
Background="Transparent">
|
||||
|
||||
<Panel>
|
||||
<ExperimentalAcrylicBorder IsHitTestVisible="False">
|
||||
<ExperimentalAcrylicBorder.Material>
|
||||
<ExperimentalAcrylicMaterial
|
||||
BackgroundSource="Digger"
|
||||
TintColor="Black"
|
||||
TintOpacity="1"
|
||||
MaterialOpacity="0.85" />
|
||||
</ExperimentalAcrylicBorder.Material>
|
||||
</ExperimentalAcrylicBorder>
|
||||
|
||||
<ContentControl Content="{Binding}" />
|
||||
</Panel>
|
||||
</Window>
|
||||
@@ -0,0 +1,23 @@
|
||||
using Artemis.UI.Screens.Root.ViewModels;
|
||||
using Avalonia;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI
|
||||
{
|
||||
public class MainWindow : ReactiveWindow<RootViewModel>
|
||||
{
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
#if DEBUG
|
||||
this.AttachDevTools();
|
||||
#endif
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Screens.Device.Tabs.ViewModels;
|
||||
using Artemis.UI.Screens.Device.ViewModels;
|
||||
using Artemis.UI.Screens.Plugins.ViewModels;
|
||||
using Artemis.UI.Screens.Root.ViewModels;
|
||||
using Artemis.UI.Screens.Settings.Tabs.ViewModels;
|
||||
using Artemis.UI.Screens.SurfaceEditor.ViewModels;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Ninject.Factories
|
||||
{
|
||||
public interface IVmFactory
|
||||
{
|
||||
}
|
||||
|
||||
public interface IDeviceVmFactory : IVmFactory
|
||||
{
|
||||
DevicePropertiesViewModel DevicePropertiesViewModel(ArtemisDevice device);
|
||||
DeviceSettingsViewModel DeviceSettingsViewModel(ArtemisDevice device, DevicesTabViewModel devicesTabViewModel);
|
||||
DeviceDetectInputViewModel DeviceDetectInputViewModel(ArtemisDevice device);
|
||||
DevicePropertiesTabViewModel DevicePropertiesTabViewModel(ArtemisDevice device);
|
||||
DeviceInfoTabViewModel DeviceInfoTabViewModel(ArtemisDevice device);
|
||||
DeviceLedsTabViewModel DeviceLedsTabViewModel(ArtemisDevice device, ObservableCollection<ArtemisLed> selectedLeds);
|
||||
InputMappingsTabViewModel InputMappingsTabViewModel(ArtemisDevice device, ObservableCollection<ArtemisLed> selectedLeds);
|
||||
}
|
||||
|
||||
public interface ISettingsVmFactory : IVmFactory
|
||||
{
|
||||
PluginSettingsViewModel CreatePluginSettingsViewModel(Plugin plugin);
|
||||
PluginFeatureViewModel CreatePluginFeatureViewModel(PluginFeatureInfo pluginFeatureInfo, bool showShield);
|
||||
// DeviceSettingsViewModel CreateDeviceSettingsViewModel(ArtemisDevice device);
|
||||
}
|
||||
|
||||
public interface ISidebarVmFactory : IVmFactory
|
||||
{
|
||||
SidebarViewModel SidebarViewModel(IScreen hostScreen);
|
||||
SidebarCategoryViewModel SidebarCategoryViewModel(ProfileCategory profileCategory);
|
||||
SidebarProfileConfigurationViewModel SidebarProfileConfigurationViewModel(ProfileConfiguration profileConfiguration);
|
||||
}
|
||||
|
||||
public interface SurfaceVmFactory : IVmFactory
|
||||
{
|
||||
SurfaceDeviceViewModel SurfaceDeviceViewModel(ArtemisDevice device);
|
||||
ListDeviceViewModel ListDeviceViewModel(ArtemisDevice device);
|
||||
}
|
||||
|
||||
public interface IPrerequisitesVmFactory : IVmFactory
|
||||
{
|
||||
PluginPrerequisiteViewModel PluginPrerequisiteViewModel(PluginPrerequisite pluginPrerequisite, bool uninstall);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using Artemis.UI.Ninject.Factories;
|
||||
using Artemis.UI.Screens;
|
||||
using Artemis.UI.Services.Interfaces;
|
||||
using Artemis.UI.Shared;
|
||||
using Ninject.Extensions.Conventions;
|
||||
using Ninject.Modules;
|
||||
using Ninject.Planning.Bindings.Resolvers;
|
||||
|
||||
namespace Artemis.UI.Ninject
|
||||
{
|
||||
public class UIModule : NinjectModule
|
||||
{
|
||||
public override void Load()
|
||||
{
|
||||
if (Kernel == null)
|
||||
throw new ArgumentNullException("Kernel shouldn't be null here.");
|
||||
|
||||
Kernel.Components.Add<IMissingBindingResolver, SelfBindingResolver>();
|
||||
|
||||
Kernel.Bind(x =>
|
||||
{
|
||||
x.FromThisAssembly()
|
||||
.SelectAllClasses()
|
||||
.InheritedFrom<ViewModelBase>()
|
||||
.BindToSelf();
|
||||
});
|
||||
|
||||
Kernel.Bind(x =>
|
||||
{
|
||||
x.FromThisAssembly()
|
||||
.SelectAllClasses()
|
||||
.InheritedFrom<MainScreenViewModel>()
|
||||
.BindAllBaseClasses();
|
||||
});
|
||||
|
||||
// Bind UI factories
|
||||
Kernel.Bind(x =>
|
||||
{
|
||||
x.FromThisAssembly()
|
||||
.SelectAllInterfaces()
|
||||
.InheritedFrom<IVmFactory>()
|
||||
.BindToFactory();
|
||||
});
|
||||
|
||||
// Bind all UI services as singletons
|
||||
Kernel.Bind(x =>
|
||||
{
|
||||
x.FromThisAssembly()
|
||||
.SelectAllClasses()
|
||||
.InheritedFrom<IArtemisUIService>()
|
||||
.BindAllInterfaces()
|
||||
.Configure(c => c.InSingletonScope());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using Artemis.Core.Services;
|
||||
|
||||
namespace Artemis.UI.Providers
|
||||
{
|
||||
public class AvaloniaInputProvider : InputProvider
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<Window xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
mc:Ignorable="d" d:DesignWidth="1200" d:DesignHeight="800"
|
||||
x:Class="Artemis.UI.Screens.Debugger.DebugView"
|
||||
Title="Artemis | Debugger"
|
||||
Width="1200"
|
||||
Height="800"
|
||||
ExtendClientAreaToDecorationsHint="True">
|
||||
|
||||
<controls:NavigationView x:Name="Navigation">
|
||||
|
||||
</controls:NavigationView>
|
||||
|
||||
|
||||
</Window>
|
||||
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Reactive.Disposables;
|
||||
using Artemis.UI.Shared.Events;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
using FluentAvalonia.Core;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Debugger
|
||||
{
|
||||
public class DebugView : ReactiveWindow<DebugViewModel>
|
||||
{
|
||||
private readonly NavigationView _navigation;
|
||||
|
||||
public DebugView()
|
||||
{
|
||||
Activated += OnActivated;
|
||||
Deactivated += OnDeactivated;
|
||||
InitializeComponent();
|
||||
|
||||
_navigation = this.Get<NavigationView>("Navigation");
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
ViewModel!.WhenAnyValue(vm => vm!.IsActive).Subscribe(_ => Activate()).DisposeWith(d);
|
||||
ViewModel!.SelectedItem = (NavigationViewItem) _navigation.MenuItems.ElementAt(0);
|
||||
});
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
private void OnDeactivated(object? sender, EventArgs e)
|
||||
{
|
||||
if (ViewModel != null)
|
||||
ViewModel.IsActive = false;
|
||||
}
|
||||
|
||||
private void OnActivated(object? sender, EventArgs e)
|
||||
{
|
||||
if (ViewModel != null)
|
||||
ViewModel.IsActive = true;
|
||||
}
|
||||
|
||||
private void DeviceVisualizer_OnLedClicked(object? sender, LedClickedEventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
using System;
|
||||
using System.Reactive.Disposables;
|
||||
using Artemis.UI.Screens.Debugger.Tabs.DataModel;
|
||||
using Artemis.UI.Screens.Debugger.Tabs.Logs;
|
||||
using Artemis.UI.Screens.Debugger.Tabs.Performance;
|
||||
using Artemis.UI.Screens.Debugger.Tabs.Render;
|
||||
using Artemis.UI.Services.Interfaces;
|
||||
using Artemis.UI.Shared;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Ninject;
|
||||
using Ninject.Parameters;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Debugger
|
||||
{
|
||||
public class DebugViewModel : ActivatableViewModelBase, IScreen
|
||||
{
|
||||
private readonly IKernel _kernel;
|
||||
private readonly IDebugService _debugService;
|
||||
private bool _isActive;
|
||||
private NavigationViewItem? _selectedItem;
|
||||
|
||||
public DebugViewModel(IKernel kernel, IDebugService debugService)
|
||||
{
|
||||
_kernel = kernel;
|
||||
_debugService = debugService;
|
||||
|
||||
this.WhenAnyValue(x => x.SelectedItem).WhereNotNull().Subscribe(NavigateToSelectedItem);
|
||||
this.WhenActivated(disposables =>
|
||||
{
|
||||
Disposable
|
||||
.Create(HandleDeactivation)
|
||||
.DisposeWith(disposables);
|
||||
});
|
||||
}
|
||||
|
||||
public bool IsActive
|
||||
{
|
||||
get => _isActive;
|
||||
set => this.RaiseAndSetIfChanged(ref _isActive, value);
|
||||
}
|
||||
|
||||
public NavigationViewItem? SelectedItem
|
||||
{
|
||||
get => _selectedItem;
|
||||
set => this.RaiseAndSetIfChanged(ref _selectedItem, value);
|
||||
}
|
||||
|
||||
private void NavigateToSelectedItem(NavigationViewItem item)
|
||||
{
|
||||
// Kind of a lame way to do this but it's so static idc
|
||||
ConstructorArgument hostScreen = new("hostScreen", this);
|
||||
switch ((string) item.Content)
|
||||
{
|
||||
case "Rendering":
|
||||
Router.Navigate.Execute(_kernel.Get<RenderDebugViewModel>(hostScreen));
|
||||
break;
|
||||
case "Logs":
|
||||
Router.Navigate.Execute(_kernel.Get<LogsDebugViewModel>(hostScreen));
|
||||
break;
|
||||
case "Data Model":
|
||||
Router.Navigate.Execute(_kernel.Get<DataModelDebugViewModel>(hostScreen));
|
||||
break;
|
||||
case "Performance":
|
||||
Router.Navigate.Execute(_kernel.Get<PerformanceDebugViewModel>(hostScreen));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleDeactivation()
|
||||
{
|
||||
_debugService.ClearDebugger();
|
||||
}
|
||||
|
||||
public RoutingState Router { get; } = new();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<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.Screens.Debugger.Tabs.DataModel.DataModelDebugView">
|
||||
<TextBlock>Data Model</TextBlock>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,18 @@
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Debugger.Tabs.DataModel
|
||||
{
|
||||
public class DataModelDebugView : ReactiveUserControl<DataModelDebugViewModel>
|
||||
{
|
||||
public DataModelDebugView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using Artemis.UI.Shared;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Debugger.Tabs.DataModel
|
||||
{
|
||||
public class DataModelDebugViewModel : ActivatableViewModelBase, IRoutableViewModel
|
||||
{
|
||||
public DataModelDebugViewModel(IScreen hostScreen)
|
||||
{
|
||||
HostScreen = hostScreen;
|
||||
}
|
||||
|
||||
public string UrlPathSegment => "data-model";
|
||||
public IScreen HostScreen { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<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.Screens.Debugger.Tabs.Logs.LogsDebugView">
|
||||
<TextBlock>Logs</TextBlock>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,18 @@
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Debugger.Tabs.Logs
|
||||
{
|
||||
public class LogsDebugView : ReactiveUserControl<LogsDebugViewModel>
|
||||
{
|
||||
public LogsDebugView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using Artemis.UI.Shared;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Debugger.Tabs.Logs
|
||||
{
|
||||
public class LogsDebugViewModel : ActivatableViewModelBase, IRoutableViewModel
|
||||
{
|
||||
public LogsDebugViewModel(IScreen hostScreen)
|
||||
{
|
||||
HostScreen = hostScreen;
|
||||
}
|
||||
|
||||
public string UrlPathSegment => "logs";
|
||||
public IScreen HostScreen { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<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.Screens.Debugger.Tabs.Performance.PerformanceDebugView">
|
||||
<TextBlock>Performance</TextBlock>
|
||||
</UserControl>
|
||||