mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Merge branch 'development'
This commit is contained in:
commit
db145d537a
@ -7,14 +7,6 @@ trigger:
|
|||||||
- master
|
- master
|
||||||
pr: none
|
pr: none
|
||||||
|
|
||||||
resources:
|
|
||||||
repositories:
|
|
||||||
- repository: RGBNET
|
|
||||||
type: github
|
|
||||||
endpoint: github.com_SpoinkyNL
|
|
||||||
name: DarthAffe/RGB.NET
|
|
||||||
ref: Development
|
|
||||||
|
|
||||||
pool:
|
pool:
|
||||||
vmImage: 'windows-latest'
|
vmImage: 'windows-latest'
|
||||||
|
|
||||||
@ -28,22 +20,13 @@ variables:
|
|||||||
SourceVersion: $(Build.SourceVersion)
|
SourceVersion: $(Build.SourceVersion)
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- checkout: RGBNET
|
|
||||||
path: s/RGB.NET
|
|
||||||
- checkout: self
|
- checkout: self
|
||||||
path: s/Artemis
|
path: s/Artemis
|
||||||
|
|
||||||
- task: DotNetCoreCLI@2
|
- task: DotNetCoreCLI@2
|
||||||
displayName: 'RGB.NET - Build'
|
displayName: 'dotnet build Artemis'
|
||||||
inputs:
|
inputs:
|
||||||
command: 'build'
|
command: 'build'
|
||||||
projects: '$(rgbSolution)'
|
|
||||||
arguments: '--configuration Release'
|
|
||||||
|
|
||||||
- task: DotNetCoreCLI@2
|
|
||||||
displayName: 'dotnet restore Artemis'
|
|
||||||
inputs:
|
|
||||||
command: 'restore'
|
|
||||||
projects: '$(artemisSolution)'
|
projects: '$(artemisSolution)'
|
||||||
feedsToUse: 'config'
|
feedsToUse: 'config'
|
||||||
nugetConfigPath: '$(Pipeline.Workspace)/s/Artemis/src/NuGet.Config'
|
nugetConfigPath: '$(Pipeline.Workspace)/s/Artemis/src/NuGet.Config'
|
||||||
|
|||||||
@ -1,2 +1,26 @@
|
|||||||
# PLACEHOLDER
|
|
||||||
TODO: Add .NET projects to the *src* folder and run `docfx` to generate **REAL** *API Documentation*!
|
# Welcome to the **Artemis API documentation**
|
||||||
|
On this website you can browse the Artemis Core and Shared UI API.
|
||||||
|
A large part of this documentation is being generated based on code but over time the plan is to expand the documentation with written guides.
|
||||||
|
|
||||||
|
|
||||||
|
## Plugins
|
||||||
|
Artemis 2.0 has been developed from the ground up with plugins in mind. This means almost all functionality can be expanded. The following plugin types are currently available and fully implemented:
|
||||||
|
- [DeviceProvider](api/Artemis.Core.DeviceProviders.DeviceProvider.html)
|
||||||
|
- [LayerBrush\<T\>](api/Artemis.Core.LayerBrushes.LayerBrush-1.html)
|
||||||
|
- [PerLedLayerBrush\<T\>](api/Artemis.Core.LayerBrushes.PerLedLayerBrush-1.html)
|
||||||
|
- [LayerEffect](api/Artemis.Core.LayerEffects.LayerEffect-1.html)
|
||||||
|
- [Module](api/Artemis.Core.Modules.Module.html)
|
||||||
|
- [Module\<T\>](api/Artemis.Core.Modules.Module-1.html)
|
||||||
|
|
||||||
|
These allow you to expand on Artemis's functionality. For quick and interactive plugin creation, use the [Visual Studio template extension](https://marketplace.visualstudio.com/items?itemName=SpoinkyNL.ArtemisTemplates).
|
||||||
|
|
||||||
|
Example implementations of these plugins can be found on [GitHub](https://github.com/Artemis-RGB/Artemis/tree/master/src/Plugins).
|
||||||
|
|
||||||
|
## Services
|
||||||
|
Artemis provides plugins with an API through a range of services.
|
||||||
|
All the services are available to plugins by using dependency injection in your plugin's constructor. Dependency injection is also available for the different view models plugins may provide.
|
||||||
|
|
||||||
|
- [Core Services](api/Artemis.Core.Services.html#interfaces)
|
||||||
|
- [UI Services](api/Artemis.UI.Shared.Services.html#interfaces)
|
||||||
|
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
Bummer, no guides 😌
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
Plugins allow you to expand on Artemis's functionality. For quick and interactive plugin creation, use the [Visual Studio template extension](https://marketplace.visualstudio.com/items?itemName=SpoinkyNL.ArtemisTemplates).
|
|
||||||
|
|
||||||
Example implementations of these plugins can be found on [GitHub](https://github.com/Artemis-RGB/Artemis/tree/master/src/Plugins).
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
- name: Introduction
|
|
||||||
href: intro.md
|
|
||||||
- name: Plugins - Getting started
|
|
||||||
href: plugins_getting_started.md
|
|
||||||
@ -4,8 +4,8 @@
|
|||||||
"src": [
|
"src": [
|
||||||
{
|
{
|
||||||
"files": [
|
"files": [
|
||||||
"Artemis.Core/Artemis.Core.csproj",
|
"Artemis.Core/bin/net6.0/Artemis.Core.dll",
|
||||||
"Artemis.UI.Shared/Artemis.UI.Shared.csproj",
|
"Artemis.UI.Shared/bin/net6.0/Artemis.UI.Shared.dll",
|
||||||
],
|
],
|
||||||
"src": "../../src"
|
"src": "../../src"
|
||||||
}
|
}
|
||||||
@ -20,14 +20,11 @@
|
|||||||
"content": [
|
"content": [
|
||||||
{
|
{
|
||||||
"files": [
|
"files": [
|
||||||
"api/**.yml",
|
"api/**.yml"
|
||||||
"api/index.md"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"files": [
|
"files": [
|
||||||
"articles/**.md",
|
|
||||||
"articles/**/toc.yml",
|
|
||||||
"toc.yml",
|
"toc.yml",
|
||||||
"*.md"
|
"*.md"
|
||||||
]
|
]
|
||||||
@ -52,16 +49,24 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"globalMetadata": {
|
"globalMetadata": {
|
||||||
"_appTitle": "Artemis documentation",
|
"_appTitle": "Artemis API Documentation",
|
||||||
"_enableSearch": true
|
"_appName": "Artemis",
|
||||||
|
"_appFaviconPath": "images/application.ico",
|
||||||
|
"_appLogoPath": "images/application.ico",
|
||||||
|
"_appFooter": "<a href=\"https://artemis-rgb.com\" style=\"margin: 0 5px;\">\r <i class=\"bi-globe\" role=\"img\" aria-label=\"GitHub\" style=\"margin: 0 10\"></i>\r</a>\r<a href=\"https://github.com/Artemis-RGB/Artemis\" style=\"margin: 0 5px;\">\r <i class=\"bi-github\" role=\"img\" aria-label=\"GitHub\"></i>\r</a>\r<a href=\"https://artemis-rgb.com\" style=\"margin: 0 5px;\">\r <i class=\"bi bi-layout-text-sidebar-reverse\" role=\"img\" aria-label=\"GitHub\"></i>\r</a>\r<a href=\"https://wiki.artemis-rgb.com/\" style=\"margin: 0 5px;\">\r <i class=\"bi-chat-fill\" role=\"img\" aria-label=\"GitHub\"></i>\r</a>\r<a href=\"https://wiki.artemis-rgb.com/en/donating\" style=\"margin: 0 5px;\">\r <i class=\"bi-gift-fill\" role=\"img\" aria-label=\"GitHub\"></i>\r</a>",
|
||||||
|
"_copyrightFooter": "Content is available under the PolyForm Noncommercial License, by Artemis RGB.",
|
||||||
|
"_enableSearch": true,
|
||||||
|
"_disableSideFilter": false,
|
||||||
|
"_enableNewTab": true,
|
||||||
|
"_disableContribution": false,
|
||||||
|
"_disableBreadcrumb": false
|
||||||
},
|
},
|
||||||
"dest": "_site",
|
"dest": "_site",
|
||||||
"globalMetadataFiles": [],
|
"globalMetadataFiles": [],
|
||||||
"fileMetadataFiles": [],
|
"fileMetadataFiles": [],
|
||||||
"template": [
|
"template": [
|
||||||
"default",
|
"default",
|
||||||
"templates/artemis",
|
"templates/singulinkfx"
|
||||||
"templates/material"
|
|
||||||
],
|
],
|
||||||
"postProcessors": [],
|
"postProcessors": [],
|
||||||
"markdownEngineName": "markdig",
|
"markdownEngineName": "markdig",
|
||||||
|
|||||||
@ -11,6 +11,9 @@ apiRules:
|
|||||||
- exclude:
|
- exclude:
|
||||||
uidRegex: ^Stylet
|
uidRegex: ^Stylet
|
||||||
type: Type
|
type: Type
|
||||||
|
- exclude:
|
||||||
|
uidRegex: ^Artemis\.Core\.CorePropertyChanged
|
||||||
|
type: Type
|
||||||
- exclude:
|
- exclude:
|
||||||
uidRegex: ^Artemis\.Core\.Properties
|
uidRegex: ^Artemis\.Core\.Properties
|
||||||
type: Type
|
type: Type
|
||||||
|
|||||||
BIN
docfx/docfx_project/images/application.ico
Normal file
BIN
docfx/docfx_project/images/application.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 111 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 22 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 110 KiB |
@ -1,6 +0,0 @@
|
|||||||
{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}
|
|
||||||
|
|
||||||
<a class="navbar-brand" href="{{_rel}}index.html">
|
|
||||||
<link rel="stylesheet" href="{{_rel}}styles/artemis.css">
|
|
||||||
<img id="logo" src="{{_rel}}images/logo-512.png" alt="Artemis logo" style="width: 38px; height: 38px; margin-top: 6px; margin-right: 6px;">
|
|
||||||
</a>
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
.sidefilter {
|
|
||||||
width: 320px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidetoc {
|
|
||||||
width: 320px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: 768px)
|
|
||||||
.article.grid-right {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article.grid-right {
|
|
||||||
margin-left: 340px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 1500px) {
|
|
||||||
.container {
|
|
||||||
width: 1470px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,38 +0,0 @@
|
|||||||
{{!Copyright (c) Oscar Vasquez. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
|
||||||
<title>{{#title}}{{title}}{{/title}}{{^title}}{{>partials/title}}{{/title}} {{#_appTitle}}| {{_appTitle}} {{/_appTitle}}</title>
|
|
||||||
<meta name="viewport" content="width=device-width">
|
|
||||||
<meta name="title" content="{{#title}}{{title}}{{/title}}{{^title}}{{>partials/title}}{{/title}} {{#_appTitle}}| {{_appTitle}} {{/_appTitle}}">
|
|
||||||
<meta name="generator" content="docfx {{_docfxVersion}}">
|
|
||||||
{{#_description}}<meta name="description" content="{{_description}}">{{/_description}}
|
|
||||||
<link rel="shortcut icon" href="{{_rel}}{{{_appFaviconPath}}}{{^_appFaviconPath}}favicon.ico{{/_appFaviconPath}}">
|
|
||||||
<link rel="stylesheet" href="{{_rel}}styles/docfx.vendor.css">
|
|
||||||
<link rel="stylesheet" href="{{_rel}}styles/docfx.css">
|
|
||||||
<link rel="stylesheet" href="{{_rel}}styles/main.css">
|
|
||||||
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet">
|
|
||||||
<meta property="docfx:navrel" content="{{_navRel}}">
|
|
||||||
<meta property="docfx:tocrel" content="{{_tocRel}}">
|
|
||||||
{{#_noindex}}<meta name="searchOption" content="noindex">{{/_noindex}}
|
|
||||||
{{#_enableSearch}}<meta property="docfx:rel" content="{{_rel}}">{{/_enableSearch}}
|
|
||||||
{{#_enableNewTab}}<meta property="docfx:newtab" content="true">{{/_enableNewTab}}
|
|
||||||
<!-- Matomo -->
|
|
||||||
<script type="text/javascript">
|
|
||||||
var _paq = window._paq = window._paq || [];
|
|
||||||
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
|
|
||||||
_paq.push(["disableCookies"]);
|
|
||||||
_paq.push(['trackPageView']);
|
|
||||||
_paq.push(['enableLinkTracking']);
|
|
||||||
(function() {
|
|
||||||
var u="https://stats.artemis-rgb.com/";
|
|
||||||
_paq.push(['setTrackerUrl', u+'matomo.php']);
|
|
||||||
_paq.push(['setSiteId', '2']);
|
|
||||||
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
|
||||||
g.type='text/javascript'; g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
<noscript><p><img src="https://stats.artemis-rgb.com/matomo.php?idsite=2&rec=1" style="border:0;" alt="" /></p></noscript>
|
|
||||||
<!-- End Matomo Code -->
|
|
||||||
</head>
|
|
||||||
@ -1,314 +0,0 @@
|
|||||||
/* COLOR VARIABLES*/
|
|
||||||
:root {
|
|
||||||
--header-bg-color: #009688;
|
|
||||||
--header-ft-color: #fff;
|
|
||||||
--highlight-light: #1de9b6;
|
|
||||||
--highlight-dark: #00bfa5;
|
|
||||||
--accent-dim: #e0e0e0;
|
|
||||||
--accent-super-dim: #f3f3f3;
|
|
||||||
--font-color: #34393e;
|
|
||||||
--card-box-shadow: 0 1px 2px 0 rgba(61, 65, 68, 0.06), 0 1px 3px 1px rgba(61, 65, 68, 0.16);
|
|
||||||
--search-box-shadow: 0 1px 2px 0 rgba(41, 45, 48, 0.36), 0 1px 3px 1px rgba(41, 45, 48, 0.46);
|
|
||||||
--transition: 350ms;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
color: var(--font-color);
|
|
||||||
font-family: "Roboto", sans-serif;
|
|
||||||
line-height: 1.5;
|
|
||||||
font-size: 16px;
|
|
||||||
-ms-text-size-adjust: 100%;
|
|
||||||
-webkit-text-size-adjust: 100%;
|
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* HIGHLIGHT COLOR */
|
|
||||||
|
|
||||||
button,
|
|
||||||
a {
|
|
||||||
color: var(--highlight-dark);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:hover,
|
|
||||||
button:focus,
|
|
||||||
a:hover,
|
|
||||||
a:focus {
|
|
||||||
color: var(--highlight-light);
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc .nav > li.active > a {
|
|
||||||
color: var(--highlight-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc .nav > li.active > a:hover,
|
|
||||||
.toc .nav > li.active > a:focus {
|
|
||||||
color: var(--highlight-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
.pagination > .active > a {
|
|
||||||
background-color: var(--header-bg-color);
|
|
||||||
border-color: var(--header-bg-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.pagination > .active > a,
|
|
||||||
.pagination > .active > a:focus,
|
|
||||||
.pagination > .active > a:hover,
|
|
||||||
.pagination > .active > span,
|
|
||||||
.pagination > .active > span:focus,
|
|
||||||
.pagination > .active > span:hover {
|
|
||||||
background-color: var(--highlight-light);
|
|
||||||
border-color: var(--highlight-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
.affix ul > li.active > a, .affix ul > li.active > a:before {
|
|
||||||
color: var(--highlight-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
.affix > ul > li.active > a, .affix > ul > li.active > a:before {
|
|
||||||
color: var(--highlight-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* HEADINGS */
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 24px;
|
|
||||||
line-height: 1.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 20px;
|
|
||||||
line-height: 1.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
h5 {
|
|
||||||
font-size: 14px;
|
|
||||||
padding: 10px 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
article h1,
|
|
||||||
article h2,
|
|
||||||
article h3,
|
|
||||||
article h4 {
|
|
||||||
margin-top: 35px;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
article h4 {
|
|
||||||
padding-bottom: 8px;
|
|
||||||
border-bottom: 2px solid #ddd;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* NAVBAR */
|
|
||||||
|
|
||||||
.navbar-brand > img {
|
|
||||||
color: var(--header-ft-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar {
|
|
||||||
border: none;
|
|
||||||
/* Both navbars use box-shadow */
|
|
||||||
-webkit-box-shadow: var(--card-box-shadow);
|
|
||||||
-moz-box-shadow: var(--card-box-shadow);
|
|
||||||
box-shadow: var(--card-box-shadow);
|
|
||||||
}
|
|
||||||
|
|
||||||
.subnav {
|
|
||||||
border-top: 1px solid #ddd;
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-inverse {
|
|
||||||
background-color: var(--header-bg-color);
|
|
||||||
z-index: 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-inverse .navbar-nav > li > a,
|
|
||||||
.navbar-inverse .navbar-text {
|
|
||||||
color: var(--header-ft-color);
|
|
||||||
background-color: var(--header-bg-color);
|
|
||||||
border-bottom: 3px solid transparent;
|
|
||||||
padding-bottom: 12px;
|
|
||||||
transition: 350ms;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-inverse .navbar-nav > li > a:focus,
|
|
||||||
.navbar-inverse .navbar-nav > li > a:hover {
|
|
||||||
color: var(--header-ft-color);
|
|
||||||
background-color: var(--header-bg-color);
|
|
||||||
border-bottom: 3px solid white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-inverse .navbar-nav > .active > a,
|
|
||||||
.navbar-inverse .navbar-nav > .active > a:focus,
|
|
||||||
.navbar-inverse .navbar-nav > .active > a:hover {
|
|
||||||
color: var(--header-ft-color);
|
|
||||||
background-color: var(--header-bg-color);
|
|
||||||
border-bottom: 3px solid white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-form .form-control {
|
|
||||||
border: 0;
|
|
||||||
border-radius: 4px;
|
|
||||||
box-shadow: var(--search-box-shadow);
|
|
||||||
transition:var(--transition);
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-form .form-control:hover {
|
|
||||||
background-color: var(--accent-dim);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* NAVBAR TOGGLED (small screens) */
|
|
||||||
|
|
||||||
.navbar-inverse .navbar-collapse, .navbar-inverse .navbar-form {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
.navbar-inverse .navbar-toggle {
|
|
||||||
box-shadow: var(--card-box-shadow);
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-inverse .navbar-toggle:focus,
|
|
||||||
.navbar-inverse .navbar-toggle:hover {
|
|
||||||
background-color: var(--highlight-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* SIDEBAR */
|
|
||||||
|
|
||||||
.toc .level1 > li {
|
|
||||||
font-weight: 400;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc .nav > li > a {
|
|
||||||
color: var(--font-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidefilter {
|
|
||||||
background-color: #fff;
|
|
||||||
border-left: none;
|
|
||||||
border-right: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidefilter {
|
|
||||||
background-color: #fff;
|
|
||||||
border-left: none;
|
|
||||||
border-right: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc-filter {
|
|
||||||
padding: 5px;
|
|
||||||
margin: 0;
|
|
||||||
box-shadow: var(--card-box-shadow);
|
|
||||||
transition:var(--transition);
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc-filter:hover {
|
|
||||||
background-color: var(--accent-super-dim);
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc-filter > input {
|
|
||||||
border: none;
|
|
||||||
background-color: inherit;
|
|
||||||
transition: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc-filter > .filter-icon {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidetoc > .toc {
|
|
||||||
background-color: #fff;
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidetoc {
|
|
||||||
background-color: #fff;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ALERTS */
|
|
||||||
|
|
||||||
.alert {
|
|
||||||
padding: 0px 0px 5px 0px;
|
|
||||||
color: inherit;
|
|
||||||
background-color: inherit;
|
|
||||||
border: none;
|
|
||||||
box-shadow: var(--card-box-shadow);
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert > p {
|
|
||||||
margin-bottom: 0;
|
|
||||||
padding: 5px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert > ul {
|
|
||||||
margin-bottom: 0;
|
|
||||||
padding: 5px 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert > h5 {
|
|
||||||
padding: 10px 15px;
|
|
||||||
margin-top: 0;
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-weight: bold;
|
|
||||||
border-radius: 4px 4px 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert-info > h5 {
|
|
||||||
color: #1976d2;
|
|
||||||
border-bottom: 4px solid #1976d2;
|
|
||||||
background-color: #e3f2fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert-warning > h5 {
|
|
||||||
color: #f57f17;
|
|
||||||
border-bottom: 4px solid #f57f17;
|
|
||||||
background-color: #fff3e0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert-danger > h5 {
|
|
||||||
color: #d32f2f;
|
|
||||||
border-bottom: 4px solid #d32f2f;
|
|
||||||
background-color: #ffebee;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* CODE HIGHLIGHT */
|
|
||||||
pre {
|
|
||||||
padding: 9.5px;
|
|
||||||
margin: 0 0 10px;
|
|
||||||
font-size: 13px;
|
|
||||||
word-break: break-all;
|
|
||||||
word-wrap: break-word;
|
|
||||||
background-color: #fffaef;
|
|
||||||
border-radius: 4px;
|
|
||||||
border: none;
|
|
||||||
box-shadow: var(--card-box-shadow);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* STYLE FOR IMAGES */
|
|
||||||
|
|
||||||
.article .small-image {
|
|
||||||
margin-top: 15px;
|
|
||||||
box-shadow: var(--card-box-shadow);
|
|
||||||
max-width: 350px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article .medium-image {
|
|
||||||
margin-top: 15px;
|
|
||||||
box-shadow: var(--card-box-shadow);
|
|
||||||
max-width: 550px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article .large-image {
|
|
||||||
margin-top: 15px;
|
|
||||||
box-shadow: var(--card-box-shadow);
|
|
||||||
max-width: 700px;
|
|
||||||
}
|
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}
|
||||||
|
{{!include(/^styles/.*/)}}
|
||||||
|
{{!include(/^fonts/.*/)}}
|
||||||
|
{{!include(favicon.ico)}}
|
||||||
|
{{!include(logo.svg)}}
|
||||||
|
{{!include(search-stopwords.json)}}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<!--[if IE]><![endif]-->
|
||||||
|
<html>
|
||||||
|
{{>partials/head}}
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="top-navbar">
|
||||||
|
<a class="burger-icon" onclick="toggleMenu()">
|
||||||
|
<svg name="Hamburger"
|
||||||
|
style="vertical-align: middle;"
|
||||||
|
width="34" height="34" viewBox="0 0 24 24"><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M20 6H4V9H20V6ZM4 10.999H20V13.999H4V10.999ZM4 15.999H20V18.999H4V15.999Z"></path></svg>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
{{>partials/logo}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="body-content">
|
||||||
|
<div id="blackout" class="blackout" onclick="toggleMenu()"></div>
|
||||||
|
|
||||||
|
<nav id="sidebar" role="navigation">
|
||||||
|
<div class="sidebar">
|
||||||
|
{{>partials/navbar}}
|
||||||
|
<div class="sidebar-item-separator"></div>
|
||||||
|
{{^_disableToc}}
|
||||||
|
{{>partials/toc}}
|
||||||
|
{{/_disableToc}}
|
||||||
|
</div>
|
||||||
|
{{>partials/footer}}
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<main class="main-panel">
|
||||||
|
{{#_enableSearch}}
|
||||||
|
{{>partials/searchResults}}
|
||||||
|
{{/_enableSearch}}
|
||||||
|
|
||||||
|
<div role="main" class="hide-when-search" >
|
||||||
|
{{^_disableBreadcrumb}}
|
||||||
|
{{>partials/breadcrumb}}
|
||||||
|
{{/_disableBreadcrumb}}
|
||||||
|
|
||||||
|
<article class="content wrap" id="_content" data-uid="{{uid}}">
|
||||||
|
{{!body}}
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#_copyrightFooter}}
|
||||||
|
<div class="copyright-footer">
|
||||||
|
<span>{{_copyrightFooter}}</span>
|
||||||
|
</div>
|
||||||
|
{{/_copyrightFooter}}
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{>partials/scripts}}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
<div class="footer">
|
||||||
|
{{{_appFooter}}}
|
||||||
|
{{^_appFooter}}<strong><a href='https://dotnet.github.io/docfx/'>DocFX</a> + <a href='https://www.singulink.com'>Singulink</a> = ♥</strong>{{/_appFooter}}
|
||||||
|
</div>
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||||
|
<title>{{#title}}{{title}}{{/title}}{{^title}}{{>partials/title}}{{/title}} {{#_appTitle}}| {{_appTitle}} {{/_appTitle}}</title>
|
||||||
|
<meta name="viewport" content="width=device-width">
|
||||||
|
<meta name="title" content="{{#title}}{{title}}{{/title}}{{^title}}{{>partials/title}}{{/title}} {{#_appTitle}}| {{_appTitle}} {{/_appTitle}}">
|
||||||
|
<meta name="generator" content="docfx {{_docfxVersion}}">
|
||||||
|
{{#_description}}<meta name="description" content="{{_description}}">{{/_description}}
|
||||||
|
<link rel="shortcut icon" href="{{_rel}}{{{_appFaviconPath}}}{{^_appFaviconPath}}favicon.ico{{/_appFaviconPath}}">
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/styles/night-owl.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.2/font/bootstrap-icons.css" integrity="sha384-EvBWSlnoFgZlXJvpzS+MAUEjvN7+gcCwH+qh7GRFOGgZO0PuwOFro7qPOJnLfe7l" crossorigin="anonymous">
|
||||||
|
<link rel="stylesheet" href="{{_rel}}styles/config.css">
|
||||||
|
<link rel="stylesheet" href="{{_rel}}styles/discord.css">
|
||||||
|
<link rel="stylesheet" href="{{_rel}}styles/singulink.css">
|
||||||
|
<link rel="stylesheet" href="{{_rel}}styles/main.css">
|
||||||
|
<meta property="docfx:navrel" content="{{_navRel}}">
|
||||||
|
<meta property="docfx:tocrel" content="{{_tocRel}}">
|
||||||
|
{{#_noindex}}<meta name="searchOption" content="noindex">{{/_noindex}}
|
||||||
|
{{#_enableSearch}}<meta property="docfx:rel" content="{{_rel}}">{{/_enableSearch}}
|
||||||
|
{{#_enableNewTab}}<meta property="docfx:newtab" content="true">{{/_enableNewTab}}
|
||||||
|
</head>
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}
|
||||||
|
|
||||||
|
<ul class="nav level{{level}}">
|
||||||
|
{{#items}}
|
||||||
|
{{^dropdown}}
|
||||||
|
<li>
|
||||||
|
{{^leaf}}
|
||||||
|
<span class="expand-stub"></span>
|
||||||
|
{{/leaf}}
|
||||||
|
{{#topicHref}}
|
||||||
|
<a href="{{topicHref}}" class="sidebar-item" name="{{tocHref}}" title="{{name}}">{{name}}</a>
|
||||||
|
{{/topicHref}}
|
||||||
|
{{^topicHref}}
|
||||||
|
<a>{{{name}}}</a>
|
||||||
|
{{/topicHref}}
|
||||||
|
|
||||||
|
{{^leaf}}
|
||||||
|
{{>partials/li}}
|
||||||
|
{{/leaf}}
|
||||||
|
</li>
|
||||||
|
{{/dropdown}}
|
||||||
|
{{#dropdown}}
|
||||||
|
<li class="dropdown">
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false">{{name}} <span class="caret"></span></a>
|
||||||
|
<ul class="dropdown-menu level{{level}}">
|
||||||
|
{{>partials/dd-li}}
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
{{/dropdown}}
|
||||||
|
{{/items}}
|
||||||
|
</ul>
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}
|
||||||
|
|
||||||
|
<a class="brand" href="{{_rel}}index.html">
|
||||||
|
<img src="{{_rel}}{{{_appLogoPath}}}{{^_appLogoPath}}logo.svg{{/_appLogoPath}}" alt="{{_appName}}" class="logomark">
|
||||||
|
<span class="brand-title">{{_appName}}</span>
|
||||||
|
</a>
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}
|
||||||
|
|
||||||
|
<h1 id="{{id}}" data-uid="{{uid}}" class="text-break">{{>partials/title}}</h1>
|
||||||
|
<div class="markdown level0 summary">{{{summary}}}</div>
|
||||||
|
<div class="markdown level0 conceptual">{{{conceptual}}}</div>
|
||||||
|
<div class="markdown level0 remarks">{{{remarks}}}</div>
|
||||||
|
{{#children}}
|
||||||
|
<h3 id="{{id}}">{{>partials/namespaceSubtitle}}</h3>
|
||||||
|
{{#children}}
|
||||||
|
<h5><xref uid="{{uid}}" altProperty="fullName" displayProperty="name"/></h5>
|
||||||
|
<section>{{{summary}}}</section>
|
||||||
|
{{/children}}
|
||||||
|
{{/children}}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="mobile-hide">
|
||||||
|
{{>partials/logo}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#_enableSearch}}
|
||||||
|
<div class="sidesearch">
|
||||||
|
<form id="search" role="search" class="search">
|
||||||
|
<i class="bi bi-search search-icon"></i>
|
||||||
|
<input type="text" id="search-query" placeholder="{{__global.search}}" autocomplete="off">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{{/_enableSearch}}
|
||||||
|
|
||||||
|
<div id="navbar">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}
|
||||||
|
|
||||||
|
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
|
||||||
|
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/highlight.min.js"></script>
|
||||||
|
<script type="text/javascript" src="{{_rel}}styles/jquery.twbsPagination.js"></script>
|
||||||
|
<script type="text/javascript" src="{{_rel}}styles/url.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/anchor-js/anchor.min.js"></script>
|
||||||
|
<script type="text/javascript" src="{{_rel}}styles/docfx.js"></script>
|
||||||
|
<script type="text/javascript" src="{{_rel}}styles/singulink.js"></script>
|
||||||
|
<script type="text/javascript" src="{{_rel}}styles/main.js"></script>
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}
|
||||||
|
|
||||||
|
<div id="search-results" style="display: none;">
|
||||||
|
<h1 class="search-list">{{__global.searchResults}} <span></span></h1>
|
||||||
|
<div class="sr-items">
|
||||||
|
<p><i class="bi bi-hourglass-split index-loading"></i></p>
|
||||||
|
</div>
|
||||||
|
<ul id="pagination" data-first={{__global.pageFirst}} data-prev={{__global.pagePrev}} data-next={{__global.pageNext}} data-last={{__global.pageLast}}></ul>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}
|
||||||
|
|
||||||
|
<div id="sidetoggle">
|
||||||
|
<div id="sidetoc"></div>
|
||||||
|
</div>
|
||||||
114
docfx/docfx_project/templates/singulinkfx/styles/config.css
Normal file
114
docfx/docfx_project/templates/singulinkfx/styles/config.css
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
/* Theme Configuration Options */
|
||||||
|
|
||||||
|
:root
|
||||||
|
{
|
||||||
|
/* General */
|
||||||
|
|
||||||
|
--base-font-size: 16px;
|
||||||
|
--smalldevice-base-font-size: 14px; /* Base font size for devices < 1024px */
|
||||||
|
|
||||||
|
--main-bg-color: #1f1f23;
|
||||||
|
--footer-bg-color: rgba(0,0,0,.4);
|
||||||
|
--separator-color: #42474f;
|
||||||
|
|
||||||
|
--table-strip-bg-color: #151515;
|
||||||
|
--table-header-bg-color: black;
|
||||||
|
--table-header-color: hsla(0,0%,100%,.8);
|
||||||
|
--table-header-border-color: #040405;
|
||||||
|
|
||||||
|
/* Text */
|
||||||
|
|
||||||
|
--appname-color: white;
|
||||||
|
|
||||||
|
--h1-color: white;
|
||||||
|
--h2-color: #f2f2f2;
|
||||||
|
--h3-color: #e3e3e3;
|
||||||
|
--h4-color: #ffffff;
|
||||||
|
--h5-color: #e0e0e0;
|
||||||
|
|
||||||
|
--text-color: #e1e1e1;
|
||||||
|
--link-color: #00b0f4;
|
||||||
|
--link-hover-color: #2ec4ff;
|
||||||
|
|
||||||
|
/* Mobile Topbar */
|
||||||
|
|
||||||
|
--topbar-bg-color: #18191c;
|
||||||
|
|
||||||
|
/* Button */
|
||||||
|
|
||||||
|
--button-color: #747f8d;
|
||||||
|
|
||||||
|
/* Sidebar */
|
||||||
|
|
||||||
|
--sidebar-width: 400px;
|
||||||
|
--sidebar-bg-color: #292B30;
|
||||||
|
|
||||||
|
--search-color: #bdbdbd;
|
||||||
|
--search-bg-color: #1b1e21;
|
||||||
|
--search-searchicon-color: #e3e3e3;
|
||||||
|
--search-border-color: black;
|
||||||
|
|
||||||
|
--sidebar-item-color: white;
|
||||||
|
--sidebar-active-item-color: #00b0f4;
|
||||||
|
--sidebar-level1-item-bg-color: #222429;
|
||||||
|
--sidebar-level1-item-hover-bg-color: #1D1F22;
|
||||||
|
|
||||||
|
--toc-filter-color: #bdbdbd;
|
||||||
|
--toc-filter-bg-color: #1b1e21;
|
||||||
|
--toc-filter-filtericon-color: #e3e3e3;
|
||||||
|
--toc-filter-clearicon-color: #e68585;
|
||||||
|
--toc-filter-border-color: black;
|
||||||
|
|
||||||
|
/* Scrollbars */
|
||||||
|
|
||||||
|
--scrollbar-bg-color: transparent;
|
||||||
|
--scrollbar-thumb-bg-color: rgba(0,0,0,.4);
|
||||||
|
--scrollbar-thumb-border-color: transparent;
|
||||||
|
|
||||||
|
/* Alerts and Blocks */
|
||||||
|
|
||||||
|
--alert-info-border-color: rgba(114,137,218,.5);
|
||||||
|
--alert-info-bg-color: rgba(114,137,218,.1);
|
||||||
|
|
||||||
|
--alert-warning-border-color: rgba(250,166,26,.5);
|
||||||
|
--alert-warning-bg-color: rgba(250,166,26,.1);
|
||||||
|
|
||||||
|
--alert-danger-border-color: rgba(240,71,71,.5);
|
||||||
|
--alert-danger-bg-color: rgba(240,71,71,.1);
|
||||||
|
|
||||||
|
--alert-tip-border-color: rgba(255,255,255,.5);
|
||||||
|
--alert-tip-bg-color: rgba(255,255,255,.1);
|
||||||
|
|
||||||
|
--blockquote-border-color: rgba(255,255,255,.5);
|
||||||
|
--blockquote-bg-color: rgba(255,255,255,.1);
|
||||||
|
|
||||||
|
--breadcrumb-bg-color: #2f3136;
|
||||||
|
|
||||||
|
/* Inline Code */
|
||||||
|
|
||||||
|
--ref-bg-color: black;
|
||||||
|
--ref-color: #89d4f1;
|
||||||
|
|
||||||
|
/* Code Blocks */
|
||||||
|
|
||||||
|
--code-bg-color: #151515;
|
||||||
|
--code-color: #d6deeb;
|
||||||
|
--code-keyword-color: #569cd6;
|
||||||
|
--code-comment-color: #57a64a;
|
||||||
|
--code-macro-color: #beb7ff;
|
||||||
|
--code-string-color: #d69d85;
|
||||||
|
--code-string-escape-color: #ffd68f;
|
||||||
|
--code-field-color: #c8c8c8;
|
||||||
|
--code-function-color: #dcdcaa;
|
||||||
|
--code-control-color: #d8a0df;
|
||||||
|
--code-class-color: #4ec9b0;
|
||||||
|
--code-number-color: #b5cea8;
|
||||||
|
--code-params-color: #9a9a9a;
|
||||||
|
--code-breakpoint-color: #8c2f2f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Code Block Overrides */
|
||||||
|
|
||||||
|
pre, legend {
|
||||||
|
--scrollbar-thumb-bg-color: #333;
|
||||||
|
}
|
||||||
681
docfx/docfx_project/templates/singulinkfx/styles/discord.css
Normal file
681
docfx/docfx_project/templates/singulinkfx/styles/discord.css
Normal file
@ -0,0 +1,681 @@
|
|||||||
|
/* Discord Style */
|
||||||
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background: var(--scrollbar-bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--scrollbar-thumb-bg-color);
|
||||||
|
border-color: var(--scrollbar-thumb-border-color);
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::marker {
|
||||||
|
unicode-bidi: isolate;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
text-transform: none;
|
||||||
|
text-indent: 0px !important;
|
||||||
|
text-align: start !important;
|
||||||
|
text-align-last: start !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
*, :after, :before
|
||||||
|
{
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body
|
||||||
|
{
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
font: 15px/150% 'Roboto', sans-serif;
|
||||||
|
overflow: hidden;
|
||||||
|
color: var(--text-color);
|
||||||
|
background-color: var(--main-bg-color);
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul > li, ol > li {
|
||||||
|
display: list-item;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,h2,h3,h4,h5
|
||||||
|
{
|
||||||
|
color: var(--link-active-color);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2
|
||||||
|
{
|
||||||
|
margin-block-start: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3
|
||||||
|
{
|
||||||
|
margin-block-start: 1em;
|
||||||
|
font-weight: 300;
|
||||||
|
font-size: 1.5em;
|
||||||
|
color: var(--h3-color);
|
||||||
|
margin-block-start: 3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4
|
||||||
|
{
|
||||||
|
opacity: 1;
|
||||||
|
color: var(--h4-color);
|
||||||
|
font-size: large;
|
||||||
|
border-bottom: 2px solid var(--separator-color);
|
||||||
|
margin: 20px 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
margin-block-end: .8em;
|
||||||
|
margin-block-start: 1em;
|
||||||
|
font-size: .85em;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--h5-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
h6 {
|
||||||
|
font-size: .75em;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
p
|
||||||
|
{
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul
|
||||||
|
{
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul, ol
|
||||||
|
{
|
||||||
|
padding-inline-start: 3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.level1
|
||||||
|
{
|
||||||
|
list-style-type: none;
|
||||||
|
padding-inline-start: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.level2, ul.level3
|
||||||
|
{
|
||||||
|
padding-inline-start: 1em;
|
||||||
|
list-style-type: none;
|
||||||
|
font-size: .9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a
|
||||||
|
{
|
||||||
|
color: var(--link-color);
|
||||||
|
text-decoration: none;
|
||||||
|
transition: color .25s;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:focus, a:hover
|
||||||
|
{
|
||||||
|
color: var(--link-hover-color);
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.anchorjs-link:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.active, a:active
|
||||||
|
{
|
||||||
|
color: var(--link-active-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.body-content
|
||||||
|
{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
height: 100%;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title
|
||||||
|
{
|
||||||
|
margin-block-start: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav
|
||||||
|
{
|
||||||
|
width: 300px;
|
||||||
|
transition: left .5s ease-out;
|
||||||
|
position: fixed;
|
||||||
|
left: -350px;
|
||||||
|
top: 40px;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: var(--sidebar-bg-color);
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1:first-child
|
||||||
|
{
|
||||||
|
margin-block-start: 1.1em;
|
||||||
|
margin-top: 1.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar
|
||||||
|
{
|
||||||
|
padding: 32px 17px 32px 32px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-item
|
||||||
|
{
|
||||||
|
font-size: 1em;
|
||||||
|
font-weight: 400;
|
||||||
|
display: block;
|
||||||
|
padding: 4px 16px;
|
||||||
|
color: var(--sidebar-item-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-item.large, #navbar .sidebar-item
|
||||||
|
{
|
||||||
|
padding: 8px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.sidebar-item:hover, a.sidebar-item:focus
|
||||||
|
{
|
||||||
|
color: var(--link-active-color);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.sidebar-item.active
|
||||||
|
{
|
||||||
|
color: var(--link-active-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.level1 > li > a.sidebar-item
|
||||||
|
{
|
||||||
|
background-color: transparent;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#toc ul.level1 > li > a.sidebar-item.active
|
||||||
|
{
|
||||||
|
background-color: var(--link-active-bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-item-separator
|
||||||
|
{
|
||||||
|
height: 2px;
|
||||||
|
width: 100%;
|
||||||
|
background-color: var(--separator-color);
|
||||||
|
margin: 2em 0;
|
||||||
|
opacity: .8;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.sidebar-item
|
||||||
|
{
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: .8em;
|
||||||
|
color: var(--text-color);
|
||||||
|
margin-block-start: 1.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-panel
|
||||||
|
{
|
||||||
|
background-color: var(--main-bg-color);
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 20px 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-navbar
|
||||||
|
{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 40px;
|
||||||
|
height: 40px;
|
||||||
|
background-color: var(--topbar-bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.burger-icon
|
||||||
|
{
|
||||||
|
margin-right: 1em;
|
||||||
|
color: var(--button-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.burger-icon:hover, .burger-icon:focus
|
||||||
|
{
|
||||||
|
color: var(--link-active-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.burger-icon.active, .burger-icon:active
|
||||||
|
{
|
||||||
|
color: var(--link-active-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand
|
||||||
|
{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logomark
|
||||||
|
{
|
||||||
|
height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand-title
|
||||||
|
{
|
||||||
|
padding: 0 .5em;
|
||||||
|
font-size: .9em;
|
||||||
|
color: var(--link-active-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer
|
||||||
|
{
|
||||||
|
background-color: var(--footer-bg-color);
|
||||||
|
padding: 20px;
|
||||||
|
margin: 0 20px 20px 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
color: var(--link-active-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer > h4
|
||||||
|
{
|
||||||
|
margin-block-start: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blackout
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
visibility: hidden;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 100;
|
||||||
|
top: 40px;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background-color: var(--footer-bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes showThat {
|
||||||
|
0% { opacity: 0; visibility: hidden; }
|
||||||
|
1% { opacity: 0; visibility: visible; }
|
||||||
|
100% { opacity: 1; visibility: visible;}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes hideThat {
|
||||||
|
0% { opacity: 1; visibility: visible; }
|
||||||
|
99% { opacity: 0; visibility: visible; }
|
||||||
|
100% { opacity: 0; visibility: hidden;}
|
||||||
|
}
|
||||||
|
|
||||||
|
.showThat
|
||||||
|
{
|
||||||
|
animation: showThat .5s forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hideThat
|
||||||
|
{
|
||||||
|
animation: hideThat .5s forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@media (min-width: 1024px)
|
||||||
|
{
|
||||||
|
nav
|
||||||
|
{
|
||||||
|
position: relative;
|
||||||
|
left: 0!important;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-navbar
|
||||||
|
{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blackout
|
||||||
|
{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Table */
|
||||||
|
|
||||||
|
.table-responsive
|
||||||
|
{
|
||||||
|
overflow-x: auto;
|
||||||
|
margin-bottom: 64px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table
|
||||||
|
{
|
||||||
|
background-color: var(--code-bg-color);
|
||||||
|
border-collapse: collapse;
|
||||||
|
width: 100%;
|
||||||
|
table-layout: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.table-striped tbody tr:nth-child(2n)
|
||||||
|
{
|
||||||
|
background-color: var(--table-strip-bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
table thead
|
||||||
|
{
|
||||||
|
background: var(--table-header-bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
table th
|
||||||
|
{
|
||||||
|
color: var(--table-header-color);
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 15px;
|
||||||
|
border-bottom: 1px solid var(--table-header-border-color);
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-condensed th {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
table td
|
||||||
|
{
|
||||||
|
padding: 8px;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
table td > p
|
||||||
|
{
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Alerts */
|
||||||
|
.alert {
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 8px;
|
||||||
|
margin: 25px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert > h5
|
||||||
|
{
|
||||||
|
display: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert > p
|
||||||
|
{
|
||||||
|
margin: 0;
|
||||||
|
font-weight: 300;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert.alert-info
|
||||||
|
{
|
||||||
|
border: 2px solid var(--alert-info-border-color);
|
||||||
|
background: var(--alert-info-bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert.alert-warning
|
||||||
|
{
|
||||||
|
border: 2px solid var(--alert-warning-border-color);
|
||||||
|
background: var(--alert-warning-bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert.alert-danger
|
||||||
|
{
|
||||||
|
border: 2px solid var(--alert-danger-border-color);
|
||||||
|
background: var(--alert-danger-bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.TIP.alert.alert-info
|
||||||
|
{
|
||||||
|
border: 2px solid var(--alert-tip-border-color);
|
||||||
|
background: var(--alert-tip-bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
margin: 8px 0;
|
||||||
|
border-left: 4px solid var(--blockquote-border-color);
|
||||||
|
padding: 8px;
|
||||||
|
background: var(--blockquote-bg-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote > p {
|
||||||
|
margin: 0;
|
||||||
|
font-style: italic;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Breadcrumb */
|
||||||
|
|
||||||
|
#breadcrumb
|
||||||
|
{
|
||||||
|
padding: 8px 16px;
|
||||||
|
background: var(--breadcrumb-bg-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#breadcrumb:empty
|
||||||
|
{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.breadcrumb
|
||||||
|
{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.breadcrumb > li {
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.breadcrumb > li::before
|
||||||
|
{
|
||||||
|
content: "/";
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.breadcrumb > li:first-child::before
|
||||||
|
{
|
||||||
|
content: "";
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Code */
|
||||||
|
|
||||||
|
legend, pre
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
background-color: var(--code-bg-color);
|
||||||
|
padding: 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
code
|
||||||
|
{
|
||||||
|
background-color: var(--code-bg-color);
|
||||||
|
padding: 2px 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs
|
||||||
|
{
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DocFX related */
|
||||||
|
|
||||||
|
.small {
|
||||||
|
font-size: .9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pull-right
|
||||||
|
{
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hide
|
||||||
|
{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1023.98px)
|
||||||
|
{
|
||||||
|
.mobile-hide
|
||||||
|
{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
li
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expand-stub
|
||||||
|
{
|
||||||
|
cursor: pointer;
|
||||||
|
position: absolute;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
left: -10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.level1 > li > .expand-stub
|
||||||
|
{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc .nav > li > .expand-stub::before, .toc .nav > li.active > .expand-stub::before
|
||||||
|
{
|
||||||
|
content: " ";
|
||||||
|
position: absolute;
|
||||||
|
transform: rotate(-90deg);
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
top: 5px;
|
||||||
|
left: 5px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background: url(down-arrow.svg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc .nav > li.active > .expand-stub::before, .toc .nav > li.in > .expand-stub::before, .toc .nav > li.in.active > .expand-stub::before, .toc .nav > li.filtered > .expand-stub::before
|
||||||
|
{
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
li > ul
|
||||||
|
{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.in > ul
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.level2 > li > a.sidebar-item,
|
||||||
|
ul.level3 > li > a.sidebar-item
|
||||||
|
{
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: .95em;
|
||||||
|
padding: 0;
|
||||||
|
margin: 2px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.level2 > li > a.sidebar-item
|
||||||
|
{
|
||||||
|
color: var(--sidebar-item-2nd-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.level3 > li > a.sidebar-item
|
||||||
|
{
|
||||||
|
color: var(--sidebar-item-3rd-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.level2 > li > a.sidebar-item:hover,
|
||||||
|
ul.level2 > li > a.sidebar-item:focus,
|
||||||
|
ul.level3 > li > a.sidebar-item:hover,
|
||||||
|
ul.level3 > li > a.sidebar-item:focus
|
||||||
|
{
|
||||||
|
color: var(--link-active-color);
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.level2 > li > a.sidebar-item.active,
|
||||||
|
ul.level3 > li > a.sidebar-item.active
|
||||||
|
{
|
||||||
|
color: var(--link-active-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.inheritance .level0:before,
|
||||||
|
.inheritance .level1:before,
|
||||||
|
.inheritance .level2:before,
|
||||||
|
.inheritance .level3:before,
|
||||||
|
.inheritance .level4:before,
|
||||||
|
.inheritance .level5:before {
|
||||||
|
content: '↳';
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inheritance .level0 {
|
||||||
|
margin-left: 0em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inheritance .level1 {
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inheritance .level2 {
|
||||||
|
margin-left: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inheritance .level3 {
|
||||||
|
margin-left: 3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inheritance .level4 {
|
||||||
|
margin-left: 4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inheritance .level5 {
|
||||||
|
margin-left: 5em;
|
||||||
|
}
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 491.996 491.996" style="enable-background:new 0 0 491.996 491.996;" xml:space="preserve">
|
||||||
|
<g>
|
||||||
|
<g color="white">
|
||||||
|
<path d="M484.132,124.986l-16.116-16.228c-5.072-5.068-11.82-7.86-19.032-7.86c-7.208,0-13.964,2.792-19.036,7.86l-183.84,183.848
|
||||||
|
L62.056,108.554c-5.064-5.068-11.82-7.856-19.028-7.856s-13.968,2.788-19.036,7.856l-16.12,16.128
|
||||||
|
c-10.496,10.488-10.496,27.572,0,38.06l219.136,219.924c5.064,5.064,11.812,8.632,19.084,8.632h0.084
|
||||||
|
c7.212,0,13.96-3.572,19.024-8.632l218.932-219.328c5.072-5.064,7.856-12.016,7.864-19.224
|
||||||
|
C491.996,136.902,489.204,130.046,484.132,124.986z" fill="currentcolor"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
@ -0,0 +1,317 @@
|
|||||||
|
/*!
|
||||||
|
* jQuery pagination plugin v1.4.1
|
||||||
|
* http://esimakin.github.io/twbs-pagination/
|
||||||
|
*
|
||||||
|
* Copyright 2014-2016, Eugene Simakin
|
||||||
|
* Released under Apache 2.0 license
|
||||||
|
* http://apache.org/licenses/LICENSE-2.0.html
|
||||||
|
*/
|
||||||
|
(function ($, window, document, undefined) {
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var old = $.fn.twbsPagination;
|
||||||
|
|
||||||
|
// PROTOTYPE AND CONSTRUCTOR
|
||||||
|
|
||||||
|
var TwbsPagination = function (element, options) {
|
||||||
|
this.$element = $(element);
|
||||||
|
this.options = $.extend({}, $.fn.twbsPagination.defaults, options);
|
||||||
|
|
||||||
|
if (this.options.startPage < 1 || this.options.startPage > this.options.totalPages) {
|
||||||
|
throw new Error('Start page option is incorrect');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.options.totalPages = parseInt(this.options.totalPages);
|
||||||
|
if (isNaN(this.options.totalPages)) {
|
||||||
|
throw new Error('Total pages option is not correct!');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.options.visiblePages = parseInt(this.options.visiblePages);
|
||||||
|
if (isNaN(this.options.visiblePages)) {
|
||||||
|
throw new Error('Visible pages option is not correct!');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.options.onPageClick instanceof Function) {
|
||||||
|
this.$element.first().on('page', this.options.onPageClick);
|
||||||
|
}
|
||||||
|
|
||||||
|
// hide if only one page exists
|
||||||
|
if (this.options.hideOnlyOnePage && this.options.totalPages == 1) {
|
||||||
|
this.$element.trigger('page', 1);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.options.totalPages < this.options.visiblePages) {
|
||||||
|
this.options.visiblePages = this.options.totalPages;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.options.href) {
|
||||||
|
this.options.startPage = this.getPageFromQueryString();
|
||||||
|
if (!this.options.startPage) {
|
||||||
|
this.options.startPage = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var tagName = (typeof this.$element.prop === 'function') ?
|
||||||
|
this.$element.prop('tagName') : this.$element.attr('tagName');
|
||||||
|
|
||||||
|
if (tagName === 'UL') {
|
||||||
|
this.$listContainer = this.$element;
|
||||||
|
} else {
|
||||||
|
this.$listContainer = $('<ul></ul>');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$listContainer.addClass(this.options.paginationClass);
|
||||||
|
|
||||||
|
if (tagName !== 'UL') {
|
||||||
|
this.$element.append(this.$listContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.options.initiateStartPageClick) {
|
||||||
|
this.show(this.options.startPage);
|
||||||
|
} else {
|
||||||
|
this.render(this.getPages(this.options.startPage));
|
||||||
|
this.setupEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
TwbsPagination.prototype = {
|
||||||
|
|
||||||
|
constructor: TwbsPagination,
|
||||||
|
|
||||||
|
destroy: function () {
|
||||||
|
this.$element.empty();
|
||||||
|
this.$element.removeData('twbs-pagination');
|
||||||
|
this.$element.off('page');
|
||||||
|
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
show: function (page) {
|
||||||
|
if (page < 1 || page > this.options.totalPages) {
|
||||||
|
throw new Error('Page is incorrect.');
|
||||||
|
}
|
||||||
|
this.currentPage = page;
|
||||||
|
|
||||||
|
this.render(this.getPages(page));
|
||||||
|
this.setupEvents();
|
||||||
|
|
||||||
|
this.$element.trigger('page', page);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
buildListItems: function (pages) {
|
||||||
|
var listItems = [];
|
||||||
|
|
||||||
|
if (this.options.first) {
|
||||||
|
listItems.push(this.buildItem('first', 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.options.prev) {
|
||||||
|
var prev = pages.currentPage > 1 ? pages.currentPage - 1 : this.options.loop ? this.options.totalPages : 1;
|
||||||
|
listItems.push(this.buildItem('prev', prev));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < pages.numeric.length; i++) {
|
||||||
|
listItems.push(this.buildItem('page', pages.numeric[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.options.next) {
|
||||||
|
var next = pages.currentPage < this.options.totalPages ? pages.currentPage + 1 : this.options.loop ? 1 : this.options.totalPages;
|
||||||
|
listItems.push(this.buildItem('next', next));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.options.last) {
|
||||||
|
listItems.push(this.buildItem('last', this.options.totalPages));
|
||||||
|
}
|
||||||
|
|
||||||
|
return listItems;
|
||||||
|
},
|
||||||
|
|
||||||
|
buildItem: function (type, page) {
|
||||||
|
var $itemContainer = $('<li></li>'),
|
||||||
|
$itemContent = $('<a></a>'),
|
||||||
|
itemText = this.options[type] ? this.makeText(this.options[type], page) : page;
|
||||||
|
|
||||||
|
$itemContainer.addClass(this.options[type + 'Class']);
|
||||||
|
$itemContainer.data('page', page);
|
||||||
|
$itemContainer.data('page-type', type);
|
||||||
|
$itemContainer.append($itemContent.attr('href', this.makeHref(page)).addClass(this.options.anchorClass).html(itemText));
|
||||||
|
|
||||||
|
return $itemContainer;
|
||||||
|
},
|
||||||
|
|
||||||
|
getPages: function (currentPage) {
|
||||||
|
var pages = [];
|
||||||
|
|
||||||
|
var half = Math.floor(this.options.visiblePages / 2);
|
||||||
|
var start = currentPage - half + 1 - this.options.visiblePages % 2;
|
||||||
|
var end = currentPage + half;
|
||||||
|
|
||||||
|
// handle boundary case
|
||||||
|
if (start <= 0) {
|
||||||
|
start = 1;
|
||||||
|
end = this.options.visiblePages;
|
||||||
|
}
|
||||||
|
if (end > this.options.totalPages) {
|
||||||
|
start = this.options.totalPages - this.options.visiblePages + 1;
|
||||||
|
end = this.options.totalPages;
|
||||||
|
}
|
||||||
|
|
||||||
|
var itPage = start;
|
||||||
|
while (itPage <= end) {
|
||||||
|
pages.push(itPage);
|
||||||
|
itPage++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {"currentPage": currentPage, "numeric": pages};
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function (pages) {
|
||||||
|
var _this = this;
|
||||||
|
this.$listContainer.children().remove();
|
||||||
|
var items = this.buildListItems(pages);
|
||||||
|
jQuery.each(items, function(key, item){
|
||||||
|
_this.$listContainer.append(item);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.$listContainer.children().each(function () {
|
||||||
|
var $this = $(this),
|
||||||
|
pageType = $this.data('page-type');
|
||||||
|
|
||||||
|
switch (pageType) {
|
||||||
|
case 'page':
|
||||||
|
if ($this.data('page') === pages.currentPage) {
|
||||||
|
$this.addClass(_this.options.activeClass);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'first':
|
||||||
|
$this.toggleClass(_this.options.disabledClass, pages.currentPage === 1);
|
||||||
|
break;
|
||||||
|
case 'last':
|
||||||
|
$this.toggleClass(_this.options.disabledClass, pages.currentPage === _this.options.totalPages);
|
||||||
|
break;
|
||||||
|
case 'prev':
|
||||||
|
$this.toggleClass(_this.options.disabledClass, !_this.options.loop && pages.currentPage === 1);
|
||||||
|
break;
|
||||||
|
case 'next':
|
||||||
|
$this.toggleClass(_this.options.disabledClass,
|
||||||
|
!_this.options.loop && pages.currentPage === _this.options.totalPages);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setupEvents: function () {
|
||||||
|
var _this = this;
|
||||||
|
this.$listContainer.off('click').on('click', 'li', function (evt) {
|
||||||
|
var $this = $(this);
|
||||||
|
if ($this.hasClass(_this.options.disabledClass) || $this.hasClass(_this.options.activeClass)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Prevent click event if href is not set.
|
||||||
|
!_this.options.href && evt.preventDefault();
|
||||||
|
_this.show(parseInt($this.data('page')));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
makeHref: function (page) {
|
||||||
|
return this.options.href ? this.generateQueryString(page) : "#";
|
||||||
|
},
|
||||||
|
|
||||||
|
makeText: function (text, page) {
|
||||||
|
return text.replace(this.options.pageVariable, page)
|
||||||
|
.replace(this.options.totalPagesVariable, this.options.totalPages)
|
||||||
|
},
|
||||||
|
getPageFromQueryString: function (searchStr) {
|
||||||
|
var search = this.getSearchString(searchStr),
|
||||||
|
regex = new RegExp(this.options.pageVariable + '(=([^&#]*)|&|#|$)'),
|
||||||
|
page = regex.exec(search);
|
||||||
|
if (!page || !page[2]) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
page = decodeURIComponent(page[2]);
|
||||||
|
page = parseInt(page);
|
||||||
|
if (isNaN(page)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return page;
|
||||||
|
},
|
||||||
|
generateQueryString: function (pageNumber, searchStr) {
|
||||||
|
var search = this.getSearchString(searchStr),
|
||||||
|
regex = new RegExp(this.options.pageVariable + '=*[^&#]*');
|
||||||
|
if (!search) return '';
|
||||||
|
return '?' + search.replace(regex, this.options.pageVariable + '=' + pageNumber);
|
||||||
|
|
||||||
|
},
|
||||||
|
getSearchString: function (searchStr) {
|
||||||
|
var search = searchStr || window.location.search;
|
||||||
|
if (search === '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (search.indexOf('?') === 0) search = search.substr(1);
|
||||||
|
return search;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// PLUGIN DEFINITION
|
||||||
|
|
||||||
|
$.fn.twbsPagination = function (option) {
|
||||||
|
var args = Array.prototype.slice.call(arguments, 1);
|
||||||
|
var methodReturn;
|
||||||
|
|
||||||
|
var $this = $(this);
|
||||||
|
var data = $this.data('twbs-pagination');
|
||||||
|
var options = typeof option === 'object' ? option : {};
|
||||||
|
|
||||||
|
if (!data) $this.data('twbs-pagination', (data = new TwbsPagination(this, options) ));
|
||||||
|
if (typeof option === 'string') methodReturn = data[ option ].apply(data, args);
|
||||||
|
|
||||||
|
return ( methodReturn === undefined ) ? $this : methodReturn;
|
||||||
|
};
|
||||||
|
|
||||||
|
$.fn.twbsPagination.defaults = {
|
||||||
|
totalPages: 1,
|
||||||
|
startPage: 1,
|
||||||
|
visiblePages: 5,
|
||||||
|
initiateStartPageClick: true,
|
||||||
|
hideOnlyOnePage: false,
|
||||||
|
href: false,
|
||||||
|
pageVariable: '{{page}}',
|
||||||
|
totalPagesVariable: '{{total_pages}}',
|
||||||
|
page: null,
|
||||||
|
first: 'First',
|
||||||
|
prev: 'Previous',
|
||||||
|
next: 'Next',
|
||||||
|
last: 'Last',
|
||||||
|
loop: false,
|
||||||
|
onPageClick: null,
|
||||||
|
paginationClass: 'pagination',
|
||||||
|
nextClass: 'page-item next',
|
||||||
|
prevClass: 'page-item prev',
|
||||||
|
lastClass: 'page-item last',
|
||||||
|
firstClass: 'page-item first',
|
||||||
|
pageClass: 'page-item',
|
||||||
|
activeClass: 'active',
|
||||||
|
disabledClass: 'disabled',
|
||||||
|
anchorClass: 'page-link'
|
||||||
|
};
|
||||||
|
|
||||||
|
$.fn.twbsPagination.Constructor = TwbsPagination;
|
||||||
|
|
||||||
|
$.fn.twbsPagination.noConflict = function () {
|
||||||
|
$.fn.twbsPagination = old;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
$.fn.twbsPagination.version = "1.4.1";
|
||||||
|
|
||||||
|
})(window.jQuery, window, document);
|
||||||
471
docfx/docfx_project/templates/singulinkfx/styles/singulink.css
Normal file
471
docfx/docfx_project/templates/singulinkfx/styles/singulink.css
Normal file
@ -0,0 +1,471 @@
|
|||||||
|
body {
|
||||||
|
font-size: var(--base-font-size);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
body {
|
||||||
|
font-size: var(--smalldevice-base-font-size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Headings */
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5 {
|
||||||
|
line-height: initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h1:first-child {
|
||||||
|
font-size: 2.25em;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
color: var(--h1-color);
|
||||||
|
margin-block-start: 1em;
|
||||||
|
margin-block-end: -0.05em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article h1 {
|
||||||
|
margin-block-end: -0.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 2.1em;
|
||||||
|
color: var(--h2-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.article h2 {
|
||||||
|
margin-block-start: 1.3em;
|
||||||
|
padding-bottom: 6px;
|
||||||
|
border-bottom: 1px solid var(--separator-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 1.95em;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-block-start: 1.7em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article h3 {
|
||||||
|
font-size: 1.85em;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-block-start: 1.2em;
|
||||||
|
margin-block-end: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size: 1.8em;
|
||||||
|
font-weight: 400;
|
||||||
|
margin-block-start: 2em;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article h4 {
|
||||||
|
font-size: 1.5em;
|
||||||
|
font-weight: 300;
|
||||||
|
margin-block-start: 1em;
|
||||||
|
margin-block-end: 1em;
|
||||||
|
padding-bottom: 0;
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article h5 {
|
||||||
|
font-size: 1.13em;
|
||||||
|
font-weight: 400;
|
||||||
|
text-decoration: underline;
|
||||||
|
margin-block-start: 1.5em;
|
||||||
|
margin-block-end: 1.0em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.brand:hover
|
||||||
|
{
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.brand .brand-title {
|
||||||
|
font-size: 1.4em;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
color: var(--appname-color);
|
||||||
|
margin-top: 1px;
|
||||||
|
padding: 0 0 0 0.4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1024px) {
|
||||||
|
a.brand .brand-title {
|
||||||
|
font-size: 1.55em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
a.brand .logomark {
|
||||||
|
height: 35px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Top bar */
|
||||||
|
|
||||||
|
.top-navbar {
|
||||||
|
height: 60px;
|
||||||
|
padding: 0 0 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.burger-icon {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Side Bar */
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
padding: 25px 17px 32px 17px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blackout {
|
||||||
|
top: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1023.98px) {
|
||||||
|
.navbar-nav {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nav {
|
||||||
|
width: 94%;
|
||||||
|
max-width: var(--sidebar-width);
|
||||||
|
left: calc(var(--sidebar-width) * -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1023.98px) {
|
||||||
|
nav {
|
||||||
|
top: 60px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul {
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav .nav a, nav .nav a:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a.sidebar-item {
|
||||||
|
padding: 4px 0 4px 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a:focus, nav a.sidebar-item:hover, nav a.sidebar-item:focus {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a, nav a:hover, nav a:focus {
|
||||||
|
color: var(--sidebar-item-color) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a.active, nav a.active:hover, nav a.active:focus {
|
||||||
|
color: var(--sidebar-active-item-color) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-item-separator {
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#toc ul li a {
|
||||||
|
padding: 0 0 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search {
|
||||||
|
background: var(--search-bg-color);
|
||||||
|
border: 1px solid var(--search-border-color);
|
||||||
|
border-radius: 5px;
|
||||||
|
position: relative;
|
||||||
|
margin-block-start: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1023.98px) {
|
||||||
|
.search {
|
||||||
|
margin-block-start: 0;
|
||||||
|
margin-block-end: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search > input {
|
||||||
|
font-size: 0.95em;
|
||||||
|
color: var(--search-color);
|
||||||
|
border: 0;
|
||||||
|
background: none;
|
||||||
|
padding: 11px 30px 10px 37px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search > input:focus {
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search > .search-icon {
|
||||||
|
font-size: 1.2em;
|
||||||
|
color: var(--search-searchicon-color);
|
||||||
|
position: absolute;
|
||||||
|
top: 9px;
|
||||||
|
left: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-filter {
|
||||||
|
background: var(--toc-filter-bg-color);
|
||||||
|
border: 1px solid var(--toc-filter-border-color);
|
||||||
|
border-radius: 5px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-filter > input {
|
||||||
|
font-size: 0.95em;
|
||||||
|
color: var(--toc-filter-color);
|
||||||
|
border: 0;
|
||||||
|
background: none;
|
||||||
|
padding: 11px 30px 10px 37px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-filter > input:focus {
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-filter > .filter-icon {
|
||||||
|
font-size: 1.2em;
|
||||||
|
color: var(--toc-filter-filtericon-color);
|
||||||
|
position: absolute;
|
||||||
|
top: 9px;
|
||||||
|
left: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-filter > .clear-icon {
|
||||||
|
color: var(--toc-filter-clearicon-color);
|
||||||
|
position: absolute;
|
||||||
|
top: 9px;
|
||||||
|
right: 9px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc .nav > li > .expand-stub::before, .toc .nav > li.active > .expand-stub::before
|
||||||
|
{
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
top: 6px;
|
||||||
|
left: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#toc ul.level2
|
||||||
|
{
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#toc ul.level1 > li > a {
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#toc ul.level1 > li > a, #toc ul.level1 > li > a.active {
|
||||||
|
background-color: var(--sidebar-level1-item-bg-color) !important;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#toc ul.level1 > li > a:hover, #toc ul.level1 > li > a.active:hover,
|
||||||
|
#toc ul.level1 > li > a:focus, #toc ul.level1 > li > a.active:focus {
|
||||||
|
background-color: var(--sidebar-level1-item-hover-bg-color) !important;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.level2 {
|
||||||
|
padding-inline-start: 0.7em;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.level2 .expand-stub {
|
||||||
|
top: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.level2 > li > a, ul.level2 > li > a.sidebar-item {
|
||||||
|
font-weight: 400;
|
||||||
|
color: var(--sidebar-item-color);
|
||||||
|
margin: 4px 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.level3 {
|
||||||
|
padding-inline-start: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.level3 > li > a, ul.level3 > li > a.sidebar-item {
|
||||||
|
font-size: 1.05em;
|
||||||
|
color: var(--sidebar-item-color);
|
||||||
|
margin: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.level4 {
|
||||||
|
padding-inline-start: 0;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.level4 > li > a, ul.level4 > li > a.sidebar-item {
|
||||||
|
font-size: 1.05em;
|
||||||
|
color: var(--sidebar-item-color);
|
||||||
|
margin: 5px 0 5px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Breadcrumbs */
|
||||||
|
|
||||||
|
.subnav.navbar {
|
||||||
|
margin: 0 -15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#breadcrumb {
|
||||||
|
overflow: scroll;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#breadcrumb::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#breadcrumb a {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
#breadcrumb wbr {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Search Results */
|
||||||
|
|
||||||
|
#search-results h1 {
|
||||||
|
margin-block-start: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#search-results .item-title {
|
||||||
|
font-size: 1.3em;
|
||||||
|
margin-top: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#search-results .item-href {
|
||||||
|
font-size: 0.85em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#search-results .item-brief {
|
||||||
|
margin-top: 0.7em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#search-results ul.pagination {
|
||||||
|
text-align: center;
|
||||||
|
padding: 10px 0 0 0;
|
||||||
|
margin-block-start: 40px;
|
||||||
|
border-top: 1px solid var(--separator-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#search-results ul.pagination > li {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#search-results ul.pagination > li.disabled a, #search-results ul.pagination > li.disabled a:hover {
|
||||||
|
color: var(--text-color);
|
||||||
|
cursor: txt;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Content */
|
||||||
|
|
||||||
|
.main-panel {
|
||||||
|
margin-bottom: 60px;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1024px) {
|
||||||
|
.main-panel {
|
||||||
|
margin-bottom: 0;
|
||||||
|
padding: 20px 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pull-right {
|
||||||
|
margin-top: 70px;
|
||||||
|
/* Fix unclickable links */
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
article ul li, article ol li {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
legend, pre {
|
||||||
|
padding: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs {
|
||||||
|
color: var(--code-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs::-webkit-scrollbar {
|
||||||
|
height: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-keyword, .hljs-title, .hljs-built_in {
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
p .xref, code {
|
||||||
|
background-color: var(--ref-bg-color);
|
||||||
|
color: var(--ref-color);
|
||||||
|
padding: 2px 3px;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 0.95em;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.parametername {
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-responsive {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
table th {
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 9px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table td p {
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
table td {
|
||||||
|
padding: 6px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
text-align: center;
|
||||||
|
color: var(--text-color);
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer a:hover, .footer a:focus {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copyright-footer {
|
||||||
|
font-size: 0.85em;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
margin-block-start: 30px;
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
function toggleMenu() {
|
||||||
|
|
||||||
|
var sidebar = document.getElementById("sidebar");
|
||||||
|
var blackout = document.getElementById("blackout");
|
||||||
|
|
||||||
|
if (sidebar.style.left === "0px")
|
||||||
|
{
|
||||||
|
sidebar.style.left = "-" + sidebar.offsetWidth + "px";
|
||||||
|
blackout.classList.remove("showThat");
|
||||||
|
blackout.classList.add("hideThat");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sidebar.style.left = "0px";
|
||||||
|
blackout.classList.remove("hideThat");
|
||||||
|
blackout.classList.add("showThat");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
$('table').each(function(a, tbl) {
|
||||||
|
var currentTableRows = $(tbl).find('tbody tr').length;
|
||||||
|
$(tbl).find('th').each(function(i) {
|
||||||
|
var remove = 0;
|
||||||
|
var currentTable = $(this).parents('table');
|
||||||
|
|
||||||
|
var tds = currentTable.find('tr td:nth-child(' + (i + 1) + ')');
|
||||||
|
tds.each(function(j) { if ($(this).text().trim() === '') remove++; });
|
||||||
|
|
||||||
|
if (remove == currentTableRows) {
|
||||||
|
$(this).hide();
|
||||||
|
tds.hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
1
docfx/docfx_project/templates/singulinkfx/styles/url.min.js
vendored
Normal file
1
docfx/docfx_project/templates/singulinkfx/styles/url.min.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/*! url - v1.8.6 - 2013-11-22 */window.url=function(){function a(a){return!isNaN(parseFloat(a))&&isFinite(a)}return function(b,c){var d=c||window.location.toString();if(!b)return d;b=b.toString(),"//"===d.substring(0,2)?d="http:"+d:1===d.split("://").length&&(d="http://"+d),c=d.split("/");var e={auth:""},f=c[2].split("@");1===f.length?f=f[0].split(":"):(e.auth=f[0],f=f[1].split(":")),e.protocol=c[0],e.hostname=f[0],e.port=f[1]||("https"===e.protocol.split(":")[0].toLowerCase()?"443":"80"),e.pathname=(c.length>3?"/":"")+c.slice(3,c.length).join("/").split("?")[0].split("#")[0];var g=e.pathname;"/"===g.charAt(g.length-1)&&(g=g.substring(0,g.length-1));var h=e.hostname,i=h.split("."),j=g.split("/");if("hostname"===b)return h;if("domain"===b)return/^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/.test(h)?h:i.slice(-2).join(".");if("sub"===b)return i.slice(0,i.length-2).join(".");if("port"===b)return e.port;if("protocol"===b)return e.protocol.split(":")[0];if("auth"===b)return e.auth;if("user"===b)return e.auth.split(":")[0];if("pass"===b)return e.auth.split(":")[1]||"";if("path"===b)return e.pathname;if("."===b.charAt(0)){if(b=b.substring(1),a(b))return b=parseInt(b,10),i[0>b?i.length+b:b-1]||""}else{if(a(b))return b=parseInt(b,10),j[0>b?j.length+b:b]||"";if("file"===b)return j.slice(-1)[0];if("filename"===b)return j.slice(-1)[0].split(".")[0];if("fileext"===b)return j.slice(-1)[0].split(".")[1]||"";if("?"===b.charAt(0)||"#"===b.charAt(0)){var k=d,l=null;if("?"===b.charAt(0)?k=(k.split("?")[1]||"").split("#")[0]:"#"===b.charAt(0)&&(k=k.split("#")[1]||""),!b.charAt(1))return k;b=b.substring(1),k=k.split("&");for(var m=0,n=k.length;n>m;m++)if(l=k[m].split("="),l[0]===b)return l[1]||"";return null}}return""}}(),"undefined"!=typeof jQuery&&jQuery.extend({url:function(a,b){return window.url(a,b)}});
|
||||||
22
docfx/docfx_project/templates/singulinkfx/toc.html.tmpl
Normal file
22
docfx/docfx_project/templates/singulinkfx/toc.html.tmpl
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}
|
||||||
|
|
||||||
|
<div id="sidetoggle">
|
||||||
|
<div>
|
||||||
|
{{^_disableSideFilter}}
|
||||||
|
<div class="sidefilter">
|
||||||
|
<form class="toc-filter">
|
||||||
|
<i class="bi bi-funnel-fill filter-icon"></i>
|
||||||
|
<i id="toc_filter_clear" class="bi bi-x-lg clear-icon"></i>
|
||||||
|
<input type="text" id="toc_filter_input" placeholder="{{__global.tocFilter}}" autocomplete="off" onkeypress="if(event.keyCode==13) {return false;}">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{{/_disableSideFilter}}
|
||||||
|
<div class="sidetoc">
|
||||||
|
<div class="toc" id="toc">
|
||||||
|
{{^leaf}}
|
||||||
|
{{>partials/li}}
|
||||||
|
{{/leaf}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -1,6 +1,2 @@
|
|||||||
- name: Articles
|
|
||||||
href: articles/
|
|
||||||
homepage: articles/intro.md
|
|
||||||
- name: API Documentation
|
- name: API Documentation
|
||||||
href: api/
|
href: api/
|
||||||
homepage: api/index.md
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Artemis.Core.JsonConverters;
|
using Artemis.Core.JsonConverters;
|
||||||
@ -90,6 +91,11 @@ public static class Constants
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly Plugin CorePlugin = new(CorePluginInfo, new DirectoryInfo(ApplicationFolder), null);
|
public static readonly Plugin CorePlugin = new(CorePluginInfo, new DirectoryInfo(ApplicationFolder), null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the startup arguments provided to the application
|
||||||
|
/// </summary>
|
||||||
|
public static ReadOnlyCollection<string> StartupArguments { get; set; } = null!;
|
||||||
|
|
||||||
internal static readonly CorePluginFeature CorePluginFeature = new() {Plugin = CorePlugin, Profiler = CorePlugin.GetProfiler("Feature - Core")};
|
internal static readonly CorePluginFeature CorePluginFeature = new() {Plugin = CorePlugin, Profiler = CorePlugin.GetProfiler("Feature - Core")};
|
||||||
internal static readonly EffectPlaceholderPlugin EffectPlaceholderPlugin = new() {Plugin = CorePlugin, Profiler = CorePlugin.GetProfiler("Feature - Effect Placeholder")};
|
internal static readonly EffectPlaceholderPlugin EffectPlaceholderPlugin = new() {Plugin = CorePlugin, Profiler = CorePlugin.GetProfiler("Feature - Effect Placeholder")};
|
||||||
|
|
||||||
|
|||||||
@ -58,7 +58,6 @@ internal class CoreService : ICoreService
|
|||||||
_scriptingService = scriptingService;
|
_scriptingService = scriptingService;
|
||||||
_loggingLevel = settingsService.GetSetting("Core.LoggingLevel", LogEventLevel.Debug);
|
_loggingLevel = settingsService.GetSetting("Core.LoggingLevel", LogEventLevel.Debug);
|
||||||
_frameStopWatch = new Stopwatch();
|
_frameStopWatch = new Stopwatch();
|
||||||
StartupArguments = new List<string>();
|
|
||||||
|
|
||||||
_rgbService.Surface.Updating += SurfaceOnUpdating;
|
_rgbService.Surface.Updating += SurfaceOnUpdating;
|
||||||
_loggingLevel.SettingChanged += (sender, args) => ApplyLoggingLevel();
|
_loggingLevel.SettingChanged += (sender, args) => ApplyLoggingLevel();
|
||||||
@ -78,7 +77,7 @@ internal class CoreService : ICoreService
|
|||||||
|
|
||||||
private void ApplyLoggingLevel()
|
private void ApplyLoggingLevel()
|
||||||
{
|
{
|
||||||
string? argument = StartupArguments.FirstOrDefault(a => a.StartsWith("--logging"));
|
string? argument = Constants.StartupArguments.FirstOrDefault(a => a.StartsWith("--logging"));
|
||||||
if (argument != null)
|
if (argument != null)
|
||||||
{
|
{
|
||||||
// Parse the provided log level
|
// Parse the provided log level
|
||||||
@ -194,7 +193,6 @@ internal class CoreService : ICoreService
|
|||||||
public int FrameRate { get; private set; }
|
public int FrameRate { get; private set; }
|
||||||
public TimeSpan FrameTime { get; private set; }
|
public TimeSpan FrameTime { get; private set; }
|
||||||
public bool ProfileRenderingDisabled { get; set; }
|
public bool ProfileRenderingDisabled { get; set; }
|
||||||
public List<string> StartupArguments { get; set; }
|
|
||||||
public bool IsElevated { get; set; }
|
public bool IsElevated { get; set; }
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
@ -217,7 +215,7 @@ internal class CoreService : ICoreService
|
|||||||
Constants.BuildInfo.BuildNumber,
|
Constants.BuildInfo.BuildNumber,
|
||||||
Constants.BuildInfo.SourceBranch
|
Constants.BuildInfo.SourceBranch
|
||||||
);
|
);
|
||||||
_logger.Information("Startup arguments: {args}", StartupArguments);
|
_logger.Information("Startup arguments: {args}", Constants.StartupArguments);
|
||||||
_logger.Information("Elevated permissions: {perms}", IsElevated);
|
_logger.Information("Elevated permissions: {perms}", IsElevated);
|
||||||
_logger.Information("Stopwatch high resolution: {perms}", Stopwatch.IsHighResolution);
|
_logger.Information("Stopwatch high resolution: {perms}", Stopwatch.IsHighResolution);
|
||||||
|
|
||||||
@ -230,9 +228,9 @@ internal class CoreService : ICoreService
|
|||||||
|
|
||||||
// Initialize the services
|
// Initialize the services
|
||||||
_pluginManagementService.CopyBuiltInPlugins();
|
_pluginManagementService.CopyBuiltInPlugins();
|
||||||
_pluginManagementService.LoadPlugins(StartupArguments, IsElevated);
|
_pluginManagementService.LoadPlugins(IsElevated);
|
||||||
|
|
||||||
_rgbService.ApplyPreferredGraphicsContext(StartupArguments.Contains("--force-software-render"));
|
_rgbService.ApplyPreferredGraphicsContext(Constants.StartupArguments.Contains("--force-software-render"));
|
||||||
_rgbService.SetRenderPaused(false);
|
_rgbService.SetRenderPaused(false);
|
||||||
OnInitialized();
|
OnInitialized();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,12 +27,7 @@ public interface ICoreService : IArtemisService, IDisposable
|
|||||||
/// Gets or sets whether profiles are rendered each frame by calling their Render method
|
/// Gets or sets whether profiles are rendered each frame by calling their Render method
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool ProfileRenderingDisabled { get; set; }
|
bool ProfileRenderingDisabled { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a list of startup arguments
|
|
||||||
/// </summary>
|
|
||||||
List<string> StartupArguments { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a boolean indicating whether Artemis is running in an elevated environment (admin permissions)
|
/// Gets a boolean indicating whether Artemis is running in an elevated environment (admin permissions)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -26,7 +26,7 @@ public interface IPluginManagementService : IArtemisService, IDisposable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads all installed plugins. If plugins already loaded this will reload them all
|
/// Loads all installed plugins. If plugins already loaded this will reload them all
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void LoadPlugins(List<string> startupArguments, bool isElevated);
|
void LoadPlugins(bool isElevated);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unloads all installed plugins.
|
/// Unloads all installed plugins.
|
||||||
|
|||||||
@ -203,17 +203,17 @@ internal class PluginManagementService : IPluginManagementService
|
|||||||
|
|
||||||
#region Plugins
|
#region Plugins
|
||||||
|
|
||||||
public void LoadPlugins(List<string> startupArguments, bool isElevated)
|
public void LoadPlugins(bool isElevated)
|
||||||
{
|
{
|
||||||
if (startupArguments.Contains("--no-plugins"))
|
if (Constants.StartupArguments.Contains("--no-plugins"))
|
||||||
{
|
{
|
||||||
_logger.Warning("Artemis launched with --no-plugins, skipping the loading of plugins");
|
_logger.Warning("Artemis launched with --no-plugins, skipping the loading of plugins");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ignorePluginLock = startupArguments.Contains("--ignore-plugin-lock");
|
bool ignorePluginLock = Constants.StartupArguments.Contains("--ignore-plugin-lock");
|
||||||
bool stayElevated = startupArguments.Contains("--force-elevation");
|
bool stayElevated = Constants.StartupArguments.Contains("--force-elevation");
|
||||||
bool droppedAdmin = startupArguments.Contains("--dropped-admin");
|
bool droppedAdmin = Constants.StartupArguments.Contains("--dropped-admin");
|
||||||
if (LoadingPlugins)
|
if (LoadingPlugins)
|
||||||
throw new ArtemisCoreException("Cannot load plugins while a previous load hasn't been completed yet.");
|
throw new ArtemisCoreException("Cannot load plugins while a previous load hasn't been completed yet.");
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Artemis.Core.Modules;
|
using Artemis.Core.Modules;
|
||||||
using EmbedIO;
|
using EmbedIO;
|
||||||
@ -17,15 +18,19 @@ internal class WebServerService : IWebServerService, IDisposable
|
|||||||
private readonly List<WebApiControllerRegistration> _controllers;
|
private readonly List<WebApiControllerRegistration> _controllers;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly List<WebModuleRegistration> _modules;
|
private readonly List<WebModuleRegistration> _modules;
|
||||||
|
private readonly PluginSetting<bool> _webServerEnabledSetting;
|
||||||
private readonly PluginSetting<int> _webServerPortSetting;
|
private readonly PluginSetting<int> _webServerPortSetting;
|
||||||
|
private CancellationTokenSource? _cts;
|
||||||
|
|
||||||
public WebServerService(ILogger logger, ISettingsService settingsService, IPluginManagementService pluginManagementService)
|
public WebServerService(ILogger logger, ICoreService coreService, ISettingsService settingsService, IPluginManagementService pluginManagementService)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_controllers = new List<WebApiControllerRegistration>();
|
_controllers = new List<WebApiControllerRegistration>();
|
||||||
_modules = new List<WebModuleRegistration>();
|
_modules = new List<WebModuleRegistration>();
|
||||||
|
|
||||||
|
_webServerEnabledSetting = settingsService.GetSetting("WebServer.Enabled", true);
|
||||||
_webServerPortSetting = settingsService.GetSetting("WebServer.Port", 9696);
|
_webServerPortSetting = settingsService.GetSetting("WebServer.Port", 9696);
|
||||||
|
_webServerEnabledSetting.SettingChanged += WebServerEnabledSettingOnSettingChanged;
|
||||||
_webServerPortSetting.SettingChanged += WebServerPortSettingOnSettingChanged;
|
_webServerPortSetting.SettingChanged += WebServerPortSettingOnSettingChanged;
|
||||||
pluginManagementService.PluginFeatureDisabled += PluginManagementServiceOnPluginFeatureDisabled;
|
pluginManagementService.PluginFeatureDisabled += PluginManagementServiceOnPluginFeatureDisabled;
|
||||||
|
|
||||||
@ -33,9 +38,9 @@ internal class WebServerService : IWebServerService, IDisposable
|
|||||||
StartWebServer();
|
StartWebServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnWebServerStarting()
|
private void WebServerEnabledSettingOnSettingChanged(object? sender, EventArgs e)
|
||||||
{
|
{
|
||||||
WebServerStarting?.Invoke(this, EventArgs.Empty);
|
StartWebServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WebServerPortSettingOnSettingChanged(object? sender, EventArgs e)
|
private void WebServerPortSettingOnSettingChanged(object? sender, EventArgs e)
|
||||||
@ -72,14 +77,23 @@ internal class WebServerService : IWebServerService, IDisposable
|
|||||||
public WebServer? Server { get; private set; }
|
public WebServer? Server { get; private set; }
|
||||||
public PluginsModule PluginsModule { get; }
|
public PluginsModule PluginsModule { get; }
|
||||||
|
|
||||||
public event EventHandler? WebServerStarting;
|
|
||||||
|
|
||||||
#region Web server managament
|
#region Web server managament
|
||||||
|
|
||||||
private WebServer CreateWebServer()
|
private WebServer CreateWebServer()
|
||||||
{
|
{
|
||||||
Server?.Dispose();
|
if (Server != null)
|
||||||
Server = null;
|
{
|
||||||
|
if (_cts != null)
|
||||||
|
{
|
||||||
|
_cts.Cancel();
|
||||||
|
_cts = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Server.Dispose();
|
||||||
|
OnWebServerStopped();
|
||||||
|
Server = null;
|
||||||
|
}
|
||||||
|
|
||||||
WebApiModule apiModule = new("/", JsonNetSerializer);
|
WebApiModule apiModule = new("/", JsonNetSerializer);
|
||||||
PluginsModule.ServerUrl = $"http://localhost:{_webServerPortSetting.Value}/";
|
PluginsModule.ServerUrl = $"http://localhost:{_webServerPortSetting.Value}/";
|
||||||
@ -112,8 +126,20 @@ internal class WebServerService : IWebServerService, IDisposable
|
|||||||
private void StartWebServer()
|
private void StartWebServer()
|
||||||
{
|
{
|
||||||
Server = CreateWebServer();
|
Server = CreateWebServer();
|
||||||
|
|
||||||
|
if (!_webServerEnabledSetting.Value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (Constants.StartupArguments.Contains("--disable-webserver"))
|
||||||
|
{
|
||||||
|
_logger.Warning("Artemis launched with --disable-webserver, not enabling the webserver");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
OnWebServerStarting();
|
OnWebServerStarting();
|
||||||
Server.Start();
|
_cts = new CancellationTokenSource();
|
||||||
|
Server.Start(_cts.Token);
|
||||||
|
OnWebServerStarted();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@ -276,4 +302,27 @@ internal class WebServerService : IWebServerService, IDisposable
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Events
|
||||||
|
|
||||||
|
protected virtual void OnWebServerStopped()
|
||||||
|
{
|
||||||
|
WebServerStopped?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnWebServerStarting()
|
||||||
|
{
|
||||||
|
WebServerStarting?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnWebServerStarted()
|
||||||
|
{
|
||||||
|
WebServerStarted?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler? WebServerStopped;
|
||||||
|
public event EventHandler? WebServerStarting;
|
||||||
|
public event EventHandler? WebServerStarted;
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
@ -159,7 +159,12 @@ public readonly struct Numeric : IComparable<Numeric>, IConvertible
|
|||||||
{
|
{
|
||||||
return (byte) Math.Clamp(p._value, 0, 255);
|
return (byte) Math.Clamp(p._value, 0, 255);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static implicit operator Numeric(double d) => new(d);
|
||||||
|
public static implicit operator Numeric(float f) => new(f);
|
||||||
|
public static implicit operator Numeric(int i) => new(i);
|
||||||
|
public static implicit operator Numeric(byte b) => new(b);
|
||||||
|
|
||||||
public static implicit operator long(Numeric p)
|
public static implicit operator long(Numeric p)
|
||||||
{
|
{
|
||||||
return (long) p._value;
|
return (long) p._value;
|
||||||
|
|||||||
@ -94,18 +94,11 @@ public sealed class InputPin : Pin
|
|||||||
/// <param name="type">The new type of the pin.</param>
|
/// <param name="type">The new type of the pin.</param>
|
||||||
public void ChangeType(Type type)
|
public void ChangeType(Type type)
|
||||||
{
|
{
|
||||||
if (_type == type)
|
if (type == _type)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Disconnect pins incompatible with the new type
|
base.ChangeType(type, ref _type);
|
||||||
List<IPin> toDisconnect = ConnectedTo.Where(p => !p.IsTypeCompatible(type)).ToList();
|
|
||||||
foreach (IPin pin in toDisconnect)
|
|
||||||
DisconnectFrom(pin);
|
|
||||||
|
|
||||||
// Change the type
|
|
||||||
SetAndNotify(ref _type, type, nameof(Type));
|
|
||||||
Value = type.GetDefault();
|
Value = type.GetDefault();
|
||||||
IsNumeric = type == typeof(Numeric);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Evaluate()
|
private void Evaluate()
|
||||||
@ -117,10 +110,10 @@ public sealed class InputPin : Pin
|
|||||||
else
|
else
|
||||||
Value = Type.GetDefault()!;
|
Value = Type.GetDefault()!;
|
||||||
}
|
}
|
||||||
else
|
else if (ConnectedTo.Count > 0)
|
||||||
{
|
|
||||||
Value = ConnectedTo[0].PinValue;
|
Value = ConnectedTo[0].PinValue;
|
||||||
}
|
else
|
||||||
|
Value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@ -85,6 +85,7 @@ public interface IPin
|
|||||||
/// Determines whether this pin is compatible with the given type
|
/// Determines whether this pin is compatible with the given type
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="type">The type to check for compatibility</param>
|
/// <param name="type">The type to check for compatibility</param>
|
||||||
|
/// <param name="forgivingEnumMatching">A boolean indicating whether or not enums should be exactly equal or just both be enums</param>
|
||||||
/// <returns><see langword="true" /> if the type is compatible, otherwise <see langword="false" />.</returns>
|
/// <returns><see langword="true" /> if the type is compatible, otherwise <see langword="false" />.</returns>
|
||||||
public bool IsTypeCompatible(Type type);
|
public bool IsTypeCompatible(Type type, bool forgivingEnumMatching = true);
|
||||||
}
|
}
|
||||||
@ -341,8 +341,14 @@ public abstract class NodeScript : CorePropertyChanged, INodeScript, IStorageMod
|
|||||||
private void SavePins(INode node, int collectionId, IEnumerable<IPin> pins)
|
private void SavePins(INode node, int collectionId, IEnumerable<IPin> pins)
|
||||||
{
|
{
|
||||||
int sourcePinId = 0;
|
int sourcePinId = 0;
|
||||||
foreach (IPin sourcePin in pins.Where(p => p.Direction == PinDirection.Input))
|
foreach (IPin sourcePin in pins)
|
||||||
{
|
{
|
||||||
|
if (sourcePin.Direction == PinDirection.Output)
|
||||||
|
{
|
||||||
|
sourcePinId++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (IPin targetPin in sourcePin.ConnectedTo)
|
foreach (IPin targetPin in sourcePin.ConnectedTo)
|
||||||
{
|
{
|
||||||
int targetPinCollectionId = -1;
|
int targetPinCollectionId = -1;
|
||||||
|
|||||||
@ -84,15 +84,11 @@ public sealed class OutputPin : Pin
|
|||||||
/// <param name="type">The new type of the pin.</param>
|
/// <param name="type">The new type of the pin.</param>
|
||||||
public void ChangeType(Type type)
|
public void ChangeType(Type type)
|
||||||
{
|
{
|
||||||
// Disconnect pins incompatible with the new type
|
if (type == _type)
|
||||||
List<IPin> toDisconnect = ConnectedTo.Where(p => !p.IsTypeCompatible(type)).ToList();
|
return;
|
||||||
foreach (IPin pin in toDisconnect)
|
|
||||||
DisconnectFrom(pin);
|
|
||||||
|
|
||||||
// Change the type
|
base.ChangeType(type, ref _type);
|
||||||
SetAndNotify(ref _type, type, nameof(Type));
|
|
||||||
Value = type.GetDefault();
|
Value = type.GetDefault();
|
||||||
IsNumeric = type == typeof(Numeric);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@ -127,13 +127,46 @@ public abstract class Pin : CorePropertyChanged, IPin
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool IsTypeCompatible(Type type)
|
public bool IsTypeCompatible(Type type, bool forgivingEnumMatching = true)
|
||||||
{
|
{
|
||||||
return Type == type
|
return Type == type
|
||||||
|| (Type == typeof(Enum) && type.IsEnum)
|
|| (Direction == PinDirection.Input && type.IsAssignableTo(Type))
|
||||||
|| (Type.IsEnum && type == typeof(Enum))
|
|| (Direction == PinDirection.Output && type.IsAssignableFrom(Type))
|
||||||
|| (Direction == PinDirection.Input && Type == typeof(object))
|
|| (Direction == PinDirection.Input && Type == typeof(Enum) && type.IsEnum && forgivingEnumMatching)
|
||||||
|| (Direction == PinDirection.Output && type == typeof(object));
|
|| (Direction == PinDirection.Output && type == typeof(Enum) && Type.IsEnum && forgivingEnumMatching);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Changes the type of this pin, disconnecting any pins that are incompatible with the new type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">The new type of the pin.</param>
|
||||||
|
/// <param name="currentType">The backing field of the current type of the pin.</param>
|
||||||
|
protected void ChangeType(Type type, ref Type currentType)
|
||||||
|
{
|
||||||
|
// Enums are a special case that disconnect and, if still compatible, reconnect
|
||||||
|
if (type.IsEnum && currentType.IsEnum)
|
||||||
|
{
|
||||||
|
List<IPin> connections = new(ConnectedTo);
|
||||||
|
DisconnectAll();
|
||||||
|
|
||||||
|
// Change the type
|
||||||
|
SetAndNotify(ref currentType, type, nameof(Type));
|
||||||
|
IsNumeric = type == typeof(Numeric);
|
||||||
|
|
||||||
|
foreach (IPin pin in connections.Where(p => p.IsTypeCompatible(type)))
|
||||||
|
ConnectTo(pin);
|
||||||
|
}
|
||||||
|
// Disconnect pins incompatible with the new type
|
||||||
|
else
|
||||||
|
{
|
||||||
|
List<IPin> toDisconnect = ConnectedTo.Where(p => !p.IsTypeCompatible(type, false)).ToList();
|
||||||
|
foreach (IPin pin in toDisconnect)
|
||||||
|
DisconnectFrom(pin);
|
||||||
|
|
||||||
|
// Change the type
|
||||||
|
SetAndNotify(ref currentType, type, nameof(Type));
|
||||||
|
IsNumeric = type == typeof(Numeric);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
|
|
||||||
namespace Artemis.UI.Shared.Services.NodeEditor;
|
namespace Artemis.UI.Shared.Services.NodeEditor;
|
||||||
@ -30,20 +31,18 @@ public class NodeConnectionStore
|
|||||||
public void Store()
|
public void Store()
|
||||||
{
|
{
|
||||||
_pinConnections.Clear();
|
_pinConnections.Clear();
|
||||||
foreach (IPin nodePin in Node.Pins)
|
|
||||||
{
|
// Iterate to save
|
||||||
|
foreach (IPin nodePin in Node.Pins.ToList())
|
||||||
|
_pinConnections.Add(nodePin, new List<IPin>(nodePin.ConnectedTo));
|
||||||
|
foreach (IPin nodePin in Node.PinCollections.ToList().SelectMany(nodePinCollection => nodePinCollection))
|
||||||
_pinConnections.Add(nodePin, new List<IPin>(nodePin.ConnectedTo));
|
_pinConnections.Add(nodePin, new List<IPin>(nodePin.ConnectedTo));
|
||||||
nodePin.DisconnectAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (IPinCollection nodePinCollection in Node.PinCollections)
|
// Iterate to disconnect
|
||||||
{
|
foreach (IPin nodePin in Node.Pins.ToList())
|
||||||
foreach (IPin nodePin in nodePinCollection)
|
nodePin.DisconnectAll();
|
||||||
{
|
foreach (IPin nodePin in Node.PinCollections.ToList().SelectMany(nodePinCollection => nodePinCollection))
|
||||||
_pinConnections.Add(nodePin, new List<IPin>(nodePin.ConnectedTo));
|
nodePin.DisconnectAll();
|
||||||
nodePin.DisconnectAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -51,23 +50,10 @@ public class NodeConnectionStore
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void Restore()
|
public void Restore()
|
||||||
{
|
{
|
||||||
foreach (IPin nodePin in Node.Pins)
|
foreach ((IPin? pin, List<IPin>? connections) in _pinConnections)
|
||||||
{
|
{
|
||||||
if (!_pinConnections.TryGetValue(nodePin, out List<IPin>? connections))
|
|
||||||
continue;
|
|
||||||
foreach (IPin connection in connections)
|
foreach (IPin connection in connections)
|
||||||
nodePin.ConnectTo(connection);
|
pin.ConnectTo(connection);
|
||||||
}
|
|
||||||
|
|
||||||
foreach (IPinCollection nodePinCollection in Node.PinCollections)
|
|
||||||
{
|
|
||||||
foreach (IPin nodePin in nodePinCollection)
|
|
||||||
{
|
|
||||||
if (!_pinConnections.TryGetValue(nodePin, out List<IPin>? connections))
|
|
||||||
continue;
|
|
||||||
foreach (IPin connection in connections)
|
|
||||||
nodePin.ConnectTo(connection);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_pinConnections.Clear();
|
_pinConnections.Clear();
|
||||||
|
|||||||
@ -1,3 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading;
|
||||||
|
using Artemis.Core;
|
||||||
using Artemis.Core.Services;
|
using Artemis.Core.Services;
|
||||||
using Artemis.UI.Windows.Ninject;
|
using Artemis.UI.Windows.Ninject;
|
||||||
using Artemis.UI.Windows.Providers.Input;
|
using Artemis.UI.Windows.Providers.Input;
|
||||||
@ -13,14 +21,23 @@ namespace Artemis.UI.Windows;
|
|||||||
|
|
||||||
public class App : Application
|
public class App : Application
|
||||||
{
|
{
|
||||||
|
private StandardKernel? _kernel;
|
||||||
|
private bool _shutDown;
|
||||||
|
|
||||||
// ReSharper disable NotAccessedField.Local
|
// ReSharper disable NotAccessedField.Local
|
||||||
private ApplicationStateManager? _applicationStateManager;
|
private ApplicationStateManager? _applicationStateManager;
|
||||||
|
private Mutex? _artemisMutex;
|
||||||
// ReSharper restore NotAccessedField.Local
|
// ReSharper restore NotAccessedField.Local
|
||||||
|
|
||||||
private StandardKernel? _kernel;
|
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
|
// If Artemis is already running, bring it to foreground and stop this process
|
||||||
|
if (FocusExistingInstance())
|
||||||
|
{
|
||||||
|
_shutDown = true;
|
||||||
|
Environment.Exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
_kernel = ArtemisBootstrapper.Bootstrap(this, new WindowsModule());
|
_kernel = ArtemisBootstrapper.Bootstrap(this, new WindowsModule());
|
||||||
Program.CreateLogger(_kernel);
|
Program.CreateLogger(_kernel);
|
||||||
RxApp.MainThreadScheduler = AvaloniaScheduler.Instance;
|
RxApp.MainThreadScheduler = AvaloniaScheduler.Instance;
|
||||||
@ -29,7 +46,7 @@ public class App : Application
|
|||||||
|
|
||||||
public override void OnFrameworkInitializationCompleted()
|
public override void OnFrameworkInitializationCompleted()
|
||||||
{
|
{
|
||||||
if (ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop || Design.IsDesignMode)
|
if (ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop || Design.IsDesignMode || _shutDown)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ArtemisBootstrapper.Initialize();
|
ArtemisBootstrapper.Initialize();
|
||||||
@ -42,4 +59,55 @@ public class App : Application
|
|||||||
IInputService inputService = standardKernel.Get<IInputService>();
|
IInputService inputService = standardKernel.Get<IInputService>();
|
||||||
inputService.AddInputProvider(standardKernel.Get<WindowsInputProvider>());
|
inputService.AddInputProvider(standardKernel.Get<WindowsInputProvider>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool FocusExistingInstance()
|
||||||
|
{
|
||||||
|
_artemisMutex = new Mutex(true, "Artemis-3c24b502-64e6-4587-84bf-9072970e535f", out bool createdNew);
|
||||||
|
return !createdNew && RemoteFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool RemoteFocus()
|
||||||
|
{
|
||||||
|
// At this point we cannot read the database yet to retrieve the web server port.
|
||||||
|
// Instead use the method external applications should use as well.
|
||||||
|
if (!File.Exists(Path.Combine(Constants.DataFolder, "webserver.txt")))
|
||||||
|
{
|
||||||
|
KillOtherInstances();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
string url = File.ReadAllText(Path.Combine(Constants.DataFolder, "webserver.txt"));
|
||||||
|
using HttpClient client = new();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
CancellationTokenSource cts = new();
|
||||||
|
cts.CancelAfter(2000);
|
||||||
|
|
||||||
|
HttpResponseMessage httpResponseMessage = client.Send(new HttpRequestMessage(HttpMethod.Post, url + "remote/bring-to-foreground"), cts.Token);
|
||||||
|
httpResponseMessage.EnsureSuccessStatusCode();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
KillOtherInstances();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void KillOtherInstances()
|
||||||
|
{
|
||||||
|
// Kill everything else heh
|
||||||
|
List<Process> processes = Process.GetProcessesByName("Artemis.UI.Windows").Where(p => p.Id != Process.GetCurrentProcess().Id).ToList();
|
||||||
|
foreach (Process process in processes)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
process.Kill(true);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -3,12 +3,9 @@ using System.Collections.Generic;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
|
||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
using System.Threading;
|
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.Core.Services;
|
using Artemis.Core.Services;
|
||||||
using Artemis.UI.Shared.Services;
|
|
||||||
using Artemis.UI.Windows.Utilities;
|
using Artemis.UI.Windows.Utilities;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls.ApplicationLifetimes;
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
@ -19,14 +16,8 @@ namespace Artemis.UI.Windows;
|
|||||||
|
|
||||||
public class ApplicationStateManager
|
public class ApplicationStateManager
|
||||||
{
|
{
|
||||||
private readonly IWindowService _windowService;
|
|
||||||
|
|
||||||
// ReSharper disable once NotAccessedField.Local - Kept in scope to ensure it does not get released
|
|
||||||
private Mutex? _artemisMutex;
|
|
||||||
|
|
||||||
public ApplicationStateManager(IKernel kernel, string[] startupArguments)
|
public ApplicationStateManager(IKernel kernel, string[] startupArguments)
|
||||||
{
|
{
|
||||||
_windowService = kernel.Get<IWindowService>();
|
|
||||||
StartupArguments = startupArguments;
|
StartupArguments = startupArguments;
|
||||||
IsElevated = new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);
|
IsElevated = new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);
|
||||||
|
|
||||||
@ -51,72 +42,6 @@ public class ApplicationStateManager
|
|||||||
public string[] StartupArguments { get; }
|
public string[] StartupArguments { get; }
|
||||||
public bool IsElevated { get; }
|
public bool IsElevated { get; }
|
||||||
|
|
||||||
public bool FocusExistingInstance()
|
|
||||||
{
|
|
||||||
_artemisMutex = new Mutex(true, "Artemis-3c24b502-64e6-4587-84bf-9072970e535f", out bool createdNew);
|
|
||||||
if (createdNew)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return RemoteFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DisplayException(Exception e)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_windowService.ShowExceptionDialog("An unhandled exception occured", e);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// ignored, we tried
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool RemoteFocus()
|
|
||||||
{
|
|
||||||
// At this point we cannot read the database yet to retrieve the web server port.
|
|
||||||
// Instead use the method external applications should use as well.
|
|
||||||
if (!File.Exists(Path.Combine(Constants.DataFolder, "webserver.txt")))
|
|
||||||
{
|
|
||||||
KillOtherInstances();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
string url = File.ReadAllText(Path.Combine(Constants.DataFolder, "webserver.txt"));
|
|
||||||
using HttpClient client = new();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
CancellationTokenSource cts = new();
|
|
||||||
cts.CancelAfter(2000);
|
|
||||||
|
|
||||||
HttpResponseMessage httpResponseMessage = client.Send(new HttpRequestMessage(HttpMethod.Post, url + "remote/bring-to-foreground"), cts.Token);
|
|
||||||
httpResponseMessage.EnsureSuccessStatusCode();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
KillOtherInstances();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void KillOtherInstances()
|
|
||||||
{
|
|
||||||
// Kill everything else heh
|
|
||||||
List<Process> processes = Process.GetProcessesByName("Artemis.UI.Windows").Where(p => p.Id != Process.GetCurrentProcess().Id).ToList();
|
|
||||||
foreach (Process process in processes)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
process.Kill(true);
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
// ignored
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UtilitiesOnRestartRequested(object? sender, RestartEventArgs e)
|
private void UtilitiesOnRestartRequested(object? sender, RestartEventArgs e)
|
||||||
{
|
{
|
||||||
List<string> argsList = new();
|
List<string> argsList = new();
|
||||||
|
|||||||
@ -1,4 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
using System.Reactive;
|
using System.Reactive;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.Core.Ninject;
|
using Artemis.Core.Ninject;
|
||||||
@ -52,6 +55,8 @@ public static class ArtemisBootstrapper
|
|||||||
if (_application.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop)
|
if (_application.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
Constants.StartupArguments = new ReadOnlyCollection<string>(new List<string>(desktop.Args));
|
||||||
|
|
||||||
// Don't shut down when the last window closes, we might still be active in the tray
|
// Don't shut down when the last window closes, we might still be active in the tray
|
||||||
desktop.ShutdownMode = ShutdownMode.OnExplicitShutdown;
|
desktop.ShutdownMode = ShutdownMode.OnExplicitShutdown;
|
||||||
// Create the root view model that drives the UI
|
// Create the root view model that drives the UI
|
||||||
|
|||||||
@ -47,12 +47,12 @@ public class MainWindow : ReactiveCoreWindow<RootViewModel>
|
|||||||
|
|
||||||
private void OnActivated(object? sender, EventArgs e)
|
private void OnActivated(object? sender, EventArgs e)
|
||||||
{
|
{
|
||||||
ViewModel.Focused();
|
ViewModel?.Focused();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDeactivated(object? sender, EventArgs e)
|
private void OnDeactivated(object? sender, EventArgs e)
|
||||||
{
|
{
|
||||||
ViewModel.Unfocused();
|
ViewModel?.Unfocused();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeComponent()
|
private void InitializeComponent()
|
||||||
|
|||||||
@ -54,8 +54,7 @@ public class RootViewModel : ActivatableViewModelBase, IScreen, IMainWindowProvi
|
|||||||
_defaultTitleBarViewModel = defaultTitleBarViewModel;
|
_defaultTitleBarViewModel = defaultTitleBarViewModel;
|
||||||
_sidebarVmFactory = sidebarVmFactory;
|
_sidebarVmFactory = sidebarVmFactory;
|
||||||
_lifeTime = (IClassicDesktopStyleApplicationLifetime) Application.Current!.ApplicationLifetime!;
|
_lifeTime = (IClassicDesktopStyleApplicationLifetime) Application.Current!.ApplicationLifetime!;
|
||||||
|
|
||||||
coreService.StartupArguments = _lifeTime.Args.ToList();
|
|
||||||
mainWindowService.ConfigureMainWindowProvider(this);
|
mainWindowService.ConfigureMainWindowProvider(this);
|
||||||
|
|
||||||
DisplayAccordingToSettings();
|
DisplayAccordingToSettings();
|
||||||
@ -99,8 +98,8 @@ public class RootViewModel : ActivatableViewModelBase, IScreen, IMainWindowProvi
|
|||||||
|
|
||||||
private void DisplayAccordingToSettings()
|
private void DisplayAccordingToSettings()
|
||||||
{
|
{
|
||||||
bool autoRunning = _coreService.StartupArguments.Contains("--autorun");
|
bool autoRunning = Constants.StartupArguments.Contains("--autorun");
|
||||||
bool minimized = _coreService.StartupArguments.Contains("--minimized");
|
bool minimized = Constants.StartupArguments.Contains("--minimized");
|
||||||
bool showOnAutoRun = _settingsService.GetSetting("UI.ShowOnStartup", true).Value;
|
bool showOnAutoRun = _settingsService.GetSetting("UI.ShowOnStartup", true).Value;
|
||||||
|
|
||||||
if ((autoRunning && !showOnAutoRun) || minimized)
|
if ((autoRunning && !showOnAutoRun) || minimized)
|
||||||
|
|||||||
@ -13,364 +13,379 @@
|
|||||||
x:DataType="settings:GeneralTabViewModel">
|
x:DataType="settings:GeneralTabViewModel">
|
||||||
|
|
||||||
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
|
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
|
||||||
<StackPanel Margin="15" MaxWidth="1000">
|
<StackPanel Margin="15" MaxWidth="1000">
|
||||||
<!-- General settings -->
|
<!-- General settings -->
|
||||||
<TextBlock Classes="h4" Margin="0 15">
|
<TextBlock Classes="h4" Margin="0 15">
|
||||||
General
|
General
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
<Border Classes="card" VerticalAlignment="Stretch" Margin="0,0,5,0">
|
<Border Classes="card" VerticalAlignment="Stretch" Margin="0,0,5,0">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<StackPanel IsVisible="{CompiledBinding IsAutoRunSupported}">
|
<StackPanel IsVisible="{CompiledBinding IsAutoRunSupported}">
|
||||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||||
<StackPanel Grid.Column="0">
|
<StackPanel Grid.Column="0">
|
||||||
<TextBlock>Auto-run on startup</TextBlock>
|
<TextBlock>Auto-run on startup</TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<ToggleSwitch Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" IsChecked="{CompiledBinding UIAutoRun.Value}" MinWidth="0" Margin="0 -10"/>
|
<ToggleSwitch Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" IsChecked="{CompiledBinding UIAutoRun.Value}" MinWidth="0" Margin="0 -10" OnContent="Yes"
|
||||||
</Grid>
|
OffContent="No" />
|
||||||
<Separator Classes="card-separator" />
|
</Grid>
|
||||||
|
<Separator Classes="card-separator" />
|
||||||
|
|
||||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||||
<StackPanel Grid.Column="0">
|
<StackPanel Grid.Column="0">
|
||||||
<TextBlock>Hide window on auto-run</TextBlock>
|
<TextBlock>Hide window on auto-run</TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||||
<ToggleSwitch IsChecked="{CompiledBinding !UIShowOnStartup.Value}" IsEnabled="{CompiledBinding UIAutoRun.Value}" MinWidth="0" Margin="0 -10"/>
|
<ToggleSwitch IsChecked="{CompiledBinding !UIShowOnStartup.Value}" IsEnabled="{CompiledBinding UIAutoRun.Value}" MinWidth="0" Margin="0 -10" OnContent="Yes"
|
||||||
</StackPanel>
|
OffContent="No" />
|
||||||
</Grid>
|
</StackPanel>
|
||||||
<Separator Classes="card-separator" />
|
</Grid>
|
||||||
|
<Separator Classes="card-separator" />
|
||||||
|
|
||||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||||
<StackPanel Grid.Column="0">
|
<StackPanel Grid.Column="0">
|
||||||
<TextBlock>Startup delay</TextBlock>
|
<TextBlock>Startup delay</TextBlock>
|
||||||
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
||||||
Set the amount of seconds to wait before auto-running Artemis.
|
Set the amount of seconds to wait before auto-running Artemis.
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
||||||
If some devices don't work because Artemis starts before the manufacturer's software, try increasing this value.
|
If some devices don't work because Artemis starts before the manufacturer's software, try increasing this value.
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" Orientation="Horizontal">
|
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" Orientation="Horizontal">
|
||||||
<controls:NumberBox IsEnabled="{CompiledBinding UIAutoRun.Value}" Width="120">
|
<controls:NumberBox IsEnabled="{CompiledBinding UIAutoRun.Value}" Width="120">
|
||||||
<Interaction.Behaviors>
|
<Interaction.Behaviors>
|
||||||
<behaviors:LostFocusNumberBoxBindingBehavior Value="{CompiledBinding UIAutoRunDelay.Value}"/>
|
<behaviors:LostFocusNumberBoxBindingBehavior Value="{CompiledBinding UIAutoRunDelay.Value}" />
|
||||||
</Interaction.Behaviors>
|
</Interaction.Behaviors>
|
||||||
</controls:NumberBox>
|
</controls:NumberBox>
|
||||||
<TextBlock VerticalAlignment="Center" TextAlignment="Right" Width="30">sec</TextBlock>
|
<TextBlock VerticalAlignment="Center" TextAlignment="Right" Width="30">sec</TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Separator Classes="card-separator" />
|
<Separator Classes="card-separator" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
|
||||||
<StackPanel Grid.Column="0">
|
|
||||||
<TextBlock>
|
|
||||||
Log level
|
|
||||||
</TextBlock>
|
|
||||||
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
|
||||||
Sets the logging level, a higher logging level will result in more log files.
|
|
||||||
</TextBlock>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
|
||||||
<shared:EnumComboBox Width="150" Value="{CompiledBinding CoreLoggingLevel.Value}" />
|
|
||||||
</StackPanel>
|
|
||||||
</Grid>
|
|
||||||
<Separator Classes="card-separator" />
|
|
||||||
|
|
||||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||||
<StackPanel Grid.Column="0" VerticalAlignment="Center">
|
<StackPanel Grid.Column="0">
|
||||||
<TextBlock>
|
<TextBlock>
|
||||||
Logs
|
Log level
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
<TextBlock Classes="subtitle">
|
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
||||||
Opens the directory where logs are stored.
|
Sets the logging level, a higher logging level will result in more log files.
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||||
<Button Command="{CompiledBinding ShowLogs}" Width="150" Content="Show logs" />
|
<shared:EnumComboBox Width="150" Value="{CompiledBinding CoreLoggingLevel.Value}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</StackPanel>
|
<Separator Classes="card-separator" />
|
||||||
</Border>
|
|
||||||
|
|
||||||
<!-- Web server settings -->
|
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||||
<TextBlock Classes="h4" Margin="0 15">
|
<StackPanel Grid.Column="0" VerticalAlignment="Center">
|
||||||
Web server
|
<TextBlock>
|
||||||
</TextBlock>
|
Logs
|
||||||
<Border Classes="card" VerticalAlignment="Stretch" Margin="0,0,5,0">
|
</TextBlock>
|
||||||
<StackPanel>
|
<TextBlock Classes="subtitle">
|
||||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
Opens the directory where logs are stored.
|
||||||
<StackPanel Grid.Column="0">
|
</TextBlock>
|
||||||
<TextBlock>
|
</StackPanel>
|
||||||
Web server port
|
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||||
</TextBlock>
|
<Button Command="{CompiledBinding ShowLogs}" Width="150" Content="Show logs" />
|
||||||
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
</StackPanel>
|
||||||
Artemis runs a local web server that can be used to externally interact with the application.
|
</Grid>
|
||||||
</TextBlock>
|
</StackPanel>
|
||||||
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
</Border>
|
||||||
This web server can only be accessed by applications running on your own computer, e.g. supported games.
|
|
||||||
</TextBlock>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
|
||||||
<controls:NumberBox Width="150">
|
|
||||||
<Interaction.Behaviors>
|
|
||||||
<behaviors:LostFocusNumberBoxBindingBehavior Value="{CompiledBinding WebServerPort.Value}"/>
|
|
||||||
</Interaction.Behaviors>
|
|
||||||
</controls:NumberBox>
|
|
||||||
</StackPanel>
|
|
||||||
</Grid>
|
|
||||||
</StackPanel>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<!-- Update settings -->
|
<!-- Web server settings -->
|
||||||
<StackPanel IsVisible="{CompiledBinding IsUpdatingSupported}">
|
<TextBlock Classes="h4" Margin="0 15">
|
||||||
<TextBlock Classes="h4" Margin="0 15">
|
Web server
|
||||||
Updating
|
</TextBlock>
|
||||||
</TextBlock>
|
<Border Classes="card" VerticalAlignment="Stretch" Margin="0,0,5,0">
|
||||||
<Border Classes="card" VerticalAlignment="Stretch" Margin="0,0,5,0">
|
<StackPanel>
|
||||||
<StackPanel>
|
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
<StackPanel Grid.Column="0">
|
||||||
<StackPanel Grid.Column="0">
|
<TextBlock>Enable web server</TextBlock>
|
||||||
<TextBlock>
|
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
||||||
Check for updates
|
Artemis runs a local web server that can be used to externally interact with the application.
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
||||||
If enabled, we'll check for updates on startup and periodically while running.
|
This web server can only be accessed by applications running on your own computer, e.g. supported games.
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||||
<ToggleSwitch IsChecked="{CompiledBinding UICheckForUpdates.Value}" MinWidth="0" />
|
<ToggleSwitch IsChecked="{CompiledBinding WebServerEnabled.Value}" OnContent="Yes" OffContent="No" MinWidth="0" Margin="0 -10" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Separator Classes="card-separator" />
|
<Separator Classes="card-separator" />
|
||||||
|
|
||||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
|
||||||
<StackPanel Grid.Column="0">
|
|
||||||
<TextBlock>
|
|
||||||
Auto-install updates
|
|
||||||
</TextBlock>
|
|
||||||
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
|
||||||
If enabled, new updates will automatically be installed.
|
|
||||||
</TextBlock>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
|
||||||
<ToggleSwitch IsEnabled="{CompiledBinding UICheckForUpdates.Value}" IsChecked="{CompiledBinding UIAutoUpdate.Value}" MinWidth="0" />
|
|
||||||
</StackPanel>
|
|
||||||
</Grid>
|
|
||||||
<Separator Classes="card-separator" />
|
|
||||||
|
|
||||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||||
<StackPanel Grid.Column="0" VerticalAlignment="Center">
|
<StackPanel Grid.Column="0">
|
||||||
<TextBlock>
|
<TextBlock>
|
||||||
Update
|
Web server port
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
<TextBlock Classes="subtitle">
|
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
||||||
Use the button on the right to check for updates now.
|
If the webserver does not work you can try changing the port to one that is available.
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||||
<Button Command="{CompiledBinding CheckForUpdate}" Width="150" Content="Check now" />
|
<controls:NumberBox Width="150">
|
||||||
</StackPanel>
|
<Interaction.Behaviors>
|
||||||
</Grid>
|
<behaviors:LostFocusNumberBoxBindingBehavior Value="{CompiledBinding WebServerPort.Value}" />
|
||||||
</StackPanel>
|
</Interaction.Behaviors>
|
||||||
</Border>
|
</controls:NumberBox>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
<!-- Profile editor settings -->
|
<!-- Update settings -->
|
||||||
<TextBlock Classes="h4" Margin="0 15">
|
<StackPanel IsVisible="{CompiledBinding IsUpdatingSupported}">
|
||||||
Profile editor
|
<TextBlock Classes="h4" Margin="0 15">
|
||||||
</TextBlock>
|
Updating
|
||||||
<Border Classes="card" VerticalAlignment="Stretch" Margin="0,0,5,0">
|
</TextBlock>
|
||||||
<StackPanel>
|
<Border Classes="card" VerticalAlignment="Stretch" Margin="0,0,5,0">
|
||||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
<StackPanel>
|
||||||
<StackPanel Grid.Column="0">
|
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||||
<TextBlock>
|
<StackPanel Grid.Column="0">
|
||||||
Show condition data model values
|
<TextBlock>
|
||||||
</TextBlock>
|
Check for updates
|
||||||
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
</TextBlock>
|
||||||
While selecting a condition target, show the current values of the data model.
|
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
||||||
</TextBlock>
|
If enabled, we'll check for updates on startup and periodically while running.
|
||||||
</StackPanel>
|
</TextBlock>
|
||||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
</StackPanel>
|
||||||
<ToggleSwitch IsChecked="{CompiledBinding ProfileEditorShowDataModelValues.Value}" MinWidth="0" />
|
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||||
</StackPanel>
|
<ToggleSwitch IsChecked="{CompiledBinding UICheckForUpdates.Value}" MinWidth="0" OnContent="Yes" OffContent="No" />
|
||||||
</Grid>
|
</StackPanel>
|
||||||
<Separator Classes="card-separator" />
|
</Grid>
|
||||||
|
<Separator Classes="card-separator" />
|
||||||
|
|
||||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||||
<StackPanel Grid.Column="0">
|
<StackPanel Grid.Column="0">
|
||||||
<TextBlock>
|
<TextBlock>
|
||||||
Default brush
|
Auto-install updates
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
||||||
Sets the default brush that is applied to new layers
|
If enabled, new updates will automatically be installed.
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||||
<StackPanel.Styles>
|
<ToggleSwitch IsEnabled="{CompiledBinding UICheckForUpdates.Value}" IsChecked="{CompiledBinding UIAutoUpdate.Value}" MinWidth="0" OnContent="Yes" OffContent="No" />
|
||||||
<Style Selector="ComboBox.brush /template/ ContentControl#ContentPresenter">
|
</StackPanel>
|
||||||
<Setter Property="ContentTemplate">
|
</Grid>
|
||||||
<Setter.Value>
|
<Separator Classes="card-separator" />
|
||||||
<DataTemplate DataType="{x:Type layerBrushes:LayerBrushDescriptor}">
|
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<avalonia:MaterialIcon Kind="{CompiledBinding Icon}" Height="20" Width="20" VerticalAlignment="Center" Margin="0 0 5 0"/>
|
|
||||||
<TextBlock Text="{CompiledBinding DisplayName}" VerticalAlignment="Center" />
|
|
||||||
</StackPanel>
|
|
||||||
</DataTemplate>
|
|
||||||
</Setter.Value>
|
|
||||||
</Setter>
|
|
||||||
</Style>
|
|
||||||
</StackPanel.Styles>
|
|
||||||
<ComboBox Classes="brush"
|
|
||||||
Width="200"
|
|
||||||
HorizontalAlignment="Left"
|
|
||||||
Items="{CompiledBinding LayerBrushDescriptors}"
|
|
||||||
SelectedItem="{CompiledBinding SelectedLayerBrushDescriptor}">
|
|
||||||
<ComboBox.ItemTemplate>
|
|
||||||
<DataTemplate DataType="{x:Type layerBrushes:LayerBrushDescriptor}">
|
|
||||||
<Grid ColumnDefinitions="30,*" RowDefinitions="Auto,Auto">
|
|
||||||
<avalonia:MaterialIcon Grid.Row="0"
|
|
||||||
Grid.RowSpan="2"
|
|
||||||
Kind="{CompiledBinding Icon}"
|
|
||||||
Height="20"
|
|
||||||
Width="20"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
HorizontalAlignment="Left" />
|
|
||||||
<TextBlock Grid.Row="0" Grid.Column="1" Text="{CompiledBinding DisplayName}" TextWrapping="Wrap" MaxWidth="350" />
|
|
||||||
<TextBlock Classes="subtitle" Grid.Row="1" Grid.Column="1" Text="{CompiledBinding Description}" TextWrapping="Wrap" MaxWidth="350" />
|
|
||||||
</Grid>
|
|
||||||
</DataTemplate>
|
|
||||||
</ComboBox.ItemTemplate>
|
|
||||||
</ComboBox>
|
|
||||||
</StackPanel>
|
|
||||||
</Grid>
|
|
||||||
</StackPanel>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<!-- Rendering settings -->
|
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||||
<TextBlock Classes="h4" Margin="0 15">
|
<StackPanel Grid.Column="0" VerticalAlignment="Center">
|
||||||
Rendering
|
<TextBlock>
|
||||||
</TextBlock>
|
Update
|
||||||
<Border Classes="card" VerticalAlignment="Stretch" Margin="0,0,5,0">
|
</TextBlock>
|
||||||
<StackPanel>
|
<TextBlock Classes="subtitle">
|
||||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
Use the button on the right to check for updates now.
|
||||||
<StackPanel Grid.Column="0">
|
</TextBlock>
|
||||||
<TextBlock>
|
</StackPanel>
|
||||||
Preferred render method
|
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||||
</TextBlock>
|
<Button Command="{CompiledBinding CheckForUpdate}" Width="150" Content="Check now" />
|
||||||
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
</StackPanel>
|
||||||
Software-based rendering is done purely on the CPU while Vulkan uses GPU-acceleration.
|
</Grid>
|
||||||
</TextBlock>
|
</StackPanel>
|
||||||
</StackPanel>
|
</Border>
|
||||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
</StackPanel>
|
||||||
<ComboBox Width="150"
|
|
||||||
SelectedItem="{CompiledBinding CorePreferredGraphicsContext.Value}"
|
|
||||||
Items="{CompiledBinding GraphicsContexts}"/>
|
|
||||||
</StackPanel>
|
|
||||||
</Grid>
|
|
||||||
<Separator Classes="card-separator" />
|
|
||||||
|
|
||||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
<!-- Profile editor settings -->
|
||||||
<StackPanel Grid.Column="0">
|
<TextBlock Classes="h4" Margin="0 15">
|
||||||
<TextBlock>
|
Profile editor
|
||||||
Render scale
|
</TextBlock>
|
||||||
</TextBlock>
|
<Border Classes="card" VerticalAlignment="Stretch" Margin="0,0,5,0">
|
||||||
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
<StackPanel>
|
||||||
Sets the resolution Artemis renders at, higher scale means more CPU-usage, especially on large surfaces.
|
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||||
</TextBlock>
|
<StackPanel Grid.Column="0">
|
||||||
</StackPanel>
|
<TextBlock>
|
||||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
Show condition data model values
|
||||||
<ComboBox Width="150"
|
</TextBlock>
|
||||||
SelectedItem="{CompiledBinding SelectedRenderScale}"
|
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
||||||
Items="{CompiledBinding RenderScales}">
|
While selecting a condition target, show the current values of the data model.
|
||||||
<ComboBox.ItemTemplate>
|
</TextBlock>
|
||||||
<DataTemplate>
|
</StackPanel>
|
||||||
<TextBlock Text="{CompiledBinding Display}" />
|
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||||
</DataTemplate>
|
<ToggleSwitch IsChecked="{CompiledBinding ProfileEditorShowDataModelValues.Value}" MinWidth="0" OnContent="Yes" OffContent="No" />
|
||||||
</ComboBox.ItemTemplate>
|
</StackPanel>
|
||||||
</ComboBox>
|
</Grid>
|
||||||
</StackPanel>
|
<Separator Classes="card-separator" />
|
||||||
</Grid>
|
|
||||||
<Separator Classes="card-separator" />
|
|
||||||
|
|
||||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||||
<StackPanel Grid.Column="0">
|
<StackPanel Grid.Column="0">
|
||||||
<TextBlock>
|
<TextBlock>
|
||||||
Target frame rate
|
Default brush
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
||||||
Sets the FPS Artemis tries to render at, higher FPS means more CPU-usage but smoother animations.
|
Sets the default brush that is applied to new layers
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
</StackPanel>
|
||||||
The options past 45 FPS are mostly useless unless you are using a custom device.
|
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||||
</TextBlock>
|
<StackPanel.Styles>
|
||||||
</StackPanel>
|
<Style Selector="ComboBox.brush /template/ ContentControl#ContentPresenter">
|
||||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
<Setter Property="ContentTemplate">
|
||||||
<ComboBox Width="150"
|
<Setter.Value>
|
||||||
SelectedItem="{CompiledBinding SelectedTargetFrameRate}"
|
<DataTemplate DataType="{x:Type layerBrushes:LayerBrushDescriptor}">
|
||||||
Items="{CompiledBinding TargetFrameRates}">
|
<StackPanel Orientation="Horizontal">
|
||||||
<ComboBox.ItemTemplate>
|
<avalonia:MaterialIcon Kind="{CompiledBinding Icon}" Height="20" Width="20" VerticalAlignment="Center" Margin="0 0 5 0" />
|
||||||
<DataTemplate>
|
<TextBlock Text="{CompiledBinding DisplayName}" VerticalAlignment="Center" />
|
||||||
<TextBlock Text="{CompiledBinding Display}" />
|
</StackPanel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ComboBox.ItemTemplate>
|
</Setter.Value>
|
||||||
</ComboBox>
|
</Setter>
|
||||||
</StackPanel>
|
</Style>
|
||||||
</Grid>
|
</StackPanel.Styles>
|
||||||
</StackPanel>
|
<ComboBox Classes="brush"
|
||||||
</Border>
|
Width="200"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
Items="{CompiledBinding LayerBrushDescriptors}"
|
||||||
|
SelectedItem="{CompiledBinding SelectedLayerBrushDescriptor}">
|
||||||
|
<ComboBox.ItemTemplate>
|
||||||
|
<DataTemplate DataType="{x:Type layerBrushes:LayerBrushDescriptor}">
|
||||||
|
<Grid ColumnDefinitions="30,*" RowDefinitions="Auto,Auto">
|
||||||
|
<avalonia:MaterialIcon Grid.Row="0"
|
||||||
|
Grid.RowSpan="2"
|
||||||
|
Kind="{CompiledBinding Icon}"
|
||||||
|
Height="20"
|
||||||
|
Width="20"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
HorizontalAlignment="Left" />
|
||||||
|
<TextBlock Grid.Row="0" Grid.Column="1" Text="{CompiledBinding DisplayName}" TextWrapping="Wrap" MaxWidth="350" />
|
||||||
|
<TextBlock Classes="subtitle" Grid.Row="1" Grid.Column="1" Text="{CompiledBinding Description}" TextWrapping="Wrap" MaxWidth="350" />
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
</ComboBox.ItemTemplate>
|
||||||
|
</ComboBox>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
<!-- Tools -->
|
<!-- Rendering settings -->
|
||||||
<TextBlock Classes="h4" Margin="0 15">
|
<TextBlock Classes="h4" Margin="0 15">
|
||||||
Tools
|
Rendering
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
<Border Classes="card" VerticalAlignment="Stretch" Margin="0,0,5,0">
|
<Border Classes="card" VerticalAlignment="Stretch" Margin="0,0,5,0">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||||
<StackPanel Grid.Column="0" VerticalAlignment="Center">
|
<StackPanel Grid.Column="0">
|
||||||
<TextBlock>
|
<TextBlock>
|
||||||
Setup wizard
|
Preferred render method
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
<TextBlock Classes="subtitle">
|
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
||||||
Opens the startup wizard usually shown when Artemis first starts.
|
Software-based rendering is done purely on the CPU while Vulkan uses GPU-acceleration.
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||||
<Button Command="{CompiledBinding ShowSetupWizard}" Width="150" Content="Show wizard" />
|
<ComboBox Width="150"
|
||||||
</StackPanel>
|
SelectedItem="{CompiledBinding CorePreferredGraphicsContext.Value}"
|
||||||
</Grid>
|
Items="{CompiledBinding GraphicsContexts}" />
|
||||||
<Separator Classes="card-separator" />
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
<Separator Classes="card-separator" />
|
||||||
|
|
||||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||||
<StackPanel Grid.Column="0" VerticalAlignment="Center">
|
<StackPanel Grid.Column="0">
|
||||||
<TextBlock>
|
<TextBlock>
|
||||||
Debugger
|
Render scale
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
<TextBlock Classes="subtitle">
|
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
||||||
Use the debugger to see the raw image Artemis is rendering on the surface.
|
Sets the resolution Artemis renders at, higher scale means more CPU-usage, especially on large surfaces.
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||||
<Button Command="{CompiledBinding ShowDebugger}" Width="150" Content="Show debugger" />
|
<ComboBox Width="150"
|
||||||
</StackPanel>
|
SelectedItem="{CompiledBinding SelectedRenderScale}"
|
||||||
</Grid>
|
Items="{CompiledBinding RenderScales}">
|
||||||
<Separator Classes="card-separator" />
|
<ComboBox.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<TextBlock Text="{CompiledBinding Display}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</ComboBox.ItemTemplate>
|
||||||
|
</ComboBox>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
<Separator Classes="card-separator" />
|
||||||
|
|
||||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||||
<StackPanel Grid.Column="0" VerticalAlignment="Center">
|
<StackPanel Grid.Column="0">
|
||||||
<TextBlock>
|
<TextBlock>
|
||||||
Application files
|
Target frame rate
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
<TextBlock Classes="subtitle">
|
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
||||||
Opens the directory where application files like plugins and settings are stored.
|
Sets the FPS Artemis tries to render at, higher FPS means more CPU-usage but smoother animations.
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
</StackPanel>
|
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
||||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
The options past 45 FPS are mostly useless unless you are using a custom device.
|
||||||
<Button Command="{CompiledBinding ShowDataFolder}" Width="150" Content="Show app files" />
|
</TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||||
</StackPanel>
|
<ComboBox Width="150"
|
||||||
</Border>
|
SelectedItem="{CompiledBinding SelectedTargetFrameRate}"
|
||||||
</StackPanel>
|
Items="{CompiledBinding TargetFrameRates}">
|
||||||
</ScrollViewer>
|
<ComboBox.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<TextBlock Text="{CompiledBinding Display}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</ComboBox.ItemTemplate>
|
||||||
|
</ComboBox>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- Tools -->
|
||||||
|
<TextBlock Classes="h4" Margin="0 15">
|
||||||
|
Tools
|
||||||
|
</TextBlock>
|
||||||
|
<Border Classes="card" VerticalAlignment="Stretch" Margin="0,0,5,0">
|
||||||
|
<StackPanel>
|
||||||
|
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||||
|
<StackPanel Grid.Column="0" VerticalAlignment="Center">
|
||||||
|
<TextBlock>
|
||||||
|
Setup wizard
|
||||||
|
</TextBlock>
|
||||||
|
<TextBlock Classes="subtitle">
|
||||||
|
Opens the startup wizard usually shown when Artemis first starts.
|
||||||
|
</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||||
|
<Button Command="{CompiledBinding ShowSetupWizard}" Width="150" Content="Show wizard" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
<Separator Classes="card-separator" />
|
||||||
|
|
||||||
|
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||||
|
<StackPanel Grid.Column="0" VerticalAlignment="Center">
|
||||||
|
<TextBlock>
|
||||||
|
Debugger
|
||||||
|
</TextBlock>
|
||||||
|
<TextBlock Classes="subtitle">
|
||||||
|
Use the debugger to see the raw image Artemis is rendering on the surface.
|
||||||
|
</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||||
|
<Button Command="{CompiledBinding ShowDebugger}" Width="150" Content="Show debugger" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
<Separator Classes="card-separator" />
|
||||||
|
|
||||||
|
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||||
|
<StackPanel Grid.Column="0" VerticalAlignment="Center">
|
||||||
|
<TextBlock>
|
||||||
|
Application files
|
||||||
|
</TextBlock>
|
||||||
|
<TextBlock Classes="subtitle">
|
||||||
|
Opens the directory where application files like plugins and settings are stored.
|
||||||
|
</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||||
|
<Button Command="{CompiledBinding ShowDataFolder}" Width="150" Content="Show app files" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</StackPanel>
|
||||||
|
</ScrollViewer>
|
||||||
|
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@ -149,6 +149,7 @@ public class GeneralTabViewModel : ActivatableViewModelBase
|
|||||||
public PluginSetting<string> CorePreferredGraphicsContext => _settingsService.GetSetting("Core.PreferredGraphicsContext", "Software");
|
public PluginSetting<string> CorePreferredGraphicsContext => _settingsService.GetSetting("Core.PreferredGraphicsContext", "Software");
|
||||||
public PluginSetting<double> CoreRenderScale => _settingsService.GetSetting("Core.RenderScale", 0.25);
|
public PluginSetting<double> CoreRenderScale => _settingsService.GetSetting("Core.RenderScale", 0.25);
|
||||||
public PluginSetting<int> CoreTargetFrameRate => _settingsService.GetSetting("Core.TargetFrameRate", 30);
|
public PluginSetting<int> CoreTargetFrameRate => _settingsService.GetSetting("Core.TargetFrameRate", 30);
|
||||||
|
public PluginSetting<bool> WebServerEnabled => _settingsService.GetSetting("WebServer.Enabled", true);
|
||||||
public PluginSetting<int> WebServerPort => _settingsService.GetSetting("WebServer.Port", 9696);
|
public PluginSetting<int> WebServerPort => _settingsService.GetSetting("WebServer.Port", 9696);
|
||||||
|
|
||||||
private void ExecuteShowLogs()
|
private void ExecuteShowLogs()
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
xmlns:skiaSharp="clr-namespace:SkiaSharp;assembly=SkiaSharp"
|
xmlns:skiaSharp="clr-namespace:SkiaSharp;assembly=SkiaSharp"
|
||||||
xmlns:shared="clr-namespace:Artemis.UI.Shared.Converters;assembly=Artemis.UI.Shared"
|
xmlns:shared="clr-namespace:Artemis.UI.Shared.Converters;assembly=Artemis.UI.Shared"
|
||||||
xmlns:core="clr-namespace:Artemis.Core;assembly=Artemis.Core"
|
xmlns:core="clr-namespace:Artemis.Core;assembly=Artemis.Core"
|
||||||
|
xmlns:collections="clr-namespace:System.Collections;assembly=System.Runtime"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Artemis.UI.Screens.VisualScripting.CableView"
|
x:Class="Artemis.UI.Screens.VisualScripting.CableView"
|
||||||
x:DataType="visualScripting:CableViewModel"
|
x:DataType="visualScripting:CableViewModel"
|
||||||
@ -79,6 +80,9 @@
|
|||||||
<DataTemplate DataType="core:Numeric">
|
<DataTemplate DataType="core:Numeric">
|
||||||
<TextBlock Text="{Binding}" FontFamily="Consolas"/>
|
<TextBlock Text="{Binding}" FontFamily="Consolas"/>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
<DataTemplate DataType="collections:IList">
|
||||||
|
<TextBlock Text="{Binding Count, StringFormat='List - {0} item(s)'}" FontFamily="Consolas"/>
|
||||||
|
</DataTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<TextBlock Text="{Binding}" FontFamily="Consolas"/>
|
<TextBlock Text="{Binding}" FontFamily="Consolas"/>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|||||||
@ -38,7 +38,8 @@
|
|||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Items="{CompiledBinding Categories}"
|
Items="{CompiledBinding Categories}"
|
||||||
IsVisible="{CompiledBinding Categories.Count}"
|
IsVisible="{CompiledBinding Categories.Count}"
|
||||||
SelectedItem="{CompiledBinding SelectedNode}">
|
SelectedItem="{CompiledBinding SelectedNode}"
|
||||||
|
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
|
||||||
<TreeView.Styles>
|
<TreeView.Styles>
|
||||||
<Style Selector="TreeViewItem">
|
<Style Selector="TreeViewItem">
|
||||||
<Setter Property="IsExpanded" Value="True" />
|
<Setter Property="IsExpanded" Value="True" />
|
||||||
@ -47,8 +48,8 @@
|
|||||||
<TreeView.DataTemplates>
|
<TreeView.DataTemplates>
|
||||||
<TreeDataTemplate DataType="{x:Type core:NodeData}">
|
<TreeDataTemplate DataType="{x:Type core:NodeData}">
|
||||||
<StackPanel Margin="-15 1 0 1" Background="Transparent" PointerReleased="InputElement_OnPointerReleased">
|
<StackPanel Margin="-15 1 0 1" Background="Transparent" PointerReleased="InputElement_OnPointerReleased">
|
||||||
<TextBlock Classes="BodyStrongTextBlockStyle" Text="{Binding Name}"></TextBlock>
|
<TextBlock Classes="BodyStrongTextBlockStyle" Text="{Binding Name}" TextWrapping="Wrap"></TextBlock>
|
||||||
<TextBlock Foreground="{DynamicResource TextFillColorSecondary}" Text="{Binding Description}"></TextBlock>
|
<TextBlock Foreground="{DynamicResource TextFillColorSecondary}" Text="{Binding Description}" TextWrapping="Wrap"></TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</TreeDataTemplate>
|
</TreeDataTemplate>
|
||||||
<TreeDataTemplate ItemsSource="{Binding Items}">
|
<TreeDataTemplate ItemsSource="{Binding Items}">
|
||||||
|
|||||||
@ -10,6 +10,7 @@ using Artemis.UI.Shared.Services.NodeEditor;
|
|||||||
using Artemis.UI.Shared.Services.NodeEditor.Commands;
|
using Artemis.UI.Shared.Services.NodeEditor.Commands;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using DynamicData;
|
using DynamicData;
|
||||||
|
using DynamicData.Binding;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
|
|
||||||
namespace Artemis.UI.Screens.VisualScripting;
|
namespace Artemis.UI.Screens.VisualScripting;
|
||||||
@ -35,6 +36,11 @@ public class NodePickerViewModel : ActivatableViewModelBase
|
|||||||
|
|
||||||
nodeSourceList.Connect()
|
nodeSourceList.Connect()
|
||||||
.Filter(nodeFilter)
|
.Filter(nodeFilter)
|
||||||
|
.Sort(SortExpressionComparer<NodeData>
|
||||||
|
.Descending(d => d.Category == "Data Model")
|
||||||
|
.ThenByDescending(d => d.Category == "Static")
|
||||||
|
.ThenByAscending(d => d.Category)
|
||||||
|
.ThenByAscending(d => d.Name))
|
||||||
.GroupWithImmutableState(n => n.Category)
|
.GroupWithImmutableState(n => n.Category)
|
||||||
.Bind(out ReadOnlyObservableCollection<DynamicData.List.IGrouping<NodeData, string>> categories)
|
.Bind(out ReadOnlyObservableCollection<DynamicData.List.IGrouping<NodeData, string>> categories)
|
||||||
.Subscribe();
|
.Subscribe();
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.Core.Services;
|
using Artemis.Core.Services;
|
||||||
using Artemis.UI.Controllers;
|
using Artemis.UI.Controllers;
|
||||||
@ -106,6 +107,9 @@ public class RegistrationService : IRegistrationService
|
|||||||
_nodeService.RegisterTypeColor(Constants.CorePlugin, typeof(Enum), new SKColor(0xFF1E90FF));
|
_nodeService.RegisterTypeColor(Constants.CorePlugin, typeof(Enum), new SKColor(0xFF1E90FF));
|
||||||
|
|
||||||
foreach (Type nodeType in typeof(SumNumericsNode).Assembly.GetTypes().Where(t => typeof(INode).IsAssignableFrom(t) && t.IsPublic && !t.IsAbstract && !t.IsInterface))
|
foreach (Type nodeType in typeof(SumNumericsNode).Assembly.GetTypes().Where(t => typeof(INode).IsAssignableFrom(t) && t.IsPublic && !t.IsAbstract && !t.IsInterface))
|
||||||
_nodeService.RegisterNodeType(Constants.CorePlugin, nodeType);
|
{
|
||||||
|
if (nodeType.GetCustomAttribute(typeof(NodeAttribute)) != null)
|
||||||
|
_nodeService.RegisterNodeType(Constants.CorePlugin, nodeType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
57
src/Artemis.VisualScripting/Nodes/Color/LerpSKColorNode.cs
Normal file
57
src/Artemis.VisualScripting/Nodes/Color/LerpSKColorNode.cs
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
using Artemis.Core;
|
||||||
|
using RGB.NET.Core;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace Artemis.VisualScripting.Nodes.Color;
|
||||||
|
|
||||||
|
[Node("Lerp (Color)", "Interpolates linear between the two colors A and B", "Color", InputType = typeof(SKColor), OutputType = typeof(SKColor))]
|
||||||
|
public class LerpSKColorNode : Node
|
||||||
|
{
|
||||||
|
#region Properties & Fields
|
||||||
|
|
||||||
|
public InputPin<SKColor> A { get; }
|
||||||
|
public InputPin<SKColor> B { get; }
|
||||||
|
public InputPin<Numeric> T { get; }
|
||||||
|
|
||||||
|
public OutputPin<SKColor> Result { get; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
|
||||||
|
public LerpSKColorNode()
|
||||||
|
: base("Lerp", "Interpolates linear between the two values A and B")
|
||||||
|
{
|
||||||
|
A = CreateInputPin<SKColor>("A");
|
||||||
|
B = CreateInputPin<SKColor>("B");
|
||||||
|
T = CreateInputPin<Numeric>("T");
|
||||||
|
|
||||||
|
Result = CreateOutputPin<SKColor>();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Evaluate()
|
||||||
|
{
|
||||||
|
SKColor a = A.Value;
|
||||||
|
SKColor b = B.Value;
|
||||||
|
float t = ((float)T.Value).Clamp(0f, 1f);
|
||||||
|
|
||||||
|
float aAlpha = a.Alpha.GetPercentageFromByteValue();
|
||||||
|
float aRed = a.Red.GetPercentageFromByteValue();
|
||||||
|
float aGreen = a.Green.GetPercentageFromByteValue();
|
||||||
|
float aBlue = a.Blue.GetPercentageFromByteValue();
|
||||||
|
|
||||||
|
float alpha = ((b.Alpha.GetPercentageFromByteValue() - aAlpha) * t) + aAlpha;
|
||||||
|
float red = ((b.Red.GetPercentageFromByteValue() - aRed) * t) + aRed;
|
||||||
|
float green = ((b.Green.GetPercentageFromByteValue() - aGreen) * t) + aGreen;
|
||||||
|
float blue = ((b.Blue.GetPercentageFromByteValue() - aBlue) * t) + aBlue;
|
||||||
|
|
||||||
|
Result.Value = new SKColor(red.GetByteValueFromPercentage(), green.GetByteValueFromPercentage(), blue.GetByteValueFromPercentage(), alpha.GetByteValueFromPercentage());
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
38
src/Artemis.VisualScripting/Nodes/Color/RgbSKColorNode.cs
Normal file
38
src/Artemis.VisualScripting/Nodes/Color/RgbSKColorNode.cs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
using Artemis.Core;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace Artemis.VisualScripting.Nodes.Color;
|
||||||
|
|
||||||
|
[Node("RGB Color", "Creates a color from red, green and blue values", "Color", InputType = typeof(Numeric), OutputType = typeof(SKColor))]
|
||||||
|
public class RgbSKColorNode : Node
|
||||||
|
{
|
||||||
|
#region Properties & Fields
|
||||||
|
|
||||||
|
public InputPin<Numeric> R { get; set; }
|
||||||
|
public InputPin<Numeric> G { get; set; }
|
||||||
|
public InputPin<Numeric> B { get; set; }
|
||||||
|
public OutputPin<SKColor> Output { get; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
|
||||||
|
public RgbSKColorNode()
|
||||||
|
: base("RGB Color", "Creates a color from red, green and blue values")
|
||||||
|
{
|
||||||
|
R = CreateInputPin<Numeric>("R");
|
||||||
|
G = CreateInputPin<Numeric>("G");
|
||||||
|
B = CreateInputPin<Numeric>("B");
|
||||||
|
|
||||||
|
Output = CreateOutputPin<SKColor>();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Evaluate() => Output.Value = new SKColor(R.Value, G.Value, B.Value);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@ -3,7 +3,7 @@ using Artemis.VisualScripting.Nodes.External.Screens;
|
|||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.External;
|
namespace Artemis.VisualScripting.Nodes.External;
|
||||||
|
|
||||||
[Node("Layer/Folder Property", "Outputs the property of a selected layer or folder", "External")]
|
// [Node("Layer/Folder Property", "Outputs the property of a selected layer or folder", "External")]
|
||||||
public class LayerPropertyNode : Node<LayerPropertyNodeEntity, LayerPropertyNodeCustomViewModel>
|
public class LayerPropertyNode : Node<LayerPropertyNodeEntity, LayerPropertyNodeCustomViewModel>
|
||||||
{
|
{
|
||||||
private readonly object _layerPropertyLock = new();
|
private readonly object _layerPropertyLock = new();
|
||||||
|
|||||||
46
src/Artemis.VisualScripting/Nodes/List/ListOperatorNode.cs
Normal file
46
src/Artemis.VisualScripting/Nodes/List/ListOperatorNode.cs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using Artemis.Core;
|
||||||
|
using Artemis.VisualScripting.Nodes.List.Screens;
|
||||||
|
|
||||||
|
namespace Artemis.VisualScripting.Nodes.List;
|
||||||
|
|
||||||
|
[Node("List Operator (Simple)", "Checks if any/all/no value in the input list matches the input value", "List", InputType = typeof(IEnumerable), OutputType = typeof(bool))]
|
||||||
|
public class ListOperatorNode : Node<ListOperator, ListOperatorNodeCustomViewModel>
|
||||||
|
{
|
||||||
|
public ListOperatorNode() : base("List Operator", "Checks if any/all/no value in the input list matches the input value")
|
||||||
|
{
|
||||||
|
InputList = CreateInputPin<IList>();
|
||||||
|
InputValue = CreateInputPin<object>();
|
||||||
|
|
||||||
|
Ouput = CreateOutputPin<bool>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputPin<IList> InputList { get; }
|
||||||
|
public InputPin<object> InputValue { get; }
|
||||||
|
public OutputPin<bool> Ouput { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Evaluate()
|
||||||
|
{
|
||||||
|
if (InputList.Value == null)
|
||||||
|
{
|
||||||
|
Ouput.Value = Storage == ListOperator.None;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
object? input = InputValue.Value;
|
||||||
|
if (Storage == ListOperator.Any)
|
||||||
|
Ouput.Value = InputList.Value.Cast<object>().Any(v => v.Equals(input));
|
||||||
|
else if (Storage == ListOperator.All)
|
||||||
|
Ouput.Value = InputList.Value.Cast<object>().All(v => v.Equals(input));
|
||||||
|
else if (Storage == ListOperator.All)
|
||||||
|
Ouput.Value = InputList.Value.Cast<object>().All(v => !v.Equals(input));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ListOperator
|
||||||
|
{
|
||||||
|
Any,
|
||||||
|
All,
|
||||||
|
None
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:screens="clr-namespace:Artemis.VisualScripting.Nodes.List.Screens"
|
||||||
|
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Artemis.VisualScripting.Nodes.List.Screens.ListOperatorNodeCustomView"
|
||||||
|
x:DataType="screens:ListOperatorNodeCustomViewModel">
|
||||||
|
<shared:EnumComboBox Value="{CompiledBinding CurrentValue}" Classes="condensed"/>
|
||||||
|
</UserControl>
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Avalonia.ReactiveUI;
|
||||||
|
|
||||||
|
namespace Artemis.VisualScripting.Nodes.List.Screens;
|
||||||
|
|
||||||
|
public partial class ListOperatorNodeCustomView : ReactiveUserControl<ListOperatorNodeCustomViewModel>
|
||||||
|
{
|
||||||
|
public ListOperatorNodeCustomView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
using Artemis.Core;
|
||||||
|
using Artemis.UI.Shared.Services.NodeEditor;
|
||||||
|
using Artemis.UI.Shared.Services.NodeEditor.Commands;
|
||||||
|
using Artemis.UI.Shared.VisualScripting;
|
||||||
|
using ReactiveUI;
|
||||||
|
|
||||||
|
namespace Artemis.VisualScripting.Nodes.List.Screens;
|
||||||
|
|
||||||
|
public class ListOperatorNodeCustomViewModel : CustomNodeViewModel
|
||||||
|
{
|
||||||
|
private readonly ListOperatorNode _node;
|
||||||
|
private readonly INodeEditorService _nodeEditorService;
|
||||||
|
|
||||||
|
public ListOperatorNodeCustomViewModel(ListOperatorNode node, INodeScript script, INodeEditorService nodeEditorService) : base(node, script)
|
||||||
|
{
|
||||||
|
_node = node;
|
||||||
|
_nodeEditorService = nodeEditorService;
|
||||||
|
|
||||||
|
NodeModified += (_, _) => this.RaisePropertyChanged(nameof(CurrentValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ListOperator CurrentValue
|
||||||
|
{
|
||||||
|
get => _node.Storage;
|
||||||
|
set => _nodeEditorService.ExecuteCommand(Script, new UpdateStorage<ListOperator>(_node, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
39
src/Artemis.VisualScripting/Nodes/Mathematics/Clamp.cs
Normal file
39
src/Artemis.VisualScripting/Nodes/Mathematics/Clamp.cs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
using Artemis.Core;
|
||||||
|
using RGB.NET.Core;
|
||||||
|
|
||||||
|
namespace Artemis.VisualScripting.Nodes.Mathematics;
|
||||||
|
|
||||||
|
[Node("Clamp", "Clamps the value to be in between min and max", "Mathematics", InputType = typeof(Numeric), OutputType = typeof(Numeric))]
|
||||||
|
public class ClampNode : Node
|
||||||
|
{
|
||||||
|
#region Properties & Fields
|
||||||
|
|
||||||
|
public InputPin<Numeric> Value { get; }
|
||||||
|
public InputPin<Numeric> Min { get; }
|
||||||
|
public InputPin<Numeric> Max { get; }
|
||||||
|
|
||||||
|
public OutputPin<Numeric> Result { get; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
|
||||||
|
public ClampNode()
|
||||||
|
: base("Clamp", "Clamps the value to be in between min and max")
|
||||||
|
{
|
||||||
|
Value = CreateInputPin<Numeric>("Value");
|
||||||
|
Min = CreateInputPin<Numeric>("Min");
|
||||||
|
Max = CreateInputPin<Numeric>("Max");
|
||||||
|
|
||||||
|
Result = CreateOutputPin<Numeric>();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Evaluate() => Result.Value = ((float)Value.Value).Clamp(Min.Value, Max.Value);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
45
src/Artemis.VisualScripting/Nodes/Mathematics/LerpNode.cs
Normal file
45
src/Artemis.VisualScripting/Nodes/Mathematics/LerpNode.cs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
using Artemis.Core;
|
||||||
|
using RGB.NET.Core;
|
||||||
|
|
||||||
|
namespace Artemis.VisualScripting.Nodes.Mathematics;
|
||||||
|
|
||||||
|
[Node("Lerp", "Interpolates linear between the two values A and B", "Mathematics", InputType = typeof(Numeric), OutputType = typeof(Numeric))]
|
||||||
|
public class LerpNode : Node
|
||||||
|
{
|
||||||
|
#region Properties & Fields
|
||||||
|
|
||||||
|
public InputPin<Numeric> A { get; }
|
||||||
|
public InputPin<Numeric> B { get; }
|
||||||
|
public InputPin<Numeric> T { get; }
|
||||||
|
|
||||||
|
public OutputPin<Numeric> Result { get; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
|
||||||
|
public LerpNode()
|
||||||
|
: base("Lerp", "Interpolates linear between the two values A and B")
|
||||||
|
{
|
||||||
|
A = CreateInputPin<Numeric>("A");
|
||||||
|
B = CreateInputPin<Numeric>("B");
|
||||||
|
T = CreateInputPin<Numeric>("T");
|
||||||
|
|
||||||
|
Result = CreateOutputPin<Numeric>();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Evaluate()
|
||||||
|
{
|
||||||
|
float a = A.Value;
|
||||||
|
float b = B.Value;
|
||||||
|
float t = ((float)T.Value).Clamp(0f, 1f);
|
||||||
|
Result.Value = ((b - a) * t) + a;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
48
src/Artemis.VisualScripting/Nodes/Mathematics/RangeNode.cs
Normal file
48
src/Artemis.VisualScripting/Nodes/Mathematics/RangeNode.cs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
using Artemis.Core;
|
||||||
|
using RGB.NET.Core;
|
||||||
|
|
||||||
|
namespace Artemis.VisualScripting.Nodes.Mathematics;
|
||||||
|
|
||||||
|
[Node("Range", "Selects the best integer value in the given range by the given percentage", "Static", InputType = typeof(Numeric), OutputType = typeof(Numeric))]
|
||||||
|
public class RangeNode : Node
|
||||||
|
{
|
||||||
|
#region Properties & Fields
|
||||||
|
|
||||||
|
public InputPin<Numeric> Min { get; }
|
||||||
|
public InputPin<Numeric> Max { get; }
|
||||||
|
public InputPin<Numeric> Percentage { get; }
|
||||||
|
|
||||||
|
public OutputPin<Numeric> Result { get; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
|
||||||
|
public RangeNode()
|
||||||
|
: base("Range", "Selects the best integer value in the given range by the given percentage")
|
||||||
|
{
|
||||||
|
Min = CreateInputPin<Numeric>("Min");
|
||||||
|
Max = CreateInputPin<Numeric>("Max");
|
||||||
|
Percentage = CreateInputPin<Numeric>("Percentage");
|
||||||
|
|
||||||
|
Result = CreateOutputPin<Numeric>();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Evaluate()
|
||||||
|
{
|
||||||
|
int min = Min.Value;
|
||||||
|
int max = Max.Value;
|
||||||
|
float percentage = ((float)Percentage.Value).Clamp(0f, 1f);
|
||||||
|
|
||||||
|
int range = max - min;
|
||||||
|
|
||||||
|
Result.Value = percentage >= 1.0f ? max : ((int)(percentage * (range + 1)) + min);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
35
src/Artemis.VisualScripting/Nodes/Mathematics/Saturate.cs
Normal file
35
src/Artemis.VisualScripting/Nodes/Mathematics/Saturate.cs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
using Artemis.Core;
|
||||||
|
using RGB.NET.Core;
|
||||||
|
|
||||||
|
namespace Artemis.VisualScripting.Nodes.Mathematics;
|
||||||
|
|
||||||
|
[Node("Saturate", "Clamps the value to be in between 0 and 1", "Mathematics", InputType = typeof(Numeric), OutputType = typeof(Numeric))]
|
||||||
|
public class SaturateNode : Node
|
||||||
|
{
|
||||||
|
#region Properties & Fields
|
||||||
|
|
||||||
|
public InputPin<Numeric> Value { get; }
|
||||||
|
|
||||||
|
public OutputPin<Numeric> Result { get; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
|
||||||
|
public SaturateNode()
|
||||||
|
: base("Saturate", "Clamps the value to be in between 0 and 1")
|
||||||
|
{
|
||||||
|
Value = CreateInputPin<Numeric>();
|
||||||
|
|
||||||
|
Result = CreateOutputPin<Numeric>();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Evaluate() => Result.Value = ((float)Value.Value).Clamp(0f, 1f);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
using Artemis.Core;
|
||||||
|
|
||||||
|
namespace Artemis.VisualScripting.Nodes.Static;
|
||||||
|
|
||||||
|
[Node("Random", "Generates a random value between 0 and 1", "Static", OutputType = typeof(Numeric))]
|
||||||
|
public class RandomNumericValueNode : Node
|
||||||
|
{
|
||||||
|
#region Properties & Fields
|
||||||
|
|
||||||
|
private static readonly Random RANDOM = new();
|
||||||
|
|
||||||
|
public OutputPin<Numeric> Output { get; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
|
||||||
|
public RandomNumericValueNode()
|
||||||
|
: base("Random", "Generates a random value between 0 and 1")
|
||||||
|
{
|
||||||
|
Output = CreateOutputPin<Numeric>();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Evaluate() => Output.Value = RANDOM.NextSingle();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@ -6,6 +6,7 @@
|
|||||||
xmlns:skiaSharp="clr-namespace:SkiaSharp;assembly=SkiaSharp"
|
xmlns:skiaSharp="clr-namespace:SkiaSharp;assembly=SkiaSharp"
|
||||||
xmlns:core="clr-namespace:Artemis.Core;assembly=Artemis.Core"
|
xmlns:core="clr-namespace:Artemis.Core;assembly=Artemis.Core"
|
||||||
xmlns:converters1="clr-namespace:Artemis.UI.Shared.Converters;assembly=Artemis.UI.Shared"
|
xmlns:converters1="clr-namespace:Artemis.UI.Shared.Converters;assembly=Artemis.UI.Shared"
|
||||||
|
xmlns:collections="clr-namespace:System.Collections;assembly=System.Runtime"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Artemis.VisualScripting.Nodes.Static.Screens.DisplayValueNodeCustomView"
|
x:Class="Artemis.VisualScripting.Nodes.Static.Screens.DisplayValueNodeCustomView"
|
||||||
x:DataType="screens:DisplayValueNodeCustomViewModel">
|
x:DataType="screens:DisplayValueNodeCustomViewModel">
|
||||||
@ -43,6 +44,9 @@
|
|||||||
<DataTemplate DataType="core:Numeric">
|
<DataTemplate DataType="core:Numeric">
|
||||||
<TextBlock Text="{Binding}" FontFamily="Consolas"/>
|
<TextBlock Text="{Binding}" FontFamily="Consolas"/>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
<DataTemplate DataType="collections:IList">
|
||||||
|
<TextBlock Text="{Binding Count, StringFormat='List - {0} item(s)'}" FontFamily="Consolas"/>
|
||||||
|
</DataTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<TextBlock Text="{Binding}" FontFamily="Consolas"/>
|
<TextBlock Text="{Binding}" FontFamily="Consolas"/>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|||||||
@ -3,13 +3,13 @@ using Artemis.VisualScripting.Nodes.Static.Screens;
|
|||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Static;
|
namespace Artemis.VisualScripting.Nodes.Static;
|
||||||
|
|
||||||
[Node("String-Value", "Outputs a configurable static string value.", "Static", OutputType = typeof(string))]
|
[Node("Text-Value", "Outputs a configurable static text value.", "Static", OutputType = typeof(string))]
|
||||||
public class StaticStringValueNode : Node<string, StaticStringValueNodeCustomViewModel>
|
public class StaticStringValueNode : Node<string, StaticStringValueNodeCustomViewModel>
|
||||||
{
|
{
|
||||||
#region Constructors
|
#region Constructors
|
||||||
|
|
||||||
public StaticStringValueNode()
|
public StaticStringValueNode()
|
||||||
: base("String", "Outputs a configurable string value.")
|
: base("Text", "Outputs a configurable text value.")
|
||||||
{
|
{
|
||||||
Output = CreateOutputPin<string>();
|
Output = CreateOutputPin<string>();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Text;
|
namespace Artemis.VisualScripting.Nodes.Text;
|
||||||
|
|
||||||
[Node("Format", "Formats the input string.", "Text", InputType = typeof(object), OutputType = typeof(string))]
|
[Node("Format", "Formats the input text.", "Text", InputType = typeof(object), OutputType = typeof(string))]
|
||||||
public class StringFormatNode : Node
|
public class StringFormatNode : Node
|
||||||
{
|
{
|
||||||
#region Constructors
|
#region Constructors
|
||||||
|
|||||||
24
src/Artemis.VisualScripting/Nodes/Text/StringLengthNode.cs
Normal file
24
src/Artemis.VisualScripting/Nodes/Text/StringLengthNode.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
using Artemis.Core;
|
||||||
|
|
||||||
|
namespace Artemis.VisualScripting.Nodes.Text;
|
||||||
|
|
||||||
|
[Node("Text Length", "Outputs the length of the input text.",
|
||||||
|
"Text", InputType = typeof(string), OutputType = typeof(Numeric))]
|
||||||
|
public class StringLengthNode : Node
|
||||||
|
{
|
||||||
|
public StringLengthNode()
|
||||||
|
: base("Text Length", "Outputs text length.")
|
||||||
|
{
|
||||||
|
Input1 = CreateInputPin<string>();
|
||||||
|
Result = CreateOutputPin<Numeric>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputPin<string> Input1 { get; }
|
||||||
|
|
||||||
|
public OutputPin<Numeric> Result { get; }
|
||||||
|
|
||||||
|
public override void Evaluate()
|
||||||
|
{
|
||||||
|
Result.Value = Input1.Value == null ? new Numeric(0) : new Numeric(Input1.Value.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
using Artemis.Core;
|
||||||
|
|
||||||
|
namespace Artemis.VisualScripting.Nodes.Text;
|
||||||
|
|
||||||
|
[Node("Text is empty", "Outputs true if the input text is empty, false if it contains any text.",
|
||||||
|
"Text", InputType = typeof(string), OutputType = typeof(bool))]
|
||||||
|
public class StringNullOrEmptyNode : Node
|
||||||
|
{
|
||||||
|
public StringNullOrEmptyNode()
|
||||||
|
: base("Text is empty", "Outputs true if empty")
|
||||||
|
{
|
||||||
|
Input1 = CreateInputPin<string>();
|
||||||
|
Output1 = CreateOutputPin<bool>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputPin<string> Input1 { get; }
|
||||||
|
|
||||||
|
public OutputPin<bool> Output1 { get; }
|
||||||
|
|
||||||
|
public override void Evaluate()
|
||||||
|
{
|
||||||
|
bool isNullOrWhiteSpace = string.IsNullOrWhiteSpace(Input1.Value);
|
||||||
|
Output1.Value = isNullOrWhiteSpace;
|
||||||
|
}
|
||||||
|
}
|
||||||
117
src/Artemis.VisualScripting/Nodes/Timing/DelayNode.cs
Normal file
117
src/Artemis.VisualScripting/Nodes/Timing/DelayNode.cs
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using Artemis.Core;
|
||||||
|
using Artemis.Core.Events;
|
||||||
|
using RGB.NET.Core;
|
||||||
|
|
||||||
|
namespace Artemis.VisualScripting.Nodes.Timing;
|
||||||
|
|
||||||
|
[Node("Delay", "Delays the resolution of the input pin(s) for the given time after each update", "Timing", InputType = typeof(object), OutputType = typeof(object))]
|
||||||
|
public class DelayNode : Node
|
||||||
|
{
|
||||||
|
#region Properties & Fields
|
||||||
|
|
||||||
|
private long _lastUpdateTimestamp = 0;
|
||||||
|
|
||||||
|
public InputPin<Numeric> Delay { get; }
|
||||||
|
public InputPinCollection Input { get; }
|
||||||
|
|
||||||
|
public OutputPin<bool> IsUpdated { get; }
|
||||||
|
public OutputPin<Numeric> NextUpdateTime { get; }
|
||||||
|
|
||||||
|
private Dictionary<IPin, OutputPin> _pinPairs = new();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
|
||||||
|
public DelayNode()
|
||||||
|
: base("Delay", "Delays the resolution of the input pin(s) for the given time after each update")
|
||||||
|
{
|
||||||
|
Delay = CreateInputPin<Numeric>("Delay");
|
||||||
|
Input = CreateInputPinCollection(typeof(object), initialCount: 0);
|
||||||
|
|
||||||
|
IsUpdated = CreateOutputPin<bool>("Updated");
|
||||||
|
NextUpdateTime = CreateOutputPin<Numeric>("Next Update");
|
||||||
|
|
||||||
|
Input.PinAdded += OnInputPinAdded;
|
||||||
|
Input.PinRemoved += OnInputPinRemoved;
|
||||||
|
|
||||||
|
Input.Add(Input.CreatePin());
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
|
||||||
|
private void OnInputPinAdded(object? sender, SingleValueEventArgs<IPin> args)
|
||||||
|
{
|
||||||
|
IPin inputPin = args.Value;
|
||||||
|
_pinPairs.Add(inputPin, CreateOutputPin(typeof(object)));
|
||||||
|
|
||||||
|
inputPin.PinConnected += OnInputPinConnected;
|
||||||
|
inputPin.PinDisconnected += OnInputPinDisconnected;
|
||||||
|
|
||||||
|
UpdatePinNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnInputPinRemoved(object? sender, SingleValueEventArgs<IPin> args)
|
||||||
|
{
|
||||||
|
IPin inputPin = args.Value;
|
||||||
|
RemovePin(_pinPairs[inputPin]);
|
||||||
|
_pinPairs.Remove(inputPin);
|
||||||
|
|
||||||
|
inputPin.PinConnected -= OnInputPinConnected;
|
||||||
|
inputPin.PinDisconnected -= OnInputPinDisconnected;
|
||||||
|
|
||||||
|
UpdatePinNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnInputPinConnected(object? sender, SingleValueEventArgs<IPin> args)
|
||||||
|
{
|
||||||
|
if (sender is not IPin inputPin || !_pinPairs.ContainsKey(inputPin)) return;
|
||||||
|
|
||||||
|
OutputPin outputPin = _pinPairs[inputPin];
|
||||||
|
outputPin.ChangeType(args.Value.Type);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnInputPinDisconnected(object? sender, SingleValueEventArgs<IPin> args)
|
||||||
|
{
|
||||||
|
if (sender is not IPin inputPin || !_pinPairs.ContainsKey(inputPin)) return;
|
||||||
|
|
||||||
|
OutputPin outputPin = _pinPairs[inputPin];
|
||||||
|
outputPin.ChangeType(typeof(object));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdatePinNames()
|
||||||
|
{
|
||||||
|
int counter = 1;
|
||||||
|
foreach (IPin inputPin in Input.Pins)
|
||||||
|
{
|
||||||
|
string name = counter.ToString();
|
||||||
|
inputPin.Name = name;
|
||||||
|
_pinPairs[inputPin].Name = name;
|
||||||
|
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Evaluate()
|
||||||
|
{
|
||||||
|
double nextUpdateIn = Delay.Value - TimerHelper.GetElapsedTime(_lastUpdateTimestamp);
|
||||||
|
NextUpdateTime.Value = nextUpdateIn;
|
||||||
|
|
||||||
|
if (nextUpdateIn <= 0)
|
||||||
|
{
|
||||||
|
IsUpdated.Value = true;
|
||||||
|
foreach ((IPin input, OutputPin output) in _pinPairs)
|
||||||
|
output.Value = input.PinValue;
|
||||||
|
|
||||||
|
_lastUpdateTimestamp = Stopwatch.GetTimestamp();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
IsUpdated.Value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
41
src/Artemis.VisualScripting/Nodes/Timing/EdgeNode.cs
Normal file
41
src/Artemis.VisualScripting/Nodes/Timing/EdgeNode.cs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
using Artemis.Core;
|
||||||
|
|
||||||
|
namespace Artemis.VisualScripting.Nodes.Timing;
|
||||||
|
|
||||||
|
[Node("Edge", "Outputs true on each edge when the input changes", "Timing", InputType = typeof(bool), OutputType = typeof(bool))]
|
||||||
|
public class EdgeNode : Node
|
||||||
|
{
|
||||||
|
#region Properties & Fields
|
||||||
|
|
||||||
|
private bool _lastInput;
|
||||||
|
|
||||||
|
public InputPin<bool> Input { get; }
|
||||||
|
public OutputPin<bool> Output { get; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
|
||||||
|
public EdgeNode()
|
||||||
|
: base("Edge", "Outputs true on each edge when the input changes")
|
||||||
|
{
|
||||||
|
Input = CreateInputPin<bool>();
|
||||||
|
Output = CreateOutputPin<bool>();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Evaluate()
|
||||||
|
{
|
||||||
|
bool input = Input.Value;
|
||||||
|
|
||||||
|
Output.Value = input != _lastInput;
|
||||||
|
|
||||||
|
_lastInput = input;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
45
src/Artemis.VisualScripting/Nodes/Timing/FlipFlopNode.cs
Normal file
45
src/Artemis.VisualScripting/Nodes/Timing/FlipFlopNode.cs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
using Artemis.Core;
|
||||||
|
|
||||||
|
namespace Artemis.VisualScripting.Nodes.Timing;
|
||||||
|
|
||||||
|
[Node("FlipFlop", "Inverts the output when the input changes from false to true", "Timing", InputType = typeof(bool), OutputType = typeof(bool))]
|
||||||
|
public class FlipFlopNode : Node
|
||||||
|
{
|
||||||
|
#region Properties & Fields
|
||||||
|
|
||||||
|
private bool _lastInput;
|
||||||
|
private bool _currentValue;
|
||||||
|
|
||||||
|
public InputPin<bool> Input { get; }
|
||||||
|
public OutputPin<bool> Output { get; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
|
||||||
|
public FlipFlopNode()
|
||||||
|
: base("FlipFlop", "Inverts the output when the input changes from false to true")
|
||||||
|
{
|
||||||
|
Input = CreateInputPin<bool>();
|
||||||
|
Output = CreateOutputPin<bool>();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Evaluate()
|
||||||
|
{
|
||||||
|
bool input = Input.Value;
|
||||||
|
if (input && !_lastInput)
|
||||||
|
{
|
||||||
|
_currentValue = !_currentValue;
|
||||||
|
Output.Value = _currentValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_lastInput = input;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
113
src/Artemis.VisualScripting/Nodes/Timing/LatchNode.cs
Normal file
113
src/Artemis.VisualScripting/Nodes/Timing/LatchNode.cs
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using Artemis.Core;
|
||||||
|
using Artemis.Core.Events;
|
||||||
|
using RGB.NET.Core;
|
||||||
|
|
||||||
|
namespace Artemis.VisualScripting.Nodes.Timing;
|
||||||
|
|
||||||
|
[Node("Latch", "Only passes the input to the output as long as the control-pin is true. If the control pin is false the last passed value is provided.", "Timing", InputType = typeof(object), OutputType = typeof(object))]
|
||||||
|
public class LatchNode : Node
|
||||||
|
{
|
||||||
|
#region Properties & Fields
|
||||||
|
|
||||||
|
private long _lastUpdateTimestamp = 0;
|
||||||
|
|
||||||
|
public InputPin<bool> Control { get; }
|
||||||
|
public InputPinCollection Input { get; }
|
||||||
|
|
||||||
|
//TODO DarthAffe 21.08.2022: Find something to output to aling in- and outputs
|
||||||
|
public OutputPin<Numeric> LastUpdateTime { get; }
|
||||||
|
|
||||||
|
private Dictionary<IPin, OutputPin> _pinPairs = new();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
|
||||||
|
public LatchNode()
|
||||||
|
: base("Latch", "Only passes the input to the output as long as the control-pin is true. If the control pin is false the last passed value is provided.")
|
||||||
|
{
|
||||||
|
Control = CreateInputPin<bool>("Control");
|
||||||
|
Input = CreateInputPinCollection(typeof(object), initialCount: 0);
|
||||||
|
|
||||||
|
LastUpdateTime = CreateOutputPin<Numeric>("Last Update");
|
||||||
|
|
||||||
|
Input.PinAdded += OnInputPinAdded;
|
||||||
|
Input.PinRemoved += OnInputPinRemoved;
|
||||||
|
|
||||||
|
Input.Add(Input.CreatePin());
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
|
||||||
|
private void OnInputPinAdded(object? sender, SingleValueEventArgs<IPin> args)
|
||||||
|
{
|
||||||
|
IPin inputPin = args.Value;
|
||||||
|
_pinPairs.Add(inputPin, CreateOutputPin(typeof(object)));
|
||||||
|
|
||||||
|
inputPin.PinConnected += OnInputPinConnected;
|
||||||
|
inputPin.PinDisconnected += OnInputPinDisconnected;
|
||||||
|
|
||||||
|
UpdatePinNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnInputPinRemoved(object? sender, SingleValueEventArgs<IPin> args)
|
||||||
|
{
|
||||||
|
IPin inputPin = args.Value;
|
||||||
|
RemovePin(_pinPairs[inputPin]);
|
||||||
|
_pinPairs.Remove(inputPin);
|
||||||
|
|
||||||
|
inputPin.PinConnected -= OnInputPinConnected;
|
||||||
|
inputPin.PinDisconnected -= OnInputPinDisconnected;
|
||||||
|
|
||||||
|
UpdatePinNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnInputPinConnected(object? sender, SingleValueEventArgs<IPin> args)
|
||||||
|
{
|
||||||
|
if (sender is not IPin inputPin || !_pinPairs.ContainsKey(inputPin)) return;
|
||||||
|
|
||||||
|
OutputPin outputPin = _pinPairs[inputPin];
|
||||||
|
outputPin.ChangeType(args.Value.Type);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnInputPinDisconnected(object? sender, SingleValueEventArgs<IPin> args)
|
||||||
|
{
|
||||||
|
if (sender is not IPin inputPin || !_pinPairs.ContainsKey(inputPin)) return;
|
||||||
|
|
||||||
|
OutputPin outputPin = _pinPairs[inputPin];
|
||||||
|
outputPin.ChangeType(typeof(object));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdatePinNames()
|
||||||
|
{
|
||||||
|
int counter = 1;
|
||||||
|
foreach (IPin inputPin in Input.Pins)
|
||||||
|
{
|
||||||
|
string name = counter.ToString();
|
||||||
|
inputPin.Name = name;
|
||||||
|
_pinPairs[inputPin].Name = name;
|
||||||
|
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Evaluate()
|
||||||
|
{
|
||||||
|
if (Control.Value)
|
||||||
|
{
|
||||||
|
foreach ((IPin input, OutputPin output) in _pinPairs)
|
||||||
|
output.Value = input.PinValue;
|
||||||
|
|
||||||
|
LastUpdateTime.Value = 0;
|
||||||
|
_lastUpdateTimestamp = Stopwatch.GetTimestamp();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
LastUpdateTime.Value = TimerHelper.GetElapsedTime(_lastUpdateTimestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
136
src/Artemis.VisualScripting/Nodes/Timing/SequencerNode.cs
Normal file
136
src/Artemis.VisualScripting/Nodes/Timing/SequencerNode.cs
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
using Artemis.Core;
|
||||||
|
using Artemis.Core.Events;
|
||||||
|
|
||||||
|
namespace Artemis.VisualScripting.Nodes.Timing;
|
||||||
|
|
||||||
|
[Node("Sequencer", "Advances on input every time the control has a rising edge (change to true)", "Timing", OutputType = typeof(object))]
|
||||||
|
public class SequencerNode : Node
|
||||||
|
{
|
||||||
|
#region Properties & Fields
|
||||||
|
|
||||||
|
private int _currentIndex;
|
||||||
|
private Type _currentType;
|
||||||
|
private bool _updating;
|
||||||
|
private IPin? _currentCyclePin;
|
||||||
|
|
||||||
|
private bool _lastInput;
|
||||||
|
|
||||||
|
public InputPin<bool> Input { get; }
|
||||||
|
public InputPinCollection CycleValues { get; }
|
||||||
|
|
||||||
|
public OutputPin Output { get; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
|
||||||
|
public SequencerNode()
|
||||||
|
: base("Sequencer", "Advances on input every time the control has a rising edge (change to true)")
|
||||||
|
{
|
||||||
|
_currentType = typeof(object);
|
||||||
|
|
||||||
|
Input = CreateInputPin<bool>("Control");
|
||||||
|
CycleValues = CreateInputPinCollection(typeof(object), "", 0);
|
||||||
|
Output = CreateOutputPin(typeof(object));
|
||||||
|
|
||||||
|
CycleValues.PinAdded += CycleValuesOnPinAdded;
|
||||||
|
CycleValues.PinRemoved += CycleValuesOnPinRemoved;
|
||||||
|
CycleValues.Add(CycleValues.CreatePin());
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
|
||||||
|
public override void Evaluate()
|
||||||
|
{
|
||||||
|
bool input = Input.Value;
|
||||||
|
|
||||||
|
if (input != _lastInput)
|
||||||
|
{
|
||||||
|
_currentIndex++;
|
||||||
|
|
||||||
|
if (_currentIndex >= CycleValues.Count())
|
||||||
|
_currentIndex = 0;
|
||||||
|
|
||||||
|
_currentCyclePin = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentCyclePin ??= CycleValues.ElementAt(_currentIndex);
|
||||||
|
|
||||||
|
object? outputValue = _currentCyclePin.PinValue;
|
||||||
|
if (Output.Type.IsInstanceOfType(outputValue))
|
||||||
|
Output.Value = outputValue;
|
||||||
|
else if (Output.Type.IsValueType)
|
||||||
|
Output.Value = Output.Type.GetDefault()!;
|
||||||
|
|
||||||
|
_lastInput = input;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CycleValuesOnPinAdded(object? sender, SingleValueEventArgs<IPin> e)
|
||||||
|
{
|
||||||
|
e.Value.PinConnected += OnPinConnected;
|
||||||
|
e.Value.PinDisconnected += OnPinDisconnected;
|
||||||
|
|
||||||
|
_currentCyclePin = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CycleValuesOnPinRemoved(object? sender, SingleValueEventArgs<IPin> e)
|
||||||
|
{
|
||||||
|
e.Value.PinConnected -= OnPinConnected;
|
||||||
|
e.Value.PinDisconnected -= OnPinDisconnected;
|
||||||
|
|
||||||
|
_currentCyclePin = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPinDisconnected(object? sender, SingleValueEventArgs<IPin> e) => ProcessPinDisconnected();
|
||||||
|
|
||||||
|
private void OnPinConnected(object? sender, SingleValueEventArgs<IPin> e) => ProcessPinConnected(e.Value);
|
||||||
|
|
||||||
|
private void ProcessPinConnected(IPin source)
|
||||||
|
{
|
||||||
|
if (_updating)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_updating = true;
|
||||||
|
|
||||||
|
// No need to change anything if the types haven't changed
|
||||||
|
if (_currentType != source.Type)
|
||||||
|
ChangeCurrentType(source.Type);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_updating = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ChangeCurrentType(Type type)
|
||||||
|
{
|
||||||
|
CycleValues.ChangeType(type);
|
||||||
|
Output.ChangeType(type);
|
||||||
|
|
||||||
|
_currentType = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessPinDisconnected()
|
||||||
|
{
|
||||||
|
if (_updating)
|
||||||
|
return;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// If there's still a connected pin, stick to the current type
|
||||||
|
if (CycleValues.Any(v => v.ConnectedTo.Any()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ChangeCurrentType(typeof(object));
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_updating = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user