diff --git a/.github/workflows/Nuke.yml b/.github/workflows/Nuke.yml
index 3c342fe6b..e69f8597d 100644
--- a/.github/workflows/Nuke.yml
+++ b/.github/workflows/Nuke.yml
@@ -1,7 +1,13 @@
name: Nuke
+
on:
- pull_request:
push:
+ branches:
+ - 'master'
+ - 'dev'
+ pull_request:
+ branches:
+ - 'dev'
jobs:
windows:
diff --git a/.github/workflows/Qodana.yml b/.github/workflows/Qodana.yml.disabled
similarity index 100%
rename from .github/workflows/Qodana.yml
rename to .github/workflows/Qodana.yml.disabled
diff --git a/Contributing.md b/Contributing.md
index 0f6803b95..a5aea1432 100644
--- a/Contributing.md
+++ b/Contributing.md
@@ -116,12 +116,13 @@ To execute NUKE build on GitHub, you can follow these steps:
| Folder | Description |
|----------|----------------------------------------------------------------------------|
+| branding | Source files for logo, banner, installer background |
+| history | Museum, storage of original RevitLookup documentation |
| build | Nuke build system. Used to automate project builds |
| install | Add-in installer, called implicitly by the Nuke build |
| source | Project source code folder. Contains all solution projects |
+| tools | Extra tools for RevitLookup development |
| output | Folder of generated files by the build system, such as bundles, installers |
-| branding | Source files for logo, banner, installer background |
-| doc | Museum, storage of original RevitLookup documentation |
## Project structure
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 000000000..6b2b2a4fb
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,32 @@
+
+
+
+
+ enable
+ latest
+ x64
+ true
+
+
+
+ 2021
+ net48
+
+
+ 2022
+ net48
+
+
+ 2023
+ net48
+
+
+ 2024
+ net48
+
+
+ 2025
+ net8.0-windows
+
+
+
\ No newline at end of file
diff --git a/Directory.Packages.props b/Directory.Packages.props
new file mode 100644
index 000000000..101633349
--- /dev/null
+++ b/Directory.Packages.props
@@ -0,0 +1,74 @@
+
+
+
+
+ true
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/RevitLookup.sln b/RevitLookup.sln
index 42ff0e5dd..a833c4e6f 100644
--- a/RevitLookup.sln
+++ b/RevitLookup.sln
@@ -1,160 +1,399 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{374DC381-78CC-4FC7-A566-EB8C71C6369F}"
- ProjectSection(SolutionItems) = preProject
- Readme.md = Readme.md
- Changelog.md = Changelog.md
- Contributing.md = Contributing.md
- Build\Build.Configuration.cs = Build\Build.Configuration.cs
- EndProjectSection
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Installer", "install\Installer.csproj", "{B3814652-9158-4056-A721-1A380DE74BAE}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Build", "build\Build.csproj", "{64D9C223-D070-46CD-A635-04FD4E1BEC3C}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RevitLookup", "source\RevitLookup\RevitLookup.csproj", "{05C77115-2277-4DFC-8F95-BE37E5F1130F}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RevitLookup.UI", "source\RevitLookup.UI\RevitLookup.UI.csproj", "{CE5C104B-D76E-49DA-8BD6-BF472224AEC0}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RevitLookup.UI.Demo", "source\RevitLookup.UI.Demo\RevitLookup.UI.Demo.csproj", "{67B8906F-21B0-4CE3-82F7-8E608B9D53CA}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmarks", "source\Benchmarks\Benchmarks.csproj", "{827646DF-D373-4D40-8F0B-1E9842E3CE9A}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug R21|Any CPU = Debug R21|Any CPU
- Debug R22|Any CPU = Debug R22|Any CPU
- Debug R23|Any CPU = Debug R23|Any CPU
- Debug R24|Any CPU = Debug R24|Any CPU
- Debug R25|Any CPU = Debug R25|Any CPU
- Release R21|Any CPU = Release R21|Any CPU
- Release R22|Any CPU = Release R22|Any CPU
- Release R23|Any CPU = Release R23|Any CPU
- Release R24|Any CPU = Release R24|Any CPU
- Release R25|Any CPU = Release R25|Any CPU
- Installer|Any CPU = Installer|Any CPU
- UI.Demo Debug|Any CPU = UI.Demo Debug|Any CPU
- UI.Demo Release|Any CPU = UI.Demo Release|Any CPU
- Benchmark|Any CPU = Benchmark|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {05C77115-2277-4DFC-8F95-BE37E5F1130F}.UI.Demo Release|Any CPU.ActiveCfg = Release R22|Any CPU
- {05C77115-2277-4DFC-8F95-BE37E5F1130F}.UI.Demo Release|Any CPU.Build.0 = Release R22|Any CPU
- {05C77115-2277-4DFC-8F95-BE37E5F1130F}.Benchmark|Any CPU.ActiveCfg = Release R24|Any CPU
- {05C77115-2277-4DFC-8F95-BE37E5F1130F}.Installer|Any CPU.ActiveCfg = Release R24|Any CPU
- {05C77115-2277-4DFC-8F95-BE37E5F1130F}.Debug R21|Any CPU.ActiveCfg = Debug R21|Any CPU
- {05C77115-2277-4DFC-8F95-BE37E5F1130F}.Debug R21|Any CPU.Build.0 = Debug R21|Any CPU
- {05C77115-2277-4DFC-8F95-BE37E5F1130F}.Debug R22|Any CPU.ActiveCfg = Debug R22|Any CPU
- {05C77115-2277-4DFC-8F95-BE37E5F1130F}.Debug R22|Any CPU.Build.0 = Debug R22|Any CPU
- {05C77115-2277-4DFC-8F95-BE37E5F1130F}.Debug R23|Any CPU.ActiveCfg = Debug R23|Any CPU
- {05C77115-2277-4DFC-8F95-BE37E5F1130F}.Debug R23|Any CPU.Build.0 = Debug R23|Any CPU
- {05C77115-2277-4DFC-8F95-BE37E5F1130F}.Debug R24|Any CPU.ActiveCfg = Debug R24|Any CPU
- {05C77115-2277-4DFC-8F95-BE37E5F1130F}.Debug R24|Any CPU.Build.0 = Debug R24|Any CPU
- {05C77115-2277-4DFC-8F95-BE37E5F1130F}.Debug R25|Any CPU.ActiveCfg = Debug R25|Any CPU
- {05C77115-2277-4DFC-8F95-BE37E5F1130F}.Debug R25|Any CPU.Build.0 = Debug R25|Any CPU
- {05C77115-2277-4DFC-8F95-BE37E5F1130F}.Release R21|Any CPU.ActiveCfg = Release R21|Any CPU
- {05C77115-2277-4DFC-8F95-BE37E5F1130F}.Release R21|Any CPU.Build.0 = Release R21|Any CPU
- {05C77115-2277-4DFC-8F95-BE37E5F1130F}.Release R22|Any CPU.ActiveCfg = Release R22|Any CPU
- {05C77115-2277-4DFC-8F95-BE37E5F1130F}.Release R22|Any CPU.Build.0 = Release R22|Any CPU
- {05C77115-2277-4DFC-8F95-BE37E5F1130F}.Release R23|Any CPU.ActiveCfg = Release R23|Any CPU
- {05C77115-2277-4DFC-8F95-BE37E5F1130F}.Release R23|Any CPU.Build.0 = Release R23|Any CPU
- {05C77115-2277-4DFC-8F95-BE37E5F1130F}.Release R24|Any CPU.ActiveCfg = Release R24|Any CPU
- {05C77115-2277-4DFC-8F95-BE37E5F1130F}.Release R24|Any CPU.Build.0 = Release R24|Any CPU
- {05C77115-2277-4DFC-8F95-BE37E5F1130F}.Release R25|Any CPU.ActiveCfg = Release R25|Any CPU
- {05C77115-2277-4DFC-8F95-BE37E5F1130F}.Release R25|Any CPU.Build.0 = Release R25|Any CPU
- {05C77115-2277-4DFC-8F95-BE37E5F1130F}.UI.Demo Debug|Any CPU.ActiveCfg = Debug R25|Any CPU
- {05C77115-2277-4DFC-8F95-BE37E5F1130F}.UI.Demo Debug|Any CPU.Build.0 = Debug R25|Any CPU
- {64D9C223-D070-46CD-A635-04FD4E1BEC3C}.UI.Demo Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {64D9C223-D070-46CD-A635-04FD4E1BEC3C}.UI.Demo Release|Any CPU.ActiveCfg = Release|Any CPU
- {64D9C223-D070-46CD-A635-04FD4E1BEC3C}.Installer|Any CPU.ActiveCfg = Release|Any CPU
- {64D9C223-D070-46CD-A635-04FD4E1BEC3C}.Benchmark|Any CPU.ActiveCfg = Release|Any CPU
- {64D9C223-D070-46CD-A635-04FD4E1BEC3C}.Debug R21|Any CPU.ActiveCfg = Debug|Any CPU
- {64D9C223-D070-46CD-A635-04FD4E1BEC3C}.Debug R22|Any CPU.ActiveCfg = Debug|Any CPU
- {64D9C223-D070-46CD-A635-04FD4E1BEC3C}.Debug R23|Any CPU.ActiveCfg = Debug|Any CPU
- {64D9C223-D070-46CD-A635-04FD4E1BEC3C}.Debug R24|Any CPU.ActiveCfg = Debug|Any CPU
- {64D9C223-D070-46CD-A635-04FD4E1BEC3C}.Debug R25|Any CPU.ActiveCfg = Debug|Any CPU
- {64D9C223-D070-46CD-A635-04FD4E1BEC3C}.Release R21|Any CPU.ActiveCfg = Release|Any CPU
- {64D9C223-D070-46CD-A635-04FD4E1BEC3C}.Release R22|Any CPU.ActiveCfg = Release|Any CPU
- {64D9C223-D070-46CD-A635-04FD4E1BEC3C}.Release R23|Any CPU.ActiveCfg = Release|Any CPU
- {64D9C223-D070-46CD-A635-04FD4E1BEC3C}.Release R24|Any CPU.ActiveCfg = Release|Any CPU
- {64D9C223-D070-46CD-A635-04FD4E1BEC3C}.Release R25|Any CPU.ActiveCfg = Release|Any CPU
- {B3814652-9158-4056-A721-1A380DE74BAE}.UI.Demo Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {B3814652-9158-4056-A721-1A380DE74BAE}.UI.Demo Release|Any CPU.ActiveCfg = Release|Any CPU
- {B3814652-9158-4056-A721-1A380DE74BAE}.Benchmark|Any CPU.ActiveCfg = Release|Any CPU
- {B3814652-9158-4056-A721-1A380DE74BAE}.Installer|Any CPU.ActiveCfg = Release|Any CPU
- {B3814652-9158-4056-A721-1A380DE74BAE}.Installer|Any CPU.Build.0 = Release|Any CPU
- {B3814652-9158-4056-A721-1A380DE74BAE}.Debug R21|Any CPU.ActiveCfg = Debug|Any CPU
- {B3814652-9158-4056-A721-1A380DE74BAE}.Debug R22|Any CPU.ActiveCfg = Debug|Any CPU
- {B3814652-9158-4056-A721-1A380DE74BAE}.Debug R23|Any CPU.ActiveCfg = Debug|Any CPU
- {B3814652-9158-4056-A721-1A380DE74BAE}.Debug R24|Any CPU.ActiveCfg = Debug|Any CPU
- {B3814652-9158-4056-A721-1A380DE74BAE}.Debug R25|Any CPU.ActiveCfg = Debug|Any CPU
- {B3814652-9158-4056-A721-1A380DE74BAE}.Release R21|Any CPU.ActiveCfg = Release|Any CPU
- {B3814652-9158-4056-A721-1A380DE74BAE}.Release R22|Any CPU.ActiveCfg = Release|Any CPU
- {B3814652-9158-4056-A721-1A380DE74BAE}.Release R23|Any CPU.ActiveCfg = Release|Any CPU
- {B3814652-9158-4056-A721-1A380DE74BAE}.Release R24|Any CPU.ActiveCfg = Release|Any CPU
- {B3814652-9158-4056-A721-1A380DE74BAE}.Release R25|Any CPU.ActiveCfg = Release|Any CPU
- {CE5C104B-D76E-49DA-8BD6-BF472224AEC0}.UI.Demo Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {CE5C104B-D76E-49DA-8BD6-BF472224AEC0}.UI.Demo Debug|Any CPU.Build.0 = Debug|Any CPU
- {CE5C104B-D76E-49DA-8BD6-BF472224AEC0}.UI.Demo Release|Any CPU.ActiveCfg = Release|Any CPU
- {CE5C104B-D76E-49DA-8BD6-BF472224AEC0}.UI.Demo Release|Any CPU.Build.0 = Release|Any CPU
- {CE5C104B-D76E-49DA-8BD6-BF472224AEC0}.Benchmark|Any CPU.ActiveCfg = Release|Any CPU
- {CE5C104B-D76E-49DA-8BD6-BF472224AEC0}.Debug R21|Any CPU.ActiveCfg = Debug|Any CPU
- {CE5C104B-D76E-49DA-8BD6-BF472224AEC0}.Debug R21|Any CPU.Build.0 = Debug|Any CPU
- {CE5C104B-D76E-49DA-8BD6-BF472224AEC0}.Debug R22|Any CPU.ActiveCfg = Debug|Any CPU
- {CE5C104B-D76E-49DA-8BD6-BF472224AEC0}.Debug R23|Any CPU.ActiveCfg = Debug|Any CPU
- {CE5C104B-D76E-49DA-8BD6-BF472224AEC0}.Debug R22|Any CPU.Build.0 = Debug|Any CPU
- {CE5C104B-D76E-49DA-8BD6-BF472224AEC0}.Debug R23|Any CPU.Build.0 = Debug|Any CPU
- {CE5C104B-D76E-49DA-8BD6-BF472224AEC0}.Debug R24|Any CPU.ActiveCfg = Debug|Any CPU
- {CE5C104B-D76E-49DA-8BD6-BF472224AEC0}.Debug R24|Any CPU.Build.0 = Debug|Any CPU
- {CE5C104B-D76E-49DA-8BD6-BF472224AEC0}.Debug R25|Any CPU.ActiveCfg = Debug|Any CPU
- {CE5C104B-D76E-49DA-8BD6-BF472224AEC0}.Debug R25|Any CPU.Build.0 = Debug|Any CPU
- {CE5C104B-D76E-49DA-8BD6-BF472224AEC0}.Release R21|Any CPU.ActiveCfg = Release|Any CPU
- {CE5C104B-D76E-49DA-8BD6-BF472224AEC0}.Release R21|Any CPU.Build.0 = Release|Any CPU
- {CE5C104B-D76E-49DA-8BD6-BF472224AEC0}.Release R22|Any CPU.ActiveCfg = Release|Any CPU
- {CE5C104B-D76E-49DA-8BD6-BF472224AEC0}.Release R22|Any CPU.Build.0 = Release|Any CPU
- {CE5C104B-D76E-49DA-8BD6-BF472224AEC0}.Release R23|Any CPU.ActiveCfg = Release|Any CPU
- {CE5C104B-D76E-49DA-8BD6-BF472224AEC0}.Release R23|Any CPU.Build.0 = Release|Any CPU
- {CE5C104B-D76E-49DA-8BD6-BF472224AEC0}.Release R24|Any CPU.ActiveCfg = Release|Any CPU
- {CE5C104B-D76E-49DA-8BD6-BF472224AEC0}.Release R24|Any CPU.Build.0 = Release|Any CPU
- {CE5C104B-D76E-49DA-8BD6-BF472224AEC0}.Release R25|Any CPU.ActiveCfg = Release|Any CPU
- {CE5C104B-D76E-49DA-8BD6-BF472224AEC0}.Release R25|Any CPU.Build.0 = Release|Any CPU
- {CE5C104B-D76E-49DA-8BD6-BF472224AEC0}.Installer|Any CPU.ActiveCfg = Release|Any CPU
- {67B8906F-21B0-4CE3-82F7-8E608B9D53CA}.UI.Demo Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {67B8906F-21B0-4CE3-82F7-8E608B9D53CA}.UI.Demo Debug|Any CPU.Build.0 = Debug|Any CPU
- {67B8906F-21B0-4CE3-82F7-8E608B9D53CA}.UI.Demo Release|Any CPU.ActiveCfg = Release|Any CPU
- {67B8906F-21B0-4CE3-82F7-8E608B9D53CA}.UI.Demo Release|Any CPU.Build.0 = Release|Any CPU
- {67B8906F-21B0-4CE3-82F7-8E608B9D53CA}.Installer|Any CPU.ActiveCfg = Release|Any CPU
- {67B8906F-21B0-4CE3-82F7-8E608B9D53CA}.Benchmark|Any CPU.ActiveCfg = Release|Any CPU
- {67B8906F-21B0-4CE3-82F7-8E608B9D53CA}.Debug R21|Any CPU.ActiveCfg = Debug|Any CPU
- {67B8906F-21B0-4CE3-82F7-8E608B9D53CA}.Debug R22|Any CPU.ActiveCfg = Debug|Any CPU
- {67B8906F-21B0-4CE3-82F7-8E608B9D53CA}.Debug R23|Any CPU.ActiveCfg = Debug|Any CPU
- {67B8906F-21B0-4CE3-82F7-8E608B9D53CA}.Debug R24|Any CPU.ActiveCfg = Debug|Any CPU
- {67B8906F-21B0-4CE3-82F7-8E608B9D53CA}.Debug R25|Any CPU.ActiveCfg = Debug|Any CPU
- {67B8906F-21B0-4CE3-82F7-8E608B9D53CA}.Release R21|Any CPU.ActiveCfg = Release|Any CPU
- {67B8906F-21B0-4CE3-82F7-8E608B9D53CA}.Release R22|Any CPU.ActiveCfg = Release|Any CPU
- {67B8906F-21B0-4CE3-82F7-8E608B9D53CA}.Release R23|Any CPU.ActiveCfg = Release|Any CPU
- {67B8906F-21B0-4CE3-82F7-8E608B9D53CA}.Release R24|Any CPU.ActiveCfg = Release|Any CPU
- {67B8906F-21B0-4CE3-82F7-8E608B9D53CA}.Release R25|Any CPU.ActiveCfg = Release|Any CPU
- {827646DF-D373-4D40-8F0B-1E9842E3CE9A}.UI.Demo Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {827646DF-D373-4D40-8F0B-1E9842E3CE9A}.UI.Demo Release|Any CPU.ActiveCfg = Release|Any CPU
- {827646DF-D373-4D40-8F0B-1E9842E3CE9A}.Benchmark|Any CPU.ActiveCfg = Release|Any CPU
- {827646DF-D373-4D40-8F0B-1E9842E3CE9A}.Benchmark|Any CPU.Build.0 = Release|Any CPU
- {827646DF-D373-4D40-8F0B-1E9842E3CE9A}.Installer|Any CPU.ActiveCfg = Release|Any CPU
- {827646DF-D373-4D40-8F0B-1E9842E3CE9A}.Debug R21|Any CPU.ActiveCfg = Debug|Any CPU
- {827646DF-D373-4D40-8F0B-1E9842E3CE9A}.Debug R22|Any CPU.ActiveCfg = Debug|Any CPU
- {827646DF-D373-4D40-8F0B-1E9842E3CE9A}.Debug R23|Any CPU.ActiveCfg = Debug|Any CPU
- {827646DF-D373-4D40-8F0B-1E9842E3CE9A}.Debug R24|Any CPU.ActiveCfg = Debug|Any CPU
- {827646DF-D373-4D40-8F0B-1E9842E3CE9A}.Debug R25|Any CPU.ActiveCfg = Debug|Any CPU
- {827646DF-D373-4D40-8F0B-1E9842E3CE9A}.Release R21|Any CPU.ActiveCfg = Release|Any CPU
- {827646DF-D373-4D40-8F0B-1E9842E3CE9A}.Release R22|Any CPU.ActiveCfg = Release|Any CPU
- {827646DF-D373-4D40-8F0B-1E9842E3CE9A}.Release R23|Any CPU.ActiveCfg = Release|Any CPU
- {827646DF-D373-4D40-8F0B-1E9842E3CE9A}.Release R24|Any CPU.ActiveCfg = Release|Any CPU
- {827646DF-D373-4D40-8F0B-1E9842E3CE9A}.Release R25|Any CPU.ActiveCfg = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {DA402884-3505-43C8-8114-271D66001F6B}
- EndGlobalSection
-EndGlobal
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RevitLookup.Abstractions", "source\RevitLookup.Abstractions\RevitLookup.Abstractions.csproj", "{71AAD043-A063-F08F-42F4-BF2AA43BEDA4}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RevitLookup.Common", "source\RevitLookup.Common\RevitLookup.Common.csproj", "{73ABAE95-3197-9EDD-91B6-7E98656CF7D1}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Automation", "Automation", "{BE4A8008-4A00-8C08-26C2-BD576741EBBE}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Build", "build\Build.csproj", "{7DF908B3-503D-51C2-3675-626DC2895B77}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Installer", "install\Installer.csproj", "{0B3682BA-1074-598D-D8A6-04BB7116F36C}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Engine", "Engine", "{D585295F-7994-3649-1065-7AA403C41681}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LookupEngine.Abstractions", "source\LookupEngine.Abstractions\LookupEngine.Abstractions.csproj", "{C075FD80-58C0-8D06-69F2-853E4D51AFE8}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LookupEngine", "source\LookupEngine\LookupEngine.csproj", "{BD145DF8-35DF-8587-A26B-968A550D1D06}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Frontend", "Frontend", "{CE609670-85B9-0D16-FB54-ED063D5D8A6D}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RevitLookup.UI.Abstractions", "source\RevitLookup.UI.Abstractions\RevitLookup.UI.Abstractions.csproj", "{B687BEC0-11DB-7690-478A-444D8D9FCE25}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RevitLookup.UI.Framework", "source\RevitLookup.UI.Framework\RevitLookup.UI.Framework.csproj", "{925534FD-C38B-5571-23B0-62982B24DC6C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RevitLookup.UI.Playground", "source\RevitLookup.UI.Playground\RevitLookup.UI.Playground.csproj", "{4FADCDA4-2A63-BF3E-03C8-20102440CA7C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RevitLookup.UI", "source\RevitLookup.UI\RevitLookup.UI.csproj", "{DED9FF57-E78C-F4C2-9CFD-368819DFE4C1}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Revit", "Revit", "{D64246B1-3E22-9D95-23C6-14262B074D58}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RevitLookupObsolete", "source\RevitLookupObsolete\RevitLookupObsolete.csproj", "{897CB786-AC5A-18EE-3091-7E14935796D5}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RevitLookup", "source\RevitLookup\RevitLookup.csproj", "{7B7CA95C-9873-5ADB-DBCF-B46046742163}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{089100B1-113F-4E66-888A-E83F3999EAFD}"
+ ProjectSection(SolutionItems) = preProject
+ Readme.md = Readme.md
+ Changelog.md = Changelog.md
+ Contributing.md = Contributing.md
+ Build\Build.Configuration.cs = Build\Build.Configuration.cs
+ Directory.Packages.props = Directory.Packages.props
+ Directory.Build.props = Directory.Build.props
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{0AB3BF05-4346-4AA6-1389-037BE0695223}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LookupEngine.Tests.Performance", "tests\LookupEngine.Tests.Performance\LookupEngine.Tests.Performance.csproj", "{280FCE1F-F215-F89A-D710-1D9770067692}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LookupEngine.Tests.Unit", "tests\LookupEngine.Tests.Unit\LookupEngine.Tests.Unit.csproj", "{E8D6B99E-2C38-C524-525B-EF812BB3926C}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug Engine|Any CPU = Debug Engine|Any CPU
+ Debug Frontend|Any CPU = Debug Frontend|Any CPU
+ Debug R21|Any CPU = Debug R21|Any CPU
+ Debug R22|Any CPU = Debug R22|Any CPU
+ Debug R23|Any CPU = Debug R23|Any CPU
+ Debug R24|Any CPU = Debug R24|Any CPU
+ Debug R25|Any CPU = Debug R25|Any CPU
+ Release Build|Any CPU = Release Build|Any CPU
+ Release Engine|Any CPU = Release Engine|Any CPU
+ Release Frontend|Any CPU = Release Frontend|Any CPU
+ Release R21|Any CPU = Release R21|Any CPU
+ Release R22|Any CPU = Release R22|Any CPU
+ Release R23|Any CPU = Release R23|Any CPU
+ Release R24|Any CPU = Release R24|Any CPU
+ Release R25|Any CPU = Release R25|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {71AAD043-A063-F08F-42F4-BF2AA43BEDA4}.Debug Engine|Any CPU.ActiveCfg = Debug|Any CPU
+ {71AAD043-A063-F08F-42F4-BF2AA43BEDA4}.Debug Frontend|Any CPU.ActiveCfg = Debug|Any CPU
+ {71AAD043-A063-F08F-42F4-BF2AA43BEDA4}.Debug Frontend|Any CPU.Build.0 = Debug|Any CPU
+ {71AAD043-A063-F08F-42F4-BF2AA43BEDA4}.Debug R21|Any CPU.ActiveCfg = Debug|Any CPU
+ {71AAD043-A063-F08F-42F4-BF2AA43BEDA4}.Debug R21|Any CPU.Build.0 = Debug|Any CPU
+ {71AAD043-A063-F08F-42F4-BF2AA43BEDA4}.Debug R22|Any CPU.ActiveCfg = Debug|Any CPU
+ {71AAD043-A063-F08F-42F4-BF2AA43BEDA4}.Debug R22|Any CPU.Build.0 = Debug|Any CPU
+ {71AAD043-A063-F08F-42F4-BF2AA43BEDA4}.Debug R23|Any CPU.ActiveCfg = Debug|Any CPU
+ {71AAD043-A063-F08F-42F4-BF2AA43BEDA4}.Debug R23|Any CPU.Build.0 = Debug|Any CPU
+ {71AAD043-A063-F08F-42F4-BF2AA43BEDA4}.Debug R24|Any CPU.ActiveCfg = Debug|Any CPU
+ {71AAD043-A063-F08F-42F4-BF2AA43BEDA4}.Debug R24|Any CPU.Build.0 = Debug|Any CPU
+ {71AAD043-A063-F08F-42F4-BF2AA43BEDA4}.Debug R25|Any CPU.ActiveCfg = Debug|Any CPU
+ {71AAD043-A063-F08F-42F4-BF2AA43BEDA4}.Debug R25|Any CPU.Build.0 = Debug|Any CPU
+ {71AAD043-A063-F08F-42F4-BF2AA43BEDA4}.Release Build|Any CPU.ActiveCfg = Release|Any CPU
+ {71AAD043-A063-F08F-42F4-BF2AA43BEDA4}.Release Engine|Any CPU.ActiveCfg = Release|Any CPU
+ {71AAD043-A063-F08F-42F4-BF2AA43BEDA4}.Release Frontend|Any CPU.ActiveCfg = Release|Any CPU
+ {71AAD043-A063-F08F-42F4-BF2AA43BEDA4}.Release Frontend|Any CPU.Build.0 = Release|Any CPU
+ {71AAD043-A063-F08F-42F4-BF2AA43BEDA4}.Release R21|Any CPU.ActiveCfg = Release|Any CPU
+ {71AAD043-A063-F08F-42F4-BF2AA43BEDA4}.Release R21|Any CPU.Build.0 = Release|Any CPU
+ {71AAD043-A063-F08F-42F4-BF2AA43BEDA4}.Release R22|Any CPU.ActiveCfg = Release|Any CPU
+ {71AAD043-A063-F08F-42F4-BF2AA43BEDA4}.Release R22|Any CPU.Build.0 = Release|Any CPU
+ {71AAD043-A063-F08F-42F4-BF2AA43BEDA4}.Release R23|Any CPU.ActiveCfg = Release|Any CPU
+ {71AAD043-A063-F08F-42F4-BF2AA43BEDA4}.Release R23|Any CPU.Build.0 = Release|Any CPU
+ {71AAD043-A063-F08F-42F4-BF2AA43BEDA4}.Release R24|Any CPU.ActiveCfg = Release|Any CPU
+ {71AAD043-A063-F08F-42F4-BF2AA43BEDA4}.Release R24|Any CPU.Build.0 = Release|Any CPU
+ {71AAD043-A063-F08F-42F4-BF2AA43BEDA4}.Release R25|Any CPU.ActiveCfg = Release|Any CPU
+ {71AAD043-A063-F08F-42F4-BF2AA43BEDA4}.Release R25|Any CPU.Build.0 = Release|Any CPU
+ {73ABAE95-3197-9EDD-91B6-7E98656CF7D1}.Debug Engine|Any CPU.ActiveCfg = Debug|Any CPU
+ {73ABAE95-3197-9EDD-91B6-7E98656CF7D1}.Debug Frontend|Any CPU.ActiveCfg = Debug|Any CPU
+ {73ABAE95-3197-9EDD-91B6-7E98656CF7D1}.Debug Frontend|Any CPU.Build.0 = Debug|Any CPU
+ {73ABAE95-3197-9EDD-91B6-7E98656CF7D1}.Debug R21|Any CPU.ActiveCfg = Debug|Any CPU
+ {73ABAE95-3197-9EDD-91B6-7E98656CF7D1}.Debug R21|Any CPU.Build.0 = Debug|Any CPU
+ {73ABAE95-3197-9EDD-91B6-7E98656CF7D1}.Debug R22|Any CPU.ActiveCfg = Debug|Any CPU
+ {73ABAE95-3197-9EDD-91B6-7E98656CF7D1}.Debug R22|Any CPU.Build.0 = Debug|Any CPU
+ {73ABAE95-3197-9EDD-91B6-7E98656CF7D1}.Debug R23|Any CPU.ActiveCfg = Debug|Any CPU
+ {73ABAE95-3197-9EDD-91B6-7E98656CF7D1}.Debug R23|Any CPU.Build.0 = Debug|Any CPU
+ {73ABAE95-3197-9EDD-91B6-7E98656CF7D1}.Debug R24|Any CPU.ActiveCfg = Debug|Any CPU
+ {73ABAE95-3197-9EDD-91B6-7E98656CF7D1}.Debug R24|Any CPU.Build.0 = Debug|Any CPU
+ {73ABAE95-3197-9EDD-91B6-7E98656CF7D1}.Debug R25|Any CPU.ActiveCfg = Debug|Any CPU
+ {73ABAE95-3197-9EDD-91B6-7E98656CF7D1}.Debug R25|Any CPU.Build.0 = Debug|Any CPU
+ {73ABAE95-3197-9EDD-91B6-7E98656CF7D1}.Release Build|Any CPU.ActiveCfg = Release|Any CPU
+ {73ABAE95-3197-9EDD-91B6-7E98656CF7D1}.Release Engine|Any CPU.ActiveCfg = Release|Any CPU
+ {73ABAE95-3197-9EDD-91B6-7E98656CF7D1}.Release Frontend|Any CPU.ActiveCfg = Release|Any CPU
+ {73ABAE95-3197-9EDD-91B6-7E98656CF7D1}.Release Frontend|Any CPU.Build.0 = Release|Any CPU
+ {73ABAE95-3197-9EDD-91B6-7E98656CF7D1}.Release R21|Any CPU.ActiveCfg = Release|Any CPU
+ {73ABAE95-3197-9EDD-91B6-7E98656CF7D1}.Release R21|Any CPU.Build.0 = Release|Any CPU
+ {73ABAE95-3197-9EDD-91B6-7E98656CF7D1}.Release R22|Any CPU.ActiveCfg = Release|Any CPU
+ {73ABAE95-3197-9EDD-91B6-7E98656CF7D1}.Release R22|Any CPU.Build.0 = Release|Any CPU
+ {73ABAE95-3197-9EDD-91B6-7E98656CF7D1}.Release R23|Any CPU.ActiveCfg = Release|Any CPU
+ {73ABAE95-3197-9EDD-91B6-7E98656CF7D1}.Release R23|Any CPU.Build.0 = Release|Any CPU
+ {73ABAE95-3197-9EDD-91B6-7E98656CF7D1}.Release R24|Any CPU.ActiveCfg = Release|Any CPU
+ {73ABAE95-3197-9EDD-91B6-7E98656CF7D1}.Release R24|Any CPU.Build.0 = Release|Any CPU
+ {73ABAE95-3197-9EDD-91B6-7E98656CF7D1}.Release R25|Any CPU.ActiveCfg = Release|Any CPU
+ {73ABAE95-3197-9EDD-91B6-7E98656CF7D1}.Release R25|Any CPU.Build.0 = Release|Any CPU
+ {7DF908B3-503D-51C2-3675-626DC2895B77}.Debug Engine|Any CPU.ActiveCfg = Debug|Any CPU
+ {7DF908B3-503D-51C2-3675-626DC2895B77}.Debug Frontend|Any CPU.ActiveCfg = Debug|Any CPU
+ {7DF908B3-503D-51C2-3675-626DC2895B77}.Debug R21|Any CPU.ActiveCfg = Debug|Any CPU
+ {7DF908B3-503D-51C2-3675-626DC2895B77}.Debug R22|Any CPU.ActiveCfg = Debug|Any CPU
+ {7DF908B3-503D-51C2-3675-626DC2895B77}.Debug R23|Any CPU.ActiveCfg = Debug|Any CPU
+ {7DF908B3-503D-51C2-3675-626DC2895B77}.Debug R24|Any CPU.ActiveCfg = Debug|Any CPU
+ {7DF908B3-503D-51C2-3675-626DC2895B77}.Debug R25|Any CPU.ActiveCfg = Debug|Any CPU
+ {7DF908B3-503D-51C2-3675-626DC2895B77}.Release Build|Any CPU.ActiveCfg = Release|Any CPU
+ {7DF908B3-503D-51C2-3675-626DC2895B77}.Release Engine|Any CPU.ActiveCfg = Release|Any CPU
+ {7DF908B3-503D-51C2-3675-626DC2895B77}.Release Frontend|Any CPU.ActiveCfg = Release|Any CPU
+ {7DF908B3-503D-51C2-3675-626DC2895B77}.Release R21|Any CPU.ActiveCfg = Release|Any CPU
+ {7DF908B3-503D-51C2-3675-626DC2895B77}.Release R22|Any CPU.ActiveCfg = Release|Any CPU
+ {7DF908B3-503D-51C2-3675-626DC2895B77}.Release R23|Any CPU.ActiveCfg = Release|Any CPU
+ {7DF908B3-503D-51C2-3675-626DC2895B77}.Release R24|Any CPU.ActiveCfg = Release|Any CPU
+ {7DF908B3-503D-51C2-3675-626DC2895B77}.Release R25|Any CPU.ActiveCfg = Release|Any CPU
+ {0B3682BA-1074-598D-D8A6-04BB7116F36C}.Debug Engine|Any CPU.ActiveCfg = Debug|Any CPU
+ {0B3682BA-1074-598D-D8A6-04BB7116F36C}.Debug Frontend|Any CPU.ActiveCfg = Debug|Any CPU
+ {0B3682BA-1074-598D-D8A6-04BB7116F36C}.Debug R21|Any CPU.ActiveCfg = Debug|Any CPU
+ {0B3682BA-1074-598D-D8A6-04BB7116F36C}.Debug R22|Any CPU.ActiveCfg = Debug|Any CPU
+ {0B3682BA-1074-598D-D8A6-04BB7116F36C}.Debug R23|Any CPU.ActiveCfg = Debug|Any CPU
+ {0B3682BA-1074-598D-D8A6-04BB7116F36C}.Debug R24|Any CPU.ActiveCfg = Debug|Any CPU
+ {0B3682BA-1074-598D-D8A6-04BB7116F36C}.Debug R25|Any CPU.ActiveCfg = Debug|Any CPU
+ {0B3682BA-1074-598D-D8A6-04BB7116F36C}.Release Build|Any CPU.ActiveCfg = Release|Any CPU
+ {0B3682BA-1074-598D-D8A6-04BB7116F36C}.Release Build|Any CPU.Build.0 = Release|Any CPU
+ {0B3682BA-1074-598D-D8A6-04BB7116F36C}.Release Engine|Any CPU.ActiveCfg = Release|Any CPU
+ {0B3682BA-1074-598D-D8A6-04BB7116F36C}.Release Frontend|Any CPU.ActiveCfg = Release|Any CPU
+ {0B3682BA-1074-598D-D8A6-04BB7116F36C}.Release R21|Any CPU.ActiveCfg = Release|Any CPU
+ {0B3682BA-1074-598D-D8A6-04BB7116F36C}.Release R22|Any CPU.ActiveCfg = Release|Any CPU
+ {0B3682BA-1074-598D-D8A6-04BB7116F36C}.Release R23|Any CPU.ActiveCfg = Release|Any CPU
+ {0B3682BA-1074-598D-D8A6-04BB7116F36C}.Release R24|Any CPU.ActiveCfg = Release|Any CPU
+ {0B3682BA-1074-598D-D8A6-04BB7116F36C}.Release R25|Any CPU.ActiveCfg = Release|Any CPU
+ {C075FD80-58C0-8D06-69F2-853E4D51AFE8}.Debug Engine|Any CPU.ActiveCfg = Debug|Any CPU
+ {C075FD80-58C0-8D06-69F2-853E4D51AFE8}.Debug Engine|Any CPU.Build.0 = Debug|Any CPU
+ {C075FD80-58C0-8D06-69F2-853E4D51AFE8}.Debug Frontend|Any CPU.ActiveCfg = Debug|Any CPU
+ {C075FD80-58C0-8D06-69F2-853E4D51AFE8}.Debug Frontend|Any CPU.Build.0 = Debug|Any CPU
+ {C075FD80-58C0-8D06-69F2-853E4D51AFE8}.Debug R21|Any CPU.ActiveCfg = Debug|Any CPU
+ {C075FD80-58C0-8D06-69F2-853E4D51AFE8}.Debug R21|Any CPU.Build.0 = Debug|Any CPU
+ {C075FD80-58C0-8D06-69F2-853E4D51AFE8}.Debug R22|Any CPU.ActiveCfg = Debug|Any CPU
+ {C075FD80-58C0-8D06-69F2-853E4D51AFE8}.Debug R22|Any CPU.Build.0 = Debug|Any CPU
+ {C075FD80-58C0-8D06-69F2-853E4D51AFE8}.Debug R23|Any CPU.ActiveCfg = Debug|Any CPU
+ {C075FD80-58C0-8D06-69F2-853E4D51AFE8}.Debug R23|Any CPU.Build.0 = Debug|Any CPU
+ {C075FD80-58C0-8D06-69F2-853E4D51AFE8}.Debug R24|Any CPU.ActiveCfg = Debug|Any CPU
+ {C075FD80-58C0-8D06-69F2-853E4D51AFE8}.Debug R24|Any CPU.Build.0 = Debug|Any CPU
+ {C075FD80-58C0-8D06-69F2-853E4D51AFE8}.Debug R25|Any CPU.ActiveCfg = Debug|Any CPU
+ {C075FD80-58C0-8D06-69F2-853E4D51AFE8}.Debug R25|Any CPU.Build.0 = Debug|Any CPU
+ {C075FD80-58C0-8D06-69F2-853E4D51AFE8}.Release Build|Any CPU.ActiveCfg = Release|Any CPU
+ {C075FD80-58C0-8D06-69F2-853E4D51AFE8}.Release Engine|Any CPU.ActiveCfg = Release|Any CPU
+ {C075FD80-58C0-8D06-69F2-853E4D51AFE8}.Release Engine|Any CPU.Build.0 = Release|Any CPU
+ {C075FD80-58C0-8D06-69F2-853E4D51AFE8}.Release Frontend|Any CPU.ActiveCfg = Release|Any CPU
+ {C075FD80-58C0-8D06-69F2-853E4D51AFE8}.Release Frontend|Any CPU.Build.0 = Release|Any CPU
+ {C075FD80-58C0-8D06-69F2-853E4D51AFE8}.Release R21|Any CPU.ActiveCfg = Release|Any CPU
+ {C075FD80-58C0-8D06-69F2-853E4D51AFE8}.Release R21|Any CPU.Build.0 = Release|Any CPU
+ {C075FD80-58C0-8D06-69F2-853E4D51AFE8}.Release R22|Any CPU.ActiveCfg = Release|Any CPU
+ {C075FD80-58C0-8D06-69F2-853E4D51AFE8}.Release R22|Any CPU.Build.0 = Release|Any CPU
+ {C075FD80-58C0-8D06-69F2-853E4D51AFE8}.Release R23|Any CPU.ActiveCfg = Release|Any CPU
+ {C075FD80-58C0-8D06-69F2-853E4D51AFE8}.Release R23|Any CPU.Build.0 = Release|Any CPU
+ {C075FD80-58C0-8D06-69F2-853E4D51AFE8}.Release R24|Any CPU.ActiveCfg = Release|Any CPU
+ {C075FD80-58C0-8D06-69F2-853E4D51AFE8}.Release R24|Any CPU.Build.0 = Release|Any CPU
+ {C075FD80-58C0-8D06-69F2-853E4D51AFE8}.Release R25|Any CPU.ActiveCfg = Release|Any CPU
+ {C075FD80-58C0-8D06-69F2-853E4D51AFE8}.Release R25|Any CPU.Build.0 = Release|Any CPU
+ {BD145DF8-35DF-8587-A26B-968A550D1D06}.Debug Engine|Any CPU.ActiveCfg = Debug|Any CPU
+ {BD145DF8-35DF-8587-A26B-968A550D1D06}.Debug Engine|Any CPU.Build.0 = Debug|Any CPU
+ {BD145DF8-35DF-8587-A26B-968A550D1D06}.Debug Frontend|Any CPU.ActiveCfg = Debug|Any CPU
+ {BD145DF8-35DF-8587-A26B-968A550D1D06}.Debug Frontend|Any CPU.Build.0 = Debug|Any CPU
+ {BD145DF8-35DF-8587-A26B-968A550D1D06}.Debug R21|Any CPU.ActiveCfg = Debug|Any CPU
+ {BD145DF8-35DF-8587-A26B-968A550D1D06}.Debug R21|Any CPU.Build.0 = Debug|Any CPU
+ {BD145DF8-35DF-8587-A26B-968A550D1D06}.Debug R22|Any CPU.ActiveCfg = Debug|Any CPU
+ {BD145DF8-35DF-8587-A26B-968A550D1D06}.Debug R22|Any CPU.Build.0 = Debug|Any CPU
+ {BD145DF8-35DF-8587-A26B-968A550D1D06}.Debug R23|Any CPU.ActiveCfg = Debug|Any CPU
+ {BD145DF8-35DF-8587-A26B-968A550D1D06}.Debug R23|Any CPU.Build.0 = Debug|Any CPU
+ {BD145DF8-35DF-8587-A26B-968A550D1D06}.Debug R24|Any CPU.ActiveCfg = Debug|Any CPU
+ {BD145DF8-35DF-8587-A26B-968A550D1D06}.Debug R24|Any CPU.Build.0 = Debug|Any CPU
+ {BD145DF8-35DF-8587-A26B-968A550D1D06}.Debug R25|Any CPU.ActiveCfg = Debug|Any CPU
+ {BD145DF8-35DF-8587-A26B-968A550D1D06}.Debug R25|Any CPU.Build.0 = Debug|Any CPU
+ {BD145DF8-35DF-8587-A26B-968A550D1D06}.Release Build|Any CPU.ActiveCfg = Release|Any CPU
+ {BD145DF8-35DF-8587-A26B-968A550D1D06}.Release Engine|Any CPU.ActiveCfg = Release|Any CPU
+ {BD145DF8-35DF-8587-A26B-968A550D1D06}.Release Engine|Any CPU.Build.0 = Release|Any CPU
+ {BD145DF8-35DF-8587-A26B-968A550D1D06}.Release Frontend|Any CPU.ActiveCfg = Release|Any CPU
+ {BD145DF8-35DF-8587-A26B-968A550D1D06}.Release Frontend|Any CPU.Build.0 = Release|Any CPU
+ {BD145DF8-35DF-8587-A26B-968A550D1D06}.Release R21|Any CPU.ActiveCfg = Release|Any CPU
+ {BD145DF8-35DF-8587-A26B-968A550D1D06}.Release R21|Any CPU.Build.0 = Release|Any CPU
+ {BD145DF8-35DF-8587-A26B-968A550D1D06}.Release R22|Any CPU.ActiveCfg = Release|Any CPU
+ {BD145DF8-35DF-8587-A26B-968A550D1D06}.Release R22|Any CPU.Build.0 = Release|Any CPU
+ {BD145DF8-35DF-8587-A26B-968A550D1D06}.Release R23|Any CPU.ActiveCfg = Release|Any CPU
+ {BD145DF8-35DF-8587-A26B-968A550D1D06}.Release R23|Any CPU.Build.0 = Release|Any CPU
+ {BD145DF8-35DF-8587-A26B-968A550D1D06}.Release R24|Any CPU.ActiveCfg = Release|Any CPU
+ {BD145DF8-35DF-8587-A26B-968A550D1D06}.Release R24|Any CPU.Build.0 = Release|Any CPU
+ {BD145DF8-35DF-8587-A26B-968A550D1D06}.Release R25|Any CPU.ActiveCfg = Release|Any CPU
+ {BD145DF8-35DF-8587-A26B-968A550D1D06}.Release R25|Any CPU.Build.0 = Release|Any CPU
+ {B687BEC0-11DB-7690-478A-444D8D9FCE25}.Debug Engine|Any CPU.ActiveCfg = Debug|Any CPU
+ {B687BEC0-11DB-7690-478A-444D8D9FCE25}.Debug Frontend|Any CPU.ActiveCfg = Debug|Any CPU
+ {B687BEC0-11DB-7690-478A-444D8D9FCE25}.Debug Frontend|Any CPU.Build.0 = Debug|Any CPU
+ {B687BEC0-11DB-7690-478A-444D8D9FCE25}.Debug R21|Any CPU.ActiveCfg = Debug|Any CPU
+ {B687BEC0-11DB-7690-478A-444D8D9FCE25}.Debug R21|Any CPU.Build.0 = Debug|Any CPU
+ {B687BEC0-11DB-7690-478A-444D8D9FCE25}.Debug R22|Any CPU.ActiveCfg = Debug|Any CPU
+ {B687BEC0-11DB-7690-478A-444D8D9FCE25}.Debug R22|Any CPU.Build.0 = Debug|Any CPU
+ {B687BEC0-11DB-7690-478A-444D8D9FCE25}.Debug R23|Any CPU.ActiveCfg = Debug|Any CPU
+ {B687BEC0-11DB-7690-478A-444D8D9FCE25}.Debug R23|Any CPU.Build.0 = Debug|Any CPU
+ {B687BEC0-11DB-7690-478A-444D8D9FCE25}.Debug R24|Any CPU.ActiveCfg = Debug|Any CPU
+ {B687BEC0-11DB-7690-478A-444D8D9FCE25}.Debug R24|Any CPU.Build.0 = Debug|Any CPU
+ {B687BEC0-11DB-7690-478A-444D8D9FCE25}.Debug R25|Any CPU.ActiveCfg = Debug|Any CPU
+ {B687BEC0-11DB-7690-478A-444D8D9FCE25}.Debug R25|Any CPU.Build.0 = Debug|Any CPU
+ {B687BEC0-11DB-7690-478A-444D8D9FCE25}.Release Build|Any CPU.ActiveCfg = Release|Any CPU
+ {B687BEC0-11DB-7690-478A-444D8D9FCE25}.Release Engine|Any CPU.ActiveCfg = Release|Any CPU
+ {B687BEC0-11DB-7690-478A-444D8D9FCE25}.Release Frontend|Any CPU.ActiveCfg = Release|Any CPU
+ {B687BEC0-11DB-7690-478A-444D8D9FCE25}.Release Frontend|Any CPU.Build.0 = Release|Any CPU
+ {B687BEC0-11DB-7690-478A-444D8D9FCE25}.Release R21|Any CPU.ActiveCfg = Release|Any CPU
+ {B687BEC0-11DB-7690-478A-444D8D9FCE25}.Release R21|Any CPU.Build.0 = Release|Any CPU
+ {B687BEC0-11DB-7690-478A-444D8D9FCE25}.Release R22|Any CPU.ActiveCfg = Release|Any CPU
+ {B687BEC0-11DB-7690-478A-444D8D9FCE25}.Release R22|Any CPU.Build.0 = Release|Any CPU
+ {B687BEC0-11DB-7690-478A-444D8D9FCE25}.Release R23|Any CPU.ActiveCfg = Release|Any CPU
+ {B687BEC0-11DB-7690-478A-444D8D9FCE25}.Release R23|Any CPU.Build.0 = Release|Any CPU
+ {B687BEC0-11DB-7690-478A-444D8D9FCE25}.Release R24|Any CPU.ActiveCfg = Release|Any CPU
+ {B687BEC0-11DB-7690-478A-444D8D9FCE25}.Release R24|Any CPU.Build.0 = Release|Any CPU
+ {B687BEC0-11DB-7690-478A-444D8D9FCE25}.Release R25|Any CPU.ActiveCfg = Release|Any CPU
+ {B687BEC0-11DB-7690-478A-444D8D9FCE25}.Release R25|Any CPU.Build.0 = Release|Any CPU
+ {925534FD-C38B-5571-23B0-62982B24DC6C}.Debug Engine|Any CPU.ActiveCfg = Debug|Any CPU
+ {925534FD-C38B-5571-23B0-62982B24DC6C}.Debug Frontend|Any CPU.ActiveCfg = Debug|Any CPU
+ {925534FD-C38B-5571-23B0-62982B24DC6C}.Debug Frontend|Any CPU.Build.0 = Debug|Any CPU
+ {925534FD-C38B-5571-23B0-62982B24DC6C}.Debug R21|Any CPU.ActiveCfg = Debug|Any CPU
+ {925534FD-C38B-5571-23B0-62982B24DC6C}.Debug R21|Any CPU.Build.0 = Debug|Any CPU
+ {925534FD-C38B-5571-23B0-62982B24DC6C}.Debug R22|Any CPU.ActiveCfg = Debug|Any CPU
+ {925534FD-C38B-5571-23B0-62982B24DC6C}.Debug R22|Any CPU.Build.0 = Debug|Any CPU
+ {925534FD-C38B-5571-23B0-62982B24DC6C}.Debug R23|Any CPU.ActiveCfg = Debug|Any CPU
+ {925534FD-C38B-5571-23B0-62982B24DC6C}.Debug R23|Any CPU.Build.0 = Debug|Any CPU
+ {925534FD-C38B-5571-23B0-62982B24DC6C}.Debug R24|Any CPU.ActiveCfg = Debug|Any CPU
+ {925534FD-C38B-5571-23B0-62982B24DC6C}.Debug R24|Any CPU.Build.0 = Debug|Any CPU
+ {925534FD-C38B-5571-23B0-62982B24DC6C}.Debug R25|Any CPU.ActiveCfg = Debug|Any CPU
+ {925534FD-C38B-5571-23B0-62982B24DC6C}.Debug R25|Any CPU.Build.0 = Debug|Any CPU
+ {925534FD-C38B-5571-23B0-62982B24DC6C}.Release Build|Any CPU.ActiveCfg = Release|Any CPU
+ {925534FD-C38B-5571-23B0-62982B24DC6C}.Release Engine|Any CPU.ActiveCfg = Release|Any CPU
+ {925534FD-C38B-5571-23B0-62982B24DC6C}.Release Frontend|Any CPU.ActiveCfg = Release|Any CPU
+ {925534FD-C38B-5571-23B0-62982B24DC6C}.Release Frontend|Any CPU.Build.0 = Release|Any CPU
+ {925534FD-C38B-5571-23B0-62982B24DC6C}.Release R21|Any CPU.ActiveCfg = Release|Any CPU
+ {925534FD-C38B-5571-23B0-62982B24DC6C}.Release R21|Any CPU.Build.0 = Release|Any CPU
+ {925534FD-C38B-5571-23B0-62982B24DC6C}.Release R22|Any CPU.ActiveCfg = Release|Any CPU
+ {925534FD-C38B-5571-23B0-62982B24DC6C}.Release R22|Any CPU.Build.0 = Release|Any CPU
+ {925534FD-C38B-5571-23B0-62982B24DC6C}.Release R23|Any CPU.ActiveCfg = Release|Any CPU
+ {925534FD-C38B-5571-23B0-62982B24DC6C}.Release R23|Any CPU.Build.0 = Release|Any CPU
+ {925534FD-C38B-5571-23B0-62982B24DC6C}.Release R24|Any CPU.ActiveCfg = Release|Any CPU
+ {925534FD-C38B-5571-23B0-62982B24DC6C}.Release R24|Any CPU.Build.0 = Release|Any CPU
+ {925534FD-C38B-5571-23B0-62982B24DC6C}.Release R25|Any CPU.ActiveCfg = Release|Any CPU
+ {925534FD-C38B-5571-23B0-62982B24DC6C}.Release R25|Any CPU.Build.0 = Release|Any CPU
+ {4FADCDA4-2A63-BF3E-03C8-20102440CA7C}.Debug Engine|Any CPU.ActiveCfg = Debug|Any CPU
+ {4FADCDA4-2A63-BF3E-03C8-20102440CA7C}.Debug Frontend|Any CPU.ActiveCfg = Debug|Any CPU
+ {4FADCDA4-2A63-BF3E-03C8-20102440CA7C}.Debug Frontend|Any CPU.Build.0 = Debug|Any CPU
+ {4FADCDA4-2A63-BF3E-03C8-20102440CA7C}.Debug R21|Any CPU.ActiveCfg = Debug|Any CPU
+ {4FADCDA4-2A63-BF3E-03C8-20102440CA7C}.Debug R22|Any CPU.ActiveCfg = Debug|Any CPU
+ {4FADCDA4-2A63-BF3E-03C8-20102440CA7C}.Debug R23|Any CPU.ActiveCfg = Debug|Any CPU
+ {4FADCDA4-2A63-BF3E-03C8-20102440CA7C}.Debug R24|Any CPU.ActiveCfg = Debug|Any CPU
+ {4FADCDA4-2A63-BF3E-03C8-20102440CA7C}.Debug R25|Any CPU.ActiveCfg = Debug|Any CPU
+ {4FADCDA4-2A63-BF3E-03C8-20102440CA7C}.Release Build|Any CPU.ActiveCfg = Release|Any CPU
+ {4FADCDA4-2A63-BF3E-03C8-20102440CA7C}.Release Engine|Any CPU.ActiveCfg = Release|Any CPU
+ {4FADCDA4-2A63-BF3E-03C8-20102440CA7C}.Release Frontend|Any CPU.ActiveCfg = Release|Any CPU
+ {4FADCDA4-2A63-BF3E-03C8-20102440CA7C}.Release Frontend|Any CPU.Build.0 = Release|Any CPU
+ {4FADCDA4-2A63-BF3E-03C8-20102440CA7C}.Release R21|Any CPU.ActiveCfg = Release|Any CPU
+ {4FADCDA4-2A63-BF3E-03C8-20102440CA7C}.Release R22|Any CPU.ActiveCfg = Release|Any CPU
+ {4FADCDA4-2A63-BF3E-03C8-20102440CA7C}.Release R23|Any CPU.ActiveCfg = Release|Any CPU
+ {4FADCDA4-2A63-BF3E-03C8-20102440CA7C}.Release R24|Any CPU.ActiveCfg = Release|Any CPU
+ {4FADCDA4-2A63-BF3E-03C8-20102440CA7C}.Release R25|Any CPU.ActiveCfg = Release|Any CPU
+ {DED9FF57-E78C-F4C2-9CFD-368819DFE4C1}.Debug Engine|Any CPU.ActiveCfg = Debug|Any CPU
+ {DED9FF57-E78C-F4C2-9CFD-368819DFE4C1}.Debug Frontend|Any CPU.ActiveCfg = Debug|Any CPU
+ {DED9FF57-E78C-F4C2-9CFD-368819DFE4C1}.Debug Frontend|Any CPU.Build.0 = Debug|Any CPU
+ {DED9FF57-E78C-F4C2-9CFD-368819DFE4C1}.Debug R21|Any CPU.ActiveCfg = Debug|Any CPU
+ {DED9FF57-E78C-F4C2-9CFD-368819DFE4C1}.Debug R21|Any CPU.Build.0 = Debug|Any CPU
+ {DED9FF57-E78C-F4C2-9CFD-368819DFE4C1}.Debug R22|Any CPU.ActiveCfg = Debug|Any CPU
+ {DED9FF57-E78C-F4C2-9CFD-368819DFE4C1}.Debug R22|Any CPU.Build.0 = Debug|Any CPU
+ {DED9FF57-E78C-F4C2-9CFD-368819DFE4C1}.Debug R23|Any CPU.ActiveCfg = Debug|Any CPU
+ {DED9FF57-E78C-F4C2-9CFD-368819DFE4C1}.Debug R23|Any CPU.Build.0 = Debug|Any CPU
+ {DED9FF57-E78C-F4C2-9CFD-368819DFE4C1}.Debug R24|Any CPU.ActiveCfg = Debug|Any CPU
+ {DED9FF57-E78C-F4C2-9CFD-368819DFE4C1}.Debug R24|Any CPU.Build.0 = Debug|Any CPU
+ {DED9FF57-E78C-F4C2-9CFD-368819DFE4C1}.Debug R25|Any CPU.ActiveCfg = Debug|Any CPU
+ {DED9FF57-E78C-F4C2-9CFD-368819DFE4C1}.Debug R25|Any CPU.Build.0 = Debug|Any CPU
+ {DED9FF57-E78C-F4C2-9CFD-368819DFE4C1}.Release Build|Any CPU.ActiveCfg = Release|Any CPU
+ {DED9FF57-E78C-F4C2-9CFD-368819DFE4C1}.Release Engine|Any CPU.ActiveCfg = Release|Any CPU
+ {DED9FF57-E78C-F4C2-9CFD-368819DFE4C1}.Release Frontend|Any CPU.ActiveCfg = Release|Any CPU
+ {DED9FF57-E78C-F4C2-9CFD-368819DFE4C1}.Release Frontend|Any CPU.Build.0 = Release|Any CPU
+ {DED9FF57-E78C-F4C2-9CFD-368819DFE4C1}.Release R21|Any CPU.ActiveCfg = Release|Any CPU
+ {DED9FF57-E78C-F4C2-9CFD-368819DFE4C1}.Release R21|Any CPU.Build.0 = Release|Any CPU
+ {DED9FF57-E78C-F4C2-9CFD-368819DFE4C1}.Release R22|Any CPU.ActiveCfg = Release|Any CPU
+ {DED9FF57-E78C-F4C2-9CFD-368819DFE4C1}.Release R22|Any CPU.Build.0 = Release|Any CPU
+ {DED9FF57-E78C-F4C2-9CFD-368819DFE4C1}.Release R23|Any CPU.ActiveCfg = Release|Any CPU
+ {DED9FF57-E78C-F4C2-9CFD-368819DFE4C1}.Release R23|Any CPU.Build.0 = Release|Any CPU
+ {DED9FF57-E78C-F4C2-9CFD-368819DFE4C1}.Release R24|Any CPU.ActiveCfg = Release|Any CPU
+ {DED9FF57-E78C-F4C2-9CFD-368819DFE4C1}.Release R24|Any CPU.Build.0 = Release|Any CPU
+ {DED9FF57-E78C-F4C2-9CFD-368819DFE4C1}.Release R25|Any CPU.ActiveCfg = Release|Any CPU
+ {DED9FF57-E78C-F4C2-9CFD-368819DFE4C1}.Release R25|Any CPU.Build.0 = Release|Any CPU
+ {897CB786-AC5A-18EE-3091-7E14935796D5}.Debug Engine|Any CPU.ActiveCfg = Debug R25|Any CPU
+ {897CB786-AC5A-18EE-3091-7E14935796D5}.Debug Frontend|Any CPU.ActiveCfg = Debug R25|Any CPU
+ {897CB786-AC5A-18EE-3091-7E14935796D5}.Debug R21|Any CPU.ActiveCfg = Debug R21|Any CPU
+ {897CB786-AC5A-18EE-3091-7E14935796D5}.Debug R22|Any CPU.ActiveCfg = Debug R22|Any CPU
+ {897CB786-AC5A-18EE-3091-7E14935796D5}.Debug R23|Any CPU.ActiveCfg = Debug R23|Any CPU
+ {897CB786-AC5A-18EE-3091-7E14935796D5}.Debug R24|Any CPU.ActiveCfg = Debug R24|Any CPU
+ {897CB786-AC5A-18EE-3091-7E14935796D5}.Debug R25|Any CPU.ActiveCfg = Debug R25|Any CPU
+ {897CB786-AC5A-18EE-3091-7E14935796D5}.Release Build|Any CPU.ActiveCfg = Release R25|Any CPU
+ {897CB786-AC5A-18EE-3091-7E14935796D5}.Release Engine|Any CPU.ActiveCfg = Release R24|Any CPU
+ {897CB786-AC5A-18EE-3091-7E14935796D5}.Release Frontend|Any CPU.ActiveCfg = Release R25|Any CPU
+ {897CB786-AC5A-18EE-3091-7E14935796D5}.Release R21|Any CPU.ActiveCfg = Release R21|Any CPU
+ {897CB786-AC5A-18EE-3091-7E14935796D5}.Release R22|Any CPU.ActiveCfg = Release R22|Any CPU
+ {897CB786-AC5A-18EE-3091-7E14935796D5}.Release R23|Any CPU.ActiveCfg = Release R23|Any CPU
+ {897CB786-AC5A-18EE-3091-7E14935796D5}.Release R24|Any CPU.ActiveCfg = Release R24|Any CPU
+ {897CB786-AC5A-18EE-3091-7E14935796D5}.Release R25|Any CPU.ActiveCfg = Release R25|Any CPU
+ {7B7CA95C-9873-5ADB-DBCF-B46046742163}.Debug Engine|Any CPU.ActiveCfg = Debug R25|Any CPU
+ {7B7CA95C-9873-5ADB-DBCF-B46046742163}.Debug Frontend|Any CPU.ActiveCfg = Debug R25|Any CPU
+ {7B7CA95C-9873-5ADB-DBCF-B46046742163}.Debug R21|Any CPU.ActiveCfg = Debug R21|Any CPU
+ {7B7CA95C-9873-5ADB-DBCF-B46046742163}.Debug R21|Any CPU.Build.0 = Debug R21|Any CPU
+ {7B7CA95C-9873-5ADB-DBCF-B46046742163}.Debug R22|Any CPU.ActiveCfg = Debug R22|Any CPU
+ {7B7CA95C-9873-5ADB-DBCF-B46046742163}.Debug R22|Any CPU.Build.0 = Debug R22|Any CPU
+ {7B7CA95C-9873-5ADB-DBCF-B46046742163}.Debug R23|Any CPU.ActiveCfg = Debug R23|Any CPU
+ {7B7CA95C-9873-5ADB-DBCF-B46046742163}.Debug R23|Any CPU.Build.0 = Debug R23|Any CPU
+ {7B7CA95C-9873-5ADB-DBCF-B46046742163}.Debug R24|Any CPU.ActiveCfg = Debug R24|Any CPU
+ {7B7CA95C-9873-5ADB-DBCF-B46046742163}.Debug R24|Any CPU.Build.0 = Debug R24|Any CPU
+ {7B7CA95C-9873-5ADB-DBCF-B46046742163}.Debug R25|Any CPU.ActiveCfg = Debug R25|Any CPU
+ {7B7CA95C-9873-5ADB-DBCF-B46046742163}.Debug R25|Any CPU.Build.0 = Debug R25|Any CPU
+ {7B7CA95C-9873-5ADB-DBCF-B46046742163}.Release Build|Any CPU.ActiveCfg = Release R25|Any CPU
+ {7B7CA95C-9873-5ADB-DBCF-B46046742163}.Release Engine|Any CPU.ActiveCfg = Release R25|Any CPU
+ {7B7CA95C-9873-5ADB-DBCF-B46046742163}.Release Frontend|Any CPU.ActiveCfg = Release R25|Any CPU
+ {7B7CA95C-9873-5ADB-DBCF-B46046742163}.Release R21|Any CPU.ActiveCfg = Release R21|Any CPU
+ {7B7CA95C-9873-5ADB-DBCF-B46046742163}.Release R21|Any CPU.Build.0 = Release R21|Any CPU
+ {7B7CA95C-9873-5ADB-DBCF-B46046742163}.Release R22|Any CPU.ActiveCfg = Release R22|Any CPU
+ {7B7CA95C-9873-5ADB-DBCF-B46046742163}.Release R22|Any CPU.Build.0 = Release R22|Any CPU
+ {7B7CA95C-9873-5ADB-DBCF-B46046742163}.Release R23|Any CPU.ActiveCfg = Release R23|Any CPU
+ {7B7CA95C-9873-5ADB-DBCF-B46046742163}.Release R23|Any CPU.Build.0 = Release R23|Any CPU
+ {7B7CA95C-9873-5ADB-DBCF-B46046742163}.Release R24|Any CPU.ActiveCfg = Release R24|Any CPU
+ {7B7CA95C-9873-5ADB-DBCF-B46046742163}.Release R24|Any CPU.Build.0 = Release R24|Any CPU
+ {7B7CA95C-9873-5ADB-DBCF-B46046742163}.Release R25|Any CPU.ActiveCfg = Release R25|Any CPU
+ {7B7CA95C-9873-5ADB-DBCF-B46046742163}.Release R25|Any CPU.Build.0 = Release R25|Any CPU
+ {280FCE1F-F215-F89A-D710-1D9770067692}.Debug Engine|Any CPU.ActiveCfg = Debug|Any CPU
+ {280FCE1F-F215-F89A-D710-1D9770067692}.Debug Frontend|Any CPU.ActiveCfg = Debug|Any CPU
+ {280FCE1F-F215-F89A-D710-1D9770067692}.Debug R21|Any CPU.ActiveCfg = Debug|Any CPU
+ {280FCE1F-F215-F89A-D710-1D9770067692}.Debug R22|Any CPU.ActiveCfg = Debug|Any CPU
+ {280FCE1F-F215-F89A-D710-1D9770067692}.Debug R23|Any CPU.ActiveCfg = Debug|Any CPU
+ {280FCE1F-F215-F89A-D710-1D9770067692}.Debug R24|Any CPU.ActiveCfg = Debug|Any CPU
+ {280FCE1F-F215-F89A-D710-1D9770067692}.Debug R25|Any CPU.ActiveCfg = Debug|Any CPU
+ {280FCE1F-F215-F89A-D710-1D9770067692}.Release Build|Any CPU.ActiveCfg = Release|Any CPU
+ {280FCE1F-F215-F89A-D710-1D9770067692}.Release Engine|Any CPU.ActiveCfg = Release|Any CPU
+ {280FCE1F-F215-F89A-D710-1D9770067692}.Release Engine|Any CPU.Build.0 = Release|Any CPU
+ {280FCE1F-F215-F89A-D710-1D9770067692}.Release Frontend|Any CPU.ActiveCfg = Release|Any CPU
+ {280FCE1F-F215-F89A-D710-1D9770067692}.Release R21|Any CPU.ActiveCfg = Release|Any CPU
+ {280FCE1F-F215-F89A-D710-1D9770067692}.Release R22|Any CPU.ActiveCfg = Release|Any CPU
+ {280FCE1F-F215-F89A-D710-1D9770067692}.Release R23|Any CPU.ActiveCfg = Release|Any CPU
+ {280FCE1F-F215-F89A-D710-1D9770067692}.Release R24|Any CPU.ActiveCfg = Release|Any CPU
+ {280FCE1F-F215-F89A-D710-1D9770067692}.Release R25|Any CPU.ActiveCfg = Release|Any CPU
+ {E8D6B99E-2C38-C524-525B-EF812BB3926C}.Debug Engine|Any CPU.ActiveCfg = Debug|Any CPU
+ {E8D6B99E-2C38-C524-525B-EF812BB3926C}.Debug Engine|Any CPU.Build.0 = Debug|Any CPU
+ {E8D6B99E-2C38-C524-525B-EF812BB3926C}.Debug Frontend|Any CPU.ActiveCfg = Debug|Any CPU
+ {E8D6B99E-2C38-C524-525B-EF812BB3926C}.Debug R21|Any CPU.ActiveCfg = Debug|Any CPU
+ {E8D6B99E-2C38-C524-525B-EF812BB3926C}.Debug R22|Any CPU.ActiveCfg = Debug|Any CPU
+ {E8D6B99E-2C38-C524-525B-EF812BB3926C}.Debug R23|Any CPU.ActiveCfg = Debug|Any CPU
+ {E8D6B99E-2C38-C524-525B-EF812BB3926C}.Debug R24|Any CPU.ActiveCfg = Debug|Any CPU
+ {E8D6B99E-2C38-C524-525B-EF812BB3926C}.Debug R25|Any CPU.ActiveCfg = Debug|Any CPU
+ {E8D6B99E-2C38-C524-525B-EF812BB3926C}.Release Build|Any CPU.ActiveCfg = Release|Any CPU
+ {E8D6B99E-2C38-C524-525B-EF812BB3926C}.Release Engine|Any CPU.ActiveCfg = Release|Any CPU
+ {E8D6B99E-2C38-C524-525B-EF812BB3926C}.Release Engine|Any CPU.Build.0 = Release|Any CPU
+ {E8D6B99E-2C38-C524-525B-EF812BB3926C}.Release Frontend|Any CPU.ActiveCfg = Release|Any CPU
+ {E8D6B99E-2C38-C524-525B-EF812BB3926C}.Release R21|Any CPU.ActiveCfg = Release|Any CPU
+ {E8D6B99E-2C38-C524-525B-EF812BB3926C}.Release R22|Any CPU.ActiveCfg = Release|Any CPU
+ {E8D6B99E-2C38-C524-525B-EF812BB3926C}.Release R23|Any CPU.ActiveCfg = Release|Any CPU
+ {E8D6B99E-2C38-C524-525B-EF812BB3926C}.Release R24|Any CPU.ActiveCfg = Release|Any CPU
+ {E8D6B99E-2C38-C524-525B-EF812BB3926C}.Release R25|Any CPU.ActiveCfg = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {7DF908B3-503D-51C2-3675-626DC2895B77} = {BE4A8008-4A00-8C08-26C2-BD576741EBBE}
+ {0B3682BA-1074-598D-D8A6-04BB7116F36C} = {BE4A8008-4A00-8C08-26C2-BD576741EBBE}
+ {C075FD80-58C0-8D06-69F2-853E4D51AFE8} = {D585295F-7994-3649-1065-7AA403C41681}
+ {BD145DF8-35DF-8587-A26B-968A550D1D06} = {D585295F-7994-3649-1065-7AA403C41681}
+ {B687BEC0-11DB-7690-478A-444D8D9FCE25} = {CE609670-85B9-0D16-FB54-ED063D5D8A6D}
+ {925534FD-C38B-5571-23B0-62982B24DC6C} = {CE609670-85B9-0D16-FB54-ED063D5D8A6D}
+ {4FADCDA4-2A63-BF3E-03C8-20102440CA7C} = {CE609670-85B9-0D16-FB54-ED063D5D8A6D}
+ {DED9FF57-E78C-F4C2-9CFD-368819DFE4C1} = {CE609670-85B9-0D16-FB54-ED063D5D8A6D}
+ {897CB786-AC5A-18EE-3091-7E14935796D5} = {D64246B1-3E22-9D95-23C6-14262B074D58}
+ {7B7CA95C-9873-5ADB-DBCF-B46046742163} = {D64246B1-3E22-9D95-23C6-14262B074D58}
+ {280FCE1F-F215-F89A-D710-1D9770067692} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
+ {E8D6B99E-2C38-C524-525B-EF812BB3926C} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
+ EndGlobalSection
+EndGlobal
diff --git a/RevitLookup.slnx b/RevitLookup.slnx
new file mode 100644
index 000000000..d084eeff0
--- /dev/null
+++ b/RevitLookup.slnx
@@ -0,0 +1,265 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/RevitLookup.slnx.DotSettings b/RevitLookup.slnx.DotSettings
new file mode 100644
index 000000000..f45b7570c
--- /dev/null
+++ b/RevitLookup.slnx.DotSettings
@@ -0,0 +1,5 @@
+
+ True
+ True
+ True
+ True
\ No newline at end of file
diff --git a/build/Build.Clean.cs b/build/Build.Clean.cs
index 9e78c80cf..70a12831b 100644
--- a/build/Build.Clean.cs
+++ b/build/Build.Clean.cs
@@ -8,12 +8,12 @@ sealed partial class Build
.Executes(() =>
{
CleanDirectory(ArtifactsDirectory);
- foreach (var project in Solution.AllProjects.Where(project => project != Solution.Build))
+ foreach (var project in Solution.AllProjects.Where(project => project != Solution.Automation.Build))
{
CleanDirectory(project.Directory / "bin");
CleanDirectory(project.Directory / "obj");
}
-
+
foreach (var configuration in GlobBuildConfigurations())
DotNetClean(settings => settings
.SetConfiguration(configuration)
diff --git a/build/Build.Configuration.cs b/build/Build.Configuration.cs
index 7b505efac..c69924974 100644
--- a/build/Build.Configuration.cs
+++ b/build/Build.Configuration.cs
@@ -13,7 +13,7 @@ protected override void OnBuildInitialized()
InstallersMap = new()
{
- { Solution.Installer, Solution.RevitLookup }
+ { Solution.Automation.Installer, Solution.Revit.RevitLookup }
};
VersionMap = new()
diff --git a/build/Build.cs b/build/Build.cs
index d0aab9f00..def828e25 100644
--- a/build/Build.cs
+++ b/build/Build.cs
@@ -1,4 +1,3 @@
-using Nuke.Common;
using Nuke.Common.Git;
using Nuke.Common.ProjectModel;
diff --git a/build/Build.csproj b/build/Build.csproj
index 55d9359a0..bd31d8f84 100644
--- a/build/Build.csproj
+++ b/build/Build.csproj
@@ -2,19 +2,16 @@
Exe
+ disable
CS0649;CS0169
- latest
- true
- net8.0
+ net9.0
..
..
1
- Release;Debug
- AnyCPU
-
+
diff --git a/global.json b/global.json
index 75a80e90b..894573b73 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,7 @@
{
"sdk": {
- "version": "8.0.0",
- "rollForward": "latestMinor"
+ "version": "9.0.0",
+ "rollForward": "latestMinor",
+ "allowPrerelease": true
}
}
\ No newline at end of file
diff --git a/doc/Images/ja_1.png b/history/Images/ja_1.png
similarity index 100%
rename from doc/Images/ja_1.png
rename to history/Images/ja_1.png
diff --git a/doc/Images/ja_2.png b/history/Images/ja_2.png
similarity index 100%
rename from doc/Images/ja_2.png
rename to history/Images/ja_2.png
diff --git a/doc/Images/ja_3.png b/history/Images/ja_3.png
similarity index 100%
rename from doc/Images/ja_3.png
rename to history/Images/ja_3.png
diff --git a/doc/Images/ja_4.png b/history/Images/ja_4.png
similarity index 100%
rename from doc/Images/ja_4.png
rename to history/Images/ja_4.png
diff --git a/doc/Images/ja_5.png b/history/Images/ja_5.png
similarity index 100%
rename from doc/Images/ja_5.png
rename to history/Images/ja_5.png
diff --git a/doc/Images/ja_6.png b/history/Images/ja_6.png
similarity index 100%
rename from doc/Images/ja_6.png
rename to history/Images/ja_6.png
diff --git a/doc/Readme_2005-05-11.doc b/history/Readme_2005-05-11.doc
similarity index 100%
rename from doc/Readme_2005-05-11.doc
rename to history/Readme_2005-05-11.doc
diff --git a/doc/Readme_2005-05-11.md b/history/Readme_2005-05-11.md
similarity index 98%
rename from doc/Readme_2005-05-11.md
rename to history/Readme_2005-05-11.md
index 88db5a3e2..d205213ba 100644
--- a/doc/Readme_2005-05-11.md
+++ b/history/Readme_2005-05-11.md
@@ -1,120 +1,120 @@
-# Revit Lookup (Formerly known as RvtMgdDbg)
-
-Jim Awe
- Autodesk, Inc.
- 05/11/2005
-
-
-
-
-Warning, this document is currently obsolete.
-
-08808006 [Revit SDK documentation outdated...]
-
-Contains outdated or non-functional information regarding creating the 'RevitLookup.addin' file and the associated file which belong in the addins folder. (If you cut and paste the suggested text as from RevitLookup.doc to create RevitLookup.addin it will fail when Revit starts)
-
-In 2012 SDK, there is a compiled DLL file, and a working Addin file.
-
-In the 2013 SDK, there is no compiled DLL file (or instructions on compiling DLL), and there is no Addin file.
-
-In the 2014 SDK there is no compiled DLL file (or instructions on compiling DLL), but there is an Addin file.
-
-In all three cases, the date listed for the Documentation (Word Doc) is 05/11/2005, and much of the information is out of date.
-
-
-
-
-Revit Lookup is a program designed with several goals:
-
-- To provide a comprehensive test of the Managed API of Revit
-- To provide sample code and utility classes for 3rd Party developers
-- To provide “scaffolding” for quick tests of issues when they arise
-- To aid my own learning experience on the Revit API
-
-Currently, the following commands exist (all accessed from the “External Tools” menu):
-
-
-
-## Hello World...
-
-The classic bare-bones test. Just brings up an Alert box to show that the connection to the external module is working.
-
-## Snoop DB...
-
-
-
-This command allows you to browse all of the Elements within the current document and view their exposed properties. There has to be code written to extract all the individual properties of an object. As a result, when new classes or methods are added to the system, they will not be visible until code is explicitly written to display them. Because, the properties of an object are obtained in a top-down manner, all object types will at least be able to display common base class properties, even when newly added.
-
-When an item in the data list appears in bold typeface, it means that there is “Drill down” information. Click on that row and a nested Form (Dialog Box) will bring up more detailed information about that data item. For instance, clicking on any item that lists a ParameterSet will bring up a nested Form displaying the contents of the set.
-
-NOTE: when “Drilling down”, the Forms will stack on top of each other. You could keep drilling down a long way if you aren’t paying attention. It is up to you to make sure you don’t get lost in all of the stacked up Forms.
-
-The DrillDown information on a Class Separator (the light blue lines), will allow you to view information about the given class.
-
-
-
-Also, in the left-hand pane, you can right-click on a particular object and get additional information.
-
-
-
-Choosing “Browse Using Reflection...” will bring up a Generic PropertyGrid Form that uses .NET Reflection to browse all the properties. Here, there is no code written specifically in Revit Lookup to retrieve, format, and display the properties.
-
-
-
-NOTE: Because we have no control over how items are displayed, read-only values appear in Grey, and editable values appear in Bold. This form is not setup to correctly handle edits, so making changes is completely at your own risk.
-
-You can continue to browse generically using Reflection if you right-click on one of the items in the PropertyGrid. You get options to see either the Class info or the Object info.
-
-## Snoop Current Selection...
-
-Same as the above command, except that it starts you off Snooping only the elements that were part of the current selection set.
-
-## Snoop Application...
-
-Same as above, except that you start out at the Autodesk.Revit.Application object which is originally passed to the command.
-
-## Test Framework ...
-
-This command packages a set of individual tests into a single location. As new tests are written, they are plugged into this same Form without the need to define new external commands and hook them up to the system. The black “CLS” nodes of the tree represent which Class the tests concern. The grey check mark nodes represent an individual test. To run a test, simply choose a check mark node and press OK.
-
-As of now, there are only a few tests, but more will be added over time.
-
-
-
-## Set Up
-
-The Revit API now offers the ability to register API applications via an .addin manifest file.
-
-Manifest files will be read automatically by Revit when they are places in one of two locations on a user's system:
-
-- In a non-user specific location in "application data"
- - For Windows XP – C:\Documents and Settings\All Users\Application Data\Autodesk\Revit\Addins\2012
- - For Vista/Windows 7 – C:\ProgramData\Autodesk\Revit\Addins\2012
-- In a user specific location in "application data"
- - For Windows XP – C:\Documents and Settings\\Application Data\Autodesk\Revit\Addins\2012
- - For Vista/Windows 7 – C:\Users\\AppData\Roaming\Autodesk\Revit\Addins\2012
-
-All files named `.addin` in these locations will be read and processed by Revit during startup.
-
-The content of RevitLookup.addin:
-
-```
-
-
-
- RevitLookup
- RevitLookup.dll
- 6066CBF6-9034-41dd-A2A6-B3761C1362FF
- RevitLookup.App
-
-
-```
-
-## Known Issues
-
-As of this date, there are a few known issues:
-
-- Random exceptions when browsing Forms. There is a strange work-around in some of the Form constructors that seems to get around a lot of the exceptions, but there is still the occasional problem. Usually, they give the exception message “Overflow or underflow operation”. The exceptions are caught and you will be returned to the editor window and can continue.
-- The Snoop XML Form seems to be especially flaky. This was ported directly over from the AutoCAD version of MgdDbg, so I’m not sure what the deal is yet because it seems much more stable over there.
-- Trying to get the Analytical model of walls if they aren’t a load-bearing wall causes an Assert. Simply click on “Ignore All” and it won’t bother you anymore.
-- Trying to get the Room or analytical model of a FamilyInstance asserts that the TopologyId is incorrect. Simply click on “Ignore All” and it won’t bother you anymore.
+# Revit Lookup (Formerly known as RvtMgdDbg)
+
+Jim Awe
+ Autodesk, Inc.
+ 05/11/2005
+
+
+
+
+Warning, this document is currently obsolete.
+
+08808006 [Revit SDK documentation outdated...]
+
+Contains outdated or non-functional information regarding creating the 'RevitLookup.addin' file and the associated file which belong in the addins folder. (If you cut and paste the suggested text as from RevitLookup.doc to create RevitLookup.addin it will fail when Revit starts)
+
+In 2012 SDK, there is a compiled DLL file, and a working Addin file.
+
+In the 2013 SDK, there is no compiled DLL file (or instructions on compiling DLL), and there is no Addin file.
+
+In the 2014 SDK there is no compiled DLL file (or instructions on compiling DLL), but there is an Addin file.
+
+In all three cases, the date listed for the Documentation (Word Doc) is 05/11/2005, and much of the information is out of date.
+
+
+
+
+Revit Lookup is a program designed with several goals:
+
+- To provide a comprehensive test of the Managed API of Revit
+- To provide sample code and utility classes for 3rd Party developers
+- To provide “scaffolding” for quick tests of issues when they arise
+- To aid my own learning experience on the Revit API
+
+Currently, the following commands exist (all accessed from the “External Tools” menu):
+
+
+
+## Hello World...
+
+The classic bare-bones test. Just brings up an Alert box to show that the connection to the external module is working.
+
+## Snoop DB...
+
+
+
+This command allows you to browse all of the Elements within the current document and view their exposed properties. There has to be code written to extract all the individual properties of an object. As a result, when new classes or methods are added to the system, they will not be visible until code is explicitly written to display them. Because, the properties of an object are obtained in a top-down manner, all object types will at least be able to display common base class properties, even when newly added.
+
+When an item in the data list appears in bold typeface, it means that there is “Drill down” information. Click on that row and a nested Form (Dialog Box) will bring up more detailed information about that data item. For instance, clicking on any item that lists a ParameterSet will bring up a nested Form displaying the contents of the set.
+
+NOTE: when “Drilling down”, the Forms will stack on top of each other. You could keep drilling down a long way if you aren’t paying attention. It is up to you to make sure you don’t get lost in all of the stacked up Forms.
+
+The DrillDown information on a Class Separator (the light blue lines), will allow you to view information about the given class.
+
+
+
+Also, in the left-hand pane, you can right-click on a particular object and get additional information.
+
+
+
+Choosing “Browse Using Reflection...” will bring up a Generic PropertyGrid Form that uses .NET Reflection to browse all the properties. Here, there is no code written specifically in Revit Lookup to retrieve, format, and display the properties.
+
+
+
+NOTE: Because we have no control over how items are displayed, read-only values appear in Grey, and editable values appear in Bold. This form is not setup to correctly handle edits, so making changes is completely at your own risk.
+
+You can continue to browse generically using Reflection if you right-click on one of the items in the PropertyGrid. You get options to see either the Class info or the Object info.
+
+## Snoop Current Selection...
+
+Same as the above command, except that it starts you off Snooping only the elements that were part of the current selection set.
+
+## Snoop Application...
+
+Same as above, except that you start out at the Autodesk.Revit.Application object which is originally passed to the command.
+
+## Test Framework ...
+
+This command packages a set of individual tests into a single location. As new tests are written, they are plugged into this same Form without the need to define new external commands and hook them up to the system. The black “CLS” nodes of the tree represent which Class the tests concern. The grey check mark nodes represent an individual test. To run a test, simply choose a check mark node and press OK.
+
+As of now, there are only a few tests, but more will be added over time.
+
+
+
+## Set Up
+
+The Revit API now offers the ability to register API applications via an .addin manifest file.
+
+Manifest files will be read automatically by Revit when they are places in one of two locations on a user's system:
+
+- In a non-user specific location in "application data"
+ - For Windows XP – C:\Documents and Settings\All Users\Application Data\Autodesk\Revit\Addins\2012
+ - For Vista/Windows 7 – C:\ProgramData\Autodesk\Revit\Addins\2012
+- In a user specific location in "application data"
+ - For Windows XP – C:\Documents and Settings\\Application Data\Autodesk\Revit\Addins\2012
+ - For Vista/Windows 7 – C:\Users\\AppData\Roaming\Autodesk\Revit\Addins\2012
+
+All files named `.addin` in these locations will be read and processed by Revit during startup.
+
+The content of RevitLookup.addin:
+
+```
+
+
+
+ RevitLookup
+ RevitLookup.dll
+ 6066CBF6-9034-41dd-A2A6-B3761C1362FF
+ RevitLookup.App
+
+
+```
+
+## Known Issues
+
+As of this date, there are a few known issues:
+
+- Random exceptions when browsing Forms. There is a strange work-around in some of the Form constructors that seems to get around a lot of the exceptions, but there is still the occasional problem. Usually, they give the exception message “Overflow or underflow operation”. The exceptions are caught and you will be returned to the editor window and can continue.
+- The Snoop XML Form seems to be especially flaky. This was ported directly over from the AutoCAD version of MgdDbg, so I’m not sure what the deal is yet because it seems much more stable over there.
+- Trying to get the Analytical model of walls if they aren’t a load-bearing wall causes an Assert. Simply click on “Ignore All” and it won’t bother you anymore.
+- Trying to get the Room or analytical model of a FamilyInstance asserts that the TopologyId is incorrect. Simply click on “Ignore All” and it won’t bother you anymore.
diff --git a/doc/Wishlist.md b/history/Wishlist.md
similarity index 100%
rename from doc/Wishlist.md
rename to history/Wishlist.md
diff --git a/install/Installer.csproj b/install/Installer.csproj
index 6a6de95ff..f8995b453 100644
--- a/install/Installer.csproj
+++ b/install/Installer.csproj
@@ -1,13 +1,14 @@
+
Exe
- latest
- x64
net48
false
+
-
-
+
+
+
diff --git a/source/RevitLookup/.editorconfig b/source/.editorconfig
similarity index 100%
rename from source/RevitLookup/.editorconfig
rename to source/.editorconfig
diff --git a/source/Benchmarks/Benchmark.cs b/source/Benchmarks/Benchmark.cs
deleted file mode 100644
index f50e18ff9..000000000
--- a/source/Benchmarks/Benchmark.cs
+++ /dev/null
@@ -1,4 +0,0 @@
-using BenchmarkDotNet.Running;
-using Benchmarks;
-
-BenchmarkRunner.Run();
\ No newline at end of file
diff --git a/source/Benchmarks/Benchmarks.csproj b/source/Benchmarks/Benchmarks.csproj
deleted file mode 100644
index 5321527a1..000000000
--- a/source/Benchmarks/Benchmarks.csproj
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
- Exe
- latest
- enable
- net48
-
-
-
-
-
diff --git a/source/Benchmarks/ClosureBenchmark.cs b/source/Benchmarks/ClosureBenchmark.cs
deleted file mode 100644
index bb1e03b1b..000000000
--- a/source/Benchmarks/ClosureBenchmark.cs
+++ /dev/null
@@ -1,163 +0,0 @@
-// Copyright 2003-2024 by Autodesk, Inc.
-//
-// Permission to use, copy, modify, and distribute this software in
-// object code form for any purpose and without fee is hereby granted,
-// provided that the above copyright notice appears in all copies and
-// that both that copyright notice and the limited warranty and
-// restricted rights notice below appear in all supporting
-// documentation.
-//
-// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
-// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
-// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
-// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
-// UNINTERRUPTED OR ERROR FREE.
-//
-// Use, duplication, or disclosure by the U.S. Government is subject to
-// restrictions set forth in FAR 52.227-19 (Commercial Computer
-// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
-// (Rights in Technical Data and Computer Software), as applicable.
-
-using BenchmarkDotNet.Attributes;
-
-namespace Benchmarks;
-
-[ShortRunJob]
-[MemoryDiagnoser]
-public class ClosureBenchmark
-{
- [GlobalSetup]
- public void Setup()
- {
- Manager = new ExtensionManager("context");
- }
-
- [Params("Text", 12d)] public object Parameter { get; set; }
- public ExtensionManager Manager { get; set; }
-
- [Benchmark]
- public void ClosureMethod()
- {
- Manager.Register("Extension", () => Parameter.ToString());
- }
-
- [Benchmark]
- public void NonClosureMethod()
- {
- Manager.Register("Extension", Parameter, extension =>
- {
- extension.Result = extension.Value.ToString();
- });
- }
-}
-
-public sealed class ExtensionManager
-{
- private readonly string _context;
-
- public ExtensionManager(string context)
- {
- _context = context;
- }
-
- public List Descriptors { get; set; } = new();
-
- public void Register(string name, T value, Action> extension)
- {
- var descriptorExtension = new DescriptorExtension
- {
- Value = value,
- Context = _context
- };
-
- var descriptor = new ObjectDescriptor
- {
- Label = name
- };
-
- try
- {
- extension.Invoke(descriptorExtension);
- descriptor.Value = new SnoopableObject(_context, descriptorExtension.Result);
- }
- catch (Exception exception)
- {
- descriptor.Value = new SnoopableObject(_context, exception);
- }
-
- Descriptors.Add(descriptor);
- }
-
- public void Register(string name, Func result)
- {
- var descriptor = new ObjectDescriptor
- {
- Label = name
- };
-
- try
- {
- descriptor.Value = new SnoopableObject(_context, result());
- }
- catch (Exception exception)
- {
- descriptor.Value = new SnoopableObject(_context, exception);
- }
-
- Descriptors.Add(descriptor);
- }
-}
-
-public sealed class DescriptorExtension
-{
- public string Context { get; set; }
- public T Value { get; set; }
- public object Result { get; set; }
-}
-
-public sealed class ObjectDescriptor : Descriptor
-{
- public ObjectDescriptor()
- {
- }
-
- public ObjectDescriptor(object value)
- {
- Label = value.ToString();
- }
-}
-
-public abstract class Descriptor : IComparable, IComparable
-{
- public string Type { get; set; }
- public string Label { get; set; }
- public SnoopableObject Value { get; set; }
-
- public int CompareTo(object obj)
- {
- if (ReferenceEquals(null, obj)) return 1;
- if (ReferenceEquals(this, obj)) return 0;
- return obj is Descriptor other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(Descriptor)}");
- }
-
- public int CompareTo(Descriptor other)
- {
- if (ReferenceEquals(this, other)) return 0;
- if (ReferenceEquals(null, other)) return 1;
- var typeComparison = string.Compare(Type, other.Type, StringComparison.Ordinal);
- if (typeComparison != 0) return typeComparison;
- return string.Compare(Label, other.Label, StringComparison.Ordinal);
- }
-}
-
-public sealed class SnoopableObject
-{
- public SnoopableObject(string context, object obj)
- {
- Object = obj;
- Context = context;
- }
-
- public object Object { get; }
- public string Context { get; }
-}
\ No newline at end of file
diff --git a/source/RevitLookup/Core/Contracts/IDescriptorCollector.cs b/source/LookupEngine.Abstractions/Configuration/IDescriptorCollector.cs
similarity index 95%
rename from source/RevitLookup/Core/Contracts/IDescriptorCollector.cs
rename to source/LookupEngine.Abstractions/Configuration/IDescriptorCollector.cs
index c47ad5858..7183d0ff0 100644
--- a/source/RevitLookup/Core/Contracts/IDescriptorCollector.cs
+++ b/source/LookupEngine.Abstractions/Configuration/IDescriptorCollector.cs
@@ -18,7 +18,7 @@
// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
// (Rights in Technical Data and Computer Software), as applicable.
-namespace RevitLookup.Core.Contracts;
+namespace LookupEngine.Abstractions.Configuration;
///
/// Indicates that the descriptor can retrieve object members by reflection
diff --git a/source/RevitLookup/Core/Contracts/IDescriptorEnumerator.cs b/source/LookupEngine.Abstractions/Configuration/IDescriptorEnumerator.cs
similarity index 96%
rename from source/RevitLookup/Core/Contracts/IDescriptorEnumerator.cs
rename to source/LookupEngine.Abstractions/Configuration/IDescriptorEnumerator.cs
index 54d76f4ae..0349f203c 100644
--- a/source/RevitLookup/Core/Contracts/IDescriptorEnumerator.cs
+++ b/source/LookupEngine.Abstractions/Configuration/IDescriptorEnumerator.cs
@@ -20,7 +20,7 @@
using System.Collections;
-namespace RevitLookup.Core.Contracts;
+namespace LookupEngine.Abstractions.Configuration;
///
/// Indicates that the descriptor is handled as a collection of descriptors
diff --git a/source/RevitLookup/Core/Contracts/IDescriptorExtension.cs b/source/LookupEngine.Abstractions/Configuration/IDescriptorExtension.cs
similarity index 95%
rename from source/RevitLookup/Core/Contracts/IDescriptorExtension.cs
rename to source/LookupEngine.Abstractions/Configuration/IDescriptorExtension.cs
index dc44aa5d6..76aff48bd 100644
--- a/source/RevitLookup/Core/Contracts/IDescriptorExtension.cs
+++ b/source/LookupEngine.Abstractions/Configuration/IDescriptorExtension.cs
@@ -18,7 +18,7 @@
// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
// (Rights in Technical Data and Computer Software), as applicable.
-namespace RevitLookup.Core.Contracts;
+namespace LookupEngine.Abstractions.Configuration;
///
/// Indicates that the descriptor can interact with the UI and execute commands
diff --git a/source/LookupEngine.Abstractions/Configuration/IDescriptorExtension{T}.cs b/source/LookupEngine.Abstractions/Configuration/IDescriptorExtension{T}.cs
new file mode 100644
index 000000000..543c8aa88
--- /dev/null
+++ b/source/LookupEngine.Abstractions/Configuration/IDescriptorExtension{T}.cs
@@ -0,0 +1,29 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+namespace LookupEngine.Abstractions.Configuration;
+
+///
+/// Indicates that the descriptor can interact with the UI and execute commands
+///
+public interface IDescriptorExtension : IDescriptorCollector
+{
+ void RegisterExtensions(IExtensionManager manager);
+}
\ No newline at end of file
diff --git a/source/RevitLookup/Core/Contracts/IDescriptorRedirection.cs b/source/LookupEngine.Abstractions/Configuration/IDescriptorRedirector.cs
similarity index 87%
rename from source/RevitLookup/Core/Contracts/IDescriptorRedirection.cs
rename to source/LookupEngine.Abstractions/Configuration/IDescriptorRedirector.cs
index d603c242e..fce809dae 100644
--- a/source/RevitLookup/Core/Contracts/IDescriptorRedirection.cs
+++ b/source/LookupEngine.Abstractions/Configuration/IDescriptorRedirector.cs
@@ -18,12 +18,12 @@
// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
// (Rights in Technical Data and Computer Software), as applicable.
-namespace RevitLookup.Core.Contracts;
+namespace LookupEngine.Abstractions.Configuration;
///
/// Indicates that the object can be redirected to another
///
-public interface IDescriptorRedirection
+public interface IDescriptorRedirector
{
- bool TryRedirect(Document context, string target, out object output);
+ bool TryRedirect(string target, out object result);
}
\ No newline at end of file
diff --git a/source/LookupEngine.Abstractions/Configuration/IDescriptorRedirector{T}.cs b/source/LookupEngine.Abstractions/Configuration/IDescriptorRedirector{T}.cs
new file mode 100644
index 000000000..78d13e895
--- /dev/null
+++ b/source/LookupEngine.Abstractions/Configuration/IDescriptorRedirector{T}.cs
@@ -0,0 +1,29 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+namespace LookupEngine.Abstractions.Configuration;
+
+///
+/// Indicates that the object can be redirected to another
+///
+public interface IDescriptorRedirector
+{
+ bool TryRedirect(string target, TContext context, out object result);
+}
\ No newline at end of file
diff --git a/source/RevitLookup/Core/Contracts/IDescriptorResolver.cs b/source/LookupEngine.Abstractions/Configuration/IDescriptorResolver.cs
similarity index 87%
rename from source/RevitLookup/Core/Contracts/IDescriptorResolver.cs
rename to source/LookupEngine.Abstractions/Configuration/IDescriptorResolver.cs
index 93a0dc89a..71fa2dce8 100644
--- a/source/RevitLookup/Core/Contracts/IDescriptorResolver.cs
+++ b/source/LookupEngine.Abstractions/Configuration/IDescriptorResolver.cs
@@ -19,13 +19,14 @@
// (Rights in Technical Data and Computer Software), as applicable.
using System.Reflection;
+using LookupEngine.Abstractions.Decomposition;
-namespace RevitLookup.Core.Contracts;
+namespace LookupEngine.Abstractions.Configuration;
///
/// Indicates that the descriptor can decide to call methods/properties with parameters or override their values
///
public interface IDescriptorResolver : IDescriptorCollector
{
- Func Resolve(Document context, string target, ParameterInfo[] parameters);
+ Func? Resolve(string target, ParameterInfo[] parameters);
}
\ No newline at end of file
diff --git a/source/LookupEngine.Abstractions/Configuration/IDescriptorResolver{T}.cs b/source/LookupEngine.Abstractions/Configuration/IDescriptorResolver{T}.cs
new file mode 100644
index 000000000..cc59bcedf
--- /dev/null
+++ b/source/LookupEngine.Abstractions/Configuration/IDescriptorResolver{T}.cs
@@ -0,0 +1,32 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using System.Reflection;
+using LookupEngine.Abstractions.Decomposition;
+
+namespace LookupEngine.Abstractions.Configuration;
+
+///
+/// Indicates that the descriptor can decide to call methods/properties with parameters or override their values
+///
+public interface IDescriptorResolver : IDescriptorCollector
+{
+ Func? Resolve(string target, ParameterInfo[] parameters);
+}
\ No newline at end of file
diff --git a/source/RevitLookup/Core/Contracts/IExtensionManager.cs b/source/LookupEngine.Abstractions/Configuration/IExtensionManager.cs
similarity index 86%
rename from source/RevitLookup/Core/Contracts/IExtensionManager.cs
rename to source/LookupEngine.Abstractions/Configuration/IExtensionManager.cs
index f5ef913e7..d919fa036 100644
--- a/source/RevitLookup/Core/Contracts/IExtensionManager.cs
+++ b/source/LookupEngine.Abstractions/Configuration/IExtensionManager.cs
@@ -18,10 +18,11 @@
// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
// (Rights in Technical Data and Computer Software), as applicable.
-namespace RevitLookup.Core.Contracts;
+using LookupEngine.Abstractions.Decomposition;
+
+namespace LookupEngine.Abstractions.Configuration;
public interface IExtensionManager
{
- Document Context { get; }
- void Register(string methodName, Func context);
+ void Register(string name, Func extension);
}
\ No newline at end of file
diff --git a/source/RevitLookup.UI/Application.cs b/source/LookupEngine.Abstractions/Configuration/IExtensionManager{T}.cs
similarity index 81%
rename from source/RevitLookup.UI/Application.cs
rename to source/LookupEngine.Abstractions/Configuration/IExtensionManager{T}.cs
index be6ad1c56..22c910d09 100644
--- a/source/RevitLookup.UI/Application.cs
+++ b/source/LookupEngine.Abstractions/Configuration/IExtensionManager{T}.cs
@@ -18,12 +18,11 @@
// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
// (Rights in Technical Data and Computer Software), as applicable.
-using Wpf.Ui.Controls;
+using LookupEngine.Abstractions.Decomposition;
-namespace Wpf.Ui;
+namespace LookupEngine.Abstractions.Configuration;
-public static class Application
+public interface IExtensionManager
{
- public static FluentWindow MainWindow { get; set; }
- public static List Windows { get; set; } = new(8);
+ void Register(string name, Func extension);
}
\ No newline at end of file
diff --git a/source/RevitLookup/Views/Pages/SettingsPage.xaml.cs b/source/LookupEngine.Abstractions/Decomposition/Containers/Variant.cs
similarity index 72%
rename from source/RevitLookup/Views/Pages/SettingsPage.xaml.cs
rename to source/LookupEngine.Abstractions/Decomposition/Containers/Variant.cs
index 2a21f995e..c237918f1 100644
--- a/source/RevitLookup/Views/Pages/SettingsPage.xaml.cs
+++ b/source/LookupEngine.Abstractions/Decomposition/Containers/Variant.cs
@@ -18,19 +18,21 @@
// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
// (Rights in Technical Data and Computer Software), as applicable.
-using RevitLookup.ViewModels.Pages;
-using Wpf.Ui.Controls;
+namespace LookupEngine.Abstractions.Decomposition.Containers;
-namespace RevitLookup.Views.Pages;
-
-public sealed partial class SettingsPage : INavigableView
+internal sealed class Variant : IVariant
{
- public SettingsPage(SettingsViewModel viewModel)
+ public Variant(object value)
+ {
+ Value = value;
+ }
+
+ public Variant(object value, string description)
{
- ViewModel = viewModel;
- InitializeComponent();
- DataContext = this;
+ Value = value;
+ Description = description;
}
- public SettingsViewModel ViewModel { get; }
+ public object Value { get; }
+ public string? Description { get; }
}
\ No newline at end of file
diff --git a/source/LookupEngine.Abstractions/Decomposition/Containers/Variants{T}.cs b/source/LookupEngine.Abstractions/Decomposition/Containers/Variants{T}.cs
new file mode 100644
index 000000000..fb925fcb8
--- /dev/null
+++ b/source/LookupEngine.Abstractions/Decomposition/Containers/Variants{T}.cs
@@ -0,0 +1,94 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using System.Collections;
+
+namespace LookupEngine.Abstractions.Decomposition.Containers;
+
+///
+/// Represents a collection of variants
+///
+/// The type of the variants
+/// The initial variants capacity. Required for atomic performance optimizations
+internal sealed class Variants(int capacity) : IVariant, IVariantsCollection, IReadOnlyCollection
+{
+ private readonly List _items = new(capacity);
+
+ ///
+ /// Gets the number of variants
+ ///
+ public int Count => _items.Count;
+
+ public object Value => _items;
+ public string? Description => null;
+
+ ///
+ /// Adds a new variant
+ ///
+ /// The variant collection with a new value
+ public IVariantsCollection Add(T? result)
+ {
+ if (result is null) return this;
+ if (result is ICollection {Count: 0}) return this;
+
+ _items.Add(new Variant(result));
+
+ return this;
+ }
+
+ ///
+ /// Adds a new variant with description
+ ///
+ /// The variant collection with a new value
+ public IVariantsCollection Add(T? result, string description)
+ {
+ if (result is null) return this;
+ if (result is ICollection {Count: 0}) return this;
+
+ _items.Add(new Variant(result, description));
+
+ return this;
+ }
+
+ public IVariant Consume()
+ {
+ return this;
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return _items.GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return _items.GetEnumerator();
+ }
+
+ public object GetResult()
+ {
+ return this;
+ }
+
+ public string? GetMetadata()
+ {
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup/Core/Contracts/IVariants.cs b/source/LookupEngine.Abstractions/Decomposition/Descriptor.cs
similarity index 78%
rename from source/RevitLookup/Core/Contracts/IVariants.cs
rename to source/LookupEngine.Abstractions/Decomposition/Descriptor.cs
index 554efdf0d..ad776f4e1 100644
--- a/source/RevitLookup/Core/Contracts/IVariants.cs
+++ b/source/LookupEngine.Abstractions/Decomposition/Descriptor.cs
@@ -18,15 +18,15 @@
// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
// (Rights in Technical Data and Computer Software), as applicable.
-namespace RevitLookup.Core.Contracts;
+using System.Diagnostics;
+using JetBrains.Annotations;
-public interface IVariants : IReadOnlyCollection
-{
- Variant Single();
-}
+namespace LookupEngine.Abstractions.Decomposition;
-public interface IVariants : IVariants
+[PublicAPI]
+[DebuggerDisplay("Name = {Name}")]
+public abstract class Descriptor
{
- Variants Add(T result);
- Variants Add(T result, string description);
+ public string? Name { get; init; }
+ public string? Description { get; set; }
}
\ No newline at end of file
diff --git a/source/RevitLookup/Core/Objects/Variant.cs b/source/LookupEngine.Abstractions/Decomposition/IVariant.cs
similarity index 86%
rename from source/RevitLookup/Core/Objects/Variant.cs
rename to source/LookupEngine.Abstractions/Decomposition/IVariant.cs
index 8ffabc45d..11b49c8e8 100644
--- a/source/RevitLookup/Core/Objects/Variant.cs
+++ b/source/LookupEngine.Abstractions/Decomposition/IVariant.cs
@@ -18,10 +18,10 @@
// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
// (Rights in Technical Data and Computer Software), as applicable.
-namespace RevitLookup.Core.Objects;
+namespace LookupEngine.Abstractions.Decomposition;
-public sealed class Variant
+public interface IVariant
{
- public string Description { get; init; }
- public object Object { get; init; }
+ object Value { get; }
+ string? Description { get; }
}
\ No newline at end of file
diff --git a/source/LookupEngine.Abstractions/Decomposition/IVariantsCollection{T}.cs b/source/LookupEngine.Abstractions/Decomposition/IVariantsCollection{T}.cs
new file mode 100644
index 000000000..fa346acbc
--- /dev/null
+++ b/source/LookupEngine.Abstractions/Decomposition/IVariantsCollection{T}.cs
@@ -0,0 +1,8 @@
+namespace LookupEngine.Abstractions.Decomposition;
+
+public interface IVariantsCollection
+{
+ IVariantsCollection Add(T? result);
+ IVariantsCollection Add(T? result, string description);
+ IVariant Consume();
+}
\ No newline at end of file
diff --git a/source/LookupEngine.Abstractions/Decomposition/Variants.cs b/source/LookupEngine.Abstractions/Decomposition/Variants.cs
new file mode 100644
index 000000000..7c0d7c86f
--- /dev/null
+++ b/source/LookupEngine.Abstractions/Decomposition/Variants.cs
@@ -0,0 +1,46 @@
+using LookupEngine.Abstractions.Decomposition.Containers;
+
+namespace LookupEngine.Abstractions.Decomposition;
+
+///
+/// A factory for .
+///
+public static class Variants
+{
+ ///
+ /// Creates a variant collection with a single value
+ ///
+ /// A variant collection containing the specified value
+ public static IVariant Value(object value)
+ {
+ return new Variant(value);
+ }
+
+ public static IVariant Value(object value, string description)
+ {
+ return new Variant(value, description);
+ }
+
+ public static IVariantsCollection Values(int capacity)
+ {
+ return new Variants(capacity);
+ }
+
+ ///
+ /// Creates an empty variant collection
+ ///
+ /// An empty variant collection
+ /// An empty collection is returned when there are no solutions for a member
+ public static IVariant Empty()
+ {
+ return new Variants(0);
+ }
+
+ ///
+ /// A variant that disables the member calculation
+ ///
+ public static IVariant Disabled()
+ {
+ return new Variant(new InvalidOperationException("Member execution disabled"));
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup/Core/Enums/MemberAttributes.cs b/source/LookupEngine.Abstractions/Enums/MemberAttributes.cs
similarity index 96%
rename from source/RevitLookup/Core/Enums/MemberAttributes.cs
rename to source/LookupEngine.Abstractions/Enums/MemberAttributes.cs
index 6e1330050..65f0662b9 100644
--- a/source/RevitLookup/Core/Enums/MemberAttributes.cs
+++ b/source/LookupEngine.Abstractions/Enums/MemberAttributes.cs
@@ -18,13 +18,14 @@
// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
// (Rights in Technical Data and Computer Software), as applicable.
-namespace RevitLookup.Core.Enums;
+namespace LookupEngine.Abstractions.Enums;
[Flags]
public enum MemberAttributes
{
Private = 0b1,
Static = 0b10,
+
Field = 0b100,
Property = 0b1000,
Method = 0b10000,
diff --git a/source/LookupEngine.Abstractions/LookupEngine.Abstractions.csproj b/source/LookupEngine.Abstractions/LookupEngine.Abstractions.csproj
new file mode 100644
index 000000000..16a5e3f7f
--- /dev/null
+++ b/source/LookupEngine.Abstractions/LookupEngine.Abstractions.csproj
@@ -0,0 +1,11 @@
+
+
+
+ net48;net8.0-windows
+
+
+
+
+
+
+
diff --git a/source/LookupEngine.Abstractions/Metadata/DecomposedMember.cs b/source/LookupEngine.Abstractions/Metadata/DecomposedMember.cs
new file mode 100644
index 000000000..94af5535e
--- /dev/null
+++ b/source/LookupEngine.Abstractions/Metadata/DecomposedMember.cs
@@ -0,0 +1,20 @@
+using System.Diagnostics;
+using JetBrains.Annotations;
+using LookupEngine.Abstractions.Enums;
+
+// ReSharper disable once CheckNamespace
+namespace LookupEngine.Abstractions;
+
+[PublicAPI]
+[DebuggerDisplay("Name = {Name} Value = {Value.Name}")]
+public sealed class DecomposedMember
+{
+ public required int Depth { get; init; }
+ public required string Name { get; init; }
+ public required string DeclaringTypeName { get; init; }
+ public required string DeclaringTypeFullName { get; init; }
+ public double ComputationTime { get; init; }
+ public long AllocatedBytes { get; init; }
+ public MemberAttributes MemberAttributes { get; init; }
+ public required DecomposedValue Value { get; init; }
+}
\ No newline at end of file
diff --git a/source/LookupEngine.Abstractions/Metadata/DecomposedObject.cs b/source/LookupEngine.Abstractions/Metadata/DecomposedObject.cs
new file mode 100644
index 000000000..838c17cd3
--- /dev/null
+++ b/source/LookupEngine.Abstractions/Metadata/DecomposedObject.cs
@@ -0,0 +1,19 @@
+using System.Diagnostics;
+using JetBrains.Annotations;
+using LookupEngine.Abstractions.Decomposition;
+
+// ReSharper disable once CheckNamespace
+namespace LookupEngine.Abstractions;
+
+[PublicAPI]
+[DebuggerDisplay("Name = {Name} Value = {RawValue}")]
+public sealed class DecomposedObject
+{
+ public required object? RawValue { get; init; }
+ public required string Name { get; init; }
+ public required string TypeName { get; init; }
+ public required string TypeFullName { get; init; }
+ public string? Description { get; init; }
+ public Descriptor? Descriptor { get; init; }
+ public List Members { get; } = [];
+}
\ No newline at end of file
diff --git a/source/LookupEngine.Abstractions/Metadata/DecomposedValue.cs b/source/LookupEngine.Abstractions/Metadata/DecomposedValue.cs
new file mode 100644
index 000000000..110cd4f34
--- /dev/null
+++ b/source/LookupEngine.Abstractions/Metadata/DecomposedValue.cs
@@ -0,0 +1,18 @@
+using System.Diagnostics;
+using JetBrains.Annotations;
+using LookupEngine.Abstractions.Decomposition;
+
+// ReSharper disable once CheckNamespace
+namespace LookupEngine.Abstractions;
+
+[PublicAPI]
+[DebuggerDisplay("Name = {Name} Value = {RawValue}")]
+public sealed class DecomposedValue
+{
+ public required object? RawValue { get; init; }
+ public required string Name { get; init; }
+ public required string TypeName { get; init; }
+ public required string TypeFullName { get; init; }
+ public string? Description { get; init; }
+ public Descriptor? Descriptor { get; init; }
+}
\ No newline at end of file
diff --git a/source/RevitLookup/Core/ComponentModel/Descriptors/BoolDescriptor.cs b/source/LookupEngine/Descriptors/BooleanDescriptor.cs
similarity index 85%
rename from source/RevitLookup/Core/ComponentModel/Descriptors/BoolDescriptor.cs
rename to source/LookupEngine/Descriptors/BooleanDescriptor.cs
index 1bfd003d8..49989a4cb 100644
--- a/source/RevitLookup/Core/ComponentModel/Descriptors/BoolDescriptor.cs
+++ b/source/LookupEngine/Descriptors/BooleanDescriptor.cs
@@ -18,11 +18,13 @@
// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
// (Rights in Technical Data and Computer Software), as applicable.
-namespace RevitLookup.Core.ComponentModel.Descriptors;
+using LookupEngine.Abstractions.Decomposition;
-public sealed class BoolDescriptor : Descriptor
+namespace LookupEngine.Descriptors;
+
+public sealed class BooleanDescriptor : Descriptor
{
- public BoolDescriptor(bool value)
+ public BooleanDescriptor(bool value)
{
Name = value ? "True" : "False";
}
diff --git a/source/LookupEngine/Descriptors/EnumerableDescriptor.cs b/source/LookupEngine/Descriptors/EnumerableDescriptor.cs
new file mode 100644
index 000000000..9c217c90c
--- /dev/null
+++ b/source/LookupEngine/Descriptors/EnumerableDescriptor.cs
@@ -0,0 +1,43 @@
+using System.Collections;
+using System.Reflection;
+using LookupEngine.Abstractions.Configuration;
+using LookupEngine.Abstractions.Decomposition;
+
+namespace LookupEngine.Descriptors;
+
+public sealed class EnumerableDescriptor : Descriptor, IDescriptorEnumerator, IDescriptorResolver
+{
+ public EnumerableDescriptor(IEnumerable value)
+ {
+ Enumerator = value.GetEnumerator();
+
+ //Checking types to reduce memory allocation when creating an iterator and increase performance
+ IsEmpty = value switch
+ {
+ ICollection enumerable => enumerable.Count == 0,
+ _ => !Enumerator.MoveNext()
+ };
+
+ if (Enumerator is IDisposable disposable)
+ {
+ disposable.Dispose();
+ }
+ }
+
+ public IEnumerator Enumerator { get; }
+ public bool IsEmpty { get; }
+
+ public Func? Resolve(string target, ParameterInfo[] parameters)
+ {
+ return target switch
+ {
+ nameof(IEnumerable.GetEnumerator) => ResolveGetEnumerator,
+ _ => null
+ };
+
+ IVariant ResolveGetEnumerator()
+ {
+ return Variants.Empty();
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup/Core/ComponentModel/Descriptors/ExceptionDescriptor.cs b/source/LookupEngine/Descriptors/ExceptionDescriptor.cs
similarity index 94%
rename from source/RevitLookup/Core/ComponentModel/Descriptors/ExceptionDescriptor.cs
rename to source/LookupEngine/Descriptors/ExceptionDescriptor.cs
index 22e1ca84c..bbe478788 100644
--- a/source/RevitLookup/Core/ComponentModel/Descriptors/ExceptionDescriptor.cs
+++ b/source/LookupEngine/Descriptors/ExceptionDescriptor.cs
@@ -18,7 +18,9 @@
// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
// (Rights in Technical Data and Computer Software), as applicable.
-namespace RevitLookup.Core.ComponentModel.Descriptors;
+using LookupEngine.Abstractions.Decomposition;
+
+namespace LookupEngine.Descriptors;
public sealed class ExceptionDescriptor : Descriptor
{
diff --git a/source/RevitLookup/Core/ComponentModel/Descriptors/ObjectDescriptor.cs b/source/LookupEngine/Descriptors/ObjectDescriptor.cs
similarity index 85%
rename from source/RevitLookup/Core/ComponentModel/Descriptors/ObjectDescriptor.cs
rename to source/LookupEngine/Descriptors/ObjectDescriptor.cs
index 37fac2806..9607aea3f 100644
--- a/source/RevitLookup/Core/ComponentModel/Descriptors/ObjectDescriptor.cs
+++ b/source/LookupEngine/Descriptors/ObjectDescriptor.cs
@@ -18,16 +18,14 @@
// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
// (Rights in Technical Data and Computer Software), as applicable.
-namespace RevitLookup.Core.ComponentModel.Descriptors;
+using LookupEngine.Abstractions.Decomposition;
+
+namespace LookupEngine.Descriptors;
public sealed class ObjectDescriptor : Descriptor
{
- public ObjectDescriptor()
- {
- }
-
- public ObjectDescriptor(object value)
+ public ObjectDescriptor(object? value)
{
- Name = value.ToString();
+ Name = value?.ToString();
}
}
\ No newline at end of file
diff --git a/source/LookupEngine/Descriptors/StringDescriptor.cs b/source/LookupEngine/Descriptors/StringDescriptor.cs
new file mode 100644
index 000000000..1a9432236
--- /dev/null
+++ b/source/LookupEngine/Descriptors/StringDescriptor.cs
@@ -0,0 +1,11 @@
+using LookupEngine.Abstractions.Decomposition;
+
+namespace LookupEngine.Descriptors;
+
+public sealed class StringDescriptor : Descriptor
+{
+ public StringDescriptor(string text)
+ {
+ Name = text;
+ }
+}
\ No newline at end of file
diff --git a/source/LookupEngine/Diagnostic/IEngineDiagnoser.cs b/source/LookupEngine/Diagnostic/IEngineDiagnoser.cs
new file mode 100644
index 000000000..1bfa09f3c
--- /dev/null
+++ b/source/LookupEngine/Diagnostic/IEngineDiagnoser.cs
@@ -0,0 +1,7 @@
+namespace LookupEngine.Diagnostic;
+
+public interface IEngineDiagnoser
+{
+ void StartMonitoring();
+ void StopMonitoring();
+}
\ No newline at end of file
diff --git a/source/RevitLookup/Core/Diagnostic/MemoryDiagnoser.cs b/source/LookupEngine/Diagnostic/MemoryDiagnoser.cs
similarity index 92%
rename from source/RevitLookup/Core/Diagnostic/MemoryDiagnoser.cs
rename to source/LookupEngine/Diagnostic/MemoryDiagnoser.cs
index d349346a5..388a50cdc 100644
--- a/source/RevitLookup/Core/Diagnostic/MemoryDiagnoser.cs
+++ b/source/LookupEngine/Diagnostic/MemoryDiagnoser.cs
@@ -18,33 +18,33 @@
// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
// (Rights in Technical Data and Computer Software), as applicable.
-namespace RevitLookup.Core.Diagnostic;
+namespace LookupEngine.Diagnostic;
-public sealed class MemoryDiagnoser
+public sealed class MemoryDiagnoser : IEngineDiagnoser
{
private long _initialAllocatedBytes;
private long _finalAllocatedBytes;
-
- public void Start()
+
+ public void StartMonitoring()
{
_initialAllocatedBytes = GetTotalAllocatedBytes();
}
-
- public void Stop()
+
+ public void StopMonitoring()
{
_finalAllocatedBytes = GetTotalAllocatedBytes();
}
-
+
public long GetAllocatedBytes()
{
var allocatedBytes = _finalAllocatedBytes - _initialAllocatedBytes;
-
+
_finalAllocatedBytes = 0;
_initialAllocatedBytes = 0;
-
+
return allocatedBytes;
}
-
+
private static long GetTotalAllocatedBytes()
{
// Ref: https://github.com/dotnet/BenchmarkDotNet/blob/master/src/BenchmarkDotNet/Engines/GcStats.cs
@@ -52,7 +52,7 @@ private static long GetTotalAllocatedBytes()
// GC.GetTotalAllocatedBytes() depends heavily on the garbage collection and gives inaccurate results;
// AppDomain.MonitoringIsEnabled almost does not see memory changes when methods are called.
// GetAllocatedBytesForCurrentThread is the perfect choice for reflexion calls
-
+
return GC.GetAllocatedBytesForCurrentThread();
}
}
\ No newline at end of file
diff --git a/source/LookupEngine/Diagnostic/TimeDiagnoser.cs b/source/LookupEngine/Diagnostic/TimeDiagnoser.cs
new file mode 100644
index 000000000..da509bd8e
--- /dev/null
+++ b/source/LookupEngine/Diagnostic/TimeDiagnoser.cs
@@ -0,0 +1,53 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using System.Diagnostics;
+
+namespace LookupEngine.Diagnostic;
+
+public sealed class TimeDiagnoser : IEngineDiagnoser
+{
+ private long _startTimeStamp;
+ private long _endTimeStamp;
+
+ public void StartMonitoring()
+ {
+ _startTimeStamp = Stopwatch.GetTimestamp();
+ }
+
+ public void StopMonitoring()
+ {
+ _endTimeStamp = Stopwatch.GetTimestamp();
+ }
+
+ public TimeSpan GetElapsed()
+ {
+#if NETCOREAPP
+ var elapsed = Stopwatch.GetElapsedTime(_startTimeStamp, _endTimeStamp);
+#else
+ var tickFrequency = (double) TimeSpan.TicksPerSecond / Stopwatch.Frequency;
+ var elapsed = new TimeSpan((long)((_endTimeStamp - _startTimeStamp) * tickFrequency));
+#endif
+ _startTimeStamp = 0;
+ _endTimeStamp = 0;
+
+ return elapsed;
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup/Core/Engine/DescriptorBuilder.Enumeration.cs b/source/LookupEngine/Engine/LookupComposer.Decomposition.Enumeration.cs
similarity index 81%
rename from source/RevitLookup/Core/Engine/DescriptorBuilder.Enumeration.cs
rename to source/LookupEngine/Engine/LookupComposer.Decomposition.Enumeration.cs
index 0ca445060..d13f57179 100644
--- a/source/RevitLookup/Core/Engine/DescriptorBuilder.Enumeration.cs
+++ b/source/LookupEngine/Engine/LookupComposer.Decomposition.Enumeration.cs
@@ -20,22 +20,25 @@
using System.Collections;
-namespace RevitLookup.Core.Engine;
+// ReSharper disable once CheckNamespace
+namespace LookupEngine;
-public sealed partial class DescriptorBuilder
+public partial class LookupComposer
{
private void AddEnumerableItems()
{
- if (_obj is not IEnumerable enumerable) return;
-
- _type = typeof(IEnumerable);
+ if (_input is not IEnumerable enumerable) return;
+
var enumerator = enumerable.GetEnumerator();
-
+
+ var index = 0;
while (enumerator.MoveNext())
{
- WriteDescriptor(enumerator.Current);
+ WriteEnumerableMember(enumerator.Current, index);
+ index++;
+ _depth--;
}
-
+
if (enumerator is IDisposable disposable)
{
disposable.Dispose();
diff --git a/source/LookupEngine/Engine/LookupComposer.Decomposition.Events.cs b/source/LookupEngine/Engine/LookupComposer.Decomposition.Events.cs
new file mode 100644
index 000000000..d1a72121a
--- /dev/null
+++ b/source/LookupEngine/Engine/LookupComposer.Decomposition.Events.cs
@@ -0,0 +1,19 @@
+using System.Reflection;
+using LookupEngine.Formaters;
+
+// ReSharper disable once CheckNamespace
+namespace LookupEngine;
+
+public partial class LookupComposer
+{
+ private void DecomposeEvents(BindingFlags bindingFlags)
+ {
+ if (!_options.IncludeEvents) return;
+
+ var members = MemberDeclaringType.GetEvents(bindingFlags);
+ foreach (var member in members)
+ {
+ WriteDecompositionMember(ReflexionFormater.FormatTypeName(member.EventHandlerType ?? typeof(object)), member);
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/LookupEngine/Engine/LookupComposer.Decomposition.Fields.cs b/source/LookupEngine/Engine/LookupComposer.Decomposition.Fields.cs
new file mode 100644
index 000000000..c2d7ef9db
--- /dev/null
+++ b/source/LookupEngine/Engine/LookupComposer.Decomposition.Fields.cs
@@ -0,0 +1,41 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using System.Reflection;
+
+// ReSharper disable once CheckNamespace
+namespace LookupEngine;
+
+public partial class LookupComposer
+{
+ private void DecomposeFields(BindingFlags bindingFlags)
+ {
+ if (!_options.IncludeFields) return;
+
+ var members = MemberDeclaringType.GetFields(bindingFlags);
+ foreach (var member in members)
+ {
+ if (member.IsSpecialName) continue;
+
+ var value = EvaluateValue(member);
+ WriteDecompositionMember(value, member);
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup/Core/Engine/DescriptorBuilder.Methods.cs b/source/LookupEngine/Engine/LookupComposer.Decomposition.Methods.cs
similarity index 54%
rename from source/RevitLookup/Core/Engine/DescriptorBuilder.Methods.cs
rename to source/LookupEngine/Engine/LookupComposer.Decomposition.Methods.cs
index 10598d1fc..a663e07db 100644
--- a/source/RevitLookup/Core/Engine/DescriptorBuilder.Methods.cs
+++ b/source/LookupEngine/Engine/LookupComposer.Decomposition.Methods.cs
@@ -19,31 +19,31 @@
// (Rights in Technical Data and Computer Software), as applicable.
using System.Reflection;
+using LookupEngine.Abstractions.Configuration;
-namespace RevitLookup.Core.Engine;
+// ReSharper disable once CheckNamespace
+namespace LookupEngine;
-public sealed partial class DescriptorBuilder
+public partial class LookupComposer
{
- private void AddMethods(BindingFlags bindingFlags)
+ private void DecomposeMethods(BindingFlags bindingFlags)
{
- var members = _type.GetMethods(bindingFlags);
+ var members = MemberDeclaringType.GetMethods(bindingFlags);
foreach (var member in members)
{
if (member.IsSpecialName) continue;
-
- object value;
+ if (member is {IsFamily: true, IsSecurityCritical: true}) continue; //Object-critical methods cause CLR exception
+
+ object? value;
var parameters = member.GetParameters();
-
+
try
{
if (!TryResolve(member, parameters, out value))
{
- if (!IsMethodSupported(member, parameters, out value)) continue;
-
- if (value is null)
- {
- Evaluate(member, out value);
- }
+ if (!TrySuppress(member, parameters, out value)) continue;
+
+ value ??= EvaluateValue(member);
}
}
catch (TargetInvocationException exception)
@@ -54,68 +54,43 @@ private void AddMethods(BindingFlags bindingFlags)
{
value = exception;
}
-
- WriteDescriptor(member, value, parameters);
+
+ WriteDecompositionMember(value, member, parameters);
}
}
-
- private bool TryResolve(MethodInfo member, ParameterInfo[] parameters, out object value)
+
+ private protected virtual bool TryResolve(MethodInfo member, ParameterInfo[] parameters, out object? value)
{
value = null;
- if (_currentDescriptor is not IDescriptorResolver resolver) return false;
-
- var handler = resolver.Resolve(Context, member.Name, parameters);
+ if (MemberDeclaringDescriptor is not IDescriptorResolver resolver) return false;
+
+ var handler = resolver.Resolve(member.Name, parameters);
if (handler is null) return false;
-
- try
- {
- _clockDiagnoser.Start();
- _memoryDiagnoser.Start();
- value = handler.Invoke();
- }
- finally
- {
- _memoryDiagnoser.Stop();
- _clockDiagnoser.Stop();
- }
-
+
+ value = EvaluateValue(handler);
+
return true;
}
-
- private void Evaluate(MethodInfo member, out object value)
- {
- try
- {
- _clockDiagnoser.Start();
- _memoryDiagnoser.Start();
- value = member.Invoke(_obj, null);
- }
- finally
- {
- _memoryDiagnoser.Stop();
- _clockDiagnoser.Stop();
- }
- }
-
- private bool IsMethodSupported(MethodInfo member, ParameterInfo[] parameters, out object value)
+
+ private bool TrySuppress(MethodInfo member, ParameterInfo[] parameters, out object? value)
{
value = null;
if (member.ReturnType.Name == "Void")
{
- if (!_settings.IncludeUnsupported) return false;
-
+ if (!_options.IncludeUnsupported) return false;
+
value = new InvalidOperationException("Method doesn't return a value");
return true;
}
-
+
if (parameters.Length > 0)
{
- if (!_settings.IncludeUnsupported) return false;
-
+ if (!_options.IncludeUnsupported) return false;
+
value = new NotSupportedException("Unsupported method overload");
return true;
}
-
+
return true;
}
}
\ No newline at end of file
diff --git a/source/RevitLookup/Core/Engine/DescriptorBuilder.Properties.cs b/source/LookupEngine/Engine/LookupComposer.Decomposition.Properties.cs
similarity index 55%
rename from source/RevitLookup/Core/Engine/DescriptorBuilder.Properties.cs
rename to source/LookupEngine/Engine/LookupComposer.Decomposition.Properties.cs
index f7e8a07d6..48e3d9764 100644
--- a/source/RevitLookup/Core/Engine/DescriptorBuilder.Properties.cs
+++ b/source/LookupEngine/Engine/LookupComposer.Decomposition.Properties.cs
@@ -19,31 +19,32 @@
// (Rights in Technical Data and Computer Software), as applicable.
using System.Reflection;
+using JetBrains.Annotations;
+using LookupEngine.Abstractions.Configuration;
-namespace RevitLookup.Core.Engine;
+// ReSharper disable once CheckNamespace
+namespace LookupEngine;
-public sealed partial class DescriptorBuilder
+[UsedImplicitly]
+public partial class LookupComposer
{
- private void AddProperties(BindingFlags bindingFlags)
+ private void DecomposeProperties(BindingFlags bindingFlags)
{
- var members = _type.GetProperties(bindingFlags);
+ var members = MemberDeclaringType.GetProperties(bindingFlags);
foreach (var member in members)
{
if (member.IsSpecialName) continue;
-
- object value;
- var parameters = member.CanRead ? member.GetMethod!.GetParameters() : null;
-
+
+ object? value;
+ var parameters = member.CanRead ? member.GetMethod!.GetParameters() : [];
+
try
{
if (!TryResolve(member, parameters, out value))
{
- if (!IsPropertySupported(member, parameters, out value)) continue;
-
- if (value is null)
- {
- Evaluate(member, out value);
- }
+ if (!TrySuppress(member, parameters, out value)) continue;
+
+ value ??= EvaluateValue(member);
}
}
catch (TargetInvocationException exception)
@@ -54,67 +55,42 @@ private void AddProperties(BindingFlags bindingFlags)
{
value = exception;
}
-
- WriteDescriptor(member, value, parameters);
+
+ WriteDecompositionMember(value, member, parameters);
}
}
-
- private bool TryResolve(PropertyInfo member, ParameterInfo[] parameters, out object value)
+
+ private protected virtual bool TryResolve(PropertyInfo member, ParameterInfo[] parameters, out object? value)
{
value = null;
- if (_currentDescriptor is not IDescriptorResolver resolver) return false;
-
- var handler = resolver.Resolve(Context, member.Name, parameters);
+ if (MemberDeclaringDescriptor is not IDescriptorResolver resolver) return false;
+
+ var handler = resolver.Resolve(member.Name, parameters);
if (handler is null) return false;
-
- try
- {
- _clockDiagnoser.Start();
- _memoryDiagnoser.Start();
- value = handler.Invoke();
- }
- finally
- {
- _memoryDiagnoser.Stop();
- _clockDiagnoser.Stop();
- }
-
+
+ value = EvaluateValue(handler);
+
return true;
}
-
- private void Evaluate(PropertyInfo member, out object value)
- {
- try
- {
- _clockDiagnoser.Start();
- _memoryDiagnoser.Start();
- value = member.GetValue(_obj);
- }
- finally
- {
- _memoryDiagnoser.Stop();
- _clockDiagnoser.Stop();
- }
- }
-
- private bool IsPropertySupported(PropertyInfo member, ParameterInfo[] parameters, out object value)
+
+ private bool TrySuppress(PropertyInfo member, ParameterInfo[] parameters, out object? value)
{
value = null;
-
+
if (!member.CanRead)
{
value = new InvalidOperationException("Property does not have a get accessor, it cannot be read");
return true;
}
-
+
if (parameters.Length > 0)
{
- if (!_settings.IncludeUnsupported) return false;
-
+ if (!_options.IncludeUnsupported) return false;
+
value = new NotSupportedException("Unsupported property overload");
return true;
}
-
+
return true;
}
}
\ No newline at end of file
diff --git a/source/LookupEngine/Engine/LookupComposer.Decomposition.cs b/source/LookupEngine/Engine/LookupComposer.Decomposition.cs
new file mode 100644
index 000000000..a234c29a2
--- /dev/null
+++ b/source/LookupEngine/Engine/LookupComposer.Decomposition.cs
@@ -0,0 +1,141 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using System.Diagnostics.Contracts;
+using System.Reflection;
+using LookupEngine.Abstractions;
+
+// ReSharper disable once CheckNamespace
+namespace LookupEngine;
+
+public partial class LookupComposer
+{
+ [Pure]
+ private DecomposedObject DecomposeInstance()
+ {
+ _decomposedObject = DecomposeInstanceObject();
+ var objectType = _input.GetType();
+ var members = DecomposeInstanceMembers(objectType);
+ _decomposedObject.Members.AddRange(members);
+
+ return _decomposedObject;
+ }
+
+ [Pure]
+ private DecomposedObject DecomposeInstanceObject()
+ {
+ _input = RedirectValue(_input, out var instanceDescriptor);
+
+ var objectType = _input.GetType();
+ return CreateInstanceDecomposition(_input, objectType, instanceDescriptor);
+ }
+
+ [Pure]
+ private DecomposedObject DecomposeStatic(Type type)
+ {
+ _decomposedObject = DecomposeStaticObject(type);
+ var members = DecomposeStaticMembers(type);
+ _decomposedObject.Members.AddRange(members);
+
+ return _decomposedObject;
+ }
+
+ [Pure]
+ private DecomposedObject DecomposeStaticObject(Type type)
+ {
+ var staticDescriptor = _options.TypeResolver.Invoke(null, type);
+ return CreateStaticDecomposition(type, staticDescriptor);
+ }
+
+ [Pure]
+ private List DecomposeInstanceMembers()
+ {
+ return DecomposeInstanceMembers(_input.GetType());
+ }
+
+ [Pure]
+ private List DecomposeInstanceMembers(Type objectType)
+ {
+ _decomposedMembers = new List(32);
+
+ var objectTypeHierarchy = GetTypeHierarchy(objectType);
+ for (var i = objectTypeHierarchy.Count - 1; i >= 0; i--)
+ {
+ MemberDeclaringType = objectTypeHierarchy[i];
+ MemberDeclaringDescriptor = _options.TypeResolver.Invoke(_input, MemberDeclaringType);
+
+ var flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly;
+ if (_options.IncludeStaticMembers) flags |= BindingFlags.Static;
+ if (_options.IncludePrivateMembers) flags |= BindingFlags.NonPublic;
+
+ DecomposeFields(flags);
+ DecomposeProperties(flags);
+ DecomposeMethods(flags);
+ DecomposeEvents(flags);
+ ExecuteExtensions();
+
+ _depth--;
+ }
+
+ MemberDeclaringType = objectType;
+ AddEnumerableItems();
+
+ return _decomposedMembers;
+ }
+
+ [Pure]
+ private List DecomposeStaticMembers(Type objectType)
+ {
+ _decomposedMembers = new List(32);
+
+ var flags = BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly;
+ if (_options.IncludePrivateMembers) flags |= BindingFlags.NonPublic;
+
+ var objectTypeHierarchy = GetTypeHierarchy(objectType);
+ for (var i = objectTypeHierarchy.Count - 1; i >= 0; i--)
+ {
+ MemberDeclaringType = objectTypeHierarchy[i];
+ MemberDeclaringDescriptor = _options.TypeResolver.Invoke(null, MemberDeclaringType);
+
+ DecomposeFields(flags);
+ DecomposeProperties(flags);
+ DecomposeMethods(flags);
+
+ _depth--;
+ }
+
+ return _decomposedMembers;
+ }
+
+ [Pure]
+ private List GetTypeHierarchy(Type inputType)
+ {
+ var types = new List();
+ while (inputType.BaseType is not null)
+ {
+ types.Add(inputType);
+ inputType = inputType.BaseType;
+ }
+
+ if (_options.IncludeRoot) types.Add(inputType);
+
+ return types;
+ }
+}
\ No newline at end of file
diff --git a/source/LookupEngine/Engine/LookupComposer.Diagnostic.cs b/source/LookupEngine/Engine/LookupComposer.Diagnostic.cs
new file mode 100644
index 000000000..d025c9c35
--- /dev/null
+++ b/source/LookupEngine/Engine/LookupComposer.Diagnostic.cs
@@ -0,0 +1,93 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using System.Reflection;
+using LookupEngine.Abstractions.Decomposition;
+using LookupEngine.Diagnostic;
+
+// ReSharper disable once CheckNamespace
+namespace LookupEngine;
+
+public partial class LookupComposer
+{
+ private protected readonly TimeDiagnoser TimeDiagnoser = new();
+ private protected readonly MemoryDiagnoser MemoryDiagnoser = new();
+
+ private object? EvaluateValue(FieldInfo member)
+ {
+ TimeDiagnoser.StartMonitoring();
+ MemoryDiagnoser.StartMonitoring();
+
+ var value = member.GetValue(_input);
+
+ MemoryDiagnoser.StopMonitoring();
+ TimeDiagnoser.StopMonitoring();
+
+ return value;
+ }
+
+ private object? EvaluateValue(PropertyInfo member)
+ {
+ try
+ {
+ TimeDiagnoser.StartMonitoring();
+ MemoryDiagnoser.StartMonitoring();
+
+ return member.GetValue(_input);
+ }
+ finally
+ {
+ MemoryDiagnoser.StopMonitoring();
+ TimeDiagnoser.StopMonitoring();
+ }
+ }
+
+ private object? EvaluateValue(MethodInfo member)
+ {
+ try
+ {
+ TimeDiagnoser.StartMonitoring();
+ MemoryDiagnoser.StartMonitoring();
+
+ return member.Invoke(_input, null);
+ }
+ finally
+ {
+ MemoryDiagnoser.StopMonitoring();
+ TimeDiagnoser.StopMonitoring();
+ }
+ }
+
+ private protected IVariant EvaluateValue(Func handler)
+ {
+ try
+ {
+ TimeDiagnoser.StartMonitoring();
+ MemoryDiagnoser.StartMonitoring();
+
+ return handler.Invoke();
+ }
+ finally
+ {
+ MemoryDiagnoser.StopMonitoring();
+ TimeDiagnoser.StopMonitoring();
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/LookupEngine/Engine/LookupComposer.Features.Extensions.cs b/source/LookupEngine/Engine/LookupComposer.Features.Extensions.cs
new file mode 100644
index 000000000..8b5f01ab4
--- /dev/null
+++ b/source/LookupEngine/Engine/LookupComposer.Features.Extensions.cs
@@ -0,0 +1,51 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using LookupEngine.Abstractions.Configuration;
+using LookupEngine.Abstractions.Decomposition;
+
+//ReSharper disable once CheckNamespace
+namespace LookupEngine;
+
+public partial class LookupComposer : IExtensionManager
+{
+ private protected virtual void ExecuteExtensions()
+ {
+ if (!_options.EnableExtensions) return;
+
+ if (MemberDeclaringDescriptor is IDescriptorExtension extension)
+ {
+ extension.RegisterExtensions(this);
+ }
+ }
+
+ public void Register(string methodName, Func handler)
+ {
+ try
+ {
+ var result = EvaluateValue(handler);
+ WriteExtensionMember(result, methodName);
+ }
+ catch (Exception exception)
+ {
+ WriteExtensionMember(exception, methodName);
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/LookupEngine/Engine/LookupComposer.Features.Redirection.cs b/source/LookupEngine/Engine/LookupComposer.Features.Redirection.cs
new file mode 100644
index 000000000..0615d6713
--- /dev/null
+++ b/source/LookupEngine/Engine/LookupComposer.Features.Redirection.cs
@@ -0,0 +1,81 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using LookupEngine.Abstractions.Configuration;
+using LookupEngine.Abstractions.Decomposition;
+
+// ReSharper disable once CheckNamespace
+namespace LookupEngine;
+
+public partial class LookupComposer
+{
+ private protected virtual object RedirectValue(object value)
+ {
+ if (!_options.EnableRedirection) return value;
+
+ var valueDescriptor = _options.TypeResolver.Invoke(value, null);
+ while (valueDescriptor is IDescriptorRedirector redirector)
+ {
+ if (!redirector.TryRedirect(string.Empty, out value)) break;
+ valueDescriptor = _options.TypeResolver.Invoke(value, null);
+ }
+
+ return value;
+ }
+
+ private object RedirectValue(object value, out Descriptor valueDescriptor)
+ {
+ return RedirectValue(value, string.Empty, out valueDescriptor);
+ }
+
+ private protected virtual object RedirectValue(object value, string target, out Descriptor valueDescriptor)
+ {
+ var variant = value as IVariant;
+ if (variant is not null)
+ {
+ value = variant.Value;
+ }
+
+ valueDescriptor = _options.TypeResolver.Invoke(value, null);
+
+ var description = valueDescriptor.Description;
+ if (variant is not null && description is null)
+ {
+ description = variant.Description;
+ }
+
+ if (_options.EnableRedirection)
+ {
+ while (valueDescriptor is IDescriptorRedirector redirector)
+ {
+ if (!redirector.TryRedirect(target, out value)) break;
+ valueDescriptor = _options.TypeResolver.Invoke(value, null);
+
+ if (valueDescriptor.Description is not null)
+ {
+ description = valueDescriptor.Description;
+ }
+ }
+ }
+
+ valueDescriptor.Description = description;
+ return value;
+ }
+}
\ No newline at end of file
diff --git a/source/LookupEngine/Engine/LookupComposer.Settings.cs b/source/LookupEngine/Engine/LookupComposer.Settings.cs
new file mode 100644
index 000000000..f535b93a2
--- /dev/null
+++ b/source/LookupEngine/Engine/LookupComposer.Settings.cs
@@ -0,0 +1,88 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using JetBrains.Annotations;
+using LookupEngine.Abstractions;
+using LookupEngine.Abstractions.Decomposition;
+using LookupEngine.Exceptions;
+
+// ReSharper disable once CheckNamespace
+namespace LookupEngine;
+
+[PublicAPI]
+public partial class LookupComposer
+{
+ private readonly DecomposeOptions _options;
+
+ private int _depth;
+ private object _input;
+ private Type? _memberDeclaringType;
+ private Descriptor? _memberDeclaringDescriptor;
+ private DecomposedObject? _decomposedObject;
+ private List? _decomposedMembers;
+
+ private protected LookupComposer(object value, DecomposeOptions options)
+ {
+ _input = value;
+ _options = options;
+ }
+
+ internal List DecomposedMembers
+ {
+ get
+ {
+ if (_decomposedMembers is null)
+ {
+ EngineException.ThrowIfEngineNotInitialized(nameof(DecomposedMembers));
+ }
+
+ return _decomposedMembers;
+ }
+ set => _decomposedMembers = value;
+ }
+
+ internal Type MemberDeclaringType
+ {
+ get
+ {
+ if (_memberDeclaringType is null)
+ {
+ EngineException.ThrowIfEngineNotInitialized(nameof(MemberDeclaringType));
+ }
+
+ return _memberDeclaringType;
+ }
+ set => _memberDeclaringType = value;
+ }
+
+ internal Descriptor MemberDeclaringDescriptor
+ {
+ get
+ {
+ if (_memberDeclaringDescriptor is null)
+ {
+ EngineException.ThrowIfEngineNotInitialized(nameof(MemberDeclaringDescriptor));
+ }
+
+ return _memberDeclaringDescriptor;
+ }
+ set => _memberDeclaringDescriptor = value;
+ }
+}
\ No newline at end of file
diff --git a/source/LookupEngine/Engine/LookupComposer.Writer.cs b/source/LookupEngine/Engine/LookupComposer.Writer.cs
new file mode 100644
index 000000000..aeaa98016
--- /dev/null
+++ b/source/LookupEngine/Engine/LookupComposer.Writer.cs
@@ -0,0 +1,185 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using System.Collections;
+using System.Reflection;
+using LookupEngine.Abstractions;
+using LookupEngine.Abstractions.Decomposition;
+using LookupEngine.Abstractions.Enums;
+using LookupEngine.Formaters;
+
+// ReSharper disable once CheckNamespace
+namespace LookupEngine;
+
+public partial class LookupComposer
+{
+ private protected static DecomposedObject CreateNullableDecomposition()
+ {
+ return new DecomposedObject
+ {
+ Name = $"{nameof(System)}.{nameof(Object)}",
+ RawValue = null,
+ TypeName = nameof(Object),
+ TypeFullName = $"{nameof(System)}.{nameof(Object)}"
+ };
+ }
+
+ private static DecomposedObject CreateInstanceDecomposition(object instance, Type type, Descriptor descriptor)
+ {
+ var formatTypeName = ReflexionFormater.FormatTypeName(type);
+ var hasUnknownName = descriptor.Name is null ||
+ type.Namespace is null ||
+ descriptor.Name!.StartsWith(type.Namespace, StringComparison.OrdinalIgnoreCase);
+
+ return new DecomposedObject
+ {
+ Name = hasUnknownName ? formatTypeName : descriptor.Name!,
+ Description = descriptor.Description,
+ RawValue = instance,
+ TypeName = formatTypeName,
+ TypeFullName = $"{type.Namespace}.{formatTypeName}",
+ Descriptor = descriptor
+ };
+ }
+
+ private static DecomposedObject CreateStaticDecomposition(Type type, Descriptor descriptor)
+ {
+ var formatTypeName = ReflexionFormater.FormatTypeName(type);
+ var hasUnknownName = descriptor.Name is null ||
+ type.Namespace is null ||
+ descriptor.Name!.StartsWith(type.Namespace, StringComparison.OrdinalIgnoreCase);
+
+ return new DecomposedObject
+ {
+ Name = hasUnknownName ? formatTypeName : descriptor.Name!,
+ Description = descriptor.Description,
+ RawValue = type,
+ TypeName = formatTypeName,
+ TypeFullName = $"{type.Namespace}.{formatTypeName}",
+ Descriptor = descriptor
+ };
+ }
+
+ private void WriteEnumerableMember(object? value, int index)
+ {
+ var member = new DecomposedMember
+ {
+ Depth = _depth,
+ Value = CreateValue(nameof(IEnumerable), value),
+ Name = $"{ReflexionFormater.FormatTypeName(MemberDeclaringType).Replace("[]", string.Empty)}[{index}]",
+ MemberAttributes = MemberAttributes.Property,
+ DeclaringTypeName = nameof(IEnumerable),
+ DeclaringTypeFullName = $"{nameof(System)}.{nameof(System.Collections)}.{nameof(IEnumerable)}",
+ };
+
+ DecomposedMembers.Add(member);
+ }
+
+ private protected void WriteExtensionMember(object? value, string name)
+ {
+ var formatTypeName = ReflexionFormater.FormatTypeName(MemberDeclaringType);
+
+ var member = new DecomposedMember
+ {
+ Depth = _depth,
+ Name = name,
+ Value = CreateValue(name, value),
+ DeclaringTypeName = formatTypeName,
+ DeclaringTypeFullName = $"{MemberDeclaringType.Namespace}.{formatTypeName}",
+ MemberAttributes = MemberAttributes.Extension,
+ ComputationTime = TimeDiagnoser.GetElapsed().TotalMilliseconds,
+ AllocatedBytes = MemoryDiagnoser.GetAllocatedBytes()
+ };
+
+ DecomposedMembers.Add(member);
+ }
+
+ private void WriteDecompositionMember(object? value, MemberInfo memberInfo)
+ {
+ var formatTypeName = ReflexionFormater.FormatTypeName(MemberDeclaringType);
+
+ var member = new DecomposedMember
+ {
+ Depth = _depth,
+ Value = CreateValue(memberInfo.Name, value),
+ Name = memberInfo.Name,
+ DeclaringTypeName = formatTypeName,
+ DeclaringTypeFullName = $"{MemberDeclaringType.Namespace}.{formatTypeName}",
+ MemberAttributes = ModifiersFormater.FormatAttributes(memberInfo),
+ ComputationTime = TimeDiagnoser.GetElapsed().TotalMilliseconds,
+ AllocatedBytes = MemoryDiagnoser.GetAllocatedBytes()
+ };
+
+ DecomposedMembers.Add(member);
+ }
+
+ private void WriteDecompositionMember(object? value, MemberInfo memberInfo, ParameterInfo[] parameters)
+ {
+ var formatTypeName = ReflexionFormater.FormatTypeName(MemberDeclaringType);
+
+ var member = new DecomposedMember
+ {
+ Depth = _depth,
+ Value = CreateValue(memberInfo.Name, value),
+ Name = ReflexionFormater.FormatMemberName(memberInfo, parameters),
+ DeclaringTypeName = formatTypeName,
+ DeclaringTypeFullName = $"{MemberDeclaringType.Namespace}.{formatTypeName}",
+ MemberAttributes = ModifiersFormater.FormatAttributes(memberInfo),
+ ComputationTime = TimeDiagnoser.GetElapsed().TotalMilliseconds,
+ AllocatedBytes = MemoryDiagnoser.GetAllocatedBytes()
+ };
+
+ DecomposedMembers.Add(member);
+ }
+
+ private DecomposedValue CreateNullableValue()
+ {
+ return new DecomposedValue
+ {
+ RawValue = null,
+ Name = string.Empty,
+ TypeName = nameof(Object),
+ TypeFullName = $"{nameof(System)}.{nameof(Object)}"
+ };
+ }
+
+ private DecomposedValue CreateValue(string targetMember, object? value)
+ {
+ if (value is null) return CreateNullableValue();
+
+ value = RedirectValue(value, targetMember, out var valueDescriptor);
+
+ var valueType = value.GetType();
+ var formatTypeName = ReflexionFormater.FormatTypeName(valueType);
+ var hasUnknownName = valueDescriptor.Name is null ||
+ valueType.Namespace is null ||
+ valueDescriptor.Name.StartsWith(valueType.Namespace, StringComparison.OrdinalIgnoreCase);
+
+ return new DecomposedValue
+ {
+ RawValue = value,
+ Name = hasUnknownName ? formatTypeName : valueDescriptor.Name!,
+ Description = valueDescriptor.Description,
+ TypeName = formatTypeName,
+ TypeFullName = $"{valueType.Namespace}.{formatTypeName}",
+ Descriptor = valueDescriptor
+ };
+ }
+}
\ No newline at end of file
diff --git a/source/LookupEngine/Engine/LookupComposer.cs b/source/LookupEngine/Engine/LookupComposer.cs
new file mode 100644
index 000000000..26231a79c
--- /dev/null
+++ b/source/LookupEngine/Engine/LookupComposer.cs
@@ -0,0 +1,67 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using JetBrains.Annotations;
+using LookupEngine.Abstractions;
+
+// ReSharper disable once CheckNamespace
+namespace LookupEngine;
+
+public partial class LookupComposer
+{
+ [Pure]
+ public static DecomposedObject Decompose(object? value, DecomposeOptions? options = null)
+ {
+ if (value is null) return CreateNullableDecomposition();
+
+ options ??= DecomposeOptions.Default;
+ return value switch
+ {
+ Type type => new LookupComposer(value, options).DecomposeStatic(type),
+ _ => new LookupComposer(value, options).DecomposeInstance()
+ };
+ }
+
+ [Pure]
+ public static DecomposedObject DecomposeObject(object? value, DecomposeOptions? options = null)
+ {
+ if (value is null) return CreateNullableDecomposition();
+
+ options ??= DecomposeOptions.Default;
+ return value switch
+ {
+ Type type => new LookupComposer(value, options).DecomposeStaticObject(type),
+ _ => new LookupComposer(value, options).DecomposeInstanceObject()
+ };
+ }
+
+ [Pure]
+ public static List DecomposeMembers(object? value, DecomposeOptions? options = null)
+ {
+ if (value is null) return [];
+
+ options ??= DecomposeOptions.Default;
+ return value switch
+ {
+ Type type => new LookupComposer(value, options).DecomposeStaticMembers(type),
+ _ => new LookupComposer(value, options).DecomposeInstanceMembers()
+ };
+ }
+}
\ No newline at end of file
diff --git a/source/LookupEngine/Engine/LookupComposer{T}.Decomposition.Methods.cs b/source/LookupEngine/Engine/LookupComposer{T}.Decomposition.Methods.cs
new file mode 100644
index 000000000..52ba52f4b
--- /dev/null
+++ b/source/LookupEngine/Engine/LookupComposer{T}.Decomposition.Methods.cs
@@ -0,0 +1,55 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using System.Reflection;
+using LookupEngine.Abstractions.Configuration;
+
+// ReSharper disable once CheckNamespace
+namespace LookupEngine;
+
+public partial class LookupComposer
+{
+ private protected override bool TryResolve(MethodInfo member, ParameterInfo[] parameters, out object? value)
+ {
+ value = null;
+
+ if (MemberDeclaringDescriptor is IDescriptorResolver resolver)
+ {
+ var handler = resolver.Resolve(member.Name, parameters);
+ if (handler is not null)
+ {
+ value = EvaluateValue(handler);
+ return true;
+ }
+ }
+
+ if (MemberDeclaringDescriptor is IDescriptorResolver contextResolver)
+ {
+ var handler = contextResolver.Resolve(member.Name, parameters);
+ if (handler is not null)
+ {
+ value = EvaluateValue(handler);
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/source/LookupEngine/Engine/LookupComposer{T}.Decomposition.Properties.cs b/source/LookupEngine/Engine/LookupComposer{T}.Decomposition.Properties.cs
new file mode 100644
index 000000000..8818ac626
--- /dev/null
+++ b/source/LookupEngine/Engine/LookupComposer{T}.Decomposition.Properties.cs
@@ -0,0 +1,57 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using System.Reflection;
+using JetBrains.Annotations;
+using LookupEngine.Abstractions.Configuration;
+
+// ReSharper disable once CheckNamespace
+namespace LookupEngine;
+
+[UsedImplicitly]
+public partial class LookupComposer
+{
+ private protected override bool TryResolve(PropertyInfo member, ParameterInfo[] parameters, out object? value)
+ {
+ value = null;
+
+ if (MemberDeclaringDescriptor is IDescriptorResolver resolver)
+ {
+ var handler = resolver.Resolve(member.Name, parameters);
+ if (handler is not null)
+ {
+ value = EvaluateValue(handler);
+ return true;
+ }
+ }
+
+ if (MemberDeclaringDescriptor is IDescriptorResolver contextResolver)
+ {
+ var handler = contextResolver.Resolve(member.Name, parameters);
+ if (handler is not null)
+ {
+ value = EvaluateValue(handler);
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/source/LookupEngine/Engine/LookupComposer{T}.Diagnostic.cs b/source/LookupEngine/Engine/LookupComposer{T}.Diagnostic.cs
new file mode 100644
index 000000000..a70f4d596
--- /dev/null
+++ b/source/LookupEngine/Engine/LookupComposer{T}.Diagnostic.cs
@@ -0,0 +1,43 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using LookupEngine.Abstractions.Decomposition;
+
+// ReSharper disable once CheckNamespace
+namespace LookupEngine;
+
+public partial class LookupComposer
+{
+ private IVariant EvaluateValue(Func handler)
+ {
+ try
+ {
+ TimeDiagnoser.StartMonitoring();
+ MemoryDiagnoser.StartMonitoring();
+
+ return handler.Invoke(_options.Context);
+ }
+ finally
+ {
+ MemoryDiagnoser.StopMonitoring();
+ TimeDiagnoser.StopMonitoring();
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup/Core/Engine/DescriptorBuilder.Extensions.cs b/source/LookupEngine/Engine/LookupComposer{T}.Features.Extensions.cs
similarity index 57%
rename from source/RevitLookup/Core/Engine/DescriptorBuilder.Extensions.cs
rename to source/LookupEngine/Engine/LookupComposer{T}.Features.Extensions.cs
index 157aedc03..abb6301e0 100644
--- a/source/RevitLookup/Core/Engine/DescriptorBuilder.Extensions.cs
+++ b/source/LookupEngine/Engine/LookupComposer{T}.Features.Extensions.cs
@@ -18,43 +18,39 @@
// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
// (Rights in Technical Data and Computer Software), as applicable.
-namespace RevitLookup.Core.Engine;
+using LookupEngine.Abstractions.Configuration;
+using LookupEngine.Abstractions.Decomposition;
-public sealed partial class DescriptorBuilder : IExtensionManager
+//ReSharper disable once CheckNamespace
+namespace LookupEngine;
+
+public partial class LookupComposer : IExtensionManager
{
- private void AddExtensions()
- {
- if (!_settings.IncludeExtensions) return;
- if (_currentDescriptor is not IDescriptorExtension extension) return;
-
- extension.RegisterExtensions(this);
- }
-
- public void Register(string methodName, Func handler)
+ private protected override void ExecuteExtensions()
{
- try
+ if (!_options.EnableExtensions) return;
+
+ if (MemberDeclaringDescriptor is IDescriptorExtension extension)
{
- var result = Evaluate(handler);
- WriteDescriptor(methodName, result);
+ extension.RegisterExtensions(this);
}
- catch (Exception exception)
+
+ if (MemberDeclaringDescriptor is IDescriptorExtension contextExtension)
{
- WriteDescriptor(methodName, exception);
+ contextExtension.RegisterExtensions(this);
}
}
-
- private object Evaluate(Func handler)
+
+ public void Register(string name, Func extension)
{
try
{
- _clockDiagnoser.Start();
- _memoryDiagnoser.Start();
- return handler.Invoke(Context);
+ var result = EvaluateValue(extension);
+ WriteExtensionMember(result, name);
}
- finally
+ catch (Exception exception)
{
- _memoryDiagnoser.Stop();
- _clockDiagnoser.Stop();
+ WriteExtensionMember(exception, name);
}
}
}
\ No newline at end of file
diff --git a/source/LookupEngine/Engine/LookupComposer{T}.Features.Redirection.cs b/source/LookupEngine/Engine/LookupComposer{T}.Features.Redirection.cs
new file mode 100644
index 000000000..5042774a8
--- /dev/null
+++ b/source/LookupEngine/Engine/LookupComposer{T}.Features.Redirection.cs
@@ -0,0 +1,114 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using LookupEngine.Abstractions.Configuration;
+using LookupEngine.Abstractions.Decomposition;
+
+// ReSharper disable once CheckNamespace
+namespace LookupEngine;
+
+public partial class LookupComposer
+{
+ private protected override object RedirectValue(object value)
+ {
+ if (!_options.EnableRedirection) return value;
+
+ var valueDescriptor = _options.TypeResolver.Invoke(value, null);
+ while (true)
+ {
+ var redirected = false;
+
+ // Generic interface is prioritised
+ if (valueDescriptor is IDescriptorRedirector genericRedirector)
+ {
+ if (genericRedirector.TryRedirect(string.Empty, _options.Context, out value))
+ {
+ redirected = true;
+ }
+ }
+ else if (valueDescriptor is IDescriptorRedirector redirector)
+ {
+ if (redirector.TryRedirect(string.Empty, out value))
+ {
+ redirected = true;
+ }
+ }
+
+ if (!redirected) break;
+
+ valueDescriptor = _options.TypeResolver.Invoke(value, null);
+ }
+
+ return value;
+ }
+
+ private protected override object RedirectValue(object value, string target, out Descriptor valueDescriptor)
+ {
+ var variant = value as IVariant;
+ if (variant is not null)
+ {
+ value = variant.Value;
+ }
+
+ valueDescriptor = _options.TypeResolver.Invoke(value, null);
+
+ var description = valueDescriptor.Description;
+ if (variant is not null && description is null)
+ {
+ description = variant.Description;
+ }
+
+ if (_options.EnableRedirection)
+ {
+ while (true)
+ {
+ var redirected = false;
+
+ // Generic interface is prioritised
+ if (valueDescriptor is IDescriptorRedirector genericRedirector)
+ {
+ if (genericRedirector.TryRedirect(target, _options.Context, out value))
+ {
+ redirected = true;
+ }
+ }
+ else if (valueDescriptor is IDescriptorRedirector redirector)
+ {
+ if (redirector.TryRedirect(target, out value))
+ {
+ redirected = true;
+ }
+ }
+
+ if (!redirected) break;
+
+ valueDescriptor = _options.TypeResolver.Invoke(value, null);
+
+ if (valueDescriptor.Description is not null)
+ {
+ description = valueDescriptor.Description;
+ }
+ }
+ }
+
+ valueDescriptor.Description = description;
+ return value;
+ }
+}
\ No newline at end of file
diff --git a/source/LookupEngine/Engine/LookupComposer{T}.Settings.cs b/source/LookupEngine/Engine/LookupComposer{T}.Settings.cs
new file mode 100644
index 000000000..9be118809
--- /dev/null
+++ b/source/LookupEngine/Engine/LookupComposer{T}.Settings.cs
@@ -0,0 +1,16 @@
+using JetBrains.Annotations;
+using LookupEngine.Options;
+
+// ReSharper disable once CheckNamespace
+namespace LookupEngine;
+
+[PublicAPI]
+public sealed partial class LookupComposer : LookupComposer
+{
+ private readonly DecomposeOptions _options;
+
+ internal LookupComposer(object value, DecomposeOptions options) : base(value, options)
+ {
+ _options = options;
+ }
+}
\ No newline at end of file
diff --git a/source/LookupEngine/Engine/LookupComposer{T}.cs b/source/LookupEngine/Engine/LookupComposer{T}.cs
new file mode 100644
index 000000000..4bffc374c
--- /dev/null
+++ b/source/LookupEngine/Engine/LookupComposer{T}.cs
@@ -0,0 +1,65 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using JetBrains.Annotations;
+using LookupEngine.Abstractions;
+using LookupEngine.Options;
+
+// ReSharper disable once CheckNamespace
+namespace LookupEngine;
+
+public partial class LookupComposer
+{
+ [Pure]
+ public static DecomposedObject Decompose(object? value, DecomposeOptions options)
+ {
+ if (value is null) return CreateNullableDecomposition();
+
+ return value switch
+ {
+ Type type => new LookupComposer(value, options).DecomposeStatic(type),
+ _ => new LookupComposer(value, options).DecomposeInstance()
+ };
+ }
+
+ [Pure]
+ public static DecomposedObject DecomposeObject(object? value, DecomposeOptions options)
+ {
+ if (value is null) return CreateNullableDecomposition();
+
+ return value switch
+ {
+ Type type => new LookupComposer(value, options).DecomposeStaticObject(type),
+ _ => new LookupComposer(value, options).DecomposeInstanceObject()
+ };
+ }
+
+ [Pure]
+ public static List DecomposeMembers(object? value, DecomposeOptions options)
+ {
+ if (value is null) return [];
+
+ return value switch
+ {
+ Type type => new LookupComposer(value, options).DecomposeStaticMembers(type),
+ _ => new LookupComposer(value, options).DecomposeInstanceMembers()
+ };
+ }
+}
\ No newline at end of file
diff --git a/source/LookupEngine/Exceptions/EngineException.cs b/source/LookupEngine/Exceptions/EngineException.cs
new file mode 100644
index 000000000..956fe53bd
--- /dev/null
+++ b/source/LookupEngine/Exceptions/EngineException.cs
@@ -0,0 +1,12 @@
+using System.Diagnostics.CodeAnalysis;
+
+namespace LookupEngine.Exceptions;
+
+public sealed class EngineException(string message) : Exception(message)
+{
+ [DoesNotReturn]
+ internal static void ThrowIfEngineNotInitialized(string propertyName)
+ {
+ throw new EngineException($"LookupEngine internal error. {propertyName} must be initialized before accessing it.");
+ }
+}
\ No newline at end of file
diff --git a/source/LookupEngine/Formaters/ModifiersFormater.cs b/source/LookupEngine/Formaters/ModifiersFormater.cs
new file mode 100644
index 000000000..2c765ef37
--- /dev/null
+++ b/source/LookupEngine/Formaters/ModifiersFormater.cs
@@ -0,0 +1,33 @@
+using System.Reflection;
+using LookupEngine.Abstractions.Enums;
+
+namespace LookupEngine.Formaters;
+
+internal static class ModifiersFormater
+{
+ internal static MemberAttributes FormatAttributes(MemberInfo member)
+ {
+ return member switch
+ {
+ MethodInfo info => CombineModifiers(MemberAttributes.Method, info.Attributes),
+ PropertyInfo info => CombineModifiers(MemberAttributes.Property, info.CanRead ? info.GetMethod!.Attributes : info.SetMethod!.Attributes),
+ FieldInfo info => CombineModifiers(MemberAttributes.Field, info.Attributes),
+ EventInfo info => CombineModifiers(MemberAttributes.Event, info.AddMethod!.Attributes),
+ _ => throw new ArgumentOutOfRangeException(nameof(member))
+ };
+ }
+
+ private static MemberAttributes CombineModifiers(MemberAttributes attributes, MethodAttributes methodAttributes)
+ {
+ if ((methodAttributes & MethodAttributes.Static) != 0) attributes |= MemberAttributes.Static;
+ if ((methodAttributes & MethodAttributes.Private) != 0) attributes |= MemberAttributes.Private;
+ return attributes;
+ }
+
+ private static MemberAttributes CombineModifiers(MemberAttributes attributes, FieldAttributes fieldAttributes)
+ {
+ if ((fieldAttributes & FieldAttributes.Static) != 0) attributes |= MemberAttributes.Static;
+ if ((fieldAttributes & FieldAttributes.Private) != 0) attributes |= MemberAttributes.Private;
+ return attributes;
+ }
+}
\ No newline at end of file
diff --git a/source/LookupEngine/Formaters/ReflexionFormater.cs b/source/LookupEngine/Formaters/ReflexionFormater.cs
new file mode 100644
index 000000000..bf08fa184
--- /dev/null
+++ b/source/LookupEngine/Formaters/ReflexionFormater.cs
@@ -0,0 +1,41 @@
+using System.Reflection;
+
+namespace LookupEngine.Formaters;
+
+internal static class ReflexionFormater
+{
+ public static string FormatTypeName(Type type)
+ {
+ if (!type.IsGenericType) return type.Name;
+
+ var typeName = type.Name;
+ var apostropheIndex = typeName.IndexOf('`');
+ if (apostropheIndex > 0) typeName = typeName[..apostropheIndex];
+ typeName += "<";
+ var genericArguments = type.GetGenericArguments();
+ for (var i = 0; i < genericArguments.Length; i++)
+ {
+ typeName += FormatTypeName(genericArguments[i]);
+ if (i < genericArguments.Length - 1) typeName += ", ";
+ }
+
+ typeName += ">";
+ return typeName;
+ }
+
+ public static string FormatMemberName(MemberInfo member, ParameterInfo[] parameters)
+ {
+ if (parameters.Length == 0) return member.Name;
+
+ var formatedParameters = parameters.Select(info =>
+ {
+ return info.ParameterType.IsByRef switch
+ {
+ true => $"ref {FormatTypeName(info.ParameterType).Replace("&", string.Empty)}",
+ false => FormatTypeName(info.ParameterType)
+ };
+ });
+
+ return $"{member.Name} ({string.Join(", ", formatedParameters)})";
+ }
+}
\ No newline at end of file
diff --git a/source/LookupEngine/LookupEngine.csproj b/source/LookupEngine/LookupEngine.csproj
new file mode 100644
index 000000000..a274d52fb
--- /dev/null
+++ b/source/LookupEngine/LookupEngine.csproj
@@ -0,0 +1,11 @@
+
+
+
+ net48;net8.0-windows
+
+
+
+
+
+
+
diff --git a/source/LookupEngine/Options/DecomposeOptions.cs b/source/LookupEngine/Options/DecomposeOptions.cs
new file mode 100644
index 000000000..092642ecf
--- /dev/null
+++ b/source/LookupEngine/Options/DecomposeOptions.cs
@@ -0,0 +1,42 @@
+using System.Collections;
+using JetBrains.Annotations;
+using LookupEngine.Abstractions.Decomposition;
+using LookupEngine.Descriptors;
+
+// ReSharper disable once CheckNamespace
+namespace LookupEngine;
+
+[PublicAPI]
+public class DecomposeOptions
+{
+ private Func? _typeResolver;
+
+ public bool IncludeRoot { get; set; }
+ public bool IncludeFields { get; set; }
+ public bool IncludeEvents { get; set; }
+ public bool IncludeUnsupported { get; set; }
+ public bool IncludePrivateMembers { get; set; }
+ public bool IncludeStaticMembers { get; set; }
+ public bool EnableExtensions { get; set; }
+ public bool EnableRedirection { get; set; }
+
+ public Func TypeResolver
+ {
+ get { return _typeResolver ??= DefaultResolveMap; }
+ set => _typeResolver = value;
+ }
+
+ public static DecomposeOptions Default => new();
+
+ private static Descriptor DefaultResolveMap(object? obj, Type? type)
+ {
+ return obj switch
+ {
+ bool value when type is null || type == typeof(bool) => new BooleanDescriptor(value),
+ string value when type is null || type == typeof(string) => new StringDescriptor(value),
+ IEnumerable value => new EnumerableDescriptor(value),
+ Exception value when type is null || type == typeof(Exception) => new ExceptionDescriptor(value),
+ _ => new ObjectDescriptor(obj)
+ };
+ }
+}
\ No newline at end of file
diff --git a/source/LookupEngine/Options/DecomposeOptions{T}.cs b/source/LookupEngine/Options/DecomposeOptions{T}.cs
new file mode 100644
index 000000000..4356bf93a
--- /dev/null
+++ b/source/LookupEngine/Options/DecomposeOptions{T}.cs
@@ -0,0 +1,9 @@
+using JetBrains.Annotations;
+
+namespace LookupEngine.Options;
+
+[PublicAPI]
+public class DecomposeOptions : DecomposeOptions
+{
+ public required TContext Context { get; set; }
+}
\ No newline at end of file
diff --git a/source/RevitLookup/Core/Contracts/IDescriptorConnector.cs b/source/RevitLookup.Abstractions/Configuration/IContextMenuConnector.cs
similarity index 86%
rename from source/RevitLookup/Core/Contracts/IDescriptorConnector.cs
rename to source/RevitLookup.Abstractions/Configuration/IContextMenuConnector.cs
index e8369e2f8..37536a07d 100644
--- a/source/RevitLookup/Core/Contracts/IDescriptorConnector.cs
+++ b/source/RevitLookup.Abstractions/Configuration/IContextMenuConnector.cs
@@ -20,12 +20,12 @@
using System.Windows.Controls;
-namespace RevitLookup.Core.Contracts;
+namespace RevitLookup.Abstractions.Configuration;
///
/// Indicates that additional members can be added to the descriptor
///
-public interface IDescriptorConnector
+public interface IContextMenuConnector
{
- void RegisterMenu(ContextMenu contextMenu);
+ void RegisterMenu(ContextMenu contextMenu, IServiceProvider serviceProvider);
}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/Models/AboutProgram/OpenSourceSoftware.cs b/source/RevitLookup.Abstractions/Models/AboutProgram/OpenSourceSoftware.cs
new file mode 100644
index 000000000..84d394f42
--- /dev/null
+++ b/source/RevitLookup.Abstractions/Models/AboutProgram/OpenSourceSoftware.cs
@@ -0,0 +1,9 @@
+namespace RevitLookup.Abstractions.Models.AboutProgram;
+
+public sealed class OpenSourceSoftware
+{
+ public required string SoftwareName { get; set; }
+ public required string SoftwareUri { get; set; }
+ public required string LicenseName { get; set; }
+ public required string LicenseUri { get; set; }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/Models/Decomposition/KnownDecompositionObject.cs b/source/RevitLookup.Abstractions/Models/Decomposition/KnownDecompositionObject.cs
new file mode 100644
index 000000000..c2a13de9b
--- /dev/null
+++ b/source/RevitLookup.Abstractions/Models/Decomposition/KnownDecompositionObject.cs
@@ -0,0 +1,23 @@
+namespace RevitLookup.Abstractions.Models.Decomposition;
+
+public enum KnownDecompositionObject
+{
+ View,
+ Document,
+ Application,
+ UiApplication,
+ UiControlledApplication,
+ Database,
+ DependentElements,
+ Selection,
+ Face,
+ Edge,
+ Point,
+ SubElement,
+ LinkedElement,
+ ComponentManager,
+ PerformanceAdviser,
+ UpdaterRegistry,
+ Services,
+ Schemas
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/Models/EventArgs/EventInfoArgs.cs b/source/RevitLookup.Abstractions/Models/EventArgs/EventInfoArgs.cs
new file mode 100644
index 000000000..b13df9573
--- /dev/null
+++ b/source/RevitLookup.Abstractions/Models/EventArgs/EventInfoArgs.cs
@@ -0,0 +1,7 @@
+namespace RevitLookup.Abstractions.Models.EventArgs;
+
+public sealed class EventInfoArgs
+{
+ public required string EventName { get; set; }
+ public required object Arguments { get; set; }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/Models/GitHub/GitHubResponse.cs b/source/RevitLookup.Abstractions/Models/GitHub/GitHubResponse.cs
new file mode 100644
index 000000000..18b6ca9a7
--- /dev/null
+++ b/source/RevitLookup.Abstractions/Models/GitHub/GitHubResponse.cs
@@ -0,0 +1,34 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using System.Text.Json.Serialization;
+
+namespace RevitLookup.Abstractions.Models.GitHub;
+
+[Serializable]
+public sealed class GitHubResponse
+{
+ [JsonPropertyName("html_url")] public string? Url { get; set; }
+ [JsonPropertyName("tag_name")] public string? TagName { get; set; }
+ [JsonPropertyName("draft")] public bool Draft { get; set; }
+ [JsonPropertyName("prerelease")] public bool PreRelease { get; set; }
+ [JsonPropertyName("published_at")] public DateTimeOffset PublishedDate { get; set; }
+ [JsonPropertyName("assets")] public List? Assets { get; set; }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/Models/GitHub/GutHubResponseAsset.cs b/source/RevitLookup.Abstractions/Models/GitHub/GutHubResponseAsset.cs
new file mode 100644
index 000000000..227793df9
--- /dev/null
+++ b/source/RevitLookup.Abstractions/Models/GitHub/GutHubResponseAsset.cs
@@ -0,0 +1,10 @@
+using System.Text.Json.Serialization;
+
+namespace RevitLookup.Abstractions.Models.GitHub;
+
+[Serializable]
+public sealed class GutHubResponseAsset
+{
+ [JsonPropertyName("name")] public string? Name { get; set; }
+ [JsonPropertyName("browser_download_url")] public string? DownloadUrl { get; set; }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/Models/Settings/GeneralSettings.cs b/source/RevitLookup.Abstractions/Models/Settings/GeneralSettings.cs
new file mode 100644
index 000000000..ba955255f
--- /dev/null
+++ b/source/RevitLookup.Abstractions/Models/Settings/GeneralSettings.cs
@@ -0,0 +1,31 @@
+using System.Text.Json.Serialization;
+using Wpf.Ui.Animations;
+using Wpf.Ui.Appearance;
+using Wpf.Ui.Controls;
+
+namespace RevitLookup.Abstractions.Models.Settings;
+
+[Serializable]
+public sealed class GeneralSettings
+{
+ [JsonPropertyName("Theme")] public ApplicationTheme Theme { get; set; }
+ [JsonPropertyName("Background")] public WindowBackdropType Background { get; set; }
+ [JsonPropertyName("Transition")] public Transition Transition { get; set; }
+
+ [JsonPropertyName("WindowWidth")] public double WindowWidth { get; set; }
+ [JsonPropertyName("WindowHeight")] public double WindowHeight { get; set; }
+
+ [JsonPropertyName("IsPrivateAllowed")] public bool IncludePrivate { get; set; }
+ [JsonPropertyName("IsFieldsAllowed")] public bool IncludeFields { get; set; }
+ [JsonPropertyName("IsStaticAllowed")] public bool IncludeStatic { get; set; }
+ [JsonPropertyName("IsEventsAllowed")] public bool IncludeEvents { get; set; }
+ [JsonPropertyName("IsExtensionsAllowed")] public bool IncludeExtensions { get; set; }
+ [JsonPropertyName("IsUnsupportedAllowed")] public bool IncludeUnsupported { get; set; }
+ [JsonPropertyName("IsRootHierarchyAllowed")] public bool IncludeRootHierarchy { get; set; }
+
+ [JsonPropertyName("IsHardwareRenderingAllowed")] public bool UseHardwareRendering { get; set; }
+ [JsonPropertyName("IsTimeColumnAllowed")] public bool ShowTimeColumn { get; set; }
+ [JsonPropertyName("ShowMemoryColumn")] public bool ShowMemoryColumn { get; set; }
+ [JsonPropertyName("UseSizeRestoring")] public bool UseSizeRestoring { get; set; }
+ [JsonPropertyName("IsModifyTabAllowed")] public bool UseModifyTab { get; set; }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/Models/Settings/RenderSettings.cs b/source/RevitLookup.Abstractions/Models/Settings/RenderSettings.cs
new file mode 100644
index 000000000..6e6103714
--- /dev/null
+++ b/source/RevitLookup.Abstractions/Models/Settings/RenderSettings.cs
@@ -0,0 +1,107 @@
+using System.Text.Json.Serialization;
+using System.Windows.Media;
+
+namespace RevitLookup.Abstractions.Models.Settings;
+
+[Serializable]
+public sealed class RenderSettings
+{
+ [JsonPropertyName("BoundingBoxSettings")] public required BoundingBoxVisualizationSettings BoundingBoxSettings { get; set; }
+ [JsonPropertyName("FaceSettings")] public required FaceVisualizationSettings FaceSettings { get; set; }
+ [JsonPropertyName("MeshSettings")] public required MeshVisualizationSettings MeshSettings { get; set; }
+ [JsonPropertyName("PolylineSettings")] public required PolylineVisualizationSettings PolylineSettings { get; set; }
+ [JsonPropertyName("SolidSettings")] public required SolidVisualizationSettings SolidSettings { get; set; }
+ [JsonPropertyName("XyzSettings")] public required XyzVisualizationSettings XyzSettings { get; set; }
+}
+
+[Serializable]
+public class BoundingBoxVisualizationSettings
+{
+ [JsonPropertyName("Transparency")] public double Transparency { get; set; }
+
+ [JsonPropertyName("SurfaceColor")] public Color SurfaceColor { get; set; }
+ [JsonPropertyName("EdgeColor")] public Color EdgeColor { get; set; }
+ [JsonPropertyName("AxisColor")] public Color AxisColor { get; set; }
+
+ [JsonPropertyName("ShowSurface")] public bool ShowSurface { get; set; }
+ [JsonPropertyName("ShowEdge")] public bool ShowEdge { get; set; }
+ [JsonPropertyName("ShowAxis")] public bool ShowAxis { get; set; }
+}
+
+[Serializable]
+public class FaceVisualizationSettings
+{
+ [JsonPropertyName("Transparency")] public double Transparency { get; set; }
+ [JsonPropertyName("Extrusion")] public double Extrusion { get; set; }
+ [JsonPropertyName("MinExtrusion")] public double MinExtrusion { get; set; }
+
+ [JsonPropertyName("SurfaceColor")] public Color SurfaceColor { get; set; }
+ [JsonPropertyName("MeshColor")] public Color MeshColor { get; set; }
+ [JsonPropertyName("NormalVectorColor")] public Color NormalVectorColor { get; set; }
+
+ [JsonPropertyName("ShowSurface")] public bool ShowSurface { get; set; }
+ [JsonPropertyName("ShowMeshGrid")] public bool ShowMeshGrid { get; set; }
+ [JsonPropertyName("ShowNormalVector")] public bool ShowNormalVector { get; set; }
+}
+
+[Serializable]
+public class MeshVisualizationSettings
+{
+ [JsonPropertyName("Transparency")] public double Transparency { get; set; }
+ [JsonPropertyName("Extrusion")] public double Extrusion { get; set; }
+ [JsonPropertyName("MinExtrusion")] public double MinExtrusion { get; set; }
+
+ [JsonPropertyName("SurfaceColor")] public Color SurfaceColor { get; set; }
+ [JsonPropertyName("MeshColor")] public Color MeshColor { get; set; }
+ [JsonPropertyName("NormalVectorColor")] public Color NormalVectorColor { get; set; }
+
+ [JsonPropertyName("ShowSurface")] public bool ShowSurface { get; set; }
+ [JsonPropertyName("ShowMeshGrid")] public bool ShowMeshGrid { get; set; }
+ [JsonPropertyName("ShowNormalVector")] public bool ShowNormalVector { get; set; }
+}
+
+[Serializable]
+public class PolylineVisualizationSettings
+{
+ [JsonPropertyName("Transparency")] public double Transparency { get; set; }
+ [JsonPropertyName("Diameter")] public double Diameter { get; set; }
+ [JsonPropertyName("MinThickness")] public double MinThickness { get; set; }
+
+ [JsonPropertyName("SurfaceColor")] public Color SurfaceColor { get; set; }
+ [JsonPropertyName("CurveColor")] public Color CurveColor { get; set; }
+ [JsonPropertyName("DirectionColor")] public Color DirectionColor { get; set; }
+
+ [JsonPropertyName("ShowSurface")] public bool ShowSurface { get; set; }
+ [JsonPropertyName("ShowCurve")] public bool ShowCurve { get; set; }
+ [JsonPropertyName("ShowDirection")] public bool ShowDirection { get; set; }
+}
+
+[Serializable]
+public sealed class SolidVisualizationSettings
+{
+ [JsonPropertyName("Transparency")] public double Transparency { get; set; }
+ [JsonPropertyName("Scale")] public double Scale { get; set; }
+
+ [JsonPropertyName("FaceColor")] public Color FaceColor { get; set; }
+ [JsonPropertyName("EdgeColor")] public Color EdgeColor { get; set; }
+
+ [JsonPropertyName("ShowFace")] public bool ShowFace { get; set; }
+ [JsonPropertyName("ShowEdge")] public bool ShowEdge { get; set; }
+}
+
+[Serializable]
+public class XyzVisualizationSettings
+{
+ [JsonPropertyName("Transparency")] public double Transparency { get; set; }
+ [JsonPropertyName("AxisLength")] public double AxisLength { get; set; }
+ [JsonPropertyName("MinAxisLength")] public double MinAxisLength { get; set; }
+
+ [JsonPropertyName("XColor")] public Color XColor { get; set; }
+ [JsonPropertyName("YColor")] public Color YColor { get; set; }
+ [JsonPropertyName("ZColor")] public Color ZColor { get; set; }
+
+ [JsonPropertyName("ShowPlane")] public bool ShowPlane { get; set; }
+ [JsonPropertyName("ShowXAxis")] public bool ShowXAxis { get; set; }
+ [JsonPropertyName("ShowYAxis")] public bool ShowYAxis { get; set; }
+ [JsonPropertyName("ShowZAxis")] public bool ShowZAxis { get; set; }
+}
\ No newline at end of file
diff --git a/source/RevitLookup/Models/ModuleInfo.cs b/source/RevitLookup.Abstractions/Models/Tools/ModuleInfo.cs
similarity index 96%
rename from source/RevitLookup/Models/ModuleInfo.cs
rename to source/RevitLookup.Abstractions/Models/Tools/ModuleInfo.cs
index 2abae7f69..8c4103abb 100644
--- a/source/RevitLookup/Models/ModuleInfo.cs
+++ b/source/RevitLookup.Abstractions/Models/Tools/ModuleInfo.cs
@@ -18,7 +18,7 @@
// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
// (Rights in Technical Data and Computer Software), as applicable.
-namespace RevitLookup.Models;
+namespace RevitLookup.Abstractions.Models.Tools;
public sealed class ModuleInfo
{
diff --git a/source/RevitLookup.Abstractions/Models/Tools/UnitInfo.cs b/source/RevitLookup.Abstractions/Models/Tools/UnitInfo.cs
new file mode 100644
index 000000000..bc2705969
--- /dev/null
+++ b/source/RevitLookup.Abstractions/Models/Tools/UnitInfo.cs
@@ -0,0 +1,29 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+namespace RevitLookup.Abstractions.Models.Tools;
+
+public sealed class UnitInfo
+{
+ public required string Unit { get; init; }
+ public required string Label { get; init; }
+ public required object Value { get; init; }
+ public string? Class { get; init; }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/Models/UserInterface/NavigationCardGroup.cs b/source/RevitLookup.Abstractions/Models/UserInterface/NavigationCardGroup.cs
new file mode 100644
index 000000000..36f2e66e8
--- /dev/null
+++ b/source/RevitLookup.Abstractions/Models/UserInterface/NavigationCardGroup.cs
@@ -0,0 +1,7 @@
+namespace RevitLookup.Abstractions.Models.UserInterface;
+
+public sealed class NavigationCardGroup
+{
+ public required string GroupName { get; set; }
+ public required List Items { get; set; }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/Models/UserInterface/NavigationCardItem.cs b/source/RevitLookup.Abstractions/Models/UserInterface/NavigationCardItem.cs
new file mode 100644
index 000000000..4aadf9250
--- /dev/null
+++ b/source/RevitLookup.Abstractions/Models/UserInterface/NavigationCardItem.cs
@@ -0,0 +1,13 @@
+using System.Windows.Input;
+using Wpf.Ui.Controls;
+
+namespace RevitLookup.Abstractions.Models.UserInterface;
+
+public sealed class NavigationCardItem
+{
+ public required string Title { get; set; }
+ public string? Description { get; set; }
+ public required SymbolRegular Icon { get; set; }
+ public required ICommand Command { get; set; }
+ public object? CommandParameter { get; set; }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/ObservableModels/Decomposition/ObservableDecomposedMember.cs b/source/RevitLookup.Abstractions/ObservableModels/Decomposition/ObservableDecomposedMember.cs
new file mode 100644
index 000000000..60245a613
--- /dev/null
+++ b/source/RevitLookup.Abstractions/ObservableModels/Decomposition/ObservableDecomposedMember.cs
@@ -0,0 +1,16 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+using LookupEngine.Abstractions.Enums;
+
+namespace RevitLookup.Abstractions.ObservableModels.Decomposition;
+
+public sealed class ObservableDecomposedMember : ObservableObject
+{
+ public required int Depth { get; set; }
+ public required string Name { get; set; }
+ public required string DeclaringTypeName { get; set; }
+ public required string DeclaringTypeFullName { get; set; }
+ public double ComputationTime { get; set; }
+ public long AllocatedBytes { get; set; }
+ public MemberAttributes MemberAttributes { get; set; }
+ public required ObservableDecomposedValue Value { get; set; }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/ObservableModels/Decomposition/ObservableDecomposedObject.cs b/source/RevitLookup.Abstractions/ObservableModels/Decomposition/ObservableDecomposedObject.cs
new file mode 100644
index 000000000..cc5c68259
--- /dev/null
+++ b/source/RevitLookup.Abstractions/ObservableModels/Decomposition/ObservableDecomposedObject.cs
@@ -0,0 +1,22 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+using LookupEngine.Abstractions.Decomposition;
+
+namespace RevitLookup.Abstractions.ObservableModels.Decomposition;
+
+public sealed partial class ObservableDecomposedObject : ObservableObject
+{
+ [ObservableProperty] private List _members = [];
+ [ObservableProperty] private List _filteredMembers = [];
+
+ public required object? RawValue { get; init; }
+ public required string Name { get; set; }
+ public required string TypeName { get; set; }
+ public required string TypeFullName { get; set; }
+ public string? Description { get; set; }
+ public Descriptor? Descriptor { get; init; }
+
+ partial void OnMembersChanged(List value)
+ {
+ FilteredMembers = value;
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/ObservableModels/Decomposition/ObservableDecomposedObjectsGroup.cs b/source/RevitLookup.Abstractions/ObservableModels/Decomposition/ObservableDecomposedObjectsGroup.cs
new file mode 100644
index 000000000..345ecda9f
--- /dev/null
+++ b/source/RevitLookup.Abstractions/ObservableModels/Decomposition/ObservableDecomposedObjectsGroup.cs
@@ -0,0 +1,10 @@
+using System.Collections.ObjectModel;
+using CommunityToolkit.Mvvm.ComponentModel;
+
+namespace RevitLookup.Abstractions.ObservableModels.Decomposition;
+
+public sealed class ObservableDecomposedObjectsGroup : ObservableObject
+{
+ public required string GroupName { get; set; }
+ public required ObservableCollection GroupItems { get; set; }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/ObservableModels/Decomposition/ObservableDecomposedValue.cs b/source/RevitLookup.Abstractions/ObservableModels/Decomposition/ObservableDecomposedValue.cs
new file mode 100644
index 000000000..37018719f
--- /dev/null
+++ b/source/RevitLookup.Abstractions/ObservableModels/Decomposition/ObservableDecomposedValue.cs
@@ -0,0 +1,14 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+using LookupEngine.Abstractions.Decomposition;
+
+namespace RevitLookup.Abstractions.ObservableModels.Decomposition;
+
+public sealed class ObservableDecomposedValue : ObservableObject
+{
+ public required object? RawValue { get; init; }
+ public required string Name { get; set; }
+ public required string TypeName { get; set; }
+ public required string TypeFullName { get; set; }
+ public string? Description { get; set; }
+ public Descriptor? Descriptor { get; set; }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/ObservableModels/Entries/ObservableIniEntry.cs b/source/RevitLookup.Abstractions/ObservableModels/Entries/ObservableIniEntry.cs
new file mode 100644
index 000000000..c05bd86ce
--- /dev/null
+++ b/source/RevitLookup.Abstractions/ObservableModels/Entries/ObservableIniEntry.cs
@@ -0,0 +1,66 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using System.ComponentModel.DataAnnotations;
+using CommunityToolkit.Mvvm.ComponentModel;
+
+namespace RevitLookup.Abstractions.ObservableModels.Entries;
+
+public sealed partial class ObservableIniEntry : ObservableValidator
+{
+ [ObservableProperty] [Required] [NotifyDataErrorInfo] private string _category = string.Empty;
+ [ObservableProperty] [Required] [NotifyDataErrorInfo] private string _property = string.Empty;
+ [ObservableProperty] private string _value = string.Empty;
+ [ObservableProperty] private string? _defaultValue;
+ [ObservableProperty] private bool _isActive;
+ [ObservableProperty] private bool _isModified;
+
+ public bool UserDefined { get; set; }
+
+ public void Validate()
+ {
+ ValidateAllProperties();
+ }
+
+ partial void OnIsActiveChanged(bool value)
+ {
+ UserDefined = true;
+ }
+
+ partial void OnValueChanged(string value)
+ {
+ IsModified = DefaultValue is not null && value != DefaultValue;
+ }
+
+ partial void OnDefaultValueChanged(string? value)
+ {
+ IsModified = value != Value;
+ }
+
+ public ObservableIniEntry Clone()
+ {
+ return new ObservableIniEntry
+ {
+ Category = Category,
+ Property = Property,
+ Value = Value
+ };
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/Options/AssemblyOptions.cs b/source/RevitLookup.Abstractions/Options/AssemblyOptions.cs
new file mode 100644
index 000000000..2f0deb717
--- /dev/null
+++ b/source/RevitLookup.Abstractions/Options/AssemblyOptions.cs
@@ -0,0 +1,8 @@
+namespace RevitLookup.Abstractions.Options;
+
+public sealed class AssemblyOptions
+{
+ public required string Framework { get; set; }
+ public required Version Version { get; set; }
+ public required bool HasAdminAccess { get; set; }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/Options/FoldersOptions.cs b/source/RevitLookup.Abstractions/Options/FoldersOptions.cs
new file mode 100644
index 000000000..2fc5739a9
--- /dev/null
+++ b/source/RevitLookup.Abstractions/Options/FoldersOptions.cs
@@ -0,0 +1,10 @@
+namespace RevitLookup.Abstractions.Options;
+
+public sealed class FoldersOptions
+{
+ public required string RootFolder { get; set; }
+ public required string ConfigFolder { get; set; }
+ public required string DownloadsFolder { get; set; }
+ public required string GeneralSettingsPath { get; set; }
+ public required string RenderSettingsPath { get; set; }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/RevitLookup.Abstractions.csproj b/source/RevitLookup.Abstractions/RevitLookup.Abstractions.csproj
new file mode 100644
index 000000000..a9d6178bf
--- /dev/null
+++ b/source/RevitLookup.Abstractions/RevitLookup.Abstractions.csproj
@@ -0,0 +1,27 @@
+
+
+
+ net48;net8.0-windows
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/Services/Appearance/IThemeWatcherService.cs b/source/RevitLookup.Abstractions/Services/Appearance/IThemeWatcherService.cs
new file mode 100644
index 000000000..fb33870ef
--- /dev/null
+++ b/source/RevitLookup.Abstractions/Services/Appearance/IThemeWatcherService.cs
@@ -0,0 +1,11 @@
+using System.Windows;
+
+namespace RevitLookup.Abstractions.Services.Appearance;
+
+public interface IThemeWatcherService
+{
+ void Initialize();
+ void Watch();
+ void Watch(FrameworkElement frameworkElement);
+ void Unwatch();
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/Services/Application/IRevitLookupUiService.cs b/source/RevitLookup.Abstractions/Services/Application/IRevitLookupUiService.cs
new file mode 100644
index 000000000..d64830bdb
--- /dev/null
+++ b/source/RevitLookup.Abstractions/Services/Application/IRevitLookupUiService.cs
@@ -0,0 +1,32 @@
+using System.Collections;
+using System.Windows;
+using System.Windows.Controls;
+using RevitLookup.Abstractions.Models.Decomposition;
+using RevitLookup.Abstractions.ObservableModels.Decomposition;
+
+namespace RevitLookup.Abstractions.Services.Application;
+
+public interface IRevitLookupUiService : ILookupServiceDependsStage, ILookupServiceRunStage
+{
+ ILookupServiceDependsStage Decompose(KnownDecompositionObject decompositionObject);
+ ILookupServiceDependsStage Decompose(object? obj);
+ ILookupServiceDependsStage Decompose(IEnumerable objects);
+ ILookupServiceDependsStage Decompose(ObservableDecomposedObject decomposedObject);
+ ILookupServiceDependsStage Decompose(List decomposedObjects);
+}
+
+public interface ILookupServiceDependsStage : ILookupServiceShowStage
+{
+ ILookupServiceShowStage DependsOn(Window parent);
+}
+
+public interface ILookupServiceShowStage
+{
+ ILookupServiceRunStage Show() where T : Page;
+ // ILookupServiceRunStage ShowDialog() where T : Page;
+}
+
+public interface ILookupServiceRunStage
+{
+ void RunService(Action handler) where T : class;
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/Services/Decomposition/IDecompositionService.cs b/source/RevitLookup.Abstractions/Services/Decomposition/IDecompositionService.cs
new file mode 100644
index 000000000..efddf22bf
--- /dev/null
+++ b/source/RevitLookup.Abstractions/Services/Decomposition/IDecompositionService.cs
@@ -0,0 +1,11 @@
+using System.Collections;
+using RevitLookup.Abstractions.ObservableModels.Decomposition;
+
+namespace RevitLookup.Abstractions.Services.Decomposition;
+
+public interface IDecompositionService
+{
+ Task DecomposeAsync(object obj);
+ Task> DecomposeAsync(IEnumerable objects);
+ Task> DecomposeMembersAsync(ObservableDecomposedObject decomposedObject);
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/Services/Decomposition/IVisualDecompositionService.cs b/source/RevitLookup.Abstractions/Services/Decomposition/IVisualDecompositionService.cs
new file mode 100644
index 000000000..d1819c4cf
--- /dev/null
+++ b/source/RevitLookup.Abstractions/Services/Decomposition/IVisualDecompositionService.cs
@@ -0,0 +1,14 @@
+using System.Collections;
+using RevitLookup.Abstractions.Models.Decomposition;
+using RevitLookup.Abstractions.ObservableModels.Decomposition;
+
+namespace RevitLookup.Abstractions.Services.Decomposition;
+
+public interface IVisualDecompositionService
+{
+ Task VisualizeDecompositionAsync(KnownDecompositionObject decompositionObject);
+ Task VisualizeDecompositionAsync(object? obj);
+ Task VisualizeDecompositionAsync(IEnumerable objects);
+ Task VisualizeDecompositionAsync(ObservableDecomposedObject decomposedObject);
+ Task VisualizeDecompositionAsync(List decomposedObjects);
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/Services/Presentation/INotificationService.cs b/source/RevitLookup.Abstractions/Services/Presentation/INotificationService.cs
new file mode 100644
index 000000000..0d1b03161
--- /dev/null
+++ b/source/RevitLookup.Abstractions/Services/Presentation/INotificationService.cs
@@ -0,0 +1,9 @@
+namespace RevitLookup.Abstractions.Services.Presentation;
+
+public interface INotificationService
+{
+ void ShowSuccess(string title, string message);
+ void ShowWarning(string title, string message);
+ void ShowError(string title, string message);
+ void ShowError(string title, Exception exception);
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/Services/Presentation/IWindowIntercomService.cs b/source/RevitLookup.Abstractions/Services/Presentation/IWindowIntercomService.cs
new file mode 100644
index 000000000..4cb5dd1da
--- /dev/null
+++ b/source/RevitLookup.Abstractions/Services/Presentation/IWindowIntercomService.cs
@@ -0,0 +1,14 @@
+using System.Windows;
+using System.Windows.Threading;
+
+namespace RevitLookup.Abstractions.Services.Presentation;
+
+public interface IWindowIntercomService
+{
+ Dispatcher Dispatcher { get; }
+ List OpenedWindows { get; }
+
+ Window GetHost();
+ void SetHost(Window host);
+ void SetSharedHost(Window host);
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/Services/Settings/ISettingsService.cs b/source/RevitLookup.Abstractions/Services/Settings/ISettingsService.cs
new file mode 100644
index 000000000..54d24b693
--- /dev/null
+++ b/source/RevitLookup.Abstractions/Services/Settings/ISettingsService.cs
@@ -0,0 +1,13 @@
+using RevitLookup.Abstractions.Models.Settings;
+
+namespace RevitLookup.Abstractions.Services.Settings;
+
+public interface ISettingsService
+{
+ public GeneralSettings GeneralSettings { get; }
+ public RenderSettings RenderSettings { get; }
+ void SaveSettings();
+ void LoadSettings();
+ void ResetGeneralSettings();
+ void ResetRenderSettings();
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/Services/Settings/ISoftwareUpdateService.cs b/source/RevitLookup.Abstractions/Services/Settings/ISoftwareUpdateService.cs
new file mode 100644
index 000000000..f8336265d
--- /dev/null
+++ b/source/RevitLookup.Abstractions/Services/Settings/ISoftwareUpdateService.cs
@@ -0,0 +1,32 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+namespace RevitLookup.Abstractions.Services.Settings;
+
+public interface ISoftwareUpdateService
+{
+ public string? NewVersion { get; }
+ public string? ReleaseNotesUrl { get; }
+ public string? LocalFilePath { get; }
+ public DateTime? LatestCheckDate { get; }
+
+ Task CheckUpdatesAsync();
+ Task DownloadUpdate();
+}
\ No newline at end of file
diff --git a/source/RevitLookup/Services/Enums/SoftwareUpdateState.cs b/source/RevitLookup.Abstractions/States/SoftwareUpdateState.cs
similarity index 91%
rename from source/RevitLookup/Services/Enums/SoftwareUpdateState.cs
rename to source/RevitLookup.Abstractions/States/SoftwareUpdateState.cs
index 305b455c6..dac9c0f9c 100644
--- a/source/RevitLookup/Services/Enums/SoftwareUpdateState.cs
+++ b/source/RevitLookup.Abstractions/States/SoftwareUpdateState.cs
@@ -18,13 +18,12 @@
// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
// (Rights in Technical Data and Computer Software), as applicable.
-namespace RevitLookup.Services.Enums;
+namespace RevitLookup.Abstractions.States;
public enum SoftwareUpdateState
{
UpToDate,
- ErrorDownloading,
- ErrorChecking,
ReadyToDownload,
- ReadyToInstall
+ ReadyToInstall,
+ Error
}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/ViewModels/AboutProgram/IAboutViewModel.cs b/source/RevitLookup.Abstractions/ViewModels/AboutProgram/IAboutViewModel.cs
new file mode 100644
index 000000000..ee6ef7964
--- /dev/null
+++ b/source/RevitLookup.Abstractions/ViewModels/AboutProgram/IAboutViewModel.cs
@@ -0,0 +1,19 @@
+using CommunityToolkit.Mvvm.Input;
+using RevitLookup.Abstractions.States;
+
+namespace RevitLookup.Abstractions.ViewModels.AboutProgram;
+
+public interface IAboutViewModel
+{
+ SoftwareUpdateState State { get; set; }
+ Version CurrentVersion { get; set; }
+ string NewVersion { get; set; }
+ string ErrorMessage { get; set; }
+ string ReleaseNotesUrl { get; set; }
+ string LatestCheckDate { get; set; }
+ string Runtime { get; set; }
+
+ IAsyncRelayCommand CheckUpdatesCommand { get; }
+ IAsyncRelayCommand DownloadUpdateCommand { get; }
+ IAsyncRelayCommand ShowSoftwareDialogCommand { get; }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/ViewModels/AboutProgram/IOpenSourceViewModel.cs b/source/RevitLookup.Abstractions/ViewModels/AboutProgram/IOpenSourceViewModel.cs
new file mode 100644
index 000000000..1122e1f24
--- /dev/null
+++ b/source/RevitLookup.Abstractions/ViewModels/AboutProgram/IOpenSourceViewModel.cs
@@ -0,0 +1,8 @@
+using RevitLookup.Abstractions.Models.AboutProgram;
+
+namespace RevitLookup.Abstractions.ViewModels.AboutProgram;
+
+public interface IOpenSourceViewModel
+{
+ List Software { get; }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/ViewModels/Dashboard/IDashboardViewModel.cs b/source/RevitLookup.Abstractions/ViewModels/Dashboard/IDashboardViewModel.cs
new file mode 100644
index 000000000..675aa08e6
--- /dev/null
+++ b/source/RevitLookup.Abstractions/ViewModels/Dashboard/IDashboardViewModel.cs
@@ -0,0 +1,31 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using CommunityToolkit.Mvvm.Input;
+using RevitLookup.Abstractions.Models.UserInterface;
+
+namespace RevitLookup.Abstractions.ViewModels.Dashboard;
+
+public interface IDashboardViewModel
+{
+ List NavigationGroups { get; }
+ IAsyncRelayCommand NavigatePageCommand { get; }
+ IAsyncRelayCommand OpenDialogCommand { get; }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/ViewModels/Decomposition/IDecompositionSummaryViewModel.cs b/source/RevitLookup.Abstractions/ViewModels/Decomposition/IDecompositionSummaryViewModel.cs
new file mode 100644
index 000000000..e7febfb9e
--- /dev/null
+++ b/source/RevitLookup.Abstractions/ViewModels/Decomposition/IDecompositionSummaryViewModel.cs
@@ -0,0 +1,10 @@
+using System.Collections.ObjectModel;
+using RevitLookup.Abstractions.ObservableModels.Decomposition;
+
+namespace RevitLookup.Abstractions.ViewModels.Decomposition;
+
+public interface IDecompositionSummaryViewModel : ISummaryViewModel
+{
+ ObservableCollection FilteredDecomposedObjects { get; }
+ void RemoveItem(object target);
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/ViewModels/Decomposition/IEventsSummaryViewModel.cs b/source/RevitLookup.Abstractions/ViewModels/Decomposition/IEventsSummaryViewModel.cs
new file mode 100644
index 000000000..7a3308eb8
--- /dev/null
+++ b/source/RevitLookup.Abstractions/ViewModels/Decomposition/IEventsSummaryViewModel.cs
@@ -0,0 +1,10 @@
+using System.Collections.ObjectModel;
+using RevitLookup.Abstractions.ObservableModels.Decomposition;
+using Wpf.Ui.Abstractions.Controls;
+
+namespace RevitLookup.Abstractions.ViewModels.Decomposition;
+
+public interface IEventsSummaryViewModel : ISummaryViewModel, INavigationAware
+{
+ ObservableCollection FilteredDecomposedObjects { get; }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/ViewModels/Decomposition/ISummaryViewModel.cs b/source/RevitLookup.Abstractions/ViewModels/Decomposition/ISummaryViewModel.cs
new file mode 100644
index 000000000..fe740ff9a
--- /dev/null
+++ b/source/RevitLookup.Abstractions/ViewModels/Decomposition/ISummaryViewModel.cs
@@ -0,0 +1,20 @@
+using RevitLookup.Abstractions.ObservableModels.Decomposition;
+
+namespace RevitLookup.Abstractions.ViewModels.Decomposition;
+
+public interface ISummaryViewModel
+{
+ string SearchText { get; set; }
+
+ //Objects
+ ObservableDecomposedObject? SelectedDecomposedObject { get; set; }
+ List DecomposedObjects { get; set; }
+
+ //Commands
+ Task RefreshMembersAsync();
+
+ //Navigation
+ void Navigate(object? value);
+ void Navigate(ObservableDecomposedObject value);
+ void Navigate(List values);
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/ViewModels/Settings/ISettingsViewModel.cs b/source/RevitLookup.Abstractions/ViewModels/Settings/ISettingsViewModel.cs
new file mode 100644
index 000000000..a17ef330d
--- /dev/null
+++ b/source/RevitLookup.Abstractions/ViewModels/Settings/ISettingsViewModel.cs
@@ -0,0 +1,39 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using CommunityToolkit.Mvvm.Input;
+using Wpf.Ui.Appearance;
+using Wpf.Ui.Controls;
+
+namespace RevitLookup.Abstractions.ViewModels.Settings;
+
+public interface ISettingsViewModel
+{
+ ApplicationTheme Theme { get; set; }
+ List Themes { get; }
+ WindowBackdropType Background { get; set; }
+ List BackgroundEffects { get; }
+ bool UseTransition { get; set; }
+ bool UseHardwareRendering { get; set; }
+ bool UseSizeRestoring { get; set; }
+ bool UseModifyTab { get; set; }
+
+ IAsyncRelayCommand ResetSettingsCommand { get; }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/ViewModels/Tools/IModulesViewModel.cs b/source/RevitLookup.Abstractions/ViewModels/Tools/IModulesViewModel.cs
new file mode 100644
index 000000000..c016c42c1
--- /dev/null
+++ b/source/RevitLookup.Abstractions/ViewModels/Tools/IModulesViewModel.cs
@@ -0,0 +1,10 @@
+using RevitLookup.Abstractions.Models.Tools;
+
+namespace RevitLookup.Abstractions.ViewModels.Tools;
+
+public interface IModulesViewModel
+{
+ string SearchText { get; set; }
+ List FilteredModules { get; set; }
+ List Modules { get; set; }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/ViewModels/Tools/IRevitSettingsViewModel.cs b/source/RevitLookup.Abstractions/ViewModels/Tools/IRevitSettingsViewModel.cs
new file mode 100644
index 000000000..bb2cacbe9
--- /dev/null
+++ b/source/RevitLookup.Abstractions/ViewModels/Tools/IRevitSettingsViewModel.cs
@@ -0,0 +1,33 @@
+using System.Collections.ObjectModel;
+using CommunityToolkit.Mvvm.Input;
+using RevitLookup.Abstractions.ObservableModels.Entries;
+
+namespace RevitLookup.Abstractions.ViewModels.Tools;
+
+public interface IRevitSettingsViewModel
+{
+ //Filter
+ bool Filtered { get; set; }
+ string CategoryFilter { get; set; }
+ string PropertyFilter { get; set; }
+ string ValueFilter { get; set; }
+ bool ShowUserSettingsFilter { get; set; }
+
+ //Items
+ ObservableIniEntry? SelectedEntry { get; set; }
+ List Entries { get; set; }
+ ObservableCollection FilteredEntries { get; set; }
+
+ //Commands
+ IRelayCommand ShowHelpCommand { get; }
+ IRelayCommand OpenSettingsCommand { get; }
+ IRelayCommand ClearFiltersCommand { get; }
+ IAsyncRelayCommand CreateEntryCommand { get; }
+ IRelayCommand ActivateEntryCommand { get; }
+ IRelayCommand DeleteEntryCommand { get; }
+ IRelayCommand RestoreDefaultCommand { get; }
+
+ Task>? InitializationTask { get; }
+ Task InitializeAsync();
+ Task UpdateEntryAsync();
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/ViewModels/Tools/ISearchElementsViewModel.cs b/source/RevitLookup.Abstractions/ViewModels/Tools/ISearchElementsViewModel.cs
new file mode 100644
index 000000000..45119a59a
--- /dev/null
+++ b/source/RevitLookup.Abstractions/ViewModels/Tools/ISearchElementsViewModel.cs
@@ -0,0 +1,7 @@
+namespace RevitLookup.Abstractions.ViewModels.Tools;
+
+public interface ISearchElementsViewModel
+{
+ string SearchText { get; set; }
+ Task SearchElementsAsync();
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/ViewModels/Tools/IUnitsViewModel.cs b/source/RevitLookup.Abstractions/ViewModels/Tools/IUnitsViewModel.cs
new file mode 100644
index 000000000..014ab79b1
--- /dev/null
+++ b/source/RevitLookup.Abstractions/ViewModels/Tools/IUnitsViewModel.cs
@@ -0,0 +1,14 @@
+using RevitLookup.Abstractions.Models.Tools;
+
+namespace RevitLookup.Abstractions.ViewModels.Tools;
+
+public interface IUnitsViewModel
+{
+ List Units { get; set; }
+ List FilteredUnits { get; set; }
+ string SearchText { get; set; }
+ void InitializeParameters();
+ void InitializeCategories();
+ void InitializeForgeSchema();
+ Task DecomposeAsync(UnitInfo unitInfo);
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/ViewModels/Visualization/IBoundingBoxVisualizationViewModel.cs b/source/RevitLookup.Abstractions/ViewModels/Visualization/IBoundingBoxVisualizationViewModel.cs
new file mode 100644
index 000000000..6826b3e02
--- /dev/null
+++ b/source/RevitLookup.Abstractions/ViewModels/Visualization/IBoundingBoxVisualizationViewModel.cs
@@ -0,0 +1,39 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using System.Windows.Media;
+
+namespace RevitLookup.Abstractions.ViewModels.Visualization;
+
+public interface IBoundingBoxVisualizationViewModel
+{
+ double Transparency { get; set; }
+
+ Color SurfaceColor { get; set; }
+ Color EdgeColor { get; set; }
+ Color AxisColor { get; set; }
+
+ bool ShowSurface { get; set; }
+ bool ShowEdge { get; set; }
+ bool ShowAxis { get; set; }
+
+ void RegisterServer(object boundingBoxXyz);
+ void UnregisterServer();
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/ViewModels/Visualization/IFaceVisualizationViewModel.cs b/source/RevitLookup.Abstractions/ViewModels/Visualization/IFaceVisualizationViewModel.cs
new file mode 100644
index 000000000..dd1a4fd8f
--- /dev/null
+++ b/source/RevitLookup.Abstractions/ViewModels/Visualization/IFaceVisualizationViewModel.cs
@@ -0,0 +1,42 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using System.Windows.Media;
+
+namespace RevitLookup.Abstractions.ViewModels.Visualization;
+
+public interface IFaceVisualizationViewModel
+{
+ public double MinExtrusion { get; }
+
+ double Extrusion { get; set; }
+ double Transparency { get; set; }
+
+ Color SurfaceColor { get; set; }
+ Color MeshColor { get; set; }
+ Color NormalVectorColor { get; set; }
+
+ bool ShowSurface { get; set; }
+ bool ShowMeshGrid { get; set; }
+ bool ShowNormalVector { get; set; }
+
+ public void RegisterServer(object face);
+ public void UnregisterServer();
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/ViewModels/Visualization/IMeshVisualizationViewModel.cs b/source/RevitLookup.Abstractions/ViewModels/Visualization/IMeshVisualizationViewModel.cs
new file mode 100644
index 000000000..8955a9b5f
--- /dev/null
+++ b/source/RevitLookup.Abstractions/ViewModels/Visualization/IMeshVisualizationViewModel.cs
@@ -0,0 +1,42 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using System.Windows.Media;
+
+namespace RevitLookup.Abstractions.ViewModels.Visualization;
+
+public interface IMeshVisualizationViewModel
+{
+ public double MinExtrusion { get; }
+
+ double Extrusion { get; set; }
+ double Transparency { get; set; }
+
+ Color SurfaceColor { get; set; }
+ Color MeshColor { get; set; }
+ Color NormalVectorColor { get; set; }
+
+ bool ShowSurface { get; set; }
+ bool ShowMeshGrid { get; set; }
+ bool ShowNormalVector { get; set; }
+
+ public void RegisterServer(object mesh);
+ public void UnregisterServer();
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/ViewModels/Visualization/IPolylineVisualizationViewModel.cs b/source/RevitLookup.Abstractions/ViewModels/Visualization/IPolylineVisualizationViewModel.cs
new file mode 100644
index 000000000..62c2c5884
--- /dev/null
+++ b/source/RevitLookup.Abstractions/ViewModels/Visualization/IPolylineVisualizationViewModel.cs
@@ -0,0 +1,42 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using System.Windows.Media;
+
+namespace RevitLookup.Abstractions.ViewModels.Visualization;
+
+public interface IPolylineVisualizationViewModel
+{
+ double MinThickness { get; }
+
+ double Diameter { get; set; }
+ double Transparency { get; set; }
+
+ Color SurfaceColor { get; set; }
+ Color CurveColor { get; set; }
+ Color DirectionColor { get; set; }
+
+ bool ShowSurface { get; set; }
+ bool ShowCurve { get; set; }
+ bool ShowDirection { get; set; }
+
+ public void RegisterServer(object curveOrEdge);
+ public void UnregisterServer();
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/ViewModels/Visualization/ISolidVisualizationViewModel.cs b/source/RevitLookup.Abstractions/ViewModels/Visualization/ISolidVisualizationViewModel.cs
new file mode 100644
index 000000000..427d140e5
--- /dev/null
+++ b/source/RevitLookup.Abstractions/ViewModels/Visualization/ISolidVisualizationViewModel.cs
@@ -0,0 +1,38 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using System.Windows.Media;
+
+namespace RevitLookup.Abstractions.ViewModels.Visualization;
+
+public interface ISolidVisualizationViewModel
+{
+ double Scale { get; set; }
+ double Transparency { get; set; }
+
+ Color FaceColor { get; set; }
+ Color EdgeColor { get; set; }
+
+ bool ShowFace { get; set; }
+ bool ShowEdge { get; set; }
+
+ public void RegisterServer(object solid);
+ public void UnregisterServer();
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Abstractions/ViewModels/Visualization/IXyzVisualizationViewModel.cs b/source/RevitLookup.Abstractions/ViewModels/Visualization/IXyzVisualizationViewModel.cs
new file mode 100644
index 000000000..55d9a0a8e
--- /dev/null
+++ b/source/RevitLookup.Abstractions/ViewModels/Visualization/IXyzVisualizationViewModel.cs
@@ -0,0 +1,43 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using System.Windows.Media;
+
+namespace RevitLookup.Abstractions.ViewModels.Visualization;
+
+public interface IXyzVisualizationViewModel
+{
+ public double MinAxisLength { get; }
+
+ double AxisLength { get; set; }
+ double Transparency { get; set; }
+
+ Color XColor { get; set; }
+ Color YColor { get; set; }
+ Color ZColor { get; set; }
+
+ bool ShowPlane { get; set; }
+ bool ShowXAxis { get; set; }
+ bool ShowYAxis { get; set; }
+ bool ShowZAxis { get; set; }
+
+ public void RegisterServer(object xyz);
+ public void UnregisterServer();
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Common/RevitLookup.Common.csproj b/source/RevitLookup.Common/RevitLookup.Common.csproj
new file mode 100644
index 000000000..44b5e7bfa
--- /dev/null
+++ b/source/RevitLookup.Common/RevitLookup.Common.csproj
@@ -0,0 +1,12 @@
+
+
+
+ true
+ net48;net8.0-windows
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/RevitLookup.Common/Tools/ProcessTasks.cs b/source/RevitLookup.Common/Tools/ProcessTasks.cs
new file mode 100644
index 000000000..bfbe343de
--- /dev/null
+++ b/source/RevitLookup.Common/Tools/ProcessTasks.cs
@@ -0,0 +1,70 @@
+using System.Diagnostics;
+using System.Text;
+
+namespace RevitLookup.Common.Tools;
+
+public static class ProcessTasks
+{
+ public static Process? StartProcess(string toolPath, string arguments = "", Action? logger = null)
+ {
+ var startInfo = new ProcessStartInfo
+ {
+ FileName = toolPath,
+ Arguments = arguments,
+ CreateNoWindow = true,
+ UseShellExecute = false,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ StandardOutputEncoding = Encoding.UTF8,
+ StandardErrorEncoding = Encoding.UTF8
+ };
+
+ var process = Process.Start(startInfo);
+ if (process == null) return null;
+
+ RedirectProcessOutput(process, logger);
+ return process;
+ }
+
+ public static Process? StartShell(string toolPath, string arguments = "")
+ {
+ var startInfo = new ProcessStartInfo
+ {
+ FileName = toolPath,
+ Arguments = arguments,
+ CreateNoWindow = true,
+ UseShellExecute = true
+ };
+
+ return Process.Start(startInfo);
+ }
+
+ private static void RedirectProcessOutput(Process process, Action? logger)
+ {
+ logger ??= DefaultLogger;
+ process.OutputDataReceived += (_, args) =>
+ {
+ if (string.IsNullOrEmpty(args.Data)) return;
+ logger.Invoke(OutputType.Standard, args.Data);
+ };
+ process.ErrorDataReceived += (_, args) =>
+ {
+ if (string.IsNullOrEmpty(args.Data)) return;
+ logger.Invoke(OutputType.Error, args.Data);
+ };
+
+ process.BeginOutputReadLine();
+ process.BeginErrorReadLine();
+ }
+
+ private static void DefaultLogger(OutputType type, string output)
+ {
+ Console.WriteLine(output);
+ }
+}
+
+public enum OutputType
+{
+ Standard,
+ Error
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Common/Utils/AccessUtils.cs b/source/RevitLookup.Common/Utils/AccessUtils.cs
new file mode 100644
index 000000000..3d13eaf70
--- /dev/null
+++ b/source/RevitLookup.Common/Utils/AccessUtils.cs
@@ -0,0 +1,47 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using System.IO;
+using System.Security.AccessControl;
+using System.Security.Principal;
+
+namespace RevitLookup.Common.Utils;
+
+public static class AccessUtils
+{
+ public static bool CheckWriteAccess(string path)
+ {
+ var identity = WindowsIdentity.GetCurrent();
+ var principal = new WindowsPrincipal(identity);
+ var accessControl = new DirectoryInfo(path).GetAccessControl();
+ var accessRules = accessControl.GetAccessRules(true, true, typeof(NTAccount));
+ var writeAccess = false;
+ foreach (FileSystemAccessRule rule in accessRules)
+ {
+ if (principal.IsInRole(rule.IdentityReference.Value) && (rule.FileSystemRights & FileSystemRights.WriteData) == FileSystemRights.WriteData)
+ {
+ writeAccess = true;
+ break;
+ }
+ }
+
+ return writeAccess;
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Common/Utils/ColorFormatUtils.cs b/source/RevitLookup.Common/Utils/ColorFormatUtils.cs
new file mode 100644
index 000000000..2deeb814a
--- /dev/null
+++ b/source/RevitLookup.Common/Utils/ColorFormatUtils.cs
@@ -0,0 +1,270 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using Color = System.Drawing.Color;
+
+namespace RevitLookup.Common.Utils;
+
+///
+/// Helper class to easier work with color formats
+///
+///
+/// Implementation: https://github.com/microsoft/PowerToys/blob/main/src/common/ManagedCommon/ColorFormatHelper.cs
+///
+public static class ColorFormatUtils
+{
+ ///
+ /// Return a drawing color of a given
+ ///
+ public static Color GetDrawingColor(this System.Windows.Media.Color color)
+ {
+ return Color.FromArgb(1, color.R, color.G, color.B);
+ }
+
+ ///
+ /// Convert a given to a CMYK color (cyan, magenta, yellow, black key)
+ ///
+ /// The to convert
+ /// The cyan[0..1], magenta[0..1], yellow[0..1] and black key[0..1] of the converted color
+ public static (double Cyan, double Magenta, double Yellow, double BlackKey) ConvertToCmykColor(Color color)
+ {
+ // special case for black (avoid division by zero)
+ if (color is {R: 0, G: 0, B: 0})
+ {
+ return (0d, 0d, 0d, 1d);
+ }
+
+ var red = color.R / 255d;
+ var green = color.G / 255d;
+ var blue = color.B / 255d;
+
+ var blackKey = 1d - Math.Max(Math.Max(red, green), blue);
+
+ // special case for black (avoid division by zero)
+ if (1d - blackKey == 0d)
+ {
+ return (0d, 0d, 0d, 1d);
+ }
+
+ var cyan = (1d - red - blackKey) / (1d - blackKey);
+ var magenta = (1d - green - blackKey) / (1d - blackKey);
+ var yellow = (1d - blue - blackKey) / (1d - blackKey);
+
+ return (cyan, magenta, yellow, blackKey);
+ }
+
+ ///
+ /// Convert a given to a HSB color (hue, saturation, brightness)
+ ///
+ /// The to convert
+ /// The hue [0°..360°], saturation [0..1] and brightness [0..1] of the converted color
+ public static (double Hue, double Saturation, double Brightness) ConvertToHsbColor(Color color)
+ {
+ // HSB and HSV represents the same color space
+ return ConvertToHsvColor(color);
+ }
+
+ ///
+ /// Convert a given to a HSV color (hue, saturation, value)
+ ///
+ /// The to convert
+ /// The hue [0°..360°], saturation [0..1] and value [0..1] of the converted color
+ public static (double Hue, double Saturation, double Value) ConvertToHsvColor(Color color)
+ {
+ var min = Math.Min(Math.Min(color.R, color.G), color.B) / 255d;
+ var max = Math.Max(Math.Max(color.R, color.G), color.B) / 255d;
+
+ return (color.GetHue(), max == 0d ? 0d : (max - min) / max, max);
+ }
+
+ ///
+ /// Convert a given to a HSI color (hue, saturation, intensity)
+ ///
+ /// The to convert
+ /// The hue [0°..360°], saturation [0..1] and intensity [0..1] of the converted color
+ public static (double Hue, double Saturation, double Intensity) ConvertToHsiColor(Color color)
+ {
+ // special case for black
+ if (color.R == 0 && color.G == 0 && color.B == 0)
+ {
+ return (0d, 0d, 0d);
+ }
+
+ var red = color.R / 255d;
+ var green = color.G / 255d;
+ var blue = color.B / 255d;
+
+ var intensity = (red + green + blue) / 3d;
+
+ var min = Math.Min(Math.Min(color.R, color.G), color.B) / 255d;
+
+ return (color.GetHue(), 1d - (min / intensity), intensity);
+ }
+
+ ///
+ /// Convert a given to a HSL color (hue, saturation, lightness)
+ ///
+ /// The to convert
+ /// The hue [0°..360°], saturation [0..1] and lightness [0..1] values of the converted color
+ public static (double Hue, double Saturation, double Lightness) ConvertToHslColor(Color color)
+ {
+ var min = Math.Min(Math.Min(color.R, color.G), color.B) / 255d;
+ var max = Math.Max(Math.Max(color.R, color.G), color.B) / 255d;
+
+ var lightness = (max + min) / 2d;
+
+ if (lightness == 0d || Math.Abs(min - max) < 1e-9)
+ {
+ return (color.GetHue(), 0d, lightness);
+ }
+
+ if (lightness is > 0d and <= 0.5d)
+ {
+ return (color.GetHue(), (max - min) / (max + min), lightness);
+ }
+
+ return (color.GetHue(), (max - min) / (2d - (max + min)), lightness);
+ }
+
+ ///
+ /// Convert a given to a HWB color (hue, whiteness, blackness)
+ ///
+ /// The to convert
+ /// The hue [0°..360°], whiteness [0..1] and blackness [0..1] of the converted color
+ public static (double Hue, double Whiteness, double Blackness) ConvertToHwbColor(Color color)
+ {
+ var min = Math.Min(Math.Min(color.R, color.G), color.B) / 255d;
+ var max = Math.Max(Math.Max(color.R, color.G), color.B) / 255d;
+
+ return (color.GetHue(), min, 1 - max);
+ }
+
+ ///
+ /// Convert a given to a CIE LAB color (LAB)
+ ///
+ /// The to convert
+ /// The lightness [0..100] and two chromaticities [-128..127]
+ public static (double Lightness, double ChromaticityA, double ChromaticityB) ConvertToCielabColor(Color color)
+ {
+ var xyz = ConvertToCiexyzColor(color);
+ var lab = GetCielabColorFromCieXyz(xyz.X, xyz.Y, xyz.Z);
+
+ return lab;
+ }
+
+ ///
+ /// Convert a given to a CIE XYZ color (XYZ)
+ /// The constants of the formula matches this Wikipedia page, but at a higher precision:
+ /// https://en.wikipedia.org/wiki/SRGB#The_reverse_transformation_(sRGB_to_CIE_XYZ)
+ /// This page provides a method to calculate the constants:
+ /// http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
+ ///
+ /// The to convert
+ /// The X [0..1], Y [0..1] and Z [0..1]
+ public static (double X, double Y, double Z) ConvertToCiexyzColor(Color color)
+ {
+ var r = color.R / 255d;
+ var g = color.G / 255d;
+ var b = color.B / 255d;
+
+ // inverse companding, gamma correction must be undone
+ var rLinear = (r > 0.04045) ? Math.Pow((r + 0.055) / 1.055, 2.4) : (r / 12.92);
+ var gLinear = (g > 0.04045) ? Math.Pow((g + 0.055) / 1.055, 2.4) : (g / 12.92);
+ var bLinear = (b > 0.04045) ? Math.Pow((b + 0.055) / 1.055, 2.4) : (b / 12.92);
+
+ return (
+ (rLinear * 0.41239079926595948) + (gLinear * 0.35758433938387796) + (bLinear * 0.18048078840183429),
+ (rLinear * 0.21263900587151036) + (gLinear * 0.71516867876775593) + (bLinear * 0.07219231536073372),
+ (rLinear * 0.01933081871559185) + (gLinear * 0.11919477979462599) + (bLinear * 0.95053215224966058)
+ );
+ }
+
+ ///
+ /// Convert a CIE XYZ color to a CIE LAB color (LAB) adapted to sRGB D65 white point
+ /// The constants of the formula used come from this wikipedia page:
+ /// https://en.wikipedia.org/wiki/CIELAB_color_space#Converting_between_CIELAB_and_CIEXYZ_coordinates
+ ///
+ /// The represents a mix of the three CIE RGB curves
+ /// The represents the luminance
+ /// The is quasi-equal to blue (of CIE RGB)
+ /// The lightness [0..100] and two chromaticities [-128..127]
+ private static (double Lightness, double ChromaticityA, double ChromaticityB) GetCielabColorFromCieXyz(double x, double y, double z)
+ {
+ // sRGB reference white (x=0.3127, y=0.3290, Y=1.0), actually CIE Standard Illuminant D65 truncated to 4 decimal places,
+ // then converted to XYZ using the formula:
+ // X = x * (Y / y)
+ // Y = Y
+ // Z = (1 - x - y) * (Y / y)
+ const double xN = 0.9504559270516717;
+ const double yN = 1.0;
+ const double zN = 1.0890577507598784;
+
+ // Scale XYZ values relative to reference white
+ x /= xN;
+ y /= yN;
+ z /= zN;
+
+ // XYZ to CIELab transformation
+ const double delta = 6d / 29;
+ var m = (1d / 3) * Math.Pow(delta, -2);
+ var t = Math.Pow(delta, 3);
+
+ var fx = (x > t) ? Math.Pow(x, 1.0 / 3.0) : (x * m) + (16.0 / 116.0);
+ var fy = (y > t) ? Math.Pow(y, 1.0 / 3.0) : (y * m) + (16.0 / 116.0);
+ var fz = (z > t) ? Math.Pow(z, 1.0 / 3.0) : (z * m) + (16.0 / 116.0);
+
+ var l = (116 * fy) - 16;
+ var a = 500 * (fx - fy);
+ var b = 200 * (fy - fz);
+
+ return (l, a, b);
+ }
+
+ ///
+ /// Convert a given to a natural color (hue, whiteness, blackness)
+ ///
+ /// The to convert
+ /// The hue, whiteness [0..1] and blackness [0..1] of the converted color
+ public static (string Hue, double Whiteness, double Blackness) ConvertToNaturalColor(Color color)
+ {
+ var min = Math.Min(Math.Min(color.R, color.G), color.B) / 255d;
+ var max = Math.Max(Math.Max(color.R, color.G), color.B) / 255d;
+
+ return (GetNaturalColorFromHue(color.GetHue()), min, 1 - max);
+ }
+
+ ///
+ /// Return the natural color for the given hue value
+ ///
+ /// The hue value to convert
+ /// A natural color
+ private static string GetNaturalColorFromHue(double hue)
+ {
+ return hue switch
+ {
+ < 60d => $"R{Math.Round(hue / 0.6d, 0)}",
+ < 120d => $"Y{Math.Round((hue - 60d) / 0.6d, 0)}",
+ < 180d => $"G{Math.Round((hue - 120d) / 0.6d, 0)}",
+ < 240d => $"C{Math.Round((hue - 180d) / 0.6d, 0)}",
+ < 300d => $"B{Math.Round((hue - 240d) / 0.6d, 0)}",
+ _ => $"M{Math.Round((hue - 300d) / 0.6d, 0)}"
+ };
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.Common/Utils/ColorRepresentationUtils.cs b/source/RevitLookup.Common/Utils/ColorRepresentationUtils.cs
new file mode 100644
index 000000000..a65aed4af
--- /dev/null
+++ b/source/RevitLookup.Common/Utils/ColorRepresentationUtils.cs
@@ -0,0 +1,777 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using System.Globalization;
+using Color = System.Drawing.Color;
+
+namespace RevitLookup.Common.Utils;
+
+///
+/// Helper class to easier work with color representation
+///
+///
+/// Implementation: https://github.com/microsoft/PowerToys/blob/main/src/modules/colorPicker/ColorPickerUI/Helpers/ColorRepresentationHelper.cs
+///
+public static class ColorRepresentationUtils
+{
+ private static readonly Dictionary KnownColors = new()
+ {
+ {"000000", "Black"},
+ {"000080", "Navy blue"},
+ {"0000FF", "Blue"},
+ {"0018A8", "Blue (Pantone)"},
+ {"002E63", "Cool Black"},
+ {"003153", "Prussian blue"},
+ {"003366", "Midnight Blue"},
+ {"003399", "Powder blue"},
+ {"003A6C", "Ateneo blue"},
+ {"004225", "British racing green"},
+ {"0047AB", "Cobalt"},
+ {"0048BA", "Absolute zero"},
+ {"00563F", "Castleton green"},
+ {"006A4E", "Bottle green"},
+ {"006B3C", "Cadmium green"},
+ {"007BA7", "Cerulean"},
+ {"007DFF", "Gradus Blue"},
+ {"007FFF", "Azure"},
+ {"008080", "Teal"},
+ {"0088DC", "Blue cola"},
+ {"0093AF", "Blue (Munsell)"},
+ {"0095B6", "Bondi Blue"},
+ {"00A86B", "Jade"},
+ {"00B9FB", "Blue Bolt"},
+ {"00BFFF", "Deep sky blue"},
+ {"00C4B0", "Amazonite"},
+ {"00CC99", "Caribbean green"},
+ {"00CCCC", "Robin egg blue"},
+ {"00FF00", "Green"},
+ {"00FF7F", "Spring Green"},
+ {"00FFFF", "Aqua"},
+ {"013220", "Dark green"},
+ {"01796F", "Pine Green"},
+ {"03C03C", "Dark pastel green"},
+ {"064E40", "Blue-green (color wheel)"},
+ {"082567", "Sapphire"},
+ {"08457E", "Dark cerulean"},
+ {"08E8DE", "Bright turquoise"},
+ {"0A1195", "Cadmium blue"},
+ {"0BDA51", "Malachite"},
+ {"0D98BA", "Blue-green"},
+ {"116062", "Dark turquoise"},
+ {"120A8F", "Ultramarine"},
+ {"126180", "Blue sapphire"},
+ {"141414", "Chinese black"},
+ {"1560BD", "Denim"},
+ {"177245", "Dark spring green"},
+ {"1974D2", "Bright navy blue"},
+ {"1A4780", "Black Sea"},
+ {"1B1811", "Black chocolate"},
+ {"1B4D3E", "Brunswick green"},
+ {"1C6B72", "Moray"},
+ {"1DACD6", "Bright cerulean"},
+ {"1E90FF", "Dodger blue"},
+ {"1F4037", "Red Sea"},
+ {"1F75FE", "Blue (Crayola)"},
+ {"21ABCD", "Ball blue"},
+ {"232B2B", "Charleston green"},
+ {"246BCE", "Celtic Blue"},
+ {"24A0ED", "Button Blue"},
+ {"253529", "Black leather jacket"},
+ {"2A52BE", "Cerulean blue"},
+ {"2A8FBD", "Christmas blue"},
+ {"2E2D88", "Cosmic Cobalt"},
+ {"2E5894", "B'dazzled blue"},
+ {"2E8B57", "Sea Green"},
+ {"2F4F4F", "Dark slate gray"},
+ {"30D5C8", "Turquoise"},
+ {"310062", "Dark Indigo"},
+ {"318CE7", "Bleu de France"},
+ {"333399", "Blue (pigment)"},
+ {"3399FF", "Brilliant azure"},
+ {"34B334", "American green"},
+ {"34C924", "Vert-de-pomme"},
+ {"36454F", "Charcoal"},
+ {"365194", "Chinese blue"},
+ {"391802", "American bronze"},
+ {"3A75C4", "Klein Blue"},
+ {"3B2F2F", "Black coffee"},
+ {"3B3B6D", "American blue"},
+ {"3B3C36", "Black olive"},
+ {"3B444B", "Arsenic"},
+ {"3B7A57", "Amazon"},
+ {"3C1421", "Chocolate Kisses"},
+ {"3C3024", "Cola"},
+ {"3C8D0D", "Christmas green"},
+ {"3CAA3C", "Toad in love"},
+ {"3D0C02", "Black bean"},
+ {"3D2B1F", "Bistre"},
+ {"3F000F", "Chocolate Brown"},
+ {"40826D", "Viridian"},
+ {"4169E1", "Royal Blue"},
+ {"423189", "Dark violet"},
+ {"442D25", "Coffee"},
+ {"45161C", "Fulvous"},
+ {"464451", "Anthracite"},
+ {"465945", "Gray-asparagus"},
+ {"4682B4", "Steel blue"},
+ {"480607", "Bulgarian rose"},
+ {"4A2C2A", "Brown Coffee"},
+ {"4AFF00", "Chlorophyll green"},
+ {"4B0082", "Indigo"},
+ {"4B3621", "Caf? noir"},
+ {"4B5320", "Army green"},
+ {"4C2F27", "Acajou"},
+ {"4C5866", "Marengo"},
+ {"4D1A7F", "Blue-violet (color wheel)"},
+ {"4E1609", "Flea belly"},
+ {"4F7942", "Fern green"},
+ {"4F86F7", "Blueberry"},
+ {"5072A7", "Blue yonder"},
+ {"50C878", "Emerald"},
+ {"54626F", "Black Coral"},
+ {"551B8C", "American violet"},
+ {"553592", "Blue-magenta violet"},
+ {"556832", "Dark Olive"},
+ {"560319", "Dark Scarlet"},
+ {"568203", "Avocado"},
+ {"56A0D3", "Carolina blue"},
+ {"58111A", "Chocolate Cosmos"},
+ {"592720", "Caput mortuum"},
+ {"5D2B2C", "Christmas brown"},
+ {"5D8AA8", "Air Force Navy(RAF)"},
+ {"5DA130", "Grass"},
+ {"5DADEC", "Blue Jeans"},
+ {"5F1933", "Brown Chocolate"},
+ {"630F0F", "Blood (Organ)"},
+ {"63775B", "Axolotl"},
+ {"6495ED", "Cornflower blue"},
+ {"654321", "Dark brown"},
+ {"660000", "Blood red"},
+ {"660066", "Plum"},
+ {"660099", "Purple"},
+ {"6600FF", "Persian blue"},
+ {"663398", "Christmas purple"},
+ {"665D1E", "Antique bronze"},
+ {"6699CC", "Blue-gray"},
+ {"66B447", "Apple"},
+ {"66FF00", "Bright green"},
+ {"6B4423", "Brown-nose"},
+ {"6B8E23", "Olive Drab"},
+ {"6E7F80", "AuroMetalSaurus"},
+ {"702963", "Byzantium"},
+ {"703642", "Catawba"},
+ {"704214", "Sepia"},
+ {"708090", "Slate gray"},
+ {"720B98", "Chinese purple"},
+ {"72A0C1", "Air superiority blue"},
+ {"734A12", "Raw umber"},
+ {"735184", "Seroburomalinovyj"},
+ {"7366BD", "Blue-violet (Crayola)"},
+ {"737000", "Bronze Yellow"},
+ {"755A57", "Russet"},
+ {"77DD77", "Pastel green"},
+ {"78866B", "Camouflage green"},
+ {"79443B", "Bole"},
+ {"79A0C1", "Bluish"},
+ {"7B3F00", "Cinnamon"},
+ {"7B917B", "Fainted frog"},
+ {"7BA05B", "Asparagus"},
+ {"7BB661", "Bud green"},
+ {"7C0A02", "Barn red"},
+ {"7CB9E8", "Aero"},
+ {"7DF9FF", "Electric"},
+ {"7F1734", "Claret"},
+ {"7F3E98", "Cadmium violet"},
+ {"7FC7FF", "Sky"},
+ {"7FFF00", "Chartreuse"},
+ {"7FFFD4", "Aquamarine"},
+ {"800000", "Maroon"},
+ {"800020", "Burgundy"},
+ {"804040", "American brown"},
+ {"808000", "Olive"},
+ {"808080", "Gray"},
+ {"81613C", "Coyote brown"},
+ {"834D18", "Byron"},
+ {"841B2D", "Antique ruby"},
+ {"848482", "Battleship grey"},
+ {"84DE02", "Alien Armpit"},
+ {"856088", "Chinese violet"},
+ {"87413F", "Brandy"},
+ {"87A96B", "Asparagus"},
+ {"884535", "Brick"},
+ {"893F45", "Cordovan"},
+ {"89CFF0", "Baby blue"},
+ {"8A0303", "Blood"},
+ {"8A2BE2", "Blue-violet"},
+ {"8A3324", "Burnt umber"},
+ {"8B00FF", "Violet"},
+ {"8C92AC", "Cool grey"},
+ {"8DB600", "Apple Green"},
+ {"8F5973", "Blackberry"},
+ {"8F9779", "Artichoke"},
+ {"900020", "Burgundy"},
+ {"904D30", "Terracotta"},
+ {"911E42", "Cherry"},
+ {"915C83", "Antique fuchsia"},
+ {"918151", "Dark tan"},
+ {"92000A", "Sangria"},
+ {"954535", "Chestnut"},
+ {"960018", "Carmine"},
+ {"964B00", "Brown"},
+ {"965A3E", "Coconut"},
+ {"967117", "Bistre brown"},
+ {"986960", "Dark chestnut"},
+ {"987654", "Pale brown"},
+ {"98777B", "Bazaar"},
+ {"98817B", "Cinereous"},
+ {"98FF98", "Mint Green"},
+ {"990066", "Eggplant"},
+ {"991199", "Violet-eggplant"},
+ {"993366", "Mauve"},
+ {"996666", "Copper rose"},
+ {"9966CC", "Amethyst"},
+ {"997A8D", "Mountbatten pink"},
+ {"99958C", "Quartz"},
+ {"9B2D30", "Wine red"},
+ {"9C2542", "Big dip o’ruby"},
+ {"9DB1CC", "Niagara"},
+ {"9F2B68", "Amaranth deep purple"},
+ {"9F8170", "Beaver"},
+ {"9FA91F", "Citron"},
+ {"A08040", "Chamois"},
+ {"A17A74", "Burnished Brown"},
+ {"A1CAF1", "Baby blue eyes"},
+ {"A25F2A", "Camelopardalis"},
+ {"A2A2D0", "Blue Bell"},
+ {"A41313", "Blood (Animal)"},
+ {"A4C639", "Android green"},
+ {"A5260A", "Bismarck-furious"},
+ {"A52A2A", "Auburn"},
+ {"A57164", "Blast-off bronze"},
+ {"A67B5B", "Caf? au lait"},
+ {"A8516E", "China rose"},
+ {"AA381E", "Chinese red"},
+ {"AB274F", "Amaranth purple"},
+ {"AB381F", "Chinese brown"},
+ {"ABCDEF", "Pale cornflower blue"},
+ {"ACB78E", "Swamp green"},
+ {"ACE1AF", "Celadon"},
+ {"ACE5EE", "Blue Lagoon"},
+ {"AD6F69", "Copper penny"},
+ {"ADDFAD", "Moss green"},
+ {"ADFF2F", "Green-yellow"},
+ {"AF002A", "Alabama crimson"},
+ {"AF4035", "Pale carmine"},
+ {"AF6E4D", "Brown Sugar"},
+ {"AFEEEE", "Pale Blue"},
+ {"B01B2E", "Christmas red"},
+ {"B08D57", "Bronze (Metallic)"},
+ {"B0BF1A", "Acid green"},
+ {"B284BE", "African violet"},
+ {"B2BEB5", "Ash gray"},
+ {"B31B1B", "Carnelian"},
+ {"B32134", "American red"},
+ {"B5A642", "Brass"},
+ {"B60C26", "Cadmium Purple"},
+ {"B7410E", "Rust"},
+ {"B87333", "Copper"},
+ {"B8860B", "Dark goldenrod"},
+ {"BADBAD", "Dark Tea Green"},
+ {"BBBBBB", "Light Grey"},
+ {"BCD4E6", "Beau blue"},
+ {"BD33A4", "Byzantine"},
+ {"BDB76B", "Dark Khaki"},
+ {"BEF574", "Pistachio"},
+ {"BF4F51", "Bittersweet shimmer"},
+ {"BF94E4", "Bright lavender"},
+ {"BFAFB2", "Black Shadows"},
+ {"BFFF00", "Bitter lime"},
+ {"C0C0C0", "Silver"},
+ {"C19A6B", "Camel"},
+ {"C32148", "Bright maroon"},
+ {"C39953", "Aztec Gold"},
+ {"C3B091", "Khaki"},
+ {"C41E3A", "Cardinal"},
+ {"C46210", "Alloy orange"},
+ {"C4D8E2", "Columbia Blue"},
+ {"C71585", "Red-violet"},
+ {"C7D0CC", "Gris de perle"},
+ {"C7FCEC", "Pang"},
+ {"C8A2C8", "Lilac"},
+ {"C95A49", "Cedar Chest"},
+ {"C9A0DC", "Wisteria"},
+ {"C9FFE5", "Aero blue"},
+ {"CAA906", "Christmas gold"},
+ {"CADABA", "Gray-Tea Green"},
+ {"CAE00D", "Bitter lemon"},
+ {"CB4154", "Brick red"},
+ {"CC0000", "Boston University Red"},
+ {"CC5500", "Burnt orange"},
+ {"CC7722", "Ochre"},
+ {"CC8899", "Puce"},
+ {"CC9900", "Chinese gold"},
+ {"CC9966", "Brown Yellow"},
+ {"CCCCCC", "Chinese silver"},
+ {"CCCCFF", "Periwinkle"},
+ {"CCFF00", "Lime"},
+ {"CD00CD", "Bright violet"},
+ {"CD5700", "Tenne"},
+ {"CD5B45", "Dark coral"},
+ {"CD5C5C", "Chestnut"},
+ {"CD607E", "Cinnamon Satin"},
+ {"CD7F32", "Bronze"},
+ {"CD8032", "Chinese bronze"},
+ {"CD853F", "Light brown"},
+ {"CD9575", "Antique brass"},
+ {"CFB53B", "Old Gold"},
+ {"CFCFCF", "American silver"},
+ {"D0DB61", "Chinese green"},
+ {"D0F0C0", "Tea Green"},
+ {"D0FF14", "Arctic lime"},
+ {"D1001C", "Blood orange"},
+ {"D19FE8", "Bright ube"},
+ {"D1E231", "Pear"},
+ {"D2691E", "Chocolate"},
+ {"D2B48C", "Tan"},
+ {"D3212D", "Amaranth red"},
+ {"D3AF37", "American gold"},
+ {"D53E07", "Titian"},
+ {"D5713F", "Vanilla"},
+ {"D5D5D5", "Abdel Kerim's beard"},
+ {"D77D31", "Reddish-brown"},
+ {"D891EF", "Bright lilac"},
+ {"D8A903", "Dark pear"},
+ {"D8BFD8", "Thistle"},
+ {"DA70D6", "Orchid"},
+ {"DAA520", "Goldenrod"},
+ {"DABDAB", "Pale Sandy Brown"},
+ {"DAD871", "Vert-de-pеche"},
+ {"DB7093", "Pale red-violet"},
+ {"DBE9F4", "Azureish white"},
+ {"DC143C", "Crimson"},
+ {"DDADAF", "Pale chestnut"},
+ {"DDE26A", "Booger Buster"},
+ {"DE3163", "Cerise"},
+ {"DE5D83", "Blush"},
+ {"DE6FA1", "China pink"},
+ {"DF73FF", "Heliotrope"},
+ {"E0218A", "Barbie pink"},
+ {"E1DFE0", "Christmas silver"},
+ {"E28B00", "Siena"},
+ {"E2E5DE", "Chinese white"},
+ {"E30022", "Cadmium red"},
+ {"E32636", "Alizarin crimson"},
+ {"E34234", "Cinnabar"},
+ {"E3DAC9", "Bone"},
+ {"E4717A", "Candy pink"},
+ {"E49B0F", "Gamboge"},
+ {"E4D00A", "Citrine"},
+ {"E52B50", "Amaranth"},
+ {"E6E6FA", "Lavender"},
+ {"E75480", "Dark pink"},
+ {"E7FEFF", "Bubbles"},
+ {"E88E5A", "Big Foot Feet"},
+ {"E97451", "Burnt sienna"},
+ {"E9967A", "Dark salmon"},
+ {"E9D66B", "Arylide yellow"},
+ {"EA8DF7", "Violaceous"},
+ {"EB4C42", "Carmine pink"},
+ {"EBC2AF", "Zinnwaldite"},
+ {"EBECF0", "Bright gray"},
+ {"ED872D", "Cadmium orange"},
+ {"ED9121", "Carrot"},
+ {"EEDC82", "Flax"},
+ {"EEE0B1", "Cookies and cream"},
+ {"EEE6A3", "Perhydor"},
+ {"EFAF8C", "Saumon"},
+ {"EFBBCC", "Cameo pink"},
+ {"EFDECD", "Almond"},
+ {"F0DC82", "Buff"},
+ {"F0F8FF", "Alice blue"},
+ {"F19CBB", "Amaranth pink"},
+ {"F1DDCF", "Champagne pink"},
+ {"F28E1C", "Beer"},
+ {"F2B400", "American yellow"},
+ {"F2E8C9", "Light cream"},
+ {"F2F0E6", "Alabaster"},
+ {"F2F3F4", "Anti-flash white"},
+ {"F37042", "Chinese orange"},
+ {"F4A460", "Sandy brown"},
+ {"F4BBFF", "Brilliant lavender"},
+ {"F4C2C2", "Baby pink"},
+ {"F4C430", "Saffron"},
+ {"F5DEB3", "Wheat"},
+ {"F5F5DC", "Beige"},
+ {"F7E7CE", "Champagne"},
+ {"F7F21A", "Child's surprise"},
+ {"F88379", "Congo pink"},
+ {"F984E5", "Pale magenta"},
+ {"FA6E79", "Begonia"},
+ {"FADADD", "Pale pink"},
+ {"FADFAD", "Peach-yellow"},
+ {"FAE7B5", "Banana Mania"},
+ {"FAEBD7", "Antique white"},
+ {"FAEEDD", "Scared nymph"},
+ {"FAF0E6", "Linen"},
+ {"FB607F", "Brink pink"},
+ {"FBCCE7", "Classic rose"},
+ {"FBCEB1", "Apricot"},
+ {"FBEC5D", "Corn"},
+ {"FC0FC0", "Hot pink"},
+ {"FD7C6E", "Coral Reef"},
+ {"FDE910", "Lemon"},
+ {"FDEE00", "Aureolin"},
+ {"FE6F5E", "Bittersweet"},
+ {"FEF200", "Christmas yellow"},
+ {"FEFEFA", "Baby powder"},
+ {"FF0000", "Red"},
+ {"FF0038", "Carmine red"},
+ {"FF007F", "Bright pink"},
+ {"FF00FF", "Magenta (Fuchsia)"},
+ {"FF033E", "American rose"},
+ {"FF0800", "Candy apple red"},
+ {"FF2052", "Awesome"},
+ {"FF2400", "Scarlet"},
+ {"FF47CA", "Shocked star"},
+ {"FF4D00", "Vermilion"},
+ {"FF4F00", "Safety orange"},
+ {"FF55A3", "Brilliant rose"},
+ {"FF6600", "Christmas orange"},
+ {"FF7518", "Pumpkin"},
+ {"FF7E00", "Amber (SAE/ECE)"},
+ {"FF7F50", "Coral"},
+ {"FF8B00", "American orange"},
+ {"FF8C69", "Salmon"},
+ {"FF91AF", "Baker-Miller pink"},
+ {"FF9218", "Jaco"},
+ {"FF9900", "Blaze Orange"},
+ {"FF9966", "Pink-orange"},
+ {"FFA500", "Orange"},
+ {"FFA600", "Cheese"},
+ {"FFA6C9", "Carnation pink"},
+ {"FFA812", "Dark tangerine"},
+ {"FFAA1D", "Bright Yellow (Crayola)"},
+ {"FFBA00", "Selective yellow"},
+ {"FFBCD9", "Cotton candy"},
+ {"FFBF00", "Amber"},
+ {"FFC0CB", "Pink"},
+ {"FFC1CC", "Bubble gum"},
+ {"FFCC00", "Tangerine"},
+ {"FFCC99", "Peach-orange"},
+ {"FFCCCB", "Christmas pink"},
+ {"FFD1DC", "Pastel pink"},
+ {"FFD59A", "Caramel"},
+ {"FFD700", "Gold"},
+ {"FFD800", "School bus yellow"},
+ {"FFDAB9", "Dark Peach"},
+ {"FFDB58", "Mustard"},
+ {"FFDEAD", "Navajo white"},
+ {"FFE135", "Banana yellow"},
+ {"FFE4B2", "Yellow Pink"},
+ {"FFE4C4", "Bisque"},
+ {"FFE5B4", "Peach"},
+ {"FFEBCD", "Blanched almond"},
+ {"FFEF00", "Canary yellow"},
+ {"FFEFD5", "Papaya whip"},
+ {"FFF0F5", "Lavender Blush"},
+ {"FFF5EE", "Seashell"},
+ {"FFF600", "Cadmium yellow"},
+ {"FFF8DC", "Cornsilk"},
+ {"FFF8E7", "Cosmic latte"},
+ {"FFFACD", "Lemon Cream"},
+ {"FFFDD0", "Cream"},
+ {"FFFDDF", "Ivory"},
+ {"FFFF00", "Yellow"},
+ {"FFFF99", "Canary"},
+ {"FFFFCC", "Conditioner"},
+ {"FFFFFF", "White"},
+ };
+
+ ///
+ /// Return a representation of a CMYK color
+ ///
+ /// The for the CMYK color presentation
+ /// A representation of a CMYK color
+ public static string ColorToCmyk(Color color)
+ {
+ var (cyan, magenta, yellow, blackKey) = ColorFormatUtils.ConvertToCmykColor(color);
+
+ cyan = Math.Round(cyan * 100);
+ magenta = Math.Round(magenta * 100);
+ yellow = Math.Round(yellow * 100);
+ blackKey = Math.Round(blackKey * 100);
+
+ return $"cmyk({cyan.ToString(CultureInfo.InvariantCulture)}%"
+ + $", {magenta.ToString(CultureInfo.InvariantCulture)}%"
+ + $", {yellow.ToString(CultureInfo.InvariantCulture)}%"
+ + $", {blackKey.ToString(CultureInfo.InvariantCulture)}%)";
+ }
+
+ ///
+ /// Return a hexadecimal representation of a RGB color
+ ///
+ /// The for the hexadecimal presentation
+ /// A hexadecimal representation of a RGB color
+ public static string ColorToHex(Color color)
+ {
+ const string hexFormat = "x2";
+
+ return $"{color.R.ToString(hexFormat, CultureInfo.InvariantCulture)}"
+ + $"{color.G.ToString(hexFormat, CultureInfo.InvariantCulture)}"
+ + $"{color.B.ToString(hexFormat, CultureInfo.InvariantCulture)}";
+ }
+
+ ///
+ /// Return a representation of a HSB color
+ ///
+ /// The for the HSB color presentation
+ /// A representation of a HSB color
+ public static string ColorToHsb(Color color)
+ {
+ var (hue, saturation, brightness) = ColorFormatUtils.ConvertToHsbColor(color);
+
+ hue = Math.Round(hue);
+ saturation = Math.Round(saturation * 100);
+ brightness = Math.Round(brightness * 100);
+
+ return $"hsb({hue.ToString(CultureInfo.InvariantCulture)}"
+ + $", {saturation.ToString(CultureInfo.InvariantCulture)}%"
+ + $", {brightness.ToString(CultureInfo.InvariantCulture)}%)";
+ }
+
+ ///
+ /// Return a representation float color styling(0.1f, 0.1f, 0.1f)
+ ///
+ /// The to convert
+ /// a string value (0.1f, 0.1f, 0.1f)
+ public static string ColorToFloat(Color color)
+ {
+ var (red, green, blue) = (color.R / 255d, color.G / 255d, color.B / 255d);
+ const int precision = 2;
+ const string floatFormat = "0.##";
+
+ return $"({Math.Round(red, precision).ToString(floatFormat, CultureInfo.InvariantCulture)}f"
+ + $", {Math.Round(green, precision).ToString(floatFormat, CultureInfo.InvariantCulture)}f"
+ + $", {Math.Round(blue, precision).ToString(floatFormat, CultureInfo.InvariantCulture)}f, 1f)";
+ }
+
+ ///
+ /// Return a representation decimal color value
+ ///
+ /// The to convert
+ /// a string value number
+ public static string ColorToDecimal(Color color)
+ {
+ return $"{(color.R * 65536) + (color.G * 256) + color.B}";
+ }
+
+ ///
+ /// Return a representation of a HSI color
+ ///
+ /// The for the HSI color presentation
+ /// A representation of a HSI color
+ public static string ColorToHsi(Color color)
+ {
+ var (hue, saturation, intensity) = ColorFormatUtils.ConvertToHsiColor(color);
+
+ hue = Math.Round(hue);
+ saturation = Math.Round(saturation * 100);
+ intensity = Math.Round(intensity * 100);
+
+ return $"hsi({hue.ToString(CultureInfo.InvariantCulture)}"
+ + $", {saturation.ToString(CultureInfo.InvariantCulture)}%"
+ + $", {intensity.ToString(CultureInfo.InvariantCulture)}%)";
+ }
+
+ ///
+ /// Return a representation of a HSL color
+ ///
+ /// The for the HSL color presentation
+ /// A representation of a HSL color
+ public static string ColorToHsl(Color color)
+ {
+ var (hue, saturation, lightness) = ColorFormatUtils.ConvertToHslColor(color);
+
+ hue = Math.Round(hue);
+ saturation = Math.Round(saturation * 100);
+ lightness = Math.Round(lightness * 100);
+
+ // Using InvariantCulture since this is used for color representation
+ return $"hsl({hue.ToString(CultureInfo.InvariantCulture)}"
+ + $", {saturation.ToString(CultureInfo.InvariantCulture)}%"
+ + $", {lightness.ToString(CultureInfo.InvariantCulture)}%)";
+ }
+
+ ///
+ /// Return a representation of a HSV color
+ ///
+ /// The for the HSV color presentation
+ /// A representation of a HSV color
+ public static string ColorToHsv(Color color)
+ {
+ var (hue, saturation, value) = ColorFormatUtils.ConvertToHsvColor(color);
+
+ hue = Math.Round(hue);
+ saturation = Math.Round(saturation * 100);
+ value = Math.Round(value * 100);
+
+ // Using InvariantCulture since this is used for color representation
+ return $"hsv({hue.ToString(CultureInfo.InvariantCulture)}"
+ + $", {saturation.ToString(CultureInfo.InvariantCulture)}%"
+ + $", {value.ToString(CultureInfo.InvariantCulture)}%)";
+ }
+
+ ///
+ /// Return a representation of a HWB color
+ ///
+ /// The for the HWB color presentation
+ /// A representation of a HWB color
+ public static string ColorToHwb(Color color)
+ {
+ var (hue, whiteness, blackness) = ColorFormatUtils.ConvertToHwbColor(color);
+
+ hue = Math.Round(hue);
+ whiteness = Math.Round(whiteness * 100);
+ blackness = Math.Round(blackness * 100);
+
+ return $"hwb({hue.ToString(CultureInfo.InvariantCulture)}"
+ + $", {whiteness.ToString(CultureInfo.InvariantCulture)}%"
+ + $", {blackness.ToString(CultureInfo.InvariantCulture)}%)";
+ }
+
+ ///
+ /// Return a representation of a natural color
+ ///
+ /// The for the natural color presentation
+ /// A representation of a natural color
+ public static string ColorToNCol(Color color)
+ {
+ var (hue, whiteness, blackness) = ColorFormatUtils.ConvertToNaturalColor(color);
+
+ whiteness = Math.Round(whiteness * 100);
+ blackness = Math.Round(blackness * 100);
+
+ return $"{hue}"
+ + $", {whiteness.ToString(CultureInfo.InvariantCulture)}%"
+ + $", {blackness.ToString(CultureInfo.InvariantCulture)}%";
+ }
+
+ ///
+ /// Return a representation of a RGB color
+ ///
+ /// The for the RGB color presentation
+ /// A representation of a RGB color
+ public static string ColorToRgb(Color color)
+ => $"rgb({color.R.ToString(CultureInfo.InvariantCulture)}"
+ + $", {color.G.ToString(CultureInfo.InvariantCulture)}"
+ + $", {color.B.ToString(CultureInfo.InvariantCulture)})";
+
+ ///
+ /// Returns a representation of a CIE LAB color
+ ///
+ /// The for the CIE LAB color presentation
+ /// A representation of a CIE LAB color
+ public static string ColorToCielab(Color color)
+ {
+ var (lightness, chromaticityA, chromaticityB) = ColorFormatUtils.ConvertToCielabColor(color);
+ lightness = Math.Round(lightness, 2);
+ chromaticityA = Math.Round(chromaticityA, 2);
+ chromaticityB = Math.Round(chromaticityB, 2);
+
+ return $"CIELab({lightness.ToString(CultureInfo.InvariantCulture)}" +
+ $", {chromaticityA.ToString(CultureInfo.InvariantCulture)}" +
+ $", {chromaticityB.ToString(CultureInfo.InvariantCulture)})";
+ }
+
+ ///
+ /// Returns a representation of a CIE XYZ color
+ ///
+ /// The for the CIE XYZ color presentation
+ /// A representation of a CIE XYZ color
+ public static string ColorToCieXyz(Color color)
+ {
+ var (x, y, z) = ColorFormatUtils.ConvertToCiexyzColor(color);
+
+ x = Math.Round(x * 100, 4);
+ y = Math.Round(y * 100, 4);
+ z = Math.Round(z * 100, 4);
+
+ return $"XYZ({x.ToString(CultureInfo.InvariantCulture)}" +
+ $", {y.ToString(CultureInfo.InvariantCulture)}" +
+ $", {z.ToString(CultureInfo.InvariantCulture)})";
+ }
+
+ ///
+ /// Return a hexadecimal integer representation of a RGB color
+ ///
+ /// The for the hexadecimal integer presentation
+ /// A hexadecimal integer representation of a RGB color
+ public static string ColorToHexInteger(Color color)
+ {
+ const string hexFormat = "X2";
+
+ return "0xFF"
+ + $"{color.R.ToString(hexFormat, CultureInfo.InvariantCulture)}"
+ + $"{color.G.ToString(hexFormat, CultureInfo.InvariantCulture)}"
+ + $"{color.B.ToString(hexFormat, CultureInfo.InvariantCulture)}";
+ }
+
+ ///
+ /// Return a name of a RGB color
+ ///
+ /// The for presentation
+ /// Approximate name of a color based on RGB representation
+ public static string GetColorName(Color color)
+ {
+ var colorName = string.Empty;
+ var closestDistance = double.MaxValue;
+ foreach (var entry in KnownColors)
+ {
+ var knownColor = ConvertHexStringToColor(entry.Key);
+ var distance = CalculateColorDistance(color, knownColor);
+
+ if (distance < closestDistance)
+ {
+ colorName = entry.Value;
+ closestDistance = distance;
+ }
+ }
+
+ return colorName;
+ }
+
+ private static Color ConvertHexStringToColor(string hex)
+ {
+ var red = byte.Parse(hex.Substring(0, 2), NumberStyles.HexNumber);
+ var green = byte.Parse(hex.Substring(2, 2), NumberStyles.HexNumber);
+ var blue = byte.Parse(hex.Substring(4, 2), NumberStyles.HexNumber);
+
+ return Color.FromArgb(red, green, blue);
+ }
+
+ private static double CalculateColorDistance(Color color1, Color color2)
+ {
+ var deltaR = color1.R - color2.R;
+ var deltaG = color1.G - color2.G;
+ var deltaB = color1.B - color2.B;
+
+ return Math.Sqrt(deltaR * deltaR + deltaG * deltaG + deltaB * deltaB);
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI/Controls/NavigationView/INavigableView.cs b/source/RevitLookup.UI.Abstractions/Controls/INavigableView.cs
similarity index 59%
rename from source/RevitLookup.UI/Controls/NavigationView/INavigableView.cs
rename to source/RevitLookup.UI.Abstractions/Controls/INavigableView.cs
index 9cd23d8b7..2a3252163 100644
--- a/source/RevitLookup.UI/Controls/NavigationView/INavigableView.cs
+++ b/source/RevitLookup.UI.Abstractions/Controls/INavigableView.cs
@@ -3,17 +3,17 @@
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
// All Rights Reserved.
-// ReSharper disable once CheckNamespace
-namespace Wpf.Ui.Controls;
+namespace Wpf.Ui.Abstractions.Controls;
///
-/// A component whose ViewModel is separate from the DataContext and can be navigated by .
+/// A component whose ViewModel is separate from the DataContext and can be navigated by INavigationView.
///
+/// The type of the ViewModel associated with the view. This type optionally may implement to participate in navigation processes.
public interface INavigableView
{
///
- /// ViewModel used by the view.
- /// Optionally, it may implement and be navigated by .
+ /// Gets the view model used by the view.
+ /// Optionally, it may implement and be navigated by INavigationView.
///
T ViewModel { get; }
-}
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI/Controls/NavigationView/INavigationAware.cs b/source/RevitLookup.UI.Abstractions/Controls/INavigationAware.cs
similarity index 50%
rename from source/RevitLookup.UI/Controls/NavigationView/INavigationAware.cs
rename to source/RevitLookup.UI.Abstractions/Controls/INavigationAware.cs
index 586aedbd6..50f195af6 100644
--- a/source/RevitLookup.UI/Controls/NavigationView/INavigationAware.cs
+++ b/source/RevitLookup.UI.Abstractions/Controls/INavigationAware.cs
@@ -3,8 +3,7 @@
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
// All Rights Reserved.
-// ReSharper disable once CheckNamespace
-namespace Wpf.Ui.Controls;
+namespace Wpf.Ui.Abstractions.Controls;
///
/// Notifies class about being navigated.
@@ -12,12 +11,14 @@ namespace Wpf.Ui.Controls;
public interface INavigationAware
{
///
- /// Method triggered when the class is navigated.
+ /// Asynchronously handles the event that is fired after the component is navigated to.
///
- void OnNavigatedTo();
+ /// A task that represents the asynchronous operation.
+ Task OnNavigatedToAsync();
///
- /// Method triggered when the navigation leaves the current class.
+ /// Asynchronously handles the event that is fired before the component is navigated from.
///
- void OnNavigatedFrom();
-}
+ /// A task that represents the asynchronous operation.
+ Task OnNavigatedFromAsync();
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Abstractions/Controls/NavigationAware.cs b/source/RevitLookup.UI.Abstractions/Controls/NavigationAware.cs
new file mode 100644
index 000000000..3b609ebcd
--- /dev/null
+++ b/source/RevitLookup.UI.Abstractions/Controls/NavigationAware.cs
@@ -0,0 +1,44 @@
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
+// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
+// All Rights Reserved.
+
+namespace Wpf.Ui.Abstractions.Controls;
+
+///
+/// Provides a base class for navigation-aware components.
+///
+public abstract class NavigationAware : INavigationAware
+{
+ ///
+ public virtual Task OnNavigatedToAsync()
+ {
+ OnNavigatedTo();
+
+ return Task.CompletedTask;
+ }
+
+ ///
+ /// Handles the event that is fired after the component is navigated to.
+ ///
+ // ReSharper disable once MemberCanBeProtected.Global
+ public virtual void OnNavigatedTo()
+ {
+ }
+
+ ///
+ public virtual Task OnNavigatedFromAsync()
+ {
+ OnNavigatedFrom();
+
+ return Task.CompletedTask;
+ }
+
+ ///
+ /// Handles the event that is fired before the component is navigated from.
+ ///
+ // ReSharper disable once MemberCanBeProtected.Global
+ public virtual void OnNavigatedFrom()
+ {
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Abstractions/GlobalUsings.cs b/source/RevitLookup.UI.Abstractions/GlobalUsings.cs
new file mode 100644
index 000000000..8fcccdc85
--- /dev/null
+++ b/source/RevitLookup.UI.Abstractions/GlobalUsings.cs
@@ -0,0 +1,7 @@
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
+// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
+// All Rights Reserved.
+
+global using System;
+global using System.Threading.Tasks;
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Abstractions/INavigationViewPageProvider.cs b/source/RevitLookup.UI.Abstractions/INavigationViewPageProvider.cs
new file mode 100644
index 000000000..cecfeb55b
--- /dev/null
+++ b/source/RevitLookup.UI.Abstractions/INavigationViewPageProvider.cs
@@ -0,0 +1,19 @@
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
+// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
+// All Rights Reserved.
+
+namespace Wpf.Ui.Abstractions;
+
+///
+/// Defines a service that provides pages for navigation.
+///
+public interface INavigationViewPageProvider
+{
+ ///
+ /// Retrieves a page of the specified type.
+ ///
+ /// The type of the page to retrieve.
+ /// An instance of the specified page type, or null if the page is not found.
+ public object? GetPage(Type pageType);
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Abstractions/NavigationException.cs b/source/RevitLookup.UI.Abstractions/NavigationException.cs
new file mode 100644
index 000000000..1f1eee3b5
--- /dev/null
+++ b/source/RevitLookup.UI.Abstractions/NavigationException.cs
@@ -0,0 +1,31 @@
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
+// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
+// All Rights Reserved.
+
+namespace Wpf.Ui.Abstractions;
+
+///
+/// Represents errors that occur during navigation.
+///
+public sealed class NavigationException : Exception
+{
+ ///
+ /// Initializes a new instance of the NavigationException class with a specified error message.
+ ///
+ /// The message that describes the error.
+ public NavigationException(string message)
+ : base(message)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the NavigationException class with a specified error message and a reference to the inner exception that is the cause of this exception.
+ ///
+ /// The exception that is the cause of the current exception.
+ /// The message that describes the error.
+ public NavigationException(Exception e, string message)
+ : base(message, e)
+ {
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Abstractions/NavigationViewPageProviderExtensions.cs b/source/RevitLookup.UI.Abstractions/NavigationViewPageProviderExtensions.cs
new file mode 100644
index 000000000..391b53d3c
--- /dev/null
+++ b/source/RevitLookup.UI.Abstractions/NavigationViewPageProviderExtensions.cs
@@ -0,0 +1,39 @@
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
+// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
+// All Rights Reserved.
+
+namespace Wpf.Ui.Abstractions;
+
+///
+/// Provides extension methods for the INavigationViewPageProvider interface.
+///
+public static class NavigationViewPageProviderExtensions
+{
+ ///
+ /// Retrieves a page of the specified type from the page service.
+ ///
+ /// The type of the page to retrieve.
+ /// The page service instance.
+ /// An instance of the specified page type, or null if the page is not found.
+ public static TPage? GetPage(this INavigationViewPageProvider navigationViewPageProvider)
+ where TPage : class
+ {
+ return navigationViewPageProvider.GetPage(typeof(TPage)) as TPage;
+ }
+
+ ///
+ /// Retrieves a page of the specified type from the page service.
+ /// Throws a NavigationException if the page is not found.
+ ///
+ /// The type of the page to retrieve.
+ /// The page service instance.
+ /// An instance of the specified page type.
+ /// Thrown when the specified page type is not found.
+ public static TPage GetRequiredPage(this INavigationViewPageProvider navigationViewPageProvider)
+ where TPage : class
+ {
+ return navigationViewPageProvider.GetPage(typeof(TPage)) as TPage
+ ?? throw new NavigationException($"{typeof(TPage)} page not found.");
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Abstractions/RevitLookup.UI.Abstractions.csproj b/source/RevitLookup.UI.Abstractions/RevitLookup.UI.Abstractions.csproj
new file mode 100644
index 000000000..8aa86de68
--- /dev/null
+++ b/source/RevitLookup.UI.Abstractions/RevitLookup.UI.Abstractions.csproj
@@ -0,0 +1,9 @@
+
+
+
+ true
+ net48;net8.0-windows
+ Wpf.Ui.Abstractions
+
+
+
diff --git a/source/RevitLookup.UI.Demo/App.xaml b/source/RevitLookup.UI.Demo/App.xaml
deleted file mode 100644
index 20afa7405..000000000
--- a/source/RevitLookup.UI.Demo/App.xaml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Demo/App.xaml.cs b/source/RevitLookup.UI.Demo/App.xaml.cs
deleted file mode 100644
index 495e06a9f..000000000
--- a/source/RevitLookup.UI.Demo/App.xaml.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2003-2024 by Autodesk, Inc.
-//
-// Permission to use, copy, modify, and distribute this software in
-// object code form for any purpose and without fee is hereby granted,
-// provided that the above copyright notice appears in all copies and
-// that both that copyright notice and the limited warranty and
-// restricted rights notice below appear in all supporting
-// documentation.
-//
-// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
-// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
-// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
-// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
-// UNINTERRUPTED OR ERROR FREE.
-//
-// Use, duplication, or disclosure by the U.S. Government is subject to
-// restrictions set forth in FAR 52.227-19 (Commercial Computer
-// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
-// (Rights in Technical Data and Computer Software), as applicable.
-
-using System.IO;
-using System.Reflection;
-using System.Windows;
-using RevitLookup.Services.Contracts;
-using RevitLookup.Views.Pages;
-
-namespace RevitLookup.UI.Demo;
-
-public sealed partial class App
-{
- private string _revitPath;
-
- private void OnStartup(object sender, StartupEventArgs e)
- {
- AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainOnAssemblyResolve;
- var host = HostProvider.CreateHost();
-
- Host.StartProxy(host);
- Host.GetService().Show();
- }
-
- private void OnExit(object sender, ExitEventArgs e)
- {
- var settingsService = Host.GetService();
- settingsService.SaveSettings();
-
- Host.Stop();
- }
-
- private Assembly CurrentDomainOnAssemblyResolve(object sender, ResolveEventArgs args)
- {
- var assemblyName = new AssemblyName(args.Name);
- _revitPath ??= $@"C:\Program Files\Autodesk\Revit 20{assemblyName.Version!.Major}";
- if (!Directory.Exists(_revitPath)) return null;
-
- var assemblyPath = Path.Combine(_revitPath, $"{assemblyName.Name}.dll");
- if (!File.Exists(assemblyPath)) return null;
-
- return Assembly.LoadFrom(assemblyPath);
- }
-}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Demo/HostProvider.cs b/source/RevitLookup.UI.Demo/HostProvider.cs
deleted file mode 100644
index 32489388a..000000000
--- a/source/RevitLookup.UI.Demo/HostProvider.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-using System.IO;
-using System.Reflection;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Hosting;
-using Microsoft.Extensions.Logging;
-using RevitLookup.Config;
-using RevitLookup.Services;
-using RevitLookup.Services.Contracts;
-using RevitLookup.UI.Demo.Mock.Services;
-using RevitLookup.ViewModels.Contracts;
-using RevitLookup.ViewModels.Pages;
-using RevitLookup.Views;
-using RevitLookup.Views.Pages;
-using Wpf.Ui;
-using MockDashboardViewModel = RevitLookup.UI.Demo.Mock.ViewModels.MockDashboardViewModel;
-using MockEventsViewModel = RevitLookup.UI.Demo.Mock.ViewModels.MockEventsViewModel;
-using MockSnoopViewModel = RevitLookup.UI.Demo.Mock.ViewModels.MockSnoopViewModel;
-
-namespace RevitLookup.UI.Demo;
-
-public static class HostProvider
-{
- public static IHost CreateHost()
- {
- var builder = new HostApplicationBuilder(new HostApplicationBuilderSettings
- {
- ContentRootPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly()!.Location),
- DisableDefaults = true
- });
-
- //Logging
- builder.Logging.ClearProviders();
- builder.Logging.AddSerilogConfiguration();
-
- //Configuration
- builder.Services.AddOptions(builder.Configuration);
-
- //App services
- builder.Services.AddSingleton();
- builder.Services.AddSingleton();
-
- //UI services
- builder.Services.AddScoped();
- builder.Services.AddScoped();
- builder.Services.AddScoped();
- builder.Services.AddScoped();
-
- //Views
- builder.Services.AddScoped();
- builder.Services.AddScoped();
- builder.Services.AddScoped();
- builder.Services.AddScoped();
- builder.Services.AddScoped();
- builder.Services.AddScoped();
- builder.Services.AddScoped();
- builder.Services.AddScoped();
- builder.Services.AddScoped();
- builder.Services.AddScoped();
- builder.Services.AddScoped();
- builder.Services.AddScoped();
-
- //Startup view
- builder.Services.AddTransient();
-
- return builder.Build();
- }
-}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Demo/Mock/Services/MockLookupService.cs b/source/RevitLookup.UI.Demo/Mock/Services/MockLookupService.cs
deleted file mode 100644
index e6d520af9..000000000
--- a/source/RevitLookup.UI.Demo/Mock/Services/MockLookupService.cs
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2003-2024 by Autodesk, Inc.
-//
-// Permission to use, copy, modify, and distribute this software in
-// object code form for any purpose and without fee is hereby granted,
-// provided that the above copyright notice appears in all copies and
-// that both that copyright notice and the limited warranty and
-// restricted rights notice below appear in all supporting
-// documentation.
-//
-// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
-// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
-// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
-// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
-// UNINTERRUPTED OR ERROR FREE.
-//
-// Use, duplication, or disclosure by the U.S. Government is subject to
-// restrictions set forth in FAR 52.227-19 (Commercial Computer
-// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
-// (Rights in Technical Data and Computer Software), as applicable.
-
-using System.Windows;
-using System.Windows.Controls;
-using Microsoft.Extensions.DependencyInjection;
-using RevitLookup.Core.Objects;
-using RevitLookup.Services.Contracts;
-using RevitLookup.Services.Enums;
-using Wpf.Ui;
-
-namespace RevitLookup.UI.Demo.Mock.Services;
-
-public sealed class MockLookupService(IServiceScopeFactory scopeFactory) : ILookupService
-{
- private Window _owner;
- private Task _activeTask;
- private readonly IServiceScope _scope = scopeFactory.CreateScope();
-
- public ILookupServiceDependsStage Snoop(SnoopableType snoopableType)
- {
- _activeTask = _scope.ServiceProvider.GetRequiredService()!.SnoopAsync(snoopableType);
- return this;
- }
-
- public ILookupServiceDependsStage Snoop(SnoopableObject snoopableObject)
- {
- _scope.ServiceProvider.GetRequiredService()!.Snoop(snoopableObject);
- return this;
- }
-
- public ILookupServiceDependsStage Snoop(IList snoopableObjects)
- {
- _scope.ServiceProvider.GetRequiredService()!.Snoop(snoopableObjects);
- return this;
- }
-
- public ILookupServiceShowStage DependsOn(IServiceProvider provider)
- {
- _owner = (Window)provider.GetService();
- return this;
- }
-
- public ILookupServiceExecuteStage Show() where T : Page
- {
- if (_activeTask is null)
- {
- ShowPage();
- }
- else
- {
- _activeTask = _activeTask.ContinueWith(_ => ShowPage(), TaskScheduler.FromCurrentSynchronizationContext());
- }
-
- return this;
- }
-
- public void Execute(Action handler) where T : class
- {
- if (_activeTask is null)
- {
- InvokeHandler(handler);
- }
- else
- {
- _activeTask = _activeTask.ContinueWith(_ => InvokeHandler(handler), TaskScheduler.FromCurrentSynchronizationContext());
- }
- }
-
- private void ShowPage() where T : Page
- {
- var window = (Window)_scope.ServiceProvider.GetRequiredService();
- window.Closed += OnWindowClosed;
-
- if (_owner is null)
- {
- window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
- }
- else
- {
- window.Left = _owner.Left + 47;
- window.Top = _owner.Top + 49;
- }
-
- window.Show();
- _scope.ServiceProvider.GetRequiredService().Navigate(typeof(T));
- }
-
- private void InvokeHandler(Action handler) where T : class
- {
- var service = _scope.ServiceProvider.GetRequiredService();
- handler.Invoke(service);
- }
-
- private void OnWindowClosed(object sender, EventArgs e)
- {
- _scope.Dispose();
- }
-}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Demo/Mock/Services/MockSnoopVisualService.cs b/source/RevitLookup.UI.Demo/Mock/Services/MockSnoopVisualService.cs
deleted file mode 100644
index 0de6438cc..000000000
--- a/source/RevitLookup.UI.Demo/Mock/Services/MockSnoopVisualService.cs
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright 2003-2024 by Autodesk, Inc.
-//
-// Permission to use, copy, modify, and distribute this software in
-// object code form for any purpose and without fee is hereby granted,
-// provided that the above copyright notice appears in all copies and
-// that both that copyright notice and the limited warranty and
-// restricted rights notice below appear in all supporting
-// documentation.
-//
-// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
-// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
-// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
-// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
-// UNINTERRUPTED OR ERROR FREE.
-//
-// Use, duplication, or disclosure by the U.S. Government is subject to
-// restrictions set forth in FAR 52.227-19 (Commercial Computer
-// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
-// (Rights in Technical Data and Computer Software), as applicable.
-
-using Autodesk.Revit.DB;
-using Bogus;
-using RevitLookup.Core.Contracts;
-using RevitLookup.Core.Objects;
-using RevitLookup.Core.Utils;
-using RevitLookup.Services;
-using RevitLookup.Services.Contracts;
-using RevitLookup.Services.Enums;
-using RevitLookup.ViewModels.Contracts;
-using Visibility = System.Windows.Visibility;
-
-namespace RevitLookup.UI.Demo.Mock.Services;
-
-public sealed class MockSnoopVisualService(NotificationService notificationService, ISnoopViewModel viewModel, IWindow window) : ISnoopVisualService
-{
- public void Snoop(SnoopableObject snoopableObject)
- {
- try
- {
- if (snoopableObject.Descriptor is IDescriptorEnumerator { IsEmpty: false } descriptor)
- {
- viewModel.SnoopableObjects = descriptor.ParseEnumerable(snoopableObject);
- }
- else
- {
- viewModel.SnoopableObjects = new[] { snoopableObject };
- }
-
- viewModel.SnoopableData = Array.Empty();
- }
- catch (Exception exception)
- {
- notificationService.ShowError("Invalid object", exception);
- }
- }
-
- public void Snoop(IList snoopableObjects)
- {
- viewModel.SnoopableObjects = snoopableObjects;
- viewModel.SnoopableData = Array.Empty();
- }
-
- public async Task SnoopAsync(SnoopableType snoopableType)
- {
- switch (snoopableType)
- {
- case SnoopableType.Face:
- case SnoopableType.Edge:
- case SnoopableType.LinkedElement:
- case SnoopableType.Point:
- case SnoopableType.SubElement:
- UpdateWindowVisibility(Visibility.Hidden);
- await Task.Delay(TimeSpan.FromSeconds(2));
- break;
- }
-
- var generationCount = snoopableType switch
- {
- SnoopableType.View => 50_000,
- SnoopableType.Document => 25_000,
- SnoopableType.Application => 10_000,
- SnoopableType.UiApplication => 5_000,
- SnoopableType.Database => 2_000,
- SnoopableType.DependentElements => 1_000,
- SnoopableType.Selection => 500,
- SnoopableType.LinkedElement => 250,
- SnoopableType.Face => 125,
- SnoopableType.Edge => 60,
- SnoopableType.Point => 30,
- SnoopableType.SubElement => 15,
- SnoopableType.ComponentManager => 8,
- SnoopableType.PerformanceAdviser => 4,
- SnoopableType.UpdaterRegistry => 2,
- SnoopableType.Services => 1,
- SnoopableType.Schemas => 0,
- _ => throw new ArgumentOutOfRangeException(nameof(snoopableType), snoopableType, null)
- };
-
- var items = await GenerateObjectsAsync(generationCount);
- Snoop(items);
- UpdateWindowVisibility(Visibility.Visible);
- }
-
- private void UpdateWindowVisibility(Visibility visibility)
- {
- if (!window.IsLoaded) return;
-
- window.Visibility = visibility;
- }
-
- private static async Task> GenerateObjectsAsync(int generationCount)
- {
- if (generationCount == 0) return Array.Empty();
-
- return await Task.Run(() => new Faker()
- .CustomInstantiator(faker =>
- {
- if (faker.IndexFaker % 2000 == 0) return new SnoopableObject((object)null);
- if (faker.IndexFaker % 1000 == 0) return new SnoopableObject(string.Empty);
- if (faker.IndexFaker % 700 == 0) return new SnoopableObject(faker.Make(150, () => faker.Internet.UserName()));
- if (faker.IndexFaker % 500 == 0) return new SnoopableObject(typeof(DateTime));
- if (faker.IndexFaker % 200 == 0) return new SnoopableObject(faker.Lorem.Sentence());
- if (faker.IndexFaker % 100 == 0) return new SnoopableObject(faker.Make(150, () => new Color(faker.Random.Byte(), faker.Random.Byte(), faker.Random.Byte())));
- if (faker.IndexFaker % 5 == 0) return new SnoopableObject(faker.Random.Int(0));
- if (faker.IndexFaker % 3 == 0) return new SnoopableObject(faker.Random.Bool());
-
- return new SnoopableObject(faker.Lorem.Word());
- })
- .Generate(generationCount));
- }
-}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Demo/Mock/ViewModels/MockDashboardViewModel.cs b/source/RevitLookup.UI.Demo/Mock/ViewModels/MockDashboardViewModel.cs
deleted file mode 100644
index af530954c..000000000
--- a/source/RevitLookup.UI.Demo/Mock/ViewModels/MockDashboardViewModel.cs
+++ /dev/null
@@ -1,143 +0,0 @@
-// Copyright 2003-2024 by Autodesk, Inc.
-//
-// Permission to use, copy, modify, and distribute this software in
-// object code form for any purpose and without fee is hereby granted,
-// provided that the above copyright notice appears in all copies and
-// that both that copyright notice and the limited warranty and
-// restricted rights notice below appear in all supporting
-// documentation.
-//
-// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
-// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
-// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
-// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
-// UNINTERRUPTED OR ERROR FREE.
-//
-// Use, duplication, or disclosure by the U.S. Government is subject to
-// restrictions set forth in FAR 52.227-19 (Commercial Computer
-// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
-// (Rights in Technical Data and Computer Software), as applicable.
-
-using CommunityToolkit.Mvvm.ComponentModel;
-using CommunityToolkit.Mvvm.Input;
-using RevitLookup.Services.Contracts;
-using RevitLookup.Services.Enums;
-using RevitLookup.ViewModels.Contracts;
-using RevitLookup.Views.Dialogs;
-using RevitLookup.Views.Pages;
-using Wpf.Ui;
-
-namespace RevitLookup.UI.Demo.Mock.ViewModels;
-
-public partial class MockDashboardViewModel(
- INavigationService navigationService,
- ISnoopVisualService snoopVisualService,
- IServiceProvider serviceProvider)
- : ObservableObject, IDashboardViewModel
-{
- [RelayCommand]
- private async Task NavigateSnoopPage(string parameter)
- {
- switch (parameter)
- {
- case "view":
- await snoopVisualService.SnoopAsync(SnoopableType.View);
- navigationService.Navigate(typeof(SnoopPage));
- break;
- case "document":
- await snoopVisualService.SnoopAsync(SnoopableType.Document);
- navigationService.Navigate(typeof(SnoopPage));
- break;
- case "application":
- await snoopVisualService.SnoopAsync(SnoopableType.Application);
- navigationService.Navigate(typeof(SnoopPage));
- break;
- case "uiApplication":
- await snoopVisualService.SnoopAsync(SnoopableType.UiApplication);
- navigationService.Navigate(typeof(SnoopPage));
- break;
- case "database":
- await snoopVisualService.SnoopAsync(SnoopableType.Database);
- navigationService.Navigate(typeof(SnoopPage));
- break;
- case "dependents":
- await snoopVisualService.SnoopAsync(SnoopableType.DependentElements);
- navigationService.Navigate(typeof(SnoopPage));
- break;
- case "selection":
- await snoopVisualService.SnoopAsync(SnoopableType.Selection);
- navigationService.Navigate(typeof(SnoopPage));
- break;
- case "linked":
- await snoopVisualService.SnoopAsync(SnoopableType.LinkedElement);
- navigationService.Navigate(typeof(SnoopPage));
- break;
- case "face":
- await snoopVisualService.SnoopAsync(SnoopableType.Face);
- navigationService.Navigate(typeof(SnoopPage));
- break;
- case "edge":
- await snoopVisualService.SnoopAsync(SnoopableType.Edge);
- navigationService.Navigate(typeof(SnoopPage));
- break;
- case "point":
- await snoopVisualService.SnoopAsync(SnoopableType.Point);
- navigationService.Navigate(typeof(SnoopPage));
- break;
- case "subElement":
- await snoopVisualService.SnoopAsync(SnoopableType.SubElement);
- navigationService.Navigate(typeof(SnoopPage));
- break;
- case "components":
- await snoopVisualService.SnoopAsync(SnoopableType.ComponentManager);
- navigationService.Navigate(typeof(SnoopPage));
- break;
- case "performance":
- await snoopVisualService.SnoopAsync(SnoopableType.PerformanceAdviser);
- navigationService.Navigate(typeof(SnoopPage));
- break;
- case "updaters":
- await snoopVisualService.SnoopAsync(SnoopableType.UpdaterRegistry);
- navigationService.Navigate(typeof(SnoopPage));
- break;
- case "services":
- await snoopVisualService.SnoopAsync(SnoopableType.Services);
- navigationService.Navigate(typeof(SnoopPage));
- break;
- case "schemas":
- await snoopVisualService.SnoopAsync(SnoopableType.Schemas);
- navigationService.Navigate(typeof(SnoopPage));
- break;
- case "events":
- navigationService.Navigate(typeof(EventsPage));
- break;
- default:
- throw new ArgumentOutOfRangeException(nameof(parameter), parameter);
- }
- }
-
- [RelayCommand]
- private Task OpenDialog(string parameter)
- {
- switch (parameter)
- {
- case "parameters":
- var unitsDialog = new UnitsDialog(serviceProvider);
- return unitsDialog.ShowParametersAsync();
- case "categories":
- unitsDialog = new UnitsDialog(serviceProvider);
- return unitsDialog.ShowCategoriesAsync();
- case "forge":
- unitsDialog = new UnitsDialog(serviceProvider);
- return unitsDialog.ShowForgeSchemaAsync();
- case "search":
- var searchDialog = new SearchElementsDialog(serviceProvider);
- return searchDialog.ShowAsync();
- case "modules":
- var modulesDialog = new ModulesDialog(serviceProvider);
- return modulesDialog.ShowAsync();
- }
-
- return Task.CompletedTask;
- }
-}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Demo/Mock/ViewModels/MockEventsViewModel.cs b/source/RevitLookup.UI.Demo/Mock/ViewModels/MockEventsViewModel.cs
deleted file mode 100644
index f98cb49ac..000000000
--- a/source/RevitLookup.UI.Demo/Mock/ViewModels/MockEventsViewModel.cs
+++ /dev/null
@@ -1,163 +0,0 @@
-using System.Windows.Media;
-using Bogus;
-using CommunityToolkit.Mvvm.ComponentModel;
-using CommunityToolkit.Mvvm.Input;
-using RevitLookup.Core.Enums;
-using RevitLookup.Core.Objects;
-using RevitLookup.Services;
-using RevitLookup.Services.Contracts;
-using RevitLookup.ViewModels.Contracts;
-using RevitLookup.ViewModels.Utils;
-using RevitLookup.Views.Pages;
-
-namespace RevitLookup.UI.Demo.Mock.ViewModels;
-
-public sealed partial class MockEventsViewModel(
- NotificationService notificationService,
- IServiceProvider provider)
- : ObservableObject, IEventsViewModel
-{
- private readonly CancellationTokenSource _cancellationTokenSource = new();
- private readonly Stack _events = new();
-
- [ObservableProperty] private string _searchText = string.Empty;
- [ObservableProperty] private IList _snoopableObjects = [];
- [ObservableProperty] private IList _filteredSnoopableObjects = [];
- [ObservableProperty] private IList _filteredSnoopableData;
- [ObservableProperty] private IList _snoopableData;
-
- public SnoopableObject SelectedObject { get; set; }
- public IServiceProvider ServiceProvider { get; } = provider;
-
- public void Navigate(SnoopableObject selectedItem)
- {
- Host.GetService()
- .Snoop(selectedItem)
- .DependsOn(ServiceProvider)
- .Show();
- }
-
- public void Navigate(IList selectedItems)
- {
- Host.GetService()
- .Snoop(selectedItems)
- .DependsOn(ServiceProvider)
- .Show();
- }
-
- public void RemoveObject(object obj)
- {
- var snoopableObject = obj switch
- {
- SnoopableObject snoopable => snoopable,
- Descriptor descriptor => descriptor.Value.Descriptor.Value,
- _ => throw new NotSupportedException($"Type {obj.GetType().Name} removing not supported")
- };
-
- SnoopableObjects.Remove(snoopableObject);
- FilteredSnoopableObjects.Remove(snoopableObject);
- }
-
- partial void OnSearchTextChanged(string value)
- {
- UpdateSearchResults(SearchOption.Objects);
- }
-
- partial void OnSnoopableObjectsChanged(IList value)
- {
- SelectedObject = null;
- UpdateSearchResults(SearchOption.Objects);
- }
-
- partial void OnSnoopableDataChanged(IList value)
- {
- UpdateSearchResults(SearchOption.Selection);
- }
-
- private void UpdateSearchResults(SearchOption option)
- {
- Task.Run(() =>
- {
- if (string.IsNullOrEmpty(SearchText))
- {
- FilteredSnoopableObjects = SnoopableObjects;
- FilteredSnoopableData = SnoopableData;
- return;
- }
-
- var results = SearchEngine.Search(this, option);
- if (results.Data is not null) FilteredSnoopableData = results.Data;
- if (results.Objects is not null) FilteredSnoopableObjects = results.Objects;
- });
- }
-
- [RelayCommand]
- private Task FetchMembersAsync()
- {
- CollectMembers(true);
- return Task.CompletedTask;
- }
-
- [RelayCommand]
- private Task RefreshMembersAsync()
- {
- CollectMembers(false);
- return Task.CompletedTask;
- }
-
- private void CollectMembers(bool useCached)
- {
- if (SelectedObject is null)
- {
- SnoopableData = Array.Empty();
- return;
- }
-
- try
- {
- // ReSharper disable once MethodHasAsyncOverload
- SnoopableData = SelectedObject.GetMembers();
- }
- catch (Exception exception)
- {
- notificationService.ShowError("Snoop engine error", exception);
- }
- }
-
- public void OnNavigatedTo()
- {
- PushEvents(_cancellationTokenSource.Token);
- }
-
- public void OnNavigatedFrom()
- {
- _cancellationTokenSource.Cancel();
- }
-
- private void PushEvents(CancellationToken cancellationToken)
- {
- Task.Run((Func)(async () =>
- {
- var iteration = 0;
- var faker = new Faker();
- while (!cancellationToken.IsCancellationRequested)
- {
- await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
-
- var snoopableObject = GenerateEvent(faker, iteration);
- _events.Push(snoopableObject);
- SnoopableObjects = new List(_events);
- iteration++;
- }
- }), cancellationToken);
- }
-
- private static SnoopableObject GenerateEvent(Faker faker, int iteration)
- {
- if (iteration % 5 == 0) return new SnoopableObject(typeof(DateTime));
- if (iteration % 4 == 0) return new SnoopableObject(Color.FromRgb(faker.Random.Byte(), faker.Random.Byte(), faker.Random.Byte()));
- if (iteration % 3 == 0) return new SnoopableObject(faker.Random.Int(0));
- if (iteration % 2 == 0) return new SnoopableObject(faker.Random.Bool());
- return new SnoopableObject(faker.Lorem.Word());
- }
-}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Demo/Mock/ViewModels/MockSnoopViewModel.cs b/source/RevitLookup.UI.Demo/Mock/ViewModels/MockSnoopViewModel.cs
deleted file mode 100644
index f7db44835..000000000
--- a/source/RevitLookup.UI.Demo/Mock/ViewModels/MockSnoopViewModel.cs
+++ /dev/null
@@ -1,148 +0,0 @@
-// Copyright 2003-2024 by Autodesk, Inc.
-//
-// Permission to use, copy, modify, and distribute this software in
-// object code form for any purpose and without fee is hereby granted,
-// provided that the above copyright notice appears in all copies and
-// that both that copyright notice and the limited warranty and
-// restricted rights notice below appear in all supporting
-// documentation.
-//
-// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
-// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
-// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
-// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
-// UNINTERRUPTED OR ERROR FREE.
-//
-// Use, duplication, or disclosure by the U.S. Government is subject to
-// restrictions set forth in FAR 52.227-19 (Commercial Computer
-// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
-// (Rights in Technical Data and Computer Software), as applicable.
-
-using System.Collections.ObjectModel;
-using CommunityToolkit.Mvvm.ComponentModel;
-using CommunityToolkit.Mvvm.Input;
-using RevitLookup.Core.Enums;
-using RevitLookup.Core.Objects;
-using RevitLookup.Services;
-using RevitLookup.Services.Contracts;
-using RevitLookup.ViewModels.Contracts;
-using RevitLookup.ViewModels.Utils;
-using RevitLookup.Views.Pages;
-
-namespace RevitLookup.UI.Demo.Mock.ViewModels;
-
-public sealed partial class MockSnoopViewModel(NotificationService notificationService, IServiceProvider provider) : ObservableObject, ISnoopViewModel
-{
- private Task _updatingTask = Task.CompletedTask;
-
- [ObservableProperty] private string _searchText = string.Empty;
- [ObservableProperty] private IList _snoopableObjects = [];
- [ObservableProperty] private IList _filteredSnoopableObjects = [];
- [ObservableProperty] private IList _filteredSnoopableData;
- [ObservableProperty] private IList _snoopableData;
-
- public SnoopableObject SelectedObject { get; set; }
- public IServiceProvider ServiceProvider { get; } = provider;
-
- public void Navigate(SnoopableObject selectedItem)
- {
- Host.GetService()
- .Snoop(selectedItem)
- .DependsOn(ServiceProvider)
- .Show();
- }
-
- public void Navigate(IList selectedItems)
- {
- Host.GetService()
- .Snoop(selectedItems)
- .DependsOn(ServiceProvider)
- .Show();
- }
-
- async partial void OnSearchTextChanged(string value)
- {
- await _updatingTask;
- UpdateSearchResults(SearchOption.Objects);
- }
-
- async partial void OnSnoopableObjectsChanged(IList value)
- {
- SelectedObject = null;
- await _updatingTask;
- UpdateSearchResults(SearchOption.Objects);
- }
-
- async partial void OnSnoopableDataChanged(IList value)
- {
- await _updatingTask;
- UpdateSearchResults(SearchOption.Selection);
- }
-
- private void UpdateSearchResults(SearchOption option)
- {
- _updatingTask = Task.Run(() =>
- {
- if (string.IsNullOrEmpty(SearchText))
- {
- if (option == SearchOption.Objects)
- {
- FilteredSnoopableObjects = SnoopableObjects;
- }
-
- FilteredSnoopableData = SnoopableData;
- return;
- }
-
- var results = SearchEngine.Search(this, option);
- if (results.Data is not null) FilteredSnoopableData = results.Data;
- if (results.Objects is not null) FilteredSnoopableObjects = new ObservableCollection(results.Objects);
- });
- }
-
- public void RemoveObject(object obj)
- {
- var snoopableObject = obj switch
- {
- SnoopableObject snoopable => snoopable,
- Descriptor descriptor => descriptor.Value.Descriptor.Value,
- _ => throw new NotSupportedException($"Type {obj.GetType().Name} removing not supported")
- };
-
- SnoopableObjects.Remove(snoopableObject);
- FilteredSnoopableObjects.Remove(snoopableObject);
- }
-
- [RelayCommand]
- private Task FetchMembersAsync()
- {
- CollectMembers(true);
- return Task.CompletedTask;
- }
-
- [RelayCommand]
- private Task RefreshMembersAsync()
- {
- CollectMembers(false);
- return Task.CompletedTask;
- }
-
- private void CollectMembers(bool useCached)
- {
- if (SelectedObject is null)
- {
- SnoopableData = Array.Empty();
- return;
- }
-
- try
- {
- // ReSharper disable once MethodHasAsyncOverload
- SnoopableData = SelectedObject.GetMembers();
- }
- catch (Exception exception)
- {
- notificationService.ShowError("Snoop engine error", exception);
- }
- }
-}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Demo/RevitLookup.UI.Demo.csproj b/source/RevitLookup.UI.Demo/RevitLookup.UI.Demo.csproj
deleted file mode 100644
index b6e68b571..000000000
--- a/source/RevitLookup.UI.Demo/RevitLookup.UI.Demo.csproj
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
- true
- WinExe
- latest
- x64
- true
- net8.0-windows
-
-
-
- true
- full
- $(DefineConstants);DEBUG
-
-
- true
- none
- $(DefineConstants);RELEASE
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/source/RevitLookup.UI.Framework/App.xaml b/source/RevitLookup.UI.Framework/App.xaml
new file mode 100644
index 000000000..e0914cacd
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/App.xaml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/App.xaml.cs b/source/RevitLookup.UI.Framework/App.xaml.cs
new file mode 100644
index 000000000..f428252d5
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/App.xaml.cs
@@ -0,0 +1,3 @@
+namespace RevitLookup.UI.Framework;
+
+public partial class App;
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Controls/Automation/NoAutomationWindowPeer.cs b/source/RevitLookup.UI.Framework/Controls/Automation/NoAutomationWindowPeer.cs
new file mode 100644
index 000000000..ba5a8a921
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Controls/Automation/NoAutomationWindowPeer.cs
@@ -0,0 +1,18 @@
+using System.Windows;
+using System.Windows.Automation.Peers;
+
+namespace RevitLookup.UI.Framework.Controls.Automation;
+
+///
+/// Windows peer disabling automation. Removes freezes when using Tooltip, Popup
+///
+///
+/// https://github.com/dotnet/wpf/issues/5807
+///
+public sealed class NoAutomationWindowPeer(Window owner) : WindowAutomationPeer(owner)
+{
+ protected override List GetChildrenCore()
+ {
+ return [];
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup/Views/Controls/ColorPicker/ColorPickerControl.xaml b/source/RevitLookup.UI.Framework/Controls/ColorPicker/ColorPickerControl.xaml
similarity index 99%
rename from source/RevitLookup/Views/Controls/ColorPicker/ColorPickerControl.xaml
rename to source/RevitLookup.UI.Framework/Controls/ColorPicker/ColorPickerControl.xaml
index e2d0e73b9..d2b328b6f 100644
--- a/source/RevitLookup/Views/Controls/ColorPicker/ColorPickerControl.xaml
+++ b/source/RevitLookup.UI.Framework/Controls/ColorPicker/ColorPickerControl.xaml
@@ -1,5 +1,5 @@
(Color) GetValue(SelectedColorProperty);
set => SetValue(SelectedColorProperty, value);
}
-
+
private static void SelectedColorPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
var control = (ColorPickerControl) dependencyObject;
var newColor = (Color) e.NewValue;
-
+
control._originalColor = control._currentColor = newColor;
var newColorBackground = new SolidColorBrush(newColor);
control.CurrentColorButton.Background = newColorBackground;
-
+
control._ignoreHexChanges = true;
control._ignoreRgbChanges = true;
-
+
control.HexCode.Text = ColorToHex(newColor);
control.RNumberBox.Value = newColor.R;
control.GNumberBox.Value = newColor.G;
control.BNumberBox.Value = newColor.B;
control.SetColorFromTextBoxes(System.Drawing.Color.FromArgb(newColor.R, newColor.G, newColor.B));
-
+
control._ignoreRgbChanges = false;
control._ignoreHexChanges = false;
-
+
var hsv = ColorFormatUtils.ConvertToHsvColor(System.Drawing.Color.FromArgb(newColor.R, newColor.G, newColor.B));
SetColorVariationsForCurrentColor(dependencyObject, hsv);
}
-
+
private void UpdateHueGradient(double saturation, double value)
{
var g6 = HsvColor.HueSpectrum(saturation, value);
-
+
var gradientBrush = new LinearGradientBrush
{
StartPoint = new Point(0, 0),
EndPoint = new Point(1, 0)
};
-
+
for (var i = 0; i < g6.Length; i++)
{
var stop = new GradientStop(g6[i], i * 0.16);
gradientBrush.GradientStops.Add(stop);
}
-
+
HueGradientSlider.Background = gradientBrush;
}
-
+
private static void SetColorVariationsForCurrentColor(DependencyObject d, (double Hue, double Saturation, double Value) hsv)
{
var hueCoefficient = 0;
@@ -97,58 +99,58 @@ private static void SetColorVariationsForCurrentColor(DependencyObject d, (doubl
{
hueCoefficient = 1;
}
-
+
if (hsv.Value - 0.3 < 0)
{
hueCoefficient2 = 1;
}
-
+
var s = hsv.Saturation;
var control = (ColorPickerControl) d;
-
+
control.ColorVariation1Button.Background = new SolidColorBrush(HsvColor.RgbFromHsv(Math.Min(hsv.Hue + (hueCoefficient * 8), 360), s, Math.Min(hsv.Value + 0.3, 1)));
control.ColorVariation2Button.Background = new SolidColorBrush(HsvColor.RgbFromHsv(Math.Min(hsv.Hue + (hueCoefficient * 4), 360), s, Math.Min(hsv.Value + 0.15, 1)));
-
+
control.ColorVariation3Button.Background = new SolidColorBrush(HsvColor.RgbFromHsv(Math.Max(hsv.Hue - (hueCoefficient2 * 4), 0), s, Math.Max(hsv.Value - 0.2, 0)));
control.ColorVariation4Button.Background = new SolidColorBrush(HsvColor.RgbFromHsv(Math.Max(hsv.Hue - (hueCoefficient2 * 8), 0), s, Math.Max(hsv.Value - 0.3, 0)));
}
-
+
private void UpdateValueColorGradient(double posX)
{
ValueGradientSlider.Value = posX;
-
+
_currV = posX / ValueGradientSlider.Maximum;
-
+
UpdateHueGradient(_currS, _currV);
-
+
SaturationStartColor.Color = HsvColor.RgbFromHsv(_currH, 0f, _currV);
SaturationStopColor.Color = HsvColor.RgbFromHsv(_currH, 1f, _currV);
}
-
+
private void UpdateSaturationColorGradient(double posX)
{
SaturationGradientSlider.Value = posX;
-
+
_currS = posX / HueGradientSlider.Maximum;
-
+
UpdateHueGradient(_currS, _currV);
-
+
ValueStartColor.Color = HsvColor.RgbFromHsv(_currH, _currS, 0f);
ValueStopColor.Color = HsvColor.RgbFromHsv(_currH, _currS, 1f);
}
-
+
private void UpdateHueColorGradient(double posX)
{
HueGradientSlider.Value = posX;
_currH = posX / HueGradientSlider.Maximum * 360;
-
+
SaturationStartColor.Color = HsvColor.RgbFromHsv(_currH, 0f, _currV);
SaturationStopColor.Color = HsvColor.RgbFromHsv(_currH, 1f, _currV);
-
+
ValueStartColor.Color = HsvColor.RgbFromHsv(_currH, _currS, 0f);
ValueStopColor.Color = HsvColor.RgbFromHsv(_currH, _currS, 1f);
}
-
+
private void UpdateTextBoxesAndCurrentColor(Color currentColor)
{
if (!_ignoreHexChanges)
@@ -156,90 +158,90 @@ private void UpdateTextBoxesAndCurrentColor(Color currentColor)
// Second parameter is set to keep the hashtag if typed by the user before
HexCode.Text = ColorToHex(currentColor, HexCode.Text);
}
-
+
if (!_ignoreRgbChanges)
{
RNumberBox.Value = currentColor.R;
GNumberBox.Value = currentColor.G;
BNumberBox.Value = currentColor.B;
}
-
+
_currentColor = currentColor;
CurrentColorButton.Background = new SolidColorBrush(currentColor);
}
-
+
private void OnCurrentColorButtonClicked(object sender, RoutedEventArgs e)
{
ShowDetails();
}
-
+
private void ShowDetails()
{
if (_isCollapsed)
{
_isCollapsed = false;
-
+
var resizeColor = new DoubleAnimation(256, new Duration(TimeSpan.FromMilliseconds(250)))
{
EasingFunction = new ExponentialEase {EasingMode = EasingMode.EaseInOut}
};
-
+
var moveColor = new ThicknessAnimation(new Thickness(0), new Duration(TimeSpan.FromMilliseconds(250)))
{
EasingFunction = new ExponentialEase {EasingMode = EasingMode.EaseInOut}
};
-
+
CurrentColorButton.BeginAnimation(WidthProperty, resizeColor);
CurrentColorButton.BeginAnimation(MarginProperty, moveColor);
CurrentColorButton.IsEnabled = false;
DetailsFlyout.IsOpen = true;
}
}
-
+
private void HideDetails()
{
if (_isCollapsed) return;
-
+
_isCollapsed = true;
-
+
var resizeColor = new DoubleAnimation(165, new Duration(TimeSpan.FromMilliseconds(150)))
{
EasingFunction = new ExponentialEase {EasingMode = EasingMode.EaseInOut}
};
-
+
var moveColor = new ThicknessAnimation(new Thickness(72, 0, 72, 0), new Duration(TimeSpan.FromMilliseconds(150)))
{
EasingFunction = new ExponentialEase {EasingMode = EasingMode.EaseInOut}
};
-
+
CurrentColorButton.BeginAnimation(WidthProperty, resizeColor);
CurrentColorButton.BeginAnimation(MarginProperty, moveColor);
CurrentColorButton.IsEnabled = true;
}
-
+
private void OnOkButtonClicked(object sender, RoutedEventArgs e)
{
SelectedColor = _currentColor;
DetailsFlyout.Hide();
}
-
+
private void OnDetailsFlyoutClosed(object sender, object e)
{
HideDetails();
-
+
// Revert to original color
var originalColorBackground = new SolidColorBrush(_originalColor);
CurrentColorButton.Background = originalColorBackground;
-
+
HexCode.Text = ColorToHex(_originalColor);
}
-
+
private void OnColorVariationButtonClicked(object sender, RoutedEventArgs e)
{
- var selectedColor = ((SolidColorBrush) ((System.Windows.Controls.Button) sender).Background).Color;
+ var selectedColor = ((SolidColorBrush) ((Button) sender).Background).Color;
SelectedColor = selectedColor;
}
-
+
private void OnSaturationGradientSliderValueChanged(object sender, RoutedPropertyChangedEventArgs e)
{
UpdateSaturationColorGradient(((Slider) sender).Value);
@@ -247,7 +249,7 @@ private void OnSaturationGradientSliderValueChanged(object sender, RoutedPropert
UpdateTextBoxesAndCurrentColor(HsvColor.RgbFromHsv(_currH, _currS, _currV));
_ignoreGradientsChanges = false;
}
-
+
private void OnHueGradientSliderValueChanged(object sender, RoutedPropertyChangedEventArgs e)
{
UpdateHueColorGradient(((Slider) sender).Value);
@@ -255,7 +257,7 @@ private void OnHueGradientSliderValueChanged(object sender, RoutedPropertyChange
UpdateTextBoxesAndCurrentColor(HsvColor.RgbFromHsv(_currH, _currS, _currV));
_ignoreGradientsChanges = false;
}
-
+
private void OnValueGradientSliderValueChanged(object sender, RoutedPropertyChangedEventArgs e)
{
UpdateValueColorGradient(((Slider) sender).Value);
@@ -263,37 +265,37 @@ private void OnValueGradientSliderValueChanged(object sender, RoutedPropertyChan
UpdateTextBoxesAndCurrentColor(HsvColor.RgbFromHsv(_currH, _currS, _currV));
_ignoreGradientsChanges = false;
}
-
+
private void OnHexCodeTextChanged(object sender, TextChangedEventArgs e)
{
var newValue = ((TextBox) sender).Text;
-
+
// support hex with 3 and 6 characters and optional with hashtag
var reg = new Regex("^#?([0-9A-Fa-f]{3}){1,2}$");
-
+
if (!reg.IsMatch(newValue))
{
return;
}
-
+
if (_ignoreHexChanges) return;
-
- var converter = new System.Drawing.ColorConverter();
-
+
+ var converter = new ColorConverter();
+
// "FormatHexColorString()" is needed to add hashtag if missing and to convert the hex code from three to six characters. Without this we get format exceptions and incorrect color values.
var color = (System.Drawing.Color) converter.ConvertFromString(FormatHexColorString(HexCode.Text))!;
-
+
_ignoreHexChanges = true;
SetColorFromTextBoxes(color);
_ignoreHexChanges = false;
}
-
+
private void SetColorFromTextBoxes(System.Drawing.Color color)
{
if (!_ignoreGradientsChanges)
{
var hsv = ColorFormatUtils.ConvertToHsvColor(color);
-
+
var huePosition = (hsv.Hue / 360) * HueGradientSlider.Maximum;
var saturationPosition = hsv.Saturation * SaturationGradientSlider.Maximum;
var valuePosition = hsv.Value * ValueGradientSlider.Maximum;
@@ -301,10 +303,10 @@ private void SetColorFromTextBoxes(System.Drawing.Color color)
UpdateSaturationColorGradient(saturationPosition);
UpdateValueColorGradient(valuePosition);
}
-
+
UpdateTextBoxesAndCurrentColor(Color.FromRgb(color.R, color.G, color.B));
}
-
+
private static string ColorToHex(Color color, string oldValue = "")
{
#if NETCOREAPP
@@ -313,7 +315,7 @@ private static string ColorToHex(Color color, string oldValue = "")
var newHexString = BitConverter.ToString([color.R, color.G, color.B]).Replace("-", string.Empty);
#endif
newHexString = newHexString.ToLowerInvariant();
-
+
// Return only with hashtag if user typed it before
#if NETCOREAPP
var addHashtag = oldValue.StartsWith('#');
@@ -322,7 +324,7 @@ private static string ColorToHex(Color color, string oldValue = "")
#endif
return addHashtag ? "#" + newHexString : newHexString;
}
-
+
///
/// Formats the hex code string to be accepted by . We are adding hashtag at the beginning if needed and convert from three characters to six characters code.
///
@@ -335,7 +337,7 @@ private static string FormatHexColorString(string hexCodeText)
// Hex with or without hashTag and three characters
return Regex.Replace(hexCodeText, "^#?([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])$", "#$1$1$2$2$3$3");
}
-
+
// Hex with or without hashTag and six characters
#if NETCOREAPP
return hexCodeText.StartsWith('#') ? hexCodeText : "#" + hexCodeText;
@@ -343,31 +345,31 @@ private static string FormatHexColorString(string hexCodeText)
return hexCodeText.StartsWith("#") ? hexCodeText : "#" + hexCodeText;
#endif
}
-
+
private void OnHexCodeGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
((TextBox) sender).SelectAll();
}
-
+
private void OnRgbNumberBoxTextChanged(object sender, TextChangedEventArgs e)
{
if (_ignoreRgbChanges) return;
-
+
var numberBox = (NumberBox) sender;
-
+
if (!RNumberBox.Value.HasValue) return;
if (!GNumberBox.Value.HasValue) return;
if (!BNumberBox.Value.HasValue) return;
-
+
var r = numberBox.Name == "RNumberBox" ? GetValueFromNumberBox(numberBox) : (byte) RNumberBox.Value;
var g = numberBox.Name == "GNumberBox" ? GetValueFromNumberBox(numberBox) : (byte) GNumberBox.Value;
var b = numberBox.Name == "BNumberBox" ? GetValueFromNumberBox(numberBox) : (byte) BNumberBox.Value;
-
+
_ignoreRgbChanges = true;
SetColorFromTextBoxes(System.Drawing.Color.FromArgb(r, g, b));
_ignoreRgbChanges = false;
}
-
+
///
/// NumberBox provides value only after it has been validated - happens after pressing enter or leaving this control.
/// However, we need to get value immediately after the underlying textbox value changes
@@ -377,28 +379,28 @@ private void OnRgbNumberBoxTextChanged(object sender, TextChangedEventArgs e)
private static byte GetValueFromNumberBox(NumberBox numberBox)
{
if (!numberBox.Value.HasValue) return byte.MinValue;
-
+
var parsedValue = ParseDouble(numberBox.Text);
if (!parsedValue.HasValue) return (byte) numberBox.Value;
-
+
var parsedValueByte = (byte) parsedValue;
-
+
if (parsedValueByte >= numberBox.Minimum && parsedValueByte <= numberBox.Maximum)
{
return parsedValueByte;
}
-
+
// not valid input, return previous value
return (byte) numberBox.Value;
}
-
+
public static double? ParseDouble(string text)
{
if (double.TryParse(text, out var result))
{
return result;
}
-
+
return null;
}
}
\ No newline at end of file
diff --git a/source/RevitLookup/Views/Controls/ColorPicker/HSVColor.cs b/source/RevitLookup.UI.Framework/Controls/ColorPicker/HSVColor.cs
similarity index 87%
rename from source/RevitLookup/Views/Controls/ColorPicker/HSVColor.cs
rename to source/RevitLookup.UI.Framework/Controls/ColorPicker/HSVColor.cs
index 919607b77..73120b22a 100644
--- a/source/RevitLookup/Views/Controls/ColorPicker/HSVColor.cs
+++ b/source/RevitLookup.UI.Framework/Controls/ColorPicker/HSVColor.cs
@@ -4,47 +4,47 @@
using Color = System.Windows.Media.Color;
-namespace RevitLookup.Views.Controls.ColorPicker;
+namespace RevitLookup.UI.Framework.Controls.ColorPicker;
public static class HsvColor
{
public static Color[] GetSpectrum()
{
var rgbs = new Color[360];
-
+
for (var h = 0; h < 360; h++)
{
rgbs[h] = RgbFromHsv(h, 1f, 1f);
}
-
+
return rgbs;
}
-
+
public static Color[] HueSpectrum(double saturation, double value)
{
var rgbs = new Color[7];
-
+
for (var h = 0; h < 7; h++)
{
rgbs[h] = RgbFromHsv(h * 60, saturation, value);
}
-
+
return rgbs;
}
-
+
public static Color RgbFromHsv(double h, double s, double v)
{
if (h > 360 || h < 0 || s > 1 || s < 0 || v > 1 || v < 0)
{
return Color.FromRgb(0, 0, 0);
}
-
+
var c = v * s;
var x = c * (1 - Math.Abs(((h / 60) % 2) - 1));
var m = v - c;
-
+
double r = 0, g = 0, b = 0;
-
+
if (h < 60)
{
r = c;
@@ -75,7 +75,7 @@ public static Color RgbFromHsv(double h, double s, double v)
r = c;
b = x;
}
-
- return Color.FromRgb((byte)((r + m) * 255), (byte)((g + m) * 255), (byte)((b + m) * 255));
+
+ return Color.FromRgb((byte) ((r + m) * 255), (byte) ((g + m) * 255), (byte) ((b + m) * 255));
}
}
\ No newline at end of file
diff --git a/source/RevitLookup/Views/Controls/ContentPlaceholder/ContentPlaceholder.xaml b/source/RevitLookup.UI.Framework/Controls/ContentPlaceholder/ContentPlaceholder.xaml
similarity index 88%
rename from source/RevitLookup/Views/Controls/ContentPlaceholder/ContentPlaceholder.xaml
rename to source/RevitLookup.UI.Framework/Controls/ContentPlaceholder/ContentPlaceholder.xaml
index 0071f76a3..5441956ea 100644
--- a/source/RevitLookup/Views/Controls/ContentPlaceholder/ContentPlaceholder.xaml
+++ b/source/RevitLookup.UI.Framework/Controls/ContentPlaceholder/ContentPlaceholder.xaml
@@ -3,7 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
- xmlns:controls="clr-namespace:RevitLookup.Views.Controls"
+ xmlns:controls="clr-namespace:RevitLookup.UI.Framework.Controls.ContentPlaceholder"
mc:Ignorable="d">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/AboutProgram/OpenSourceDialog.xaml.cs b/source/RevitLookup.UI.Framework/Views/AboutProgram/OpenSourceDialog.xaml.cs
new file mode 100644
index 000000000..239658300
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/AboutProgram/OpenSourceDialog.xaml.cs
@@ -0,0 +1,49 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using System.Windows;
+using System.Windows.Documents;
+using RevitLookup.Abstractions.Services.Appearance;
+using RevitLookup.Abstractions.ViewModels.AboutProgram;
+using RevitLookup.Common.Tools;
+using Wpf.Ui;
+
+namespace RevitLookup.UI.Framework.Views.AboutProgram;
+
+public sealed partial class OpenSourceDialog
+{
+ public OpenSourceDialog(
+ IContentDialogService dialogService,
+ IOpenSourceViewModel viewModel,
+ IThemeWatcherService themeWatcherService)
+ : base(dialogService.GetDialogHost())
+ {
+ DataContext = viewModel;
+ InitializeComponent();
+
+ themeWatcherService.Watch(this);
+ }
+
+ private void OpenLink(object sender, RoutedEventArgs args)
+ {
+ var link = (Hyperlink) args.OriginalSource;
+ ProcessTasks.StartShell(link.NavigateUri.OriginalString);
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Dashboard/DashboardPage.xaml b/source/RevitLookup.UI.Framework/Views/Dashboard/DashboardPage.xaml
new file mode 100644
index 000000000..b18542f28
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Dashboard/DashboardPage.xaml
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Dashboard/DashboardPage.xaml.cs b/source/RevitLookup.UI.Framework/Views/Dashboard/DashboardPage.xaml.cs
new file mode 100644
index 000000000..2820d9a77
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Dashboard/DashboardPage.xaml.cs
@@ -0,0 +1,39 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using RevitLookup.Abstractions.Services.Appearance;
+using RevitLookup.Abstractions.ViewModels.Dashboard;
+using Wpf.Ui.Abstractions.Controls;
+
+namespace RevitLookup.UI.Framework.Views.Dashboard;
+
+public sealed partial class DashboardPage : INavigableView
+{
+ public DashboardPage(IDashboardViewModel viewModel, IThemeWatcherService themeWatcherService)
+ {
+ themeWatcherService.Watch(this);
+
+ ViewModel = viewModel;
+ DataContext = this;
+ InitializeComponent();
+ }
+
+ public IDashboardViewModel ViewModel { get; }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Decomposition/DecompositionSummaryPage.xaml b/source/RevitLookup.UI.Framework/Views/Decomposition/DecompositionSummaryPage.xaml
new file mode 100644
index 000000000..5de44190f
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Decomposition/DecompositionSummaryPage.xaml
@@ -0,0 +1,173 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Decomposition/DecompositionSummaryPage.xaml.cs b/source/RevitLookup.UI.Framework/Views/Decomposition/DecompositionSummaryPage.xaml.cs
new file mode 100644
index 000000000..f6bf52e53
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Decomposition/DecompositionSummaryPage.xaml.cs
@@ -0,0 +1,52 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using Microsoft.Extensions.Logging;
+using RevitLookup.Abstractions.Services.Appearance;
+using RevitLookup.Abstractions.Services.Presentation;
+using RevitLookup.Abstractions.Services.Settings;
+using RevitLookup.Abstractions.ViewModels.Decomposition;
+
+namespace RevitLookup.UI.Framework.Views.Decomposition;
+
+public sealed partial class DecompositionSummaryPage
+{
+ public DecompositionSummaryPage(
+ IServiceProvider serviceProvider,
+ IDecompositionSummaryViewModel viewModel,
+ ISettingsService settingsService,
+ IWindowIntercomService intercomService,
+ INotificationService notificationService,
+ IThemeWatcherService themeWatcherService,
+ ILoggerFactory loggerFactory)
+ : base(serviceProvider, settingsService, intercomService, notificationService, loggerFactory)
+ {
+ themeWatcherService.Watch(this);
+
+ DataContext = this;
+ ViewModel = viewModel;
+ InitializeComponent();
+
+ SearchBoxControl = SummarySearchBox;
+ TreeViewControl = SummaryTreeView;
+ DataGridControl = SummaryDataGrid;
+ InitializeControls();
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Decomposition/EventsSummaryPage.xaml b/source/RevitLookup.UI.Framework/Views/Decomposition/EventsSummaryPage.xaml
new file mode 100644
index 000000000..fa035df7a
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Decomposition/EventsSummaryPage.xaml
@@ -0,0 +1,194 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Decomposition/EventsSummaryPage.xaml.cs b/source/RevitLookup.UI.Framework/Views/Decomposition/EventsSummaryPage.xaml.cs
new file mode 100644
index 000000000..9911240ce
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Decomposition/EventsSummaryPage.xaml.cs
@@ -0,0 +1,52 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using Microsoft.Extensions.Logging;
+using RevitLookup.Abstractions.Services.Appearance;
+using RevitLookup.Abstractions.Services.Presentation;
+using RevitLookup.Abstractions.Services.Settings;
+using RevitLookup.Abstractions.ViewModels.Decomposition;
+
+namespace RevitLookup.UI.Framework.Views.Decomposition;
+
+public sealed partial class EventsSummaryPage
+{
+ public EventsSummaryPage(
+ IServiceProvider serviceProvider,
+ IEventsSummaryViewModel viewModel,
+ ISettingsService settingsService,
+ IWindowIntercomService intercomService,
+ INotificationService notificationService,
+ IThemeWatcherService themeWatcherService,
+ ILoggerFactory loggerFactory)
+ : base(serviceProvider, settingsService, intercomService, notificationService, loggerFactory)
+ {
+ themeWatcherService.Watch(this);
+
+ DataContext = this;
+ ViewModel = viewModel;
+ InitializeComponent();
+
+ SearchBoxControl = SummarySearchBox;
+ TreeViewControl = SummaryTreeView;
+ DataGridControl = SummaryDataGrid;
+ InitializeControls();
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Decomposition/SummaryViewBase.Compability.cs b/source/RevitLookup.UI.Framework/Views/Decomposition/SummaryViewBase.Compability.cs
new file mode 100644
index 000000000..3f40a0de1
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Decomposition/SummaryViewBase.Compability.cs
@@ -0,0 +1,82 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using System.Reflection;
+using System.Windows;
+using RevitLookup.UI.Framework.Utils;
+using Wpf.Ui.Controls;
+using DataGrid = Wpf.Ui.Controls.DataGrid;
+
+namespace RevitLookup.UI.Framework.Views.Decomposition;
+
+public partial class SummaryViewBase
+{
+ private static readonly FieldInfo InternalGridScrollHostField =
+ typeof(System.Windows.Controls.DataGrid).GetField("_internalScrollHost",
+ BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.DeclaredOnly)!;
+
+ private static readonly PropertyInfo InternalGridColumnsProperty =
+ typeof(System.Windows.Controls.DataGrid).GetProperty("InternalColumns",
+ BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.DeclaredOnly)!;
+
+ private static readonly MethodInfo InternalGridInvalidateColumnWidthsComputationMethod =
+ InternalGridColumnsProperty.PropertyType.GetMethod("InvalidateColumnWidthsComputation",
+ BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)!;
+
+ private static readonly MethodInfo InternalGridOnViewportSizeChangedMethod =
+ typeof(System.Windows.Controls.DataGrid).GetMethod("OnViewportSizeChanged",
+ BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)!;
+
+ ///
+ /// By default, WPF calculates the column width after adding items to the ItemSource. This fix calculates it on loading
+ ///
+ ///
+ /// https://github.com/dotnet/wpf/blob/main/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/DataGrid.cs#L98
+ ///
+ private static void FixInitialGridColumnSize(object sender, RoutedEventArgs args)
+ {
+ var dataGrid = (DataGrid) sender;
+ var passiveScrollViewer = dataGrid.FindVisualChild();
+ if (passiveScrollViewer is null)
+ {
+ dataGrid.ApplyTemplate();
+ passiveScrollViewer = dataGrid.FindVisualChild()!;
+ }
+
+ var gridColumns = InternalGridColumnsProperty.GetValue(dataGrid);
+ InternalGridScrollHostField.SetValue(dataGrid, passiveScrollViewer);
+ InternalGridInvalidateColumnWidthsComputationMethod.Invoke(gridColumns, null);
+
+ passiveScrollViewer.SizeChanged += FixCanContentScrollResizing;
+ }
+
+ ///
+ /// By default, WPF doesn't recalculate column widths if ScrollViewer.CanContentScroll is enabled
+ ///
+ ///
+ /// https://github.com/dotnet/wpf/blob/main/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/DataGrid.cs#L1961-L1968
+ ///
+ private static void FixCanContentScrollResizing(object sender, SizeChangedEventArgs e)
+ {
+ var scrollViewer = (PassiveScrollViewer) sender;
+ var dataGrid = scrollViewer.FindVisualParent(); //find parent to avoid closure allocations
+ InternalGridOnViewportSizeChangedMethod.Invoke(dataGrid, [e.PreviousSize, e.NewSize]);
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Decomposition/SummaryViewBase.ContextMenu.cs b/source/RevitLookup.UI.Framework/Views/Decomposition/SummaryViewBase.ContextMenu.cs
new file mode 100644
index 000000000..3f5f33c7a
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Decomposition/SummaryViewBase.ContextMenu.cs
@@ -0,0 +1,216 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using Microsoft.Extensions.Logging;
+using RevitLookup.Abstractions.Configuration;
+using RevitLookup.Abstractions.ObservableModels.Decomposition;
+using RevitLookup.UI.Framework.Extensions;
+using RevitLookup.UI.Framework.Utils;
+using Wpf.Ui;
+
+namespace RevitLookup.UI.Framework.Views.Decomposition;
+
+public partial class SummaryViewBase
+{
+ ///
+ /// Tree view context menu
+ ///
+ private void CreateTreeContextMenu(ObservableDecomposedObject decomposedObject, FrameworkElement row)
+ {
+ var contextMenu = new ContextMenu
+ {
+ PlacementTarget = row,
+ Resources = UiApplication.Current.Resources
+ };
+
+ row.ContextMenu = contextMenu;
+
+ contextMenu.AddMenuItem("CopyMenuItem")
+ .SetCommand(decomposedObject, parameter => Clipboard.SetDataObject(parameter.Name))
+ .SetShortcut(ModifierKeys.Control, Key.C);
+ contextMenu.AddMenuItem("HelpMenuItem")
+ .SetCommand(decomposedObject, parameter => HelpUtils.ShowHelp(parameter.TypeFullName))
+ .SetShortcut(Key.F1);
+
+ if (decomposedObject.Descriptor is not IContextMenuConnector connector) return;
+
+ try
+ {
+ connector.RegisterMenu(contextMenu, _serviceProvider);
+ }
+ catch (Exception exception)
+ {
+ _logger.LogError(exception, "Failed to register the context menu");
+ _notificationService.ShowError("Failed to register the context menu", exception);
+ }
+ }
+
+ ///
+ /// Data grid row context menu
+ ///
+ private void CreateGridRowContextMenu(ObservableDecomposedMember member, FrameworkElement row)
+ {
+ var contextMenu = new ContextMenu
+ {
+ PlacementTarget = row,
+ Resources = UiApplication.Current.Resources,
+ };
+
+ row.ContextMenu = contextMenu;
+
+ contextMenu.AddMenuItem("CopyMenuItem")
+ .SetCommand(member, parameter => Clipboard.SetDataObject($"{parameter.Name}: {parameter.Value.Name}"))
+ .SetShortcut(ModifierKeys.Control, Key.C)
+ .SetAvailability(member.Value.Name != string.Empty);
+
+ contextMenu.AddMenuItem("CopyMenuItem")
+ .SetHeader("Copy value")
+ .SetCommand(member, parameter => Clipboard.SetDataObject(parameter.Value.Name))
+ .SetShortcut(ModifierKeys.Control | ModifierKeys.Shift, Key.C)
+ .SetAvailability(member.Value.Name != string.Empty);
+
+ contextMenu.AddMenuItem("HelpMenuItem")
+ .SetCommand(member, parameter => HelpUtils.ShowHelp(parameter.DeclaringTypeFullName, parameter.Name))
+ .SetShortcut(Key.F1);
+
+ if (member.Value.Descriptor is not IContextMenuConnector connector) return;
+
+ try
+ {
+ connector.RegisterMenu(contextMenu, _serviceProvider);
+ }
+ catch (Exception exception)
+ {
+ _logger.LogError(exception, "Failed to register the context menu");
+ _notificationService.ShowError("Failed to register the context menu", exception);
+ }
+ }
+
+ ///
+ /// Data grid context menu
+ ///
+ private void CreateGridContextMenu(DataGrid dataGrid)
+ {
+ var contextMenu = new ContextMenu
+ {
+ PlacementTarget = dataGrid,
+ Resources = UiApplication.Current.Resources
+ };
+
+ dataGrid.ContextMenu = contextMenu;
+
+ contextMenu.AddMenuItem("RefreshMenuItem")
+ .SetCommand(ViewModel, async parameter => await parameter.RefreshMembersAsync())
+ .SetGestureText(Key.F5);
+
+ contextMenu.AddSeparator();
+ contextMenu.AddLabel("Columns");
+
+ contextMenu.AddMenuItem()
+ .SetHeader("Time")
+ .SetStaysOpenOnClick(true)
+ .SetChecked(dataGrid.Columns[2].Visibility == Visibility.Visible)
+ .SetCommand(dataGrid.Columns[2], parameter =>
+ {
+ _settingsService.GeneralSettings.ShowTimeColumn = parameter.Visibility != Visibility.Visible;
+ parameter.Visibility = _settingsService.GeneralSettings.ShowTimeColumn ? Visibility.Visible : Visibility.Collapsed;
+ });
+
+ contextMenu.AddMenuItem()
+ .SetHeader("Memory")
+ .SetStaysOpenOnClick(true)
+ .SetChecked(dataGrid.Columns[3].Visibility == Visibility.Visible)
+ .SetCommand(dataGrid.Columns[3], parameter =>
+ {
+ _settingsService.GeneralSettings.ShowMemoryColumn = parameter.Visibility != Visibility.Visible;
+ parameter.Visibility = _settingsService.GeneralSettings.ShowMemoryColumn ? Visibility.Visible : Visibility.Collapsed;
+ });
+
+ contextMenu.AddSeparator();
+ contextMenu.AddLabel("Show");
+
+ contextMenu.AddMenuItem()
+ .SetHeader("Events")
+ .SetStaysOpenOnClick(true)
+ .SetChecked(_settingsService.GeneralSettings.IncludeEvents)
+ .SetCommand(_settingsService.GeneralSettings, async parameter =>
+ {
+ parameter.IncludeEvents = !parameter.IncludeEvents;
+ await ViewModel.RefreshMembersAsync();
+ });
+ contextMenu.AddMenuItem()
+ .SetHeader("Extensions")
+ .SetStaysOpenOnClick(true)
+ .SetChecked(_settingsService.GeneralSettings.IncludeExtensions)
+ .SetCommand(_settingsService.GeneralSettings, async parameter =>
+ {
+ parameter.IncludeExtensions = !parameter.IncludeExtensions;
+ await ViewModel.RefreshMembersAsync();
+ });
+ contextMenu.AddMenuItem()
+ .SetHeader("Fields")
+ .SetStaysOpenOnClick(true)
+ .SetChecked(_settingsService.GeneralSettings.IncludeFields)
+ .SetCommand(_settingsService.GeneralSettings, async parameter =>
+ {
+ parameter.IncludeFields = !parameter.IncludeFields;
+ await ViewModel.RefreshMembersAsync();
+ });
+ contextMenu.AddMenuItem()
+ .SetHeader("Non-public")
+ .SetStaysOpenOnClick(true)
+ .SetChecked(_settingsService.GeneralSettings.IncludePrivate)
+ .SetCommand(_settingsService.GeneralSettings, async parameter =>
+ {
+ parameter.IncludePrivate = !parameter.IncludePrivate;
+ await ViewModel.RefreshMembersAsync();
+ });
+ contextMenu.AddMenuItem()
+ .SetHeader("Root")
+ .SetStaysOpenOnClick(true)
+ .SetChecked(_settingsService.GeneralSettings.IncludeRootHierarchy)
+ .SetCommand(_settingsService.GeneralSettings, async parameter =>
+ {
+ parameter.IncludeRootHierarchy = !parameter.IncludeRootHierarchy;
+ await ViewModel.RefreshMembersAsync();
+ });
+ contextMenu.AddMenuItem()
+ .SetHeader("Static")
+ .SetStaysOpenOnClick(true)
+ .SetChecked(_settingsService.GeneralSettings.IncludeStatic)
+ .SetCommand(_settingsService.GeneralSettings, async parameter =>
+ {
+ parameter.IncludeStatic = !parameter.IncludeStatic;
+ await ViewModel.RefreshMembersAsync();
+ });
+ contextMenu.AddMenuItem()
+ .SetHeader("Unsupported")
+ .SetStaysOpenOnClick(true)
+ .SetChecked(_settingsService.GeneralSettings.IncludeUnsupported)
+ .SetCommand(_settingsService.GeneralSettings, async parameter =>
+ {
+ parameter.IncludeUnsupported = !parameter.IncludeUnsupported;
+ await ViewModel.RefreshMembersAsync();
+ });
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Decomposition/SummaryViewBase.Gestures.cs b/source/RevitLookup.UI.Framework/Views/Decomposition/SummaryViewBase.Gestures.cs
new file mode 100644
index 000000000..00dae0eda
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Decomposition/SummaryViewBase.Gestures.cs
@@ -0,0 +1,82 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using System.Windows.Input;
+using RevitLookup.UI.Framework.Views.Windows;
+using Wpf.Ui.Abstractions.Controls;
+
+namespace RevitLookup.UI.Framework.Views.Decomposition;
+
+public partial class SummaryViewBase : INavigationAware
+{
+ ///
+ /// Callback when navigating to the current page
+ ///
+ public Task OnNavigatedToAsync()
+ {
+ var host = _intercomService.GetHost();
+ host.PreviewKeyDown += OnPageKeyPressed;
+ return Task.CompletedTask;
+ }
+
+ ///
+ /// Callback when navigating from this page
+ ///
+ public Task OnNavigatedFromAsync()
+ {
+ var host = _intercomService.GetHost();
+ host.PreviewKeyDown -= OnPageKeyPressed;
+ return Task.CompletedTask;
+ }
+
+ ///
+ /// Page shortcuts
+ ///
+ private void OnPageKeyPressed(object sender, KeyEventArgs args)
+ {
+ AddRefreshShortcut(args);
+ if (args.Handled) return;
+
+ AddFocusSearchShortcut(sender, args);
+ }
+
+ private void AddRefreshShortcut(KeyEventArgs args)
+ {
+ if (args.Key != Key.F5) return;
+
+ ViewModel.RefreshMembersAsync();
+ args.Handled = true;
+ }
+
+ private void AddFocusSearchShortcut(object sender, KeyEventArgs args)
+ {
+ if (SearchBoxControl.IsKeyboardFocused) return;
+ if (args.KeyboardDevice.Modifiers != ModifierKeys.None) return;
+
+ var rootWindow = (RevitLookupView) sender;
+ if (rootWindow.RootContentDialog.Content is not null) return;
+
+ if (args.Key is >= Key.D0 and <= Key.Z or >= Key.NumPad0 and <= Key.NumPad9)
+ {
+ SearchBoxControl.Focus();
+ args.Handled = true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Decomposition/SummaryViewBase.Navigation.cs b/source/RevitLookup.UI.Framework/Views/Decomposition/SummaryViewBase.Navigation.cs
new file mode 100644
index 000000000..fc0f1b2d7
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Decomposition/SummaryViewBase.Navigation.cs
@@ -0,0 +1,134 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using LookupEngine.Abstractions.Configuration;
+using RevitLookup.Abstractions.ObservableModels.Decomposition;
+using RevitLookup.UI.Framework.Utils;
+
+namespace RevitLookup.UI.Framework.Views.Decomposition;
+
+public partial class SummaryViewBase
+{
+ ///
+ /// Handle tree view select event
+ ///
+ ///
+ /// Collect data for selected item
+ ///
+ private void OnTreeItemSelected(object sender, RoutedPropertyChangedEventArgs args)
+ {
+ switch (args.NewValue)
+ {
+ case ObservableDecomposedObject decomposedObject:
+ ViewModel.SelectedDecomposedObject = decomposedObject;
+ break;
+ case ObservableDecomposedObjectsGroup:
+ ViewModel.SelectedDecomposedObject = null;
+ break;
+ default:
+ return;
+ }
+ }
+
+ ///
+ /// Handle tree view click event
+ ///
+ ///
+ /// Navigate on Ctrl pressed
+ ///
+ private void OnTreeItemClicked(object sender, RoutedEventArgs args)
+ {
+ if ((Keyboard.Modifiers & ModifierKeys.Control) == 0) return;
+ args.Handled = true;
+
+ var element = (FrameworkElement) args.OriginalSource;
+ switch (element.DataContext)
+ {
+ case ObservableDecomposedObject item:
+ ViewModel.Navigate(item);
+ break;
+ case ObservableDecomposedObjectsGroup group:
+ ViewModel.Navigate(group.GroupItems);
+ break;
+ }
+ }
+
+ ///
+ /// Handle data grid click event
+ ///
+ ///
+ /// Navigate on row clicked
+ ///
+ private void OnGridRowClicked(object sender, RoutedEventArgs args)
+ {
+ var row = (DataGridRow) sender;
+ if (row.DataContext is not ObservableDecomposedMember context) return;
+
+ if ((Keyboard.Modifiers & ModifierKeys.Control) == 0)
+ {
+ if (context.Value.Descriptor is not IDescriptorCollector) return;
+ if (context.Value.Descriptor is IDescriptorEnumerator {IsEmpty: true}) return;
+ }
+
+ ViewModel.Navigate(context.Value);
+ }
+
+ ///
+ /// Handle cursor interaction
+ ///
+ private static void OnPresenterCursorInteracted(object sender, MouseEventArgs args)
+ {
+ var presenter = (FrameworkElement) sender;
+ if ((Keyboard.Modifiers & ModifierKeys.Control) == 0)
+ {
+ presenter.Cursor = null;
+ return;
+ }
+
+ FrameworkElement? item = sender switch
+ {
+ DataGrid => ((DependencyObject) args.OriginalSource).FindVisualParent(),
+ TreeView => ((DependencyObject) args.OriginalSource).FindVisualParent(),
+ _ => throw new NotSupportedException()
+ };
+
+ if (item is null)
+ {
+ presenter.Cursor = null;
+ return;
+ }
+
+ presenter.Cursor = Cursors.Hand;
+ presenter.PreviewKeyUp += OnPresenterCursorRestored;
+ }
+
+ ///
+ /// Restore cursor
+ ///
+ private static void OnPresenterCursorRestored(object sender, KeyEventArgs e)
+ {
+ var presenter = (FrameworkElement) sender;
+ presenter.PreviewKeyUp -= OnPresenterCursorRestored;
+ presenter.Cursor = null;
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Decomposition/SummaryViewBase.ToolTips.cs b/source/RevitLookup.UI.Framework/Views/Decomposition/SummaryViewBase.ToolTips.cs
new file mode 100644
index 000000000..20fbf93c9
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Decomposition/SummaryViewBase.ToolTips.cs
@@ -0,0 +1,114 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using System.Text;
+using System.Windows;
+using LookupEngine.Abstractions.Enums;
+using RevitLookup.Abstractions.ObservableModels.Decomposition;
+
+namespace RevitLookup.UI.Framework.Views.Decomposition;
+
+public partial class SummaryViewBase
+{
+ ///
+ /// Create tree view tooltips
+ ///
+ private static void CreateTreeTooltip(ObservableDecomposedObject decomposedObject, FrameworkElement row)
+ {
+ var builder = new StringBuilder()
+ .Append("Name: ")
+ .AppendLine(decomposedObject.Name)
+ .Append("Type: ")
+ .AppendLine(decomposedObject.TypeName)
+ .Append("Full type: ")
+ .Append(decomposedObject.TypeFullName);
+
+ if (decomposedObject.Description is not null)
+ {
+ builder.AppendLine()
+ .Append("Description: ")
+ .Append(decomposedObject.Description);
+ }
+
+ row.ToolTip = builder.ToString();
+ }
+
+ ///
+ /// Create tree view tooltips
+ ///
+ private static void CreateTreeTooltip(ObservableDecomposedObjectsGroup decomposedGroup, FrameworkElement row)
+ {
+ row.ToolTip = new StringBuilder()
+ .Append("Type: ")
+ .AppendLine(decomposedGroup.GroupName)
+ .Append("Items: ")
+ .Append(decomposedGroup.GroupItems.Count)
+ .ToString();
+ }
+
+ ///
+ /// Create data grid tooltips
+ ///
+ private static void CreateGridRowTooltip(ObservableDecomposedMember member, FrameworkElement row)
+ {
+ var builder = new StringBuilder();
+
+ if ((member.MemberAttributes & MemberAttributes.Private) != 0) builder.Append("Private ");
+ if ((member.MemberAttributes & MemberAttributes.Static) != 0) builder.Append("Static ");
+ if ((member.MemberAttributes & MemberAttributes.Property) != 0) builder.Append("Property: ");
+ if ((member.MemberAttributes & MemberAttributes.Extension) != 0) builder.Append("Extension: ");
+ if ((member.MemberAttributes & MemberAttributes.Method) != 0) builder.Append("Method: ");
+ if ((member.MemberAttributes & MemberAttributes.Event) != 0) builder.Append("Event: ");
+ if ((member.MemberAttributes & MemberAttributes.Field) != 0) builder.Append("Field: ");
+
+ builder.AppendLine(member.Name)
+ .Append("Type: ")
+ .AppendLine(member.Value.TypeName)
+ .Append("Full type: ")
+ .AppendLine(member.Value.TypeFullName)
+ .Append("Value: ")
+ .Append(member.Value.Name);
+
+ if (member.Value.Description is not null)
+ {
+ builder.AppendLine()
+ .Append("Description: ")
+ .Append(member.Value.Description);
+ }
+
+ if (member.ComputationTime > 0)
+ {
+ builder.AppendLine()
+ .Append("Time: ")
+ .Append(member.ComputationTime)
+ .Append(" ms");
+ }
+
+ if (member.AllocatedBytes > 0)
+ {
+ builder.AppendLine()
+ .Append("Allocated: ")
+ .Append(member.AllocatedBytes)
+ .Append(" bytes");
+ }
+
+ row.ToolTip = builder.ToString();
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Decomposition/SummaryViewBase.xaml.cs b/source/RevitLookup.UI.Framework/Views/Decomposition/SummaryViewBase.xaml.cs
new file mode 100644
index 000000000..5af055132
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Decomposition/SummaryViewBase.xaml.cs
@@ -0,0 +1,275 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using System.Collections;
+using System.ComponentModel;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Data;
+using Microsoft.Extensions.Logging;
+using RevitLookup.Abstractions.ObservableModels.Decomposition;
+using RevitLookup.Abstractions.Services.Presentation;
+using RevitLookup.Abstractions.Services.Settings;
+using RevitLookup.Abstractions.ViewModels.Decomposition;
+using RevitLookup.UI.Framework.Utils;
+using Wpf.Ui.Abstractions.Controls;
+using Wpf.Ui.Controls;
+using DataGrid = Wpf.Ui.Controls.DataGrid;
+using TreeView = Wpf.Ui.Controls.TreeView;
+using TreeViewItem = System.Windows.Controls.TreeViewItem;
+using Visibility = System.Windows.Visibility;
+
+namespace RevitLookup.UI.Framework.Views.Decomposition;
+
+public partial class SummaryViewBase : Page, INavigableView
+{
+ private readonly IServiceProvider _serviceProvider;
+ private readonly ISettingsService _settingsService;
+ private readonly IWindowIntercomService _intercomService;
+ private readonly INotificationService _notificationService;
+ private readonly ILogger _logger;
+
+ protected SummaryViewBase(
+ IServiceProvider serviceProvider,
+ ISettingsService settingsService,
+ IWindowIntercomService intercomService,
+ INotificationService notificationService,
+ ILoggerFactory loggerFactory)
+ {
+ _serviceProvider = serviceProvider;
+ _settingsService = settingsService;
+ _intercomService = intercomService;
+ _notificationService = notificationService;
+ _logger = loggerFactory.CreateLogger();
+ }
+
+ public required UIElement SearchBoxControl { get; init; }
+ public required TreeView TreeViewControl { get; init; }
+ public required DataGrid DataGridControl { get; init; }
+ public required ISummaryViewModel ViewModel { get; init; }
+
+ protected void InitializeControls()
+ {
+ InitializeTreeView(TreeViewControl);
+ InitializeDataGrid(DataGridControl);
+ }
+
+ ///
+ /// Tree view initialization
+ ///
+ private void InitializeTreeView(TreeView control)
+ {
+ control.SelectedItemChanged += OnTreeItemSelected;
+ control.ItemsSourceChanged += OnTreeSourceChanged;
+ control.MouseMove += OnPresenterCursorInteracted;
+ control.ItemContainerGenerator.StatusChanged += OnTreeViewItemGenerated;
+
+ if (control.ItemsSource is not null) OnTreeSourceChanged(control, control.ItemsSource);
+ }
+
+ ///
+ /// Tree view source changed handled. Setup action after the setting source
+ ///
+ private static void OnTreeSourceChanged(object? sender, IEnumerable enumerable)
+ {
+ var treeView = (TreeView) sender!;
+
+ if (treeView.IsLoaded)
+ {
+ ExpandFirstTreeGroup(treeView);
+ return;
+ }
+
+ treeView.Loaded += OnLoaded;
+ return;
+
+ void OnLoaded(object nestedSender, RoutedEventArgs args)
+ {
+ var self = (TreeView) nestedSender;
+ self.Loaded -= OnLoaded;
+ ExpandFirstTreeGroup(treeView);
+ }
+ }
+
+ ///
+ /// Expand the first tree view group after setting source
+ ///
+ ///
+ private static async void ExpandFirstTreeGroup(TreeView treeView)
+ {
+ try
+ {
+ // Await Frame transition. GetMembers freezes the thread and breaks the animation
+ var transitionDuration = (int) NavigationView.TransitionDurationProperty.DefaultMetadata.DefaultValue;
+ await Task.Delay(transitionDuration);
+
+ //3 is optimal groups count for expanding
+ if (treeView.Items.Count > 3) return;
+
+ var rootItem = (TreeViewItem?) treeView.GetItemAtIndex(0);
+ if (rootItem is null) return;
+
+ var nestedItem = (TreeViewItem?) rootItem.GetItemAtIndex(0);
+ if (nestedItem is null) return;
+
+ nestedItem.IsSelected = true;
+ }
+ catch
+ {
+ // ignored
+ }
+ }
+
+ ///
+ /// Handle tree view item loaded
+ ///
+ ///
+ /// TreeView item customization after loading
+ ///
+ private void OnTreeViewItemGenerated(object? sender, EventArgs _)
+ {
+ var generator = (ItemContainerGenerator) sender!;
+ if (generator.Status == GeneratorStatus.ContainersGenerated)
+ {
+ foreach (var item in generator.Items)
+ {
+ var treeItem = (ItemsControl) generator.ContainerFromItem(item);
+ if (treeItem is null) continue;
+
+ treeItem.MouseEnter -= OnTreeItemCaptured;
+ treeItem.PreviewMouseLeftButtonUp -= OnTreeItemClicked;
+
+ treeItem.MouseEnter += OnTreeItemCaptured;
+ treeItem.PreviewMouseLeftButtonUp += OnTreeItemClicked;
+
+ if (treeItem.Items.Count > 0)
+ {
+ treeItem.ItemContainerGenerator.StatusChanged -= OnTreeViewItemGenerated;
+ treeItem.ItemContainerGenerator.StatusChanged += OnTreeViewItemGenerated;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Create tree view tooltips, menus
+ ///
+ private void OnTreeItemCaptured(object? sender, RoutedEventArgs args)
+ {
+ var element = (FrameworkElement) sender!;
+ switch (element.DataContext)
+ {
+ case ObservableDecomposedObjectsGroup decomposedGroup:
+ CreateTreeTooltip(decomposedGroup, element);
+ break;
+ case ObservableDecomposedObject decomposedObject:
+ CreateTreeTooltip(decomposedObject, element);
+ CreateTreeContextMenu(decomposedObject, element);
+ break;
+ }
+ }
+
+ ///
+ /// Handle data grid reference changed event
+ ///
+ ///
+ /// Data grid initialization, validation
+ ///
+ private void InitializeDataGrid(DataGrid dataGrid)
+ {
+ ApplyGrouping(dataGrid);
+ ValidateTimeColumn(dataGrid);
+ ValidateAllocatedColumn(dataGrid);
+ CreateGridContextMenu(dataGrid);
+ dataGrid.LoadingRow += OnGridRowLoading;
+ dataGrid.MouseMove += OnPresenterCursorInteracted;
+ dataGrid.ItemsSourceChanged += ApplySorting;
+ dataGrid.Loaded += FixInitialGridColumnSize;
+ }
+
+ ///
+ /// Set DataGrid grouping rules
+ ///
+ private void ApplyGrouping(DataGrid dataGrid)
+ {
+ dataGrid.Items.GroupDescriptions!.Clear();
+ dataGrid.Items.GroupDescriptions.Add(new PropertyGroupDescription(nameof(ObservableDecomposedMember.DeclaringTypeName)));
+ }
+
+ ///
+ /// Set DataGrid sorting rules
+ ///
+ private static void ApplySorting(object? sender, EventArgs eventArgs)
+ {
+ var dataGrid = (DataGrid) sender!;
+
+ dataGrid.Items.SortDescriptions.Add(new SortDescription(nameof(ObservableDecomposedMember.Depth), ListSortDirection.Descending));
+ dataGrid.Items.SortDescriptions.Add(new SortDescription(nameof(ObservableDecomposedMember.MemberAttributes), ListSortDirection.Ascending));
+ dataGrid.Items.SortDescriptions.Add(new SortDescription(nameof(ObservableDecomposedMember.Name), ListSortDirection.Ascending));
+ }
+
+ //
+ // Handle data grid row loading event
+ //
+ //
+ // Select row style
+ //
+ private void OnGridRowLoading(object? sender, DataGridRowEventArgs args)
+ {
+ var row = args.Row;
+
+ row.MouseEnter -= OnGridRowCaptured;
+ row.PreviewMouseLeftButtonUp -= OnGridRowClicked;
+
+ row.MouseEnter += OnGridRowCaptured;
+ row.PreviewMouseLeftButtonUp += OnGridRowClicked;
+ }
+
+ ///
+ /// Handle data grid row loaded event
+ ///
+ ///
+ /// Create tooltips, context menu
+ ///
+ private void OnGridRowCaptured(object sender, RoutedEventArgs args)
+ {
+ var element = (FrameworkElement) sender;
+ var member = (ObservableDecomposedMember) element.DataContext;
+ CreateGridRowTooltip(member, element);
+ CreateGridRowContextMenu(member, element);
+ }
+
+ ///
+ /// Show/hide time column
+ ///
+ private void ValidateTimeColumn(DataGrid control)
+ {
+ control.Columns[2].Visibility = _settingsService.GeneralSettings.ShowTimeColumn ? Visibility.Visible : Visibility.Collapsed;
+ }
+
+ ///
+ /// Show/hide allocated column
+ ///
+ private void ValidateAllocatedColumn(DataGrid control)
+ {
+ control.Columns[3].Visibility = _settingsService.GeneralSettings.ShowMemoryColumn ? Visibility.Visible : Visibility.Collapsed;
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/EditDialogs/EditIniEntryDialog.xaml b/source/RevitLookup.UI.Framework/Views/EditDialogs/EditIniEntryDialog.xaml
new file mode 100644
index 000000000..9993451de
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/EditDialogs/EditIniEntryDialog.xaml
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/EditDialogs/EditIniEntryDialog.xaml.cs b/source/RevitLookup.UI.Framework/Views/EditDialogs/EditIniEntryDialog.xaml.cs
new file mode 100644
index 000000000..d8ee7b136
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/EditDialogs/EditIniEntryDialog.xaml.cs
@@ -0,0 +1,89 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using RevitLookup.Abstractions.ObservableModels.Entries;
+using RevitLookup.Abstractions.Services.Appearance;
+using Wpf.Ui;
+using Wpf.Ui.Controls;
+
+namespace RevitLookup.UI.Framework.Views.EditDialogs;
+
+public sealed partial class EditSettingsEntryDialog
+{
+ private ObservableIniEntry? _entry;
+
+ public EditSettingsEntryDialog(
+ IContentDialogService dialogService,
+ IThemeWatcherService themeWatcherService)
+ : base(dialogService.GetDialogHost())
+ {
+ InitializeComponent();
+ themeWatcherService.Watch(this);
+ }
+
+ public ObservableIniEntry Entry
+ {
+ get => _entry ?? throw new InvalidOperationException("Entry was never set");
+ private set => _entry = value;
+ }
+
+ public async Task ShowCreateDialogAsync(ObservableIniEntry? selectedEntry)
+ {
+ Title = "Create the entry";
+ PrimaryButtonText = "Create";
+
+ Entry = new ObservableIniEntry
+ {
+ IsActive = true
+ };
+
+ if (selectedEntry is not null)
+ {
+ Entry.Category = selectedEntry.Category;
+ }
+
+ DataContext = Entry;
+ return await ShowAsync();
+ }
+
+ public async Task ShowUpdateDialogAsync(ObservableIniEntry entry)
+ {
+ Title = "Update the entry";
+ PrimaryButtonText = "Update";
+
+ Entry = entry;
+ DataContext = entry;
+ return await ShowAsync();
+ }
+
+ protected override void OnButtonClick(ContentDialogButton button)
+ {
+ if (button == ContentDialogButton.Primary)
+ {
+ Entry.Validate();
+ if (Entry.HasErrors)
+ {
+ return;
+ }
+ }
+
+ base.OnButtonClick(button);
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/EditDialogs/EditValueDialog.xaml b/source/RevitLookup.UI.Framework/Views/EditDialogs/EditValueDialog.xaml
new file mode 100644
index 000000000..5d0a04cb4
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/EditDialogs/EditValueDialog.xaml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/EditDialogs/EditValueDialog.xaml.cs b/source/RevitLookup.UI.Framework/Views/EditDialogs/EditValueDialog.xaml.cs
new file mode 100644
index 000000000..88c0bff5d
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/EditDialogs/EditValueDialog.xaml.cs
@@ -0,0 +1,56 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using RevitLookup.Abstractions.Services.Appearance;
+using Wpf.Ui;
+using Wpf.Ui.Controls;
+
+namespace RevitLookup.UI.Framework.Views.EditDialogs;
+
+public sealed partial class EditValueDialog
+{
+ public EditValueDialog(IContentDialogService dialogService, IThemeWatcherService themeWatcherService) : base(dialogService.GetDialogHost())
+ {
+ InitializeComponent();
+ themeWatcherService.Watch(this);
+ }
+
+ public async Task ShowAsync(string name, string value)
+ {
+ ValueLabel.Content = name;
+ ValueBox.Text = value;
+ ValueBox.PlaceholderText = value;
+
+ return await ShowAsync();
+ }
+
+ public async Task ShowAsync(string name, string value, string caption)
+ {
+ Title = caption;
+
+ ValueLabel.Content = name;
+ ValueBox.Text = value;
+ ValueBox.PlaceholderText = value;
+
+ return await ShowAsync();
+ }
+
+ public string Value => ValueBox.Text;
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Settings/ResetSettingsDialog.xaml b/source/RevitLookup.UI.Framework/Views/Settings/ResetSettingsDialog.xaml
new file mode 100644
index 000000000..fc9206280
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Settings/ResetSettingsDialog.xaml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Settings/ResetSettingsDialog.xaml.cs b/source/RevitLookup.UI.Framework/Views/Settings/ResetSettingsDialog.xaml.cs
new file mode 100644
index 000000000..5e2d642d6
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Settings/ResetSettingsDialog.xaml.cs
@@ -0,0 +1,36 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using RevitLookup.Abstractions.Services.Appearance;
+using Wpf.Ui;
+
+namespace RevitLookup.UI.Framework.Views.Settings;
+
+public sealed partial class ResetSettingsDialog
+{
+ public ResetSettingsDialog(IContentDialogService dialogService, IThemeWatcherService themeWatcherService) : base(dialogService.GetDialogHost())
+ {
+ InitializeComponent();
+ themeWatcherService.Watch(this);
+ }
+
+ public bool CanResetGeneralSettings => GeneralBox.IsChecked == true;
+ public bool CanResetRenderSettings => RenderBox.IsChecked == true;
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Settings/SettingsPage.xaml b/source/RevitLookup.UI.Framework/Views/Settings/SettingsPage.xaml
new file mode 100644
index 000000000..7fa6db423
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Settings/SettingsPage.xaml
@@ -0,0 +1,176 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Settings/SettingsPage.xaml.cs b/source/RevitLookup.UI.Framework/Views/Settings/SettingsPage.xaml.cs
new file mode 100644
index 000000000..5d33d51d9
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Settings/SettingsPage.xaml.cs
@@ -0,0 +1,39 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using RevitLookup.Abstractions.Services.Appearance;
+using RevitLookup.Abstractions.ViewModels.Settings;
+using Wpf.Ui.Abstractions.Controls;
+
+namespace RevitLookup.UI.Framework.Views.Settings;
+
+public sealed partial class SettingsPage : INavigableView
+{
+ public SettingsPage(ISettingsViewModel viewModel, IThemeWatcherService themeWatcherService)
+ {
+ themeWatcherService.Watch(this);
+
+ ViewModel = viewModel;
+ DataContext = this;
+ InitializeComponent();
+ }
+
+ public ISettingsViewModel ViewModel { get; }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Tools/ModulesDialog.xaml b/source/RevitLookup.UI.Framework/Views/Tools/ModulesDialog.xaml
new file mode 100644
index 000000000..f4ab40731
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Tools/ModulesDialog.xaml
@@ -0,0 +1,141 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Tools/ModulesDialog.xaml.cs b/source/RevitLookup.UI.Framework/Views/Tools/ModulesDialog.xaml.cs
new file mode 100644
index 000000000..7535431a8
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Tools/ModulesDialog.xaml.cs
@@ -0,0 +1,43 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using RevitLookup.Abstractions.Services.Appearance;
+using RevitLookup.Abstractions.ViewModels.Tools;
+using Wpf.Ui;
+
+namespace RevitLookup.UI.Framework.Views.Tools;
+
+public sealed partial class ModulesDialog
+{
+ public ModulesDialog(
+ IContentDialogService dialogService,
+ IModulesViewModel viewModel, IThemeWatcherService themeWatcherService)
+ : base(dialogService.GetDialogHost())
+ {
+ DataContext = viewModel;
+ InitializeComponent();
+
+ themeWatcherService.Watch(this);
+
+#if NETFRAMEWORK
+ ContainerColumn.Header = "Domain";
+#endif
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Tools/RevitSettingsPage.xaml b/source/RevitLookup.UI.Framework/Views/Tools/RevitSettingsPage.xaml
new file mode 100644
index 000000000..e20a53e41
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Tools/RevitSettingsPage.xaml
@@ -0,0 +1,304 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Tools/RevitSettingsPage.xaml.cs b/source/RevitLookup.UI.Framework/Views/Tools/RevitSettingsPage.xaml.cs
new file mode 100644
index 000000000..41b85b27a
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Tools/RevitSettingsPage.xaml.cs
@@ -0,0 +1,115 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using System.Windows;
+using System.Windows.Controls.Primitives;
+using System.Windows.Data;
+using System.Windows.Input;
+using RevitLookup.Abstractions.ObservableModels.Entries;
+using RevitLookup.Abstractions.Services.Appearance;
+using RevitLookup.Abstractions.Services.Presentation;
+using RevitLookup.Abstractions.ViewModels.Tools;
+using Wpf.Ui;
+using Wpf.Ui.Controls;
+using Wpf.Ui.Extensions;
+
+namespace RevitLookup.UI.Framework.Views.Tools;
+
+public sealed partial class RevitSettingsPage
+{
+ private readonly INotificationService _notificationService;
+
+ public RevitSettingsPage(
+ IRevitSettingsViewModel viewModel,
+ IContentDialogService dialogService,
+ INavigationService navigationService,
+ IThemeWatcherService themeWatcherService,
+ INotificationService notificationService)
+ {
+ _notificationService = notificationService;
+
+ ViewModel = viewModel;
+ DataContext = this;
+
+ InitializeComponent();
+ ApplyGrouping();
+ themeWatcherService.Watch(this);
+
+ if (viewModel.Entries.Count == 0)
+ {
+ ShowWarningDialog(dialogService, navigationService);
+ }
+ }
+
+ private void ApplyGrouping()
+ {
+ EntriesList.Items.GroupDescriptions!.Clear();
+ EntriesList.Items.GroupDescriptions.Add(new PropertyGroupDescription(nameof(ObservableIniEntry.Category)));
+ }
+
+ public IRevitSettingsViewModel ViewModel { get; }
+
+ private async void ShowWarningDialog(IContentDialogService dialogService, INavigationService navigationService)
+ {
+ try
+ {
+ var options = new SimpleContentDialogCreateOptions
+ {
+ Title = "Proceed with caution",
+ Content = "Changing advanced configuration preferences can impact Revit performance or security",
+ PrimaryButtonText = "Accept the Risk and Continue",
+ CloseButtonText = "Quit"
+ };
+
+ var result = await dialogService.ShowSimpleDialogAsync(options);
+ if (result != ContentDialogResult.Primary)
+ {
+ navigationService.GoBack();
+ }
+ else
+ {
+ await ViewModel.InitializeAsync();
+ }
+ }
+ catch (Exception exception)
+ {
+ _notificationService.ShowError("Initialization error", exception.Message);
+ }
+ }
+
+ private async void OnEntryClicked(object sender, MouseButtonEventArgs args)
+ {
+ try
+ {
+ if (args.OriginalSource is ButtonBase) return;
+
+ await ViewModel.UpdateEntryAsync();
+ }
+ catch (Exception exception)
+ {
+ _notificationService.ShowError("Entry updating error", exception.Message);
+ }
+ }
+
+ private void OnFilterClicked(object sender, RoutedEventArgs args)
+ {
+ FilterFlyout.IsOpen = !FilterFlyout.IsOpen;
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Tools/SearchElementsDialog.xaml b/source/RevitLookup.UI.Framework/Views/Tools/SearchElementsDialog.xaml
new file mode 100644
index 000000000..659099752
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Tools/SearchElementsDialog.xaml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Tools/SearchElementsDialog.xaml.cs b/source/RevitLookup.UI.Framework/Views/Tools/SearchElementsDialog.xaml.cs
new file mode 100644
index 000000000..b7649f707
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Tools/SearchElementsDialog.xaml.cs
@@ -0,0 +1,81 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using Microsoft.Extensions.Logging;
+using RevitLookup.Abstractions.Services.Appearance;
+using RevitLookup.Abstractions.Services.Presentation;
+using RevitLookup.Abstractions.ViewModels.Tools;
+using RevitLookup.UI.Framework.Views.Decomposition;
+using Wpf.Ui;
+using Wpf.Ui.Controls;
+
+namespace RevitLookup.UI.Framework.Views.Tools;
+
+public sealed partial class SearchElementsDialog
+{
+ private readonly ISearchElementsViewModel _viewModel;
+ private readonly INavigationService _navigationService;
+ private readonly INotificationService _notificationService;
+ private readonly ILogger _logger;
+
+ public SearchElementsDialog(
+ IContentDialogService dialogService,
+ ISearchElementsViewModel viewModel,
+ INavigationService navigationService,
+ IThemeWatcherService themeWatcherService,
+ INotificationService notificationService,
+ ILogger logger)
+ : base(dialogService.GetDialogHost())
+ {
+ _viewModel = viewModel;
+ _navigationService = navigationService;
+ _notificationService = notificationService;
+ _logger = logger;
+
+ DataContext = viewModel;
+ InitializeComponent();
+
+ themeWatcherService.Watch(this);
+ }
+
+ protected override async void OnButtonClick(ContentDialogButton button)
+ {
+ try
+ {
+ if (button == ContentDialogButton.Primary)
+ {
+ var success = await _viewModel.SearchElementsAsync();
+ if (!success)
+ {
+ return;
+ }
+
+ _navigationService.Navigate(typeof(DecompositionSummaryPage));
+ }
+
+ base.OnButtonClick(button);
+ }
+ catch (Exception exception)
+ {
+ _logger.LogError(exception, "Error while searching elements");
+ _notificationService.ShowError("Search error", exception.Message);
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Tools/UnitsDialog.xaml b/source/RevitLookup.UI.Framework/Views/Tools/UnitsDialog.xaml
new file mode 100644
index 000000000..7969fe591
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Tools/UnitsDialog.xaml
@@ -0,0 +1,109 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Tools/UnitsDialog.xaml.cs b/source/RevitLookup.UI.Framework/Views/Tools/UnitsDialog.xaml.cs
new file mode 100644
index 000000000..99140d8ad
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Tools/UnitsDialog.xaml.cs
@@ -0,0 +1,129 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using RevitLookup.Abstractions.Models.Tools;
+using RevitLookup.Abstractions.Services.Appearance;
+using RevitLookup.Abstractions.ViewModels.Tools;
+using RevitLookup.UI.Framework.Extensions;
+using RevitLookup.UI.Framework.Views.Decomposition;
+using Wpf.Ui;
+using Visibility = System.Windows.Visibility;
+
+namespace RevitLookup.UI.Framework.Views.Tools;
+
+public sealed partial class UnitsDialog
+{
+ private readonly IUnitsViewModel _viewModel;
+ private readonly INavigationService _navigationService;
+
+ public UnitsDialog(
+ IContentDialogService dialogService,
+ IUnitsViewModel viewModel,
+ INavigationService navigationService,
+ IThemeWatcherService themeWatcherService)
+ : base(dialogService.GetDialogHost())
+ {
+ _viewModel = viewModel;
+ _navigationService = navigationService;
+
+ DataContext = _viewModel;
+ InitializeComponent();
+
+ themeWatcherService.Watch(this);
+ }
+
+ public async Task ShowParametersDialogAsync()
+ {
+ _viewModel.InitializeParameters();
+
+ Title = "BuiltIn Parameters";
+ DialogMaxWidth = 1000;
+
+ await ShowAsync();
+ }
+
+ public async Task ShowCategoriesDialogAsync()
+ {
+ _viewModel.InitializeCategories();
+
+ Title = "BuiltIn Categories";
+ DialogMaxWidth = 600;
+
+ await ShowAsync();
+ }
+
+ public async Task ShowForgeSchemaDialogAsync()
+ {
+ _viewModel.InitializeForgeSchema();
+
+ ClassColumn.Visibility = Visibility.Visible;
+ Title = "Forge Schema";
+ DialogMaxWidth = 1100;
+
+ await ShowAsync();
+ }
+
+ private void OnMouseEnter(object sender, RoutedEventArgs routedEventArgs)
+ {
+ var element = (FrameworkElement) sender;
+ var unitInfo = (UnitInfo) element.DataContext;
+ CreateTreeContextMenu(unitInfo, element);
+ }
+
+ private void CreateTreeContextMenu(UnitInfo info, FrameworkElement row)
+ {
+ var contextMenu = new ContextMenu
+ {
+ Resources = UiApplication.Current.Resources,
+ PlacementTarget = row
+ };
+
+ contextMenu.AddMenuItem("CopyMenuItem")
+ .SetHeader("Copy unit")
+ .SetCommand(info, unitInfo => Clipboard.SetDataObject(unitInfo.Unit))
+ .SetShortcut(ModifierKeys.Control, Key.C);
+
+ contextMenu.AddMenuItem("CopyMenuItem")
+ .SetHeader("Copy label")
+ .SetCommand(info, unitInfo => Clipboard.SetDataObject(unitInfo.Label));
+
+ if (info.Class is not null)
+ {
+ contextMenu.AddMenuItem("CopyMenuItem")
+ .SetHeader("Copy class")
+ .SetCommand(info, unitInfo => Clipboard.SetDataObject(unitInfo.Class!))
+ .SetShortcut(ModifierKeys.Control | ModifierKeys.Shift, Key.C);
+ }
+
+ contextMenu.AddMenuItem("SnoopMenuItem")
+ .SetHeader("Snoop")
+ .SetCommand(info, async unitInfo =>
+ {
+ Hide();
+ await _viewModel.DecomposeAsync(unitInfo);
+ _navigationService.Navigate(typeof(DecompositionSummaryPage));
+ });
+
+ row.ContextMenu = contextMenu;
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Visualization/BoundingBoxVisualizationDialog.xaml b/source/RevitLookup.UI.Framework/Views/Visualization/BoundingBoxVisualizationDialog.xaml
new file mode 100644
index 000000000..53a454e06
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Visualization/BoundingBoxVisualizationDialog.xaml
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Visualization/BoundingBoxVisualizationDialog.xaml.cs b/source/RevitLookup.UI.Framework/Views/Visualization/BoundingBoxVisualizationDialog.xaml.cs
new file mode 100644
index 000000000..194a2e747
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Visualization/BoundingBoxVisualizationDialog.xaml.cs
@@ -0,0 +1,57 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using RevitLookup.Abstractions.Services.Appearance;
+using RevitLookup.Abstractions.ViewModels.Visualization;
+using Wpf.Ui;
+
+namespace RevitLookup.UI.Framework.Views.Visualization;
+
+public sealed partial class BoundingBoxVisualizationDialog
+{
+ private readonly IBoundingBoxVisualizationViewModel _viewModel;
+
+ public BoundingBoxVisualizationDialog(
+ IContentDialogService dialogService,
+ IBoundingBoxVisualizationViewModel viewModel,
+ IThemeWatcherService themeWatcherService)
+ : base(dialogService.GetDialogHost())
+ {
+ _viewModel = viewModel;
+
+ DataContext = _viewModel;
+ InitializeComponent();
+
+ themeWatcherService.Watch(this);
+ }
+
+ public async Task ShowDialogAsync(object boundingBoxXyz)
+ {
+ _viewModel.RegisterServer(boundingBoxXyz);
+ MonitorServerConnection();
+
+ await ShowAsync();
+ }
+
+ private void MonitorServerConnection()
+ {
+ Unloaded += (_, _) => _viewModel.UnregisterServer();
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Visualization/FaceVisualizationDialog.xaml b/source/RevitLookup.UI.Framework/Views/Visualization/FaceVisualizationDialog.xaml
new file mode 100644
index 000000000..839077e8d
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Visualization/FaceVisualizationDialog.xaml
@@ -0,0 +1,119 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Visualization/FaceVisualizationDialog.xaml.cs b/source/RevitLookup.UI.Framework/Views/Visualization/FaceVisualizationDialog.xaml.cs
new file mode 100644
index 000000000..34d9a9115
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Visualization/FaceVisualizationDialog.xaml.cs
@@ -0,0 +1,57 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using RevitLookup.Abstractions.Services.Appearance;
+using RevitLookup.Abstractions.ViewModels.Visualization;
+using Wpf.Ui;
+
+namespace RevitLookup.UI.Framework.Views.Visualization;
+
+public sealed partial class FaceVisualizationDialog
+{
+ private readonly IFaceVisualizationViewModel _viewModel;
+
+ public FaceVisualizationDialog(
+ IContentDialogService dialogService,
+ IFaceVisualizationViewModel viewModel,
+ IThemeWatcherService themeWatcherService)
+ : base(dialogService.GetDialogHost())
+ {
+ _viewModel = viewModel;
+
+ DataContext = _viewModel;
+ InitializeComponent();
+
+ themeWatcherService.Watch(this);
+ }
+
+ public async Task ShowDialogAsync(object face)
+ {
+ _viewModel.RegisterServer(face);
+ MonitorServerConnection();
+
+ await ShowAsync();
+ }
+
+ private void MonitorServerConnection()
+ {
+ Unloaded += (_, _) => _viewModel.UnregisterServer();
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Visualization/MeshVisualizationDialog.xaml b/source/RevitLookup.UI.Framework/Views/Visualization/MeshVisualizationDialog.xaml
new file mode 100644
index 000000000..775cb8bcc
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Visualization/MeshVisualizationDialog.xaml
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Visualization/MeshVisualizationDialog.xaml.cs b/source/RevitLookup.UI.Framework/Views/Visualization/MeshVisualizationDialog.xaml.cs
new file mode 100644
index 000000000..2fa1871fb
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Visualization/MeshVisualizationDialog.xaml.cs
@@ -0,0 +1,57 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using RevitLookup.Abstractions.Services.Appearance;
+using RevitLookup.Abstractions.ViewModels.Visualization;
+using Wpf.Ui;
+
+namespace RevitLookup.UI.Framework.Views.Visualization;
+
+public sealed partial class MeshVisualizationDialog
+{
+ private readonly IMeshVisualizationViewModel _viewModel;
+
+ public MeshVisualizationDialog(
+ IContentDialogService dialogService,
+ IMeshVisualizationViewModel viewModel,
+ IThemeWatcherService themeWatcherService)
+ : base(dialogService.GetDialogHost())
+ {
+ _viewModel = viewModel;
+
+ DataContext = _viewModel;
+ InitializeComponent();
+
+ themeWatcherService.Watch(this);
+ }
+
+ public async Task ShowDialogAsync(object mesh)
+ {
+ _viewModel.RegisterServer(mesh);
+ MonitorServerConnection();
+
+ await ShowAsync();
+ }
+
+ private void MonitorServerConnection()
+ {
+ Unloaded += (_, _) => _viewModel.UnregisterServer();
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Visualization/PolylineVisualizationDialog.xaml b/source/RevitLookup.UI.Framework/Views/Visualization/PolylineVisualizationDialog.xaml
new file mode 100644
index 000000000..0711b6617
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Visualization/PolylineVisualizationDialog.xaml
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Visualization/PolylineVisualizationDialog.xaml.cs b/source/RevitLookup.UI.Framework/Views/Visualization/PolylineVisualizationDialog.xaml.cs
new file mode 100644
index 000000000..86a1dc267
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Visualization/PolylineVisualizationDialog.xaml.cs
@@ -0,0 +1,57 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using RevitLookup.Abstractions.Services.Appearance;
+using RevitLookup.Abstractions.ViewModels.Visualization;
+using Wpf.Ui;
+
+namespace RevitLookup.UI.Framework.Views.Visualization;
+
+public sealed partial class PolylineVisualizationDialog
+{
+ private readonly IPolylineVisualizationViewModel _viewModel;
+
+ public PolylineVisualizationDialog(
+ IContentDialogService dialogService,
+ IPolylineVisualizationViewModel viewModel,
+ IThemeWatcherService themeWatcherService)
+ : base(dialogService.GetDialogHost())
+ {
+ _viewModel = viewModel;
+
+ DataContext = _viewModel;
+ InitializeComponent();
+
+ themeWatcherService.Watch(this);
+ }
+
+ public async Task ShowDialogAsync(object curveOrEdge)
+ {
+ _viewModel.RegisterServer(curveOrEdge);
+ MonitorServerConnection();
+
+ await ShowAsync();
+ }
+
+ private void MonitorServerConnection()
+ {
+ Unloaded += (_, _) => _viewModel.UnregisterServer();
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Visualization/SolidVisualizationDialog.xaml b/source/RevitLookup.UI.Framework/Views/Visualization/SolidVisualizationDialog.xaml
new file mode 100644
index 000000000..6ca9182cc
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Visualization/SolidVisualizationDialog.xaml
@@ -0,0 +1,105 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Visualization/SolidVisualizationDialog.xaml.cs b/source/RevitLookup.UI.Framework/Views/Visualization/SolidVisualizationDialog.xaml.cs
new file mode 100644
index 000000000..90ada7497
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Visualization/SolidVisualizationDialog.xaml.cs
@@ -0,0 +1,57 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using RevitLookup.Abstractions.Services.Appearance;
+using RevitLookup.Abstractions.ViewModels.Visualization;
+using Wpf.Ui;
+
+namespace RevitLookup.UI.Framework.Views.Visualization;
+
+public sealed partial class SolidVisualizationDialog
+{
+ private readonly ISolidVisualizationViewModel _viewModel;
+
+ public SolidVisualizationDialog(
+ IContentDialogService dialogService,
+ ISolidVisualizationViewModel viewModel,
+ IThemeWatcherService themeWatcherService)
+ : base(dialogService.GetDialogHost())
+ {
+ _viewModel = viewModel;
+
+ DataContext = _viewModel;
+ InitializeComponent();
+
+ themeWatcherService.Watch(this);
+ }
+
+ public async Task ShowDialogAsync(object solid)
+ {
+ _viewModel.RegisterServer(solid);
+ MonitorServerConnection();
+
+ await ShowAsync();
+ }
+
+ private void MonitorServerConnection()
+ {
+ Unloaded += (_, _) => _viewModel.UnregisterServer();
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Visualization/XyzVisualizationDialog.xaml b/source/RevitLookup.UI.Framework/Views/Visualization/XyzVisualizationDialog.xaml
new file mode 100644
index 000000000..fb960a02a
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Visualization/XyzVisualizationDialog.xaml
@@ -0,0 +1,125 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Visualization/XyzVisualizationDialog.xaml.cs b/source/RevitLookup.UI.Framework/Views/Visualization/XyzVisualizationDialog.xaml.cs
new file mode 100644
index 000000000..f94775aae
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Visualization/XyzVisualizationDialog.xaml.cs
@@ -0,0 +1,57 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using RevitLookup.Abstractions.Services.Appearance;
+using RevitLookup.Abstractions.ViewModels.Visualization;
+using Wpf.Ui;
+
+namespace RevitLookup.UI.Framework.Views.Visualization;
+
+public sealed partial class XyzVisualizationDialog
+{
+ private readonly IXyzVisualizationViewModel _viewModel;
+
+ public XyzVisualizationDialog(
+ IContentDialogService dialogService,
+ IXyzVisualizationViewModel viewModel,
+ IThemeWatcherService themeWatcherService)
+ : base(dialogService.GetDialogHost())
+ {
+ _viewModel = viewModel;
+
+ DataContext = _viewModel;
+ InitializeComponent();
+
+ themeWatcherService.Watch(this);
+ }
+
+ public async Task ShowDialogAsync(object xyz)
+ {
+ _viewModel.RegisterServer(xyz);
+ MonitorServerConnection();
+
+ await ShowAsync();
+ }
+
+ private void MonitorServerConnection()
+ {
+ Unloaded += (_, _) => _viewModel.UnregisterServer();
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Windows/RevitLookupView.Compability.xaml.cs b/source/RevitLookup.UI.Framework/Views/Windows/RevitLookupView.Compability.xaml.cs
new file mode 100644
index 000000000..03635ca5a
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Windows/RevitLookupView.Compability.xaml.cs
@@ -0,0 +1,55 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using RevitLookup.UI.Framework.Utils;
+using Wpf.Ui.Controls;
+
+namespace RevitLookup.UI.Framework.Views.Windows;
+
+public sealed partial class RevitLookupView
+{
+ private void FixComponentsTheme()
+ {
+ RootNavigation.Loaded += OnNavigationScrollLoaded;
+ }
+
+ private void OnNavigationScrollLoaded(object sender, RoutedEventArgs args)
+ {
+ var contentPresenter = RootNavigation.FindVisualChild()!;
+ contentPresenter.LoadCompleted += ContentPresenterOnContentRendered;
+ }
+
+ private void ContentPresenterOnContentRendered(object? sender, EventArgs e)
+ {
+ var contentPresenter = (NavigationViewContentPresenter) sender!;
+ if (!contentPresenter.IsDynamicScrollViewerEnabled) return;
+
+ if (VisualTreeHelper.GetChildrenCount(contentPresenter) == 0)
+ {
+ contentPresenter.ApplyTemplate();
+ }
+
+ var scrollViewer = (ScrollViewer) VisualTreeHelper.GetChild(contentPresenter, 0);
+ _themeWatcherService.Watch(scrollViewer);
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Windows/RevitLookupView.Gestures.xaml.cs b/source/RevitLookup.UI.Framework/Views/Windows/RevitLookupView.Gestures.xaml.cs
new file mode 100644
index 000000000..d1c7c6f5c
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Windows/RevitLookupView.Gestures.xaml.cs
@@ -0,0 +1,53 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using System.Windows.Input;
+using CommunityToolkit.Mvvm.Input;
+
+namespace RevitLookup.UI.Framework.Views.Windows;
+
+public sealed partial class RevitLookupView
+{
+ private void AddShortcuts()
+ {
+ AddCloseShortcut();
+ AddCloseAllShortcut();
+ }
+
+ private void AddCloseShortcut()
+ {
+ var command = new RelayCommand(Close);
+ InputBindings.Add(new KeyBinding(command, new KeyGesture(Key.Escape)));
+ }
+
+ private void AddCloseAllShortcut()
+ {
+ var command = new RelayCommand(() =>
+ {
+ for (var i = _intercomService.OpenedWindows.Count - 1; i >= 0; i--)
+ {
+ var window = _intercomService.OpenedWindows[i];
+ window.Close();
+ }
+ });
+
+ InputBindings.Add(new KeyBinding(command, new KeyGesture(Key.Escape, ModifierKeys.Shift)));
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Windows/RevitLookupView.xaml b/source/RevitLookup.UI.Framework/Views/Windows/RevitLookupView.xaml
new file mode 100644
index 000000000..cc21a91c4
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Windows/RevitLookupView.xaml
@@ -0,0 +1,93 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Framework/Views/Windows/RevitLookupView.xaml.cs b/source/RevitLookup.UI.Framework/Views/Windows/RevitLookupView.xaml.cs
new file mode 100644
index 000000000..a5b11ef2e
--- /dev/null
+++ b/source/RevitLookup.UI.Framework/Views/Windows/RevitLookupView.xaml.cs
@@ -0,0 +1,114 @@
+// Copyright 2003-2024 by Autodesk, Inc.
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+//
+// Use, duplication, or disclosure by the U.S. Government is subject to
+// restrictions set forth in FAR 52.227-19 (Commercial Computer
+// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
+// (Rights in Technical Data and Computer Software), as applicable.
+
+using System.Windows;
+using System.Windows.Automation.Peers;
+using RevitLookup.Abstractions.Services.Appearance;
+using RevitLookup.Abstractions.Services.Presentation;
+using RevitLookup.Abstractions.Services.Settings;
+using RevitLookup.UI.Framework.Controls.Automation;
+using Wpf.Ui;
+using Wpf.Ui.Appearance;
+
+namespace RevitLookup.UI.Framework.Views.Windows;
+
+public sealed partial class RevitLookupView
+{
+ private readonly IWindowIntercomService _intercomService;
+ private readonly ISoftwareUpdateService _updateService;
+ private readonly ISettingsService _settingsService;
+ private readonly IThemeWatcherService _themeWatcherService;
+
+ public RevitLookupView(
+ INavigationService navigationService,
+ IContentDialogService dialogService,
+ ISnackbarService snackbarService,
+ IWindowIntercomService intercomService,
+ ISoftwareUpdateService updateService,
+ ISettingsService settingsService,
+ IThemeWatcherService themeWatcherService)
+ {
+ _intercomService = intercomService;
+ _updateService = updateService;
+ _settingsService = settingsService;
+ _themeWatcherService = themeWatcherService;
+
+ themeWatcherService.Watch(this);
+ InitializeComponent();
+
+ intercomService.SetSharedHost(this);
+ navigationService.SetNavigationControl(RootNavigation);
+ dialogService.SetDialogHost(RootContentDialog);
+ snackbarService.SetSnackbarPresenter(RootSnackbar);
+
+ ApplyEffects();
+ AddShortcuts();
+ AddBadges();
+ ApplyWindowSize();
+ FixComponentsTheme();
+ }
+
+ private void AddBadges()
+ {
+ if (_updateService.NewVersion is null) return;
+ if (_updateService.LocalFilePath is not null) return;
+
+ UpdatesNotifier.Visibility = Visibility.Visible;
+ }
+
+ private void ApplyEffects()
+ {
+ WindowBackdropType = _settingsService.GeneralSettings.Background;
+ RootNavigation.Transition = _settingsService.GeneralSettings.Transition;
+ WindowBackgroundManager.UpdateBackground(this, _settingsService.GeneralSettings.Theme, WindowBackdropType);
+ }
+
+ private void ApplyWindowSize()
+ {
+ if (!_settingsService.GeneralSettings.UseSizeRestoring) return;
+
+ if (_settingsService.GeneralSettings.WindowWidth >= MinWidth) Width = _settingsService.GeneralSettings.WindowWidth;
+ if (_settingsService.GeneralSettings.WindowHeight >= MinHeight) Height = _settingsService.GeneralSettings.WindowHeight;
+
+ EnableSizeTracking();
+ }
+
+ public void EnableSizeTracking()
+ {
+ SizeChanged += OnSizeChanged;
+ }
+
+ public void DisableSizeTracking()
+ {
+ SizeChanged -= OnSizeChanged;
+ }
+
+ private static void OnSizeChanged(object sender, SizeChangedEventArgs args)
+ {
+ var self = (RevitLookupView) sender;
+ self._settingsService.GeneralSettings.WindowWidth = args.NewSize.Width;
+ self._settingsService.GeneralSettings.WindowHeight = args.NewSize.Height;
+ }
+
+ protected override AutomationPeer OnCreateAutomationPeer()
+ {
+ return new NoAutomationWindowPeer(this);
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Playground/App.xaml b/source/RevitLookup.UI.Playground/App.xaml
new file mode 100644
index 000000000..91134136d
--- /dev/null
+++ b/source/RevitLookup.UI.Playground/App.xaml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Playground/App.xaml.cs b/source/RevitLookup.UI.Playground/App.xaml.cs
new file mode 100644
index 000000000..ffb0f9f5a
--- /dev/null
+++ b/source/RevitLookup.UI.Playground/App.xaml.cs
@@ -0,0 +1,22 @@
+using System.Windows;
+using RevitLookup.Abstractions.Services.Settings;
+using RevitLookup.UI.Playground.Client.Views;
+
+namespace RevitLookup.UI.Playground;
+
+public sealed partial class App
+{
+ private void OnStartup(object sender, StartupEventArgs e)
+ {
+ Initialize();
+
+ var view = Host.CreateScope();
+ view.ShowDialog();
+ }
+
+ private static void Initialize()
+ {
+ var settingsService = Host.GetService();
+ settingsService.LoadSettings();
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Playground/Client/Controls/ControlExample.xaml b/source/RevitLookup.UI.Playground/Client/Controls/ControlExample.xaml
new file mode 100644
index 000000000..c405f4a8e
--- /dev/null
+++ b/source/RevitLookup.UI.Playground/Client/Controls/ControlExample.xaml
@@ -0,0 +1,237 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Playground/Client/Controls/ControlExample.xaml.cs b/source/RevitLookup.UI.Playground/Client/Controls/ControlExample.xaml.cs
new file mode 100644
index 000000000..84ae5f3b2
--- /dev/null
+++ b/source/RevitLookup.UI.Playground/Client/Controls/ControlExample.xaml.cs
@@ -0,0 +1,174 @@
+using System.Diagnostics;
+using System.IO;
+using System.Text;
+using System.Windows;
+using System.Windows.Automation;
+using System.Windows.Automation.Peers;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Markup;
+
+namespace RevitLookup.UI.Playground.Client.Controls;
+
+///
+/// A control that displays an example of a control
+///
+[ContentProperty(nameof(ExampleContent))]
+public sealed class ControlExample : Control
+{
+ static ControlExample()
+ {
+ CommandManager.RegisterClassCommandBinding(typeof(ControlExample), new CommandBinding(ApplicationCommands.Copy, Copy_SourceCode));
+ }
+
+ public static readonly DependencyProperty HeaderTextProperty = DependencyProperty.Register(
+ nameof(HeaderText),
+ typeof(string),
+ typeof(ControlExample),
+ new PropertyMetadata(null)
+ );
+
+ public static readonly DependencyProperty ExampleContentProperty = DependencyProperty.Register(
+ nameof(ExampleContent),
+ typeof(object),
+ typeof(ControlExample),
+ new PropertyMetadata(null)
+ );
+
+ public static readonly DependencyProperty XamlCodeProperty = DependencyProperty.Register(
+ nameof(XamlCode),
+ typeof(string),
+ typeof(ControlExample),
+ new PropertyMetadata(null)
+ );
+
+ public static readonly DependencyProperty XamlCodeSourceProperty = DependencyProperty.Register(
+ nameof(XamlCodeSource),
+ typeof(Uri),
+ typeof(ControlExample),
+ new PropertyMetadata(
+ null,
+ static (o, args) => ((ControlExample) o).OnXamlCodeSourceChanged((Uri) args.NewValue)
+ )
+ );
+
+ public static readonly DependencyProperty CsharpCodeProperty = DependencyProperty.Register(
+ nameof(CsharpCode),
+ typeof(string),
+ typeof(ControlExample),
+ new PropertyMetadata(null)
+ );
+
+ public static readonly DependencyProperty CsharpCodeSourceProperty = DependencyProperty.Register(
+ nameof(CsharpCodeSource),
+ typeof(Uri),
+ typeof(ControlExample),
+ new PropertyMetadata(
+ null,
+ static (o, args) => ((ControlExample) o).OnCsharpCodeSourceChanged((Uri) args.NewValue)
+ )
+ );
+
+ public string? HeaderText
+ {
+ get => (string) GetValue(HeaderTextProperty);
+ set => SetValue(HeaderTextProperty, value);
+ }
+
+ public object? ExampleContent
+ {
+ get => GetValue(ExampleContentProperty);
+ set => SetValue(ExampleContentProperty, value);
+ }
+
+ public string? XamlCode
+ {
+ get => (string) GetValue(XamlCodeProperty);
+ set => SetValue(XamlCodeProperty, value);
+ }
+
+ public Uri? XamlCodeSource
+ {
+ get => (Uri) GetValue(XamlCodeSourceProperty);
+ set => SetValue(XamlCodeSourceProperty, value);
+ }
+
+ public string? CsharpCode
+ {
+ get => (string) GetValue(CsharpCodeProperty);
+ set => SetValue(CsharpCodeProperty, value);
+ }
+
+ public Uri? CsharpCodeSource
+ {
+ get => (Uri) GetValue(CsharpCodeSourceProperty);
+ set => SetValue(CsharpCodeSourceProperty, value);
+ }
+
+ private void OnXamlCodeSourceChanged(Uri uri)
+ {
+ XamlCode = LoadResource(uri);
+ }
+
+ private void OnCsharpCodeSourceChanged(Uri uri)
+ {
+ CsharpCode = LoadResource(uri);
+ }
+
+ private static void Copy_SourceCode(object sender, RoutedEventArgs e)
+ {
+ var controlExample = (ControlExample) sender;
+
+ try
+ {
+ switch (((ExecutedRoutedEventArgs) e).Parameter.ToString())
+ {
+ case "Copy_XamlCode":
+ if (string.IsNullOrEmpty(controlExample.XamlCode)) break;
+
+ Clipboard.SetText(controlExample.XamlCode);
+ var peer = UIElementAutomationPeer.CreatePeerForElement((Button) e.OriginalSource);
+#if NETCOREAPP
+ peer.RaiseNotificationEvent(
+ AutomationNotificationKind.Other,
+ AutomationNotificationProcessing.ImportantMostRecent,
+ "Source Code Copied",
+ "ButtonClickedActivity"
+ );
+#endif
+
+ break;
+ case "Copy_CsharpCode":
+ if (string.IsNullOrEmpty(controlExample.CsharpCode)) break;
+
+ Clipboard.SetText(controlExample.CsharpCode);
+ break;
+ default:
+ throw new InvalidOperationException();
+ }
+ }
+ catch
+ {
+ // ignored
+ }
+ }
+
+ private static string LoadResource(Uri uri)
+ {
+ try
+ {
+ if (Application.GetResourceStream(uri) is not { } steamInfo)
+ {
+ return string.Empty;
+ }
+
+ using StreamReader streamReader = new(steamInfo.Stream, Encoding.UTF8);
+ return streamReader.ReadToEnd();
+ }
+ catch (Exception exception)
+ {
+ Debug.WriteLine(exception);
+ return exception.ToString();
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Playground/Client/Controls/PageViewer.xaml b/source/RevitLookup.UI.Playground/Client/Controls/PageViewer.xaml
new file mode 100644
index 000000000..a23cad965
--- /dev/null
+++ b/source/RevitLookup.UI.Playground/Client/Controls/PageViewer.xaml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Playground/Client/Controls/PageViewer.xaml.cs b/source/RevitLookup.UI.Playground/Client/Controls/PageViewer.xaml.cs
new file mode 100644
index 000000000..8e67a5b7a
--- /dev/null
+++ b/source/RevitLookup.UI.Playground/Client/Controls/PageViewer.xaml.cs
@@ -0,0 +1,67 @@
+using System.Windows;
+using System.Windows.Automation.Peers;
+using System.Windows.Controls;
+using Microsoft.Extensions.DependencyInjection;
+using RevitLookup.Abstractions.Services.Presentation;
+using RevitLookup.UI.Framework.Controls.Automation;
+using Wpf.Ui;
+
+namespace RevitLookup.UI.Playground.Client.Controls;
+
+public sealed partial class PageViewer
+{
+ private readonly IServiceProvider _serviceProvider;
+
+ public PageViewer(
+ IServiceProvider serviceProvider,
+ ISnackbarService snackbarService,
+ IContentDialogService dialogService,
+ IWindowIntercomService intercomService)
+ {
+ _serviceProvider = serviceProvider;
+ InitializeComponent();
+
+ intercomService.SetHost(this);
+ dialogService.SetDialogHost(RootContentDialog);
+ snackbarService.SetSnackbarPresenter(RootSnackbar);
+ }
+
+ public bool? ShowPage() where T : Page
+ {
+ var page = _serviceProvider.GetRequiredService();
+ Viewer.Navigate(page);
+
+ if (WindowStartupLocation == WindowStartupLocation.CenterScreen) Viewer.SizeChanged += OnViewerFrameResized;
+ return ShowDialog();
+ }
+
+ public void RunService(Action handler) where T : class
+ {
+ var service = _serviceProvider.GetRequiredService();
+ handler.Invoke(service);
+ }
+
+ private void OnViewerFrameResized(object sender, SizeChangedEventArgs args)
+ {
+ if (args.PreviousSize.Height == 0 || args.PreviousSize.Width == 0) return;
+
+ var self = (Frame) sender;
+ self.SizeChanged -= OnViewerFrameResized;
+
+ //Move the owner to the screen center after navigation
+ if (SizeToContent is SizeToContent.WidthAndHeight or SizeToContent.Width)
+ {
+ Left -= (ActualWidth - MinWidth) / 2;
+ }
+
+ if (SizeToContent is SizeToContent.WidthAndHeight or SizeToContent.Height)
+ {
+ Top -= (ActualHeight - MinHeight) / 2;
+ }
+ }
+
+ protected override AutomationPeer OnCreateAutomationPeer()
+ {
+ return new NoAutomationWindowPeer(this);
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Playground/Client/Helpers/NullToVisibilityConverter.cs b/source/RevitLookup.UI.Playground/Client/Helpers/NullToVisibilityConverter.cs
new file mode 100644
index 000000000..6d3e485bb
--- /dev/null
+++ b/source/RevitLookup.UI.Playground/Client/Helpers/NullToVisibilityConverter.cs
@@ -0,0 +1,27 @@
+using System.Globalization;
+using System.Windows;
+using System.Windows.Data;
+using System.Windows.Markup;
+
+namespace RevitLookup.UI.Playground.Client.Helpers;
+
+///
+/// Converts a null value to Visibility.Collapsed
+///
+internal sealed class NullToVisibilityConverter : MarkupExtension, IValueConverter
+{
+ public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ return value is null ? Visibility.Collapsed : Visibility.Visible;
+ }
+
+ public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override object? ProvideValue(IServiceProvider serviceProvider)
+ {
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Playground/Client/Helpers/SymbolIconXamlConverter.cs b/source/RevitLookup.UI.Playground/Client/Helpers/SymbolIconXamlConverter.cs
new file mode 100644
index 000000000..92933f1f9
--- /dev/null
+++ b/source/RevitLookup.UI.Playground/Client/Helpers/SymbolIconXamlConverter.cs
@@ -0,0 +1,43 @@
+using System.Globalization;
+using System.Text;
+using System.Windows.Data;
+using System.Windows.Markup;
+using Wpf.Ui.Controls;
+
+namespace RevitLookup.UI.Playground.Client.Helpers;
+
+public sealed class SymbolIconXamlConverter : MarkupExtension, IMultiValueConverter
+{
+ public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (values[0] is string text) return text;
+
+ var icon = (SymbolRegular) values[0];
+ var filled = (bool) values[1];
+
+ var builder = new StringBuilder();
+ builder.Append(" ");
+
+ return builder.ToString();
+ }
+
+ public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override object ProvideValue(IServiceProvider serviceProvider)
+ {
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Playground/Client/Models/FontIconData.cs b/source/RevitLookup.UI.Playground/Client/Models/FontIconData.cs
new file mode 100644
index 000000000..edf024eed
--- /dev/null
+++ b/source/RevitLookup.UI.Playground/Client/Models/FontIconData.cs
@@ -0,0 +1,15 @@
+namespace RevitLookup.UI.Playground.Client.Models;
+
+///
+/// IconData class for icons in icon page
+///
+[Serializable]
+public class FontIconData
+{
+ public required string Name { get; set; }
+ public required string Code { get; set; }
+
+ public string Character => char.ConvertFromUtf32(Convert.ToInt32(Code, 16));
+ public string CodeGlyph => "\\x" + Code;
+ public string TextGlyph => "" + Code + ";";
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Playground/Client/Models/FontIcons.json b/source/RevitLookup.UI.Playground/Client/Models/FontIcons.json
new file mode 100644
index 000000000..f234337b2
--- /dev/null
+++ b/source/RevitLookup.UI.Playground/Client/Models/FontIcons.json
@@ -0,0 +1,5614 @@
+[
+ {
+ "Code": "E700",
+ "Name": "GlobalNavButton"
+ },
+ {
+ "Code": "E701",
+ "Name": "Wifi"
+ },
+ {
+ "Code": "E702",
+ "Name": "Bluetooth"
+ },
+ {
+ "Code": "E703",
+ "Name": "Connect"
+ },
+ {
+ "Code": "E704",
+ "Name": "InternetSharing"
+ },
+ {
+ "Code": "E705",
+ "Name": "VPN"
+ },
+ {
+ "Code": "E706",
+ "Name": "Brightness"
+ },
+ {
+ "Code": "E707",
+ "Name": "MapPin"
+ },
+ {
+ "Code": "E708",
+ "Name": "QuietHours"
+ },
+ {
+ "Code": "E709",
+ "Name": "Airplane"
+ },
+ {
+ "Code": "E70A",
+ "Name": "Tablet"
+ },
+ {
+ "Code": "E70B",
+ "Name": "QuickNote"
+ },
+ {
+ "Code": "E70C",
+ "Name": "RememberedDevice"
+ },
+ {
+ "Code": "E70D",
+ "Name": "ChevronDown"
+ },
+ {
+ "Code": "E70E",
+ "Name": "ChevronUp"
+ },
+ {
+ "Code": "E70F",
+ "Name": "Edit"
+ },
+ {
+ "Code": "E710",
+ "Name": "Add"
+ },
+ {
+ "Code": "E711",
+ "Name": "Cancel"
+ },
+ {
+ "Code": "E712",
+ "Name": "More"
+ },
+ {
+ "Code": "E713",
+ "Name": "Settings"
+ },
+ {
+ "Code": "E714",
+ "Name": "Video"
+ },
+ {
+ "Code": "E715",
+ "Name": "Mail"
+ },
+ {
+ "Code": "E716",
+ "Name": "People"
+ },
+ {
+ "Code": "E717",
+ "Name": "Phone"
+ },
+ {
+ "Code": "E718",
+ "Name": "Pin"
+ },
+ {
+ "Code": "E719",
+ "Name": "Shop"
+ },
+ {
+ "Code": "E71A",
+ "Name": "Stop"
+ },
+ {
+ "Code": "E71B",
+ "Name": "Link"
+ },
+ {
+ "Code": "E71C",
+ "Name": "Filter"
+ },
+ {
+ "Code": "E71D",
+ "Name": "AllApps"
+ },
+ {
+ "Code": "E71E",
+ "Name": "Zoom"
+ },
+ {
+ "Code": "E71F",
+ "Name": "ZoomOut"
+ },
+ {
+ "Code": "E720",
+ "Name": "Microphone"
+ },
+ {
+ "Code": "E721",
+ "Name": "Search"
+ },
+ {
+ "Code": "E722",
+ "Name": "Camera"
+ },
+ {
+ "Code": "E723",
+ "Name": "Attach"
+ },
+ {
+ "Code": "E724",
+ "Name": "Send"
+ },
+ {
+ "Code": "E725",
+ "Name": "SendFill"
+ },
+ {
+ "Code": "E726",
+ "Name": "WalkSolid"
+ },
+ {
+ "Code": "E727",
+ "Name": "InPrivate"
+ },
+ {
+ "Code": "E728",
+ "Name": "FavoriteList"
+ },
+ {
+ "Code": "E729",
+ "Name": "PageSolid"
+ },
+ {
+ "Code": "E72A",
+ "Name": "Forward"
+ },
+ {
+ "Code": "E72B",
+ "Name": "Back"
+ },
+ {
+ "Code": "E72C",
+ "Name": "Refresh"
+ },
+ {
+ "Code": "E72D",
+ "Name": "Share"
+ },
+ {
+ "Code": "E72E",
+ "Name": "Lock"
+ },
+ {
+ "Code": "E730",
+ "Name": "ReportHacked"
+ },
+ {
+ "Code": "E731",
+ "Name": "EMI"
+ },
+ {
+ "Code": "E734",
+ "Name": "FavoriteStar"
+ },
+ {
+ "Code": "E735",
+ "Name": "FavoriteStarFill"
+ },
+ {
+ "Code": "E736",
+ "Name": "ReadingMode"
+ },
+ {
+ "Code": "E737",
+ "Name": "Favicon"
+ },
+ {
+ "Code": "E738",
+ "Name": "Remove"
+ },
+ {
+ "Code": "E739",
+ "Name": "Checkbox"
+ },
+ {
+ "Code": "E73A",
+ "Name": "CheckboxComposite"
+ },
+ {
+ "Code": "E73B",
+ "Name": "CheckboxFill"
+ },
+ {
+ "Code": "E73C",
+ "Name": "CheckboxIndeterminate"
+ },
+ {
+ "Code": "E73D",
+ "Name": "CheckboxCompositeReversed"
+ },
+ {
+ "Code": "E73E",
+ "Name": "CheckMark"
+ },
+ {
+ "Code": "E73F",
+ "Name": "BackToWindow"
+ },
+ {
+ "Code": "E740",
+ "Name": "FullScreen"
+ },
+ {
+ "Code": "E741",
+ "Name": "ResizeTouchLarger"
+ },
+ {
+ "Code": "E742",
+ "Name": "ResizeTouchSmaller"
+ },
+ {
+ "Code": "E743",
+ "Name": "ResizeMouseSmall"
+ },
+ {
+ "Code": "E744",
+ "Name": "ResizeMouseMedium"
+ },
+ {
+ "Code": "E745",
+ "Name": "ResizeMouseWide"
+ },
+ {
+ "Code": "E746",
+ "Name": "ResizeMouseTall"
+ },
+ {
+ "Code": "E747",
+ "Name": "ResizeMouseLarge"
+ },
+ {
+ "Code": "E748",
+ "Name": "SwitchUser"
+ },
+ {
+ "Code": "E749",
+ "Name": "Print"
+ },
+ {
+ "Code": "E74A",
+ "Name": "Up"
+ },
+ {
+ "Code": "E74B",
+ "Name": "Down"
+ },
+ {
+ "Code": "E74C",
+ "Name": "OEM"
+ },
+ {
+ "Code": "E74D",
+ "Name": "Delete"
+ },
+ {
+ "Code": "E74E",
+ "Name": "Save"
+ },
+ {
+ "Code": "E74F",
+ "Name": "Mute"
+ },
+ {
+ "Code": "E750",
+ "Name": "BackSpaceQWERTY"
+ },
+ {
+ "Code": "E751",
+ "Name": "ReturnKey"
+ },
+ {
+ "Code": "E752",
+ "Name": "UpArrowShiftKey"
+ },
+ {
+ "Code": "E753",
+ "Name": "Cloud"
+ },
+ {
+ "Code": "E754",
+ "Name": "Flashlight"
+ },
+ {
+ "Code": "E755",
+ "Name": "RotationLock"
+ },
+ {
+ "Code": "E756",
+ "Name": "CommandPrompt"
+ },
+ {
+ "Code": "E759",
+ "Name": "SIPMove"
+ },
+ {
+ "Code": "E75A",
+ "Name": "SIPUndock"
+ },
+ {
+ "Code": "E75B",
+ "Name": "SIPRedock"
+ },
+ {
+ "Code": "E75C",
+ "Name": "EraseTool"
+ },
+ {
+ "Code": "E75D",
+ "Name": "UnderscoreSpace"
+ },
+ {
+ "Code": "E75E",
+ "Name": "GripperTool"
+ },
+ {
+ "Code": "E75F",
+ "Name": "Dialpad"
+ },
+ {
+ "Code": "E760",
+ "Name": "PageLeft"
+ },
+ {
+ "Code": "E761",
+ "Name": "PageRight"
+ },
+ {
+ "Code": "E762",
+ "Name": "MultiSelect"
+ },
+ {
+ "Code": "E763",
+ "Name": "KeyboardLeftHanded"
+ },
+ {
+ "Code": "E764",
+ "Name": "KeyboardRightHanded"
+ },
+ {
+ "Code": "E765",
+ "Name": "KeyboardClassic"
+ },
+ {
+ "Code": "E766",
+ "Name": "KeyboardSplit"
+ },
+ {
+ "Code": "E767",
+ "Name": "Volume"
+ },
+ {
+ "Code": "E768",
+ "Name": "Play"
+ },
+ {
+ "Code": "E769",
+ "Name": "Pause"
+ },
+ {
+ "Code": "E76B",
+ "Name": "ChevronLeft"
+ },
+ {
+ "Code": "E76C",
+ "Name": "ChevronRight"
+ },
+ {
+ "Code": "E76D",
+ "Name": "InkingTool"
+ },
+ {
+ "Code": "E76E",
+ "Name": "Emoji2"
+ },
+ {
+ "Code": "E76F",
+ "Name": "GripperBarHorizontal"
+ },
+ {
+ "Code": "E770",
+ "Name": "System"
+ },
+ {
+ "Code": "E771",
+ "Name": "Personalize"
+ },
+ {
+ "Code": "E772",
+ "Name": "Devices"
+ },
+ {
+ "Code": "E773",
+ "Name": "SearchAndApps"
+ },
+ {
+ "Code": "E774",
+ "Name": "Globe"
+ },
+ {
+ "Code": "E775",
+ "Name": "TimeLanguage"
+ },
+ {
+ "Code": "E776",
+ "Name": "EaseOfAccess"
+ },
+ {
+ "Code": "E777",
+ "Name": "UpdateRestore"
+ },
+ {
+ "Code": "E778",
+ "Name": "HangUp"
+ },
+ {
+ "Code": "E779",
+ "Name": "ContactInfo"
+ },
+ {
+ "Code": "E77A",
+ "Name": "Unpin"
+ },
+ {
+ "Code": "E77B",
+ "Name": "Contact"
+ },
+ {
+ "Code": "E77C",
+ "Name": "Memo"
+ },
+ {
+ "Code": "E77E",
+ "Name": "IncomingCall"
+ },
+ {
+ "Code": "E77F",
+ "Name": "Paste"
+ },
+ {
+ "Code": "E780",
+ "Name": "PhoneBook"
+ },
+ {
+ "Code": "E781",
+ "Name": "LEDLight"
+ },
+ {
+ "Code": "E783",
+ "Name": "Error"
+ },
+ {
+ "Code": "E784",
+ "Name": "GripperBarVertical"
+ },
+ {
+ "Code": "E785",
+ "Name": "Unlock"
+ },
+ {
+ "Code": "E786",
+ "Name": "Slideshow"
+ },
+ {
+ "Code": "E787",
+ "Name": "Calendar"
+ },
+ {
+ "Code": "E788",
+ "Name": "GripperResize"
+ },
+ {
+ "Code": "E789",
+ "Name": "Megaphone"
+ },
+ {
+ "Code": "E78A",
+ "Name": "Trim"
+ },
+ {
+ "Code": "E78B",
+ "Name": "NewWindow"
+ },
+ {
+ "Code": "E78C",
+ "Name": "SaveLocal"
+ },
+ {
+ "Code": "E790",
+ "Name": "Color"
+ },
+ {
+ "Code": "E791",
+ "Name": "DataSense"
+ },
+ {
+ "Code": "E792",
+ "Name": "SaveAs"
+ },
+ {
+ "Code": "E793",
+ "Name": "Light"
+ },
+ {
+ "Code": "E799",
+ "Name": "AspectRatio"
+ },
+ {
+ "Code": "E7A5",
+ "Name": "DataSenseBar"
+ },
+ {
+ "Code": "E7A6",
+ "Name": "Redo"
+ },
+ {
+ "Code": "E7A7",
+ "Name": "Undo"
+ },
+ {
+ "Code": "E7A8",
+ "Name": "Crop"
+ },
+ {
+ "Code": "E7AC",
+ "Name": "OpenWith"
+ },
+ {
+ "Code": "E7AD",
+ "Name": "Rotate"
+ },
+ {
+ "Code": "E7B3",
+ "Name": "RedEye"
+ },
+ {
+ "Code": "E7B5",
+ "Name": "SetlockScreen"
+ },
+ {
+ "Code": "E7B7",
+ "Name": "MapPin2"
+ },
+ {
+ "Code": "E7B8",
+ "Name": "Package"
+ },
+ {
+ "Code": "E7BA",
+ "Name": "Warning"
+ },
+ {
+ "Code": "E7BC",
+ "Name": "ReadingList"
+ },
+ {
+ "Code": "E7BE",
+ "Name": "Education"
+ },
+ {
+ "Code": "E7BF",
+ "Name": "ShoppingCart"
+ },
+ {
+ "Code": "E7C0",
+ "Name": "Train"
+ },
+ {
+ "Code": "E7C1",
+ "Name": "Flag"
+ },
+ {
+ "Code": "E7C2",
+ "Name": "Move"
+ },
+ {
+ "Code": "E7C3",
+ "Name": "Page"
+ },
+ {
+ "Code": "E7C4",
+ "Name": "TaskView"
+ },
+ {
+ "Code": "E7C5",
+ "Name": "BrowsePhotos"
+ },
+ {
+ "Code": "E7C6",
+ "Name": "HalfStarLeft"
+ },
+ {
+ "Code": "E7C7",
+ "Name": "HalfStarRight"
+ },
+ {
+ "Code": "E7C8",
+ "Name": "Record"
+ },
+ {
+ "Code": "E7C9",
+ "Name": "TouchPointer"
+ },
+ {
+ "Code": "E7DE",
+ "Name": "LangJPN"
+ },
+ {
+ "Code": "E7E3",
+ "Name": "Ferry"
+ },
+ {
+ "Code": "E7E6",
+ "Name": "Highlight"
+ },
+ {
+ "Code": "E7E7",
+ "Name": "ActionCenterNotification"
+ },
+ {
+ "Code": "E7E8",
+ "Name": "PowerButton"
+ },
+ {
+ "Code": "E7EA",
+ "Name": "ResizeTouchNarrower"
+ },
+ {
+ "Code": "E7EB",
+ "Name": "ResizeTouchShorter"
+ },
+ {
+ "Code": "E7EC",
+ "Name": "DrivingMode"
+ },
+ {
+ "Code": "E7ED",
+ "Name": "RingerSilent"
+ },
+ {
+ "Code": "E7EE",
+ "Name": "OtherUser"
+ },
+ {
+ "Code": "E7EF",
+ "Name": "Admin"
+ },
+ {
+ "Code": "E7F0",
+ "Name": "CC"
+ },
+ {
+ "Code": "E7F1",
+ "Name": "SDCard"
+ },
+ {
+ "Code": "E7F2",
+ "Name": "CallForwarding"
+ },
+ {
+ "Code": "E7F3",
+ "Name": "SettingsDisplaySound"
+ },
+ {
+ "Code": "E7F4",
+ "Name": "TVMonitor"
+ },
+ {
+ "Code": "E7F5",
+ "Name": "Speakers"
+ },
+ {
+ "Code": "E7F6",
+ "Name": "Headphone"
+ },
+ {
+ "Code": "E7F7",
+ "Name": "DeviceLaptopPic"
+ },
+ {
+ "Code": "E7F8",
+ "Name": "DeviceLaptopNoPic"
+ },
+ {
+ "Code": "E7F9",
+ "Name": "DeviceMonitorRightPic"
+ },
+ {
+ "Code": "E7FA",
+ "Name": "DeviceMonitorLeftPic"
+ },
+ {
+ "Code": "E7FB",
+ "Name": "DeviceMonitorNoPic"
+ },
+ {
+ "Code": "E7FC",
+ "Name": "Game"
+ },
+ {
+ "Code": "E7FD",
+ "Name": "HorizontalTabKey"
+ },
+ {
+ "Code": "E802",
+ "Name": "StreetsideSplitMinimize"
+ },
+ {
+ "Code": "E803",
+ "Name": "StreetsideSplitExpand"
+ },
+ {
+ "Code": "E804",
+ "Name": "Car"
+ },
+ {
+ "Code": "E805",
+ "Name": "Walk"
+ },
+ {
+ "Code": "E806",
+ "Name": "Bus"
+ },
+ {
+ "Code": "E809",
+ "Name": "TiltUp"
+ },
+ {
+ "Code": "E80A",
+ "Name": "TiltDown"
+ },
+ {
+ "Code": "E80B",
+ "Name": "CallControl"
+ },
+ {
+ "Code": "E80C",
+ "Name": "RotateMapRight"
+ },
+ {
+ "Code": "E80D",
+ "Name": "RotateMapLeft"
+ },
+ {
+ "Code": "E80F",
+ "Name": "Home"
+ },
+ {
+ "Code": "E811",
+ "Name": "ParkingLocation"
+ },
+ {
+ "Code": "E812",
+ "Name": "MapCompassTop"
+ },
+ {
+ "Code": "E813",
+ "Name": "MapCompassBottom"
+ },
+ {
+ "Code": "E814",
+ "Name": "IncidentTriangle"
+ },
+ {
+ "Code": "E815",
+ "Name": "Touch"
+ },
+ {
+ "Code": "E816",
+ "Name": "MapDirections"
+ },
+ {
+ "Code": "E819",
+ "Name": "StartPoint"
+ },
+ {
+ "Code": "E81A",
+ "Name": "StopPoint"
+ },
+ {
+ "Code": "E81B",
+ "Name": "EndPoint"
+ },
+ {
+ "Code": "E81C",
+ "Name": "History"
+ },
+ {
+ "Code": "E81D",
+ "Name": "Location"
+ },
+ {
+ "Code": "E81E",
+ "Name": "MapLayers"
+ },
+ {
+ "Code": "E81F",
+ "Name": "Accident"
+ },
+ {
+ "Code": "E821",
+ "Name": "Work"
+ },
+ {
+ "Code": "E822",
+ "Name": "Construction"
+ },
+ {
+ "Code": "E823",
+ "Name": "Recent"
+ },
+ {
+ "Code": "E825",
+ "Name": "Bank"
+ },
+ {
+ "Code": "E826",
+ "Name": "DownloadMap"
+ },
+ {
+ "Code": "E829",
+ "Name": "InkingToolFill2"
+ },
+ {
+ "Code": "E82A",
+ "Name": "HighlightFill2"
+ },
+ {
+ "Code": "E82B",
+ "Name": "EraseToolFill"
+ },
+ {
+ "Code": "E82C",
+ "Name": "EraseToolFill2"
+ },
+ {
+ "Code": "E82D",
+ "Name": "Dictionary"
+ },
+ {
+ "Code": "E82E",
+ "Name": "DictionaryAdd"
+ },
+ {
+ "Code": "E82F",
+ "Name": "ToolTip"
+ },
+ {
+ "Code": "E830",
+ "Name": "ChromeBack"
+ },
+ {
+ "Code": "E835",
+ "Name": "ProvisioningPackage"
+ },
+ {
+ "Code": "E836",
+ "Name": "AddRemoteDevice"
+ },
+ {
+ "Code": "E838",
+ "Name": "FolderOpen"
+ },
+ {
+ "Code": "E839",
+ "Name": "Ethernet"
+ },
+ {
+ "Code": "E83A",
+ "Name": "ShareBroadband"
+ },
+ {
+ "Code": "E83B",
+ "Name": "DirectAccess"
+ },
+ {
+ "Code": "E83C",
+ "Name": "DialUp"
+ },
+ {
+ "Code": "E83D",
+ "Name": "DefenderApp"
+ },
+ {
+ "Code": "E83E",
+ "Name": "BatteryCharging9"
+ },
+ {
+ "Code": "E83F",
+ "Name": "Battery10"
+ },
+ {
+ "Code": "E840",
+ "Name": "Pinned"
+ },
+ {
+ "Code": "E841",
+ "Name": "PinFill"
+ },
+ {
+ "Code": "E842",
+ "Name": "PinnedFill"
+ },
+ {
+ "Code": "E843",
+ "Name": "PeriodKey"
+ },
+ {
+ "Code": "E844",
+ "Name": "PuncKey"
+ },
+ {
+ "Code": "E845",
+ "Name": "RevToggleKey"
+ },
+ {
+ "Code": "E846",
+ "Name": "RightArrowKeyTime1"
+ },
+ {
+ "Code": "E847",
+ "Name": "RightArrowKeyTime2"
+ },
+ {
+ "Code": "E848",
+ "Name": "LeftQuote"
+ },
+ {
+ "Code": "E849",
+ "Name": "RightQuote"
+ },
+ {
+ "Code": "E84A",
+ "Name": "DownShiftKey"
+ },
+ {
+ "Code": "E84B",
+ "Name": "UpShiftKey"
+ },
+ {
+ "Code": "E84C",
+ "Name": "PuncKey0"
+ },
+ {
+ "Code": "E84D",
+ "Name": "PuncKeyLeftBottom"
+ },
+ {
+ "Code": "E84E",
+ "Name": "RightArrowKeyTime3"
+ },
+ {
+ "Code": "E84F",
+ "Name": "RightArrowKeyTime4"
+ },
+ {
+ "Code": "E850",
+ "Name": "Battery0"
+ },
+ {
+ "Code": "E851",
+ "Name": "Battery1"
+ },
+ {
+ "Code": "E852",
+ "Name": "Battery2"
+ },
+ {
+ "Code": "E853",
+ "Name": "Battery3"
+ },
+ {
+ "Code": "E854",
+ "Name": "Battery4"
+ },
+ {
+ "Code": "E855",
+ "Name": "Battery5"
+ },
+ {
+ "Code": "E856",
+ "Name": "Battery6"
+ },
+ {
+ "Code": "E857",
+ "Name": "Battery7"
+ },
+ {
+ "Code": "E858",
+ "Name": "Battery8"
+ },
+ {
+ "Code": "E859",
+ "Name": "Battery9"
+ },
+ {
+ "Code": "E85A",
+ "Name": "BatteryCharging0"
+ },
+ {
+ "Code": "E85B",
+ "Name": "BatteryCharging1"
+ },
+ {
+ "Code": "E85C",
+ "Name": "BatteryCharging2"
+ },
+ {
+ "Code": "E85D",
+ "Name": "BatteryCharging3"
+ },
+ {
+ "Code": "E85E",
+ "Name": "BatteryCharging4"
+ },
+ {
+ "Code": "E85F",
+ "Name": "BatteryCharging5"
+ },
+ {
+ "Code": "E860",
+ "Name": "BatteryCharging6"
+ },
+ {
+ "Code": "E861",
+ "Name": "BatteryCharging7"
+ },
+ {
+ "Code": "E862",
+ "Name": "BatteryCharging8"
+ },
+ {
+ "Code": "E863",
+ "Name": "BatterySaver0"
+ },
+ {
+ "Code": "E864",
+ "Name": "BatterySaver1"
+ },
+ {
+ "Code": "E865",
+ "Name": "BatterySaver2"
+ },
+ {
+ "Code": "E866",
+ "Name": "BatterySaver3"
+ },
+ {
+ "Code": "E867",
+ "Name": "BatterySaver4"
+ },
+ {
+ "Code": "E868",
+ "Name": "BatterySaver5"
+ },
+ {
+ "Code": "E869",
+ "Name": "BatterySaver6"
+ },
+ {
+ "Code": "E86A",
+ "Name": "BatterySaver7"
+ },
+ {
+ "Code": "E86B",
+ "Name": "BatterySaver8"
+ },
+ {
+ "Code": "E86C",
+ "Name": "SignalBars1"
+ },
+ {
+ "Code": "E86D",
+ "Name": "SignalBars2"
+ },
+ {
+ "Code": "E86E",
+ "Name": "SignalBars3"
+ },
+ {
+ "Code": "E86F",
+ "Name": "SignalBars4"
+ },
+ {
+ "Code": "E870",
+ "Name": "SignalBars5"
+ },
+ {
+ "Code": "E871",
+ "Name": "SignalNotConnected"
+ },
+ {
+ "Code": "E872",
+ "Name": "Wifi1"
+ },
+ {
+ "Code": "E873",
+ "Name": "Wifi2"
+ },
+ {
+ "Code": "E874",
+ "Name": "Wifi3"
+ },
+ {
+ "Code": "E875",
+ "Name": "MobSIMLock"
+ },
+ {
+ "Code": "E876",
+ "Name": "MobSIMMissing"
+ },
+ {
+ "Code": "E877",
+ "Name": "Vibrate"
+ },
+ {
+ "Code": "E878",
+ "Name": "RoamingInternational"
+ },
+ {
+ "Code": "E879",
+ "Name": "RoamingDomestic"
+ },
+ {
+ "Code": "E87A",
+ "Name": "CallForwardInternational"
+ },
+ {
+ "Code": "E87B",
+ "Name": "CallForwardRoaming"
+ },
+ {
+ "Code": "E87C",
+ "Name": "JpnRomanji"
+ },
+ {
+ "Code": "E87D",
+ "Name": "JpnRomanjiLock"
+ },
+ {
+ "Code": "E87E",
+ "Name": "JpnRomanjiShift"
+ },
+ {
+ "Code": "E87F",
+ "Name": "JpnRomanjiShiftLock"
+ },
+ {
+ "Code": "E880",
+ "Name": "StatusDataTransfer"
+ },
+ {
+ "Code": "E881",
+ "Name": "StatusDataTransferVPN"
+ },
+ {
+ "Code": "E882",
+ "Name": "StatusDualSIM2"
+ },
+ {
+ "Code": "E883",
+ "Name": "StatusDualSIM2VPN"
+ },
+ {
+ "Code": "E884",
+ "Name": "StatusDualSIM1"
+ },
+ {
+ "Code": "E885",
+ "Name": "StatusDualSIM1VPN"
+ },
+ {
+ "Code": "E886",
+ "Name": "StatusSGLTE"
+ },
+ {
+ "Code": "E887",
+ "Name": "StatusSGLTECell"
+ },
+ {
+ "Code": "E888",
+ "Name": "StatusSGLTEDataVPN"
+ },
+ {
+ "Code": "E889",
+ "Name": "StatusVPN"
+ },
+ {
+ "Code": "E88A",
+ "Name": "WifiHotspot"
+ },
+ {
+ "Code": "E88B",
+ "Name": "LanguageKor"
+ },
+ {
+ "Code": "E88C",
+ "Name": "LanguageCht"
+ },
+ {
+ "Code": "E88D",
+ "Name": "LanguageChs"
+ },
+ {
+ "Code": "E88E",
+ "Name": "USB"
+ },
+ {
+ "Code": "E88F",
+ "Name": "InkingToolFill"
+ },
+ {
+ "Code": "E890",
+ "Name": "View"
+ },
+ {
+ "Code": "E891",
+ "Name": "HighlightFill"
+ },
+ {
+ "Code": "E892",
+ "Name": "Previous"
+ },
+ {
+ "Code": "E893",
+ "Name": "Next"
+ },
+ {
+ "Code": "E894",
+ "Name": "Clear"
+ },
+ {
+ "Code": "E895",
+ "Name": "Sync"
+ },
+ {
+ "Code": "E896",
+ "Name": "Download"
+ },
+ {
+ "Code": "E897",
+ "Name": "Help"
+ },
+ {
+ "Code": "E898",
+ "Name": "Upload"
+ },
+ {
+ "Code": "E899",
+ "Name": "Emoji"
+ },
+ {
+ "Code": "E89A",
+ "Name": "TwoPage"
+ },
+ {
+ "Code": "E89B",
+ "Name": "LeaveChat"
+ },
+ {
+ "Code": "E89C",
+ "Name": "MailForward"
+ },
+ {
+ "Code": "E89E",
+ "Name": "RotateCamera"
+ },
+ {
+ "Code": "E89F",
+ "Name": "ClosePane"
+ },
+ {
+ "Code": "E8A0",
+ "Name": "OpenPane"
+ },
+ {
+ "Code": "E8A1",
+ "Name": "PreviewLink"
+ },
+ {
+ "Code": "E8A2",
+ "Name": "AttachCamera"
+ },
+ {
+ "Code": "E8A3",
+ "Name": "ZoomIn"
+ },
+ {
+ "Code": "E8A4",
+ "Name": "Bookmarks"
+ },
+ {
+ "Code": "E8A5",
+ "Name": "Document"
+ },
+ {
+ "Code": "E8A6",
+ "Name": "ProtectedDocument"
+ },
+ {
+ "Code": "E8A7",
+ "Name": "OpenInNewWindow"
+ },
+ {
+ "Code": "E8A8",
+ "Name": "MailFill"
+ },
+ {
+ "Code": "E8A9",
+ "Name": "ViewAll"
+ },
+ {
+ "Code": "E8AA",
+ "Name": "VideoChat"
+ },
+ {
+ "Code": "E8AB",
+ "Name": "Switch"
+ },
+ {
+ "Code": "E8AC",
+ "Name": "Rename"
+ },
+ {
+ "Code": "E8AD",
+ "Name": "Go"
+ },
+ {
+ "Code": "E8AE",
+ "Name": "SurfaceHub"
+ },
+ {
+ "Code": "E8AF",
+ "Name": "Remote"
+ },
+ {
+ "Code": "E8B0",
+ "Name": "Click"
+ },
+ {
+ "Code": "E8B1",
+ "Name": "Shuffle"
+ },
+ {
+ "Code": "E8B2",
+ "Name": "Movies"
+ },
+ {
+ "Code": "E8B3",
+ "Name": "SelectAll"
+ },
+ {
+ "Code": "E8B4",
+ "Name": "Orientation"
+ },
+ {
+ "Code": "E8B5",
+ "Name": "Import"
+ },
+ {
+ "Code": "E8B6",
+ "Name": "ImportAll"
+ },
+ {
+ "Code": "E8B7",
+ "Name": "Folder"
+ },
+ {
+ "Code": "E8B8",
+ "Name": "Webcam"
+ },
+ {
+ "Code": "E8B9",
+ "Name": "Picture"
+ },
+ {
+ "Code": "E8BA",
+ "Name": "Caption"
+ },
+ {
+ "Code": "E8BB",
+ "Name": "ChromeClose"
+ },
+ {
+ "Code": "E8BC",
+ "Name": "ShowResults"
+ },
+ {
+ "Code": "E8BD",
+ "Name": "Message"
+ },
+ {
+ "Code": "E8BE",
+ "Name": "Leaf"
+ },
+ {
+ "Code": "E8BF",
+ "Name": "CalendarDay"
+ },
+ {
+ "Code": "E8C0",
+ "Name": "CalendarWeek"
+ },
+ {
+ "Code": "E8C1",
+ "Name": "Characters"
+ },
+ {
+ "Code": "E8C2",
+ "Name": "MailReplyAll"
+ },
+ {
+ "Code": "E8C3",
+ "Name": "Read"
+ },
+ {
+ "Code": "E8C4",
+ "Name": "ShowBcc"
+ },
+ {
+ "Code": "E8C5",
+ "Name": "HideBcc"
+ },
+ {
+ "Code": "E8C6",
+ "Name": "Cut"
+ },
+ {
+ "Code": "E8C7",
+ "Name": "PaymentCard"
+ },
+ {
+ "Code": "E8C8",
+ "Name": "Copy"
+ },
+ {
+ "Code": "E8C9",
+ "Name": "Important"
+ },
+ {
+ "Code": "E8CA",
+ "Name": "MailReply"
+ },
+ {
+ "Code": "E8CB",
+ "Name": "Sort"
+ },
+ {
+ "Code": "E8CC",
+ "Name": "MobileTablet"
+ },
+ {
+ "Code": "E8CD",
+ "Name": "DisconnectDrive"
+ },
+ {
+ "Code": "E8CE",
+ "Name": "MapDrive"
+ },
+ {
+ "Code": "E8CF",
+ "Name": "ContactPresence"
+ },
+ {
+ "Code": "E8D0",
+ "Name": "Priority"
+ },
+ {
+ "Code": "E8D1",
+ "Name": "GotoToday"
+ },
+ {
+ "Code": "E8D2",
+ "Name": "Font"
+ },
+ {
+ "Code": "E8D3",
+ "Name": "FontColor"
+ },
+ {
+ "Code": "E8D4",
+ "Name": "Contact2"
+ },
+ {
+ "Code": "E8D5",
+ "Name": "FolderFill"
+ },
+ {
+ "Code": "E8D6",
+ "Name": "Audio"
+ },
+ {
+ "Code": "E8D7",
+ "Name": "Permissions"
+ },
+ {
+ "Code": "E8D8",
+ "Name": "DisableUpdates"
+ },
+ {
+ "Code": "E8D9",
+ "Name": "Unfavorite"
+ },
+ {
+ "Code": "E8DA",
+ "Name": "OpenLocal"
+ },
+ {
+ "Code": "E8DB",
+ "Name": "Italic"
+ },
+ {
+ "Code": "E8DC",
+ "Name": "Underline"
+ },
+ {
+ "Code": "E8DD",
+ "Name": "Bold"
+ },
+ {
+ "Code": "E8DE",
+ "Name": "MoveToFolder"
+ },
+ {
+ "Code": "E8DF",
+ "Name": "LikeDislike"
+ },
+ {
+ "Code": "E8E0",
+ "Name": "Dislike"
+ },
+ {
+ "Code": "E8E1",
+ "Name": "Like"
+ },
+ {
+ "Code": "E8E2",
+ "Name": "AlignRight"
+ },
+ {
+ "Code": "E8E3",
+ "Name": "AlignCenter"
+ },
+ {
+ "Code": "E8E4",
+ "Name": "AlignLeft"
+ },
+ {
+ "Code": "E8E5",
+ "Name": "OpenFile"
+ },
+ {
+ "Code": "E8E6",
+ "Name": "ClearSelection"
+ },
+ {
+ "Code": "E8E7",
+ "Name": "FontDecrease"
+ },
+ {
+ "Code": "E8E8",
+ "Name": "FontIncrease"
+ },
+ {
+ "Code": "E8E9",
+ "Name": "FontSize"
+ },
+ {
+ "Code": "E8EA",
+ "Name": "CellPhone"
+ },
+ {
+ "Code": "E8EB",
+ "Name": "Reshare"
+ },
+ {
+ "Code": "E8EC",
+ "Name": "Tag"
+ },
+ {
+ "Code": "E8ED",
+ "Name": "RepeatOne"
+ },
+ {
+ "Code": "E8EE",
+ "Name": "RepeatAll"
+ },
+ {
+ "Code": "E8EF",
+ "Name": "Calculator"
+ },
+ {
+ "Code": "E8F0",
+ "Name": "Directions"
+ },
+ {
+ "Code": "E8F1",
+ "Name": "Library"
+ },
+ {
+ "Code": "E8F2",
+ "Name": "ChatBubbles"
+ },
+ {
+ "Code": "E8F3",
+ "Name": "PostUpdate"
+ },
+ {
+ "Code": "E8F4",
+ "Name": "NewFolder"
+ },
+ {
+ "Code": "E8F5",
+ "Name": "CalendarReply"
+ },
+ {
+ "Code": "E8F6",
+ "Name": "UnsyncFolder"
+ },
+ {
+ "Code": "E8F7",
+ "Name": "SyncFolder"
+ },
+ {
+ "Code": "E8F8",
+ "Name": "BlockContact"
+ },
+ {
+ "Code": "E8F9",
+ "Name": "SwitchApps"
+ },
+ {
+ "Code": "E8FA",
+ "Name": "AddFriend"
+ },
+ {
+ "Code": "E8FB",
+ "Name": "Accept"
+ },
+ {
+ "Code": "E8FC",
+ "Name": "GoToStart"
+ },
+ {
+ "Code": "E8FD",
+ "Name": "BulletedList"
+ },
+ {
+ "Code": "E8FE",
+ "Name": "Scan"
+ },
+ {
+ "Code": "E8FF",
+ "Name": "Preview"
+ },
+ {
+ "Code": "E902",
+ "Name": "Group"
+ },
+ {
+ "Code": "E904",
+ "Name": "ZeroBars"
+ },
+ {
+ "Code": "E905",
+ "Name": "OneBar"
+ },
+ {
+ "Code": "E906",
+ "Name": "TwoBars"
+ },
+ {
+ "Code": "E907",
+ "Name": "ThreeBars"
+ },
+ {
+ "Code": "E908",
+ "Name": "FourBars"
+ },
+ {
+ "Code": "E909",
+ "Name": "World"
+ },
+ {
+ "Code": "E90A",
+ "Name": "Comment"
+ },
+ {
+ "Code": "E90B",
+ "Name": "MusicInfo"
+ },
+ {
+ "Code": "E90C",
+ "Name": "DockLeft"
+ },
+ {
+ "Code": "E90D",
+ "Name": "DockRight"
+ },
+ {
+ "Code": "E90E",
+ "Name": "DockBottom"
+ },
+ {
+ "Code": "E90F",
+ "Name": "Repair"
+ },
+ {
+ "Code": "E910",
+ "Name": "Accounts"
+ },
+ {
+ "Code": "E911",
+ "Name": "DullSound"
+ },
+ {
+ "Code": "E912",
+ "Name": "Manage"
+ },
+ {
+ "Code": "E913",
+ "Name": "Street"
+ },
+ {
+ "Code": "E914",
+ "Name": "Printer3D"
+ },
+ {
+ "Code": "E915",
+ "Name": "RadioBullet"
+ },
+ {
+ "Code": "E916",
+ "Name": "Stopwatch"
+ },
+ {
+ "Code": "E91B",
+ "Name": "Photo"
+ },
+ {
+ "Code": "E91C",
+ "Name": "ActionCenter"
+ },
+ {
+ "Code": "E91F",
+ "Name": "FullCircleMask"
+ },
+ {
+ "Code": "E921",
+ "Name": "ChromeMinimize"
+ },
+ {
+ "Code": "E922",
+ "Name": "ChromeMaximize"
+ },
+ {
+ "Code": "E923",
+ "Name": "ChromeRestore"
+ },
+ {
+ "Code": "E924",
+ "Name": "Annotation"
+ },
+ {
+ "Code": "E925",
+ "Name": "BackSpaceQWERTYSm"
+ },
+ {
+ "Code": "E926",
+ "Name": "BackSpaceQWERTYMd"
+ },
+ {
+ "Code": "E927",
+ "Name": "Swipe"
+ },
+ {
+ "Code": "E928",
+ "Name": "Fingerprint"
+ },
+ {
+ "Code": "E929",
+ "Name": "Handwriting"
+ },
+ {
+ "Code": "E92C",
+ "Name": "ChromeBackToWindow"
+ },
+ {
+ "Code": "E92D",
+ "Name": "ChromeFullScreen"
+ },
+ {
+ "Code": "E92E",
+ "Name": "KeyboardStandard"
+ },
+ {
+ "Code": "E92F",
+ "Name": "KeyboardDismiss"
+ },
+ {
+ "Code": "E930",
+ "Name": "Completed"
+ },
+ {
+ "Code": "E931",
+ "Name": "ChromeAnnotate"
+ },
+ {
+ "Code": "E932",
+ "Name": "Label"
+ },
+ {
+ "Code": "E933",
+ "Name": "IBeam"
+ },
+ {
+ "Code": "E934",
+ "Name": "IBeamOutline"
+ },
+ {
+ "Code": "E935",
+ "Name": "FlickDown"
+ },
+ {
+ "Code": "E936",
+ "Name": "FlickUp"
+ },
+ {
+ "Code": "E937",
+ "Name": "FlickLeft"
+ },
+ {
+ "Code": "E938",
+ "Name": "FlickRight"
+ },
+ {
+ "Code": "E939",
+ "Name": "FeedbackApp"
+ },
+ {
+ "Code": "E93C",
+ "Name": "MusicAlbum"
+ },
+ {
+ "Code": "E93E",
+ "Name": "Streaming"
+ },
+ {
+ "Code": "E943",
+ "Name": "Code"
+ },
+ {
+ "Code": "E944",
+ "Name": "ReturnToWindow"
+ },
+ {
+ "Code": "E945",
+ "Name": "LightningBolt"
+ },
+ {
+ "Code": "E946",
+ "Name": "Info"
+ },
+ {
+ "Code": "E947",
+ "Name": "CalculatorMultiply"
+ },
+ {
+ "Code": "E948",
+ "Name": "CalculatorAddition"
+ },
+ {
+ "Code": "E949",
+ "Name": "CalculatorSubtract"
+ },
+ {
+ "Code": "E94A",
+ "Name": "CalculatorDivide"
+ },
+ {
+ "Code": "E94B",
+ "Name": "CalculatorSquareroot"
+ },
+ {
+ "Code": "E94C",
+ "Name": "CalculatorPercentage"
+ },
+ {
+ "Code": "E94D",
+ "Name": "CalculatorNegate"
+ },
+ {
+ "Code": "E94E",
+ "Name": "CalculatorEqualTo"
+ },
+ {
+ "Code": "E94F",
+ "Name": "CalculatorBackspace"
+ },
+ {
+ "Code": "E950",
+ "Name": "Component"
+ },
+ {
+ "Code": "E951",
+ "Name": "DMC"
+ },
+ {
+ "Code": "E952",
+ "Name": "Dock"
+ },
+ {
+ "Code": "E953",
+ "Name": "MultimediaDMS"
+ },
+ {
+ "Code": "E954",
+ "Name": "MultimediaDVR"
+ },
+ {
+ "Code": "E955",
+ "Name": "MultimediaPMP"
+ },
+ {
+ "Code": "E956",
+ "Name": "PrintfaxPrinterFile"
+ },
+ {
+ "Code": "E957",
+ "Name": "Sensor"
+ },
+ {
+ "Code": "E958",
+ "Name": "StorageOptical"
+ },
+ {
+ "Code": "E95A",
+ "Name": "Communications"
+ },
+ {
+ "Code": "E95B",
+ "Name": "Headset"
+ },
+ {
+ "Code": "E95D",
+ "Name": "Projector"
+ },
+ {
+ "Code": "E95E",
+ "Name": "Health"
+ },
+ {
+ "Code": "E95F",
+ "Name": "Wire"
+ },
+ {
+ "Code": "E960",
+ "Name": "Webcam2"
+ },
+ {
+ "Code": "E961",
+ "Name": "Input"
+ },
+ {
+ "Code": "E962",
+ "Name": "Mouse"
+ },
+ {
+ "Code": "E963",
+ "Name": "Smartcard"
+ },
+ {
+ "Code": "E964",
+ "Name": "SmartcardVirtual"
+ },
+ {
+ "Code": "E965",
+ "Name": "MediaStorageTower"
+ },
+ {
+ "Code": "E966",
+ "Name": "ReturnKeySm"
+ },
+ {
+ "Code": "E967",
+ "Name": "GameConsole"
+ },
+ {
+ "Code": "E968",
+ "Name": "Network"
+ },
+ {
+ "Code": "E969",
+ "Name": "StorageNetworkWireless"
+ },
+ {
+ "Code": "E96A",
+ "Name": "StorageTape"
+ },
+ {
+ "Code": "E96D",
+ "Name": "ChevronUpSmall"
+ },
+ {
+ "Code": "E96E",
+ "Name": "ChevronDownSmall"
+ },
+ {
+ "Code": "E96F",
+ "Name": "ChevronLeftSmall"
+ },
+ {
+ "Code": "E970",
+ "Name": "ChevronRightSmall"
+ },
+ {
+ "Code": "E971",
+ "Name": "ChevronUpMed"
+ },
+ {
+ "Code": "E972",
+ "Name": "ChevronDownMed"
+ },
+ {
+ "Code": "E973",
+ "Name": "ChevronLeftMed"
+ },
+ {
+ "Code": "E974",
+ "Name": "ChevronRightMed"
+ },
+ {
+ "Code": "E975",
+ "Name": "Devices2"
+ },
+ {
+ "Code": "E976",
+ "Name": "ExpandTile"
+ },
+ {
+ "Code": "E977",
+ "Name": "PC1"
+ },
+ {
+ "Code": "E978",
+ "Name": "PresenceChicklet"
+ },
+ {
+ "Code": "E979",
+ "Name": "PresenceChickletVideo"
+ },
+ {
+ "Code": "E97A",
+ "Name": "Reply"
+ },
+ {
+ "Code": "E97B",
+ "Name": "SetTile"
+ },
+ {
+ "Code": "E97C",
+ "Name": "Type"
+ },
+ {
+ "Code": "E97D",
+ "Name": "Korean"
+ },
+ {
+ "Code": "E97E",
+ "Name": "HalfAlpha"
+ },
+ {
+ "Code": "E97F",
+ "Name": "FullAlpha"
+ },
+ {
+ "Code": "E980",
+ "Name": "Key12On"
+ },
+ {
+ "Code": "E981",
+ "Name": "ChineseChangjie"
+ },
+ {
+ "Code": "E982",
+ "Name": "QWERTYOn"
+ },
+ {
+ "Code": "E983",
+ "Name": "QWERTYOff"
+ },
+ {
+ "Code": "E984",
+ "Name": "ChineseQuick"
+ },
+ {
+ "Code": "E985",
+ "Name": "Japanese"
+ },
+ {
+ "Code": "E986",
+ "Name": "FullHiragana"
+ },
+ {
+ "Code": "E987",
+ "Name": "FullKatakana"
+ },
+ {
+ "Code": "E988",
+ "Name": "HalfKatakana"
+ },
+ {
+ "Code": "E989",
+ "Name": "ChineseBoPoMoFo"
+ },
+ {
+ "Code": "E98A",
+ "Name": "ChinesePinyin"
+ },
+ {
+ "Code": "E98F",
+ "Name": "ConstructionCone"
+ },
+ {
+ "Code": "E990",
+ "Name": "XboxOneConsole"
+ },
+ {
+ "Code": "E992",
+ "Name": "Volume0"
+ },
+ {
+ "Code": "E993",
+ "Name": "Volume1"
+ },
+ {
+ "Code": "E994",
+ "Name": "Volume2"
+ },
+ {
+ "Code": "E995",
+ "Name": "Volume3"
+ },
+ {
+ "Code": "E996",
+ "Name": "BatteryUnknown"
+ },
+ {
+ "Code": "E998",
+ "Name": "WifiAttentionOverlay"
+ },
+ {
+ "Code": "E99A",
+ "Name": "Robot"
+ },
+ {
+ "Code": "E9A1",
+ "Name": "TapAndSend"
+ },
+ {
+ "Code": "E9A6",
+ "Name": "FitPage"
+ },
+ {
+ "Code": "E9A8",
+ "Name": "PasswordKeyShow"
+ },
+ {
+ "Code": "E9A9",
+ "Name": "PasswordKeyHide"
+ },
+ {
+ "Code": "E9AA",
+ "Name": "BidiLtr"
+ },
+ {
+ "Code": "E9AB",
+ "Name": "BidiRtl"
+ },
+ {
+ "Code": "E9AC",
+ "Name": "ForwardSm"
+ },
+ {
+ "Code": "E9AD",
+ "Name": "CommaKey"
+ },
+ {
+ "Code": "E9AE",
+ "Name": "DashKey"
+ },
+ {
+ "Code": "E9AF",
+ "Name": "DullSoundKey"
+ },
+ {
+ "Code": "E9B0",
+ "Name": "HalfDullSound"
+ },
+ {
+ "Code": "E9B1",
+ "Name": "RightDoubleQuote"
+ },
+ {
+ "Code": "E9B2",
+ "Name": "LeftDoubleQuote"
+ },
+ {
+ "Code": "E9B3",
+ "Name": "PuncKeyRightBottom"
+ },
+ {
+ "Code": "E9B4",
+ "Name": "PuncKey1"
+ },
+ {
+ "Code": "E9B5",
+ "Name": "PuncKey2"
+ },
+ {
+ "Code": "E9B6",
+ "Name": "PuncKey3"
+ },
+ {
+ "Code": "E9B7",
+ "Name": "PuncKey4"
+ },
+ {
+ "Code": "E9B8",
+ "Name": "PuncKey5"
+ },
+ {
+ "Code": "E9B9",
+ "Name": "PuncKey6"
+ },
+ {
+ "Code": "E9BA",
+ "Name": "PuncKey9"
+ },
+ {
+ "Code": "E9BB",
+ "Name": "PuncKey7"
+ },
+ {
+ "Code": "E9BC",
+ "Name": "PuncKey8"
+ },
+ {
+ "Code": "E9CA",
+ "Name": "Frigid"
+ },
+ {
+ "Code": "E9CE",
+ "Name": "Unknown"
+ },
+ {
+ "Code": "E9D2",
+ "Name": "AreaChart"
+ },
+ {
+ "Code": "E9D5",
+ "Name": "CheckList"
+ },
+ {
+ "Code": "E9D9",
+ "Name": "Diagnostic"
+ },
+ {
+ "Code": "E9E9",
+ "Name": "Equalizer"
+ },
+ {
+ "Code": "E9F3",
+ "Name": "Process"
+ },
+ {
+ "Code": "E9F5",
+ "Name": "Processing"
+ },
+ {
+ "Code": "E9F9",
+ "Name": "ReportDocument"
+ },
+ {
+ "Code": "EA0C",
+ "Name": "VideoSolid"
+ },
+ {
+ "Code": "EA0D",
+ "Name": "MixedMediaBadge"
+ },
+ {
+ "Code": "EA14",
+ "Name": "DisconnectDisplay"
+ },
+ {
+ "Code": "EA18",
+ "Name": "Shield"
+ },
+ {
+ "Code": "EA1F",
+ "Name": "Info2"
+ },
+ {
+ "Code": "EA21",
+ "Name": "ActionCenterAsterisk"
+ },
+ {
+ "Code": "EA24",
+ "Name": "Beta"
+ },
+ {
+ "Code": "EA35",
+ "Name": "SaveCopy"
+ },
+ {
+ "Code": "EA37",
+ "Name": "List"
+ },
+ {
+ "Code": "EA38",
+ "Name": "Asterisk"
+ },
+ {
+ "Code": "EA39",
+ "Name": "ErrorBadge"
+ },
+ {
+ "Code": "EA3A",
+ "Name": "CircleRing"
+ },
+ {
+ "Code": "EA3B",
+ "Name": "CircleFill"
+ },
+ {
+ "Code": "EA3C",
+ "Name": "MergeCall"
+ },
+ {
+ "Code": "EA3D",
+ "Name": "PrivateCall"
+ },
+ {
+ "Code": "EA3F",
+ "Name": "Record2"
+ },
+ {
+ "Code": "EA40",
+ "Name": "AllAppsMirrored"
+ },
+ {
+ "Code": "EA41",
+ "Name": "BookmarksMirrored"
+ },
+ {
+ "Code": "EA42",
+ "Name": "BulletedListMirrored"
+ },
+ {
+ "Code": "EA43",
+ "Name": "CallForwardInternationalMirrored"
+ },
+ {
+ "Code": "EA44",
+ "Name": "CallForwardRoamingMirrored"
+ },
+ {
+ "Code": "EA47",
+ "Name": "ChromeBackMirrored"
+ },
+ {
+ "Code": "EA48",
+ "Name": "ClearSelectionMirrored"
+ },
+ {
+ "Code": "EA49",
+ "Name": "ClosePaneMirrored"
+ },
+ {
+ "Code": "EA4A",
+ "Name": "ContactInfoMirrored"
+ },
+ {
+ "Code": "EA4B",
+ "Name": "DockRightMirrored"
+ },
+ {
+ "Code": "EA4C",
+ "Name": "DockLeftMirrored"
+ },
+ {
+ "Code": "EA4E",
+ "Name": "ExpandTileMirrored"
+ },
+ {
+ "Code": "EA4F",
+ "Name": "GoMirrored"
+ },
+ {
+ "Code": "EA50",
+ "Name": "GripperResizeMirrored"
+ },
+ {
+ "Code": "EA51",
+ "Name": "HelpMirrored"
+ },
+ {
+ "Code": "EA52",
+ "Name": "ImportMirrored"
+ },
+ {
+ "Code": "EA53",
+ "Name": "ImportAllMirrored"
+ },
+ {
+ "Code": "EA54",
+ "Name": "LeaveChatMirrored"
+ },
+ {
+ "Code": "EA55",
+ "Name": "ListMirrored"
+ },
+ {
+ "Code": "EA56",
+ "Name": "MailForwardMirrored"
+ },
+ {
+ "Code": "EA57",
+ "Name": "MailReplyMirrored"
+ },
+ {
+ "Code": "EA58",
+ "Name": "MailReplyAllMirrored"
+ },
+ {
+ "Code": "EA5B",
+ "Name": "OpenPaneMirrored"
+ },
+ {
+ "Code": "EA5C",
+ "Name": "OpenWithMirrored"
+ },
+ {
+ "Code": "EA5E",
+ "Name": "ParkingLocationMirrored"
+ },
+ {
+ "Code": "EA5F",
+ "Name": "ResizeMouseMediumMirrored"
+ },
+ {
+ "Code": "EA60",
+ "Name": "ResizeMouseSmallMirrored"
+ },
+ {
+ "Code": "EA61",
+ "Name": "ResizeMouseTallMirrored"
+ },
+ {
+ "Code": "EA62",
+ "Name": "ResizeTouchNarrowerMirrored"
+ },
+ {
+ "Code": "EA63",
+ "Name": "SendMirrored"
+ },
+ {
+ "Code": "EA64",
+ "Name": "SendFillMirrored"
+ },
+ {
+ "Code": "EA65",
+ "Name": "ShowResultsMirrored"
+ },
+ {
+ "Code": "EA69",
+ "Name": "Media"
+ },
+ {
+ "Code": "EA6A",
+ "Name": "SyncError"
+ },
+ {
+ "Code": "EA6C",
+ "Name": "Devices3"
+ },
+ {
+ "Code": "EA79",
+ "Name": "SlowMotionOn"
+ },
+ {
+ "Code": "EA80",
+ "Name": "Lightbulb"
+ },
+ {
+ "Code": "EA81",
+ "Name": "StatusCircle"
+ },
+ {
+ "Code": "EA82",
+ "Name": "StatusTriangle"
+ },
+ {
+ "Code": "EA83",
+ "Name": "StatusError"
+ },
+ {
+ "Code": "EA84",
+ "Name": "StatusWarning"
+ },
+ {
+ "Code": "EA86",
+ "Name": "Puzzle"
+ },
+ {
+ "Code": "EA89",
+ "Name": "CalendarSolid"
+ },
+ {
+ "Code": "EA8A",
+ "Name": "HomeSolid"
+ },
+ {
+ "Code": "EA8B",
+ "Name": "ParkingLocationSolid"
+ },
+ {
+ "Code": "EA8C",
+ "Name": "ContactSolid"
+ },
+ {
+ "Code": "EA8D",
+ "Name": "ConstructionSolid"
+ },
+ {
+ "Code": "EA8E",
+ "Name": "AccidentSolid"
+ },
+ {
+ "Code": "EA8F",
+ "Name": "Ringer"
+ },
+ {
+ "Code": "EA90",
+ "Name": "PDF"
+ },
+ {
+ "Code": "EA91",
+ "Name": "ThoughtBubble"
+ },
+ {
+ "Code": "EA92",
+ "Name": "HeartBroken"
+ },
+ {
+ "Code": "EA93",
+ "Name": "BatteryCharging10"
+ },
+ {
+ "Code": "EA94",
+ "Name": "BatterySaver9"
+ },
+ {
+ "Code": "EA95",
+ "Name": "BatterySaver10"
+ },
+ {
+ "Code": "EA97",
+ "Name": "CallForwardingMirrored"
+ },
+ {
+ "Code": "EA98",
+ "Name": "MultiSelectMirrored"
+ },
+ {
+ "Code": "EA99",
+ "Name": "Broom"
+ },
+ {
+ "Code": "EAC2",
+ "Name": "ForwardCall"
+ },
+ {
+ "Code": "EADF",
+ "Name": "Trackers"
+ },
+ {
+ "Code": "EAFC",
+ "Name": "Market"
+ },
+ {
+ "Code": "EB05",
+ "Name": "PieSingle"
+ },
+ {
+ "Code": "EB0F",
+ "Name": "StockUp"
+ },
+ {
+ "Code": "EB11",
+ "Name": "StockDown"
+ },
+ {
+ "Code": "EB3C",
+ "Name": "Design"
+ },
+ {
+ "Code": "EB41",
+ "Name": "Website"
+ },
+ {
+ "Code": "EB42",
+ "Name": "Drop"
+ },
+ {
+ "Code": "EB44",
+ "Name": "Radar"
+ },
+ {
+ "Code": "EB47",
+ "Name": "BusSolid"
+ },
+ {
+ "Code": "EB48",
+ "Name": "FerrySolid"
+ },
+ {
+ "Code": "EB49",
+ "Name": "StartPointSolid"
+ },
+ {
+ "Code": "EB4A",
+ "Name": "StopPointSolid"
+ },
+ {
+ "Code": "EB4B",
+ "Name": "EndPointSolid"
+ },
+ {
+ "Code": "EB4C",
+ "Name": "AirplaneSolid"
+ },
+ {
+ "Code": "EB4D",
+ "Name": "TrainSolid"
+ },
+ {
+ "Code": "EB4E",
+ "Name": "WorkSolid"
+ },
+ {
+ "Code": "EB4F",
+ "Name": "ReminderFill"
+ },
+ {
+ "Code": "EB50",
+ "Name": "Reminder"
+ },
+ {
+ "Code": "EB51",
+ "Name": "Heart"
+ },
+ {
+ "Code": "EB52",
+ "Name": "HeartFill"
+ },
+ {
+ "Code": "EB55",
+ "Name": "EthernetError"
+ },
+ {
+ "Code": "EB56",
+ "Name": "EthernetWarning"
+ },
+ {
+ "Code": "EB57",
+ "Name": "StatusConnecting1"
+ },
+ {
+ "Code": "EB58",
+ "Name": "StatusConnecting2"
+ },
+ {
+ "Code": "EB59",
+ "Name": "StatusUnsecure"
+ },
+ {
+ "Code": "EB5A",
+ "Name": "WifiError0"
+ },
+ {
+ "Code": "EB5B",
+ "Name": "WifiError1"
+ },
+ {
+ "Code": "EB5C",
+ "Name": "WifiError2"
+ },
+ {
+ "Code": "EB5D",
+ "Name": "WifiError3"
+ },
+ {
+ "Code": "EB5E",
+ "Name": "WifiError4"
+ },
+ {
+ "Code": "EB5F",
+ "Name": "WifiWarning0"
+ },
+ {
+ "Code": "EB60",
+ "Name": "WifiWarning1"
+ },
+ {
+ "Code": "EB61",
+ "Name": "WifiWarning2"
+ },
+ {
+ "Code": "EB62",
+ "Name": "WifiWarning3"
+ },
+ {
+ "Code": "EB63",
+ "Name": "WifiWarning4"
+ },
+ {
+ "Code": "EB66",
+ "Name": "Devices4"
+ },
+ {
+ "Code": "EB67",
+ "Name": "NUIIris"
+ },
+ {
+ "Code": "EB68",
+ "Name": "NUIFace"
+ },
+ {
+ "Code": "EB77",
+ "Name": "GatewayRouter"
+ },
+ {
+ "Code": "EB7E",
+ "Name": "EditMirrored"
+ },
+ {
+ "Code": "EB82",
+ "Name": "NUIFPStartSlideHand"
+ },
+ {
+ "Code": "EB83",
+ "Name": "NUIFPStartSlideAction"
+ },
+ {
+ "Code": "EB84",
+ "Name": "NUIFPContinueSlideHand"
+ },
+ {
+ "Code": "EB85",
+ "Name": "NUIFPContinueSlideAction"
+ },
+ {
+ "Code": "EB86",
+ "Name": "NUIFPRollRightHand"
+ },
+ {
+ "Code": "EB87",
+ "Name": "NUIFPRollRightHandAction"
+ },
+ {
+ "Code": "EB88",
+ "Name": "NUIFPRollLeftHand"
+ },
+ {
+ "Code": "EB89",
+ "Name": "NUIFPRollLeftAction"
+ },
+ {
+ "Code": "EB8A",
+ "Name": "NUIFPPressHand"
+ },
+ {
+ "Code": "EB8B",
+ "Name": "NUIFPPressAction"
+ },
+ {
+ "Code": "EB8C",
+ "Name": "NUIFPPressRepeatHand"
+ },
+ {
+ "Code": "EB8D",
+ "Name": "NUIFPPressRepeatAction"
+ },
+ {
+ "Code": "EB90",
+ "Name": "StatusErrorFull"
+ },
+ {
+ "Code": "EB91",
+ "Name": "TaskViewExpanded"
+ },
+ {
+ "Code": "EB95",
+ "Name": "Certificate"
+ },
+ {
+ "Code": "EB96",
+ "Name": "BackSpaceQWERTYLg"
+ },
+ {
+ "Code": "EB97",
+ "Name": "ReturnKeyLg"
+ },
+ {
+ "Code": "EB9D",
+ "Name": "FastForward"
+ },
+ {
+ "Code": "EB9E",
+ "Name": "Rewind"
+ },
+ {
+ "Code": "EB9F",
+ "Name": "Photo2"
+ },
+ {
+ "Code": "EBA0",
+ "Name": "MobBattery0"
+ },
+ {
+ "Code": "EBA1",
+ "Name": "MobBattery1"
+ },
+ {
+ "Code": "EBA2",
+ "Name": "MobBattery2"
+ },
+ {
+ "Code": "EBA3",
+ "Name": "MobBattery3"
+ },
+ {
+ "Code": "EBA4",
+ "Name": "MobBattery4"
+ },
+ {
+ "Code": "EBA5",
+ "Name": "MobBattery5"
+ },
+ {
+ "Code": "EBA6",
+ "Name": "MobBattery6"
+ },
+ {
+ "Code": "EBA7",
+ "Name": "MobBattery7"
+ },
+ {
+ "Code": "EBA8",
+ "Name": "MobBattery8"
+ },
+ {
+ "Code": "EBA9",
+ "Name": "MobBattery9"
+ },
+ {
+ "Code": "EBAA",
+ "Name": "MobBattery10"
+ },
+ {
+ "Code": "EBAB",
+ "Name": "MobBatteryCharging0"
+ },
+ {
+ "Code": "EBAC",
+ "Name": "MobBatteryCharging1"
+ },
+ {
+ "Code": "EBAD",
+ "Name": "MobBatteryCharging2"
+ },
+ {
+ "Code": "EBAE",
+ "Name": "MobBatteryCharging3"
+ },
+ {
+ "Code": "EBAF",
+ "Name": "MobBatteryCharging4"
+ },
+ {
+ "Code": "EBB0",
+ "Name": "MobBatteryCharging5"
+ },
+ {
+ "Code": "EBB1",
+ "Name": "MobBatteryCharging6"
+ },
+ {
+ "Code": "EBB2",
+ "Name": "MobBatteryCharging7"
+ },
+ {
+ "Code": "EBB3",
+ "Name": "MobBatteryCharging8"
+ },
+ {
+ "Code": "EBB4",
+ "Name": "MobBatteryCharging9"
+ },
+ {
+ "Code": "EBB5",
+ "Name": "MobBatteryCharging10"
+ },
+ {
+ "Code": "EBB6",
+ "Name": "MobBatterySaver0"
+ },
+ {
+ "Code": "EBB7",
+ "Name": "MobBatterySaver1"
+ },
+ {
+ "Code": "EBB8",
+ "Name": "MobBatterySaver2"
+ },
+ {
+ "Code": "EBB9",
+ "Name": "MobBatterySaver3"
+ },
+ {
+ "Code": "EBBA",
+ "Name": "MobBatterySaver4"
+ },
+ {
+ "Code": "EBBB",
+ "Name": "MobBatterySaver5"
+ },
+ {
+ "Code": "EBBC",
+ "Name": "MobBatterySaver6"
+ },
+ {
+ "Code": "EBBD",
+ "Name": "MobBatterySaver7"
+ },
+ {
+ "Code": "EBBE",
+ "Name": "MobBatterySaver8"
+ },
+ {
+ "Code": "EBBF",
+ "Name": "MobBatterySaver9"
+ },
+ {
+ "Code": "EBC0",
+ "Name": "MobBatterySaver10"
+ },
+ {
+ "Code": "EBC3",
+ "Name": "DictionaryCloud"
+ },
+ {
+ "Code": "EBC4",
+ "Name": "ResetDrive"
+ },
+ {
+ "Code": "EBC5",
+ "Name": "VolumeBars"
+ },
+ {
+ "Code": "EBC6",
+ "Name": "Project"
+ },
+ {
+ "Code": "EBD2",
+ "Name": "AdjustHologram"
+ },
+ {
+ "Code": "EBD3",
+ "Name": "CloudDownload"
+ },
+ {
+ "Code": "EBD4",
+ "Name": "MobWifiCallBars"
+ },
+ {
+ "Code": "EBD5",
+ "Name": "MobWifiCall0"
+ },
+ {
+ "Code": "EBD6",
+ "Name": "MobWifiCall1"
+ },
+ {
+ "Code": "EBD7",
+ "Name": "MobWifiCall2"
+ },
+ {
+ "Code": "EBD8",
+ "Name": "MobWifiCall3"
+ },
+ {
+ "Code": "EBD9",
+ "Name": "MobWifiCall4"
+ },
+ {
+ "Code": "EBDA",
+ "Name": "Family"
+ },
+ {
+ "Code": "EBDB",
+ "Name": "LockFeedback"
+ },
+ {
+ "Code": "EBDE",
+ "Name": "DeviceDiscovery"
+ },
+ {
+ "Code": "EBE6",
+ "Name": "WindDirection"
+ },
+ {
+ "Code": "EBE7",
+ "Name": "RightArrowKeyTime0"
+ },
+ {
+ "Code": "EBE8",
+ "Name": "Bug"
+ },
+ {
+ "Code": "EBFC",
+ "Name": "TabletMode"
+ },
+ {
+ "Code": "EBFD",
+ "Name": "StatusCircleLeft"
+ },
+ {
+ "Code": "EBFE",
+ "Name": "StatusTriangleLeft"
+ },
+ {
+ "Code": "EBFF",
+ "Name": "StatusErrorLeft"
+ },
+ {
+ "Code": "EC00",
+ "Name": "StatusWarningLeft"
+ },
+ {
+ "Code": "EC02",
+ "Name": "MobBatteryUnknown"
+ },
+ {
+ "Code": "EC05",
+ "Name": "NetworkTower"
+ },
+ {
+ "Code": "EC06",
+ "Name": "CityNext"
+ },
+ {
+ "Code": "EC07",
+ "Name": "CityNext2"
+ },
+ {
+ "Code": "EC08",
+ "Name": "Courthouse"
+ },
+ {
+ "Code": "EC09",
+ "Name": "Groceries"
+ },
+ {
+ "Code": "EC0A",
+ "Name": "Sustainable"
+ },
+ {
+ "Code": "EC0B",
+ "Name": "BuildingEnergy"
+ },
+ {
+ "Code": "EC11",
+ "Name": "ToggleFilled"
+ },
+ {
+ "Code": "EC12",
+ "Name": "ToggleBorder"
+ },
+ {
+ "Code": "EC13",
+ "Name": "SliderThumb"
+ },
+ {
+ "Code": "EC14",
+ "Name": "ToggleThumb"
+ },
+ {
+ "Code": "EC15",
+ "Name": "MiracastLogoSmall"
+ },
+ {
+ "Code": "EC16",
+ "Name": "MiracastLogoLarge"
+ },
+ {
+ "Code": "EC19",
+ "Name": "PLAP"
+ },
+ {
+ "Code": "EC1B",
+ "Name": "Badge"
+ },
+ {
+ "Code": "EC1E",
+ "Name": "SignalRoaming"
+ },
+ {
+ "Code": "EC20",
+ "Name": "MobileLocked"
+ },
+ {
+ "Code": "EC24",
+ "Name": "InsiderHubApp"
+ },
+ {
+ "Code": "EC25",
+ "Name": "PersonalFolder"
+ },
+ {
+ "Code": "EC26",
+ "Name": "HomeGroup"
+ },
+ {
+ "Code": "EC27",
+ "Name": "MyNetwork"
+ },
+ {
+ "Code": "EC31",
+ "Name": "KeyboardFull"
+ },
+ {
+ "Code": "EC32",
+ "Name": "Cafe"
+ },
+ {
+ "Code": "EC37",
+ "Name": "MobSignal1"
+ },
+ {
+ "Code": "EC38",
+ "Name": "MobSignal2"
+ },
+ {
+ "Code": "EC39",
+ "Name": "MobSignal3"
+ },
+ {
+ "Code": "EC3A",
+ "Name": "MobSignal4"
+ },
+ {
+ "Code": "EC3B",
+ "Name": "MobSignal5"
+ },
+ {
+ "Code": "EC3C",
+ "Name": "MobWifi1"
+ },
+ {
+ "Code": "EC3D",
+ "Name": "MobWifi2"
+ },
+ {
+ "Code": "EC3E",
+ "Name": "MobWifi3"
+ },
+ {
+ "Code": "EC3F",
+ "Name": "MobWifi4"
+ },
+ {
+ "Code": "EC40",
+ "Name": "MobAirplane"
+ },
+ {
+ "Code": "EC41",
+ "Name": "MobBluetooth"
+ },
+ {
+ "Code": "EC42",
+ "Name": "MobActionCenter"
+ },
+ {
+ "Code": "EC43",
+ "Name": "MobLocation"
+ },
+ {
+ "Code": "EC44",
+ "Name": "MobWifiHotspot"
+ },
+ {
+ "Code": "EC45",
+ "Name": "LanguageJpn"
+ },
+ {
+ "Code": "EC46",
+ "Name": "MobQuietHours"
+ },
+ {
+ "Code": "EC47",
+ "Name": "MobDrivingMode"
+ },
+ {
+ "Code": "EC48",
+ "Name": "SpeedOff"
+ },
+ {
+ "Code": "EC49",
+ "Name": "SpeedMedium"
+ },
+ {
+ "Code": "EC4A",
+ "Name": "SpeedHigh"
+ },
+ {
+ "Code": "EC4E",
+ "Name": "ThisPC"
+ },
+ {
+ "Code": "EC4F",
+ "Name": "MusicNote"
+ },
+ {
+ "Code": "EC50",
+ "Name": "FileExplorer"
+ },
+ {
+ "Code": "EC51",
+ "Name": "FileExplorerApp"
+ },
+ {
+ "Code": "EC52",
+ "Name": "LeftArrowKeyTime0"
+ },
+ {
+ "Code": "EC54",
+ "Name": "MicOff"
+ },
+ {
+ "Code": "EC55",
+ "Name": "MicSleep"
+ },
+ {
+ "Code": "EC56",
+ "Name": "MicError"
+ },
+ {
+ "Code": "EC57",
+ "Name": "PlaybackRate1x"
+ },
+ {
+ "Code": "EC58",
+ "Name": "PlaybackRateOther"
+ },
+ {
+ "Code": "EC59",
+ "Name": "CashDrawer"
+ },
+ {
+ "Code": "EC5A",
+ "Name": "BarcodeScanner"
+ },
+ {
+ "Code": "EC5B",
+ "Name": "ReceiptPrinter"
+ },
+ {
+ "Code": "EC5C",
+ "Name": "MagStripeReader"
+ },
+ {
+ "Code": "EC61",
+ "Name": "CompletedSolid"
+ },
+ {
+ "Code": "EC64",
+ "Name": "CompanionApp"
+ },
+ {
+ "Code": "EC6C",
+ "Name": "Favicon2"
+ },
+ {
+ "Code": "EC6D",
+ "Name": "SwipeRevealArt"
+ },
+ {
+ "Code": "EC71",
+ "Name": "MicOn"
+ },
+ {
+ "Code": "EC72",
+ "Name": "MicClipping"
+ },
+ {
+ "Code": "EC74",
+ "Name": "TabletSelected"
+ },
+ {
+ "Code": "EC75",
+ "Name": "MobileSelected"
+ },
+ {
+ "Code": "EC76",
+ "Name": "LaptopSelected"
+ },
+ {
+ "Code": "EC77",
+ "Name": "TVMonitorSelected"
+ },
+ {
+ "Code": "EC7A",
+ "Name": "DeveloperTools"
+ },
+ {
+ "Code": "EC7E",
+ "Name": "MobCallForwarding"
+ },
+ {
+ "Code": "EC7F",
+ "Name": "MobCallForwardingMirrored"
+ },
+ {
+ "Code": "EC80",
+ "Name": "BodyCam"
+ },
+ {
+ "Code": "EC81",
+ "Name": "PoliceCar"
+ },
+ {
+ "Code": "EC87",
+ "Name": "Draw"
+ },
+ {
+ "Code": "EC88",
+ "Name": "DrawSolid"
+ },
+ {
+ "Code": "EC8A",
+ "Name": "LowerBrightness"
+ },
+ {
+ "Code": "EC8F",
+ "Name": "ScrollUpDown"
+ },
+ {
+ "Code": "EC92",
+ "Name": "DateTime"
+ },
+ {
+ "Code": "EC94",
+ "Name": "HoloLens"
+ },
+ {
+ "Code": "ECA5",
+ "Name": "Tiles"
+ },
+ {
+ "Code": "ECA7",
+ "Name": "PartyLeader"
+ },
+ {
+ "Code": "ECAA",
+ "Name": "AppIconDefault"
+ },
+ {
+ "Code": "ECAD",
+ "Name": "Calories"
+ },
+ {
+ "Code": "ECAF",
+ "Name": "POI"
+ },
+ {
+ "Code": "ECB9",
+ "Name": "BandBattery0"
+ },
+ {
+ "Code": "ECBA",
+ "Name": "BandBattery1"
+ },
+ {
+ "Code": "ECBB",
+ "Name": "BandBattery2"
+ },
+ {
+ "Code": "ECBC",
+ "Name": "BandBattery3"
+ },
+ {
+ "Code": "ECBD",
+ "Name": "BandBattery4"
+ },
+ {
+ "Code": "ECBE",
+ "Name": "BandBattery5"
+ },
+ {
+ "Code": "ECBF",
+ "Name": "BandBattery6"
+ },
+ {
+ "Code": "ECC4",
+ "Name": "AddSurfaceHub"
+ },
+ {
+ "Code": "ECC5",
+ "Name": "DevUpdate"
+ },
+ {
+ "Code": "ECC6",
+ "Name": "Unit"
+ },
+ {
+ "Code": "ECC8",
+ "Name": "AddTo"
+ },
+ {
+ "Code": "ECC9",
+ "Name": "RemoveFrom"
+ },
+ {
+ "Code": "ECCA",
+ "Name": "RadioBtnOff"
+ },
+ {
+ "Code": "ECCB",
+ "Name": "RadioBtnOn"
+ },
+ {
+ "Code": "ECCC",
+ "Name": "RadioBullet2"
+ },
+ {
+ "Code": "ECCD",
+ "Name": "ExploreContent"
+ },
+ {
+ "Code": "ECE4",
+ "Name": "Blocked2"
+ },
+ {
+ "Code": "ECE7",
+ "Name": "ScrollMode"
+ },
+ {
+ "Code": "ECE8",
+ "Name": "ZoomMode"
+ },
+ {
+ "Code": "ECE9",
+ "Name": "PanMode"
+ },
+ {
+ "Code": "ECF0",
+ "Name": "WiredUSB"
+ },
+ {
+ "Code": "ECF1",
+ "Name": "WirelessUSB"
+ },
+ {
+ "Code": "ECF3",
+ "Name": "USBSafeConnect"
+ },
+ {
+ "Code": "ED0C",
+ "Name": "ActionCenterNotificationMirrored"
+ },
+ {
+ "Code": "ED0D",
+ "Name": "ActionCenterMirrored"
+ },
+ {
+ "Code": "ED0E",
+ "Name": "SubscriptionAdd"
+ },
+ {
+ "Code": "ED10",
+ "Name": "ResetDevice"
+ },
+ {
+ "Code": "ED11",
+ "Name": "SubscriptionAddMirrored"
+ },
+ {
+ "Code": "ED14",
+ "Name": "QRCode"
+ },
+ {
+ "Code": "ED15",
+ "Name": "Feedback"
+ },
+ {
+ "Code": "ED1A",
+ "Name": "Hide"
+ },
+ {
+ "Code": "ED1E",
+ "Name": "Subtitles"
+ },
+ {
+ "Code": "ED1F",
+ "Name": "SubtitlesAudio"
+ },
+ {
+ "Code": "ED25",
+ "Name": "OpenFolderHorizontal"
+ },
+ {
+ "Code": "ED28",
+ "Name": "CalendarMirrored"
+ },
+ {
+ "Code": "ED2A",
+ "Name": "MobeSIM"
+ },
+ {
+ "Code": "ED2B",
+ "Name": "MobeSIMNoProfile"
+ },
+ {
+ "Code": "ED2C",
+ "Name": "MobeSIMLocked"
+ },
+ {
+ "Code": "ED2D",
+ "Name": "MobeSIMBusy"
+ },
+ {
+ "Code": "ED2E",
+ "Name": "SignalError"
+ },
+ {
+ "Code": "ED2F",
+ "Name": "StreamingEnterprise"
+ },
+ {
+ "Code": "ED30",
+ "Name": "Headphone0"
+ },
+ {
+ "Code": "ED31",
+ "Name": "Headphone1"
+ },
+ {
+ "Code": "ED32",
+ "Name": "Headphone2"
+ },
+ {
+ "Code": "ED33",
+ "Name": "Headphone3"
+ },
+ {
+ "Code": "ED35",
+ "Name": "Apps"
+ },
+ {
+ "Code": "ED39",
+ "Name": "KeyboardBrightness"
+ },
+ {
+ "Code": "ED3A",
+ "Name": "KeyboardLowerBrightness"
+ },
+ {
+ "Code": "ED3C",
+ "Name": "SkipBack10"
+ },
+ {
+ "Code": "ED3D",
+ "Name": "SkipForward30"
+ },
+ {
+ "Code": "ED41",
+ "Name": "TreeFolderFolder"
+ },
+ {
+ "Code": "ED42",
+ "Name": "TreeFolderFolderFill"
+ },
+ {
+ "Code": "ED43",
+ "Name": "TreeFolderFolderOpen"
+ },
+ {
+ "Code": "ED44",
+ "Name": "TreeFolderFolderOpenFill"
+ },
+ {
+ "Code": "ED47",
+ "Name": "MultimediaDMP"
+ },
+ {
+ "Code": "ED4C",
+ "Name": "KeyboardOneHanded"
+ },
+ {
+ "Code": "ED4D",
+ "Name": "Narrator"
+ },
+ {
+ "Code": "ED53",
+ "Name": "EmojiTabPeople"
+ },
+ {
+ "Code": "ED54",
+ "Name": "EmojiTabSmilesAnimals"
+ },
+ {
+ "Code": "ED55",
+ "Name": "EmojiTabCelebrationObjects"
+ },
+ {
+ "Code": "ED56",
+ "Name": "EmojiTabFoodPlants"
+ },
+ {
+ "Code": "ED57",
+ "Name": "EmojiTabTransitPlaces"
+ },
+ {
+ "Code": "ED58",
+ "Name": "EmojiTabSymbols"
+ },
+ {
+ "Code": "ED59",
+ "Name": "EmojiTabTextSmiles"
+ },
+ {
+ "Code": "ED5A",
+ "Name": "EmojiTabFavorites"
+ },
+ {
+ "Code": "ED5B",
+ "Name": "EmojiSwatch"
+ },
+ {
+ "Code": "ED5C",
+ "Name": "ConnectApp"
+ },
+ {
+ "Code": "ED5D",
+ "Name": "CompanionDeviceFramework"
+ },
+ {
+ "Code": "ED5E",
+ "Name": "Ruler"
+ },
+ {
+ "Code": "ED5F",
+ "Name": "FingerInking"
+ },
+ {
+ "Code": "ED60",
+ "Name": "StrokeErase"
+ },
+ {
+ "Code": "ED61",
+ "Name": "PointErase"
+ },
+ {
+ "Code": "ED62",
+ "Name": "ClearAllInk"
+ },
+ {
+ "Code": "ED63",
+ "Name": "Pencil"
+ },
+ {
+ "Code": "ED64",
+ "Name": "Marker"
+ },
+ {
+ "Code": "ED65",
+ "Name": "InkingCaret"
+ },
+ {
+ "Code": "ED66",
+ "Name": "InkingColorOutline"
+ },
+ {
+ "Code": "ED67",
+ "Name": "InkingColorFill"
+ },
+ {
+ "Code": "EDA2",
+ "Name": "HardDrive"
+ },
+ {
+ "Code": "EDA3",
+ "Name": "NetworkAdapter"
+ },
+ {
+ "Code": "EDA4",
+ "Name": "Touchscreen"
+ },
+ {
+ "Code": "EDA5",
+ "Name": "NetworkPrinter"
+ },
+ {
+ "Code": "EDA6",
+ "Name": "CloudPrinter"
+ },
+ {
+ "Code": "EDA7",
+ "Name": "KeyboardShortcut"
+ },
+ {
+ "Code": "EDA8",
+ "Name": "BrushSize"
+ },
+ {
+ "Code": "EDA9",
+ "Name": "NarratorForward"
+ },
+ {
+ "Code": "EDAA",
+ "Name": "NarratorForwardMirrored"
+ },
+ {
+ "Code": "EDAB",
+ "Name": "SyncBadge12"
+ },
+ {
+ "Code": "EDAC",
+ "Name": "RingerBadge12"
+ },
+ {
+ "Code": "EDAD",
+ "Name": "AsteriskBadge12"
+ },
+ {
+ "Code": "EDAE",
+ "Name": "ErrorBadge12"
+ },
+ {
+ "Code": "EDAF",
+ "Name": "CircleRingBadge12"
+ },
+ {
+ "Code": "EDB0",
+ "Name": "CircleFillBadge12"
+ },
+ {
+ "Code": "EDB1",
+ "Name": "ImportantBadge12"
+ },
+ {
+ "Code": "EDB3",
+ "Name": "MailBadge12"
+ },
+ {
+ "Code": "EDB4",
+ "Name": "PauseBadge12"
+ },
+ {
+ "Code": "EDB5",
+ "Name": "PlayBadge12"
+ },
+ {
+ "Code": "EDC6",
+ "Name": "PenWorkspace"
+ },
+ {
+ "Code": "EDD5",
+ "Name": "CaretLeft8"
+ },
+ {
+ "Code": "EDD6",
+ "Name": "CaretRight8"
+ },
+ {
+ "Code": "EDD7",
+ "Name": "CaretUp8"
+ },
+ {
+ "Code": "EDD8",
+ "Name": "CaretDown8"
+ },
+ {
+ "Code": "EDD9",
+ "Name": "CaretLeftSolid8"
+ },
+ {
+ "Code": "EDDA",
+ "Name": "CaretRightSolid8"
+ },
+ {
+ "Code": "EDDB",
+ "Name": "CaretUpSolid8"
+ },
+ {
+ "Code": "EDDC",
+ "Name": "CaretDownSolid8"
+ },
+ {
+ "Code": "EDE0",
+ "Name": "Strikethrough"
+ },
+ {
+ "Code": "EDE1",
+ "Name": "Export"
+ },
+ {
+ "Code": "EDE2",
+ "Name": "ExportMirrored"
+ },
+ {
+ "Code": "EDE3",
+ "Name": "ButtonMenu"
+ },
+ {
+ "Code": "EDE4",
+ "Name": "CloudSearch"
+ },
+ {
+ "Code": "EDE5",
+ "Name": "PinyinIMELogo"
+ },
+ {
+ "Code": "EDFB",
+ "Name": "CalligraphyPen"
+ },
+ {
+ "Code": "EE35",
+ "Name": "ReplyMirrored"
+ },
+ {
+ "Code": "EE3F",
+ "Name": "LockscreenDesktop"
+ },
+ {
+ "Code": "EE40",
+ "Name": "TaskViewSettings"
+ },
+ {
+ "Code": "EE47",
+ "Name": "MiniExpand2Mirrored"
+ },
+ {
+ "Code": "EE49",
+ "Name": "MiniContract2Mirrored"
+ },
+ {
+ "Code": "EE4A",
+ "Name": "Play36"
+ },
+ {
+ "Code": "EE56",
+ "Name": "PenPalette"
+ },
+ {
+ "Code": "EE57",
+ "Name": "GuestUser"
+ },
+ {
+ "Code": "EE63",
+ "Name": "SettingsBattery"
+ },
+ {
+ "Code": "EE64",
+ "Name": "TaskbarPhone"
+ },
+ {
+ "Code": "EE65",
+ "Name": "LockScreenGlance"
+ },
+ {
+ "Code": "EE6F",
+ "Name": "GenericScan"
+ },
+ {
+ "Code": "EE71",
+ "Name": "ImageExport"
+ },
+ {
+ "Code": "EE77",
+ "Name": "WifiEthernet"
+ },
+ {
+ "Code": "EE79",
+ "Name": "ActionCenterQuiet"
+ },
+ {
+ "Code": "EE7A",
+ "Name": "ActionCenterQuietNotification"
+ },
+ {
+ "Code": "EE92",
+ "Name": "TrackersMirrored"
+ },
+ {
+ "Code": "EE93",
+ "Name": "DateTimeMirrored"
+ },
+ {
+ "Code": "EE94",
+ "Name": "Wheel"
+ },
+ {
+ "Code": "EEA3",
+ "Name": "VirtualMachineGroup"
+ },
+ {
+ "Code": "EECA",
+ "Name": "ButtonView2"
+ },
+ {
+ "Code": "EF15",
+ "Name": "PenWorkspaceMirrored"
+ },
+ {
+ "Code": "EF16",
+ "Name": "PenPaletteMirrored"
+ },
+ {
+ "Code": "EF17",
+ "Name": "StrokeEraseMirrored"
+ },
+ {
+ "Code": "EF18",
+ "Name": "PointEraseMirrored"
+ },
+ {
+ "Code": "EF19",
+ "Name": "ClearAllInkMirrored"
+ },
+ {
+ "Code": "EF1F",
+ "Name": "BackgroundToggle"
+ },
+ {
+ "Code": "EF20",
+ "Name": "Marquee"
+ },
+ {
+ "Code": "EF2C",
+ "Name": "ChromeCloseContrast"
+ },
+ {
+ "Code": "EF2D",
+ "Name": "ChromeMinimizeContrast"
+ },
+ {
+ "Code": "EF2E",
+ "Name": "ChromeMaximizeContrast"
+ },
+ {
+ "Code": "EF2F",
+ "Name": "ChromeRestoreContrast"
+ },
+ {
+ "Code": "EF31",
+ "Name": "TrafficLight"
+ },
+ {
+ "Code": "EF3B",
+ "Name": "Replay"
+ },
+ {
+ "Code": "EF3C",
+ "Name": "Eyedropper"
+ },
+ {
+ "Code": "EF3D",
+ "Name": "LineDisplay"
+ },
+ {
+ "Code": "EF3E",
+ "Name": "PINPad"
+ },
+ {
+ "Code": "EF3F",
+ "Name": "SignatureCapture"
+ },
+ {
+ "Code": "EF40",
+ "Name": "ChipCardCreditCardReader"
+ },
+ {
+ "Code": "EF42",
+ "Name": "MarketDown"
+ },
+ {
+ "Code": "EF58",
+ "Name": "PlayerSettings"
+ },
+ {
+ "Code": "EF6B",
+ "Name": "LandscapeOrientation"
+ },
+ {
+ "Code": "EF90",
+ "Name": "Flow"
+ },
+ {
+ "Code": "EFA5",
+ "Name": "Touchpad"
+ },
+ {
+ "Code": "EFA9",
+ "Name": "Speech"
+ },
+ {
+ "Code": "F000",
+ "Name": "KnowledgeArticle"
+ },
+ {
+ "Code": "F003",
+ "Name": "Relationship"
+ },
+ {
+ "Code": "F012",
+ "Name": "ZipFolder"
+ },
+ {
+ "Code": "F080",
+ "Name": "DefaultAPN"
+ },
+ {
+ "Code": "F081",
+ "Name": "UserAPN"
+ },
+ {
+ "Code": "F085",
+ "Name": "DoublePinyin"
+ },
+ {
+ "Code": "F08C",
+ "Name": "BlueLight"
+ },
+ {
+ "Code": "F08D",
+ "Name": "CaretSolidLeft"
+ },
+ {
+ "Code": "F08E",
+ "Name": "CaretSolidDown"
+ },
+ {
+ "Code": "F08F",
+ "Name": "CaretSolidRight"
+ },
+ {
+ "Code": "F090",
+ "Name": "CaretSolidUp"
+ },
+ {
+ "Code": "F093",
+ "Name": "ButtonA"
+ },
+ {
+ "Code": "F094",
+ "Name": "ButtonB"
+ },
+ {
+ "Code": "F095",
+ "Name": "ButtonY"
+ },
+ {
+ "Code": "F096",
+ "Name": "ButtonX"
+ },
+ {
+ "Code": "F0AD",
+ "Name": "ArrowUp8"
+ },
+ {
+ "Code": "F0AE",
+ "Name": "ArrowDown8"
+ },
+ {
+ "Code": "F0AF",
+ "Name": "ArrowRight8"
+ },
+ {
+ "Code": "F0B0",
+ "Name": "ArrowLeft8"
+ },
+ {
+ "Code": "F0B2",
+ "Name": "QuarentinedItems"
+ },
+ {
+ "Code": "F0B3",
+ "Name": "QuarentinedItemsMirrored"
+ },
+ {
+ "Code": "F0B4",
+ "Name": "Protractor"
+ },
+ {
+ "Code": "F0B5",
+ "Name": "ChecklistMirrored"
+ },
+ {
+ "Code": "F0B6",
+ "Name": "StatusCircle7"
+ },
+ {
+ "Code": "F0B7",
+ "Name": "StatusCheckmark7"
+ },
+ {
+ "Code": "F0B8",
+ "Name": "StatusErrorCircle7"
+ },
+ {
+ "Code": "F0B9",
+ "Name": "Connected"
+ },
+ {
+ "Code": "F0C6",
+ "Name": "PencilFill"
+ },
+ {
+ "Code": "F0C7",
+ "Name": "CalligraphyFill"
+ },
+ {
+ "Code": "F0CA",
+ "Name": "QuarterStarLeft"
+ },
+ {
+ "Code": "F0CB",
+ "Name": "QuarterStarRight"
+ },
+ {
+ "Code": "F0CC",
+ "Name": "ThreeQuarterStarLeft"
+ },
+ {
+ "Code": "F0CD",
+ "Name": "ThreeQuarterStarRight"
+ },
+ {
+ "Code": "F0CE",
+ "Name": "QuietHoursBadge12"
+ },
+ {
+ "Code": "F0D2",
+ "Name": "BackMirrored"
+ },
+ {
+ "Code": "F0D3",
+ "Name": "ForwardMirrored"
+ },
+ {
+ "Code": "F0D5",
+ "Name": "ChromeBackContrast"
+ },
+ {
+ "Code": "F0D6",
+ "Name": "ChromeBackContrastMirrored"
+ },
+ {
+ "Code": "F0D7",
+ "Name": "ChromeBackToWindowContrast"
+ },
+ {
+ "Code": "F0D8",
+ "Name": "ChromeFullScreenContrast"
+ },
+ {
+ "Code": "F0E2",
+ "Name": "GridView"
+ },
+ {
+ "Code": "F0E3",
+ "Name": "ClipboardList"
+ },
+ {
+ "Code": "F0E4",
+ "Name": "ClipboardListMirrored"
+ },
+ {
+ "Code": "F0E5",
+ "Name": "OutlineQuarterStarLeft"
+ },
+ {
+ "Code": "F0E6",
+ "Name": "OutlineQuarterStarRight"
+ },
+ {
+ "Code": "F0E7",
+ "Name": "OutlineHalfStarLeft"
+ },
+ {
+ "Code": "F0E8",
+ "Name": "OutlineHalfStarRight"
+ },
+ {
+ "Code": "F0E9",
+ "Name": "OutlineThreeQuarterStarLeft"
+ },
+ {
+ "Code": "F0EA",
+ "Name": "OutlineThreeQuarterStarRight"
+ },
+ {
+ "Code": "F0EB",
+ "Name": "SpatialVolume0"
+ },
+ {
+ "Code": "F0EC",
+ "Name": "SpatialVolume1"
+ },
+ {
+ "Code": "F0ED",
+ "Name": "SpatialVolume2"
+ },
+ {
+ "Code": "F0EE",
+ "Name": "SpatialVolume3"
+ },
+ {
+ "Code": "F0EF",
+ "Name": "ApplicationGuard"
+ },
+ {
+ "Code": "F0F7",
+ "Name": "OutlineStarLeftHalf"
+ },
+ {
+ "Code": "F0F8",
+ "Name": "OutlineStarRightHalf"
+ },
+ {
+ "Code": "F0F9",
+ "Name": "ChromeAnnotateContrast"
+ },
+ {
+ "Code": "F0FB",
+ "Name": "DefenderBadge12"
+ },
+ {
+ "Code": "F103",
+ "Name": "DetachablePC"
+ },
+ {
+ "Code": "F108",
+ "Name": "LeftStick"
+ },
+ {
+ "Code": "F109",
+ "Name": "RightStick"
+ },
+ {
+ "Code": "F10A",
+ "Name": "TriggerLeft"
+ },
+ {
+ "Code": "F10B",
+ "Name": "TriggerRight"
+ },
+ {
+ "Code": "F10C",
+ "Name": "BumperLeft"
+ },
+ {
+ "Code": "F10D",
+ "Name": "BumperRight"
+ },
+ {
+ "Code": "F10E",
+ "Name": "Dpad"
+ },
+ {
+ "Code": "F110",
+ "Name": "EnglishPunctuation"
+ },
+ {
+ "Code": "F111",
+ "Name": "ChinesePunctuation"
+ },
+ {
+ "Code": "F119",
+ "Name": "HMD"
+ },
+ {
+ "Code": "F11B",
+ "Name": "CtrlSpatialRight"
+ },
+ {
+ "Code": "F126",
+ "Name": "PaginationDotOutline10"
+ },
+ {
+ "Code": "F127",
+ "Name": "PaginationDotSolid10"
+ },
+ {
+ "Code": "F128",
+ "Name": "StrokeErase2"
+ },
+ {
+ "Code": "F129",
+ "Name": "SmallErase"
+ },
+ {
+ "Code": "F12A",
+ "Name": "LargeErase"
+ },
+ {
+ "Code": "F12B",
+ "Name": "FolderHorizontal"
+ },
+ {
+ "Code": "F12E",
+ "Name": "MicrophoneListening"
+ },
+ {
+ "Code": "F12F",
+ "Name": "StatusExclamationCircle7"
+ },
+ {
+ "Code": "F131",
+ "Name": "Video360"
+ },
+ {
+ "Code": "F133",
+ "Name": "GiftboxOpen"
+ },
+ {
+ "Code": "F136",
+ "Name": "StatusCircleOuter"
+ },
+ {
+ "Code": "F137",
+ "Name": "StatusCircleInner"
+ },
+ {
+ "Code": "F138",
+ "Name": "StatusCircleRing"
+ },
+ {
+ "Code": "F139",
+ "Name": "StatusTriangleOuter"
+ },
+ {
+ "Code": "F13A",
+ "Name": "StatusTriangleInner"
+ },
+ {
+ "Code": "F13B",
+ "Name": "StatusTriangleExclamation"
+ },
+ {
+ "Code": "F13C",
+ "Name": "StatusCircleExclamation"
+ },
+ {
+ "Code": "F13D",
+ "Name": "StatusCircleErrorX"
+ },
+ {
+ "Code": "F13E",
+ "Name": "StatusCircleCheckmark"
+ },
+ {
+ "Code": "F13F",
+ "Name": "StatusCircleInfo"
+ },
+ {
+ "Code": "F140",
+ "Name": "StatusCircleBlock"
+ },
+ {
+ "Code": "F141",
+ "Name": "StatusCircleBlock2"
+ },
+ {
+ "Code": "F142",
+ "Name": "StatusCircleQuestionMark"
+ },
+ {
+ "Code": "F143",
+ "Name": "StatusCircleSync"
+ },
+ {
+ "Code": "F146",
+ "Name": "Dial1"
+ },
+ {
+ "Code": "F147",
+ "Name": "Dial2"
+ },
+ {
+ "Code": "F148",
+ "Name": "Dial3"
+ },
+ {
+ "Code": "F149",
+ "Name": "Dial4"
+ },
+ {
+ "Code": "F14A",
+ "Name": "Dial5"
+ },
+ {
+ "Code": "F14B",
+ "Name": "Dial6"
+ },
+ {
+ "Code": "F14C",
+ "Name": "Dial7"
+ },
+ {
+ "Code": "F14D",
+ "Name": "Dial8"
+ },
+ {
+ "Code": "F14E",
+ "Name": "Dial9"
+ },
+ {
+ "Code": "F14F",
+ "Name": "Dial10"
+ },
+ {
+ "Code": "F150",
+ "Name": "Dial11"
+ },
+ {
+ "Code": "F151",
+ "Name": "Dial12"
+ },
+ {
+ "Code": "F152",
+ "Name": "Dial13"
+ },
+ {
+ "Code": "F153",
+ "Name": "Dial14"
+ },
+ {
+ "Code": "F154",
+ "Name": "Dial15"
+ },
+ {
+ "Code": "F155",
+ "Name": "Dial16"
+ },
+ {
+ "Code": "F156",
+ "Name": "DialShape1"
+ },
+ {
+ "Code": "F157",
+ "Name": "DialShape2"
+ },
+ {
+ "Code": "F158",
+ "Name": "DialShape3"
+ },
+ {
+ "Code": "F159",
+ "Name": "DialShape4"
+ },
+ {
+ "Code": "F15F",
+ "Name": "ClosedCaptionsInternational"
+ },
+ {
+ "Code": "F161",
+ "Name": "TollSolid"
+ },
+ {
+ "Code": "F163",
+ "Name": "TrafficCongestionSolid"
+ },
+ {
+ "Code": "F164",
+ "Name": "ExploreContentSingle"
+ },
+ {
+ "Code": "F165",
+ "Name": "CollapseContent"
+ },
+ {
+ "Code": "F166",
+ "Name": "CollapseContentSingle"
+ },
+ {
+ "Code": "F167",
+ "Name": "InfoSolid"
+ },
+ {
+ "Code": "F168",
+ "Name": "GroupList"
+ },
+ {
+ "Code": "F169",
+ "Name": "CaretBottomRightSolidCenter8"
+ },
+ {
+ "Code": "F16A",
+ "Name": "ProgressRingDots"
+ },
+ {
+ "Code": "F16B",
+ "Name": "Checkbox14"
+ },
+ {
+ "Code": "F16C",
+ "Name": "CheckboxComposite14"
+ },
+ {
+ "Code": "F16D",
+ "Name": "CheckboxIndeterminateCombo14"
+ },
+ {
+ "Code": "F16E",
+ "Name": "CheckboxIndeterminateCombo"
+ },
+ {
+ "Code": "F175",
+ "Name": "StatusPause7"
+ },
+ {
+ "Code": "F17F",
+ "Name": "CharacterAppearance"
+ },
+ {
+ "Code": "F180",
+ "Name": "Lexicon"
+ },
+ {
+ "Code": "F182",
+ "Name": "ScreenTime"
+ },
+ {
+ "Code": "F191",
+ "Name": "HeadlessDevice"
+ },
+ {
+ "Code": "F193",
+ "Name": "NetworkSharing"
+ },
+ {
+ "Code": "F19D",
+ "Name": "EyeGaze"
+ },
+ {
+ "Code": "F19E",
+ "Name": "ToggleLeft"
+ },
+ {
+ "Code": "F19F",
+ "Name": "ToggleRight"
+ },
+ {
+ "Code": "F1AD",
+ "Name": "WindowsInsider"
+ },
+ {
+ "Code": "F1CB",
+ "Name": "ChromeSwitch"
+ },
+ {
+ "Code": "F1CC",
+ "Name": "ChromeSwitchContast"
+ },
+ {
+ "Code": "F1D8",
+ "Name": "StatusCheckmark"
+ },
+ {
+ "Code": "F1D9",
+ "Name": "StatusCheckmarkLeft"
+ },
+ {
+ "Code": "F20C",
+ "Name": "KeyboardLeftAligned"
+ },
+ {
+ "Code": "F20D",
+ "Name": "KeyboardRightAligned"
+ },
+ {
+ "Code": "F210",
+ "Name": "KeyboardSettings"
+ },
+ {
+ "Code": "F211",
+ "Name": "NetworkPhysical"
+ },
+ {
+ "Code": "F22C",
+ "Name": "IOT"
+ },
+ {
+ "Code": "F22E",
+ "Name": "UnknownMirrored"
+ },
+ {
+ "Code": "F246",
+ "Name": "ViewDashboard"
+ },
+ {
+ "Code": "F259",
+ "Name": "ExploitProtectionSettings"
+ },
+ {
+ "Code": "F260",
+ "Name": "KeyboardNarrow"
+ },
+ {
+ "Code": "F261",
+ "Name": "Keyboard12Key"
+ },
+ {
+ "Code": "F26B",
+ "Name": "KeyboardDock"
+ },
+ {
+ "Code": "F26C",
+ "Name": "KeyboardUndock"
+ },
+ {
+ "Code": "F26D",
+ "Name": "KeyboardLeftDock"
+ },
+ {
+ "Code": "F26E",
+ "Name": "KeyboardRightDock"
+ },
+ {
+ "Code": "F270",
+ "Name": "Ear"
+ },
+ {
+ "Code": "F271",
+ "Name": "PointerHand"
+ },
+ {
+ "Code": "F272",
+ "Name": "Bullseye"
+ },
+ {
+ "Code": "F28B",
+ "Name": "DocumentApproval"
+ },
+ {
+ "Code": "F2B7",
+ "Name": "LocaleLanguage"
+ },
+ {
+ "Code": "F32A",
+ "Name": "PassiveAuthentication"
+ },
+ {
+ "Code": "F354",
+ "Name": "ColorSolid"
+ },
+ {
+ "Code": "F384",
+ "Name": "NetworkOffline"
+ },
+ {
+ "Code": "F385",
+ "Name": "NetworkConnected"
+ },
+ {
+ "Code": "F386",
+ "Name": "NetworkConnectedCheckmark"
+ },
+ {
+ "Code": "F3B1",
+ "Name": "SignOut"
+ },
+ {
+ "Code": "F3CC",
+ "Name": "StatusInfo"
+ },
+ {
+ "Code": "F3CD",
+ "Name": "StatusInfoLeft"
+ },
+ {
+ "Code": "F3E2",
+ "Name": "NearbySharing"
+ },
+ {
+ "Code": "F3E7",
+ "Name": "CtrlSpatialLeft"
+ },
+ {
+ "Code": "F404",
+ "Name": "InteractiveDashboard"
+ },
+ {
+ "Code": "F405",
+ "Name": "DeclineCall"
+ },
+ {
+ "Code": "F406",
+ "Name": "ClippingTool"
+ },
+ {
+ "Code": "F407",
+ "Name": "RectangularClipping"
+ },
+ {
+ "Code": "F408",
+ "Name": "FreeFormClipping"
+ },
+ {
+ "Code": "F413",
+ "Name": "CopyTo"
+ },
+ {
+ "Code": "F427",
+ "Name": "IDBadge"
+ },
+ {
+ "Code": "F439",
+ "Name": "DynamicLock"
+ },
+ {
+ "Code": "F45E",
+ "Name": "PenTips"
+ },
+ {
+ "Code": "F45F",
+ "Name": "PenTipsMirrored"
+ },
+ {
+ "Code": "F460",
+ "Name": "HWPJoin"
+ },
+ {
+ "Code": "F461",
+ "Name": "HWPInsert"
+ },
+ {
+ "Code": "F462",
+ "Name": "HWPStrikeThrough"
+ },
+ {
+ "Code": "F463",
+ "Name": "HWPScratchOut"
+ },
+ {
+ "Code": "F464",
+ "Name": "HWPSplit"
+ },
+ {
+ "Code": "F465",
+ "Name": "HWPNewLine"
+ },
+ {
+ "Code": "F466",
+ "Name": "HWPOverwrite"
+ },
+ {
+ "Code": "F473",
+ "Name": "MobWifiWarning1"
+ },
+ {
+ "Code": "F474",
+ "Name": "MobWifiWarning2"
+ },
+ {
+ "Code": "F475",
+ "Name": "MobWifiWarning3"
+ },
+ {
+ "Code": "F476",
+ "Name": "MobWifiWarning4"
+ },
+ {
+ "Code": "F47F",
+ "Name": "MicLocationCombo"
+ },
+ {
+ "Code": "F49A",
+ "Name": "Globe2"
+ },
+ {
+ "Code": "F4A5",
+ "Name": "SpecialEffectSize"
+ },
+ {
+ "Code": "F4A9",
+ "Name": "GIF"
+ },
+ {
+ "Code": "F4AA",
+ "Name": "Sticker2"
+ },
+ {
+ "Code": "F4BE",
+ "Name": "SurfaceHubSelected"
+ },
+ {
+ "Code": "F4BF",
+ "Name": "HoloLensSelected"
+ },
+ {
+ "Code": "F4C0",
+ "Name": "Earbud"
+ },
+ {
+ "Code": "F4C3",
+ "Name": "MixVolumes"
+ },
+ {
+ "Code": "F540",
+ "Name": "Safe"
+ },
+ {
+ "Code": "F552",
+ "Name": "LaptopSecure"
+ },
+ {
+ "Code": "F56D",
+ "Name": "PrintDefault"
+ },
+ {
+ "Code": "F56E",
+ "Name": "PageMirrored"
+ },
+ {
+ "Code": "F56F",
+ "Name": "LandscapeOrientationMirrored"
+ },
+ {
+ "Code": "F570",
+ "Name": "ColorOff"
+ },
+ {
+ "Code": "F571",
+ "Name": "PrintAllPages"
+ },
+ {
+ "Code": "F572",
+ "Name": "PrintCustomRange"
+ },
+ {
+ "Code": "F573",
+ "Name": "PageMarginPortraitNarrow"
+ },
+ {
+ "Code": "F574",
+ "Name": "PageMarginPortraitNormal"
+ },
+ {
+ "Code": "F575",
+ "Name": "PageMarginPortraitModerate"
+ },
+ {
+ "Code": "F576",
+ "Name": "PageMarginPortraitWide"
+ },
+ {
+ "Code": "F577",
+ "Name": "PageMarginLandscapeNarrow"
+ },
+ {
+ "Code": "F578",
+ "Name": "PageMarginLandscapeNormal"
+ },
+ {
+ "Code": "F579",
+ "Name": "PageMarginLandscapeModerate"
+ },
+ {
+ "Code": "F57A",
+ "Name": "PageMarginLandscapeWide"
+ },
+ {
+ "Code": "F57B",
+ "Name": "CollateLandscape"
+ },
+ {
+ "Code": "F57C",
+ "Name": "CollatePortrait"
+ },
+ {
+ "Code": "F57D",
+ "Name": "CollatePortraitSeparated"
+ },
+ {
+ "Code": "F57E",
+ "Name": "DuplexLandscapeOneSided"
+ },
+ {
+ "Code": "F57F",
+ "Name": "DuplexLandscapeOneSidedMirrored"
+ },
+ {
+ "Code": "F580",
+ "Name": "DuplexLandscapeTwoSidedLongEdge"
+ },
+ {
+ "Code": "F581",
+ "Name": "DuplexLandscapeTwoSidedLongEdgeMirrored"
+ },
+ {
+ "Code": "F582",
+ "Name": "DuplexLandscapeTwoSidedShortEdge"
+ },
+ {
+ "Code": "F583",
+ "Name": "DuplexLandscapeTwoSidedShortEdgeMirrored"
+ },
+ {
+ "Code": "F584",
+ "Name": "DuplexPortraitOneSided"
+ },
+ {
+ "Code": "F585",
+ "Name": "DuplexPortraitOneSidedMirrored"
+ },
+ {
+ "Code": "F586",
+ "Name": "DuplexPortraitTwoSidedLongEdge"
+ },
+ {
+ "Code": "F587",
+ "Name": "DuplexPortraitTwoSidedLongEdgeMirrored"
+ },
+ {
+ "Code": "F588",
+ "Name": "DuplexPortraitTwoSidedShortEdge"
+ },
+ {
+ "Code": "F589",
+ "Name": "DuplexPortraitTwoSidedShortEdgeMirrored"
+ },
+ {
+ "Code": "F58A",
+ "Name": "PPSOneLandscape"
+ },
+ {
+ "Code": "F58B",
+ "Name": "PPSTwoLandscape"
+ },
+ {
+ "Code": "F58C",
+ "Name": "PPSTwoPortrait"
+ },
+ {
+ "Code": "F58D",
+ "Name": "PPSFourLandscape"
+ },
+ {
+ "Code": "F58E",
+ "Name": "PPSFourPortrait"
+ },
+ {
+ "Code": "F58F",
+ "Name": "HolePunchOff"
+ },
+ {
+ "Code": "F590",
+ "Name": "HolePunchPortraitLeft"
+ },
+ {
+ "Code": "F591",
+ "Name": "HolePunchPortraitRight"
+ },
+ {
+ "Code": "F592",
+ "Name": "HolePunchPortraitTop"
+ },
+ {
+ "Code": "F593",
+ "Name": "HolePunchPortraitBottom"
+ },
+ {
+ "Code": "F594",
+ "Name": "HolePunchLandscapeLeft"
+ },
+ {
+ "Code": "F595",
+ "Name": "HolePunchLandscapeRight"
+ },
+ {
+ "Code": "F596",
+ "Name": "HolePunchLandscapeTop"
+ },
+ {
+ "Code": "F597",
+ "Name": "HolePunchLandscapeBottom"
+ },
+ {
+ "Code": "F598",
+ "Name": "StaplingOff"
+ },
+ {
+ "Code": "F599",
+ "Name": "StaplingPortraitTopLeft"
+ },
+ {
+ "Code": "F59A",
+ "Name": "StaplingPortraitTopRight"
+ },
+ {
+ "Code": "F59B",
+ "Name": "StaplingPortraitBottomRight"
+ },
+ {
+ "Code": "F59C",
+ "Name": "StaplingPortraitTwoLeft"
+ },
+ {
+ "Code": "F59D",
+ "Name": "StaplingPortraitTwoRight"
+ },
+ {
+ "Code": "F59E",
+ "Name": "StaplingPortraitTwoTop"
+ },
+ {
+ "Code": "F59F",
+ "Name": "StaplingPortraitTwoBottom"
+ },
+ {
+ "Code": "F5A0",
+ "Name": "StaplingPortraitBookBinding"
+ },
+ {
+ "Code": "F5A1",
+ "Name": "StaplingLandscapeTopLeft"
+ },
+ {
+ "Code": "F5A2",
+ "Name": "StaplingLandscapeTopRight"
+ },
+ {
+ "Code": "F5A3",
+ "Name": "StaplingLandscapeBottomLeft"
+ },
+ {
+ "Code": "F5A4",
+ "Name": "StaplingLandscapeBottomRight"
+ },
+ {
+ "Code": "F5A5",
+ "Name": "StaplingLandscapeTwoLeft"
+ },
+ {
+ "Code": "F5A6",
+ "Name": "StaplingLandscapeTwoRight"
+ },
+ {
+ "Code": "F5A7",
+ "Name": "StaplingLandscapeTwoTop"
+ },
+ {
+ "Code": "F5A8",
+ "Name": "StaplingLandscapeTwoBottom"
+ },
+ {
+ "Code": "F5A9",
+ "Name": "StaplingLandscapeBookBinding"
+ },
+ {
+ "Code": "F5AA",
+ "Name": "StatusDataTransferRoaming"
+ },
+ {
+ "Code": "F5AB",
+ "Name": "MobSIMError"
+ },
+ {
+ "Code": "F5AC",
+ "Name": "CollateLandscapeSeparated"
+ },
+ {
+ "Code": "F5AD",
+ "Name": "PPSOnePortrait"
+ },
+ {
+ "Code": "F5AE",
+ "Name": "StaplingPortraitBottomLeft"
+ },
+ {
+ "Code": "F5B0",
+ "Name": "PlaySolid"
+ },
+ {
+ "Code": "F5E7",
+ "Name": "RepeatOff"
+ },
+ {
+ "Code": "F5ED",
+ "Name": "Set"
+ },
+ {
+ "Code": "F5EE",
+ "Name": "SetSolid"
+ },
+ {
+ "Code": "F5EF",
+ "Name": "FuzzyReading"
+ },
+ {
+ "Code": "F5F2",
+ "Name": "VerticalBattery0"
+ },
+ {
+ "Code": "F5F3",
+ "Name": "VerticalBattery1"
+ },
+ {
+ "Code": "F5F4",
+ "Name": "VerticalBattery2"
+ },
+ {
+ "Code": "F5F5",
+ "Name": "VerticalBattery3"
+ },
+ {
+ "Code": "F5F6",
+ "Name": "VerticalBattery4"
+ },
+ {
+ "Code": "F5F7",
+ "Name": "VerticalBattery5"
+ },
+ {
+ "Code": "F5F8",
+ "Name": "VerticalBattery6"
+ },
+ {
+ "Code": "F5F9",
+ "Name": "VerticalBattery7"
+ },
+ {
+ "Code": "F5FA",
+ "Name": "VerticalBattery8"
+ },
+ {
+ "Code": "F5FB",
+ "Name": "VerticalBattery9"
+ },
+ {
+ "Code": "F5FC",
+ "Name": "VerticalBattery10"
+ },
+ {
+ "Code": "F5FD",
+ "Name": "VerticalBatteryCharging0"
+ },
+ {
+ "Code": "F5FE",
+ "Name": "VerticalBatteryCharging1"
+ },
+ {
+ "Code": "F5FF",
+ "Name": "VerticalBatteryCharging2"
+ },
+ {
+ "Code": "F600",
+ "Name": "VerticalBatteryCharging3"
+ },
+ {
+ "Code": "F601",
+ "Name": "VerticalBatteryCharging4"
+ },
+ {
+ "Code": "F602",
+ "Name": "VerticalBatteryCharging5"
+ },
+ {
+ "Code": "F603",
+ "Name": "VerticalBatteryCharging6"
+ },
+ {
+ "Code": "F604",
+ "Name": "VerticalBatteryCharging7"
+ },
+ {
+ "Code": "F605",
+ "Name": "VerticalBatteryCharging8"
+ },
+ {
+ "Code": "F606",
+ "Name": "VerticalBatteryCharging9"
+ },
+ {
+ "Code": "F607",
+ "Name": "VerticalBatteryCharging10"
+ },
+ {
+ "Code": "F608",
+ "Name": "VerticalBatteryUnknown"
+ },
+ {
+ "Code": "F618",
+ "Name": "SIMError"
+ },
+ {
+ "Code": "F619",
+ "Name": "SIMMissing"
+ },
+ {
+ "Code": "F61A",
+ "Name": "SIMLock"
+ },
+ {
+ "Code": "F61B",
+ "Name": "eSIM"
+ },
+ {
+ "Code": "F61C",
+ "Name": "eSIMNoProfile"
+ },
+ {
+ "Code": "F61D",
+ "Name": "eSIMLocked"
+ },
+ {
+ "Code": "F61E",
+ "Name": "eSIMBusy"
+ },
+ {
+ "Code": "F61F",
+ "Name": "NoiseCancelation"
+ },
+ {
+ "Code": "F620",
+ "Name": "NoiseCancelationOff"
+ },
+ {
+ "Code": "F623",
+ "Name": "MusicSharing"
+ },
+ {
+ "Code": "F624",
+ "Name": "MusicSharingOff"
+ },
+ {
+ "Code": "F63C",
+ "Name": "CircleShapeSolid"
+ },
+ {
+ "Code": "F657",
+ "Name": "WifiCallBars"
+ },
+ {
+ "Code": "F658",
+ "Name": "WifiCall0"
+ },
+ {
+ "Code": "F659",
+ "Name": "WifiCall1"
+ },
+ {
+ "Code": "F65A",
+ "Name": "WifiCall2"
+ },
+ {
+ "Code": "F65B",
+ "Name": "WifiCall3"
+ },
+ {
+ "Code": "F65C",
+ "Name": "WifiCall4"
+ },
+ {
+ "Code": "F69E",
+ "Name": "CHTLanguageBar"
+ },
+ {
+ "Code": "F6A9",
+ "Name": "ComposeMode"
+ },
+ {
+ "Code": "F6B8",
+ "Name": "ExpressiveInputEntry"
+ },
+ {
+ "Code": "F6BA",
+ "Name": "EmojiTabMoreSymbols"
+ },
+ {
+ "Code": "F6FA",
+ "Name": "WebSearch"
+ },
+ {
+ "Code": "F712",
+ "Name": "Kiosk"
+ },
+ {
+ "Code": "F714",
+ "Name": "RTTLogo"
+ },
+ {
+ "Code": "F715",
+ "Name": "VoiceCall"
+ },
+ {
+ "Code": "F716",
+ "Name": "GoToMessage"
+ },
+ {
+ "Code": "F71A",
+ "Name": "ReturnToCall"
+ },
+ {
+ "Code": "F71C",
+ "Name": "StartPresenting"
+ },
+ {
+ "Code": "F71D",
+ "Name": "StopPresenting"
+ },
+ {
+ "Code": "F71E",
+ "Name": "ProductivityMode"
+ },
+ {
+ "Code": "F738",
+ "Name": "SetHistoryStatus"
+ },
+ {
+ "Code": "F739",
+ "Name": "SetHistoryStatus2"
+ },
+ {
+ "Code": "F73D",
+ "Name": "Keyboardsettings20"
+ },
+ {
+ "Code": "F73E",
+ "Name": "OneHandedRight20"
+ },
+ {
+ "Code": "F73F",
+ "Name": "OneHandedLeft20"
+ },
+ {
+ "Code": "F740",
+ "Name": "Split20"
+ },
+ {
+ "Code": "F741",
+ "Name": "Full20"
+ },
+ {
+ "Code": "F742",
+ "Name": "Handwriting20"
+ },
+ {
+ "Code": "F743",
+ "Name": "ChevronLeft20"
+ },
+ {
+ "Code": "F744",
+ "Name": "ChevronLeft32"
+ },
+ {
+ "Code": "F745",
+ "Name": "ChevronRight20"
+ },
+ {
+ "Code": "F746",
+ "Name": "ChevronRight32"
+ },
+ {
+ "Code": "F763",
+ "Name": "Event12"
+ },
+ {
+ "Code": "F781",
+ "Name": "MicOff2"
+ },
+ {
+ "Code": "F785",
+ "Name": "DeliveryOptimization"
+ },
+ {
+ "Code": "F78A",
+ "Name": "CancelMedium"
+ },
+ {
+ "Code": "F78B",
+ "Name": "SearchMedium"
+ },
+ {
+ "Code": "F78C",
+ "Name": "AcceptMedium"
+ },
+ {
+ "Code": "F78D",
+ "Name": "RevealPasswordMedium"
+ },
+ {
+ "Code": "F7AD",
+ "Name": "DeleteWord"
+ },
+ {
+ "Code": "F7AE",
+ "Name": "DeleteWordFill"
+ },
+ {
+ "Code": "F7AF",
+ "Name": "DeleteLines"
+ },
+ {
+ "Code": "F7B0",
+ "Name": "DeleteLinesFill"
+ },
+ {
+ "Code": "F7B1",
+ "Name": "InstertWords"
+ },
+ {
+ "Code": "F7B2",
+ "Name": "InstertWordsFill"
+ },
+ {
+ "Code": "F7B3",
+ "Name": "JoinWords"
+ },
+ {
+ "Code": "F7B4",
+ "Name": "JoinWordsFill"
+ },
+ {
+ "Code": "F7B5",
+ "Name": "OverwriteWords"
+ },
+ {
+ "Code": "F7B6",
+ "Name": "OverwriteWordsFill"
+ },
+ {
+ "Code": "F7B7",
+ "Name": "AddNewLine"
+ },
+ {
+ "Code": "F7B8",
+ "Name": "AddNewLineFill"
+ },
+ {
+ "Code": "F7B9",
+ "Name": "OverwriteWordsKorean"
+ },
+ {
+ "Code": "F7BA",
+ "Name": "OverwriteWordsFillKorean"
+ },
+ {
+ "Code": "F7BB",
+ "Name": "EducationIcon"
+ },
+ {
+ "Code": "F7ED",
+ "Name": "WindowSnipping"
+ },
+ {
+ "Code": "F7EE",
+ "Name": "VideoCapture"
+ },
+ {
+ "Code": "F809",
+ "Name": "StatusSecured"
+ },
+ {
+ "Code": "F83B",
+ "Name": "NarratorApp"
+ },
+ {
+ "Code": "F83D",
+ "Name": "PowerButtonUpdate"
+ },
+ {
+ "Code": "F83E",
+ "Name": "RestartUpdate"
+ },
+ {
+ "Code": "F83F",
+ "Name": "UpdateStatusDot"
+ },
+ {
+ "Code": "F847",
+ "Name": "Eject"
+ },
+ {
+ "Code": "F87B",
+ "Name": "Spelling"
+ },
+ {
+ "Code": "F87C",
+ "Name": "SpellingKorean"
+ },
+ {
+ "Code": "F87D",
+ "Name": "SpellingSerbian"
+ },
+ {
+ "Code": "F87E",
+ "Name": "SpellingChinese"
+ },
+ {
+ "Code": "F89A",
+ "Name": "FolderSelect"
+ },
+ {
+ "Code": "F8A5",
+ "Name": "SmartScreen"
+ },
+ {
+ "Code": "F8A6",
+ "Name": "ExploitProtection"
+ },
+ {
+ "Code": "F8AA",
+ "Name": "AddBold"
+ },
+ {
+ "Code": "F8AB",
+ "Name": "SubtractBold"
+ },
+ {
+ "Code": "F8AC",
+ "Name": "BackSolidBold"
+ },
+ {
+ "Code": "F8AD",
+ "Name": "ForwardSolidBold"
+ },
+ {
+ "Code": "F8AE",
+ "Name": "PauseBold"
+ },
+ {
+ "Code": "F8AF",
+ "Name": "ClickSolid"
+ },
+ {
+ "Code": "F8B0",
+ "Name": "SettingsSolid"
+ },
+ {
+ "Code": "F8B1",
+ "Name": "MicrophoneSolidBold"
+ },
+ {
+ "Code": "F8B2",
+ "Name": "SpeechSolidBold"
+ },
+ {
+ "Code": "F8B3",
+ "Name": "ClickedOutLoudSolidBold"
+ }
+]
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Playground/Client/Models/SymbolIconData.cs b/source/RevitLookup.UI.Playground/Client/Models/SymbolIconData.cs
new file mode 100644
index 000000000..3c54bf251
--- /dev/null
+++ b/source/RevitLookup.UI.Playground/Client/Models/SymbolIconData.cs
@@ -0,0 +1,14 @@
+using Wpf.Ui.Controls;
+
+namespace RevitLookup.UI.Playground.Client.Models;
+
+///
+/// IconData class for icons in icon page
+///
+public sealed class SymbolIconData
+{
+ public required string Name { get; init; }
+ public required SymbolRegular Icon { get; init; }
+ public required string Code { get; init; }
+ public string TextGlyph => $"{Code};";
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Playground/Client/Resources/Images/ProductIcon.png b/source/RevitLookup.UI.Playground/Client/Resources/Images/ProductIcon.png
new file mode 100644
index 000000000..8e012ed13
Binary files /dev/null and b/source/RevitLookup.UI.Playground/Client/Resources/Images/ProductIcon.png differ
diff --git a/source/RevitLookup.UI.Playground/Client/Resources/Images/ProductLogo.png b/source/RevitLookup.UI.Playground/Client/Resources/Images/ProductLogo.png
new file mode 100644
index 000000000..7236de89e
Binary files /dev/null and b/source/RevitLookup.UI.Playground/Client/Resources/Images/ProductLogo.png differ
diff --git a/source/RevitLookup.UI.Playground/Client/Services/ViewModelServices.cs b/source/RevitLookup.UI.Playground/Client/Services/ViewModelServices.cs
new file mode 100644
index 000000000..d4dd9693a
--- /dev/null
+++ b/source/RevitLookup.UI.Playground/Client/Services/ViewModelServices.cs
@@ -0,0 +1,17 @@
+using Microsoft.Extensions.DependencyInjection;
+
+namespace RevitLookup.UI.Playground.Client.Services;
+
+public static class ViewModelServices
+{
+ public static void RegisterViewModels(this IServiceCollection services)
+ {
+ services.Scan(selector => selector.FromCallingAssembly()
+ .AddClasses(filter => filter.InNamespaces("RevitLookup.UI.Playground.Client.ViewModels"))
+ .AsSelf()
+ .WithScopedLifetime()
+ .AddClasses(filter => filter.NotInNamespaces("RevitLookup.UI.Playground.Client.ViewModels").Where(type => type.Name.EndsWith("ViewModel")))
+ .AsImplementedInterfaces(type => type.Name.EndsWith("ViewModel"))
+ .WithScopedLifetime());
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Playground/Client/Services/ViewServices.cs b/source/RevitLookup.UI.Playground/Client/Services/ViewServices.cs
new file mode 100644
index 000000000..774f42b72
--- /dev/null
+++ b/source/RevitLookup.UI.Playground/Client/Services/ViewServices.cs
@@ -0,0 +1,30 @@
+using System.Windows.Controls;
+using Microsoft.Extensions.DependencyInjection;
+using Wpf.Ui.Abstractions.Controls;
+using Wpf.Ui.Controls;
+
+namespace RevitLookup.UI.Playground.Client.Services;
+
+public static class ViewServices
+{
+ public static void RegisterViews(this IServiceCollection services)
+ {
+ services.Scan(selector => selector.FromAssemblyOf()
+ .AddClasses(filter => filter.AssignableTo()).AsSelf().WithScopedLifetime()
+ .AddClasses(filter => filter.AssignableTo()).AsSelf().WithTransientLifetime()
+ .AddClasses(filter =>
+ {
+ filter.AssignableTo();
+ filter.Where(type => type.IsAssignableTo(typeof(INavigableView)));
+ }).AsSelf().WithScopedLifetime()
+ .AddClasses(filter =>
+ {
+ filter.AssignableTo();
+ filter.Where(type => !type.IsAssignableTo(typeof(INavigableView)));
+ }).AsSelf().WithTransientLifetime());
+
+ services.Scan(selector => selector.FromCallingAssembly()
+ .AddClasses(filter => filter.AssignableTo()).AsSelf().WithScopedLifetime()
+ .AddClasses(filter => filter.AssignableTo()).AsSelf().WithScopedLifetime());
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Playground/Client/ViewModels/Pages/DashboardViewModel.cs b/source/RevitLookup.UI.Playground/Client/ViewModels/Pages/DashboardViewModel.cs
new file mode 100644
index 000000000..0ac7c638d
--- /dev/null
+++ b/source/RevitLookup.UI.Playground/Client/ViewModels/Pages/DashboardViewModel.cs
@@ -0,0 +1,29 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using JetBrains.Annotations;
+using RevitLookup.UI.Playground.Client.Views.Pages;
+using Wpf.Ui;
+
+namespace RevitLookup.UI.Playground.Client.ViewModels.Pages;
+
+[UsedImplicitly]
+public sealed partial class DashboardViewModel(INavigationService navigationService) : ObservableObject
+{
+ [RelayCommand]
+ private void NavigateToWindowsPage()
+ {
+ navigationService.NavigateWithHierarchy(typeof(WindowsPage));
+ }
+
+ [RelayCommand]
+ private void NavigateToPagesPage()
+ {
+ navigationService.NavigateWithHierarchy(typeof(PagesPage));
+ }
+
+ [RelayCommand]
+ private void NavigateToDialogsPage()
+ {
+ navigationService.NavigateWithHierarchy(typeof(DialogsPage));
+ }
+}
\ No newline at end of file
diff --git a/source/RevitLookup.UI.Playground/Client/ViewModels/Pages/DesignGuidance/FontIconsPageViewModel.cs b/source/RevitLookup.UI.Playground/Client/ViewModels/Pages/DesignGuidance/FontIconsPageViewModel.cs
new file mode 100644
index 000000000..1673ad6c1
--- /dev/null
+++ b/source/RevitLookup.UI.Playground/Client/ViewModels/Pages/DesignGuidance/FontIconsPageViewModel.cs
@@ -0,0 +1,70 @@
+using System.IO;
+using System.Reflection;
+using System.Text.Json;
+using CommunityToolkit.Mvvm.ComponentModel;
+using JetBrains.Annotations;
+using RevitLookup.UI.Playground.Client.Models;
+#if NETFRAMEWORK
+using RevitLookup.UI.Framework.Extensions;
+#endif
+
+namespace RevitLookup.UI.Playground.Client.ViewModels.Pages.DesignGuidance;
+
+[UsedImplicitly]
+public partial class FontIconsPageViewModel : ObservableObject
+{
+ [ObservableProperty] private List _icons = [];
+ [ObservableProperty] private List _filteredIcons = [];
+ [ObservableProperty] private FontIconData? _selectedIcon;
+ [ObservableProperty] private string _searchText = string.Empty;
+
+ public FontIconsPageViewModel()
+ {
+ var jsonText = ReadIconData();
+ Icons = JsonSerializer.Deserialize>(jsonText)!
+ .OrderBy(data => data.Name)
+ .ToList();
+
+ SelectedIcon = _icons.FirstOrDefault();
+ }
+
+ private static string ReadIconData()
+ {
+ const string resourceName = "RevitLookup.UI.Playground.Client.Models.FontIcons.json";
+
+ var assembly = Assembly.GetExecutingAssembly();
+ using var stream = assembly.GetManifestResourceStream(resourceName)!;
+ using var reader = new StreamReader(stream);
+ return reader.ReadToEnd();
+ }
+
+ partial void OnIconsChanged(List