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
|
||||
pr: none
|
||||
|
||||
resources:
|
||||
repositories:
|
||||
- repository: RGBNET
|
||||
type: github
|
||||
endpoint: github.com_SpoinkyNL
|
||||
name: DarthAffe/RGB.NET
|
||||
ref: Development
|
||||
|
||||
pool:
|
||||
vmImage: 'windows-latest'
|
||||
|
||||
@ -28,22 +20,13 @@ variables:
|
||||
SourceVersion: $(Build.SourceVersion)
|
||||
|
||||
steps:
|
||||
- checkout: RGBNET
|
||||
path: s/RGB.NET
|
||||
- checkout: self
|
||||
path: s/Artemis
|
||||
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: 'RGB.NET - Build'
|
||||
displayName: 'dotnet build Artemis'
|
||||
inputs:
|
||||
command: 'build'
|
||||
projects: '$(rgbSolution)'
|
||||
arguments: '--configuration Release'
|
||||
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: 'dotnet restore Artemis'
|
||||
inputs:
|
||||
command: 'restore'
|
||||
projects: '$(artemisSolution)'
|
||||
feedsToUse: '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": [
|
||||
{
|
||||
"files": [
|
||||
"Artemis.Core/Artemis.Core.csproj",
|
||||
"Artemis.UI.Shared/Artemis.UI.Shared.csproj",
|
||||
"Artemis.Core/bin/net6.0/Artemis.Core.dll",
|
||||
"Artemis.UI.Shared/bin/net6.0/Artemis.UI.Shared.dll",
|
||||
],
|
||||
"src": "../../src"
|
||||
}
|
||||
@ -20,14 +20,11 @@
|
||||
"content": [
|
||||
{
|
||||
"files": [
|
||||
"api/**.yml",
|
||||
"api/index.md"
|
||||
"api/**.yml"
|
||||
]
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"articles/**.md",
|
||||
"articles/**/toc.yml",
|
||||
"toc.yml",
|
||||
"*.md"
|
||||
]
|
||||
@ -52,16 +49,24 @@
|
||||
}
|
||||
],
|
||||
"globalMetadata": {
|
||||
"_appTitle": "Artemis documentation",
|
||||
"_enableSearch": true
|
||||
"_appTitle": "Artemis API Documentation",
|
||||
"_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",
|
||||
"globalMetadataFiles": [],
|
||||
"fileMetadataFiles": [],
|
||||
"template": [
|
||||
"default",
|
||||
"templates/artemis",
|
||||
"templates/material"
|
||||
"templates/singulinkfx"
|
||||
],
|
||||
"postProcessors": [],
|
||||
"markdownEngineName": "markdig",
|
||||
|
||||
@ -11,6 +11,9 @@ apiRules:
|
||||
- exclude:
|
||||
uidRegex: ^Stylet
|
||||
type: Type
|
||||
- exclude:
|
||||
uidRegex: ^Artemis\.Core\.CorePropertyChanged
|
||||
type: Type
|
||||
- exclude:
|
||||
uidRegex: ^Artemis\.Core\.Properties
|
||||
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
|
||||
href: api/
|
||||
homepage: api/index.md
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Artemis.Core.JsonConverters;
|
||||
@ -90,6 +91,11 @@ public static class Constants
|
||||
/// </summary>
|
||||
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 EffectPlaceholderPlugin EffectPlaceholderPlugin = new() {Plugin = CorePlugin, Profiler = CorePlugin.GetProfiler("Feature - Effect Placeholder")};
|
||||
|
||||
|
||||
@ -58,7 +58,6 @@ internal class CoreService : ICoreService
|
||||
_scriptingService = scriptingService;
|
||||
_loggingLevel = settingsService.GetSetting("Core.LoggingLevel", LogEventLevel.Debug);
|
||||
_frameStopWatch = new Stopwatch();
|
||||
StartupArguments = new List<string>();
|
||||
|
||||
_rgbService.Surface.Updating += SurfaceOnUpdating;
|
||||
_loggingLevel.SettingChanged += (sender, args) => ApplyLoggingLevel();
|
||||
@ -78,7 +77,7 @@ internal class CoreService : ICoreService
|
||||
|
||||
private void ApplyLoggingLevel()
|
||||
{
|
||||
string? argument = StartupArguments.FirstOrDefault(a => a.StartsWith("--logging"));
|
||||
string? argument = Constants.StartupArguments.FirstOrDefault(a => a.StartsWith("--logging"));
|
||||
if (argument != null)
|
||||
{
|
||||
// Parse the provided log level
|
||||
@ -194,7 +193,6 @@ internal class CoreService : ICoreService
|
||||
public int FrameRate { get; private set; }
|
||||
public TimeSpan FrameTime { get; private set; }
|
||||
public bool ProfileRenderingDisabled { get; set; }
|
||||
public List<string> StartupArguments { get; set; }
|
||||
public bool IsElevated { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
@ -217,7 +215,7 @@ internal class CoreService : ICoreService
|
||||
Constants.BuildInfo.BuildNumber,
|
||||
Constants.BuildInfo.SourceBranch
|
||||
);
|
||||
_logger.Information("Startup arguments: {args}", StartupArguments);
|
||||
_logger.Information("Startup arguments: {args}", Constants.StartupArguments);
|
||||
_logger.Information("Elevated permissions: {perms}", IsElevated);
|
||||
_logger.Information("Stopwatch high resolution: {perms}", Stopwatch.IsHighResolution);
|
||||
|
||||
@ -230,9 +228,9 @@ internal class CoreService : ICoreService
|
||||
|
||||
// Initialize the services
|
||||
_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);
|
||||
OnInitialized();
|
||||
}
|
||||
|
||||
@ -28,11 +28,6 @@ public interface ICoreService : IArtemisService, IDisposable
|
||||
/// </summary>
|
||||
bool ProfileRenderingDisabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a list of startup arguments
|
||||
/// </summary>
|
||||
List<string> StartupArguments { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether Artemis is running in an elevated environment (admin permissions)
|
||||
/// </summary>
|
||||
|
||||
@ -26,7 +26,7 @@ public interface IPluginManagementService : IArtemisService, IDisposable
|
||||
/// <summary>
|
||||
/// Loads all installed plugins. If plugins already loaded this will reload them all
|
||||
/// </summary>
|
||||
void LoadPlugins(List<string> startupArguments, bool isElevated);
|
||||
void LoadPlugins(bool isElevated);
|
||||
|
||||
/// <summary>
|
||||
/// Unloads all installed plugins.
|
||||
|
||||
@ -203,17 +203,17 @@ internal class PluginManagementService : IPluginManagementService
|
||||
|
||||
#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");
|
||||
return;
|
||||
}
|
||||
|
||||
bool ignorePluginLock = startupArguments.Contains("--ignore-plugin-lock");
|
||||
bool stayElevated = startupArguments.Contains("--force-elevation");
|
||||
bool droppedAdmin = startupArguments.Contains("--dropped-admin");
|
||||
bool ignorePluginLock = Constants.StartupArguments.Contains("--ignore-plugin-lock");
|
||||
bool stayElevated = Constants.StartupArguments.Contains("--force-elevation");
|
||||
bool droppedAdmin = Constants.StartupArguments.Contains("--dropped-admin");
|
||||
if (LoadingPlugins)
|
||||
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.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core.Modules;
|
||||
using EmbedIO;
|
||||
@ -17,15 +18,19 @@ internal class WebServerService : IWebServerService, IDisposable
|
||||
private readonly List<WebApiControllerRegistration> _controllers;
|
||||
private readonly ILogger _logger;
|
||||
private readonly List<WebModuleRegistration> _modules;
|
||||
private readonly PluginSetting<bool> _webServerEnabledSetting;
|
||||
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;
|
||||
_controllers = new List<WebApiControllerRegistration>();
|
||||
_modules = new List<WebModuleRegistration>();
|
||||
|
||||
_webServerEnabledSetting = settingsService.GetSetting("WebServer.Enabled", true);
|
||||
_webServerPortSetting = settingsService.GetSetting("WebServer.Port", 9696);
|
||||
_webServerEnabledSetting.SettingChanged += WebServerEnabledSettingOnSettingChanged;
|
||||
_webServerPortSetting.SettingChanged += WebServerPortSettingOnSettingChanged;
|
||||
pluginManagementService.PluginFeatureDisabled += PluginManagementServiceOnPluginFeatureDisabled;
|
||||
|
||||
@ -33,9 +38,9 @@ internal class WebServerService : IWebServerService, IDisposable
|
||||
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)
|
||||
@ -72,14 +77,23 @@ internal class WebServerService : IWebServerService, IDisposable
|
||||
public WebServer? Server { get; private set; }
|
||||
public PluginsModule PluginsModule { get; }
|
||||
|
||||
public event EventHandler? WebServerStarting;
|
||||
|
||||
#region Web server managament
|
||||
|
||||
private WebServer CreateWebServer()
|
||||
{
|
||||
Server?.Dispose();
|
||||
if (Server != null)
|
||||
{
|
||||
if (_cts != null)
|
||||
{
|
||||
_cts.Cancel();
|
||||
_cts = null;
|
||||
}
|
||||
|
||||
Server.Dispose();
|
||||
OnWebServerStopped();
|
||||
Server = null;
|
||||
}
|
||||
|
||||
WebApiModule apiModule = new("/", JsonNetSerializer);
|
||||
PluginsModule.ServerUrl = $"http://localhost:{_webServerPortSetting.Value}/";
|
||||
@ -112,8 +126,20 @@ internal class WebServerService : IWebServerService, IDisposable
|
||||
private void StartWebServer()
|
||||
{
|
||||
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();
|
||||
Server.Start();
|
||||
_cts = new CancellationTokenSource();
|
||||
Server.Start(_cts.Token);
|
||||
OnWebServerStarted();
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -276,4 +302,27 @@ internal class WebServerService : IWebServerService, IDisposable
|
||||
}
|
||||
|
||||
#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
|
||||
}
|
||||
@ -160,6 +160,11 @@ public readonly struct Numeric : IComparable<Numeric>, IConvertible
|
||||
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)
|
||||
{
|
||||
return (long) p._value;
|
||||
|
||||
@ -94,18 +94,11 @@ public sealed class InputPin : Pin
|
||||
/// <param name="type">The new type of the pin.</param>
|
||||
public void ChangeType(Type type)
|
||||
{
|
||||
if (_type == type)
|
||||
if (type == _type)
|
||||
return;
|
||||
|
||||
// Disconnect pins incompatible with the new 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));
|
||||
base.ChangeType(type, ref _type);
|
||||
Value = type.GetDefault();
|
||||
IsNumeric = type == typeof(Numeric);
|
||||
}
|
||||
|
||||
private void Evaluate()
|
||||
@ -117,10 +110,10 @@ public sealed class InputPin : Pin
|
||||
else
|
||||
Value = Type.GetDefault()!;
|
||||
}
|
||||
else
|
||||
{
|
||||
else if (ConnectedTo.Count > 0)
|
||||
Value = ConnectedTo[0].PinValue;
|
||||
}
|
||||
else
|
||||
Value = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@ -85,6 +85,7 @@ public interface IPin
|
||||
/// Determines whether this pin is compatible with the given type
|
||||
/// </summary>
|
||||
/// <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>
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
int targetPinCollectionId = -1;
|
||||
|
||||
@ -84,15 +84,11 @@ public sealed class OutputPin : Pin
|
||||
/// <param name="type">The new type of the pin.</param>
|
||||
public void ChangeType(Type type)
|
||||
{
|
||||
// Disconnect pins incompatible with the new type
|
||||
List<IPin> toDisconnect = ConnectedTo.Where(p => !p.IsTypeCompatible(type)).ToList();
|
||||
foreach (IPin pin in toDisconnect)
|
||||
DisconnectFrom(pin);
|
||||
if (type == _type)
|
||||
return;
|
||||
|
||||
// Change the type
|
||||
SetAndNotify(ref _type, type, nameof(Type));
|
||||
base.ChangeType(type, ref _type);
|
||||
Value = type.GetDefault();
|
||||
IsNumeric = type == typeof(Numeric);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@ -127,13 +127,46 @@ public abstract class Pin : CorePropertyChanged, IPin
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsTypeCompatible(Type type)
|
||||
public bool IsTypeCompatible(Type type, bool forgivingEnumMatching = true)
|
||||
{
|
||||
return Type == type
|
||||
|| (Type == typeof(Enum) && type.IsEnum)
|
||||
|| (Type.IsEnum && type == typeof(Enum))
|
||||
|| (Direction == PinDirection.Input && Type == typeof(object))
|
||||
|| (Direction == PinDirection.Output && type == typeof(object));
|
||||
|| (Direction == PinDirection.Input && type.IsAssignableTo(Type))
|
||||
|| (Direction == PinDirection.Output && type.IsAssignableFrom(Type))
|
||||
|| (Direction == PinDirection.Input && Type == typeof(Enum) && type.IsEnum && forgivingEnumMatching)
|
||||
|| (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
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Artemis.Core;
|
||||
|
||||
namespace Artemis.UI.Shared.Services.NodeEditor;
|
||||
@ -30,20 +31,18 @@ public class NodeConnectionStore
|
||||
public void Store()
|
||||
{
|
||||
_pinConnections.Clear();
|
||||
foreach (IPin nodePin in Node.Pins)
|
||||
{
|
||||
_pinConnections.Add(nodePin, new List<IPin>(nodePin.ConnectedTo));
|
||||
nodePin.DisconnectAll();
|
||||
}
|
||||
|
||||
foreach (IPinCollection nodePinCollection in Node.PinCollections)
|
||||
{
|
||||
foreach (IPin nodePin in nodePinCollection)
|
||||
{
|
||||
// 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));
|
||||
|
||||
// Iterate to disconnect
|
||||
foreach (IPin nodePin in Node.Pins.ToList())
|
||||
nodePin.DisconnectAll();
|
||||
foreach (IPin nodePin in Node.PinCollections.ToList().SelectMany(nodePinCollection => nodePinCollection))
|
||||
nodePin.DisconnectAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -51,23 +50,10 @@ public class NodeConnectionStore
|
||||
/// </summary>
|
||||
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)
|
||||
nodePin.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);
|
||||
}
|
||||
pin.ConnectTo(connection);
|
||||
}
|
||||
|
||||
_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.UI.Windows.Ninject;
|
||||
using Artemis.UI.Windows.Providers.Input;
|
||||
@ -13,14 +21,23 @@ namespace Artemis.UI.Windows;
|
||||
|
||||
public class App : Application
|
||||
{
|
||||
private StandardKernel? _kernel;
|
||||
private bool _shutDown;
|
||||
|
||||
// ReSharper disable NotAccessedField.Local
|
||||
private ApplicationStateManager? _applicationStateManager;
|
||||
private Mutex? _artemisMutex;
|
||||
// ReSharper restore NotAccessedField.Local
|
||||
|
||||
private StandardKernel? _kernel;
|
||||
|
||||
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());
|
||||
Program.CreateLogger(_kernel);
|
||||
RxApp.MainThreadScheduler = AvaloniaScheduler.Instance;
|
||||
@ -29,7 +46,7 @@ public class App : Application
|
||||
|
||||
public override void OnFrameworkInitializationCompleted()
|
||||
{
|
||||
if (ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop || Design.IsDesignMode)
|
||||
if (ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop || Design.IsDesignMode || _shutDown)
|
||||
return;
|
||||
|
||||
ArtemisBootstrapper.Initialize();
|
||||
@ -42,4 +59,55 @@ public class App : Application
|
||||
IInputService inputService = standardKernel.Get<IInputService>();
|
||||
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.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Security.Principal;
|
||||
using System.Threading;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Artemis.UI.Windows.Utilities;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
@ -19,14 +16,8 @@ namespace Artemis.UI.Windows;
|
||||
|
||||
public class ApplicationStateManager
|
||||
{
|
||||
private readonly IWindowService _windowService;
|
||||
|
||||
// ReSharper disable once NotAccessedField.Local - Kept in scope to ensure it does not get released
|
||||
private Mutex? _artemisMutex;
|
||||
|
||||
public ApplicationStateManager(IKernel kernel, string[] startupArguments)
|
||||
{
|
||||
_windowService = kernel.Get<IWindowService>();
|
||||
StartupArguments = startupArguments;
|
||||
IsElevated = new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);
|
||||
|
||||
@ -51,72 +42,6 @@ public class ApplicationStateManager
|
||||
public string[] StartupArguments { 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)
|
||||
{
|
||||
List<string> argsList = new();
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reactive;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Ninject;
|
||||
@ -52,6 +55,8 @@ public static class ArtemisBootstrapper
|
||||
if (_application.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop)
|
||||
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
|
||||
desktop.ShutdownMode = ShutdownMode.OnExplicitShutdown;
|
||||
// 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)
|
||||
{
|
||||
ViewModel.Focused();
|
||||
ViewModel?.Focused();
|
||||
}
|
||||
|
||||
private void OnDeactivated(object? sender, EventArgs e)
|
||||
{
|
||||
ViewModel.Unfocused();
|
||||
ViewModel?.Unfocused();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
|
||||
@ -55,7 +55,6 @@ public class RootViewModel : ActivatableViewModelBase, IScreen, IMainWindowProvi
|
||||
_sidebarVmFactory = sidebarVmFactory;
|
||||
_lifeTime = (IClassicDesktopStyleApplicationLifetime) Application.Current!.ApplicationLifetime!;
|
||||
|
||||
coreService.StartupArguments = _lifeTime.Args.ToList();
|
||||
mainWindowService.ConfigureMainWindowProvider(this);
|
||||
|
||||
DisplayAccordingToSettings();
|
||||
@ -99,8 +98,8 @@ public class RootViewModel : ActivatableViewModelBase, IScreen, IMainWindowProvi
|
||||
|
||||
private void DisplayAccordingToSettings()
|
||||
{
|
||||
bool autoRunning = _coreService.StartupArguments.Contains("--autorun");
|
||||
bool minimized = _coreService.StartupArguments.Contains("--minimized");
|
||||
bool autoRunning = Constants.StartupArguments.Contains("--autorun");
|
||||
bool minimized = Constants.StartupArguments.Contains("--minimized");
|
||||
bool showOnAutoRun = _settingsService.GetSetting("UI.ShowOnStartup", true).Value;
|
||||
|
||||
if ((autoRunning && !showOnAutoRun) || minimized)
|
||||
|
||||
@ -25,7 +25,8 @@
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock>Auto-run on startup</TextBlock>
|
||||
</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"
|
||||
OffContent="No" />
|
||||
</Grid>
|
||||
<Separator Classes="card-separator" />
|
||||
|
||||
@ -34,7 +35,8 @@
|
||||
<TextBlock>Hide window on auto-run</TextBlock>
|
||||
</StackPanel>
|
||||
<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"
|
||||
OffContent="No" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Classes="card-separator" />
|
||||
@ -52,7 +54,7 @@
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" Orientation="Horizontal">
|
||||
<controls:NumberBox IsEnabled="{CompiledBinding UIAutoRun.Value}" Width="120">
|
||||
<Interaction.Behaviors>
|
||||
<behaviors:LostFocusNumberBoxBindingBehavior Value="{CompiledBinding UIAutoRunDelay.Value}"/>
|
||||
<behaviors:LostFocusNumberBoxBindingBehavior Value="{CompiledBinding UIAutoRunDelay.Value}" />
|
||||
</Interaction.Behaviors>
|
||||
</controls:NumberBox>
|
||||
<TextBlock VerticalAlignment="Center" TextAlignment="Right" Width="30">sec</TextBlock>
|
||||
@ -100,9 +102,7 @@
|
||||
<StackPanel>
|
||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock>
|
||||
Web server port
|
||||
</TextBlock>
|
||||
<TextBlock>Enable web server</TextBlock>
|
||||
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
||||
Artemis runs a local web server that can be used to externally interact with the application.
|
||||
</TextBlock>
|
||||
@ -110,10 +110,25 @@
|
||||
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">
|
||||
<ToggleSwitch IsChecked="{CompiledBinding WebServerEnabled.Value}" OnContent="Yes" OffContent="No" MinWidth="0" Margin="0 -10" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Classes="card-separator" />
|
||||
|
||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock>
|
||||
Web server port
|
||||
</TextBlock>
|
||||
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
||||
If the webserver does not work you can try changing the port to one that is available.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<controls:NumberBox Width="150">
|
||||
<Interaction.Behaviors>
|
||||
<behaviors:LostFocusNumberBoxBindingBehavior Value="{CompiledBinding WebServerPort.Value}"/>
|
||||
<behaviors:LostFocusNumberBoxBindingBehavior Value="{CompiledBinding WebServerPort.Value}" />
|
||||
</Interaction.Behaviors>
|
||||
</controls:NumberBox>
|
||||
</StackPanel>
|
||||
@ -138,7 +153,7 @@
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<ToggleSwitch IsChecked="{CompiledBinding UICheckForUpdates.Value}" MinWidth="0" />
|
||||
<ToggleSwitch IsChecked="{CompiledBinding UICheckForUpdates.Value}" MinWidth="0" OnContent="Yes" OffContent="No" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Classes="card-separator" />
|
||||
@ -153,7 +168,7 @@
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<ToggleSwitch IsEnabled="{CompiledBinding UICheckForUpdates.Value}" IsChecked="{CompiledBinding UIAutoUpdate.Value}" MinWidth="0" />
|
||||
<ToggleSwitch IsEnabled="{CompiledBinding UICheckForUpdates.Value}" IsChecked="{CompiledBinding UIAutoUpdate.Value}" MinWidth="0" OnContent="Yes" OffContent="No" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Classes="card-separator" />
|
||||
@ -191,7 +206,7 @@
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<ToggleSwitch IsChecked="{CompiledBinding ProfileEditorShowDataModelValues.Value}" MinWidth="0" />
|
||||
<ToggleSwitch IsChecked="{CompiledBinding ProfileEditorShowDataModelValues.Value}" MinWidth="0" OnContent="Yes" OffContent="No" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Classes="card-separator" />
|
||||
@ -212,7 +227,7 @@
|
||||
<Setter.Value>
|
||||
<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"/>
|
||||
<avalonia:MaterialIcon Kind="{CompiledBinding Icon}" Height="20" Width="20" VerticalAlignment="Center" Margin="0 0 5 0" />
|
||||
<TextBlock Text="{CompiledBinding DisplayName}" VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
@ -264,7 +279,7 @@
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<ComboBox Width="150"
|
||||
SelectedItem="{CompiledBinding CorePreferredGraphicsContext.Value}"
|
||||
Items="{CompiledBinding GraphicsContexts}"/>
|
||||
Items="{CompiledBinding GraphicsContexts}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Classes="card-separator" />
|
||||
|
||||
@ -149,6 +149,7 @@ public class GeneralTabViewModel : ActivatableViewModelBase
|
||||
public PluginSetting<string> CorePreferredGraphicsContext => _settingsService.GetSetting("Core.PreferredGraphicsContext", "Software");
|
||||
public PluginSetting<double> CoreRenderScale => _settingsService.GetSetting("Core.RenderScale", 0.25);
|
||||
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);
|
||||
|
||||
private void ExecuteShowLogs()
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
xmlns:skiaSharp="clr-namespace:SkiaSharp;assembly=SkiaSharp"
|
||||
xmlns:shared="clr-namespace:Artemis.UI.Shared.Converters;assembly=Artemis.UI.Shared"
|
||||
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"
|
||||
x:Class="Artemis.UI.Screens.VisualScripting.CableView"
|
||||
x:DataType="visualScripting:CableViewModel"
|
||||
@ -79,6 +80,9 @@
|
||||
<DataTemplate DataType="core:Numeric">
|
||||
<TextBlock Text="{Binding}" FontFamily="Consolas"/>
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="collections:IList">
|
||||
<TextBlock Text="{Binding Count, StringFormat='List - {0} item(s)'}" FontFamily="Consolas"/>
|
||||
</DataTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding}" FontFamily="Consolas"/>
|
||||
</DataTemplate>
|
||||
|
||||
@ -38,7 +38,8 @@
|
||||
Grid.Row="1"
|
||||
Items="{CompiledBinding Categories}"
|
||||
IsVisible="{CompiledBinding Categories.Count}"
|
||||
SelectedItem="{CompiledBinding SelectedNode}">
|
||||
SelectedItem="{CompiledBinding SelectedNode}"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
|
||||
<TreeView.Styles>
|
||||
<Style Selector="TreeViewItem">
|
||||
<Setter Property="IsExpanded" Value="True" />
|
||||
@ -47,8 +48,8 @@
|
||||
<TreeView.DataTemplates>
|
||||
<TreeDataTemplate DataType="{x:Type core:NodeData}">
|
||||
<StackPanel Margin="-15 1 0 1" Background="Transparent" PointerReleased="InputElement_OnPointerReleased">
|
||||
<TextBlock Classes="BodyStrongTextBlockStyle" Text="{Binding Name}"></TextBlock>
|
||||
<TextBlock Foreground="{DynamicResource TextFillColorSecondary}" Text="{Binding Description}"></TextBlock>
|
||||
<TextBlock Classes="BodyStrongTextBlockStyle" Text="{Binding Name}" TextWrapping="Wrap"></TextBlock>
|
||||
<TextBlock Foreground="{DynamicResource TextFillColorSecondary}" Text="{Binding Description}" TextWrapping="Wrap"></TextBlock>
|
||||
</StackPanel>
|
||||
</TreeDataTemplate>
|
||||
<TreeDataTemplate ItemsSource="{Binding Items}">
|
||||
|
||||
@ -10,6 +10,7 @@ using Artemis.UI.Shared.Services.NodeEditor;
|
||||
using Artemis.UI.Shared.Services.NodeEditor.Commands;
|
||||
using Avalonia;
|
||||
using DynamicData;
|
||||
using DynamicData.Binding;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.VisualScripting;
|
||||
@ -35,6 +36,11 @@ public class NodePickerViewModel : ActivatableViewModelBase
|
||||
|
||||
nodeSourceList.Connect()
|
||||
.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)
|
||||
.Bind(out ReadOnlyObservableCollection<DynamicData.List.IGrouping<NodeData, string>> categories)
|
||||
.Subscribe();
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Controllers;
|
||||
@ -106,6 +107,9 @@ public class RegistrationService : IRegistrationService
|
||||
_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))
|
||||
{
|
||||
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;
|
||||
|
||||
[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>
|
||||
{
|
||||
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:core="clr-namespace:Artemis.Core;assembly=Artemis.Core"
|
||||
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"
|
||||
x:Class="Artemis.VisualScripting.Nodes.Static.Screens.DisplayValueNodeCustomView"
|
||||
x:DataType="screens:DisplayValueNodeCustomViewModel">
|
||||
@ -43,6 +44,9 @@
|
||||
<DataTemplate DataType="core:Numeric">
|
||||
<TextBlock Text="{Binding}" FontFamily="Consolas"/>
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="collections:IList">
|
||||
<TextBlock Text="{Binding Count, StringFormat='List - {0} item(s)'}" FontFamily="Consolas"/>
|
||||
</DataTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding}" FontFamily="Consolas"/>
|
||||
</DataTemplate>
|
||||
|
||||
@ -3,13 +3,13 @@ using Artemis.VisualScripting.Nodes.Static.Screens;
|
||||
|
||||
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>
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
public StaticStringValueNode()
|
||||
: base("String", "Outputs a configurable string value.")
|
||||
: base("Text", "Outputs a configurable text value.")
|
||||
{
|
||||
Output = CreateOutputPin<string>();
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
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
|
||||
{
|
||||
#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