mirror of
http://git.xinwangdao.com/cnnc-embedded-parts-detect/detect.git
synced 2025-06-24 13:34:13 +08:00
Initial commit
This commit is contained in:
commit
76e2d8e072
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
################################################################################
|
||||||
|
# 此 .gitignore 文件已由 Microsoft(R) Visual Studio 自动创建。
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
bin
|
||||||
|
obj
|
||||||
|
/packages
|
13
.idea/.idea.detect/.idea/.gitignore
generated
vendored
Normal file
13
.idea/.idea.detect/.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# 默认忽略的文件
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Rider 忽略的文件
|
||||||
|
/modules.xml
|
||||||
|
/.idea.detect.iml
|
||||||
|
/projectSettingsUpdater.xml
|
||||||
|
/contentModel.xml
|
||||||
|
# 基于编辑器的 HTTP 客户端请求
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
23
.idea/.idea.detect/.idea/avalonia.xml
generated
Normal file
23
.idea/.idea.detect/.idea/avalonia.xml
generated
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="AvaloniaProject">
|
||||||
|
<option name="projectPerEditor">
|
||||||
|
<map>
|
||||||
|
<entry key="detect.gui/App.axaml" value="detect.gui/detect.gui.csproj" />
|
||||||
|
<entry key="detect.gui/Assets/DefaultResources.axaml" value="detect.gui/detect.gui.csproj" />
|
||||||
|
<entry key="detect.gui/Controls/Arrow.axaml" value="detect.gui/detect.gui.csproj" />
|
||||||
|
<entry key="detect.gui/Controls/PageControl.axaml" value="detect.gui/detect.gui.csproj" />
|
||||||
|
<entry key="detect.gui/Views/Camera/VideoFullWindow.axaml" value="detect.gui/detect.gui.csproj" />
|
||||||
|
<entry key="detect.gui/Views/DetectTaskView.axaml" value="detect.gui/detect.gui.csproj" />
|
||||||
|
<entry key="detect.gui/Views/DeviceView.axaml" value="detect.gui/detect.gui.csproj" />
|
||||||
|
<entry key="detect.gui/Views/LogView.axaml" value="detect.gui/detect.gui.csproj" />
|
||||||
|
<entry key="detect.gui/Views/MainView.axaml" value="detect.gui/detect.gui.csproj" />
|
||||||
|
<entry key="detect.gui/Views/MainWindow.axaml" value="detect.gui/detect.gui.csproj" />
|
||||||
|
<entry key="detect.gui/Views/Settings/WebBrowserWindow.axaml" value="detect.gui/detect.gui.csproj" />
|
||||||
|
<entry key="detect.gui/Views/StatisticsView.axaml" value="detect.gui/detect.gui.csproj" />
|
||||||
|
<entry key="detect.gui/Views/UserView.axaml" value="detect.gui/detect.gui.csproj" />
|
||||||
|
<entry key="detect.gui/Views/WebBrowserWindow.axaml" value="detect.gui/detect.gui.csproj" />
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
4
.idea/.idea.detect/.idea/encodings.xml
generated
Normal file
4
.idea/.idea.detect/.idea/encodings.xml
generated
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
|
||||||
|
</project>
|
8
.idea/.idea.detect/.idea/indexLayout.xml
generated
Normal file
8
.idea/.idea.detect/.idea/indexLayout.xml
generated
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="UserContentModel">
|
||||||
|
<attachedFolders />
|
||||||
|
<explicitIncludes />
|
||||||
|
<explicitExcludes />
|
||||||
|
</component>
|
||||||
|
</project>
|
4
.idea/.idea.detect/.idea/vcs.xml
generated
Normal file
4
.idea/.idea.detect/.idea/vcs.xml
generated
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings" defaultProject="true" />
|
||||||
|
</project>
|
99
WebViewControl.Avalonia/AssemblyCache.cs
Normal file
99
WebViewControl.Avalonia/AssemblyCache.cs
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
internal class AssemblyCache {
|
||||||
|
|
||||||
|
private object SyncRoot { get; } = new object();
|
||||||
|
|
||||||
|
// We now allow to load multiple versions of the same assembly, which means that resource urls can
|
||||||
|
// (optionally) specify the version. We don't force the version to be specified to maintain backwards
|
||||||
|
// compatibility, and thus for each assembly name we two entries in the dictionary: with and without a version.
|
||||||
|
// Note that no guarantee is provided about which version is resolved if there are multiple loaded assemblies
|
||||||
|
// with the same name and no specific version is provided.
|
||||||
|
// This, consumer apps are encouraged to include the version in the resource url
|
||||||
|
private IDictionary<(string AssemblyName, Version AssemblyVersion), Assembly> assemblies;
|
||||||
|
|
||||||
|
private bool newAssembliesLoaded = true;
|
||||||
|
|
||||||
|
internal Assembly ResolveResourceAssembly(Uri resourceUrl, bool failOnMissingAssembly) {
|
||||||
|
if (assemblies == null) {
|
||||||
|
lock (SyncRoot) {
|
||||||
|
if (assemblies == null) {
|
||||||
|
assemblies = new Dictionary<(string, Version), Assembly>();
|
||||||
|
AppDomain.CurrentDomain.AssemblyLoad += delegate { newAssembliesLoaded = true; };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (assemblyName, assemblyVersion) = ResourceUrl.GetEmbeddedResourceAssemblyNameAndVersion(resourceUrl);
|
||||||
|
var assembly = GetAssemblyByNameAndVersion(assemblyName, assemblyVersion);
|
||||||
|
|
||||||
|
if (assembly == null) {
|
||||||
|
if (newAssembliesLoaded) {
|
||||||
|
lock (SyncRoot) {
|
||||||
|
if (newAssembliesLoaded) {
|
||||||
|
// add loaded assemblies to cache
|
||||||
|
newAssembliesLoaded = false;
|
||||||
|
foreach (var domainAssembly in AppDomain.CurrentDomain.GetAssemblies()) {
|
||||||
|
AddOrReplace(domainAssembly);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assembly = GetAssemblyByNameAndVersion(assemblyName, assemblyVersion);
|
||||||
|
if (assembly == null) {
|
||||||
|
try {
|
||||||
|
// try loading the assembly from a file named AssemblyName.dll (or AssemblyName-AssemblyVersion.dll if
|
||||||
|
// a version was provided)
|
||||||
|
var fileName = $"{assemblyName}{(assemblyVersion == null ? "" : $"-{assemblyVersion}")}.dll";
|
||||||
|
var assemblyPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName);
|
||||||
|
assembly = AssemblyLoader.LoadAssembly(assemblyPath);
|
||||||
|
} catch (IOException) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
if (assembly != null) {
|
||||||
|
lock (SyncRoot) {
|
||||||
|
AddOrReplace(assembly);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (failOnMissingAssembly && assembly == null) {
|
||||||
|
throw new InvalidOperationException("Could not find assembly for: " + resourceUrl);
|
||||||
|
}
|
||||||
|
return assembly;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddOrReplace(Assembly assembly) {
|
||||||
|
var identity = assembly.GetName();
|
||||||
|
var assemblyName = identity.Name;
|
||||||
|
if (assemblyName == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add two entries, with and without the version.
|
||||||
|
// for the null-version entry, keep the assembly with the highest version
|
||||||
|
var version = identity.Version;
|
||||||
|
if (!assemblies.TryGetValue((assemblyName, null), out var nullVersionAssembly) ||
|
||||||
|
(nullVersionAssembly.GetName().Version is { } previousVersion && previousVersion < version)) {
|
||||||
|
assemblies[(assemblyName, null)] = assembly;
|
||||||
|
}
|
||||||
|
assemblies[(assemblyName, version)] = assembly;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Assembly GetAssemblyByNameAndVersion(string assemblyName, Version assemblyVersion) {
|
||||||
|
lock (SyncRoot) {
|
||||||
|
assemblies.TryGetValue((assemblyName, assemblyVersion), out var assembly);
|
||||||
|
return assembly;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
WebViewControl.Avalonia/AssemblyLoader.NETCore.cs
Normal file
11
WebViewControl.Avalonia/AssemblyLoader.NETCore.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.Loader;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
internal static class AssemblyLoader {
|
||||||
|
|
||||||
|
internal static Assembly LoadAssembly(string path) => AssemblyLoadContext.Default.LoadFromAssemblyPath(path);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
89
WebViewControl.Avalonia/AsyncResourceHandler.cs
Normal file
89
WebViewControl.Avalonia/AsyncResourceHandler.cs
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using Xilium.CefGlue;
|
||||||
|
using Xilium.CefGlue.Common.Handlers;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
internal class AsyncResourceHandler : DefaultResourceHandler {
|
||||||
|
|
||||||
|
private CefCallback responseCallback;
|
||||||
|
private bool autoDisposeStream;
|
||||||
|
private object SyncRoot { get; } = new object();
|
||||||
|
|
||||||
|
protected override RequestHandlingFashion ProcessRequestAsync(CefRequest request, CefCallback callback) {
|
||||||
|
lock (SyncRoot) {
|
||||||
|
if (Response == null && string.IsNullOrEmpty(RedirectUrl)) {
|
||||||
|
responseCallback = callback;
|
||||||
|
return RequestHandlingFashion.ContinueAsync;
|
||||||
|
}
|
||||||
|
return RequestHandlingFashion.Continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetResponse(string response, string mimeType = null) {
|
||||||
|
var responseStream = GetMemoryStream(response, Encoding.UTF8, includePreamble: true);
|
||||||
|
SetResponse(responseStream, mimeType, autoDisposeStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetResponse(Stream response, string mimeType = null, bool autoDisposeStream = false) {
|
||||||
|
lock (SyncRoot) {
|
||||||
|
Response = response;
|
||||||
|
MimeType = mimeType;
|
||||||
|
this.autoDisposeStream = autoDisposeStream;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RedirectTo(string targetUrl) {
|
||||||
|
lock (SyncRoot) {
|
||||||
|
RedirectUrl = targetUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Continue() {
|
||||||
|
lock (SyncRoot) {
|
||||||
|
if (responseCallback != null) {
|
||||||
|
using (responseCallback) {
|
||||||
|
responseCallback.Continue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DefaultResourceHandler FromText(string text) {
|
||||||
|
var handler = new AsyncResourceHandler();
|
||||||
|
handler.SetResponse(text);
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MemoryStream GetMemoryStream(string text, Encoding encoding, bool includePreamble = true) {
|
||||||
|
if (includePreamble) {
|
||||||
|
var preamble = encoding.GetPreamble();
|
||||||
|
var bytes = encoding.GetBytes(text);
|
||||||
|
|
||||||
|
var memoryStream = new MemoryStream(preamble.Length + bytes.Length);
|
||||||
|
|
||||||
|
memoryStream.Write(preamble, 0, preamble.Length);
|
||||||
|
memoryStream.Write(bytes, 0, bytes.Length);
|
||||||
|
|
||||||
|
memoryStream.Position = 0;
|
||||||
|
|
||||||
|
return memoryStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MemoryStream(encoding.GetBytes(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing) {
|
||||||
|
base.Dispose(disposing);
|
||||||
|
if (autoDisposeStream) {
|
||||||
|
var response = Response;
|
||||||
|
if (response != null) {
|
||||||
|
lock (SyncRoot) {
|
||||||
|
response.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
32
WebViewControl.Avalonia/BaseControl.cs
Normal file
32
WebViewControl.Avalonia/BaseControl.cs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
using System;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
|
using Avalonia.LogicalTree;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
public abstract class BaseControl : Control {
|
||||||
|
|
||||||
|
protected abstract void InternalDispose();
|
||||||
|
|
||||||
|
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) {
|
||||||
|
if (e.Root is Window window) {
|
||||||
|
// need to subscribe the event this way because close gets called after all elements get detached
|
||||||
|
window.AddHandler(Window.WindowClosedEvent, (EventHandler<RoutedEventArgs>)OnHostWindowClosed);
|
||||||
|
}
|
||||||
|
base.OnAttachedToLogicalTree(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e) {
|
||||||
|
if (e.Root is Window window) {
|
||||||
|
window.RemoveHandler(Window.WindowClosedEvent, (EventHandler<RoutedEventArgs>)OnHostWindowClosed);
|
||||||
|
}
|
||||||
|
base.OnDetachedFromLogicalTree(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnHostWindowClosed(object sender, RoutedEventArgs eventArgs) {
|
||||||
|
((Window)sender).RemoveHandler(Window.WindowClosedEvent, (EventHandler<RoutedEventArgs>)OnHostWindowClosed);
|
||||||
|
InternalDispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
WebViewControl.Avalonia/ChromiumBrowser.Avalonia.cs
Normal file
14
WebViewControl.Avalonia/ChromiumBrowser.Avalonia.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using Xilium.CefGlue.Avalonia;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
partial class ChromiumBrowser : AvaloniaCefBrowser {
|
||||||
|
|
||||||
|
public new void CreateBrowser(int width, int height) {
|
||||||
|
if (IsBrowserInitialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
base.CreateBrowser(width, height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
WebViewControl.Avalonia/ChromiumBrowser.cs
Normal file
9
WebViewControl.Avalonia/ChromiumBrowser.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
using Xilium.CefGlue;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
internal partial class ChromiumBrowser {
|
||||||
|
|
||||||
|
internal CefBrowser GetBrowser() => UnderlyingBrowser;
|
||||||
|
}
|
||||||
|
}
|
29
WebViewControl.Avalonia/EditCommands.cs
Normal file
29
WebViewControl.Avalonia/EditCommands.cs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
using Xilium.CefGlue;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
public class EditCommands {
|
||||||
|
|
||||||
|
private ChromiumBrowser ChromiumBrowser { get; }
|
||||||
|
|
||||||
|
internal EditCommands(ChromiumBrowser chromiumBrowser) {
|
||||||
|
ChromiumBrowser = chromiumBrowser;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CefFrame GetFocusedFrame() => ChromiumBrowser.GetBrowser()?.GetFocusedFrame() ?? ChromiumBrowser.GetBrowser()?.GetMainFrame();
|
||||||
|
|
||||||
|
public void Cut() => GetFocusedFrame()?.Cut();
|
||||||
|
|
||||||
|
public void Copy() => GetFocusedFrame()?.Copy();
|
||||||
|
|
||||||
|
public void Paste() => GetFocusedFrame()?.Paste();
|
||||||
|
|
||||||
|
public void SelectAll() => GetFocusedFrame()?.SelectAll();
|
||||||
|
|
||||||
|
public void Undo() => GetFocusedFrame()?.Undo();
|
||||||
|
|
||||||
|
public void Redo() => GetFocusedFrame()?.Redo();
|
||||||
|
|
||||||
|
public void Delete() => GetFocusedFrame()?.Delete();
|
||||||
|
}
|
||||||
|
}
|
92
WebViewControl.Avalonia/GlobalSettings.cs
Normal file
92
WebViewControl.Avalonia/GlobalSettings.cs
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using Xilium.CefGlue.Common;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
public class GlobalSettings {
|
||||||
|
|
||||||
|
private bool persistCache;
|
||||||
|
private bool enableErrorLogOnly;
|
||||||
|
private bool osrEnabled = true;
|
||||||
|
private string userAgent;
|
||||||
|
private string logFile;
|
||||||
|
private string cachePath = Path.Combine(Path.GetTempPath(), "WebView" + Guid.NewGuid().ToString().Replace("-", null) + DateTime.UtcNow.Ticks);
|
||||||
|
private readonly List<KeyValuePair<string, string>> commandLineSwitches = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Use this method to pass flags to the browser. List of available flags: https://peter.sh/experiments/chromium-command-line-switches/
|
||||||
|
/// </summary>
|
||||||
|
public void AddCommandLineSwitch(string key, string value) {
|
||||||
|
EnsureNotLoaded(nameof(AddCommandLineSwitch));
|
||||||
|
commandLineSwitches.Add(new KeyValuePair<string, string>(key, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<KeyValuePair<string, string>> CommandLineSwitches => commandLineSwitches;
|
||||||
|
|
||||||
|
public string CachePath {
|
||||||
|
get => cachePath;
|
||||||
|
set {
|
||||||
|
EnsureNotLoaded(nameof(CachePath));
|
||||||
|
cachePath = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool PersistCache {
|
||||||
|
get => persistCache;
|
||||||
|
set {
|
||||||
|
EnsureNotLoaded(nameof(PersistCache));
|
||||||
|
persistCache = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool EnableErrorLogOnly {
|
||||||
|
get => enableErrorLogOnly;
|
||||||
|
set {
|
||||||
|
EnsureNotLoaded(nameof(EnableErrorLogOnly));
|
||||||
|
enableErrorLogOnly = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string UserAgent {
|
||||||
|
get => userAgent;
|
||||||
|
set {
|
||||||
|
EnsureNotLoaded(nameof(UserAgent));
|
||||||
|
userAgent = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string LogFile {
|
||||||
|
get => logFile;
|
||||||
|
set {
|
||||||
|
EnsureNotLoaded(nameof(LogFile));
|
||||||
|
logFile = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OsrEnabled {
|
||||||
|
get => osrEnabled;
|
||||||
|
set {
|
||||||
|
EnsureNotLoaded(nameof(OsrEnabled));
|
||||||
|
osrEnabled = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureNotLoaded(string propertyName) {
|
||||||
|
if (CefRuntimeLoader.IsLoaded) {
|
||||||
|
throw new InvalidOperationException($"Cannot set {propertyName} after WebView engine has been loaded");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal int GetRemoteDebuggingPort() {
|
||||||
|
#if REMOTE_DEBUG_SUPPORT
|
||||||
|
var port = Environment.GetEnvironmentVariable("WEBVIEW_REMOTE_DEBUGGING_PORT");
|
||||||
|
int.TryParse(port != null ? port : "", out var result);
|
||||||
|
return result;
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
64
WebViewControl.Avalonia/HttpResourceHandler.cs
Normal file
64
WebViewControl.Avalonia/HttpResourceHandler.cs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xilium.CefGlue;
|
||||||
|
using Xilium.CefGlue.Common.Handlers;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
internal class HttpResourceHandler : DefaultResourceHandler {
|
||||||
|
|
||||||
|
private const string AccessControlAllowOriginHeaderKey = "Access-Control-Allow-Origin";
|
||||||
|
|
||||||
|
internal static readonly CefResourceType[] AcceptedResources = new CefResourceType[] {
|
||||||
|
// These resources types need an "Access-Control-Allow-Origin" header response entry
|
||||||
|
// to comply with CORS security restrictions.
|
||||||
|
CefResourceType.SubFrame,
|
||||||
|
CefResourceType.FontResource,
|
||||||
|
CefResourceType.Stylesheet
|
||||||
|
};
|
||||||
|
|
||||||
|
protected override RequestHandlingFashion ProcessRequestAsync(CefRequest request, CefCallback callback) {
|
||||||
|
Task.Run(async () => {
|
||||||
|
try {
|
||||||
|
var httpRequest = WebRequest.CreateHttp(request.Url);
|
||||||
|
var headers = request.GetHeaderMap();
|
||||||
|
foreach (var key in headers.AllKeys) {
|
||||||
|
httpRequest.Headers.Add(key, headers[key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
var response = (HttpWebResponse) await httpRequest.GetResponseAsync();
|
||||||
|
Response = response.GetResponseStream();
|
||||||
|
Headers = response.Headers;
|
||||||
|
|
||||||
|
MimeType = response.ContentType;
|
||||||
|
Status = (int) response.StatusCode;
|
||||||
|
StatusText = response.StatusDescription;
|
||||||
|
|
||||||
|
// we have to smash any existing value here
|
||||||
|
Headers.Remove(AccessControlAllowOriginHeaderKey);
|
||||||
|
Headers.Add(AccessControlAllowOriginHeaderKey, "*");
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
// we should catch exceptions.. network errors cannot crash the app
|
||||||
|
} finally {
|
||||||
|
callback.Continue();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
return RequestHandlingFashion.ContinueAsync;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool Read(Stream outResponse, int bytesToRead, out int bytesRead, CefResourceReadCallback callback) {
|
||||||
|
var buffer = new byte[bytesToRead];
|
||||||
|
bytesRead = Response?.Read(buffer, 0, buffer.Length) ?? 0;
|
||||||
|
|
||||||
|
if (bytesRead == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
outResponse.Write(buffer, 0, bytesRead);
|
||||||
|
return bytesRead > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
WebViewControl.Avalonia/HttpResourceRequestHandler.cs
Normal file
15
WebViewControl.Avalonia/HttpResourceRequestHandler.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using Xilium.CefGlue;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
internal class HttpResourceRequestHandler : CefResourceRequestHandler {
|
||||||
|
|
||||||
|
protected override CefCookieAccessFilter GetCookieAccessFilter(CefBrowser browser, CefFrame frame, CefRequest request) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override CefResourceHandler GetResourceHandler(CefBrowser browser, CefFrame frame, CefRequest request) {
|
||||||
|
return new HttpResourceHandler();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
61
WebViewControl.Avalonia/JavascriptSerializationHelper.cs
Normal file
61
WebViewControl.Avalonia/JavascriptSerializationHelper.cs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Web;
|
||||||
|
using JavascriptObject = System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, object>>;
|
||||||
|
using SerializationHandler = System.Func<object, string>;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
public static class JavascriptSerializer {
|
||||||
|
|
||||||
|
public static object Undefined { get; } = new object();
|
||||||
|
|
||||||
|
public static string Serialize(object o, SerializationHandler handleComplexType = null) {
|
||||||
|
if (o == null) return "null";
|
||||||
|
if (o == Undefined) return "undefined";
|
||||||
|
if (o is string str) return Serialize(str);
|
||||||
|
if (o is IEnumerable col) return Serialize(col);
|
||||||
|
if (o is JavascriptObject jso) return Serialize(jso);
|
||||||
|
|
||||||
|
var type = o.GetType();
|
||||||
|
|
||||||
|
if (type.IsPrimitive) return Convert.ToString(o, CultureInfo.InvariantCulture).ToLowerInvariant(); // ints, bools, ... but not structs
|
||||||
|
if (type.IsEnum) return ((int) o).ToString();
|
||||||
|
if (handleComplexType != null) return handleComplexType(o);
|
||||||
|
return SerializeComplexType(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string Serialize(JavascriptObject o, SerializationHandler handleComplexType = null) {
|
||||||
|
// order members to create a stable serialization
|
||||||
|
return "{" + string.Join(",", o.OrderBy(kvp => kvp.Key).Select(kvp => Serialize(kvp.Key) + ":" + Serialize(kvp.Value, handleComplexType))) + "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string Serialize(string str) {
|
||||||
|
return str == null ? "null" : HttpUtility.JavaScriptStringEncode(str, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string Serialize(bool boolean) {
|
||||||
|
return boolean.ToString().ToLowerInvariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string Serialize(IEnumerable arr, SerializationHandler handleComplexType = null) {
|
||||||
|
return "[" + string.Join(",", arr.Cast<object>().Select(i => Serialize(i, handleComplexType))) + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string SerializeComplexType(object obj) {
|
||||||
|
var fields = obj.GetType().GetProperties().Where(p => p.PropertyType.IsPrimitive || p.PropertyType == typeof(string));
|
||||||
|
return Serialize(fields.Select(f => new KeyValuePair<string, object>(f.Name, f.GetValue(obj, null))));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string GetJavascriptName(string str) {
|
||||||
|
if (string.IsNullOrEmpty(str)) {
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
return char.ToLowerInvariant(str[0]) + str.Substring(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5
WebViewControl.Avalonia/Properties/AssemblyInfo.cs
Normal file
5
WebViewControl.Avalonia/Properties/AssemblyInfo.cs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
[assembly: InternalsVisibleTo("Tests.ReactView")]
|
||||||
|
[assembly: InternalsVisibleTo("Tests.WebView")]
|
||||||
|
[assembly: InternalsVisibleTo("ReactViewControl.Avalonia")]
|
6
WebViewControl.Avalonia/ProxyAuthentication.cs
Normal file
6
WebViewControl.Avalonia/ProxyAuthentication.cs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
namespace WebViewControl {
|
||||||
|
public class ProxyAuthentication {
|
||||||
|
public string UserName { get; set; }
|
||||||
|
public string Password { get; set; }
|
||||||
|
}
|
||||||
|
}
|
25
WebViewControl.Avalonia/RenderProcessTerminatedException.cs
Normal file
25
WebViewControl.Avalonia/RenderProcessTerminatedException.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
public class RenderProcessCrashedException : Exception {
|
||||||
|
|
||||||
|
internal RenderProcessCrashedException(string message) : base(message) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RenderProcessKilledException : Exception {
|
||||||
|
|
||||||
|
public bool IsWebViewDisposing { get; }
|
||||||
|
|
||||||
|
internal RenderProcessKilledException(string message, bool webViewDisposing = false) : base(message) {
|
||||||
|
IsWebViewDisposing = webViewDisposing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RenderProcessOutOfMemoryException : Exception {
|
||||||
|
|
||||||
|
internal RenderProcessOutOfMemoryException(string message) : base(message) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
44
WebViewControl.Avalonia/Request.cs
Normal file
44
WebViewControl.Avalonia/Request.cs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
using System.Collections.Specialized;
|
||||||
|
using Xilium.CefGlue;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
public class Request {
|
||||||
|
|
||||||
|
private CefRequest CefRequest { get; }
|
||||||
|
private string UrlOverride { get; }
|
||||||
|
|
||||||
|
internal Request(CefRequest request, string urlOverride) {
|
||||||
|
CefRequest = request;
|
||||||
|
UrlOverride = urlOverride;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Method {
|
||||||
|
get { return CefRequest.Method; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Url {
|
||||||
|
get { return UrlOverride ?? CefRequest.Url; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Cancel() {
|
||||||
|
Canceled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Canceled { get; private set; }
|
||||||
|
|
||||||
|
internal bool IsMainFrame => CefRequest.ResourceType == CefResourceType.MainFrame;
|
||||||
|
|
||||||
|
public NameValueCollection GetHeaderMap() =>
|
||||||
|
CefRequest.GetHeaderMap();
|
||||||
|
|
||||||
|
public void SetHeaderMap(NameValueCollection headers) =>
|
||||||
|
CefRequest.SetHeaderMap(headers);
|
||||||
|
|
||||||
|
public string GetHeaderByName(string name) =>
|
||||||
|
CefRequest.GetHeaderByName(name);
|
||||||
|
|
||||||
|
public void SetHeaderByName(string name, string value, bool overwrite) =>
|
||||||
|
CefRequest.SetHeaderByName(name, value, overwrite);
|
||||||
|
}
|
||||||
|
}
|
15
WebViewControl.Avalonia/ResourceHandlerExtensions.cs
Normal file
15
WebViewControl.Avalonia/ResourceHandlerExtensions.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
internal static class ResourceHandlerExtensions {
|
||||||
|
|
||||||
|
public static void LoadEmbeddedResource(this ResourceHandler resourceHandler, Uri url) {
|
||||||
|
var stream = ResourcesManager.TryGetResource(url, true, out string extension);
|
||||||
|
|
||||||
|
if (stream != null) {
|
||||||
|
resourceHandler.RespondWith(stream, extension);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
WebViewControl.Avalonia/ResourceType.cs
Normal file
13
WebViewControl.Avalonia/ResourceType.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using Xilium.CefGlue;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
public enum ResourceType {
|
||||||
|
Stylesheet = CefResourceType.Stylesheet,
|
||||||
|
Script = CefResourceType.Script,
|
||||||
|
Image = CefResourceType.Image,
|
||||||
|
FontResource = CefResourceType.FontResource,
|
||||||
|
Xhr = CefResourceType.Xhr,
|
||||||
|
ServiceWorker = CefResourceType.ServiceWorker
|
||||||
|
}
|
||||||
|
}
|
109
WebViewControl.Avalonia/ResourceUrl.cs
Normal file
109
WebViewControl.Avalonia/ResourceUrl.cs
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
public partial class ResourceUrl {
|
||||||
|
|
||||||
|
public const string LocalScheme = "local";
|
||||||
|
public const string CustomScheme = "custom";
|
||||||
|
|
||||||
|
internal const string EmbeddedScheme = "embedded";
|
||||||
|
internal const string PathSeparator = "/";
|
||||||
|
|
||||||
|
private const string AssemblyPathSeparator = ";";
|
||||||
|
private const char AssemblyVersionSeparator = '-';
|
||||||
|
private const string AssemblyPrefix = "assembly:";
|
||||||
|
private const string DefaultDomain = "webview{0}";
|
||||||
|
|
||||||
|
private string Url { get; }
|
||||||
|
|
||||||
|
public ResourceUrl(params string[] path) {
|
||||||
|
Url = string.Join("/", path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourceUrl(Assembly assembly, params string[] path) : this(path) {
|
||||||
|
var identity = assembly.GetName();
|
||||||
|
var assemblyName = identity.Name;
|
||||||
|
var assemblyVersion = identity.Version is { } version ? $"{AssemblyVersionSeparator}{version}" : "";
|
||||||
|
|
||||||
|
if (Url.StartsWith(PathSeparator)) {
|
||||||
|
// only prefix with assembly if necessary, to avoid having the same resource loaded from multiple locations
|
||||||
|
Url = AssemblyPrefix + assemblyName + assemblyVersion + AssemblyPathSeparator + Url.Substring(1);
|
||||||
|
} else {
|
||||||
|
Url = assemblyName + assemblyVersion + PathSeparator + Url;
|
||||||
|
}
|
||||||
|
Url = BuildUrl(EmbeddedScheme, Url);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal ResourceUrl(string scheme, string path) {
|
||||||
|
Url = BuildUrl(scheme, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildUrl(string scheme, string path) {
|
||||||
|
return scheme + Uri.SchemeDelimiter + CombinePath(DefaultDomain, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string CombinePath(string path1, string path2) {
|
||||||
|
return path1 + (path1.EndsWith(PathSeparator) ? "" : PathSeparator) + (path2.StartsWith(PathSeparator) ? path2.Substring(1) : path2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString() {
|
||||||
|
return string.Format(Url, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool ContainsAssemblyLocation(Uri url) {
|
||||||
|
return url.Scheme == EmbeddedScheme && url.AbsolutePath.StartsWith(PathSeparator + AssemblyPrefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Supported syntax:
|
||||||
|
/// embedded://webview/assembly:AssemblyName;Path/To/Resource
|
||||||
|
/// embedded://webview/assembly:AssemblyName-AssemblyVersion;Path/To/Resource
|
||||||
|
/// embedded://webview/AssemblyName/Path/To/Resource (AssemblyName is also assumed as default namespace)
|
||||||
|
/// embedded://webview/AssemblyName-AssemblyVersion/Path/To/Resource
|
||||||
|
/// </summary>
|
||||||
|
internal static string[] GetEmbeddedResourcePath(Uri resourceUrl) {
|
||||||
|
if (ContainsAssemblyLocation(resourceUrl)) {
|
||||||
|
var indexOfPath = resourceUrl.AbsolutePath.IndexOf(AssemblyPathSeparator);
|
||||||
|
return resourceUrl.AbsolutePath.Substring(indexOfPath + 1).Split(new [] { PathSeparator }, StringSplitOptions.None);
|
||||||
|
}
|
||||||
|
var uriParts = resourceUrl.Segments.Select(p => p.Replace(PathSeparator, "")).ToArray();
|
||||||
|
var (assemblyName, _) = GetAssemblyNameAndVersion(uriParts[1]);
|
||||||
|
return uriParts.Skip(2).Prepend(assemblyName).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Supported syntax:
|
||||||
|
/// embedded://webview/assembly:AssemblyName;Path/To/Resource
|
||||||
|
/// embedded://webview/assembly:AssemblyName-AssemblyVersion;Path/To/Resource
|
||||||
|
/// embedded://webview/AssemblyName/Path/To/Resource (AssemblyName is also assumed as default namespace)
|
||||||
|
/// embedded://webview/AssemblyName-AssemblyVersion/Path/To/Resource
|
||||||
|
/// </summary>
|
||||||
|
public static (string, Version) GetEmbeddedResourceAssemblyNameAndVersion(Uri resourceUrl) {
|
||||||
|
if (ContainsAssemblyLocation(resourceUrl)) {
|
||||||
|
var resourcePath = resourceUrl.AbsolutePath.Substring((PathSeparator + AssemblyPrefix).Length);
|
||||||
|
var indexOfPath = Math.Max(0, resourcePath.IndexOf(AssemblyPathSeparator));
|
||||||
|
return GetAssemblyNameAndVersion(resourcePath.Substring(0, indexOfPath));
|
||||||
|
}
|
||||||
|
if (resourceUrl.Segments.Length > 1) {
|
||||||
|
var assemblySegment = resourceUrl.Segments[1];
|
||||||
|
// default assembly name to the first path
|
||||||
|
return GetAssemblyNameAndVersion(assemblySegment.EndsWith(PathSeparator) ? assemblySegment.Substring(0, assemblySegment.Length - PathSeparator.Length) : assemblySegment);
|
||||||
|
}
|
||||||
|
return (string.Empty, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static (string, Version) GetAssemblyNameAndVersion(string assemblyNameAndVersion) {
|
||||||
|
var parts = assemblyNameAndVersion.Split(AssemblyVersionSeparator);
|
||||||
|
return parts.Length == 2 ?
|
||||||
|
(parts[0], new Version(parts[1])) :
|
||||||
|
(parts[0], null);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal string WithDomain(string domain) {
|
||||||
|
return string.Format(Url, string.IsNullOrEmpty(domain) ? "" : ("_" + domain));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
WebViewControl.Avalonia/ResourcesManager.Avalonia.cs
Normal file
10
WebViewControl.Avalonia/ResourcesManager.Avalonia.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
partial class ResourcesManager {
|
||||||
|
|
||||||
|
private static Stream GetApplicationResource(string assemblyName, string resourceName) {
|
||||||
|
return null; // TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
107
WebViewControl.Avalonia/ResourcesManager.cs
Normal file
107
WebViewControl.Avalonia/ResourcesManager.cs
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using Xilium.CefGlue;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
public static partial class ResourcesManager {
|
||||||
|
|
||||||
|
private static readonly AssemblyCache cache = new AssemblyCache();
|
||||||
|
|
||||||
|
private static Stream InternalTryGetResource(string assemblyName, string defaultNamespace, IEnumerable<string> resourcePath, bool failOnMissingResource) {
|
||||||
|
var assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.GetName().Name == assemblyName);
|
||||||
|
if (assembly == null) {
|
||||||
|
if (failOnMissingResource) {
|
||||||
|
throw new InvalidOperationException("Assembly not found: " + assemblyName);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return InternalTryGetResource(assembly, defaultNamespace, resourcePath, failOnMissingResource);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ComputeEmbeddedResourceName(string defaultNamespace, IEnumerable<string> resourcePath) {
|
||||||
|
var resourceParts = (new[] { defaultNamespace }).Concat(resourcePath).ToArray();
|
||||||
|
for (int i = 0; i < resourceParts.Length - 1; i++) {
|
||||||
|
resourceParts[i] = resourceParts[i].Replace('-', '_').Replace('@', '_');
|
||||||
|
}
|
||||||
|
return string.Join(".", resourceParts);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Stream InternalTryGetResource(Assembly assembly, string defaultNamespace, IEnumerable<string> resourcePath, bool failOnMissingResource) {
|
||||||
|
var resourceName = ComputeEmbeddedResourceName(defaultNamespace, resourcePath);
|
||||||
|
var stream = assembly.GetManifestResourceStream(resourceName);
|
||||||
|
if (stream == null) {
|
||||||
|
var assemblyName = assembly.GetName().Name;
|
||||||
|
var alternativeResourceName = string.Join(ResourceUrl.PathSeparator, resourcePath);
|
||||||
|
try {
|
||||||
|
stream = GetApplicationResource(assemblyName, alternativeResourceName);
|
||||||
|
} catch (IOException) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (failOnMissingResource && stream == null) {
|
||||||
|
throw new InvalidOperationException("Resource not found: " + resourceName);
|
||||||
|
}
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Stream GetResourceWithFullPath(Assembly assembly, IEnumerable<string> resourcePath) {
|
||||||
|
return InternalTryGetResource(assembly, resourcePath.First(), resourcePath.Skip(1), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Stream GetResource(Assembly assembly, IEnumerable<string> resourcePath) {
|
||||||
|
return InternalTryGetResource(assembly, assembly.GetName().Name, resourcePath, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Stream GetResource(string assemblyName, IEnumerable<string> resourcePath) {
|
||||||
|
return InternalTryGetResource(assemblyName, assemblyName, resourcePath, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Stream TryGetResource(Assembly assembly, IEnumerable<string> resourcePath) {
|
||||||
|
return InternalTryGetResource(assembly, assembly.GetName().Name, resourcePath, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Stream TryGetResource(string assemblyName, IEnumerable<string> resourcePath) {
|
||||||
|
return InternalTryGetResource(assemblyName, assemblyName, resourcePath, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Stream TryGetResourceWithFullPath(Assembly assembly, IEnumerable<string> resourcePath) {
|
||||||
|
return InternalTryGetResource(assembly, resourcePath.First(), resourcePath.Skip(1), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Stream TryGetResourceWithFullPath(string assemblyName, IEnumerable<string> resourcePath) {
|
||||||
|
return InternalTryGetResource(assemblyName, resourcePath.First(), resourcePath.Skip(1), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static Stream TryGetResource(Uri url, bool failOnMissingAssembly, out string extension) {
|
||||||
|
var resourceAssembly = cache.ResolveResourceAssembly(url, failOnMissingAssembly);
|
||||||
|
if (resourceAssembly == null) {
|
||||||
|
extension = string.Empty;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var resourcePath = ResourceUrl.GetEmbeddedResourcePath(url);
|
||||||
|
|
||||||
|
extension = Path.GetExtension(resourcePath.Last()).ToLower();
|
||||||
|
var resourceStream = TryGetResourceWithFullPath(resourceAssembly, resourcePath);
|
||||||
|
|
||||||
|
return resourceStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Stream TryGetResource(Uri url) {
|
||||||
|
return TryGetResource(url, false, out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetMimeType(string resourceName) {
|
||||||
|
var extension = Path.GetExtension(resourceName);
|
||||||
|
return GetExtensionMimeType(extension);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetExtensionMimeType(string extension) {
|
||||||
|
extension = string.IsNullOrEmpty(extension) ? "html" : extension.TrimStart('.');
|
||||||
|
return CefRuntime.GetMimeType(extension);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
WebViewControl.Avalonia/SchemeHandlerFactory.cs
Normal file
11
WebViewControl.Avalonia/SchemeHandlerFactory.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using Xilium.CefGlue;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
internal class SchemeHandlerFactory : CefSchemeHandlerFactory {
|
||||||
|
|
||||||
|
protected override CefResourceHandler Create(CefBrowser browser, CefFrame frame, string schemeName, CefRequest request) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
WebViewControl.Avalonia/UnhandledAsyncExceptionEventArgs.cs
Normal file
18
WebViewControl.Avalonia/UnhandledAsyncExceptionEventArgs.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
public class UnhandledAsyncExceptionEventArgs {
|
||||||
|
|
||||||
|
public UnhandledAsyncExceptionEventArgs(Exception e, string frameName) {
|
||||||
|
Exception = e;
|
||||||
|
FrameName = frameName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Exception Exception { get; private set; }
|
||||||
|
|
||||||
|
public string FrameName { get; private set; }
|
||||||
|
|
||||||
|
public bool Handled { get; set; }
|
||||||
|
}
|
||||||
|
}
|
31
WebViewControl.Avalonia/UrlHelper.cs
Normal file
31
WebViewControl.Avalonia/UrlHelper.cs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
internal static class UrlHelper {
|
||||||
|
|
||||||
|
private const string ChromeInternalProtocol = "devtools:";
|
||||||
|
|
||||||
|
public const string AboutBlankUrl = "about:blank";
|
||||||
|
|
||||||
|
public static ResourceUrl DefaultLocalUrl = new ResourceUrl(ResourceUrl.LocalScheme, "index.html");
|
||||||
|
|
||||||
|
public static bool IsChromeInternalUrl(string url) {
|
||||||
|
return url != null && url.StartsWith(ChromeInternalProtocol, StringComparison.InvariantCultureIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsInternalUrl(string url) {
|
||||||
|
return IsChromeInternalUrl(url) || url.StartsWith(DefaultLocalUrl.ToString(), StringComparison.InvariantCultureIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void OpenInExternalBrowser(string url) {
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
|
||||||
|
Process.Start("explorer", "\"" + url + "\"");
|
||||||
|
} else {
|
||||||
|
Process.Start("open", url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
100
WebViewControl.Avalonia/WebView.Avalonia.cs
Normal file
100
WebViewControl.Avalonia/WebView.Avalonia.cs
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.ExceptionServices;
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Data;
|
||||||
|
using Avalonia.Input;
|
||||||
|
using Avalonia.Threading;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
partial class WebView : BaseControl {
|
||||||
|
|
||||||
|
private bool IsInDesignMode => false;
|
||||||
|
|
||||||
|
public static readonly StyledProperty<string> AddressProperty =
|
||||||
|
AvaloniaProperty.Register<WebView, string>(nameof(Address), defaultBindingMode: BindingMode.TwoWay);
|
||||||
|
|
||||||
|
public string Address {
|
||||||
|
get => GetValue(AddressProperty);
|
||||||
|
set => SetValue(AddressProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) {
|
||||||
|
base.OnPropertyChanged(change);
|
||||||
|
|
||||||
|
if (change.Property == AddressProperty) {
|
||||||
|
InternalAddress = Address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
partial void ExtraInitialize() {
|
||||||
|
VisualChildren.Add(chromium);
|
||||||
|
chromium[!FocusableProperty] = this[!FocusableProperty];
|
||||||
|
chromium.AddressChanged += (o, address) => ExecuteInUI(() => Address = address);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnKeyDown(KeyEventArgs e) {
|
||||||
|
if (AllowDeveloperTools && e.Key == Key.F12) {
|
||||||
|
ToggleDeveloperTools();
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnGotFocus(GotFocusEventArgs e) {
|
||||||
|
if (!e.Handled) {
|
||||||
|
e.Handled = true;
|
||||||
|
base.OnGotFocus(e);
|
||||||
|
|
||||||
|
// use async call to avoid reentrancy, otherwise the webview will fight to get the focus
|
||||||
|
Dispatcher.UIThread.Post(() => {
|
||||||
|
if (IsFocused) {
|
||||||
|
chromium.Focus();
|
||||||
|
}
|
||||||
|
}, DispatcherPriority.Background);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void InternalDispose() => Dispose();
|
||||||
|
|
||||||
|
private void ForwardException(ExceptionDispatchInfo exceptionInfo) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
private T ExecuteInUI<T>(Func<T> action) {
|
||||||
|
if (Dispatcher.UIThread.CheckAccess()) {
|
||||||
|
return action();
|
||||||
|
}
|
||||||
|
return Dispatcher.UIThread.InvokeAsync<T>(action).Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AsyncExecuteInUI(Action action) {
|
||||||
|
if (isDisposing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// use async call to avoid dead-locks, otherwise if the source action tries to to evaluate js it would block
|
||||||
|
Dispatcher.UIThread.InvokeAsync(
|
||||||
|
() => {
|
||||||
|
if (!isDisposing) {
|
||||||
|
ExecuteWithAsyncErrorHandling(action);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
DispatcherPriority.Normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void InitializeBrowser(int initialWidth, int initialHeight) {
|
||||||
|
chromium.CreateBrowser(initialWidth, initialHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when the webview is requesting focus. Return false to allow the
|
||||||
|
/// focus to be set or true to cancel setting the focus.
|
||||||
|
/// <paramref name="isSystemEvent">True if is a system focus event, or false if is a navigation</paramref>
|
||||||
|
/// </summary>
|
||||||
|
protected virtual bool OnSetFocus(bool isSystemEvent) {
|
||||||
|
// VisualRoot can be null when webview is not yet added to the Visual tree
|
||||||
|
var focusedElement = TopLevel.GetTopLevel(this)?.FocusManager.GetFocusedElement();
|
||||||
|
return !(focusedElement == chromium || focusedElement == this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
WebViewControl.Avalonia/WebView.Extensions.cs
Normal file
36
WebViewControl.Avalonia/WebView.Extensions.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using Xilium.CefGlue;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
internal static class WebViewExtensions {
|
||||||
|
|
||||||
|
public static string[] GetFrameNames(this WebView webview) {
|
||||||
|
return webview.GetCefBrowser()?.GetFrameNames().Where(n => !webview.IsMainFrame(n)).ToArray() ?? new string[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static bool HasFrame(this WebView webview, string name) {
|
||||||
|
return webview.GetFrame(name) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static CefFrame GetFrame(this WebView webview, string frameName) {
|
||||||
|
return webview.GetCefBrowser()?.GetFrame(frameName ?? "");
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static bool IsMainFrame(this WebView webview, string frameName) {
|
||||||
|
return string.IsNullOrEmpty(frameName);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void SendKeyEvent(this WebView webview, CefKeyEvent keyEvent) {
|
||||||
|
webview.GetCefBrowser()?.GetHost()?.SendKeyEvent(keyEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void SetAccessibilityState(this WebView webview, CefState state) {
|
||||||
|
webview.GetCefBrowser()?.GetHost()?.SetAccessibilityState(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CefBrowser GetCefBrowser(this WebView webview) {
|
||||||
|
return webview.UnderlyingBrowser.GetBrowser();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
using Xilium.CefGlue;
|
||||||
|
using Xilium.CefGlue.Common.Handlers;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
partial class WebView {
|
||||||
|
|
||||||
|
private class InternalContextMenuHandler : ContextMenuHandler {
|
||||||
|
|
||||||
|
private WebView OwnerWebView { get; }
|
||||||
|
|
||||||
|
public InternalContextMenuHandler(WebView webView) {
|
||||||
|
OwnerWebView = webView;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnBeforeContextMenu(CefBrowser browser, CefFrame frame, CefContextMenuParams state, CefMenuModel model) {
|
||||||
|
if (OwnerWebView.DisableBuiltinContextMenus) {
|
||||||
|
model.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
WebViewControl.Avalonia/WebView.InternalDialogHandler.cs
Normal file
23
WebViewControl.Avalonia/WebView.InternalDialogHandler.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
using Xilium.CefGlue;
|
||||||
|
using Xilium.CefGlue.Common.Handlers;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
partial class WebView {
|
||||||
|
private class InternalDialogHandler : DialogHandler {
|
||||||
|
|
||||||
|
private WebView OwnerWebView { get; }
|
||||||
|
|
||||||
|
public InternalDialogHandler(WebView webView) {
|
||||||
|
OwnerWebView = webView;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnFileDialog(CefBrowser browser, CefFileDialogMode mode, string title, string defaultFilePath, string[] acceptFilters, CefFileDialogCallback callback) {
|
||||||
|
if (OwnerWebView.DisableFileDialogs) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
37
WebViewControl.Avalonia/WebView.InternalDownloadHandler.cs
Normal file
37
WebViewControl.Avalonia/WebView.InternalDownloadHandler.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
using Xilium.CefGlue;
|
||||||
|
using Xilium.CefGlue.Common.Handlers;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
partial class WebView {
|
||||||
|
|
||||||
|
private class InternalDownloadHandler : DownloadHandler {
|
||||||
|
|
||||||
|
private WebView OwnerWebView { get; }
|
||||||
|
|
||||||
|
public InternalDownloadHandler(WebView owner) {
|
||||||
|
OwnerWebView = owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnBeforeDownload(CefBrowser browser, CefDownloadItem downloadItem, string suggestedName, CefBeforeDownloadCallback callback) {
|
||||||
|
callback.Continue(downloadItem.SuggestedFileName, showDialog: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnDownloadUpdated(CefBrowser browser, CefDownloadItem downloadItem, CefDownloadItemCallback callback) {
|
||||||
|
if (downloadItem.IsComplete) {
|
||||||
|
if (OwnerWebView.DownloadCompleted != null) {
|
||||||
|
OwnerWebView.AsyncExecuteInUI(() => OwnerWebView.DownloadCompleted?.Invoke(downloadItem.FullPath));
|
||||||
|
}
|
||||||
|
} else if (downloadItem.IsCanceled) {
|
||||||
|
if (OwnerWebView.DownloadCancelled != null) {
|
||||||
|
OwnerWebView.AsyncExecuteInUI(() => OwnerWebView.DownloadCancelled?.Invoke(downloadItem.FullPath));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (OwnerWebView.DownloadProgressChanged != null) {
|
||||||
|
OwnerWebView.AsyncExecuteInUI(() => OwnerWebView.DownloadProgressChanged?.Invoke(downloadItem.FullPath, downloadItem.ReceivedBytes, downloadItem.TotalBytes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
39
WebViewControl.Avalonia/WebView.InternalDragHandler.cs
Normal file
39
WebViewControl.Avalonia/WebView.InternalDragHandler.cs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
using Xilium.CefGlue;
|
||||||
|
using Xilium.CefGlue.Common.Handlers;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
partial class WebView {
|
||||||
|
private class InternalDragHandler : DragHandler {
|
||||||
|
|
||||||
|
private WebView OwnerWebView { get; }
|
||||||
|
|
||||||
|
public InternalDragHandler(WebView owner) {
|
||||||
|
OwnerWebView = owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnDragEnter(CefBrowser browser, CefDragData dragData, CefDragOperationsMask mask) {
|
||||||
|
var filesDragging = OwnerWebView.FilesDragging;
|
||||||
|
if (filesDragging != null) {
|
||||||
|
var fileNames = dragData.GetFileNames();
|
||||||
|
if (fileNames != null) {
|
||||||
|
filesDragging(fileNames);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var textDragging = OwnerWebView.TextDragging;
|
||||||
|
if (textDragging != null) {
|
||||||
|
var textContent = dragData.FragmentText;
|
||||||
|
if (!string.IsNullOrEmpty(textContent)) {
|
||||||
|
textDragging(textContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnDraggableRegionsChanged(CefBrowser browser, CefFrame frame, CefDraggableRegion[] regions) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
WebViewControl.Avalonia/WebView.InternalFocusHandler.cs
Normal file
29
WebViewControl.Avalonia/WebView.InternalFocusHandler.cs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
using Xilium.CefGlue;
|
||||||
|
using Xilium.CefGlue.Common.Handlers;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
partial class WebView {
|
||||||
|
|
||||||
|
private class InternalFocusHandler : FocusHandler {
|
||||||
|
|
||||||
|
private WebView OwnerWebView { get; }
|
||||||
|
|
||||||
|
public InternalFocusHandler(WebView webView) {
|
||||||
|
OwnerWebView = webView;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnGotFocus(CefBrowser browser) {
|
||||||
|
OwnerWebView.OnGotFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnSetFocus(CefBrowser browser, CefFocusSource source) {
|
||||||
|
return OwnerWebView.OnSetFocus(source == CefFocusSource.System);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnTakeFocus(CefBrowser browser, bool next) {
|
||||||
|
OwnerWebView.OnLostFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
42
WebViewControl.Avalonia/WebView.InternalJsDialogHandler.cs
Normal file
42
WebViewControl.Avalonia/WebView.InternalJsDialogHandler.cs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
using Xilium.CefGlue;
|
||||||
|
using Xilium.CefGlue.Common.Handlers;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
partial class WebView {
|
||||||
|
|
||||||
|
private class InternalJsDialogHandler : JSDialogHandler {
|
||||||
|
|
||||||
|
private WebView OwnerWebView { get; }
|
||||||
|
|
||||||
|
public InternalJsDialogHandler(WebView webView) {
|
||||||
|
OwnerWebView = webView;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnJSDialog(CefBrowser browser, string originUrl, CefJSDialogType dialogType, string message_text, string default_prompt_text, CefJSDialogCallback callback, out bool suppress_message) {
|
||||||
|
suppress_message = false;
|
||||||
|
|
||||||
|
var javacriptDialogShown = OwnerWebView.JavacriptDialogShown;
|
||||||
|
if (javacriptDialogShown == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Close() {
|
||||||
|
callback.Continue(true, "");
|
||||||
|
callback.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
javacriptDialogShown.Invoke(message_text, Close);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnBeforeUnloadDialog(CefBrowser browser, string messageText, bool isReload, CefJSDialogCallback callback) {
|
||||||
|
return false; // use default
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnResetDialogState(CefBrowser browser) { }
|
||||||
|
|
||||||
|
protected override void OnDialogClosed(CefBrowser browser) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
WebViewControl.Avalonia/WebView.InternalKeyboardHandler.cs
Normal file
28
WebViewControl.Avalonia/WebView.InternalKeyboardHandler.cs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
using System;
|
||||||
|
using Xilium.CefGlue;
|
||||||
|
using Xilium.CefGlue.Common.Handlers;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
partial class WebView {
|
||||||
|
|
||||||
|
private class InternalKeyboardHandler : KeyboardHandler {
|
||||||
|
|
||||||
|
private WebView OwnerWebView { get; }
|
||||||
|
|
||||||
|
public InternalKeyboardHandler(WebView webView) {
|
||||||
|
OwnerWebView = webView;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnPreKeyEvent(CefBrowser browser, CefKeyEvent keyEvent, IntPtr os_event, out bool isKeyboardShortcut) {
|
||||||
|
var handler = OwnerWebView.KeyPressed;
|
||||||
|
if (handler != null && !browser.IsPopup) {
|
||||||
|
handler(keyEvent, out var handled);
|
||||||
|
isKeyboardShortcut = false;
|
||||||
|
return handled;
|
||||||
|
}
|
||||||
|
return base.OnPreKeyEvent(browser, keyEvent, os_event, out isKeyboardShortcut);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
50
WebViewControl.Avalonia/WebView.InternalLifeSpanHandler.cs
Normal file
50
WebViewControl.Avalonia/WebView.InternalLifeSpanHandler.cs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using Xilium.CefGlue;
|
||||||
|
using Xilium.CefGlue.Common.Handlers;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
partial class WebView {
|
||||||
|
|
||||||
|
private class InternalLifeSpanHandler : LifeSpanHandler {
|
||||||
|
|
||||||
|
private WebView OwnerWebView { get; }
|
||||||
|
|
||||||
|
public InternalLifeSpanHandler(WebView webView) {
|
||||||
|
OwnerWebView = webView;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnBeforePopup(CefBrowser browser, CefFrame frame, string targetUrl, string targetFrameName, CefWindowOpenDisposition targetDisposition, bool userGesture, CefPopupFeatures popupFeatures, CefWindowInfo windowInfo, ref CefClient client, CefBrowserSettings settings, ref CefDictionaryValue extraInfo, ref bool noJavascriptAccess) {
|
||||||
|
if (UrlHelper.IsChromeInternalUrl(targetUrl)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Uri.IsWellFormedUriString(targetUrl, UriKind.RelativeOrAbsolute)) {
|
||||||
|
var uri = new Uri(targetUrl);
|
||||||
|
if (!uri.IsAbsoluteUri) {
|
||||||
|
// turning relative urls into full path to avoid that someone runs custom command lines
|
||||||
|
targetUrl = new Uri(new Uri(frame.Url), uri).AbsoluteUri;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false; // if the url is not well formed let's use the browser to handle the things
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var popupOpening = OwnerWebView.PopupOpening;
|
||||||
|
if (popupOpening != null) {
|
||||||
|
popupOpening(targetUrl);
|
||||||
|
} else {
|
||||||
|
UrlHelper.OpenInExternalBrowser(targetUrl);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// if we can't handle the command line let's continue the normal request with the popup
|
||||||
|
// with this, will not blow in the users face
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
98
WebViewControl.Avalonia/WebView.InternalRequestHandler.cs
Normal file
98
WebViewControl.Avalonia/WebView.InternalRequestHandler.cs
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using Xilium.CefGlue;
|
||||||
|
using Xilium.CefGlue.Common.Handlers;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
partial class WebView {
|
||||||
|
|
||||||
|
private class InternalRequestHandler : RequestHandler {
|
||||||
|
|
||||||
|
private static readonly Lazy<HttpResourceRequestHandler> HttpResourceRequestHandler = new Lazy<HttpResourceRequestHandler>(() => new HttpResourceRequestHandler());
|
||||||
|
|
||||||
|
private WebView OwnerWebView { get; }
|
||||||
|
|
||||||
|
private InternalResourceRequestHandler ResourceRequestHandler { get; }
|
||||||
|
|
||||||
|
public InternalRequestHandler(WebView webView) {
|
||||||
|
OwnerWebView = webView;
|
||||||
|
ResourceRequestHandler = new InternalResourceRequestHandler(OwnerWebView);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool GetAuthCredentials(CefBrowser browser, string originUrl, bool isProxy, string host, int port, string realm, string scheme, CefAuthCallback callback) {
|
||||||
|
using (callback) {
|
||||||
|
if (OwnerWebView.ProxyAuthentication != null) {
|
||||||
|
callback.Continue(OwnerWebView.ProxyAuthentication.UserName, OwnerWebView.ProxyAuthentication.Password);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnBeforeBrowse(CefBrowser browser, CefFrame frame, CefRequest request, bool userGesture, bool isRedirect) {
|
||||||
|
if (UrlHelper.IsInternalUrl(request.Url)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (OwnerWebView.IsHistoryDisabled && request.TransitionType.HasFlag(CefTransitionType.ForwardBackFlag)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cancel = false;
|
||||||
|
var beforeNavigate = OwnerWebView.BeforeNavigate;
|
||||||
|
if (beforeNavigate != null) {
|
||||||
|
var wrappedRequest = new Request(request, OwnerWebView.GetRequestUrl(request.Url, (ResourceType) request.ResourceType));
|
||||||
|
OwnerWebView.ExecuteWithAsyncErrorHandling(() => beforeNavigate(wrappedRequest));
|
||||||
|
cancel = wrappedRequest.Canceled;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cancel;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnCertificateError(CefBrowser browser, CefErrorCode certError, string requestUrl, CefSslInfo sslInfo, CefCallback callback) {
|
||||||
|
using (callback) {
|
||||||
|
if (OwnerWebView.IgnoreCertificateErrors) {
|
||||||
|
callback.Continue();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnRenderProcessTerminated(CefBrowser browser, CefTerminationStatus status) {
|
||||||
|
OwnerWebView.HandleRenderProcessCrashed();
|
||||||
|
|
||||||
|
const string ExceptionPrefix = "WebView render process ";
|
||||||
|
|
||||||
|
Exception exception;
|
||||||
|
|
||||||
|
switch (status) {
|
||||||
|
case CefTerminationStatus.ProcessCrashed:
|
||||||
|
exception = new RenderProcessCrashedException(ExceptionPrefix + "crashed");
|
||||||
|
break;
|
||||||
|
case CefTerminationStatus.WasKilled:
|
||||||
|
exception = new RenderProcessKilledException(ExceptionPrefix + "was killed", OwnerWebView.IsDisposing);
|
||||||
|
break;
|
||||||
|
case CefTerminationStatus.OutOfMemory:
|
||||||
|
exception = new RenderProcessOutOfMemoryException(ExceptionPrefix + "ran out of memory");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
exception = new RenderProcessCrashedException(ExceptionPrefix + "terminated with an unknown reason");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
OwnerWebView.ForwardUnhandledAsyncException(exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override CefResourceRequestHandler GetResourceRequestHandler(CefBrowser browser, CefFrame frame, CefRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling) {
|
||||||
|
if (OwnerWebView.IsSecurityDisabled && HttpResourceHandler.AcceptedResources.Contains(request.ResourceType) && request.Url != null) {
|
||||||
|
var url = new Uri(request.Url);
|
||||||
|
if (url.Scheme == Uri.UriSchemeHttp || url.Scheme == Uri.UriSchemeHttps) {
|
||||||
|
return HttpResourceRequestHandler.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ResourceRequestHandler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
using System;
|
||||||
|
using Xilium.CefGlue;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
partial class WebView {
|
||||||
|
|
||||||
|
private class InternalResourceRequestHandler : CefResourceRequestHandler {
|
||||||
|
|
||||||
|
public InternalResourceRequestHandler(WebView ownerWebView) {
|
||||||
|
OwnerWebView = ownerWebView;
|
||||||
|
}
|
||||||
|
|
||||||
|
private WebView OwnerWebView { get; }
|
||||||
|
|
||||||
|
protected override CefCookieAccessFilter GetCookieAccessFilter(CefBrowser browser, CefFrame frame, CefRequest request) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override CefResourceHandler GetResourceHandler(CefBrowser browser, CefFrame frame, CefRequest request) {
|
||||||
|
if (request.Url == OwnerWebView.DefaultLocalUrl) {
|
||||||
|
return AsyncResourceHandler.FromText(OwnerWebView.htmlToLoad ?? "");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UrlHelper.IsChromeInternalUrl(request.Url)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var resourceHandler = new ResourceHandler(request, OwnerWebView.GetRequestUrl(request.Url, (ResourceType)request.ResourceType));
|
||||||
|
|
||||||
|
void TriggerBeforeResourceLoadEvent() {
|
||||||
|
var beforeResourceLoad = OwnerWebView.BeforeResourceLoad;
|
||||||
|
if (beforeResourceLoad != null) {
|
||||||
|
OwnerWebView.ExecuteWithAsyncErrorHandling(() => beforeResourceLoad(resourceHandler));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Uri.TryCreate(resourceHandler.Url, UriKind.Absolute, out var url) && url.Scheme == ResourceUrl.EmbeddedScheme) {
|
||||||
|
resourceHandler.BeginAsyncResponse(() => {
|
||||||
|
var urlWithoutQuery = new UriBuilder(url);
|
||||||
|
if (!string.IsNullOrEmpty(url.Query)) {
|
||||||
|
urlWithoutQuery.Query = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
OwnerWebView.ExecuteWithAsyncErrorHandling(() => resourceHandler.LoadEmbeddedResource(urlWithoutQuery.Uri));
|
||||||
|
|
||||||
|
TriggerBeforeResourceLoadEvent();
|
||||||
|
|
||||||
|
if (resourceHandler.Handled || OwnerWebView.IgnoreMissingResources) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var resourceLoadFailed = OwnerWebView.ResourceLoadFailed;
|
||||||
|
if (resourceLoadFailed != null) {
|
||||||
|
resourceLoadFailed(url.ToString());
|
||||||
|
} else {
|
||||||
|
OwnerWebView.ForwardUnhandledAsyncException(new InvalidOperationException("Resource not found: " + url));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
TriggerBeforeResourceLoadEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourceHandler.Handler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
42
WebViewControl.Avalonia/WebView.JavascriptException.cs
Normal file
42
WebViewControl.Avalonia/WebView.JavascriptException.cs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Xilium.CefGlue.Common.Events;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
partial class WebView {
|
||||||
|
|
||||||
|
public class JavascriptException : Exception {
|
||||||
|
|
||||||
|
private JavascriptStackFrame[] JsStack { get; }
|
||||||
|
private string InnerStack { get; }
|
||||||
|
|
||||||
|
internal JavascriptException(string message, IEnumerable<JavascriptStackFrame> stack = null, string innerStackTrace = null)
|
||||||
|
: base(message, null) {
|
||||||
|
JsStack = stack?.ToArray() ?? new JavascriptStackFrame[0];
|
||||||
|
InnerStack = innerStackTrace;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal JavascriptException(string name, string message, IEnumerable<JavascriptStackFrame> stack = null, string baseStackTrace = null)
|
||||||
|
: this((string.IsNullOrEmpty(name) ? "" : name + ": ") + message, stack, baseStackTrace) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string StackTrace {
|
||||||
|
get { return string.Join(Environment.NewLine, JsStack.Select(FormatStackFrame).Concat(new[] { BaseStackTrace })); }
|
||||||
|
}
|
||||||
|
|
||||||
|
private string BaseStackTrace => (InnerStack != null ? InnerStack + Environment.NewLine : "") + base.StackTrace;
|
||||||
|
|
||||||
|
private static string FormatStackFrame(JavascriptStackFrame frame) {
|
||||||
|
var functionName = string.IsNullOrEmpty(frame.FunctionName) ? "<anonymous>" : frame.FunctionName;
|
||||||
|
var location = string.IsNullOrEmpty(frame.ScriptNameOrSourceUrl) ? "" : ($" in {frame.ScriptNameOrSourceUrl}:line {frame.LineNumber} {frame.Column}");
|
||||||
|
return $" at {functionName}{location}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString() {
|
||||||
|
return GetType().FullName + ": " + Message + Environment.NewLine + StackTrace;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
132
WebViewControl.Avalonia/WebView.JavascriptExecutionApi.cs
Normal file
132
WebViewControl.Avalonia/WebView.JavascriptExecutionApi.cs
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
partial class WebView {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers an object with the specified name in the window context of the browser
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Name of the object that will be available on the JS window context.</param>
|
||||||
|
/// <param name="objectToBind">.Net object instance that will be bound to the JS proxy object.</param>
|
||||||
|
/// <param name="interceptCall">Optional method that allows intercepting every call.</param>
|
||||||
|
/// <returns>True if the object was registered or false if the object was already registered before</returns>
|
||||||
|
public bool RegisterJavascriptObject(string name, object objectToBind, Func<Func<object>, object> interceptCall = null) {
|
||||||
|
if (interceptCall == null) {
|
||||||
|
interceptCall = target => target();
|
||||||
|
}
|
||||||
|
return InnerRegisterJavascriptObject(name, objectToBind, interceptCall);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers an object with the specified name in the window context of the browser.
|
||||||
|
/// Use this overload with async interceptor methods.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Name of the object that will be available on the JS window context.</param>
|
||||||
|
/// <param name="objectToBind">.Net object instance that will be bound to the JS proxy object.</param>
|
||||||
|
/// <param name="interceptCall">Async method that allows intercepting every call.</param>
|
||||||
|
/// <returns>True if the object was registered or false if the object was already registered before</returns>
|
||||||
|
public bool RegisterJavascriptObject(string name, object objectToBind, Func<Func<object>, Task<object>> interceptCall) {
|
||||||
|
return InnerRegisterJavascriptObject(name, objectToBind, interceptCall);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool InnerRegisterJavascriptObject<T>(string name, object objectToBind, Func<Func<object>, T> interceptCall) where T : class {
|
||||||
|
if (chromium.IsJavascriptObjectRegistered(name)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
T CallTargetMethod(Func<object> target) {
|
||||||
|
if (isDisposing) {
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
var isAsync = false;
|
||||||
|
try {
|
||||||
|
JavascriptPendingCalls.AddCount();
|
||||||
|
|
||||||
|
if (isDisposing) {
|
||||||
|
// check again, to avoid concurrency problems with dispose
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = interceptCall(target);
|
||||||
|
if (result is Task task) {
|
||||||
|
task.ContinueWith(t => JavascriptPendingCalls.Signal());
|
||||||
|
isAsync = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} finally {
|
||||||
|
if (!isAsync) {
|
||||||
|
JavascriptPendingCalls.Signal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chromium.RegisterJavascriptObject(objectToBind, name, CallTargetMethod);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unregisters an object with the specified name in the window context of the browser
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name"></param>
|
||||||
|
public void UnregisterJavascriptObject(string name) {
|
||||||
|
chromium.UnregisterJavascriptObject(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<T> EvaluateScript<T>(string script, string frameName = MainFrameName, TimeSpan? timeout = null) {
|
||||||
|
var jsExecutor = GetJavascriptExecutor(frameName);
|
||||||
|
if (jsExecutor != null) {
|
||||||
|
return jsExecutor.EvaluateScript<T>(script, timeout: timeout);
|
||||||
|
}
|
||||||
|
return Task.FromResult(default(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ExecuteScript(string script, string frameName = MainFrameName) {
|
||||||
|
GetJavascriptExecutor(frameName)?.ExecuteScript(script);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ExecuteScriptFunction(string functionName, params string[] args) {
|
||||||
|
ExecuteScriptFunctionInFrame(functionName, MainFrameName, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ExecuteScriptFunctionInFrame(string functionName, string frameName, params string[] args) {
|
||||||
|
GetJavascriptExecutor(frameName)?.ExecuteScriptFunction(functionName, false, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<T> EvaluateScriptFunction<T>(string functionName, params string[] args) {
|
||||||
|
return EvaluateScriptFunctionInFrame<T>(functionName, MainFrameName, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<T> EvaluateScriptFunctionInFrame<T>(string functionName, string frameName, params string[] args) {
|
||||||
|
var jsExecutor = GetJavascriptExecutor(frameName);
|
||||||
|
if (jsExecutor != null) {
|
||||||
|
return jsExecutor.EvaluateScriptFunction<T>(functionName, false, args);
|
||||||
|
}
|
||||||
|
return Task.FromResult(default(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void ExecuteScriptFunctionWithSerializedParams(string functionName, params object[] args) {
|
||||||
|
ExecuteScriptFunctionWithSerializedParamsInFrame(functionName, MainFrameName, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void ExecuteScriptFunctionWithSerializedParamsInFrame(string functionName, string frameName, params object[] args) {
|
||||||
|
GetJavascriptExecutor(frameName)?.ExecuteScriptFunction(functionName, true, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Task<T> EvaluateScriptFunctionWithSerializedParams<T>(string functionName, params object[] args) {
|
||||||
|
return EvaluateScriptFunctionWithSerializedParamsInFrame<T>(functionName, MainFrameName, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Task<T> EvaluateScriptFunctionWithSerializedParamsInFrame<T>(string functionName, string frameName, params object[] args) {
|
||||||
|
var jsExecutor = GetJavascriptExecutor(frameName);
|
||||||
|
if (jsExecutor != null) {
|
||||||
|
return jsExecutor.EvaluateScriptFunction<T>(functionName, true, args);
|
||||||
|
}
|
||||||
|
return Task.FromResult(default(T));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
321
WebViewControl.Avalonia/WebView.JavascriptExecutor.cs
Normal file
321
WebViewControl.Avalonia/WebView.JavascriptExecutor.cs
Normal file
@ -0,0 +1,321 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
using System.Runtime.Serialization.Json;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xilium.CefGlue;
|
||||||
|
using Xilium.CefGlue.Common.Events;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
partial class WebView {
|
||||||
|
|
||||||
|
[DataContract]
|
||||||
|
internal class JsError {
|
||||||
|
[DataMember(Name = "stack")]
|
||||||
|
public string Stack;
|
||||||
|
[DataMember(Name = "name")]
|
||||||
|
public string Name;
|
||||||
|
[DataMember(Name = "message")]
|
||||||
|
public string Message;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class ScriptTask {
|
||||||
|
|
||||||
|
public ScriptTask(string script, string functionName, Action<string> evaluate = null) {
|
||||||
|
Script = script;
|
||||||
|
Evaluate = evaluate;
|
||||||
|
FunctionName = functionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Script { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// We store the function name apart from the script and use it later in the exception details
|
||||||
|
/// this prevents any params to be shown in the message because they can contain sensitive information
|
||||||
|
/// </summary>
|
||||||
|
public string FunctionName { get; }
|
||||||
|
|
||||||
|
public Action<string> Evaluate { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class JavascriptExecutor : IDisposable {
|
||||||
|
|
||||||
|
private const string InternalException = "|WebViewInternalException";
|
||||||
|
|
||||||
|
private static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(60);
|
||||||
|
private static readonly TimeSpan InitializationTimeout = TimeSpan.FromSeconds(15);
|
||||||
|
|
||||||
|
private static Regex StackFrameRegex { get; } = new Regex(@"at\s*(?<method>.*?)\s\(?(?<location>[^\s]+):(?<line>\d+):(?<column>\d+)", RegexOptions.Compiled);
|
||||||
|
|
||||||
|
private BlockingCollection<ScriptTask> PendingScripts { get; } = new BlockingCollection<ScriptTask>();
|
||||||
|
private CancellationTokenSource FlushTaskCancelationToken { get; } = new CancellationTokenSource();
|
||||||
|
|
||||||
|
private WebView OwnerWebView { get; }
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
private string Id { get; } = Guid.NewGuid().ToString();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private CefFrame frame;
|
||||||
|
private Task flushTask;
|
||||||
|
|
||||||
|
public JavascriptExecutor(WebView owner, CefFrame frame = null) {
|
||||||
|
OwnerWebView = owner;
|
||||||
|
this.frame = frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsValid => frame == null || frame.IsValid; // consider valid when not bound (yet) or frame is valid
|
||||||
|
|
||||||
|
private bool IsFlushTaskInitializing => flushTask == null || flushTask.Status < TaskStatus.Running;
|
||||||
|
|
||||||
|
public void StartFlush(CefFrame frame) {
|
||||||
|
#if DEBUG
|
||||||
|
System.Diagnostics.Debug.WriteLine($"{nameof(StartFlush)} ('{Id}')");
|
||||||
|
#endif
|
||||||
|
lock (FlushTaskCancelationToken) {
|
||||||
|
if (flushTask != null || !frame.IsValid || FlushTaskCancelationToken.IsCancellationRequested) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.frame = frame;
|
||||||
|
flushTask = Task.Factory.StartNew(FlushScripts, FlushTaskCancelationToken.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StopFlush() {
|
||||||
|
#if DEBUG
|
||||||
|
System.Diagnostics.Debug.WriteLine($"{nameof(StopFlush)} ('{Id}')");
|
||||||
|
#endif
|
||||||
|
lock (FlushTaskCancelationToken) {
|
||||||
|
if (FlushTaskCancelationToken.IsCancellationRequested) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FlushTaskCancelationToken.Cancel();
|
||||||
|
PendingScripts.CompleteAdding();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ScriptTask QueueScript(string script, string functionName = null, Action<string> evaluate = null) {
|
||||||
|
lock (FlushTaskCancelationToken) {
|
||||||
|
if (FlushTaskCancelationToken.IsCancellationRequested) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var scriptTask = new ScriptTask(script, functionName, evaluate);
|
||||||
|
PendingScripts.Add(scriptTask);
|
||||||
|
return scriptTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FlushScripts() {
|
||||||
|
#if DEBUG
|
||||||
|
System.Diagnostics.Debug.WriteLine($"{nameof(FlushScripts)} running ('{Id}')");
|
||||||
|
#endif
|
||||||
|
OwnerWebView.ExecuteWithAsyncErrorHandling(InnerFlushScripts);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InnerFlushScripts() {
|
||||||
|
try {
|
||||||
|
var scriptsToExecute = new List<ScriptTask>();
|
||||||
|
foreach (var scriptTask in PendingScripts.GetConsumingEnumerable()) {
|
||||||
|
if (scriptTask.Evaluate == null) {
|
||||||
|
scriptsToExecute.Add(scriptTask);
|
||||||
|
}
|
||||||
|
if ((PendingScripts.Count == 0 || scriptTask.Evaluate != null) && scriptsToExecute.Count > 0) {
|
||||||
|
BulkExecuteScripts(scriptsToExecute);
|
||||||
|
scriptsToExecute.Clear();
|
||||||
|
}
|
||||||
|
scriptTask.Evaluate?.Invoke(scriptTask.Script);
|
||||||
|
}
|
||||||
|
} catch (OperationCanceledException) {
|
||||||
|
// stop
|
||||||
|
} finally {
|
||||||
|
PendingScripts.Dispose();
|
||||||
|
FlushTaskCancelationToken.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BulkExecuteScripts(IEnumerable<ScriptTask> scriptsToExecute) {
|
||||||
|
var script = string.Join(";" + Environment.NewLine, scriptsToExecute.Select(s => s.Script));
|
||||||
|
if (frame.IsValid) {
|
||||||
|
var frameName = frame.Name;
|
||||||
|
try {
|
||||||
|
var timeout = OwnerWebView.DefaultScriptsExecutionTimeout ?? DefaultTimeout;
|
||||||
|
var task = OwnerWebView.chromium.EvaluateJavaScript<object>(WrapScriptWithErrorHandling(script), timeout: timeout);
|
||||||
|
task.Wait(FlushTaskCancelationToken.Token);
|
||||||
|
} catch (OperationCanceledException) {
|
||||||
|
// ignore
|
||||||
|
} catch (Exception e) {
|
||||||
|
var evaluatedScriptFunctions = scriptsToExecute.Select(s => s.FunctionName);
|
||||||
|
OwnerWebView.ForwardUnhandledAsyncException(ParseException(e, evaluatedScriptFunctions), frameName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<T> EvaluateScript<T>(string script, string functionName = null, TimeSpan? timeout = null) {
|
||||||
|
const string TimeoutExceptionName = "Timeout";
|
||||||
|
#if DEBUG
|
||||||
|
System.Diagnostics.Debug.WriteLine($"{nameof(EvaluateScript)} '{script}' on ('{Id}')");
|
||||||
|
#endif
|
||||||
|
var evaluationTask = new TaskCompletionSource<T>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
|
||||||
|
void Evaluate(string scriptToEvaluate) {
|
||||||
|
#if DEBUG
|
||||||
|
System.Diagnostics.Debug.WriteLine($"Evaluating '{script}' on ('{Id}')");
|
||||||
|
#endif
|
||||||
|
try {
|
||||||
|
var innerEvaluationTask = OwnerWebView.chromium.EvaluateJavaScript<T>(WrapScriptWithErrorHandling(scriptToEvaluate), timeout: timeout);
|
||||||
|
innerEvaluationTask.Wait(FlushTaskCancelationToken.Token);
|
||||||
|
evaluationTask.SetResult(GetResult<T>(innerEvaluationTask.Result));
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (FlushTaskCancelationToken.IsCancellationRequested) {
|
||||||
|
evaluationTask.SetResult(GetResult<T>(default(T)));
|
||||||
|
} else if (e.InnerException is TaskCanceledException) {
|
||||||
|
evaluationTask.SetException(new JavascriptException(TimeoutExceptionName, "Script evaluation timed out"));
|
||||||
|
} else {
|
||||||
|
evaluationTask.SetException(ParseException(e, new[] { functionName }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var scriptTask = QueueScript(script, functionName, Evaluate);
|
||||||
|
if (scriptTask == null) {
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeout.HasValue) {
|
||||||
|
var tasks = new [] {
|
||||||
|
evaluationTask.Task,
|
||||||
|
Task.Delay(timeout.Value)
|
||||||
|
};
|
||||||
|
|
||||||
|
// wait with timeout if flush is not running yet to avoid hanging forever
|
||||||
|
var task = await Task.WhenAny(tasks).ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (task != evaluationTask.Task) {
|
||||||
|
if (IsFlushTaskInitializing) {
|
||||||
|
throw new JavascriptException(TimeoutExceptionName, $"Javascript engine is not initialized after {InitializationTimeout.Seconds}s");
|
||||||
|
}
|
||||||
|
// flush is already running, timeout will fire from the evaluation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return await evaluationTask.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<T> EvaluateScriptFunction<T>(string functionName, bool serializeParams, params object[] args) {
|
||||||
|
return EvaluateScript<T>(MakeScript(functionName, serializeParams, args, emitReturn: true), functionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ExecuteScriptFunction(string functionName, bool serializeParams, params object[] args) {
|
||||||
|
QueueScript(MakeScript(functionName, serializeParams, args, emitReturn: false), functionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ExecuteScript(string script) {
|
||||||
|
QueueScript(script);
|
||||||
|
}
|
||||||
|
|
||||||
|
private T GetResult<T>(object result) {
|
||||||
|
var targetType = typeof(T);
|
||||||
|
if (result == null) {
|
||||||
|
if (targetType.IsArray) {
|
||||||
|
// return empty arrays when value is null and return type is array
|
||||||
|
return (T)(object)Array.CreateInstance(targetType.GetElementType(), 0);
|
||||||
|
}
|
||||||
|
return default(T); // return default T (its safer, because we allow returning null and converting into a default struct value)
|
||||||
|
}
|
||||||
|
if (IsBasicType(targetType)) {
|
||||||
|
return (T)result;
|
||||||
|
}
|
||||||
|
return (T)result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() {
|
||||||
|
StopFlush();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsBasicType(Type type) {
|
||||||
|
return type.IsPrimitive || type.IsEnum || type == typeof(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string MakeScript(string functionName, bool serializeParams, object[] args, bool emitReturn) {
|
||||||
|
string SerializeParam(object value) {
|
||||||
|
if (serializeParams || value == null) {
|
||||||
|
return JavascriptSerializer.Serialize(value);
|
||||||
|
}
|
||||||
|
// TODO complex types
|
||||||
|
return value.ToString();
|
||||||
|
}
|
||||||
|
var argsSerialized = args.Select(SerializeParam);
|
||||||
|
return $"{(emitReturn ? "return " : string.Empty)}{functionName}({string.Join(",", argsSerialized)})";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string WrapScriptWithErrorHandling(string script) {
|
||||||
|
return "try {" + script + Environment.NewLine + "} catch (e) { throw JSON.stringify({ stack: e.stack, message: e.message, name: e.name }) + '" + InternalException + "' }";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static T DeserializeJSON<T>(string json) {
|
||||||
|
var serializer = new DataContractJsonSerializer(typeof(JsError));
|
||||||
|
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(json))) {
|
||||||
|
return (T)serializer.ReadObject(stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Exception MakeTimeoutException(string functionName, TimeSpan timeout) {
|
||||||
|
return new JavascriptException("Timeout", $"More than {timeout.TotalMilliseconds}ms elapsed evaluating: '{functionName}'");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Exception ParseException(Exception exception, IEnumerable<string> evaluatedScriptFunctions) {
|
||||||
|
var jsErrorJSON = ((exception is AggregateException aggregateException) ? aggregateException.InnerExceptions.FirstOrDefault(e => IsInternalException(e.Message))?.Message : exception.Message) ?? "";
|
||||||
|
|
||||||
|
// try parse js exception
|
||||||
|
jsErrorJSON = jsErrorJSON.Substring(Math.Max(0, jsErrorJSON.IndexOf("{")));
|
||||||
|
jsErrorJSON = jsErrorJSON.Substring(0, jsErrorJSON.LastIndexOf("}") + 1);
|
||||||
|
|
||||||
|
var evaluatedStackFrames = evaluatedScriptFunctions.Where(f => !string.IsNullOrEmpty(f))
|
||||||
|
.Select(f => new JavascriptStackFrame(f, "eval", 0, 0));
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(jsErrorJSON)) {
|
||||||
|
JsError jsError = null;
|
||||||
|
try {
|
||||||
|
jsError = DeserializeJSON<JsError>(jsErrorJSON);
|
||||||
|
} catch {
|
||||||
|
// ignore will throw error at the end
|
||||||
|
}
|
||||||
|
if (jsError != null) {
|
||||||
|
jsError.Name = jsError.Name ?? "";
|
||||||
|
jsError.Message = jsError.Message ?? "";
|
||||||
|
jsError.Stack = jsError.Stack ?? "";
|
||||||
|
var jsStack = jsError.Stack.Substring(Math.Min(jsError.Stack.Length, (jsError.Name + ": " + jsError.Message).Length))
|
||||||
|
.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
|
var parsedStack = new List<JavascriptStackFrame>();
|
||||||
|
|
||||||
|
parsedStack.AddRange(evaluatedStackFrames);
|
||||||
|
|
||||||
|
foreach (var stackFrame in jsStack) {
|
||||||
|
var frameParts = StackFrameRegex.Match(stackFrame);
|
||||||
|
if (frameParts.Success) {
|
||||||
|
parsedStack.Add(new JavascriptStackFrame(frameParts.Groups["method"].Value, frameParts.Groups["location"].Value, int.Parse(frameParts.Groups["column"].Value), int.Parse(frameParts.Groups["line"].Value)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JavascriptException(jsError.Name, jsError.Message, parsedStack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JavascriptException(exception.Message, evaluatedStackFrames, exception.StackTrace);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static bool IsInternalException(string exceptionMessage) {
|
||||||
|
return exceptionMessage.EndsWith(InternalException);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
110
WebViewControl.Avalonia/WebView.ResourceHandler.cs
Normal file
110
WebViewControl.Avalonia/WebView.ResourceHandler.cs
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xilium.CefGlue;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public sealed class ResourceHandler : Request {
|
||||||
|
|
||||||
|
private bool isAsync;
|
||||||
|
|
||||||
|
private readonly object syncRoot = new object();
|
||||||
|
|
||||||
|
internal ResourceHandler(CefRequest request, string urlOverride)
|
||||||
|
: base(request, urlOverride) {
|
||||||
|
}
|
||||||
|
|
||||||
|
internal AsyncResourceHandler Handler { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public bool Handled { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public Stream Response => Handler?.Response;
|
||||||
|
|
||||||
|
private AsyncResourceHandler GetOrCreateCefResourceHandler() {
|
||||||
|
if (Handler != null) {
|
||||||
|
return Handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (syncRoot) {
|
||||||
|
if (Handler != null) {
|
||||||
|
return Handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
var handler = new AsyncResourceHandler();
|
||||||
|
handler.Headers.Add("cache-control", "public, max-age=315360000");
|
||||||
|
Handler = handler;
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="handleResponse"></param>
|
||||||
|
public void BeginAsyncResponse(Action handleResponse) {
|
||||||
|
isAsync = true;
|
||||||
|
var handler = GetOrCreateCefResourceHandler();
|
||||||
|
Task.Run(() => {
|
||||||
|
handleResponse();
|
||||||
|
handler.Continue();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Continue() {
|
||||||
|
var handler = Handler;
|
||||||
|
Handled = handler != null && (handler.Response != null || !string.IsNullOrEmpty(handler.RedirectUrl));
|
||||||
|
if (isAsync || handler == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handler.Continue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filename"></param>
|
||||||
|
public void RespondWith(string filename) {
|
||||||
|
var fileStream = File.OpenRead(filename);
|
||||||
|
GetOrCreateCefResourceHandler().SetResponse(fileStream, ResourcesManager.GetMimeType(filename), autoDisposeStream: true);
|
||||||
|
Continue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="text"></param>
|
||||||
|
public void RespondWithText(string text) {
|
||||||
|
GetOrCreateCefResourceHandler().SetResponse(text);
|
||||||
|
Continue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream"></param>
|
||||||
|
/// <param name="extension"></param>
|
||||||
|
public void RespondWith(Stream stream, string extension = null) {
|
||||||
|
GetOrCreateCefResourceHandler().SetResponse(stream, ResourcesManager.GetExtensionMimeType(extension));
|
||||||
|
Continue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="url"></param>
|
||||||
|
public void Redirect(string url) {
|
||||||
|
GetOrCreateCefResourceHandler().RedirectTo(url);
|
||||||
|
Continue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
540
WebViewControl.Avalonia/WebView.cs
Normal file
540
WebViewControl.Avalonia/WebView.cs
Normal file
@ -0,0 +1,540 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.ExceptionServices;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xilium.CefGlue;
|
||||||
|
using Xilium.CefGlue.Common.Events;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
public delegate void BeforeNavigateEventHandler(Request request);
|
||||||
|
public delegate void BeforeResourceLoadEventHandler(ResourceHandler resourceHandler);
|
||||||
|
public delegate void NavigatedEventHandler(string url, string frameName);
|
||||||
|
public delegate void LoadFailedEventHandler(string url, int errorCode, string frameName);
|
||||||
|
public delegate void ResourceLoadFailedEventHandler(string resourceUrl);
|
||||||
|
public delegate void DownloadProgressChangedEventHandler(string resourcePath, long receivedBytes, long totalBytes);
|
||||||
|
public delegate void DownloadStatusChangedEventHandler(string resourcePath);
|
||||||
|
public delegate void JavascriptContextCreatedEventHandler(string frameName);
|
||||||
|
public delegate void UnhandledAsyncExceptionEventHandler(UnhandledAsyncExceptionEventArgs eventArgs);
|
||||||
|
public delegate void FilesDraggingEventHandler(string[] fileNames);
|
||||||
|
public delegate void TextDraggingEventHandler(string textContent);
|
||||||
|
|
||||||
|
internal delegate void JavacriptDialogShowEventHandler(string text, Action closeDialog);
|
||||||
|
internal delegate void JavascriptContextReleasedEventHandler(string frameName);
|
||||||
|
internal delegate void KeyPressedEventHandler(CefKeyEvent keyEvent, out bool handled);
|
||||||
|
|
||||||
|
public partial class WebView : IDisposable {
|
||||||
|
|
||||||
|
private const string MainFrameName = null;
|
||||||
|
|
||||||
|
// converts cef zoom percentage to css zoom (between 0 and 1)
|
||||||
|
// from https://code.google.com/p/chromium/issues/detail?id=71484
|
||||||
|
private const float PercentageToZoomFactor = 1.2f;
|
||||||
|
|
||||||
|
private object SyncRoot { get; } = new object();
|
||||||
|
|
||||||
|
private Dictionary<string, JavascriptExecutor> JsExecutors { get; } = new Dictionary<string, JavascriptExecutor>();
|
||||||
|
|
||||||
|
private CountdownEvent JavascriptPendingCalls { get; } = new CountdownEvent(1);
|
||||||
|
|
||||||
|
private ChromiumBrowser chromium;
|
||||||
|
private bool isDeveloperToolsOpened;
|
||||||
|
private Action pendingInitialization;
|
||||||
|
private string htmlToLoad;
|
||||||
|
private volatile bool isDisposing;
|
||||||
|
private IDisposable[] disposables;
|
||||||
|
|
||||||
|
private CancellationTokenSource AsyncCancellationTokenSource { get; } = new CancellationTokenSource();
|
||||||
|
|
||||||
|
public event Action WebViewInitialized;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// NOTE: This event is not executed on the UI thread. Do not use Dipatcher.Invoke (use BeginInvoke) while in the context of this event otherwise it may lead to a dead-lock.
|
||||||
|
/// </summary>
|
||||||
|
public event BeforeNavigateEventHandler BeforeNavigate;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// NOTE: This event is not executed on the UI thread. Do not use Dipatcher.Invoke (use BeginInvoke) while in the context of this event otherwise it may lead to a dead-lock.
|
||||||
|
/// </summary>
|
||||||
|
public event BeforeResourceLoadEventHandler BeforeResourceLoad;
|
||||||
|
|
||||||
|
public event NavigatedEventHandler Navigated;
|
||||||
|
public event LoadFailedEventHandler LoadFailed;
|
||||||
|
public event ResourceLoadFailedEventHandler ResourceLoadFailed;
|
||||||
|
public event DownloadProgressChangedEventHandler DownloadProgressChanged;
|
||||||
|
public event DownloadStatusChangedEventHandler DownloadCompleted;
|
||||||
|
public event DownloadStatusChangedEventHandler DownloadCancelled;
|
||||||
|
public event JavascriptContextCreatedEventHandler JavascriptContextCreated;
|
||||||
|
public event Action TitleChanged;
|
||||||
|
public event UnhandledAsyncExceptionEventHandler UnhandledAsyncException;
|
||||||
|
public event Action</*url*/string> PopupOpening;
|
||||||
|
|
||||||
|
internal event Action Disposed;
|
||||||
|
internal event JavascriptContextReleasedEventHandler JavascriptContextReleased;
|
||||||
|
internal event JavacriptDialogShowEventHandler JavacriptDialogShown;
|
||||||
|
internal event FilesDraggingEventHandler FilesDragging;
|
||||||
|
internal event TextDraggingEventHandler TextDragging;
|
||||||
|
internal event KeyPressedEventHandler KeyPressed;
|
||||||
|
|
||||||
|
private static int domainId = 1;
|
||||||
|
|
||||||
|
// cef maints same zoom level for all browser instances under the same domain
|
||||||
|
// having different domains will prevent synced zoom
|
||||||
|
private string CurrentDomainId { get; }
|
||||||
|
|
||||||
|
private string DefaultLocalUrl { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executed when a web view is initialized. Can be used to attach or configure the webview before it's ready.
|
||||||
|
/// </summary>
|
||||||
|
public static event Action<WebView> GlobalWebViewInitialized;
|
||||||
|
|
||||||
|
public static GlobalSettings Settings { get; } = new GlobalSettings();
|
||||||
|
|
||||||
|
public WebView() : this(false) { }
|
||||||
|
|
||||||
|
/// <param name="useSharedDomain">Shared domains means that the webview default domain will always be the same. When <paramref ref="useSharedDomain"/> is false a
|
||||||
|
/// unique domain is used for every webview.</param>
|
||||||
|
internal WebView(bool useSharedDomain) {
|
||||||
|
if (IsInDesignMode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useSharedDomain) {
|
||||||
|
CurrentDomainId = string.Empty;
|
||||||
|
} else {
|
||||||
|
CurrentDomainId = domainId.ToString();
|
||||||
|
domainId++;
|
||||||
|
}
|
||||||
|
|
||||||
|
DefaultLocalUrl = UrlHelper.DefaultLocalUrl.WithDomain(CurrentDomainId);
|
||||||
|
|
||||||
|
Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
private void Initialize() {
|
||||||
|
WebViewLoader.Initialize(Settings);
|
||||||
|
|
||||||
|
chromium = new ChromiumBrowser();
|
||||||
|
chromium.BrowserInitialized += OnWebViewBrowserInitialized;
|
||||||
|
chromium.LoadEnd += OnWebViewLoadEnd;
|
||||||
|
chromium.LoadError += OnWebViewLoadError;
|
||||||
|
chromium.TitleChanged += delegate { TitleChanged?.Invoke(); };
|
||||||
|
chromium.JavascriptContextCreated += OnJavascriptContextCreated;
|
||||||
|
chromium.JavascriptContextReleased += OnJavascriptContextReleased;
|
||||||
|
chromium.JavascriptUncaughException += OnJavascriptUncaughException;
|
||||||
|
chromium.UnhandledException += (o, e) => ForwardUnhandledAsyncException(e.Exception);
|
||||||
|
|
||||||
|
chromium.RequestHandler = new InternalRequestHandler(this);
|
||||||
|
chromium.LifeSpanHandler = new InternalLifeSpanHandler(this);
|
||||||
|
chromium.ContextMenuHandler = new InternalContextMenuHandler(this);
|
||||||
|
chromium.DialogHandler = new InternalDialogHandler(this);
|
||||||
|
chromium.DownloadHandler = new InternalDownloadHandler(this);
|
||||||
|
chromium.JSDialogHandler = new InternalJsDialogHandler(this);
|
||||||
|
chromium.DragHandler = new InternalDragHandler(this);
|
||||||
|
chromium.KeyboardHandler = new InternalKeyboardHandler(this);
|
||||||
|
|
||||||
|
if (!Settings.OsrEnabled) {
|
||||||
|
// having the handler (by default) seems to cause some focus troubles, enable only osr disabled
|
||||||
|
chromium.FocusHandler = new InternalFocusHandler(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
EditCommands = new EditCommands(chromium);
|
||||||
|
|
||||||
|
disposables = new IDisposable[] {
|
||||||
|
AsyncCancellationTokenSource,
|
||||||
|
chromium // browser should be the last
|
||||||
|
};
|
||||||
|
|
||||||
|
ExtraInitialize();
|
||||||
|
|
||||||
|
GlobalWebViewInitialized?.Invoke(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
partial void ExtraInitialize();
|
||||||
|
|
||||||
|
~WebView() {
|
||||||
|
InnerDispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() {
|
||||||
|
InnerDispose();
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InnerDispose() {
|
||||||
|
lock (SyncRoot) {
|
||||||
|
if (isDisposing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
isDisposing = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var disposed = false;
|
||||||
|
|
||||||
|
void InternalDispose() {
|
||||||
|
if (disposed) {
|
||||||
|
return; // bail-out
|
||||||
|
}
|
||||||
|
|
||||||
|
disposed = true;
|
||||||
|
|
||||||
|
AsyncCancellationTokenSource?.Cancel();
|
||||||
|
|
||||||
|
chromium.JavascriptContextCreated -= OnJavascriptContextCreated;
|
||||||
|
chromium.JavascriptContextReleased -= OnJavascriptContextReleased;
|
||||||
|
|
||||||
|
WebViewInitialized = null;
|
||||||
|
BeforeNavigate = null;
|
||||||
|
BeforeResourceLoad = null;
|
||||||
|
Navigated = null;
|
||||||
|
LoadFailed = null;
|
||||||
|
DownloadProgressChanged = null;
|
||||||
|
DownloadCompleted = null;
|
||||||
|
DownloadCancelled = null;
|
||||||
|
JavascriptContextCreated = null;
|
||||||
|
TitleChanged = null;
|
||||||
|
UnhandledAsyncException = null;
|
||||||
|
JavascriptContextReleased = null;
|
||||||
|
|
||||||
|
// dispose the js executors before the browser to prevent (the browser) from throwing cancellation exceptions
|
||||||
|
DisposeJavascriptExecutors();
|
||||||
|
|
||||||
|
foreach (var disposable in disposables) {
|
||||||
|
disposable?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
Disposed?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JavascriptPendingCalls?.CurrentCount > 1) {
|
||||||
|
// avoid dead-lock, wait for all pending calls to finish
|
||||||
|
Task.Run(() => {
|
||||||
|
JavascriptPendingCalls?.Signal(); // remove dummy entry
|
||||||
|
JavascriptPendingCalls?.Wait();
|
||||||
|
InternalDispose();
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
InternalDispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal ChromiumBrowser UnderlyingBrowser => chromium;
|
||||||
|
|
||||||
|
internal bool IsDisposing => isDisposing;
|
||||||
|
|
||||||
|
public bool AllowDeveloperTools { get; set; }
|
||||||
|
|
||||||
|
private string InternalAddress {
|
||||||
|
get { return chromium.Address; }
|
||||||
|
set {
|
||||||
|
if (chromium.Address != value) {
|
||||||
|
LoadUrl(value, MainFrameName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public EditCommands EditCommands { get; private set; }
|
||||||
|
|
||||||
|
public bool CanGoBack => chromium.CanGoBack;
|
||||||
|
|
||||||
|
public bool CanGoForward => chromium.CanGoForward;
|
||||||
|
|
||||||
|
public void GoBack() => chromium.GoBack();
|
||||||
|
|
||||||
|
public void GoForward() => chromium.GoForward();
|
||||||
|
|
||||||
|
public bool IsSecurityDisabled { get; set; }
|
||||||
|
|
||||||
|
public bool IgnoreCertificateErrors { get; set; }
|
||||||
|
|
||||||
|
public bool IsHistoryDisabled { get; set; }
|
||||||
|
|
||||||
|
public TimeSpan? DefaultScriptsExecutionTimeout { get; set; }
|
||||||
|
|
||||||
|
public bool DisableBuiltinContextMenus { get; set; }
|
||||||
|
|
||||||
|
public bool DisableFileDialogs { get; set; }
|
||||||
|
|
||||||
|
public bool IsBrowserInitialized => chromium.IsBrowserInitialized;
|
||||||
|
|
||||||
|
public bool IsJavascriptEngineInitialized => chromium.IsJavascriptEngineInitialized;
|
||||||
|
|
||||||
|
public ProxyAuthentication ProxyAuthentication { get; set; }
|
||||||
|
|
||||||
|
public bool IgnoreMissingResources { get; set; }
|
||||||
|
|
||||||
|
public string Title => chromium.Title;
|
||||||
|
|
||||||
|
public double ZoomPercentage {
|
||||||
|
get { return Math.Pow(PercentageToZoomFactor, chromium.ZoomLevel); }
|
||||||
|
set { ExecuteWhenInitialized(() => chromium.ZoomLevel = Math.Log(value, PercentageToZoomFactor)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowDeveloperTools() {
|
||||||
|
ExecuteWhenInitialized(() => {
|
||||||
|
chromium.ShowDeveloperTools();
|
||||||
|
isDeveloperToolsOpened = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CloseDeveloperTools() {
|
||||||
|
if (isDeveloperToolsOpened) {
|
||||||
|
chromium.CloseDeveloperTools();
|
||||||
|
isDeveloperToolsOpened = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ToggleDeveloperTools() {
|
||||||
|
if (isDeveloperToolsOpened) {
|
||||||
|
CloseDeveloperTools();
|
||||||
|
} else {
|
||||||
|
ShowDeveloperTools();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LoadUrl(string address, string frameName = MainFrameName) {
|
||||||
|
if (this.IsMainFrame(frameName) && address != DefaultLocalUrl) {
|
||||||
|
htmlToLoad = null;
|
||||||
|
}
|
||||||
|
if (this.IsMainFrame(frameName)) {
|
||||||
|
chromium.Address = address;
|
||||||
|
} else {
|
||||||
|
this.GetFrame(frameName)?.LoadUrl(address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LoadResource(ResourceUrl resourceUrl, string frameName = MainFrameName) {
|
||||||
|
LoadUrl(resourceUrl.WithDomain(CurrentDomainId), frameName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LoadHtml(string html) {
|
||||||
|
htmlToLoad = html;
|
||||||
|
LoadUrl(DefaultLocalUrl, MainFrameName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reload(bool ignoreCache = false) {
|
||||||
|
if (IsBrowserInitialized && !chromium.IsLoading) {
|
||||||
|
chromium.Reload(ignoreCache);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnWebViewBrowserInitialized() {
|
||||||
|
if (IsBrowserInitialized) {
|
||||||
|
AsyncExecuteInUI(() => {
|
||||||
|
lock (SyncRoot) {
|
||||||
|
if (pendingInitialization != null) {
|
||||||
|
pendingInitialization();
|
||||||
|
pendingInitialization = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WebViewInitialized?.Invoke();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnWebViewLoadEnd(object sender, LoadEndEventArgs e) {
|
||||||
|
var url = e.Frame.Url;
|
||||||
|
if (UrlHelper.IsChromeInternalUrl(url)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.Frame.IsMain && url == DefaultLocalUrl) {
|
||||||
|
// finished loading local url, discard html
|
||||||
|
htmlToLoad = null;
|
||||||
|
} else {
|
||||||
|
// js context created event is not called for child frames
|
||||||
|
HandleJavascriptContextCreated(e.Frame);
|
||||||
|
}
|
||||||
|
if (Navigated != null) {
|
||||||
|
// store frame name and url beforehand (cannot do it later, since frame might be disposed)
|
||||||
|
var frameName = e.Frame.Name;
|
||||||
|
AsyncExecuteInUI(() => Navigated?.Invoke(url, frameName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnWebViewLoadError(object sender, LoadErrorEventArgs e) {
|
||||||
|
var url = e.FailedUrl;
|
||||||
|
if (UrlHelper.IsChromeInternalUrl(url)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.Frame.IsMain && url == DefaultLocalUrl) {
|
||||||
|
// failed loading default local url, discard html
|
||||||
|
htmlToLoad = null;
|
||||||
|
}
|
||||||
|
if (e.ErrorCode != CefErrorCode.Aborted && LoadFailed != null) {
|
||||||
|
var frameName = e.Frame.Name; // store frame name beforehand (cannot do it later, since frame might be disposed)
|
||||||
|
// ignore aborts, to prevent situations where we try to load an address inside Load failed handler (and its aborted)
|
||||||
|
AsyncExecuteInUI(() => LoadFailed?.Invoke(url, (int)e.ErrorCode, frameName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExecuteWhenInitialized(Action action) {
|
||||||
|
if (IsBrowserInitialized) {
|
||||||
|
action();
|
||||||
|
} else {
|
||||||
|
lock (SyncRoot) {
|
||||||
|
if (IsBrowserInitialized) {
|
||||||
|
action();
|
||||||
|
} else {
|
||||||
|
pendingInitialization += action;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[DebuggerNonUserCode]
|
||||||
|
private void ExecuteWithAsyncErrorHandling(Action action) => ExecuteWithAsyncErrorHandlingOnFrame(action, null);
|
||||||
|
|
||||||
|
[DebuggerNonUserCode]
|
||||||
|
private void ExecuteWithAsyncErrorHandlingOnFrame(Action action, string frameName) {
|
||||||
|
try {
|
||||||
|
action();
|
||||||
|
} catch (Exception e) {
|
||||||
|
ForwardUnhandledAsyncException(e, frameName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ForwardUnhandledAsyncException(Exception e, string frameName = null) {
|
||||||
|
if (isDisposing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var handled = false;
|
||||||
|
|
||||||
|
var unhandledAsyncException = UnhandledAsyncException;
|
||||||
|
if (unhandledAsyncException != null) {
|
||||||
|
var eventArgs = new UnhandledAsyncExceptionEventArgs(e, frameName);
|
||||||
|
unhandledAsyncException(eventArgs);
|
||||||
|
handled = eventArgs.Handled;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!handled) {
|
||||||
|
ForwardException(ExceptionDispatchInfo.Capture(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private JavascriptExecutor GetJavascriptExecutor(string frameName) {
|
||||||
|
if (isDisposing) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (JsExecutors) {
|
||||||
|
if (isDisposing) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var frameNameForIndex = frameName ?? "";
|
||||||
|
if (!JsExecutors.TryGetValue(frameNameForIndex, out var jsExecutor)) {
|
||||||
|
jsExecutor = new JavascriptExecutor(this, this.GetFrame(frameName));
|
||||||
|
JsExecutors.Add(frameNameForIndex, jsExecutor);
|
||||||
|
}
|
||||||
|
return jsExecutor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnJavascriptContextCreated(object sender, JavascriptContextLifetimeEventArgs e) => HandleJavascriptContextCreated(e.Frame);
|
||||||
|
|
||||||
|
private void HandleJavascriptContextCreated(CefFrame frame) {
|
||||||
|
if (isDisposing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExecuteWithAsyncErrorHandling(() => {
|
||||||
|
if (UrlHelper.IsChromeInternalUrl(frame.Url)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (JsExecutors) {
|
||||||
|
if (isDisposing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var frameName = frame.Name;
|
||||||
|
|
||||||
|
if (this.IsMainFrame(frameName)) {
|
||||||
|
// when a new main frame in created, dispose all running executors -> since they should not be valid anymore
|
||||||
|
// all child iframes were gone
|
||||||
|
DisposeJavascriptExecutors(JsExecutors.Where(je => !je.Value.IsValid).Select(je => je.Key).ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
var jsExecutor = GetJavascriptExecutor(frameName);
|
||||||
|
jsExecutor?.StartFlush(frame);
|
||||||
|
|
||||||
|
JavascriptContextCreated?.Invoke(frameName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnJavascriptContextReleased(object sender, JavascriptContextLifetimeEventArgs e) {
|
||||||
|
ExecuteWithAsyncErrorHandling(() => {
|
||||||
|
if (UrlHelper.IsChromeInternalUrl(e.Frame.Url)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var frameName = e.Frame.Name;
|
||||||
|
|
||||||
|
lock (JsExecutors) {
|
||||||
|
DisposeJavascriptExecutors(new[] { frameName });
|
||||||
|
}
|
||||||
|
|
||||||
|
JavascriptContextReleased?.Invoke(frameName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnJavascriptUncaughException(object sender, JavascriptUncaughtExceptionEventArgs e) {
|
||||||
|
if (JavascriptExecutor.IsInternalException(e.Message)) {
|
||||||
|
// ignore internal exceptions, they will be handled by the EvaluateScript caller
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var javascriptException = new JavascriptException(e.Message, e.StackFrames);
|
||||||
|
ForwardUnhandledAsyncException(javascriptException, e.Frame.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleRenderProcessCrashed() => DisposeJavascriptExecutors();
|
||||||
|
|
||||||
|
private void DisposeJavascriptExecutors() {
|
||||||
|
lock (JsExecutors) {
|
||||||
|
DisposeJavascriptExecutors(JsExecutors.Keys.ToArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DisposeJavascriptExecutors(string[] executorsKeys) {
|
||||||
|
foreach (var executorKey in executorsKeys) {
|
||||||
|
var indexedExecutorKey = executorKey ?? "";
|
||||||
|
if (JsExecutors.TryGetValue(indexedExecutorKey, out var executor)) {
|
||||||
|
executor.Dispose();
|
||||||
|
JsExecutors.Remove(indexedExecutorKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual string GetRequestUrl(string url, ResourceType resourceType) => url;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when the webview has received focus.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual void OnGotFocus() { }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when the webview is about to loose focus. For instance, if
|
||||||
|
/// focus was on the last HTML element and the user pressed the TAB key.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual void OnLostFocus() { }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enables or disable support for accessibility tools like screen readers.
|
||||||
|
/// </summary>
|
||||||
|
public void EnableAccessibility(bool enable) =>
|
||||||
|
ExecuteWhenInitialized(() => this.SetAccessibilityState(enable ? CefState.Enabled : CefState.Disabled));
|
||||||
|
}
|
||||||
|
}
|
36
WebViewControl.Avalonia/WebViewControl.Avalonia.csproj
Normal file
36
WebViewControl.Avalonia/WebViewControl.Avalonia.csproj
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<RootNamespace>WebViewControl</RootNamespace>
|
||||||
|
<AssemblyTitle>WebViewControl Avalonia</AssemblyTitle>
|
||||||
|
<Description>WebViewControl for Avalonia powered by CefGlue</Description>
|
||||||
|
<Configuration></Configuration>
|
||||||
|
<Company></Company>
|
||||||
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
|
<PackageId>WebViewControl-Avalonia$(PackageSuffix)</PackageId>
|
||||||
|
<Configurations>Debug;Release;ReleaseAvalonia;ReleaseWPF;ReleaseAvaloniaRemoteDebugSupport</Configurations>
|
||||||
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)' == 'ReleaseAvaloniaRemoteDebugSupport'">
|
||||||
|
<DefineConstants>REMOTE_DEBUG_SUPPORT</DefineConstants>
|
||||||
|
<PackageId>WebViewControl-Avalonia-RemoteDebugSupport$(PackageSuffix)</PackageId>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)' == 'ReleaseAvalonia'">
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<!--When we upload Avalonia here, we should increase the version of both WebViewControl.Avalonia as well as ReactViewControl.Avalonia-->
|
||||||
|
<PackageReference Include="Avalonia" Version="11.0.10" />
|
||||||
|
<PackageReference Include="CefGlue.Avalonia" Version="120.6099.205">
|
||||||
|
<PrivateAssets>None</PrivateAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.CSharp" Version="4.5.0" />
|
||||||
|
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
14
WebViewControl.Avalonia/WebViewControl.nuspec
Normal file
14
WebViewControl.Avalonia/WebViewControl.nuspec
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
|
||||||
|
<metadata>
|
||||||
|
<id>$id$</id>
|
||||||
|
<version>$version$</version>
|
||||||
|
<title>$title$</title>
|
||||||
|
<authors>$authors$</authors>
|
||||||
|
<owners>OutSystems</owners>
|
||||||
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
|
<description>$description$</description>
|
||||||
|
<copyright>$copyright$</copyright>
|
||||||
|
<projectUrl>$packageProjectUrl$</projectUrl>
|
||||||
|
</metadata>
|
||||||
|
</package>
|
78
WebViewControl.Avalonia/WebViewLoader.cs
Normal file
78
WebViewControl.Avalonia/WebViewLoader.cs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using Xilium.CefGlue;
|
||||||
|
using Xilium.CefGlue.Common;
|
||||||
|
using Xilium.CefGlue.Common.Shared;
|
||||||
|
|
||||||
|
namespace WebViewControl {
|
||||||
|
|
||||||
|
internal static class WebViewLoader {
|
||||||
|
|
||||||
|
private static string[] CustomSchemes { get; } = new[] {
|
||||||
|
ResourceUrl.LocalScheme,
|
||||||
|
ResourceUrl.EmbeddedScheme,
|
||||||
|
ResourceUrl.CustomScheme,
|
||||||
|
Uri.UriSchemeHttp,
|
||||||
|
Uri.UriSchemeHttps
|
||||||
|
};
|
||||||
|
|
||||||
|
private static GlobalSettings globalSettings;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
public static void Initialize(GlobalSettings settings) {
|
||||||
|
if (CefRuntimeLoader.IsLoaded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
globalSettings = settings;
|
||||||
|
|
||||||
|
var cefSettings = new CefSettings {
|
||||||
|
LogSeverity = string.IsNullOrWhiteSpace(settings.LogFile) ? CefLogSeverity.Disable : (settings.EnableErrorLogOnly ? CefLogSeverity.Error : CefLogSeverity.Verbose),
|
||||||
|
LogFile = settings.LogFile,
|
||||||
|
UncaughtExceptionStackSize = 100, // enable stack capture
|
||||||
|
CachePath = settings.CachePath, // enable cache for external resources to speedup loading
|
||||||
|
WindowlessRenderingEnabled = settings.OsrEnabled,
|
||||||
|
RemoteDebuggingPort = settings.GetRemoteDebuggingPort(),
|
||||||
|
UserAgent = settings.UserAgent
|
||||||
|
};
|
||||||
|
|
||||||
|
var customSchemes = CustomSchemes.Select(s => new CustomScheme() {
|
||||||
|
SchemeName = s,
|
||||||
|
SchemeHandlerFactory = new SchemeHandlerFactory()
|
||||||
|
}).ToArray();
|
||||||
|
|
||||||
|
settings.AddCommandLineSwitch("enable-experimental-web-platform-features", null);
|
||||||
|
|
||||||
|
CefRuntimeLoader.Initialize(settings: cefSettings, flags: settings.CommandLineSwitches.ToArray(), customSchemes: customSchemes);
|
||||||
|
|
||||||
|
AppDomain.CurrentDomain.ProcessExit += delegate { Cleanup(); };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Release all resources and shutdown web view
|
||||||
|
/// </summary>
|
||||||
|
[DebuggerNonUserCode]
|
||||||
|
public static void Cleanup() {
|
||||||
|
CefRuntime.Shutdown(); // must shutdown cef to free cache files (so that cleanup is able to delete files)
|
||||||
|
|
||||||
|
if (globalSettings.PersistCache) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var dirInfo = new DirectoryInfo(globalSettings.CachePath);
|
||||||
|
if (dirInfo.Exists) {
|
||||||
|
dirInfo.Delete(true);
|
||||||
|
}
|
||||||
|
} catch (UnauthorizedAccessException) {
|
||||||
|
// ignore
|
||||||
|
} catch (IOException) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
98
detect.device/DeviceClient.cs
Normal file
98
detect.device/DeviceClient.cs
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
using detect.device.Utils;
|
||||||
|
|
||||||
|
namespace detect.device;
|
||||||
|
|
||||||
|
public class DeviceEvent
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public IDictionary<string, object> Result { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DeviceClientRequestBuilder
|
||||||
|
{
|
||||||
|
private readonly Dictionary<string, object> _params = new();
|
||||||
|
public readonly string RequestId = Guid.NewGuid().ToString();
|
||||||
|
private string? _type;
|
||||||
|
private string? _component;
|
||||||
|
private string? _method;
|
||||||
|
|
||||||
|
public static DeviceClientRequestBuilder Create()
|
||||||
|
{
|
||||||
|
return new DeviceClientRequestBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DeviceClientRequestBuilder WithType(string type)
|
||||||
|
{
|
||||||
|
this._type = type;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DeviceClientRequestBuilder WithComponent(string component)
|
||||||
|
{
|
||||||
|
this._component = component;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DeviceClientRequestBuilder WithMethod(string method)
|
||||||
|
{
|
||||||
|
this._method = method;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DeviceClientRequestBuilder WithParam(string name, string value)
|
||||||
|
{
|
||||||
|
this._params.TryAdd(name, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Build()
|
||||||
|
{
|
||||||
|
Dictionary<string, object?> request = new()
|
||||||
|
{
|
||||||
|
["requestId"] = RequestId,
|
||||||
|
["type"] = _type,
|
||||||
|
["component"] = _component,
|
||||||
|
["method"] = _method,
|
||||||
|
["params"] = _params
|
||||||
|
};
|
||||||
|
return JsonUtil.SerializeObject(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DeviceClientResponse<TResult>
|
||||||
|
{
|
||||||
|
public DeviceClientResponse(int code, string message)
|
||||||
|
{
|
||||||
|
Code = code;
|
||||||
|
Message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string? RequestId { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 回复类型,service,task,event
|
||||||
|
/// </summary>
|
||||||
|
public string Type { get; set; } = "service";
|
||||||
|
public int Code { get; set; }
|
||||||
|
public string Message { get; set; }
|
||||||
|
public double? Duration { get; set; }
|
||||||
|
public TResult? Result { get; set; }
|
||||||
|
public bool IsSuccess => this.Code == 0;
|
||||||
|
public bool IsFailed => this.Code != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IDeviceClient
|
||||||
|
{
|
||||||
|
public event EventHandler<DeviceEvent> DeviceEvent;
|
||||||
|
|
||||||
|
public bool Connected();
|
||||||
|
|
||||||
|
public bool ConnectAsync();
|
||||||
|
|
||||||
|
public bool DisConnectAsync();
|
||||||
|
|
||||||
|
public void PublishEvent(DeviceEvent deviceEvent);
|
||||||
|
|
||||||
|
public Task<DeviceClientResponse<T>> RequestAction<T>(DeviceClientRequestBuilder builder);
|
||||||
|
|
||||||
|
public DeviceClientResponse<object> RequestActionSync(DeviceClientRequestBuilder builder);
|
||||||
|
}
|
131
detect.device/DeviceClientSocket.cs
Normal file
131
detect.device/DeviceClientSocket.cs
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Text;
|
||||||
|
using detect.device.Utils;
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
|
namespace detect.device;
|
||||||
|
using TcpClient = NetCoreServer.TcpClient;
|
||||||
|
|
||||||
|
public class DeviceClientSocket : TcpClient, IDeviceClient
|
||||||
|
{
|
||||||
|
private bool _stop;
|
||||||
|
private readonly ConcurrentDictionary<string, string> _deviceRequests = new();
|
||||||
|
public event EventHandler<DeviceEvent>? DeviceEvent;
|
||||||
|
|
||||||
|
public DeviceClientSocket(string address, int port = 13000) : base(address, port) {}
|
||||||
|
|
||||||
|
public bool Connected()
|
||||||
|
{
|
||||||
|
return IsConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool DisConnectAsync()
|
||||||
|
{
|
||||||
|
_stop = true;
|
||||||
|
var flag = DisconnectAsync();
|
||||||
|
while (IsConnected)
|
||||||
|
Thread.Yield();
|
||||||
|
return flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public new bool ConnectAsync()
|
||||||
|
{
|
||||||
|
return base.ConnectAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnConnecting()
|
||||||
|
{
|
||||||
|
Log.Information("({address}:{port})OnConnecting...", Address, Port);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnConnected()
|
||||||
|
{
|
||||||
|
Log.Information("({address}:{port})OnConnected.", Address, Port);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnDisconnected()
|
||||||
|
{
|
||||||
|
Log.Information("({address}:{port})OnDisconnected", Address, Port);
|
||||||
|
Thread.Sleep(1000);
|
||||||
|
if (!_stop)
|
||||||
|
ConnectAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnError(SocketError error)
|
||||||
|
{
|
||||||
|
Log.Error("({address}:{port})OnError:{error}", Address, Port, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PublishEvent(DeviceEvent deviceEvent)
|
||||||
|
{
|
||||||
|
DeviceEvent?.Invoke(this, deviceEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnReceived(byte[] buffer, long offset, long size)
|
||||||
|
{
|
||||||
|
var received = Encoding.UTF8.GetString(buffer, (int)offset, (int)size);
|
||||||
|
Log.Debug("({address}:{port})received: {content}", Address, Port, received);
|
||||||
|
var jsons = received.Split("}{");
|
||||||
|
if (jsons.Length > 1)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < jsons.Length; i++)
|
||||||
|
{
|
||||||
|
var json = jsons[i];
|
||||||
|
var content = "{" + json + "}";
|
||||||
|
if (i == 0)
|
||||||
|
{
|
||||||
|
content = json + "}";
|
||||||
|
} else if (i == jsons.Length - 1)
|
||||||
|
{
|
||||||
|
content = "{" + json;
|
||||||
|
}
|
||||||
|
ProcessReceived(content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ProcessReceived(received);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessReceived(string content)
|
||||||
|
{
|
||||||
|
var result = JsonUtil.DeserializeObject<DeviceClientResponse<object>>(content);
|
||||||
|
if ("event".Equals(result.Type))
|
||||||
|
{
|
||||||
|
var deviceEvent =
|
||||||
|
JsonUtil.DeserializeObject<DeviceEvent>(JsonUtil.SerializeObject(content));
|
||||||
|
DeviceEvent?.Invoke(this, deviceEvent);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_deviceRequests.TryAdd(result.RequestId!, content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<DeviceClientResponse<T>> RequestAction<T>(DeviceClientRequestBuilder builder)
|
||||||
|
{
|
||||||
|
var requestId = builder.RequestId;
|
||||||
|
_deviceRequests.TryRemove(requestId, out _);
|
||||||
|
Send(builder.Build());
|
||||||
|
var task = Task.Run(() =>
|
||||||
|
{
|
||||||
|
_deviceRequests.TryGetValue(requestId, out var result);
|
||||||
|
while (string.IsNullOrEmpty(result))
|
||||||
|
{
|
||||||
|
Thread.Sleep(100);
|
||||||
|
_deviceRequests.TryGetValue(requestId, out result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
var result = await task;
|
||||||
|
_deviceRequests.TryRemove(requestId, out _);
|
||||||
|
return JsonUtil.DeserializeObject<DeviceClientResponse<T>>(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DeviceClientResponse<object> RequestActionSync(DeviceClientRequestBuilder builder)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
26
detect.device/Utils/FileUtil.cs
Normal file
26
detect.device/Utils/FileUtil.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
namespace detect.device.Utils;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
public static class FileUtil
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 下载文件到指定路径
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="url">文件的 URL</param>
|
||||||
|
/// <param name="destinationPath">下载文件的保存路径</param>
|
||||||
|
/// <returns>表示异步操作的 Task</returns>
|
||||||
|
public static async Task DownloadFileAsync(string url, string destinationPath)
|
||||||
|
{
|
||||||
|
using HttpClient client = new HttpClient();
|
||||||
|
using HttpResponseMessage response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
|
||||||
|
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
|
await using FileStream fs = new FileStream(destinationPath, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||||
|
await response.Content.CopyToAsync(fs);
|
||||||
|
}
|
||||||
|
}
|
110
detect.device/Utils/JsonUtil.cs
Normal file
110
detect.device/Utils/JsonUtil.cs
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
using System.Net;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using Newtonsoft.Json.Serialization;
|
||||||
|
|
||||||
|
namespace detect.device.Utils;
|
||||||
|
|
||||||
|
public class IpAddressConverter : JsonConverter
|
||||||
|
{
|
||||||
|
public override bool CanConvert(Type objectType)
|
||||||
|
{
|
||||||
|
return (objectType == typeof(IPAddress));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
writer.WriteValue(value.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
return IPAddress.Parse((string)reader.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class IpEndPointConverter : JsonConverter
|
||||||
|
{
|
||||||
|
public override bool CanConvert(Type objectType)
|
||||||
|
{
|
||||||
|
return (objectType == typeof(IPEndPoint));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
var ep = (IPEndPoint)value;
|
||||||
|
var jo = new JObject
|
||||||
|
{
|
||||||
|
{ "Address", JToken.FromObject(ep.Address, serializer) },
|
||||||
|
{ "Port", ep.Port }
|
||||||
|
};
|
||||||
|
jo.WriteTo(writer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
var jo = JObject.Load(reader);
|
||||||
|
var address = jo["Address"]?.ToObject<IPAddress>(serializer);
|
||||||
|
var port = (int)jo["Port"]!;
|
||||||
|
return new IPEndPoint(address!, port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class JsonUtil
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 将对象序列化为json字符串
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj"></param>
|
||||||
|
/// <param name="formatting"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string SerializeObject(object obj, Formatting formatting = Formatting.None)
|
||||||
|
{
|
||||||
|
switch (obj)
|
||||||
|
{
|
||||||
|
case null:
|
||||||
|
return null;
|
||||||
|
case string s:
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
var settings = new JsonSerializerSettings
|
||||||
|
{
|
||||||
|
ContractResolver = new CamelCasePropertyNamesContractResolver()
|
||||||
|
};
|
||||||
|
settings.Converters.Add(new IpAddressConverter());
|
||||||
|
settings.Converters.Add(new IpEndPointConverter());
|
||||||
|
settings.Formatting = formatting;
|
||||||
|
return JsonConvert.SerializeObject(obj, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将json字符串解析为对象
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="jsonStr"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static T DeserializeObject<T>(string jsonStr)
|
||||||
|
{
|
||||||
|
var settings = new JsonSerializerSettings();
|
||||||
|
settings.ContractResolver = new CamelCasePropertyNamesContractResolver();
|
||||||
|
settings.Converters.Add(new IpAddressConverter());
|
||||||
|
settings.Converters.Add(new IpEndPointConverter());
|
||||||
|
return JsonConvert.DeserializeObject<T>(jsonStr, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将json字符串解析为对象
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="jsonStr"></param>
|
||||||
|
/// <param name="type"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static object DeserializeObject(string jsonStr, Type type)
|
||||||
|
{
|
||||||
|
var settings = new JsonSerializerSettings();
|
||||||
|
settings.Converters.Add(new IpAddressConverter());
|
||||||
|
settings.Converters.Add(new IpEndPointConverter());
|
||||||
|
return JsonConvert.DeserializeObject(jsonStr, type, settings);
|
||||||
|
}
|
||||||
|
}
|
21
detect.device/detect.device.csproj
Normal file
21
detect.device/detect.device.csproj
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="NetCoreServer" Version="8.0.7" />
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
|
<PackageReference Include="Serilog" Version="4.0.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="xwd.utils">
|
||||||
|
<HintPath>..\detect.gui\Libs\xwd.utils.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
114
detect.gui/Api/ApiHelper.cs
Normal file
114
detect.gui/Api/ApiHelper.cs
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Xml;
|
||||||
|
using detect.gui.Models;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Serialization;
|
||||||
|
using RestSharp;
|
||||||
|
using Serilog;
|
||||||
|
using xwd.utils;
|
||||||
|
|
||||||
|
namespace detect.gui.Api;
|
||||||
|
|
||||||
|
public class ApiHelper
|
||||||
|
{
|
||||||
|
private static readonly string BaseUrl = AppSettingsManager.Manager().GetString("ApiUrl");
|
||||||
|
private const string ContentType = "application/json";
|
||||||
|
public static string? Token { get; set; }
|
||||||
|
|
||||||
|
public static ApiResponse<T>? Post<T>(string url, string? param, bool useToken = true)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var request = new RestRequest(string.Concat(BaseUrl, url), Method.Post)
|
||||||
|
{
|
||||||
|
Timeout = TimeSpan.FromMilliseconds(10000)
|
||||||
|
};
|
||||||
|
if (useToken && !string.IsNullOrEmpty(Token))
|
||||||
|
request.AddHeader("Authorization", Token);
|
||||||
|
request.AddHeader("Cache-Control", "no-cache");
|
||||||
|
if (!string.IsNullOrEmpty(param))
|
||||||
|
request.AddParameter(ContentType, param, ParameterType.RequestBody);
|
||||||
|
var client = new RestClient();
|
||||||
|
var response = client.Execute(request);
|
||||||
|
if (string.IsNullOrEmpty(response.Content))
|
||||||
|
return new ApiResponse<T>(-1100, "接口调用失败");
|
||||||
|
return JsonConvert.DeserializeObject<ApiResponse<T>>(response.Content, new JsonSerializerSettings
|
||||||
|
{
|
||||||
|
MissingMemberHandling = MissingMemberHandling.Ignore,
|
||||||
|
ContractResolver = new CamelCasePropertyNamesContractResolver()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
// ReSharper disable once TemplateIsNotCompileTimeConstantProblem
|
||||||
|
Log.Error(e.Message);
|
||||||
|
return new ApiResponse<T>(-1100, "接口调用失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ApiResponse<T>? Get<T>(string url, string param)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var request = new RestRequest(string.Concat(BaseUrl, url))
|
||||||
|
{
|
||||||
|
Timeout = TimeSpan.FromMilliseconds(10000)
|
||||||
|
};
|
||||||
|
if (!string.IsNullOrEmpty(Token))
|
||||||
|
request.AddHeader("Authorization", Token);
|
||||||
|
request.AddHeader("Cache-Control", "no-cache");
|
||||||
|
var dict = JsonConvert.DeserializeObject<Dictionary<string, string>>(param);
|
||||||
|
if (dict != null)
|
||||||
|
{
|
||||||
|
foreach (var kvp in dict)
|
||||||
|
{
|
||||||
|
request.AddParameter(kvp.Key, kvp.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var client = new RestClient();
|
||||||
|
var response = client.Execute(request);
|
||||||
|
if (string.IsNullOrEmpty(response.Content))
|
||||||
|
return new ApiResponse<T>(-1100, "接口调用失败");
|
||||||
|
return JsonConvert.DeserializeObject<ApiResponse<T>>(response.Content, new JsonSerializerSettings
|
||||||
|
{
|
||||||
|
MissingMemberHandling = MissingMemberHandling.Ignore,
|
||||||
|
ContractResolver = new CamelCasePropertyNamesContractResolver()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
// ReSharper disable once TemplateIsNotCompileTimeConstantProblem
|
||||||
|
Log.Error(e.Message);
|
||||||
|
return new ApiResponse<T>(-1100, "接口调用失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<XmlDocument?> Upload(string filename, string filePath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var client = new RestClient(BaseUrl);
|
||||||
|
var request = new RestRequest($"/v1/system/upload?filePath={filePath}", Method.Post)
|
||||||
|
{
|
||||||
|
Timeout = TimeSpan.FromMilliseconds(10000)
|
||||||
|
};
|
||||||
|
if (!string.IsNullOrEmpty(Token))
|
||||||
|
request.AddHeader("Authorization", Token);
|
||||||
|
request.AddHeader("Cache-Control", "no-cache");
|
||||||
|
request.AddFile("file", filename);
|
||||||
|
var response = await client.ExecuteAsync(request);
|
||||||
|
return string.IsNullOrEmpty(response.Content)
|
||||||
|
? null
|
||||||
|
: JsonConvert.DeserializeXmlNode(response.Content, "root");
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
// ReSharper disable once TemplateIsNotCompileTimeConstantProblem
|
||||||
|
Log.Error(e.Message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
74
detect.gui/Api/ApiService.cs
Normal file
74
detect.gui/Api/ApiService.cs
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using detect.gui.Api.System;
|
||||||
|
using detect.gui.Converters;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Http.Json;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Serilog;
|
||||||
|
using xwd.utils;
|
||||||
|
|
||||||
|
namespace detect.gui.Api;
|
||||||
|
|
||||||
|
public class ApiService
|
||||||
|
{
|
||||||
|
private static ApiService? _instance;
|
||||||
|
|
||||||
|
public static ApiService Instance
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
_instance = new ApiService();
|
||||||
|
return _instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private WebApplication? _webApp = null;
|
||||||
|
|
||||||
|
public void StartService()
|
||||||
|
{
|
||||||
|
if (_webApp != null) return;
|
||||||
|
var appBuilder = WebApplication.CreateBuilder();
|
||||||
|
appBuilder.Services.AddRouting();
|
||||||
|
appBuilder.Services.AddLogging();
|
||||||
|
// 添加 CORS 服务
|
||||||
|
appBuilder.Services.AddCors(options =>
|
||||||
|
{
|
||||||
|
options.AddPolicy("AllowAll", builder =>
|
||||||
|
{
|
||||||
|
builder.AllowAnyOrigin()
|
||||||
|
.AllowAnyMethod()
|
||||||
|
.AllowAnyHeader();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// json
|
||||||
|
appBuilder.Services.Configure<JsonOptions>(options =>
|
||||||
|
{
|
||||||
|
options.SerializerOptions.Converters.Add(new JsonStringEnumConverter()); // 可选:支持枚举为字符串
|
||||||
|
options.SerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; // 使用驼峰命名法
|
||||||
|
options.SerializerOptions.Converters.Add(new JsonDateTimeConverter("yyyy-MM-dd HH:mm:ss")); // 设置日期时间格式
|
||||||
|
});
|
||||||
|
_webApp = appBuilder.Build();
|
||||||
|
// 使用 CORS 中间件
|
||||||
|
_webApp.UseCors("AllowAll");
|
||||||
|
|
||||||
|
// api
|
||||||
|
_ = new DetectTaskApi(_webApp);
|
||||||
|
_ = new DeviceApi(_webApp);
|
||||||
|
_ = new LogApi(_webApp);
|
||||||
|
_ = new UserApi(_webApp);
|
||||||
|
_ = new AuthorityApi(_webApp);
|
||||||
|
|
||||||
|
Task.Run(() => { _webApp.RunAsync(AppSettingsManager.Manager().GetString("ApiUrl")); });
|
||||||
|
Log.Information("Web Application is started!");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void StopService()
|
||||||
|
{
|
||||||
|
if (_webApp == null) return;
|
||||||
|
await _webApp.StopAsync();
|
||||||
|
_webApp = null;
|
||||||
|
Log.Information("Web Application is stopped!");
|
||||||
|
}
|
||||||
|
}
|
13
detect.gui/Api/Helpers/DeviceHelper.cs
Normal file
13
detect.gui/Api/Helpers/DeviceHelper.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using detect.gui.Models;
|
||||||
|
using detect.gui.Models.Entities;
|
||||||
|
|
||||||
|
namespace detect.gui.Api.Helpers;
|
||||||
|
|
||||||
|
public class DeviceHelper
|
||||||
|
{
|
||||||
|
public static ApiResponse<List<DeviceEntity>>? GetList()
|
||||||
|
{
|
||||||
|
return ApiHelper.Get<List<DeviceEntity>>("/v1/system/device/all", "");
|
||||||
|
}
|
||||||
|
}
|
18
detect.gui/Api/Helpers/UserHelper.cs
Normal file
18
detect.gui/Api/Helpers/UserHelper.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
using detect.gui.Models;
|
||||||
|
using detect.gui.Models.Entities;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace detect.gui.Api.Helpers;
|
||||||
|
|
||||||
|
public class UserHelper
|
||||||
|
{
|
||||||
|
public static ApiResponse<UserEntity>? Login(string? username, string? password)
|
||||||
|
{
|
||||||
|
var data = new { Username = username, Password = password };
|
||||||
|
var param = JsonConvert.SerializeObject(data, new JsonSerializerSettings
|
||||||
|
{
|
||||||
|
NullValueHandling = NullValueHandling.Ignore
|
||||||
|
});
|
||||||
|
return ApiHelper.Post<UserEntity>("/v1/system/user/login", param, false);
|
||||||
|
}
|
||||||
|
}
|
41
detect.gui/Api/System/AuthorityApi.cs
Normal file
41
detect.gui/Api/System/AuthorityApi.cs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
using detect.gui.Models.Entities;
|
||||||
|
using detect.gui.Services.Detect;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
using Splat;
|
||||||
|
|
||||||
|
namespace detect.gui.Api.System;
|
||||||
|
|
||||||
|
public class AuthorityApi
|
||||||
|
{
|
||||||
|
public AuthorityApi(IEndpointRouteBuilder? webApp)
|
||||||
|
{
|
||||||
|
// all
|
||||||
|
webApp?.MapGet("/v1/system/authority/all", () => Locator.Current.GetService<DetectAuthorityService>()!.ListAll());
|
||||||
|
|
||||||
|
// search
|
||||||
|
webApp?.MapGet("/v1/system/authority/search", ([FromQuery] int? id, [FromQuery] string? name = "", [FromQuery]int pageNum = 1, [FromQuery]int pageSize = 10) =>
|
||||||
|
Locator.Current.GetService<DetectAuthorityService>()!.Search(id, name, pageNum, pageSize));
|
||||||
|
|
||||||
|
// one
|
||||||
|
webApp?.MapGet("/v1/system/authority/one", ([FromQuery] int? id, [FromQuery] string? name = "") =>
|
||||||
|
Locator.Current.GetService<DetectAuthorityService>()!.ListOne(id, name));
|
||||||
|
|
||||||
|
// id
|
||||||
|
webApp?.MapGet("/v1/system/authority/{id:long}", ([FromRoute] long id) =>
|
||||||
|
Locator.Current.GetService<DetectAuthorityService>()!.ListById(id));
|
||||||
|
|
||||||
|
// add
|
||||||
|
webApp?.MapPost("/v1/system/authority/", ([FromBody] AuthorityEntity entity) =>
|
||||||
|
Locator.Current.GetService<DetectAuthorityService>()!.AddData(entity));
|
||||||
|
|
||||||
|
// update
|
||||||
|
webApp?.MapPut("/v1/system/authority/", ([FromBody] AuthorityEntity entity) =>
|
||||||
|
Locator.Current.GetService<DetectAuthorityService>()!.UpdateData(entity));
|
||||||
|
|
||||||
|
// delete
|
||||||
|
webApp?.MapDelete("/v1/system/authority/{id:long}", ([FromRoute] long id) =>
|
||||||
|
Locator.Current.GetService<DetectAuthorityService>()!.DeleteById(id));
|
||||||
|
}
|
||||||
|
}
|
37
detect.gui/Api/System/DetectTaskApi.cs
Normal file
37
detect.gui/Api/System/DetectTaskApi.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
using detect.gui.Models.Entities;
|
||||||
|
using detect.gui.Services.Detect;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
using Splat;
|
||||||
|
|
||||||
|
namespace detect.gui.Api.System;
|
||||||
|
|
||||||
|
public class DetectTaskApi
|
||||||
|
{
|
||||||
|
public DetectTaskApi(IEndpointRouteBuilder? webApp)
|
||||||
|
{
|
||||||
|
// // all
|
||||||
|
// webApp?.MapGet("/v1/system/device/all", () => Locator.Current.GetService<VapDeviceService>()!.ListAll());
|
||||||
|
//
|
||||||
|
// // search
|
||||||
|
// webApp?.MapGet("/v1/system/device/search", ([FromQuery] int? regionId, [FromQuery] string? name, [FromQuery] string? ip, [FromQuery]int pageNum = 1, [FromQuery]int pageSize = 10) =>
|
||||||
|
// Locator.Current.GetService<VapDeviceService>()!.Search(regionId, name, ip, pageNum, pageSize));
|
||||||
|
//
|
||||||
|
// // id
|
||||||
|
// webApp?.MapGet("/v1/system/device/{id:long}", ([FromRoute] long id) =>
|
||||||
|
// Locator.Current.GetService<VapDeviceService>()!.ListById(id));
|
||||||
|
//
|
||||||
|
// // add
|
||||||
|
// webApp?.MapPost("/v1/system/device/", ([FromBody] DeviceEntity entity) =>
|
||||||
|
// Locator.Current.GetService<VapDeviceService>()!.AddData(entity));
|
||||||
|
//
|
||||||
|
// // update
|
||||||
|
// webApp?.MapPut("/v1/system/device/", ([FromBody] DeviceEntity entity) =>
|
||||||
|
// Locator.Current.GetService<VapDeviceService>()!.UpdateData(entity));
|
||||||
|
//
|
||||||
|
// // delete
|
||||||
|
// webApp?.MapDelete("/v1/system/device/{id:long}", ([FromRoute] long id) =>
|
||||||
|
// Locator.Current.GetService<VapDeviceService>()!.DeleteById(id));
|
||||||
|
}
|
||||||
|
}
|
37
detect.gui/Api/System/DeviceApi.cs
Normal file
37
detect.gui/Api/System/DeviceApi.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
using detect.gui.Models.Entities;
|
||||||
|
using detect.gui.Services.Detect;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
using Splat;
|
||||||
|
|
||||||
|
namespace detect.gui.Api.System;
|
||||||
|
|
||||||
|
public class DeviceApi
|
||||||
|
{
|
||||||
|
public DeviceApi(IEndpointRouteBuilder? webApp)
|
||||||
|
{
|
||||||
|
// all
|
||||||
|
webApp?.MapGet("/v1/system/device/all", () => Locator.Current.GetService<DetectDeviceService>()!.ListAll());
|
||||||
|
|
||||||
|
// search
|
||||||
|
webApp?.MapGet("/v1/system/device/search", ([FromQuery] int? regionId, [FromQuery] string? name, [FromQuery] string? ip, [FromQuery]int pageNum = 1, [FromQuery]int pageSize = 10) =>
|
||||||
|
Locator.Current.GetService<DetectDeviceService>()!.Search(regionId, name, ip, pageNum, pageSize));
|
||||||
|
|
||||||
|
// id
|
||||||
|
webApp?.MapGet("/v1/system/device/{id:long}", ([FromRoute] long id) =>
|
||||||
|
Locator.Current.GetService<DetectDeviceService>()!.ListById(id));
|
||||||
|
|
||||||
|
// add
|
||||||
|
webApp?.MapPost("/v1/system/device/", ([FromBody] DeviceEntity entity) =>
|
||||||
|
Locator.Current.GetService<DetectDeviceService>()!.AddData(entity));
|
||||||
|
|
||||||
|
// update
|
||||||
|
webApp?.MapPut("/v1/system/device/", ([FromBody] DeviceEntity entity) =>
|
||||||
|
Locator.Current.GetService<DetectDeviceService>()!.UpdateData(entity));
|
||||||
|
|
||||||
|
// delete
|
||||||
|
webApp?.MapDelete("/v1/system/device/{id:long}", ([FromRoute] long id) =>
|
||||||
|
Locator.Current.GetService<DetectDeviceService>()!.DeleteById(id));
|
||||||
|
}
|
||||||
|
}
|
41
detect.gui/Api/System/LogApi.cs
Normal file
41
detect.gui/Api/System/LogApi.cs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
using detect.gui.Models.Entities;
|
||||||
|
using detect.gui.Services.Detect;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
using Splat;
|
||||||
|
|
||||||
|
namespace detect.gui.Api.System;
|
||||||
|
|
||||||
|
public class LogApi
|
||||||
|
{
|
||||||
|
public LogApi(IEndpointRouteBuilder? webApp)
|
||||||
|
{
|
||||||
|
// all
|
||||||
|
webApp?.MapGet("/v1/system/log/all", () => Locator.Current.GetService<DetectLogService>()!.ListAll());
|
||||||
|
|
||||||
|
// search
|
||||||
|
webApp?.MapGet("/v1/system/log/search", ([FromQuery] int? userId, [FromQuery] string? description, [FromQuery]int pageNum = 1, [FromQuery]int pageSize = 10) =>
|
||||||
|
Locator.Current.GetService<DetectLogService>()!.Search(userId, description, pageNum, pageSize));
|
||||||
|
|
||||||
|
// one
|
||||||
|
webApp?.MapGet("/v1/system/log/one", ([FromQuery] int? userId, [FromQuery] string? description) =>
|
||||||
|
Locator.Current.GetService<DetectLogService>()!.ListOne(userId, description));
|
||||||
|
|
||||||
|
// id
|
||||||
|
webApp?.MapGet("/v1/system/log/{id:long}", ([FromRoute] long id) =>
|
||||||
|
Locator.Current.GetService<DetectLogService>()!.ListById(id));
|
||||||
|
|
||||||
|
// add
|
||||||
|
webApp?.MapPost("/v1/system/log/", ([FromBody] LogEntity entity) =>
|
||||||
|
Locator.Current.GetService<DetectLogService>()!.AddData(entity));
|
||||||
|
|
||||||
|
// update
|
||||||
|
webApp?.MapPut("/v1/system/log/", ([FromBody] LogEntity entity) =>
|
||||||
|
Locator.Current.GetService<DetectLogService>()!.UpdateData(entity));
|
||||||
|
|
||||||
|
// delete
|
||||||
|
webApp?.MapDelete("/v1/system/log/{id:long}", ([FromRoute] long id) =>
|
||||||
|
Locator.Current.GetService<DetectLogService>()!.DeleteById(id));
|
||||||
|
}
|
||||||
|
}
|
42
detect.gui/Api/System/UserApi.cs
Normal file
42
detect.gui/Api/System/UserApi.cs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
using detect.gui.Models.Entities;
|
||||||
|
using detect.gui.Services.Detect;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
using Splat;
|
||||||
|
|
||||||
|
namespace detect.gui.Api.System;
|
||||||
|
|
||||||
|
public class UserApi
|
||||||
|
{
|
||||||
|
public UserApi(IEndpointRouteBuilder? webApp)
|
||||||
|
{
|
||||||
|
// 用户登录
|
||||||
|
webApp?.MapPost("/v1/system/user/login", ([FromBody] UserEntity user) => Locator.Current.GetService<DetectUserService>()!.Login(user.Username, user.Password));
|
||||||
|
|
||||||
|
// 用户列表(所有)
|
||||||
|
webApp?.MapGet("/v1/system/user/all", () => Locator.Current.GetService<DetectUserService>()!.ListAll());
|
||||||
|
|
||||||
|
// Search
|
||||||
|
webApp?.MapGet("/v1/system/user/search", ([FromQuery] string? realName, [FromQuery]int pageNum = 1, [FromQuery]int pageSize = 10) => Locator.Current.GetService<DetectUserService>()!.Search(realName, pageNum, pageSize));
|
||||||
|
|
||||||
|
// 用户列表(按条件查询)
|
||||||
|
webApp?.MapGet("/v1/system/user/list", (string username) => Locator.Current.GetService<DetectUserService>()!.List(x=> x.Username == username));
|
||||||
|
|
||||||
|
// 新增用户
|
||||||
|
webApp?.MapPost("/v1/system/user/add", ([FromBody] UserEntity user) => Locator.Current.GetService<DetectUserService>()!.AddData(user));
|
||||||
|
|
||||||
|
// 修改用户
|
||||||
|
webApp?.MapPost("/v1/system/user/update", ([FromBody] UserEntity user) => Locator.Current.GetService<DetectUserService>()!.UpdateData(user));
|
||||||
|
|
||||||
|
// 根据ID删除用户
|
||||||
|
webApp?.MapGet("/v1/system/user/delete", (long id) => Locator.Current.GetService<DetectUserService>()!.DeleteData(id));
|
||||||
|
|
||||||
|
// 根据ID查询用户
|
||||||
|
webApp?.MapGet("/v1/system/user", (long id) => Locator.Current.GetService<DetectUserService>()!.GetDataById(id));
|
||||||
|
|
||||||
|
// id
|
||||||
|
webApp?.MapGet("/v1/system/user/{id:long}", ([FromRoute] long id) =>
|
||||||
|
Locator.Current.GetService<DetectUserService>()!.GetDataById(id));
|
||||||
|
}
|
||||||
|
}
|
31
detect.gui/App.axaml
Normal file
31
detect.gui/App.axaml
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<Application xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
x:Class="detect.gui.App"
|
||||||
|
RequestedThemeVariant="Light">
|
||||||
|
<Application.Resources>
|
||||||
|
<ResourceDictionary>
|
||||||
|
<ResourceDictionary.MergedDictionaries>
|
||||||
|
<ResourceInclude Source="avares://detect.gui/Assets/Expander.axaml" />
|
||||||
|
</ResourceDictionary.MergedDictionaries>
|
||||||
|
</ResourceDictionary>
|
||||||
|
</Application.Resources>
|
||||||
|
<Application.Styles>
|
||||||
|
<FluentTheme />
|
||||||
|
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml"/>
|
||||||
|
<StyleInclude Source="avares://detect.gui/Assets/DefaultResources.axaml" />
|
||||||
|
<StyleInclude Source="avares://detect.gui/Assets/Calendar.axaml" />
|
||||||
|
<StyleInclude Source="avares://detect.gui/Assets/CalendarDatePicker.axaml" />
|
||||||
|
<StyleInclude Source="avares://detect.gui/Assets/TextBlock.axaml" />
|
||||||
|
<StyleInclude Source="avares://detect.gui/Assets/TextBox.axaml" />
|
||||||
|
<StyleInclude Source="avares://detect.gui/Assets/Button.axaml" />
|
||||||
|
<StyleInclude Source="avares://detect.gui/Assets/PathIcon.axaml" />
|
||||||
|
<StyleInclude Source="avares://detect.gui/Assets/RadioButton.axaml" />
|
||||||
|
<StyleInclude Source="avares://detect.gui/Assets/ToolTip.axaml" />
|
||||||
|
<StyleInclude Source="avares://detect.gui/Assets/NotificationCard.axaml" />
|
||||||
|
<StyleInclude Source="avares://detect.gui/Assets/DataGrid.axaml" />
|
||||||
|
<StyleInclude Source="avares://detect.gui/Assets/ListBox.axaml" />
|
||||||
|
<StyleInclude Source="avares://detect.gui/Assets/FlyoutPresenter.axaml" />
|
||||||
|
<StyleInclude Source="avares://detect.gui/Assets/WebView.axaml" />
|
||||||
|
<StyleInclude Source="avares://detect.gui/Assets/ComboBox.axaml" />
|
||||||
|
</Application.Styles>
|
||||||
|
</Application>
|
48
detect.gui/App.axaml.cs
Normal file
48
detect.gui/App.axaml.cs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
using System;
|
||||||
|
using System.Text;
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Avalonia.ReactiveUI;
|
||||||
|
using detect.gui.Drivers;
|
||||||
|
using detect.gui.Services;
|
||||||
|
using detect.gui.Services.Detect;
|
||||||
|
using detect.gui.ViewModels;
|
||||||
|
using detect.gui.Views;
|
||||||
|
using ReactiveUI;
|
||||||
|
using Splat;
|
||||||
|
|
||||||
|
namespace detect.gui;
|
||||||
|
|
||||||
|
public class App : Application
|
||||||
|
{
|
||||||
|
public override void Initialize() => AvaloniaXamlLoader.Load(this);
|
||||||
|
|
||||||
|
public override void OnFrameworkInitializationCompleted()
|
||||||
|
{
|
||||||
|
var suspension = new AutoSuspendHelper(ApplicationLifetime!);
|
||||||
|
RxApp.SuspensionHost.CreateNewAppState = () => new MainViewModel();
|
||||||
|
RxApp.SuspensionHost.SetupDefaultSuspendResume(new NewtonsoftJsonSuspensionDriver("app.state.json"));
|
||||||
|
suspension.OnFrameworkInitializationCompleted();
|
||||||
|
|
||||||
|
Locator.CurrentMutable.RegisterConstant<IScreen>(RxApp.SuspensionHost.GetAppState<MainViewModel>());
|
||||||
|
Locator.CurrentMutable.Register(() => new LoginView(), typeof(IViewFor<LoginViewModel>));
|
||||||
|
Locator.CurrentMutable.Register(() => new HomeView(), typeof(IViewFor<HomeViewModel>));
|
||||||
|
Locator.CurrentMutable.Register(() => new DetectTaskView(), typeof(IViewFor<DetectTaskViewModel>));
|
||||||
|
Locator.CurrentMutable.Register(() => new DeviceView(), typeof(IViewFor<DeviceViewModel>));
|
||||||
|
Locator.CurrentMutable.Register(() => new LogView(), typeof(IViewFor<LogViewModel>));
|
||||||
|
Locator.CurrentMutable.Register(() => new UserView(), typeof(IViewFor<UserViewModel>));
|
||||||
|
|
||||||
|
Locator.CurrentMutable.Register(() => new DetectUserService(), typeof(DetectUserService));
|
||||||
|
Locator.CurrentMutable.Register(() => new DetectDeviceService(), typeof(DetectDeviceService));
|
||||||
|
Locator.CurrentMutable.Register(() => new DetectTaskService(), typeof(DetectTaskService));
|
||||||
|
Locator.CurrentMutable.Register(() => new DetectLogService(), typeof(DetectLogService));
|
||||||
|
Locator.CurrentMutable.Register(() => new DetectAuthorityService(), typeof(DetectAuthorityService));
|
||||||
|
|
||||||
|
Locator.CurrentMutable.Register(() => new DeviceClientService(), typeof(DeviceClientService));
|
||||||
|
Locator.Current.GetService<DeviceClientService>();
|
||||||
|
|
||||||
|
new MainView { DataContext = Locator.Current.GetService<IScreen>() }.Show();
|
||||||
|
|
||||||
|
base.OnFrameworkInitializationCompleted();
|
||||||
|
}
|
||||||
|
}
|
479
detect.gui/Assets/Button.axaml
Normal file
479
detect.gui/Assets/Button.axaml
Normal file
@ -0,0 +1,479 @@
|
|||||||
|
<Styles xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||||
|
|
||||||
|
<Styles.Resources>
|
||||||
|
<BoxShadows x:Key="TabRadioButtonShadows">inset 0 0 20 2 #006CC6</BoxShadows>
|
||||||
|
<SolidColorBrush x:Key="TabRadioButtonPointeroverBackgroundColor" Color="#45afdf" Opacity="0.8" />
|
||||||
|
<SolidColorBrush x:Key="TabRadioButtonCheckedBackgroundColor" Color="#3793d4" />
|
||||||
|
|
||||||
|
<BoxShadows x:Key="DarkTabRadioButtonShadows">inset 0 0 20 2 #3A62CB</BoxShadows>
|
||||||
|
<SolidColorBrush x:Key="DarkTabRadioButtonPointeroverBackgroundColor" Color="#3A62CB" Opacity="0.8" />
|
||||||
|
<SolidColorBrush x:Key="DarkTabRadioButtonCheckedBackgroundColor" Color="#3A62CB" Opacity="0.8" />
|
||||||
|
|
||||||
|
<BoxShadows x:Key="ButtonShadows">inset 0 0 30 2 #006CC6</BoxShadows>
|
||||||
|
<BoxShadows x:Key="WindowButtonShadows">inset 0 0 15 2 #2F1A9A</BoxShadows>
|
||||||
|
<SolidColorBrush x:Key="BlueButtonBackgroundColor" Color="{StaticResource Blue1}" />
|
||||||
|
<SolidColorBrush x:Key="BlueButtonPointeroverBackgroundColor" Color="#45afdf" Opacity="0.8" />
|
||||||
|
|
||||||
|
<LinearGradientBrush x:Key="ButtonPointeroverBackgroundColor" StartPoint="0% 0%" EndPoint="100% 0%">
|
||||||
|
<GradientStop Offset="0.0" Color="#3A62CB" />
|
||||||
|
<GradientStop Offset="0.5" Color="#68D9FF" />
|
||||||
|
<GradientStop Offset="1.0" Color="#3A62CB" />
|
||||||
|
</LinearGradientBrush>
|
||||||
|
</Styles.Resources>
|
||||||
|
|
||||||
|
<Style Selector="Button">
|
||||||
|
<Setter Property="FontFamily" Value="{StaticResource Font-Family}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="Button.rect-button">
|
||||||
|
<Setter Property="Padding" Value="10,5" />
|
||||||
|
<Setter Property="CornerRadius" Value="6" />
|
||||||
|
<Setter Property="FontSize" Value="14" />
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Light}" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate>
|
||||||
|
<Border x:Name="BackBorder"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}">
|
||||||
|
<Border Background="{TemplateBinding Background}"
|
||||||
|
BoxShadow="{StaticResource ButtonShadows}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}"
|
||||||
|
Padding="{TemplateBinding Padding}"
|
||||||
|
Width="{TemplateBinding Width}"
|
||||||
|
Height="{TemplateBinding Height}">
|
||||||
|
<TextBlock Text="{TemplateBinding Content}"
|
||||||
|
FontFamily="{TemplateBinding FontFamily}"
|
||||||
|
FontSize="{TemplateBinding FontSize}"
|
||||||
|
Foreground="{TemplateBinding Foreground}"
|
||||||
|
HorizontalAlignment="Center" />
|
||||||
|
</Border>
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.rect-button:disabled">
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Gray}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.rect-button.login">
|
||||||
|
<Setter Property="FontSize" Value="16" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="Button.blue">
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Light}" />
|
||||||
|
<Setter Property="Background" Value="{StaticResource BlueButtonBackgroundColor}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.blue:pointerover">
|
||||||
|
<Setter Property="Background" Value="{StaticResource BlueButtonPointeroverBackgroundColor}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.blue:disabled">
|
||||||
|
<Setter Property="Foreground" Value="#bbbbbb" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="Button.icon-button">
|
||||||
|
<Setter Property="Margin" Value="0" />
|
||||||
|
<Setter Property="Width" Value="33" />
|
||||||
|
<Setter Property="Height" Value="33" />
|
||||||
|
<Setter Property="CornerRadius" Value="0" />
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Light}" />
|
||||||
|
<Setter Property="Background" Value="Transparent" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate>
|
||||||
|
<Border x:Name="BackBorder"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}">
|
||||||
|
<Border Width="{TemplateBinding Width}"
|
||||||
|
Height="{TemplateBinding Height}"
|
||||||
|
CornerRadius="{Binding $parent.CornerRadius}">
|
||||||
|
<PathIcon Name="ButtonIcon"
|
||||||
|
Width="13"
|
||||||
|
Height="13"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Foreground="{TemplateBinding Foreground}"/>
|
||||||
|
</Border>
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.icon-button:disabled">
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Gray}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.icon-button:disabled /template/ Border#BackBorder">
|
||||||
|
<Setter Property="Background" Value="Transparent" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.icon-button:pointerover /template/ Border#BackBorder">
|
||||||
|
<Setter Property="BoxShadow" Value="{StaticResource TabRadioButtonShadows}"/>
|
||||||
|
<Setter Property="Background" Value="{StaticResource TabRadioButtonPointeroverBackgroundColor}"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="Button.circle">
|
||||||
|
<Setter Property="CornerRadius" Value="100" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="Button.window">
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Light}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.window:disabled">
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Gray}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.window:pointerover">
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Light}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.window:pointerover /template/ Border#BackBorder">
|
||||||
|
<Setter Property="BoxShadow" Value="{StaticResource WindowButtonShadows}"/>
|
||||||
|
<Setter Property="Background" Value="{StaticResource BlueButtonPointeroverBackgroundColor}"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="Button.page">
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Light}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.page:disabled">
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Gray}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.page:pointerover">
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Light}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.page:pointerover /template/ Border#BackBorder">
|
||||||
|
<Setter Property="BoxShadow" Value="{StaticResource TabRadioButtonShadows}"/>
|
||||||
|
<Setter Property="Background" Value="{StaticResource TabRadioButtonPointeroverBackgroundColor}"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="Button.list-item">
|
||||||
|
<Setter Property="FontFamily" Value="{StaticResource Font-Family}" />
|
||||||
|
<Setter Property="FontSize" Value="14" />
|
||||||
|
<Setter Property="CornerRadius" Value="6" />
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Light}" />
|
||||||
|
<Setter Property="Height" Value="38" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate>
|
||||||
|
<Grid Height="{TemplateBinding Height}">
|
||||||
|
<Border Height="{TemplateBinding Height}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}"
|
||||||
|
BorderThickness="0"
|
||||||
|
BoxShadow="{StaticResource TabRadioButtonShadows}"
|
||||||
|
BorderBrush="{StaticResource Light}"
|
||||||
|
Background="{StaticResource TabRadioButtonCheckedBackgroundColor}"/>
|
||||||
|
<Border x:Name="BackBorder"
|
||||||
|
Height="{TemplateBinding Height}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}"
|
||||||
|
Padding="0"
|
||||||
|
BorderThickness="1"
|
||||||
|
Background="Transparent">
|
||||||
|
<Grid ColumnDefinitions="15, Auto, 10, *, 10, Auto, 15"
|
||||||
|
Width="{TemplateBinding Width}"
|
||||||
|
VerticalAlignment="Center">
|
||||||
|
<PathIcon Grid.Column="1"
|
||||||
|
x:Name="ButtonIcon"
|
||||||
|
Width="17"
|
||||||
|
Height="17"
|
||||||
|
Foreground="{TemplateBinding Foreground}" />
|
||||||
|
<TextBlock Grid.Column="3"
|
||||||
|
x:Name="ButtonText"
|
||||||
|
FontSize="{TemplateBinding FontSize}"
|
||||||
|
Foreground="{TemplateBinding Foreground}"
|
||||||
|
Text="{TemplateBinding Content}" />
|
||||||
|
<PathIcon Grid.Column="5"
|
||||||
|
x:Name="ArrowIcon"
|
||||||
|
Width="15"
|
||||||
|
Height="15"
|
||||||
|
Foreground="{TemplateBinding Foreground}" />
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.list-item:pointerover Border#BackBorder">
|
||||||
|
<Setter Property="BoxShadow" Value="{StaticResource TabRadioButtonShadows}" />
|
||||||
|
<Setter Property="Background" Value="{StaticResource TabRadioButtonPointeroverBackgroundColor}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="Button.split">
|
||||||
|
<Setter Property="FontFamily" Value="{StaticResource Font-Family}" />
|
||||||
|
<Setter Property="FontSize" Value="14" />
|
||||||
|
<Setter Property="CornerRadius" Value="4" />
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Light}" />
|
||||||
|
<Setter Property="Width" Value="15" />
|
||||||
|
<Setter Property="Height" Value="58" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate>
|
||||||
|
<Grid Height="{TemplateBinding Height}">
|
||||||
|
<Border Height="{TemplateBinding Height}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}"
|
||||||
|
BorderThickness="0"
|
||||||
|
BoxShadow="{StaticResource TabRadioButtonShadows}"
|
||||||
|
BorderBrush="{StaticResource Light}"
|
||||||
|
Background="Transparent"/>
|
||||||
|
<Border x:Name="BackBorder"
|
||||||
|
Height="{TemplateBinding Height}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}"
|
||||||
|
Padding="0"
|
||||||
|
BorderThickness="1"
|
||||||
|
Background="Transparent">
|
||||||
|
<PathIcon x:Name="ArrowIcon"
|
||||||
|
Width="10"
|
||||||
|
Height="10"
|
||||||
|
Foreground="{TemplateBinding Foreground}" />
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.split:pointerover Border#BackBorder">
|
||||||
|
<Setter Property="BoxShadow" Value="{StaticResource TabRadioButtonShadows}" />
|
||||||
|
<Setter Property="Background" Value="{StaticResource TabRadioButtonPointeroverBackgroundColor}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="Button.filter">
|
||||||
|
<Setter Property="FontFamily" Value="{StaticResource Font-Family}" />
|
||||||
|
<Setter Property="FontSize" Value="14" />
|
||||||
|
<Setter Property="CornerRadius" Value="6" />
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Light}" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate>
|
||||||
|
<Grid Height="{TemplateBinding Height}">
|
||||||
|
<Border Height="{TemplateBinding Height}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}"
|
||||||
|
BorderThickness="0"
|
||||||
|
BoxShadow="{StaticResource TabRadioButtonShadows}"
|
||||||
|
BorderBrush="{StaticResource Light}"
|
||||||
|
Background="{StaticResource TabRadioButtonCheckedBackgroundColor}"/>
|
||||||
|
<Border x:Name="BackBorder"
|
||||||
|
Height="{TemplateBinding Height}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}"
|
||||||
|
Padding="0"
|
||||||
|
BorderThickness="1"
|
||||||
|
Background="Transparent">
|
||||||
|
<Grid ColumnDefinitions="15, Auto, 10, *, 15"
|
||||||
|
Width="{TemplateBinding Width}"
|
||||||
|
VerticalAlignment="Center">
|
||||||
|
<PathIcon Grid.Column="1"
|
||||||
|
x:Name="ButtonIcon"
|
||||||
|
Width="12"
|
||||||
|
Height="12"
|
||||||
|
Foreground="{TemplateBinding Foreground}" />
|
||||||
|
<TextBlock Grid.Column="3"
|
||||||
|
x:Name="ButtonText"
|
||||||
|
FontSize="{TemplateBinding FontSize}"
|
||||||
|
Foreground="{TemplateBinding Foreground}"
|
||||||
|
Text="{TemplateBinding Content}" />
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.filter:pointerover Border#BackBorder">
|
||||||
|
<Setter Property="BoxShadow" Value="{StaticResource TabRadioButtonShadows}" />
|
||||||
|
<Setter Property="Background" Value="{StaticResource TabRadioButtonPointeroverBackgroundColor}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="Button.filter-dark">
|
||||||
|
<Setter Property="FontFamily" Value="{StaticResource Font-Family}" />
|
||||||
|
<Setter Property="FontSize" Value="14" />
|
||||||
|
<Setter Property="CornerRadius" Value="6" />
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Light}" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate>
|
||||||
|
<Grid Height="{TemplateBinding Height}">
|
||||||
|
<Border Height="{TemplateBinding Height}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}"
|
||||||
|
BorderThickness="0"
|
||||||
|
BoxShadow="{StaticResource DarkTabRadioButtonShadows}"
|
||||||
|
BorderBrush="{StaticResource Light}"
|
||||||
|
Background="{StaticResource DarkTabRadioButtonCheckedBackgroundColor}"/>
|
||||||
|
<Border x:Name="BackBorder"
|
||||||
|
Height="{TemplateBinding Height}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}"
|
||||||
|
Padding="0"
|
||||||
|
BorderThickness="1"
|
||||||
|
Background="Transparent">
|
||||||
|
<Grid ColumnDefinitions="15, Auto, 10, *, 15"
|
||||||
|
Width="{TemplateBinding Width}"
|
||||||
|
VerticalAlignment="Center">
|
||||||
|
<PathIcon Grid.Column="1"
|
||||||
|
x:Name="ButtonIcon"
|
||||||
|
Width="12"
|
||||||
|
Height="12"
|
||||||
|
Foreground="{TemplateBinding Foreground}" />
|
||||||
|
<TextBlock Grid.Column="3"
|
||||||
|
x:Name="ButtonText"
|
||||||
|
FontSize="{TemplateBinding FontSize}"
|
||||||
|
Foreground="{TemplateBinding Foreground}"
|
||||||
|
Text="{TemplateBinding Content}" />
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.filter-dark:pointerover Border#BackBorder">
|
||||||
|
<Setter Property="BoxShadow" Value="{StaticResource TabRadioButtonShadows}" />
|
||||||
|
<Setter Property="Background" Value="{StaticResource DarkTabRadioButtonPointeroverBackgroundColor}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="Button.logout /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource LogoutData}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.close /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource CloseData}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.min /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource MinData}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.max /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource MaxData}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.restore /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource RestoreData}"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="Button.first-page /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource PageFirstData}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.left-page /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource PageLeftData}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.right-page /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource PageRightData}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.last-page /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource PageLastData}"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="Button.record-add /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource RecordAddData}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.record-edit /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource RecordEditData}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.record-delete /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource RecordDeleteData}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.record-reload /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource RecordReLoadData}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.record-search /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource RecordSearchData}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.record-detail /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource RecordDetailData}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.record-settings /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource RecordSettingsData}"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="Button.submit /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource SubmitData}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.ok /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource OkData}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.cancel /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource CancelData}"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="Button.tool-expand /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource ToolExpandData}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.tool-collapse /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource ToolCollapseData}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.tool-expand-all /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource ToolExpandAllData}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.tool-collapse-all /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource ToolCollapseAllData}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.tool-go-back /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource ToolGoBackData}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.run /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource ToolRunData}"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="Button.list-region /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource ListRegionData}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.list-region-left /template/ PathIcon#ArrowIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource ToolExpandData}"/>
|
||||||
|
<Setter Property="RenderTransform">
|
||||||
|
<RotateTransform Angle="-90"></RotateTransform>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.list-region-down /template/ PathIcon#ArrowIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource ToolExpandData}"/>
|
||||||
|
<Setter Property="RenderTransform">
|
||||||
|
<RotateTransform Angle="0"></RotateTransform>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="Button.split-left /template/ PathIcon#ArrowIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource ToolExpandData}"/>
|
||||||
|
<Setter Property="RenderTransform">
|
||||||
|
<RotateTransform Angle="90"></RotateTransform>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.split-right /template/ PathIcon#ArrowIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource ToolExpandData}"/>
|
||||||
|
<Setter Property="RenderTransform">
|
||||||
|
<RotateTransform Angle="-90"></RotateTransform>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="Button.search /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource SearchData}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.reset /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource ResetData}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.view /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource ViewData}"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="Button.video-control-button">
|
||||||
|
<Setter Property="Width" Value="32" />
|
||||||
|
<Setter Property="Height" Value="32" />
|
||||||
|
<Setter Property="CornerRadius" Value="0" />
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Light}" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate>
|
||||||
|
<Border Background="Transparent"
|
||||||
|
CornerRadius="2"
|
||||||
|
Padding="4">
|
||||||
|
<Border CornerRadius="50"
|
||||||
|
Width="24"
|
||||||
|
Height="24"
|
||||||
|
Name="ButtonBorder"
|
||||||
|
VerticalAlignment="Center" HorizontalAlignment="Center">
|
||||||
|
<PathIcon Name="ButtonIcon"
|
||||||
|
Foreground="{TemplateBinding Foreground}"
|
||||||
|
Width="24"
|
||||||
|
Height="24"
|
||||||
|
CornerRadius="50" />
|
||||||
|
</Border>
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.video-control-button:disabled">
|
||||||
|
<Setter Property="Foreground" Value="#999999" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.video-control-button:pointerover /template/ Border#ButtonBorder">
|
||||||
|
<Setter Property="Background" Value="#13265a"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.video-control-button.play /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="PathIcon.Data" Value="{StaticResource VideoPlayData}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.video-control-button.stop /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="PathIcon.Data" Value="{StaticResource VideoStopData}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.video-control-button.pause /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="PathIcon.Data" Value="{StaticResource VideoPauseData}"/>
|
||||||
|
</Style>
|
||||||
|
</Styles>
|
272
detect.gui/Assets/Calendar.axaml
Normal file
272
detect.gui/Assets/Calendar.axaml
Normal file
@ -0,0 +1,272 @@
|
|||||||
|
<Styles xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||||
|
|
||||||
|
<Styles.Resources>
|
||||||
|
<ControlTheme x:Key="FluentCalendarButton" TargetType="Button">
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
||||||
|
<Setter Property="VerticalAlignment" Value="Stretch" />
|
||||||
|
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||||
|
<Setter Property="FontSize" Value="20" />
|
||||||
|
<Setter Property="Background" Value="{DynamicResource CalendarViewNavigationButtonBackground}" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate>
|
||||||
|
<!-- HCA was changed here to ensure nav arrows display correctly -->
|
||||||
|
<ContentPresenter Name="Text" Background="{TemplateBinding Background}"
|
||||||
|
BorderBrush="{DynamicResource CalendarViewNavigationButtonBorderBrush}"
|
||||||
|
BorderThickness="{TemplateBinding BorderThickness}"
|
||||||
|
Content="{TemplateBinding Content}"
|
||||||
|
Margin="{TemplateBinding Padding}"
|
||||||
|
HorizontalContentAlignment="Stretch"
|
||||||
|
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}">
|
||||||
|
<ContentPresenter.Styles>
|
||||||
|
<Style Selector="TextBlock">
|
||||||
|
<Setter Property="FontSize" Value="14" />
|
||||||
|
<Setter Property="Foreground" Value="#FFFFFF" />
|
||||||
|
</Style>
|
||||||
|
</ContentPresenter.Styles>
|
||||||
|
</ContentPresenter>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
</ControlTheme>
|
||||||
|
</Styles.Resources>
|
||||||
|
|
||||||
|
<Style Selector="Calendar">
|
||||||
|
<Setter Property="Foreground" Value="#FFFFFF" />
|
||||||
|
<Setter Property="Background" Value="#13265a" />
|
||||||
|
<Setter Property="BorderBrush" Value="#183171" />
|
||||||
|
<Setter Property="BorderThickness" Value="1" />
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Left" />
|
||||||
|
<Setter Property="VerticalAlignment" Value="Center" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate>
|
||||||
|
<StackPanel
|
||||||
|
Name="PART_Root"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
ClipToBounds="True">
|
||||||
|
<CalendarItem
|
||||||
|
Name="PART_CalendarItem"
|
||||||
|
Background="{TemplateBinding Background}"
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
|
BorderThickness="{TemplateBinding BorderThickness}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}"
|
||||||
|
HeaderBackground="{TemplateBinding HeaderBackground}" />
|
||||||
|
</StackPanel>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="CalendarItem">
|
||||||
|
<Setter Property="CornerRadius" Value="{DynamicResource ControlCornerRadius}" />
|
||||||
|
<Setter Property="DayTitleTemplate">
|
||||||
|
<Template x:DataType="x:String">
|
||||||
|
<TextBlock Text="{Binding}"
|
||||||
|
Foreground="#FFFFFF"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
FontSize="14" />
|
||||||
|
</Template>
|
||||||
|
</Setter>
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate>
|
||||||
|
<Border BorderThickness="{TemplateBinding BorderThickness}"
|
||||||
|
Background="{TemplateBinding Background}"
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}">
|
||||||
|
<Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch" RowDefinitions="40,*" MinWidth="294">
|
||||||
|
<Grid ColumnDefinitions="12,*,Auto,10,Auto,12">
|
||||||
|
<Button Grid.Column="1" Name="PART_HeaderButton"
|
||||||
|
IsHitTestVisible="False"
|
||||||
|
Theme="{StaticResource FluentCalendarButton}"
|
||||||
|
Foreground="{TemplateBinding Foreground}"
|
||||||
|
HorizontalContentAlignment="Left" />
|
||||||
|
<Button Name="PART_PreviousButton"
|
||||||
|
Grid.Column="2"
|
||||||
|
Theme="{StaticResource FluentCalendarButton}"
|
||||||
|
Foreground="{TemplateBinding Foreground}"
|
||||||
|
Padding="1"
|
||||||
|
HorizontalContentAlignment="Left">
|
||||||
|
<!--Path mimics Segoe MDL2 Assets font glyph used in WinUI-->
|
||||||
|
<Path Stroke="#FFFFFF"
|
||||||
|
StrokeThickness="1.5"
|
||||||
|
Data="M 0,7 L 7,0 L 14,7"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
</Button>
|
||||||
|
<Button Name="PART_NextButton"
|
||||||
|
Grid.Column="4"
|
||||||
|
Theme="{StaticResource FluentCalendarButton}"
|
||||||
|
Foreground="{TemplateBinding Foreground}"
|
||||||
|
Padding="1"
|
||||||
|
HorizontalContentAlignment="Left">
|
||||||
|
<!--Path mimics Segoe MDL2 Assets font glyph used in WinUI-->
|
||||||
|
<Path Stroke="#FFFFFF"
|
||||||
|
StrokeThickness="1.5"
|
||||||
|
Data="M 0,0 L 7,7 L 14,0"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
<!--Border below is used only for MonthView but it can't be moved inside of Grid because CalendarItem expects it to be empty and it will cause side-effects-->
|
||||||
|
<Border Name="BackgroundLayer" Background="{TemplateBinding BorderBrush}" Margin="0,38,0,0" IsVisible="{Binding #PART_MonthView.IsVisible}" Grid.Row="1" />
|
||||||
|
<Grid Name="PART_MonthView" Grid.Row="1" IsVisible="False" MinHeight="290">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<!--This should always be the week day names??-->
|
||||||
|
<RowDefinition Height="38" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
</Grid>
|
||||||
|
<Grid Name="PART_YearView"
|
||||||
|
Background="{TemplateBinding BorderBrush}"
|
||||||
|
MinHeight="290"
|
||||||
|
Grid.Row="1"
|
||||||
|
IsVisible="False">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
<Style Selector="^ /template/ Button:pointerover > Path">
|
||||||
|
<Setter Property="Stroke" Value="#FFFFFF" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^ /template/ Button:pressed > Path">
|
||||||
|
<Setter Property="Stroke" Value="#FFFFFF" />
|
||||||
|
</Style>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="CalendarDayButton">
|
||||||
|
<Setter Property="ClickMode" Value="Release" />
|
||||||
|
<Setter Property="MinWidth" Value="40" />
|
||||||
|
<Setter Property="MinHeight" Value="40" />
|
||||||
|
<Setter Property="Margin" Value="1" />
|
||||||
|
<Setter Property="Padding" Value="0,0,0,0" />
|
||||||
|
<!--These are actually set on the CalendarView in WinUI-->
|
||||||
|
<!-- <Setter Property="Foreground" Value="{DynamicResource CalendarViewCalendarItemForeground}" /> -->
|
||||||
|
<Setter Property="Foreground" Value="#FFFFFF" />
|
||||||
|
<Setter Property="Background" Value="#13265a" />
|
||||||
|
<!-- <Setter Property="Background" Value="{DynamicResource CalendarViewCalendarItemRevealBackground}" /> -->
|
||||||
|
<Setter Property="BorderBrush" Value="{DynamicResource CalendarViewCalendarItemRevealBorderBrush}" />
|
||||||
|
<Setter Property="BorderThickness" Value="2" />
|
||||||
|
<Setter Property="FontSize" Value="20" />
|
||||||
|
<Setter Property="ClipToBounds" Value="False" />
|
||||||
|
<Setter Property="HorizontalContentAlignment" Value="Center" />
|
||||||
|
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate>
|
||||||
|
<Panel>
|
||||||
|
<!-- To mimic WinUI SystemFocusVisual, Focus visual is drawn outside the bounds of the item -->
|
||||||
|
<Border Name="Root" Background="{TemplateBinding Background}"
|
||||||
|
BorderThickness="0" ClipToBounds="True">
|
||||||
|
|
||||||
|
<ContentControl Name="Content"
|
||||||
|
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||||
|
Content="{TemplateBinding Content}"
|
||||||
|
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||||
|
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||||
|
FontSize="{TemplateBinding FontSize}"
|
||||||
|
Margin="{TemplateBinding Padding}">
|
||||||
|
<ContentControl.Styles>
|
||||||
|
<Style Selector="TextBlock">
|
||||||
|
<Setter Property="Foreground" Value="#FFFFFF"></Setter>
|
||||||
|
<Setter Property="FontSize" Value="14"></Setter>
|
||||||
|
</Style>
|
||||||
|
</ContentControl.Styles>
|
||||||
|
</ContentControl>
|
||||||
|
</Border>
|
||||||
|
<!-- Drawn Border should render on top of background to preserve the 1px margin between items-->
|
||||||
|
<Border Name="Border"
|
||||||
|
BorderThickness="2"
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}" />
|
||||||
|
</Panel>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
|
||||||
|
<Style Selector="^:pointerover /template/ Border#Border">
|
||||||
|
<Setter Property="BorderBrush" Value="#2D77F3" />
|
||||||
|
<!-- <Setter Property="BorderBrush" Value="{DynamicResource CalendarViewHoverBorderBrush}" /> -->
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="^:pressed /template/ Border#Border">
|
||||||
|
<Setter Property="BorderBrush" Value="#2D77F3" />
|
||||||
|
<!-- <Setter Property="BorderBrush" Value="{DynamicResource CalendarViewPressedBorderBrush}" /> -->
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="^:selected">
|
||||||
|
<Style Selector="^ /template/ Border#Border">
|
||||||
|
<Setter Property="BorderBrush" Value="{DynamicResource CalendarViewSelectedBorderBrush}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^:pointerover /template/ Border#Border">
|
||||||
|
<Setter Property="BorderBrush" Value="{DynamicResource CalendarViewSelectedHoverBorderBrush}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^:pressed /template/ Border#Border">
|
||||||
|
<Setter Property="BorderBrush" Value="{DynamicResource CalendarViewSelectedPressedBorderBrush}" />
|
||||||
|
</Style>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="^:today">
|
||||||
|
<Style Selector="^ /template/ Border#Root">
|
||||||
|
<Setter Property="Background" Value="{DynamicResource SystemAccentColor}" />
|
||||||
|
</Style>
|
||||||
|
<!-- These are probably set in code, but consistent -->
|
||||||
|
<Style Selector="^:pointerover /template/ Border#Border">
|
||||||
|
<Setter Property="BorderBrush" Value="{DynamicResource SystemAccentColorDark1}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^:pressed /template/ Border#Border">
|
||||||
|
<Setter Property="BorderBrush" Value="{DynamicResource SystemAccentColorDark2}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^ ContentControl#Content">
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource CalendarViewTodayForeground}" />
|
||||||
|
<Setter Property="FontWeight" Value="SemiBold" />
|
||||||
|
</Style>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<!-- WinUI calls this OutOfFocus -->
|
||||||
|
<Style Selector="^:inactive">
|
||||||
|
<Style Selector="^ /template/ Border#Root">
|
||||||
|
<Setter Property="Background">
|
||||||
|
<Setter.Value>
|
||||||
|
<SolidColorBrush Color="#FFFFFF" Opacity="0.1" />
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^ /template/ ContentControl#Content TextBlock">
|
||||||
|
<Setter Property="Foreground" Value="#AAAAAA" />
|
||||||
|
</Style>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<!-- <Style Selector="^:blackout /template/ ContentControl#Content"> -->
|
||||||
|
<!-- <Setter Property="Foreground" Value="{DynamicResource CalendarViewBlackoutForeground}" /> -->
|
||||||
|
<!-- <Setter Property="TextBlock.Foreground" Value="Red" /> -->
|
||||||
|
<!-- </Style> -->
|
||||||
|
|
||||||
|
<!-- <Style Selector="^:disabled /template/ ContentControl#Content"> -->
|
||||||
|
<!-- <Setter Property="TextBlock.Foreground" Value="Orange" /> -->
|
||||||
|
|
||||||
|
<!-- <Setter Property="Foreground" Value="{DynamicResource CalendarViewWeekDayForegroundDisabled}" /> -->
|
||||||
|
<!-- </Style> -->
|
||||||
|
</Style>
|
||||||
|
</Styles>
|
144
detect.gui/Assets/CalendarDatePicker.axaml
Normal file
144
detect.gui/Assets/CalendarDatePicker.axaml
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
<Styles xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||||
|
|
||||||
|
<Style Selector="CalendarDatePicker">
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource CalendarDatePickerForeground}" />
|
||||||
|
<Setter Property="BorderThickness" Value="{DynamicResource CalendarDatePickerBorderThemeThickness}" />
|
||||||
|
<Setter Property="CornerRadius" Value="6" />
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Left" />
|
||||||
|
<Setter Property="VerticalAlignment" Value="Center" />
|
||||||
|
<Setter Property="HorizontalContentAlignment" Value="Center" />
|
||||||
|
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||||
|
<Setter Property="Padding" Value="4" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate>
|
||||||
|
<DataValidationErrors>
|
||||||
|
<Panel x:Name="LayoutRoot"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Stretch">
|
||||||
|
<Border x:Name="Background"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}"
|
||||||
|
BorderThickness="{TemplateBinding BorderThickness}"
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
|
Background="{TemplateBinding Background}" />
|
||||||
|
<Grid ColumnDefinitions="*,Auto">
|
||||||
|
<TextBox Name="PART_TextBox"
|
||||||
|
Foreground="{TemplateBinding Foreground}"
|
||||||
|
Background="Transparent"
|
||||||
|
BorderBrush="Transparent"
|
||||||
|
BorderThickness="0"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}"
|
||||||
|
Margin="4, 1, 1, 1"
|
||||||
|
Padding="{TemplateBinding Padding}"
|
||||||
|
Watermark="{TemplateBinding Watermark}"
|
||||||
|
UseFloatingWatermark="{TemplateBinding UseFloatingWatermark}"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
Focusable="False"
|
||||||
|
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||||
|
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||||
|
Grid.Column="0">
|
||||||
|
<TextBox.Styles>
|
||||||
|
<Style Selector="TextBox#PART_TextBox:pointerover">
|
||||||
|
<Setter Property="BorderThickness" Value="0" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="TextBox#PART_TextBox:focus">
|
||||||
|
<Setter Property="BorderThickness" Value="1" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="TextBox#PART_TextBox:focus /template/ Border#PART_BorderElement">
|
||||||
|
<Setter Property="Background" Value="Transparent"/>
|
||||||
|
<Setter Property="BorderBrush" Value="Transparent"/>
|
||||||
|
<Setter Property="BorderThickness" Value="0" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="TextBox#PART_TextBox:disabled">
|
||||||
|
<Style Selector="^ /template/ Border#PART_BorderElement">
|
||||||
|
<Setter Property="Background" Value="Transparent" />
|
||||||
|
<Setter Property="BorderBrush" Value="Transparent" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^ /template/ TextBlock#PART_Watermark, ^ TextBlock#PART_FloatingWatermark">
|
||||||
|
<Setter Property="TextElement.Foreground" Value="{DynamicResource CalendarDatePickerTextForegroundDisabled}" />
|
||||||
|
</Style>
|
||||||
|
</Style>
|
||||||
|
</TextBox.Styles>
|
||||||
|
</TextBox>
|
||||||
|
<Button Name="PART_Button"
|
||||||
|
Margin="2,0,4,0"
|
||||||
|
Width="24"
|
||||||
|
Height="20"
|
||||||
|
Grid.Column="1"
|
||||||
|
Focusable="False">
|
||||||
|
<Button.Template>
|
||||||
|
<ControlTemplate>
|
||||||
|
<PathIcon Width="12"
|
||||||
|
Height="12"
|
||||||
|
Foreground="#FFFFFF"
|
||||||
|
Data="M1939 486L2029 576L1024 1581L19 576L109 486L1024 1401L1939 486Z" />
|
||||||
|
</ControlTemplate>
|
||||||
|
</Button.Template>
|
||||||
|
</Button>
|
||||||
|
<Popup Grid.Column="0"
|
||||||
|
Name="PART_Popup"
|
||||||
|
PlacementTarget="{TemplateBinding}"
|
||||||
|
IsLightDismissEnabled="True">
|
||||||
|
<Calendar Name="PART_Calendar"
|
||||||
|
CornerRadius="6, 6, 0, 0"
|
||||||
|
Margin="0, 5, 0, 0"
|
||||||
|
FirstDayOfWeek="{TemplateBinding FirstDayOfWeek}"
|
||||||
|
IsTodayHighlighted="{TemplateBinding IsTodayHighlighted}"
|
||||||
|
SelectedDate="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=SelectedDate, Mode=TwoWay}"
|
||||||
|
DisplayDate="{TemplateBinding DisplayDate}"
|
||||||
|
DisplayDateStart="{TemplateBinding DisplayDateStart}"
|
||||||
|
DisplayDateEnd="{TemplateBinding DisplayDateEnd}" />
|
||||||
|
</Popup>
|
||||||
|
</Grid>
|
||||||
|
</Panel>
|
||||||
|
</DataValidationErrors>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="CalendarDatePicker.filter">
|
||||||
|
<Setter Property="FontFamily" Value="{StaticResource Font-Family}" />
|
||||||
|
<Setter Property="FontSize" Value="14" />
|
||||||
|
<Setter Property="Background">
|
||||||
|
<Setter.Value>
|
||||||
|
<SolidColorBrush Color="#2D77F3" Opacity="0.2" />
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
<Setter Property="BorderBrush">
|
||||||
|
<Setter.Value>
|
||||||
|
<SolidColorBrush Color="#2D77F3" Opacity="0.3" />
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
<Setter Property="CornerRadius" Value="6" />
|
||||||
|
|
||||||
|
<!-- Pointer-over State -->
|
||||||
|
<Style Selector="^:pointerover /template/ Border#Background">
|
||||||
|
<Setter Property="Background">
|
||||||
|
<Setter.Value>
|
||||||
|
<SolidColorBrush Color="#2D77F3" Opacity="0.3" />
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
<Setter Property="BorderBrush">
|
||||||
|
<Setter.Value>
|
||||||
|
<SolidColorBrush Color="#2D77F3" Opacity="0.4" />
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<!-- Focused State -->
|
||||||
|
<Style Selector="^:focus-within:not(CalendarDatePicker:focus) /template/ Border#Background">
|
||||||
|
<Setter Property="Background">
|
||||||
|
<Setter.Value>
|
||||||
|
<SolidColorBrush Color="#2D77F3" Opacity="0.3" />
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
<Setter Property="BorderBrush">
|
||||||
|
<Setter.Value>
|
||||||
|
<SolidColorBrush Color="#2D77F3" Opacity="0.4" />
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
<Setter Property="BorderThickness" Value="1" />
|
||||||
|
</Style>
|
||||||
|
</Style>
|
||||||
|
</Styles>
|
221
detect.gui/Assets/ComboBox.axaml
Normal file
221
detect.gui/Assets/ComboBox.axaml
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
<Styles xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||||
|
<Styles.Resources>
|
||||||
|
<Thickness x:Key="ComboBoxTopHeaderMargin">0,0,0,4</Thickness>
|
||||||
|
<x:Int32 x:Key="ComboBoxPopupMaxNumberOfItems">15</x:Int32>
|
||||||
|
<x:Int32 x:Key="ComboBoxPopupMaxNumberOfItemsThatCanBeShownOnOneSide">7</x:Int32>
|
||||||
|
|
||||||
|
<Thickness x:Key="ComboBoxPadding">12,5,0,7</Thickness>
|
||||||
|
<Thickness x:Key="ComboBoxEditableTextPadding">11,5,32,6</Thickness>
|
||||||
|
<x:Double x:Key="ComboBoxMinHeight">32</x:Double>
|
||||||
|
|
||||||
|
<SolidColorBrush x:Key="ComboBoxDropDownGlyphForeground" Color="{StaticResource Light}" />
|
||||||
|
</Styles.Resources>
|
||||||
|
|
||||||
|
<Style Selector="ComboBox">
|
||||||
|
<Setter Property="Padding" Value="{DynamicResource ComboBoxPadding}" />
|
||||||
|
<Setter Property="FocusAdorner" Value="{x:Null}" />
|
||||||
|
<Setter Property="MaxDropDownHeight" Value="504" />
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource ComboBoxForeground}" />
|
||||||
|
<Setter Property="Background" Value="{DynamicResource ComboBoxBackground}" />
|
||||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ComboBoxBorderBrush}" />
|
||||||
|
<Setter Property="BorderThickness" Value="{DynamicResource ComboBoxBorderThemeThickness}" />
|
||||||
|
<Setter Property="CornerRadius" Value="{DynamicResource ControlCornerRadius}" />
|
||||||
|
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled" />
|
||||||
|
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
|
||||||
|
<Setter Property="MinHeight" Value="{DynamicResource ComboBoxMinHeight}" />
|
||||||
|
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||||
|
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Left" />
|
||||||
|
<Setter Property="VerticalAlignment" Value="Top" />
|
||||||
|
<Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" />
|
||||||
|
<Setter Property="PlaceholderForeground" Value="{DynamicResource ComboBoxPlaceHolderForeground}" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate>
|
||||||
|
<DataValidationErrors>
|
||||||
|
<Grid ColumnDefinitions="*,32">
|
||||||
|
<Border x:Name="Background"
|
||||||
|
Grid.Column="0"
|
||||||
|
Grid.ColumnSpan="2"
|
||||||
|
Background="{TemplateBinding Background}"
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
|
BorderThickness="{TemplateBinding BorderThickness}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}"
|
||||||
|
MinWidth="{DynamicResource ComboBoxThemeMinWidth}" />
|
||||||
|
<Border x:Name="HighlightBackground"
|
||||||
|
Grid.Column="0"
|
||||||
|
Grid.ColumnSpan="2"
|
||||||
|
Background="{DynamicResource ComboBoxBackgroundUnfocused}"
|
||||||
|
BorderBrush="{DynamicResource ComboBoxBackgroundBorderBrushUnfocused}"
|
||||||
|
BorderThickness="{TemplateBinding BorderThickness}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}"
|
||||||
|
IsVisible="False"/>
|
||||||
|
<TextBlock x:Name="PlaceholderTextBlock"
|
||||||
|
Grid.Column="0"
|
||||||
|
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||||
|
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||||
|
Margin="{TemplateBinding Padding}"
|
||||||
|
Text="{TemplateBinding PlaceholderText}"
|
||||||
|
Foreground="{TemplateBinding PlaceholderForeground}"
|
||||||
|
IsVisible="{TemplateBinding SelectionBoxItem, Converter={x:Static ObjectConverters.IsNull}}" />
|
||||||
|
<ContentControl x:Name="ContentPresenter"
|
||||||
|
Content="{TemplateBinding SelectionBoxItem}"
|
||||||
|
ContentTemplate="{TemplateBinding ItemTemplate}"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{TemplateBinding Padding}"
|
||||||
|
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||||
|
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}">
|
||||||
|
<ContentControl.Styles>
|
||||||
|
<Style Selector="TextBlock">
|
||||||
|
<Setter Property="Foreground" Value="#FFFFFF" />
|
||||||
|
<Setter Property="FontSize" Value="14" />
|
||||||
|
</Style>
|
||||||
|
</ContentControl.Styles>
|
||||||
|
</ContentControl>
|
||||||
|
|
||||||
|
<Border x:Name="DropDownOverlay"
|
||||||
|
Grid.Column="1"
|
||||||
|
Background="Transparent"
|
||||||
|
Margin="0,1,1,1"
|
||||||
|
Width="30"
|
||||||
|
IsVisible="False"
|
||||||
|
HorizontalAlignment="Right" />
|
||||||
|
|
||||||
|
<PathIcon x:Name="DropDownGlyph"
|
||||||
|
Grid.Column="1"
|
||||||
|
UseLayoutRounding="False"
|
||||||
|
IsHitTestVisible="False"
|
||||||
|
Height="12"
|
||||||
|
Width="12"
|
||||||
|
Margin="0,0,10,0"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Foreground="{DynamicResource ComboBoxDropDownGlyphForeground}"
|
||||||
|
Data="M1939 486L2029 576L1024 1581L19 576L109 486L1024 1401L1939 486Z"/>
|
||||||
|
|
||||||
|
<Popup Grid.Column="0"
|
||||||
|
Name="PART_Popup"
|
||||||
|
WindowManagerAddShadowHint="False"
|
||||||
|
IsOpen="{TemplateBinding IsDropDownOpen, Mode=TwoWay}"
|
||||||
|
MinWidth="{Binding Bounds.Width, RelativeSource={RelativeSource TemplatedParent}}"
|
||||||
|
MaxHeight="{TemplateBinding MaxDropDownHeight}"
|
||||||
|
PlacementTarget="Background"
|
||||||
|
IsLightDismissEnabled="True"
|
||||||
|
InheritsTransform="True">
|
||||||
|
<Border x:Name="PopupBorder"
|
||||||
|
Margin="0, 5, 0, 0"
|
||||||
|
BorderThickness="{DynamicResource ComboBoxDropdownBorderThickness}"
|
||||||
|
Padding="{DynamicResource ComboBoxDropdownBorderPadding}"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
CornerRadius="{DynamicResource OverlayCornerRadius}">
|
||||||
|
<Border.Background>
|
||||||
|
<SolidColorBrush Color="#13265a" />
|
||||||
|
</Border.Background>
|
||||||
|
<Border.BorderBrush>
|
||||||
|
<SolidColorBrush Color="#183171" />
|
||||||
|
</Border.BorderBrush>
|
||||||
|
<ScrollViewer HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
|
||||||
|
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}">
|
||||||
|
<ItemsPresenter Name="PART_ItemsPresenter"
|
||||||
|
Margin="{DynamicResource ComboBoxDropdownContentMargin}"
|
||||||
|
ItemsPanel="{TemplateBinding ItemsPanel}" />
|
||||||
|
</ScrollViewer>
|
||||||
|
</Border>
|
||||||
|
</Popup>
|
||||||
|
</Grid>
|
||||||
|
</DataValidationErrors>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="ComboBox.filter">
|
||||||
|
<Setter Property="FontFamily" Value="{StaticResource Font-Family}" />
|
||||||
|
<Setter Property="FontSize" Value="14" />
|
||||||
|
<Setter Property="Background">
|
||||||
|
<Setter.Value>
|
||||||
|
<SolidColorBrush Color="#2D77F3" Opacity="0.2" />
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
<Setter Property="BorderBrush">
|
||||||
|
<Setter.Value>
|
||||||
|
<SolidColorBrush Color="#2D77F3" Opacity="0.3" />
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
<Setter Property="CornerRadius" Value="6" />
|
||||||
|
<Setter Property="PlaceholderForeground" Value="{StaticResource Light-Gray}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="ComboBox.filter:pointerover /template/ Border#Background">
|
||||||
|
<Setter Property="Background">
|
||||||
|
<Setter.Value>
|
||||||
|
<SolidColorBrush Color="#2D77F3" Opacity="0.3" />
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
<Setter Property="BorderBrush">
|
||||||
|
<Setter.Value>
|
||||||
|
<SolidColorBrush Color="#2D77F3" Opacity="0.4" />
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="ComboBoxItem">
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource ComboBoxItemForeground}" />
|
||||||
|
<Setter Property="Background" Value="{DynamicResource ComboBoxItemBackground}" />
|
||||||
|
<Setter Property="Padding" Value="{DynamicResource ComboBoxItemThemePadding}" />
|
||||||
|
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate>
|
||||||
|
<ContentPresenter Name="PART_ContentPresenter"
|
||||||
|
Foreground="{TemplateBinding Foreground}"
|
||||||
|
Background="{TemplateBinding Background}"
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
|
BorderThickness="{TemplateBinding BorderThickness}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}"
|
||||||
|
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||||
|
Content="{TemplateBinding Content}"
|
||||||
|
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||||
|
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||||
|
Padding="{TemplateBinding Padding}">
|
||||||
|
<ContentPresenter.Styles>
|
||||||
|
<Style Selector="TextBlock">
|
||||||
|
<Setter Property="Foreground" Value="#FFFFFF" />
|
||||||
|
<Setter Property="FontSize" Value="14" />
|
||||||
|
</Style>
|
||||||
|
</ContentPresenter.Styles>
|
||||||
|
</ContentPresenter>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="ComboBoxItem /template/ ContentPresenter">
|
||||||
|
<Setter Property="Margin" Value="3, 0" />
|
||||||
|
<Setter Property="CornerRadius" Value="6" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="ComboBoxItem:pointerover /template/ ContentPresenter">
|
||||||
|
<Setter Property="Margin" Value="3, 0" />
|
||||||
|
<Setter Property="CornerRadius" Value="6" />
|
||||||
|
<Setter Property="Background">
|
||||||
|
<Setter.Value>
|
||||||
|
<SolidColorBrush Color="{StaticResource Blue9}" Opacity="0.3" />
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="ComboBoxItem:selected /template/ ContentPresenter">
|
||||||
|
<Setter Property="Margin" Value="3, 0" />
|
||||||
|
<Setter Property="CornerRadius" Value="6" />
|
||||||
|
<Setter Property="Background">
|
||||||
|
<Setter.Value>
|
||||||
|
<SolidColorBrush Color="{StaticResource Blue9}" Opacity="0.4" />
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="ComboBoxItem:pressed /template/ ContentPresenter">
|
||||||
|
<Setter Property="Margin" Value="3" />
|
||||||
|
<Setter Property="CornerRadius" Value="6" />
|
||||||
|
<Setter Property="Background">
|
||||||
|
<Setter.Value>
|
||||||
|
<SolidColorBrush Color="{StaticResource Blue9}" Opacity="0.3" />
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
</Styles>
|
48
detect.gui/Assets/DataGrid.axaml
Normal file
48
detect.gui/Assets/DataGrid.axaml
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<Styles xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||||
|
<Design.PreviewWith>
|
||||||
|
<Border Padding="20">
|
||||||
|
<!-- Add Controls for Previewer Here -->
|
||||||
|
</Border>
|
||||||
|
</Design.PreviewWith>
|
||||||
|
<Styles.Resources>
|
||||||
|
|
||||||
|
<SolidColorBrush x:Key="DataGridColumnHeaderForegroundBrush" Color="#FFFFFF" />
|
||||||
|
<SolidColorBrush x:Key="DataGridColumnHeaderBackgroundBrush" Color="#2D77F3" Opacity="0.2" />
|
||||||
|
<SolidColorBrush x:Key="DataGridColumnHeaderHoveredBackgroundColor" Color="#2D77F3" Opacity="0.2" />
|
||||||
|
<SolidColorBrush x:Key="DataGridColumnHeaderPressedBackgroundColor" Color="#2D77F3" Opacity="0.2" />
|
||||||
|
|
||||||
|
<SolidColorBrush x:Key="DataGridRowSelectedUnfocusedBackgroundBrush" Color="{StaticResource Blue9}" Opacity="0.2" />
|
||||||
|
<SolidColorBrush x:Key="DataGridRowSelectedHoveredUnfocusedBackgroundBrush" Color="{StaticResource Blue9}" Opacity="0.2" />
|
||||||
|
<SolidColorBrush x:Key="DataGridGridLinesBrush" Color="#2D77F3" Opacity="0.3" />
|
||||||
|
|
||||||
|
</Styles.Resources>
|
||||||
|
|
||||||
|
<Style Selector="DataGrid">
|
||||||
|
<Setter Property="Background" Value="#0d1540" />
|
||||||
|
<Setter Property="BorderThickness" Value="1" />
|
||||||
|
<Setter Property="BorderBrush" Value="{StaticResource DataGridGridLinesBrush}" />
|
||||||
|
<Setter Property="GridLinesVisibility" Value="All" />
|
||||||
|
<Setter Property="ColumnHeaderHeight" Value="30" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="DataGridCell">
|
||||||
|
<Setter Property="FontFamily" Value="{DynamicResource Font-Family}" />
|
||||||
|
<Setter Property="FontSize" Value="14" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="DataGrid:focus DataGridCell:current /template/ Grid#FocusVisual">
|
||||||
|
<Setter Property="IsVisible" Value="False" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="DataGridCell > TextBlock#CellTextBlock">
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Light}" />
|
||||||
|
<Setter Property="FontFamily" Value="{StaticResource Font-Family}" />
|
||||||
|
<Setter Property="FontSize" Value="14" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="DataGridRow:pointerover /template/ Rectangle#BackgroundRectangle">
|
||||||
|
<Setter Property="Fill" Value="{DynamicResource Blue1}" />
|
||||||
|
<Setter Property="Opacity" Value="0.4" />
|
||||||
|
</Style>
|
||||||
|
</Styles>
|
266
detect.gui/Assets/DefaultResources.axaml
Normal file
266
detect.gui/Assets/DefaultResources.axaml
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
<Styles xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:converters="clr-namespace:detect.gui.Converters">
|
||||||
|
<Styles.Resources>
|
||||||
|
<converters:WindowStateConverter x:Key="WindowStateConverter" />
|
||||||
|
<converters:SidebarMarginConverter x:Key="SidebarMarginConverter" />
|
||||||
|
|
||||||
|
<FontFamily x:Key="Font-Family">avares://detect.gui/Assets/Fonts/NotoSansSC-Regular.otf#Noto Sans SC</FontFamily>
|
||||||
|
|
||||||
|
<!-- <FontFamily x:Key="Font-Family">Noto Sans SC,等线,宋体,苹方-简,宋体-简,微软雅黑</FontFamily> -->
|
||||||
|
|
||||||
|
<Color x:Key="Light">#f8f9fa</Color>
|
||||||
|
<Color x:Key="Dark">#343a40</Color>
|
||||||
|
<Color x:Key="Light-Dark">#444a50</Color>
|
||||||
|
<Color x:Key="Gray">#6c757d</Color>
|
||||||
|
<Color x:Key="Light-Gray">#ccc</Color>
|
||||||
|
<Color x:Key="Green">#3d7620</Color>
|
||||||
|
<Color x:Key="Light-Green">#419c10</Color>
|
||||||
|
<Color x:Key="Orange">#fd7e14</Color>
|
||||||
|
<Color x:Key="Success">#28a745</Color>
|
||||||
|
<Color x:Key="Info">#17a2b8</Color>
|
||||||
|
<Color x:Key="Warning">#ffc107</Color>
|
||||||
|
<Color x:Key="Danger">#dc3545</Color>
|
||||||
|
<Color x:Key="Blue1">#3793d4</Color>
|
||||||
|
<Color x:Key="Blue2">#45afdf</Color>
|
||||||
|
<Color x:Key="Blue9">#92d6ed</Color>
|
||||||
|
<Color x:Key="Blue10">#c4d8f8</Color>
|
||||||
|
<Color x:Key="Gray1">#f0f0f0</Color>
|
||||||
|
<Color x:Key="Transparent">#00000000</Color>
|
||||||
|
|
||||||
|
<SolidColorBrush x:Key="BarBackground" Color="{StaticResource Dark}" Opacity="0.9" />
|
||||||
|
<SolidColorBrush x:Key="PanelHeaderBackground" Color="{StaticResource Blue1}" Opacity="0.9" />
|
||||||
|
<SolidColorBrush x:Key="DialogBackground" Color="{StaticResource Blue9}" Opacity="0.15" />
|
||||||
|
<!-- <SolidColorBrush x:Key="DialogBackground" Color="#3A62CB" Opacity="0.15" /> -->
|
||||||
|
<BoxShadows x:Key="DialogShadows">0 0 10 1 #2D77F3</BoxShadows>
|
||||||
|
<BoxShadows x:Key="LoginDialogShadows">0 0 0 1 #2D77F3</BoxShadows>
|
||||||
|
|
||||||
|
<LinearGradientBrush x:Key="TaskRunStateBackground" StartPoint="0%,0%" EndPoint="0%,100%">
|
||||||
|
<GradientStop Offset="0" Color="{StaticResource Blue9}" />
|
||||||
|
<GradientStop Offset="0.5" Color="{StaticResource Blue1}" />
|
||||||
|
<GradientStop Offset="1" Color="{StaticResource Blue2}" />
|
||||||
|
</LinearGradientBrush>
|
||||||
|
|
||||||
|
<Geometry x:Key="LogoData">
|
||||||
|
M22.505495 7.876923c0 3.375824 9.002198 16.879121 19.12967 28.131868 11.252747 12.378022 40.50989 63.015385 65.265934 113.652747 43.885714 88.896703 47.261538 92.272527 90.021978 97.898902s45.010989 7.876923 50.637363 50.637362 7.876923 45.010989 48.386813 48.386813c40.50989 3.375824 41.635165 4.501099 41.635165 48.386814v45.010989l-41.635165-3.375825c-40.50989-3.375824-42.76044-5.626374-46.136264-46.136263-3.375824-36.008791-7.876923-41.635165-31.507692-41.635165-21.38022 0-27.006593 5.626374-27.006594 28.131868C191.296703 437.731868 292.571429 540.131868 353.336264 540.131868c10.127473 0 18.004396 4.501099 18.004395 11.252747 0 5.626374-10.127473 11.252747-23.630769 11.252748-13.503297 0-40.50989 16.879121-61.89011 38.25934C247.56044 639.156044 247.56044 640.281319 247.56044 742.681319c0 56.263736-5.626374 113.652747-12.378022 126.030769-14.628571 28.131868-25.881319 33.758242 167.665934-94.523077L551.384615 675.164835V561.512088c0-108.026374-1.125275-113.652747-30.382417-141.784615-83.27033-77.643956-124.905495-104.650549-159.789011-104.65055-29.257143 0-34.883516-4.501099-34.883517-25.881319 0-34.883516-9.002198-45.010989-159.789011-175.542857C38.259341 3.375824 22.505495-7.876923 22.505495 7.876923zM1080.263736 21.38022c-9.002198 11.252747-122.654945 110.276923-250.936263 220.553846L595.27033 441.107692l3.375824 120.404396 3.375824 119.279121 155.287912 103.525275c85.520879 56.263736 151.912088 94.523077 146.285714 84.395604-4.501099-10.127473-9.002198-64.140659-9.002197-120.404396 0-121.52967-11.252747-146.285714-82.145055-177.793406l-52.887912-22.505495 39.384615-11.252747c51.762637-13.503297 108.026374-56.263736 130.531868-101.274725 11.252747-19.12967 38.259341-94.523077 63.015385-165.415385 23.630769-70.892308 60.764835-157.538462 82.145055-193.547253C1096.017582 41.635165 1114.021978 10.127473 1114.021978 6.751648c0-13.503297-16.879121-5.626374-33.758242 14.628572zM486.118681 90.021978C432.105495 101.274725 303.824176 156.413187 303.824176 168.791209c0 4.501099 10.127473 16.879121 22.505494 28.131868 21.38022 19.12967 23.630769 19.12967 81.019781-6.751648 85.520879-38.259341 235.182418-38.259341 327.454945 0 63.015385 25.881319 65.265934 25.881319 87.771428 5.626373l23.630769-21.38022-24.756044-19.12967C757.30989 103.525275 585.142857 69.767033 486.118681 90.021978zM2.250549 204.8c2.250549 16.879121 12.378022 27.006593 29.257143 29.257143 20.254945 3.375824 24.756044-1.125275 24.756044-24.756044S50.637363 180.043956 27.006593 180.043956s-28.131868 4.501099-24.756044 24.756044z M47.261538 311.701099c3.375824 40.50989 5.626374 42.76044 42.76044 42.760439s39.384615-2.250549 39.384615-39.384615-2.250549-39.384615-42.760439-42.760439l-42.76044-3.375825 3.375824 42.76044zM985.740659 414.101099c-12.378022 24.756044-15.753846 52.887912-10.127472 110.276923 4.501099 54.013187 0 99.024176-12.378022 148.536264-10.127473 39.384615-18.004396 91.147253-18.004396 115.903296 0 127.156044 91.147253-41.635165 99.024176-185.670329 5.626374-86.646154-12.378022-202.549451-31.507692-214.927473-5.626374-3.375824-18.004396 7.876923-27.006594 25.881319zM119.279121 403.973626c-3.375824 5.626374-12.378022 45.010989-20.254945 88.896704-10.127473 60.764835-10.127473 95.648352 0 148.536263 13.503297 74.268132 61.89011 191.296703 78.769231 191.296704 21.38022 0 25.881319-77.643956 7.876923-137.283517-11.252747-37.134066-16.879121-96.773626-15.753846-156.413187 1.125275-91.147253-11.252747-145.16044-34.883517-145.160439-5.626374 0-12.378022 4.501099-15.753846 10.127472zM285.81978 894.593407c-21.38022 16.879121-21.38022 16.879121 1.125275 34.883516 12.378022 10.127473 55.138462 33.758242 95.648352 54.013187 67.516484 32.632967 82.145055 34.883516 191.296703 34.883516 112.527473 0 121.52967-2.250549 205.925275-41.635164 90.021978-42.76044 103.525275-60.764835 63.015384-85.52088-19.12967-12.378022-31.507692-10.127473-82.145055 14.628572-118.153846 57.389011-255.437363 57.389011-372.465934 0-68.641758-32.632967-73.142857-33.758242-102.4-11.252747z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="LoginData">
|
||||||
|
M515.088689 523.818745c-107.548507 0-194.702413-84.58624-194.702413-188.68674a183.404684 183.404684 0 0 1 33.526377-105.71446 198.810678 198.810678 0 0 1 322.792244 0 186.485883 186.485883 0 0 1 19.147449 34.700166 21.12822 21.12822 0 0 1-38.881793 16.43306 146.063491 146.063491 0 0 0-14.672375-26.923808 156.627601 156.627601 0 0 0-253.905445 0 141.881864 141.881864 0 0 0-26.190189 81.505042c0 80.698061 68.593352 146.723748 152.812783 146.723748a152.225888 152.225888 0 0 0 141.808502-91.775704 21.12822 21.12822 0 0 1 38.955155 16.286336 194.335604 194.335604 0 0 1-180.690295 117.378998 M515.088689 534.823026c-113.637542 0-206.073503-89.574848-206.073503-199.691021a194.262242 194.262242 0 0 1 35.507147-112.096943 209.741597 209.741597 0 0 1 340.985989 0 197.490164 197.490164 0 0 1 20.321239 36.680937 31.985777 31.985777 0 0 1-17.019955 42.036354 32.132501 32.132501 0 0 1-42.109715-17.019955 129.19026 129.19026 0 0 0-13.792032-24.943037 145.549958 145.549958 0 0 0-235.858425 0 130.070602 130.070602 0 0 0-23.842609 75.342644c0 74.682388 63.604745 135.499381 141.808502 135.499381a141.294969 141.294969 0 0 0 132.051373-85.026412 32.132501 32.132501 0 0 1 59.276394 24.722952A205.413247 205.413247 0 0 1 515.088689 534.823026z m0-377.373479a185.678903 185.678903 0 0 0-152.519335 78.277119 172.25368 172.25368 0 0 0-31.545606 99.405339c0 98.011463 82.532108 177.682458 184.064941 177.682459a183.404684 183.404684 0 0 0 170.566357-110.923154 10.123939 10.123939 0 0 0-18.707278-7.336187 163.083445 163.083445 0 0 1-151.932441 98.451635c-90.308467 0-163.817064-70.647484-163.817064-157.507943a152.079164 152.079164 0 0 1 27.80415-88.034249 167.631882 167.631882 0 0 1 271.952466 0 155.233725 155.233725 0 0 1 15.992889 29.34475 10.123939 10.123939 0 0 0 13.278499 5.355416 9.977215 9.977215 0 0 0 5.428779-5.50214 9.903853 9.903853 0 0 0 0-7.776359 179.29642 179.29642 0 0 0-18.047021-32.646034A185.678903 185.678903 0 0 0 515.088689 157.449547zM303.586407 598.354408a90.675276 90.675276 0 0 0 0 181.27719h411.85356a90.675276 90.675276 0 0 0 0-181.27719H303.586407z m411.926921 223.680353H303.586407a132.858353 132.858353 0 0 1 0-265.643344h411.85356a132.858353 132.858353 0 0 1 0 265.643344z M715.513328 832.818957H303.586407a144.009358 144.009358 0 0 1-100.579129-245.542192 141.148245 141.148245 0 0 1 100.872577-42.183077h411.853559a143.862634 143.862634 0 0 1 0 287.651907zM303.513045 567.175612a119.213045 119.213045 0 0 0-85.246497 35.653871 122.000796 122.000796 0 0 0 85.319859 207.980912h411.926921a121.854072 121.854072 0 0 0 0-243.634783z m411.85356 223.460268H303.513045a99.331977 99.331977 0 0 1-71.161017-29.858283 101.899643 101.899643 0 0 1 71.161017-173.500832h412.000283a101.679557 101.679557 0 0 1 0 203.285753zM303.586407 609.285328a79.817719 79.817719 0 0 0-55.461576 135.939552 77.250053 77.250053 0 0 0 55.388214 23.329076H715.366605a79.670995 79.670995 0 0 0 0-159.268628z M512.080852 1024a511.992517 511.992517 0 1 1 511.919156-511.992517 512.579412 512.579412 0 0 1-511.919156 511.992517zM512.080852 58.777827a453.303018 453.303018 0 1 0 453.229657 453.229656A453.743189 453.743189 0 0 0 512.080852 59.071274z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="LogoutData">
|
||||||
|
M725.333333 736 725.333333 597.333333 426.666667 597.333333 426.666667 426.666667 725.333333 426.666667 725.333333 288 949.333333 512 725.333333 736M554.666667 85.333333C601.6 85.333333 640 123.733333 640 170.666667L640 341.333333 554.666667 341.333333 554.666667 170.666667 170.666667 170.666667 170.666667 853.333333 554.666667 853.333333 554.666667 682.666667 640 682.666667 640 853.333333C640 900.266667 601.6 938.666667 554.666667 938.666667L170.666667 938.666667C123.733333 938.666667 85.333333 900.266667 85.333333 853.333333L85.333333 170.666667C85.333333 123.733333 123.733333 85.333333 170.666667 85.333333L554.666667 85.333333Z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="CloseData">
|
||||||
|
M574.293333 512 810.666667 748.373333 810.666667 810.666667 748.373333 810.666667 512 574.293333 275.626667 810.666667 213.333333 810.666667 213.333333 748.373333 449.706667 512 213.333333 275.626667 213.333333 213.333333 275.626667 213.333333 512 449.706667 748.373333 213.333333 810.666667 213.333333 810.666667 275.626667 574.293333 512Z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="MinData">
|
||||||
|
M853.333333 597.333333 170.666667 597.333333 170.666667 426.666667 853.333333 426.666667
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="MaxData">
|
||||||
|
M170.666667 170.666667 853.333333 170.666667 853.333333 853.333333 170.666667 853.333333 170.666667 170.666667M256 341.333333 256 768 768 768 768 341.333333 256 341.333333Z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="RestoreData">
|
||||||
|
M170.666667 341.333333 341.333333 341.333333 341.333333 170.666667 853.333333 170.666667 853.333333 682.666667 682.666667 682.666667 682.666667 853.333333 170.666667 853.333333 170.666667 341.333333M682.666667 341.333333 682.666667 597.333333 768 597.333333 768 256 426.666667 256 426.666667 341.333333 682.666667 341.333333M256 512 256 768 597.333333 768 597.333333 512 256 512Z
|
||||||
|
</Geometry>
|
||||||
|
|
||||||
|
<Geometry x:Key="NotificationSuccessData">
|
||||||
|
M464.247573 677.487844C474.214622 686.649009 489.665824 686.201589 499.086059 676.479029L798.905035 367.037897C808.503379 357.131511 808.253662 341.319802 798.347275 331.721455 788.44089 322.12311 772.62918 322.372828 763.030833 332.279215L463.211857 641.720346 498.050342 640.711531 316.608838 473.940461C306.453342 464.606084 290.653675 465.271735 281.319298 475.427234 271.984922 485.582733 272.650573 501.382398 282.806071 510.716774L464.247573 677.487844Z M1024 512C1024 229.230208 794.769792 0 512 0 229.230208 0 0 229.230208 0 512 0 794.769792 229.230208 1024 512 1024 629.410831 1024 740.826187 984.331046 830.768465 912.686662 841.557579 904.092491 843.33693 888.379234 834.742758 877.590121 826.148587 866.801009 810.43533 865.021658 799.646219 873.615827 718.470035 938.277495 618.001779 974.048781 512 974.048781 256.817504 974.048781 49.951219 767.182496 49.951219 512 49.951219 256.817504 256.817504 49.951219 512 49.951219 767.182496 49.951219 974.048781 256.817504 974.048781 512 974.048781 599.492834 949.714859 683.336764 904.470807 755.960693 897.177109 767.668243 900.755245 783.071797 912.462793 790.365493 924.170342 797.659191 939.573897 794.081058 946.867595 782.373508 997.013826 701.880796 1024 608.898379 1024 512Z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="NotificationInformationData">
|
||||||
|
M1024 512C1024 229.230208 794.769792 0 512 0 229.230208 0 0 229.230208 0 512 0 794.769792 229.230208 1024 512 1024 629.410831 1024 740.826187 984.331046 830.768465 912.686662 841.557579 904.092491 843.33693 888.379234 834.742758 877.590121 826.148587 866.801009 810.43533 865.021658 799.646219 873.615827 718.470035 938.277495 618.001779 974.048781 512 974.048781 256.817504 974.048781 49.951219 767.182496 49.951219 512 49.951219 256.817504 256.817504 49.951219 512 49.951219 767.182496 49.951219 974.048781 256.817504 974.048781 512 974.048781 599.492834 949.714859 683.336764 904.470807 755.960693 897.177109 767.668243 900.755245 783.071797 912.462793 790.365493 924.170342 797.659191 939.573897 794.081058 946.867595 782.373508 997.013826 701.880796 1024 608.898379 1024 512Z M499.512194 743.02439C499.512194 756.818039 510.694157 768 524.487806 768 538.281453 768 549.463415 756.818039 549.463415 743.02439L549.463415 424.585365C549.463415 410.791718 538.281453 399.609756 524.487806 399.609756 510.694157 399.609756 499.512194 410.791718 499.512194 424.585365L499.512194 743.02439Z M499.512194 318.439025C499.512194 332.232672 510.694157 343.414635 524.487806 343.414635 538.281453 343.414635 549.463415 332.232672 549.463415 318.439025L549.463415 274.731708C549.463415 260.938059 538.281453 249.756098 524.487806 249.756098 510.694157 249.756098 499.512194 260.938059 499.512194 274.731708L499.512194 318.439025Z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="NotificationWaringData">
|
||||||
|
M598.272514 158.17909C545.018272 71.994036 451.264177 71.951401 397.724122 158.397341L25.049726 760.118586C-28.93569 847.283607 14.324655 927.325257 116.435565 929.308966L891.057077 929.313666C993.88467 931.315989 1036.926865 868.038259 983.25955 781.189694 980.374633 776.521099 980.374633 776.521099 971.719878 762.515313 967.393745 755.514432 967.393745 755.514432 963.78822 749.679695 956.511588 737.90409 941.113263 734.285867 929.3951 741.59817 917.676937 748.910473 914.076365 764.384279 921.352996 776.159885 924.958522 781.994622 924.958522 781.994622 929.284655 788.995503 937.939409 803.001289 937.939409 803.001289 940.824326 807.669884 972.284602 858.581314 957.441559 880.402549 891.539823 879.122276L116.918309 879.117577C54.037254 877.891296 33.95555 840.735497 67.458075 786.642217L440.132471 184.920971C474.112981 130.055931 522.112175 130.077759 556.029583 184.965509L857.08969 656.83971C864.534622 668.508595 879.98329 671.9032 891.595253 664.421773 903.207217 656.940343 906.585263 641.415949 899.140331 629.747063L598.272514 158.17909Z M474.536585 619.793346C474.536585 633.654611 485.718547 644.891386 499.512194 644.891386 513.305843 644.891386 524.487806 633.654611 524.487806 619.793346L524.487806 299.793346C524.487806 285.932082 513.305843 274.695307 499.512194 274.695307 485.718547 274.695307 474.536585 285.932082 474.536585 299.793346L474.536585 619.793346Z M474.465781 776.736145C474.565553 790.597047 485.828105 801.75225 499.621393 801.651987 513.414679 801.551725 524.515467 790.233967 524.415695 776.373065L523.955031 712.375667C523.855258 698.514767 512.592708 687.359563 498.79942 687.459825 485.006133 687.560087 473.905346 698.877847 474.005118 712.738748L474.465781 776.736145Z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="NotificationErrorData">
|
||||||
|
M1024 512C1024 229.230208 794.769792 0 512 0 229.230208 0 0 229.230208 0 512 0 794.769792 229.230208 1024 512 1024 629.410831 1024 740.826187 984.331046 830.768465 912.686662 841.557579 904.092491 843.33693 888.379234 834.742758 877.590121 826.148587 866.801009 810.43533 865.021658 799.646219 873.615827 718.470035 938.277495 618.001779 974.048781 512 974.048781 256.817504 974.048781 49.951219 767.182496 49.951219 512 49.951219 256.817504 256.817504 49.951219 512 49.951219 767.182496 49.951219 974.048781 256.817504 974.048781 512 974.048781 599.492834 949.714859 683.336764 904.470807 755.960693 897.177109 767.668243 900.755245 783.071797 912.462793 790.365493 924.170342 797.659191 939.573897 794.081058 946.867595 782.373508 997.013826 701.880796 1024 608.898379 1024 512Z M331.838918 663.575492C322.174057 673.416994 322.317252 689.230029 332.158756 698.894891 342.000258 708.559753 357.813293 708.416557 367.478155 698.575053L717.473766 342.182707C727.138628 332.341205 726.995433 316.528171 717.153931 306.863309 707.312427 297.198447 691.499394 297.341643 681.834532 307.183147L331.838918 663.575492Z M681.834532 698.575053C691.499394 708.416557 707.312427 708.559753 717.153931 698.894891 726.995433 689.230029 727.138628 673.416994 717.473766 663.575492L367.478155 307.183147C357.813293 297.341643 342.000258 297.198447 332.158756 306.863309 322.317252 316.528171 322.174057 332.341205 331.838918 342.182707L681.834532 698.575053Z
|
||||||
|
</Geometry>
|
||||||
|
|
||||||
|
<Geometry x:Key="SubmitData">
|
||||||
|
M640 384 213.333333 384 213.333333 213.333333 640 213.333333M512 810.666667C441.173333 810.666667 384 753.493333 384 682.666667 384 611.84 441.173333 554.666667 512 554.666667 582.826667 554.666667 640 611.84 640 682.666667 640 753.493333 582.826667 810.666667 512 810.666667M725.333333 128 213.333333 128C165.973333 128 128 166.4 128 213.333333L128 810.666667C128 857.6 166.4 896 213.333333 896L810.666667 896C857.6 896 896 857.6 896 810.666667L896 298.666667 725.333333 128Z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="OkData">
|
||||||
|
M896 298.666667 384 810.666667 149.333333 576 209.493333 515.84 384 689.92 835.84 238.506667 896 298.666667Z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="CancelData">
|
||||||
|
M810.666667 273.493333 750.506667 213.333333 512 451.84 273.493333 213.333333 213.333333 273.493333 451.84 512 213.333333 750.506667 273.493333 810.666667 512 572.16 750.506667 810.666667 810.666667 750.506667 572.16 512 810.666667 273.493333Z
|
||||||
|
</Geometry>
|
||||||
|
|
||||||
|
<Geometry x:Key="PageFirstData">
|
||||||
|
M785.493333 707.84L589.653333 512l195.84-195.84L725.333333 256l-256 256 256 256 60.16-60.16M256 256h85.333333v512H256V256z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="PageLastData">
|
||||||
|
M238.506667 316.16L434.346667 512l-195.84 195.84L298.666667 768l256-256-256-256-60.16 60.16M682.666667 256h85.333333v512h-85.333333V256z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="PageLeftData">
|
||||||
|
M657.493333 707.413333L462.08 512l195.413333-195.84L597.333333 256l-256 256 256 256 60.16-60.586667z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="PageRightData">
|
||||||
|
M366.506667 707.413333L561.92 512 366.506667 316.16 426.666667 256l256 256-256 256-60.16-60.586667z
|
||||||
|
</Geometry>
|
||||||
|
|
||||||
|
<Geometry x:Key="RecordAddData">
|
||||||
|
M832 448H576V192a64 64 0 0 0-128 0v256H192a64 64 0 0 0 0 128h256v256a64 64 0 1 0 128 0V576h256a64 64 0 1 0 0-128z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="RecordEditData">
|
||||||
|
M864 448a32 32 0 0 0-32 32v320c0 52.928-43.072 96-96 96h-512c-52.928 0-96-43.072-96-96v-512C128 235.072 171.072 192 224 192h320a32 32 0 0 0 0-64h-320A160.192 160.192 0 0 0 64 288v512C64 888.256 135.808 960 224 960h512c88.256 0 160-71.744 160-160v-320a32 32 0 0 0-32-32z M950.656 73.344a32 32 0 0 0-45.248 0l-448 448a32 32 0 0 0 45.248 45.312l448-448a32.128 32.128 0 0 0 0-45.312z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="RecordDeleteData">
|
||||||
|
M352 128h320c19.2 0 32-12.8 32-32s-12.8-32-32-32h-320c-19.2 0-32 12.8-32 32s12.8 32 32 32zM928 192h-832c-19.2 0-32 12.8-32 32s12.8 32 32 32H192v672c0 19.2 12.8 32 32 32h576c19.2 0 32-12.8 32-32V256h96c19.2 0 32-12.8 32-32s-12.8-32-32-32zM768 896H256V256h512v640z M416 768c19.2 0 32-12.8 32-32v-320c0-19.2-12.8-32-32-32s-32 12.8-32 32v320c0 19.2 12.8 32 32 32zM608 768c19.2 0 32-12.8 32-32v-320c0-19.2-12.8-32-32-32s-32 12.8-32 32v320c0 19.2 12.8 32 32 32z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="RecordReLoadData">
|
||||||
|
M522.964255 951.958925a42.56809 42.56809 0 0 1 3.165784-60.111891l59.39594-53.524341H441.097852c-211.178564 0-383.031816-171.808255-383.031817-383.043816v-14.147032a42.56709 42.56709 0 1 1 85.135181 0v14.147032c0 164.300769 133.652864 297.957632 297.896636 297.957632h144.432126l-59.395939-53.473344a42.56409 42.56409 0 1 1 56.944107-63.278675l141.874301 127.681272a42.557091 42.557091 0 0 1 0 63.278674l-141.877301 127.681272a42.677083 42.677083 0 0 1-60.110891-3.166783z m357.955531-368.998776v-14.147033c0-164.249772-133.70086-297.961632-297.947633-297.961631H438.49303l59.495933 53.524341a42.581089 42.581089 0 0 1-56.996104 63.278674L299.120557 259.977228a42.661084 42.661084 0 0 1 0-63.278674L440.995859 69.016282a42.581089 42.581089 0 0 1 56.996103 63.278674l-59.495933 53.473345h144.476124c211.178564 0 383.031816 171.859252 383.031816 383.044815v14.147033a42.542092 42.542092 0 1 1-85.084183 0z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="RecordSearchData">
|
||||||
|
M960 899.072l-195.584-195.584a386.56 386.56 0 1 0-51.2 55.296l194.56 194.56a38.4 38.4 0 1 0 52.224-54.272zM153.6 464.896a307.2 307.2 0 1 1 307.2 307.2 309.76 309.76 0 0 1-307.2-307.2z M460.8 215.552a249.856 249.856 0 0 0-248.832 249.344 25.6 25.6 0 0 0 51.2 0A198.656 198.656 0 0 1 460.8 266.752a25.6 25.6 0 0 0 0-51.2z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="RecordDetailData">
|
||||||
|
M186.181818 279.272727h651.636364v46.545455H186.181818zM186.181818 418.909091h651.636364v46.545454H186.181818zM186.181818 558.545455h651.636364v46.545454H186.181818zM186.181818 698.181818h651.636364v46.545455H186.181818z M837.818182 46.545455c79.127273 0 139.636364 60.509091 139.636363 139.636363v651.636364c0 79.127273-60.509091 139.636364-139.636363 139.636363H186.181818c-79.127273 0-139.636364-60.509091-139.636363-139.636363V186.181818c0-79.127273 60.509091-139.636364 139.636363-139.636363h651.636364m0-46.545455H186.181818C83.781818 0 0 83.781818 0 186.181818v651.636364c0 102.4 83.781818 186.181818 186.181818 186.181818h651.636364c102.4 0 186.181818-83.781818 186.181818-186.181818V186.181818c0-102.4-83.781818-186.181818-186.181818-186.181818z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="RecordSettingsData">
|
||||||
|
M407.466667 938.666667h-6.4c-32-8.533333-64-21.333333-93.866667-38.4-8.533333-4.266667-12.8-12.8-10.666667-21.333334l19.2-125.866666c-12.8-10.666667-23.466667-21.333333-34.133333-34.133334l-125.866667 19.2c-8.533333 2.133333-17.066667-2.133333-21.333333-10.666666-17.066667-29.866667-29.866667-59.733333-38.4-93.866667-2.133333-8.533333 0-17.066667 8.533333-23.466667l102.4-74.666666V512v-23.466667L106.666667 413.866667c-6.4-4.266667-10.666667-14.933333-8.533334-23.466667 8.533333-32 21.333333-64 38.4-93.866667 4.266667-8.533333 12.8-12.8 21.333334-10.666666l125.866666 19.2c10.666667-12.8 21.333333-23.466667 34.133334-34.133334L298.666667 147.2c-2.133333-8.533333 2.133333-17.066667 10.666666-21.333333 29.866667-17.066667 59.733333-29.866667 93.866667-38.4 8.533333-2.133333 17.066667 0 23.466667 8.533333l74.666666 102.4c17.066667-2.133333 32-2.133333 49.066667 0l74.666667-102.4c4.266667-6.4 14.933333-10.666667 23.466666-8.533333 32 8.533333 64 21.333333 93.866667 38.4 8.533333 4.266667 12.8 12.8 10.666667 21.333333l-19.2 125.866667c12.8 10.666667 23.466667 21.333333 34.133333 34.133333l125.866667-19.2c8.533333-2.133333 17.066667 2.133333 21.333333 10.666667 17.066667 29.866667 29.866667 59.733333 38.4 93.866666 2.133333 8.533333 0 17.066667-8.533333 23.466667L842.666667 490.666667v46.933333l102.4 74.666667c6.4 4.266667 10.666667 14.933333 8.533333 23.466666-8.533333 32-21.333333 64-38.4 93.866667-4.266667 8.533333-12.8 12.8-21.333333 10.666667l-125.866667-19.2c-10.666667 12.8-21.333333 23.466667-34.133333 34.133333l19.2 125.866667c2.133333 8.533333-2.133333 17.066667-10.666667 21.333333-29.866667 17.066667-59.733333 29.866667-93.866667 38.4-8.533333 2.133333-17.066667 0-23.466666-8.533333l-74.666667-102.4c-17.066667 2.133333-32 2.133333-49.066667 0L426.666667 932.266667c-6.4 4.266667-12.8 6.4-19.2 6.4z m149.333333-155.733334c6.4 0 12.8 4.266667 17.066667 8.533334l72.533333 100.266666c19.2-6.4 38.4-14.933333 57.6-23.466666l-17.066667-121.6c-2.133333-8.533333 2.133333-14.933333 8.533334-19.2 17.066667-12.8 32-27.733333 44.8-44.8 4.266667-6.4 12.8-8.533333 19.2-8.533334l121.6 19.2c8.533333-17.066667 17.066667-36.266667 23.466666-57.6l-100.266666-72.533333c-6.4-4.266667-8.533333-12.8-8.533334-19.2 2.133333-12.8 2.133333-21.333333 2.133334-32s0-19.2-2.133334-32c0-8.533333 2.133333-14.933333 8.533334-19.2l100.266666-72.533333c-6.4-19.2-14.933333-38.4-23.466666-57.6l-121.6 19.2c-8.533333 2.133333-14.933333-2.133333-19.2-8.533334-12.8-17.066667-27.733333-32-44.8-44.8-6.4-4.266667-8.533333-12.8-8.533334-19.2l17.066667-121.6c-19.2-8.533333-36.266667-17.066667-57.6-23.466666L576 230.4c-4.266667 6.4-12.8 8.533333-19.2 8.533333-23.466667-2.133333-40.533333-2.133333-64 0-8.533333 0-14.933333-2.133333-19.2-8.533333l-72.533333-100.266667c-19.2 6.4-38.4 14.933333-57.6 23.466667l19.2 121.6c2.133333 8.533333-2.133333 14.933333-8.533334 19.2-17.066667 12.8-32 27.733333-44.8 44.8-4.266667 6.4-12.8 8.533333-19.2 8.533333l-121.6-19.2c-8.533333 19.2-17.066667 36.266667-23.466666 57.6l100.266666 72.533334c6.4 4.266667 8.533333 12.8 8.533334 19.2-2.133333 10.666667-2.133333 21.333333-2.133334 32s0 19.2 2.133334 32c0 8.533333-2.133333 14.933333-8.533334 19.2l-100.266666 72.533333c6.4 19.2 14.933333 38.4 23.466666 57.6l121.6-19.2c8.533333-2.133333 14.933333 2.133333 19.2 8.533333 12.8 17.066667 27.733333 32 44.8 44.8 6.4 4.266667 8.533333 12.8 8.533334 19.2l-19.2 121.6c17.066667 8.533333 36.266667 17.066667 57.6 23.466667l72.533333-100.266667c4.266667-6.4 12.8-10.666667 19.2-8.533333 21.333333 2.133333 38.4 2.133333 64 0v2.133333z M509.866667 597.333333c-46.933333 0-85.333333-38.4-85.333334-85.333333s38.4-85.333333 85.333334-85.333333 85.333333 38.4 85.333333 85.333333-38.4 85.333333-85.333333 85.333333z m0-128c-23.466667 0-42.666667 19.2-42.666667 42.666667s19.2 42.666667 42.666667 42.666667 42.666667-19.2 42.666666-42.666667-19.2-42.666667-42.666666-42.666667z
|
||||||
|
</Geometry>
|
||||||
|
|
||||||
|
<Geometry x:Key="HomeAppData">
|
||||||
|
M387.264 479.68l-192 0c-52.928 0-96-43.072-96-96l0-192c0-52.928 43.072-96 96-96l192 0c52.928 0 96 43.072 96 96l0 192C483.264 436.64 440.192 479.68 387.264 479.68zM195.264 159.68c-17.632 0-32 14.368-32 32l0 192c0 17.632 14.368 32 32 32l192 0c17.632 0 32-14.368 32-32l0-192c0-17.632-14.368-32-32-32L195.264 159.68z M387.264 927.68l-192 0c-52.928 0-96-43.072-96-96l0-192c0-52.928 43.072-96 96-96l192 0c52.928 0 96 43.072 96 96l0 192C483.264 884.64 440.192 927.68 387.264 927.68zM195.264 607.68c-17.632 0-32 14.336-32 32l0 192c0 17.664 14.368 32 32 32l192 0c17.632 0 32-14.336 32-32l0-192c0-17.664-14.368-32-32-32L195.264 607.68z M832.128 479.68l-192 0c-52.928 0-96-43.072-96-96l0-192c0-52.928 43.072-96 96-96l192 0c52.928 0 96 43.072 96 96l0 192C928.128 436.64 885.056 479.68 832.128 479.68zM640.128 159.68c-17.664 0-32 14.368-32 32l0 192c0 17.632 14.336 32 32 32l192 0c17.664 0 32-14.368 32-32l0-192c0-17.632-14.336-32-32-32L640.128 159.68z M832.128 927.68l-192 0c-52.928 0-96-43.072-96-96l0-192c0-52.928 43.072-96 96-96l192 0c52.928 0 96 43.072 96 96l0 192C928.128 884.64 885.056 927.68 832.128 927.68zM640.128 607.68c-17.664 0-32 14.336-32 32l0 192c0 17.664 14.336 32 32 32l192 0c17.664 0 32-14.336 32-32l0-192c0-17.664-14.336-32-32-32L640.128 607.68z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="HomeCameraData">
|
||||||
|
M680.675556 673.536l92.700444 93.411556L910.222222 550.058667l-86.272-16.071111z m-85.020445 21.959111L185.543111 491.605333a80.128 80.128 0 0 1-27.534222-107.008l112.867555-203.463111a73.614222 73.614222 0 0 1 102.769778-28.672l418.474667 234.951111a79.189333 79.189333 0 0 1 30.321778 98.360889z m-295.480889-3.100444L172.942222 615.936a20.053333 20.053333 0 0 1-6.883555-26.766222l18.830222-33.905778a18.403556 18.403556 0 0 1 25.6-7.168l293.205333 149.845333a20.081778 20.081778 0 0 1 6.883556 26.766223l-18.801778 33.905777a18.432 18.432 0 0 1-25.6 7.168l-97.137778-32.028444-104.618666 100.408889a19.2 19.2 0 0 1-18.801778 19.569778H132.778667a19.2 19.2 0 0 1-18.830223-19.569778v-39.168a19.2 19.2 0 0 1 18.830223-19.569778H207.644444z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="HomeCenterData">
|
||||||
|
M993.882353 198.776471l-481.882353-186.729412-481.882353 186.729412 481.882353 192.752941zM18.070588 493.929412l493.929412 192.752941 493.929412-192.752941c18.070588-6.023529 24.094118-24.094118 18.070588-36.141177-6.023529-18.070588-24.094118-24.094118-36.141176-18.070588l-469.835295 186.729412-469.835294-186.729412c-18.070588-6.023529-30.117647 0-36.141176 18.070588-18.070588 12.047059-6.023529 30.117647 6.023529 36.141177zM981.835294 764.988235l-469.835294 186.729412-469.835294-186.729412c-18.070588-6.023529-30.117647 0-36.141177 18.070589-6.023529 18.070588 0 30.117647 18.070589 36.141176l493.929411 192.752941 493.929412-192.752941c18.070588-6.023529 24.094118-24.094118 18.070588-36.141176-12.047059-18.070588-30.117647-24.094118-48.188235-18.070589z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="HomeStatisticsData">
|
||||||
|
M918.766328 363.040062c41.44176 0 75.113191 33.67143 75.11319 75.11319v500.614058c0 41.501996-33.67143 75.113191-75.11319 75.11319H818.595328a75.173426 75.173426 0 0 1-75.113191-75.11319V438.093017c0-41.562231 33.611195-75.173426 75.113191-75.173426h100.110764v0.120471zM217.870507 463.090591c41.44176 0 75.113191 33.67143 75.11319 75.173426v400.382822c0 41.501996-33.67143 75.173426-75.11319 75.173426H117.759742a75.173426 75.173426 0 0 1-75.113191-75.173426V538.143547c0-41.501996 33.67143-75.113191 75.113191-75.113191h100.110765v0.060235zM568.318417 563.201355c41.44176 0 75.113191 33.67143 75.113191 75.113191v300.332293c0 41.501996-33.67143 75.173426-75.113191 75.173426H468.207653a75.173426 75.173426 0 0 1-75.113191-75.173426v-300.332293c0-41.44176 33.611195-75.113191 75.113191-75.113191h100.110764z m259.131473-487.482801l-59.030414 0.421646a37.526478 37.526478 0 0 1-0.602352-75.052956l150.587793-1.084232a37.285537 37.285537 0 0 1 37.827653 37.526478v150.888968a37.526478 37.526478 0 0 1-74.99272 0v-60.235117L544.826721 464.656704a37.406008 37.406008 0 0 1-41.742936 7.710095L52.40464 272.14527a37.646948 37.646948 0 0 1 30.47897-68.668033l426.946508 189.740618 317.559537-317.439066z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="HomeAnalysisData">
|
||||||
|
M180.192928 127.998133a69.758983 69.758983 0 0 0-69.446099 69.446099v629.096603A69.431876 69.431876 0 0 0 180.192928 895.986934h629.110825a69.758983 69.758983 0 0 0 69.431877-69.446099V197.444232A69.758983 69.758983 0 0 0 809.303753 127.998133H180.192928z m413.43397 468.473168a31.985311 31.985311 0 0 1-47.686415 0l-108.158423-111.998366-141.750822 145.604987a31.956867 31.956867 0 0 1-36.891906 7.864775 31.956867 31.956867 0 0 1-10.794509-7.864775 35.839477 35.839477 0 0 1 0-49.606387l165.444698-169.910411a31.971089 31.971089 0 0 1 47.672194 0l107.845538 111.998366 123.191093-127.998133a31.985311 31.985311 0 0 1 31.999533-8.959869 34.559496 34.559496 0 0 1 24.006761 24.632529 35.853699 35.853699 0 0 1-8.959869 33.606621L593.626898 596.471301z M568.880593 796.43283a227.538015 227.538015 0 0 0 388.460112 160.907875A227.594903 227.594903 0 0 0 1023.985067 796.43283a227.552237 227.552237 0 1 0-455.104474 0z M784.216119 693.906325h-44.358464L660.897029 898.959335h48.937953l17.635298-48.440183h68.89144l17.635298 48.440183h49.208171l-78.974848-205.05301z m-41.798501 115.852533l19.597936-53.176114 19.41305 53.176114h-39.010986zM928.086021 693.906325h-45.510447v205.05301h45.510447V693.906325z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="HomeApplyData">
|
||||||
|
M525.767111 5.12l381.553778 215.665778a38.684444 38.684444 0 0 1 19.000889 33.336889v431.559111a38.627556 38.627556 0 0 1-19.683556 33.393778l-381.155555 215.722666a39.992889 39.992889 0 0 1-39.253334 0L105.073778 719.018667A38.627556 38.627556 0 0 1 85.333333 685.568V254.122667a38.684444 38.684444 0 0 1 20.024889-33.336889l381.155556-215.608889a39.822222 39.822222 0 0 1 39.253333 0z m248.263111 324.152889a39.765333 39.765333 0 0 0-53.589333-14.108445L506.140444 436.337778 291.84 315.164444a39.936 39.936 0 0 0-53.532444 14.620445 38.115556 38.115556 0 0 0 14.449777 52.736L466.488889 503.011556v238.990222a39.253333 39.253333 0 0 0 78.506667 0V503.466667l214.584888-121.344a37.888 37.888 0 0 0 14.392889-52.792889z M85.333333 860.956444C93.639111 950.727111 222.264889 1005.056 471.153778 1024c126.464-0.568889 212.821333-8.874667 259.185778-24.746667 104.391111-32.085333 165.319111-64.170667 182.727111-96.142222 22.129778-31.345778 18.204444-64.170667-11.605334-98.417778-17.123556-15.018667-29.240889-24.405333-36.352-28.046222 34.929778 51.655111 22.414222 91.761778-37.660444 120.32-102.570667 54.385778-251.790222 72.931556-447.658667 55.637333-114.574222-19.512889-191.829333-45.681778-231.651555-78.336-34.929778-14.165333-36.977778-45.397333-6.030223-93.752888-19.569778 11.946667-36.465778 29.468444-50.801777 52.622222
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="HomeSettingsData">
|
||||||
|
M407.466667 938.666667h-6.4c-32-8.533333-64-21.333333-93.866667-38.4-8.533333-4.266667-12.8-12.8-10.666667-21.333334l19.2-125.866666c-12.8-10.666667-23.466667-21.333333-34.133333-34.133334l-125.866667 19.2c-8.533333 2.133333-17.066667-2.133333-21.333333-10.666666-17.066667-29.866667-29.866667-59.733333-38.4-93.866667-2.133333-8.533333 0-17.066667 8.533333-23.466667l102.4-74.666666V512v-23.466667L106.666667 413.866667c-6.4-4.266667-10.666667-14.933333-8.533334-23.466667 8.533333-32 21.333333-64 38.4-93.866667 4.266667-8.533333 12.8-12.8 21.333334-10.666666l125.866666 19.2c10.666667-12.8 21.333333-23.466667 34.133334-34.133334L298.666667 147.2c-2.133333-8.533333 2.133333-17.066667 10.666666-21.333333 29.866667-17.066667 59.733333-29.866667 93.866667-38.4 8.533333-2.133333 17.066667 0 23.466667 8.533333l74.666666 102.4c17.066667-2.133333 32-2.133333 49.066667 0l74.666667-102.4c4.266667-6.4 14.933333-10.666667 23.466666-8.533333 32 8.533333 64 21.333333 93.866667 38.4 8.533333 4.266667 12.8 12.8 10.666667 21.333333l-19.2 125.866667c12.8 10.666667 23.466667 21.333333 34.133333 34.133333l125.866667-19.2c8.533333-2.133333 17.066667 2.133333 21.333333 10.666667 17.066667 29.866667 29.866667 59.733333 38.4 93.866666 2.133333 8.533333 0 17.066667-8.533333 23.466667L842.666667 490.666667v46.933333l102.4 74.666667c6.4 4.266667 10.666667 14.933333 8.533333 23.466666-8.533333 32-21.333333 64-38.4 93.866667-4.266667 8.533333-12.8 12.8-21.333333 10.666667l-125.866667-19.2c-10.666667 12.8-21.333333 23.466667-34.133333 34.133333l19.2 125.866667c2.133333 8.533333-2.133333 17.066667-10.666667 21.333333-29.866667 17.066667-59.733333 29.866667-93.866667 38.4-8.533333 2.133333-17.066667 0-23.466666-8.533333l-74.666667-102.4c-17.066667 2.133333-32 2.133333-49.066667 0L426.666667 932.266667c-6.4 4.266667-12.8 6.4-19.2 6.4z m149.333333-155.733334c6.4 0 12.8 4.266667 17.066667 8.533334l72.533333 100.266666c19.2-6.4 38.4-14.933333 57.6-23.466666l-17.066667-121.6c-2.133333-8.533333 2.133333-14.933333 8.533334-19.2 17.066667-12.8 32-27.733333 44.8-44.8 4.266667-6.4 12.8-8.533333 19.2-8.533334l121.6 19.2c8.533333-17.066667 17.066667-36.266667 23.466666-57.6l-100.266666-72.533333c-6.4-4.266667-8.533333-12.8-8.533334-19.2 2.133333-12.8 2.133333-21.333333 2.133334-32s0-19.2-2.133334-32c0-8.533333 2.133333-14.933333 8.533334-19.2l100.266666-72.533333c-6.4-19.2-14.933333-38.4-23.466666-57.6l-121.6 19.2c-8.533333 2.133333-14.933333-2.133333-19.2-8.533334-12.8-17.066667-27.733333-32-44.8-44.8-6.4-4.266667-8.533333-12.8-8.533334-19.2l17.066667-121.6c-19.2-8.533333-36.266667-17.066667-57.6-23.466666L576 230.4c-4.266667 6.4-12.8 8.533333-19.2 8.533333-23.466667-2.133333-40.533333-2.133333-64 0-8.533333 0-14.933333-2.133333-19.2-8.533333l-72.533333-100.266667c-19.2 6.4-38.4 14.933333-57.6 23.466667l19.2 121.6c2.133333 8.533333-2.133333 14.933333-8.533334 19.2-17.066667 12.8-32 27.733333-44.8 44.8-4.266667 6.4-12.8 8.533333-19.2 8.533333l-121.6-19.2c-8.533333 19.2-17.066667 36.266667-23.466666 57.6l100.266666 72.533334c6.4 4.266667 8.533333 12.8 8.533334 19.2-2.133333 10.666667-2.133333 21.333333-2.133334 32s0 19.2 2.133334 32c0 8.533333-2.133333 14.933333-8.533334 19.2l-100.266666 72.533333c6.4 19.2 14.933333 38.4 23.466666 57.6l121.6-19.2c8.533333-2.133333 14.933333 2.133333 19.2 8.533333 12.8 17.066667 27.733333 32 44.8 44.8 6.4 4.266667 8.533333 12.8 8.533334 19.2l-19.2 121.6c17.066667 8.533333 36.266667 17.066667 57.6 23.466667l72.533333-100.266667c4.266667-6.4 12.8-10.666667 19.2-8.533333 21.333333 2.133333 38.4 2.133333 64 0v2.133333z M509.866667 597.333333c-46.933333 0-85.333333-38.4-85.333334-85.333333s38.4-85.333333 85.333334-85.333333 85.333333 38.4 85.333333 85.333333-38.4 85.333333-85.333333 85.333333z m0-128c-23.466667 0-42.666667 19.2-42.666667 42.666667s19.2 42.666667 42.666667 42.666667 42.666667-19.2 42.666666-42.666667-19.2-42.666667-42.666666-42.666667z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="CenterEventData">
|
||||||
|
M682.145684 286.181053c0-19.887158-16.087579-38.588632-36.001684-38.588632H214.069895c-19.887158 0-36.001684 18.701474-36.001684 38.615579 0 19.914105 16.114526 38.615579 36.001684 38.615579h432.074105c19.914105 0 36.001684-18.728421 36.001684-38.615579z m-468.075789 186.125473c-19.887158 0-36.001684 19.294316-36.001684 39.208421 0 19.887158 16.114526 39.208421 36.001684 39.208421h288.067368c19.887158 0 36.001684-19.321263 36.001684-39.208421 0-19.914105-16.114526-39.208421-36.028631-39.208421H214.069895zM430.106947 699.365053H214.069895c-19.887158 0-36.001684 19.294316-36.001684 39.208421 0 19.887158 16.114526 39.208421 36.001684 39.208421h216.037052c19.914105 0 36.001684-19.321263 36.001685-39.208421 0-19.914105-16.087579-39.208421-36.001685-39.208421z M370.822737 936.609684H138.617263c-36.217263 0-65.670737-28.16-65.670737-62.787368V135.275789c0-34.627368 29.453474-62.787368 65.670737-62.787368h660.830316c36.163368 0 65.616842 28.16 65.616842 62.787368v153.222737a35.974737 35.974737 0 1 0 72.003368 0V135.275789c0-74.347789-61.736421-134.817684-137.62021-134.817684H138.617263C62.679579 0.458105 0.916211 60.928 0.916211 135.275789v738.546527c0 74.320842 61.763368 134.790737 137.701052 134.790737h232.205474a35.974737 35.974737 0 1 0 0-72.003369zM828.847158 821.140211h-38.858105a10.132211 10.132211 0 0 0-7.922527 4.069052 15.710316 15.710316 0 0 0-3.260631 9.862737v23.174737c0 3.584 1.077895 7.141053 3.260631 9.862737 2.155789 2.694737 5.066105 4.042105 7.922527 4.042105h38.858105a10.24 10.24 0 0 0 7.922526-4.042105c2.155789-2.694737 3.287579-6.278737 3.287579-9.862737v-23.174737a15.872 15.872 0 0 0-3.287579-9.862737 10.24 10.24 0 0 0-7.922526-4.042105zM782.336 782.012632c2.155789 1.778526 5.012211 2.694737 7.868632 2.694736l38.588631 0.161685a12.314947 12.314947 0 0 0 7.949474-2.694737 8.434526 8.434526 0 0 0 3.287579-6.629053l-0.053895-66.371368v-102.130527a8.542316 8.542316 0 0 0-3.287579-6.575157 12.395789 12.395789 0 0 0-7.922526-2.748632h-38.804211a12.530526 12.530526 0 0 0-7.922526 2.748632 8.596211 8.596211 0 0 0-3.233684 6.54821v102.912l0.242526 65.482105a8.677053 8.677053 0 0 0 3.287579 6.575158z M1132.166737 851.536842l-247.969684-429.594947c-30.234947-52.547368-114.903579-52.547368-145.138527 0L490.981053 851.536842l-0.080842 0.053895a83.078737 83.078737 0 0 0 0.080842 83.806316 82.997895 82.997895 0 0 0 72.569263 41.849263H1059.570526a82.997895 82.997895 0 0 0 72.650106-41.930105 82.836211 82.836211 0 0 0-0.053895-83.779369zM1069.810526 899.287579a11.398737 11.398737 0 0 1-10.24 5.901474H563.550316a11.371789 11.371789 0 0 1-10.186105-5.928421 11.425684 11.425684 0 0 1 0-11.749053l247.942736-429.568c6.063158-10.293895 14.497684-10.293895 20.560842 0L1069.810526 887.565474c2.964211 5.093053 1.266526 9.566316 0 11.749052z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="CenterVideoData">
|
||||||
|
M768 480c127.3856 0 224 96.6144 224 224S895.3856 928 768 928s-224-96.6144-224-224a31.9488 31.9488 0 1 1 64 0c0 92.0064 67.9936 160 160 160s160-67.9936 160-160-67.9936-160-160-160a31.9488 31.9488 0 1 1 0-64z m0 64a32 32 0 0 1 31.488 26.2144l0.512 5.7856v114.7392l54.6304 54.5792a32 32 0 0 1 3.6864 40.8576l-3.6864 4.5056a32 32 0 0 1-40.8064 3.584l-4.5056-3.584-64-64a32 32 0 0 1-8.96-17.6128l-0.3584-5.0688v-128a31.9488 31.9488 0 0 1 32-32zM832 256a64 64 0 0 1 64 64l0.0512 162.304a254.464 254.464 0 0 0-64-26.2144L832 320H256v384h256c0 22.1184 2.816 43.52 8.0896 64H256a64 64 0 0 1-64-64v-54.8352l-83.4048 35.6864a32.0512 32.0512 0 0 1-44.5952-29.44V368.64a32 32 0 0 1 44.5952-29.44l83.4048 35.84V320A64 64 0 0 1 256 256h576z m-224 320a31.9488 31.9488 0 1 1 0 63.9488 31.9488 31.9488 0 0 1 0-63.9488zM448 384L640 512l-192 128v-256z m-320 32v192l64-32v-128L128 416zM672 512a31.9488 31.9488 0 1 1 0 63.9488 31.9488 31.9488 0 0 1 0-63.9488z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="CenterPictureData">
|
||||||
|
M896 460.8c-17.066667 0-34.133333 12.8-34.133333 34.133333V597.333333l-89.6-68.266666c-29.866667-21.333333-72.533333-21.333333-98.133334 8.533333l-98.133333 98.133333-140.8-123.733333c-29.866667-25.6-72.533333-25.6-98.133333 0l-174.933334 157.866667v-512h375.466667c17.066667 0 34.133333-12.8 34.133333-34.133334s-12.8-34.133333-34.133333-34.133333H128c-17.066667 4.266667-34.133333 21.333333-34.133333 38.4v768c0 17.066667 12.8 34.133333 34.133333 34.133333h768c17.066667 0 34.133333-12.8 34.133333-34.133333v-405.333333c0-17.066667-17.066667-29.866667-34.133333-29.866667z m-34.133333 401.066667H162.133333v-102.4l217.6-200.533334c4.266667 0 4.266667-4.266667 8.533334-4.266666s4.266667 0 8.533333 4.266666l268.8 230.4c12.8 12.8 34.133333 8.533333 46.933333-4.266666 12.8-12.8 8.533333-34.133333-4.266666-46.933334l-76.8-64 93.866666-93.866666c4.266667-4.266667 8.533333-4.266667 12.8 0l128 93.866666v187.733334z M725.333333 93.866667c-110.933333 0-204.8 89.6-204.8 204.8s89.6 204.8 204.8 204.8 204.8-89.6 204.8-204.8-93.866667-204.8-204.8-204.8z m0 341.333333c-76.8 0-136.533333-64-136.533333-136.533333s64-136.533333 136.533333-136.533334 136.533333 64 136.533334 136.533334-59.733333 136.533333-136.533334 136.533333z M819.2 320c0 17.066667-12.8 34.133333-34.133333 34.133333h-85.333334c-8.533333 0-17.066667-4.266667-21.333333-8.533333-4.266667-4.266667-8.533333-12.8-8.533333-21.333333v-85.333334c0-17.066667 12.8-34.133333 34.133333-34.133333s34.133333 12.8 34.133333 34.133333v51.2h51.2c17.066667 0 29.866667 12.8 29.866667 29.866667z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="SettingsRegionData">
|
||||||
|
M395.968 239.552l232.064-140.8v709.504l-232.064 139.072zM76.928 76.928l232 138.88v684.416l-232-140.096z m638.144 0l232.256 138.88v684.416l-232-140.096z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="SettingsDeviceData">
|
||||||
|
M967.111111 20.935111c18.033778 0 32.995556 13.312 35.555556 30.663111l0.398222 5.290667v910.222222c0 18.033778-13.312 32.995556-30.663111 35.555556l-5.290667 0.398222H488.049778v-71.907556h443.107555V92.785778H308.394667v203.662222H236.544V56.888889c0-18.033778 13.312-32.995556 30.606222-35.555556L272.497778 20.935111H967.111111z M56.888889 296.391111h431.160889V967.111111H56.888889z M296.391111 787.456a35.953778 35.953778 0 0 1 5.347556 71.452444l-5.290667 0.398223h-47.957333a35.953778 35.953778 0 0 1-5.290667-71.452445l5.290667-0.398222h47.957333z M655.701333 787.456a35.953778 35.953778 0 0 1 5.347556 71.452444l-5.347556 0.398223h-47.900444a35.953778 35.953778 0 0 1-5.290667-71.452445l5.290667-0.398222h47.900444z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="SettingsStorageData">
|
||||||
|
M844.0576 435.2H781.6704l5.73696-327.0144c0-13.91616-23.84896-18.88512-37.76512-18.88512H120.20736c-13.91104 0-31.47008 17.55648-31.47008 31.47264v346.1888h188.83072c13.9136 0 31.47264 30.14912 31.47264 44.06272v81.82528H435.2v62.94272h-157.632c1.19296 5.03552-31.47008-17.5616-31.47008-31.47264v-94.4128H88.73728v251.77344c0-1.19552 17.5616 0 31.47008 0H435.2v62.94272H88.73728c-27.83232 0-62.94272-35.11808-62.94272-62.94528V89.30048c0-27.82976 35.1104-62.94272 62.94272-62.94272H781.11744c27.83232 0 62.9376 35.11296 62.9376 62.94272V435.2z m-182.53312-251.48416c13.91872 0 25.1776 17.55648 25.1776 31.47264 0 13.9136-17.55904 31.47264-31.47264 31.47264H214.6304c-13.91872 0-31.4752-17.55648-31.4752-31.47264 0-13.91616 11.264-31.47264 25.1776-31.47264h453.19424z m-6.29504 157.36064c13.91104 0 31.47264 17.5488 31.47264 31.47008 0 13.9136-17.5616 31.47008-31.47264 31.47008H214.6304c-14.88896 3.67872-36.61568 1.8816-31.4752-31.47008 2.11968-13.75744 17.55648-31.47008 31.47008-31.47008H655.232z m269.79072 657.20064H559.488c-40.35328 0-73.088-33.9328-73.088-75.8272 0-8.25856 1.58464-16.00512 3.93728-23.4496l62.04672-369.94048c2.5344-23.28832 21.5424-41.33376 44.93312-42.65984h289.9584c22.85056 0.85504 41.33888 18.52928 44.71808 41.50784l62.2208 371.008c2.30912 7.44448 3.93472 15.232 3.93472 23.53408 0 41.8944-32.77824 75.8272-73.12896 75.8272z m-0.09728-154.36032c8.58368 0 16.67072 1.83552 24.2176 4.67712l-55.12192-334.2208h-306.3296L532.48 848.64c7.5904-2.79552 15.67744-4.67712 24.2176-4.67712h368.27136l-0.04352-0.04608z m6.24384 30.3232h-378.3168c-20.82816 0-37.79328 20.94592-37.79328 46.66368 0 25.88416 16.96512 46.77632 37.79328 46.77632h378.26816c20.87424 0 37.84192-20.8896 37.84192-46.77632 0-25.71776-17.01376-46.66368-37.84192-46.66368h0.04864z m-24.9344 69.26848c-6.2336-0.0896-12.1728-2.57792-16.49664-6.912-4.3264-4.3392-6.67904-10.1632-6.53824-16.18432 0-12.66944 10.26816-22.98368 23.03488-22.98368 12.76416 0 23.03488 10.31424 23.03488 22.98368 0.12288 6.016-2.23488 11.83232-6.55872 16.1664-4.32128 4.33152-10.24768 6.82496-16.47616 6.92992z m-216.35584-259.07712c0-26.67008 23.53408-48.27136 52.3776-48.27136 28.84096 0 52.29312 21.63968 52.29312 48.27136 19.3408 0 34.9184 14.4768 34.9184 32.2688 0 17.8304-15.5776 32.27136-34.9184 32.27136H689.92c-19.25888 0-34.87744-14.4384-34.87744-32.27136 0-17.82784 15.61856-32.27136 34.87744-32.27136h-0.04352z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="SettingsAlgoData">
|
||||||
|
M857.23116 419.240086h-455.705693v50.921801h296.731702v50.871228H811.48639L935.486312 630.091971h110.512664c10.729759-30.550833 40.510741-50.201721 72.818827-48.05031 42.561003-3.544208 79.939354 28.086344 83.482759 70.648149 0.316289 3.793065 0.350808 7.604594 0.106768 11.402477 3.158078 42.081752-28.397817 78.753669-70.480372 81.910944-0.776274 0.057799-1.55897 0.105162-2.338455 0.139682-37.45943 2.428365-71.826609-20.763521-83.588724-56.409505H919.33548L784.517495 574.592803H533.8027v42.849998H636.365581c-0.358033 16.615633 0 76.306841 0 76.306841h51.281441l88.972067 114.95597h64.718123c8.783054-26.846072 33.753866-45.063222 61.998356-45.230197 39.378039-4.645602 75.065766 23.50978 79.710565 62.887819 0.325923 2.751076 0.491292 5.519813 0.494503 8.290156 6.457443 38.173891-19.252717 74.354516-57.425805 80.813565a69.97463 69.97463 0 0 1-11.189743 0.980176c-34.589545-0.765838-65.967226-20.462484-81.69179-51.281441h-75.64456c1.641655 2.666786-94.357818-114.699085-94.357818-114.699084 1.282016-0.307459-188.714834 0-188.714835 0a528.658581 528.658581 0 0 1 0 59.383749H614.719822l24.255549 62.203863a24.408074 24.408074 0 0 1-5.384949 28.307105c-94.357818 84.819363-266.66333 70.665811-266.66333 70.66581-15.04703-5.892296-24.761291-20.615812-24.255549-36.768249-4.178393-40.483446-40.383101-69.915225-80.865745-65.737635a73.716318 73.716318 0 0 0-16.158057 3.533772c-87.895559 17.53801-79.38384-32.410037-76.408792-37.742807 3.589966-49.897473-28.768695-47.076557-28.768695-47.076556-45.794541-2.820114-24.255549-22.614697-24.255549-22.614697 35.07602-16.973666 8.102309-22.614697 8.102309-22.614697-56.409504-4.256261-26.973712-28.307105-26.973712-28.307105 45.845918-57.948405-29.640498-65.02478-29.640498-65.02478-97.075981-1.383967 0-96.100622 0-96.100621a125.943416 125.943416 0 0 0 26.973712-115.947385 280.353687 280.353687 0 0 1 24.306926-186.715949c57.389681-85.827636 152.83926-138.493044 256.048367-141.280244A336.308826 336.308826 0 0 1 614.976706 187.808512h88.972068l145.486733-71.91411h102.468958c6.576252-34.475552 37.794183-58.660458 72.818826-56.409504 43.656777-2.524696 81.094533 30.818957 83.616821 74.474931a79.289114 79.289114 0 0 1-0.077868 10.344432c-0.738544 39.072185-28.933262 72.2015-67.384909 79.178332a101.231896 101.231896 0 0 1-88.972068-39.589166h-91.547337L704.100496 261.450173h-208.045419v53.742719h202.202092v53.742718H854.514602C862.052567 323.909316 892.557643 299.826361 939.061025 300.269488c1.477088 0.071446 2.954979 0.183833 4.425644 0.337161C983.270105 299.826361 1021.802832 329.528672 1024.553105 368.384111c0.390947 4.879207 0.933616 6.044019 0.460787 10.916-0.219155 39.610038-14.717093 90.979783-81.07928 92.050672-1.12066 0.018464-4.281147-0.535444-8.027651 0-20.33645 0-58.869177-8.563096-78.675801-52.110697z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="SettingsConfigData">
|
||||||
|
M799.878 928.557a40.632 40.632 0 0 1-28.9-12.14 69.759 69.759 0 0 0-35.24-21.79 68.719 68.719 0 0 0-34.69 21.13 39.975 39.975 0 0 1-28.59 12.24 37.21 37.21 0 0 1-14.93-3.05l-1.73-0.76-53.43-29.71c-14.24-10.11-19.49-28.79-12.6-44.84a49.972 49.972 0 0 0 3.76-16.3c-0.36-22.92-19.08-41.3-42-41.24h-2.34c-16.03-0.9-28.92-13.54-30.12-29.56a270.966 270.966 0 0 1-4.52-42.71v-0.25c0.37-14.5 1.88-28.95 4.52-43.22 1.4-15.95 14.24-28.49 30.22-29.51h2.08c23.1 0 41.9-18.6 42.15-41.69a48.42 48.42 0 0 0-3.71-15.9c-6.73-15.9-1.61-34.33 12.34-44.49l1.83-1.22 56.22-30.47a37.97 37.97 0 0 1 14.73-2.94h0.51c10.51 0.01 20.59 4.15 28.08 11.53a69.447 69.447 0 0 0 34.54 20.32 68.513 68.513 0 0 0 34.23-20.01 40.6 40.6 0 0 1 28.29-11.58c5.25-0.02 10.44 1.07 15.24 3.2l1.07 0.51 54.44 30.47c14.2 10.05 19.52 28.6 12.8 44.64a49.075 49.075 0 0 0-3.81 16.35c0.39 22.93 19.12 41.3 42.05 41.24h2.64c15.88 1.17 28.53 13.74 29.81 29.61 2.6 14.1 4.13 28.38 4.57 42.71v0.3c-0.38 14.48-1.88 28.91-4.47 43.17-1.52 15.89-14.29 28.36-30.22 29.51h-2.13c-23.1 0.02-41.9 18.6-42.21 41.69 0.32 5.48 1.61 10.86 3.81 15.9 6.74 15.99 1.49 34.52-12.65 44.59l-1.57 1.07-55.1 30.22a38.908 38.908 0 0 1-14.83 3.05l-0.14-0.07z m-12.9-63.89c4.98 3.65 9.73 7.6 14.22 11.83l33.11-18.18a93.96 93.96 0 0 1-3.86-24.12c0.11-44.88 32.69-83.09 76.99-90.3 1.15-8.15 1.86-16.35 2.13-24.58-0.3-8.11-1.01-16.19-2.13-24.22-44.23-7.07-76.83-45.15-76.99-89.94 0.25-8.29 1.56-16.52 3.91-24.48l-32.81-18.13c-4.46 3.96-9.13 7.66-14.02 11.07a86.312 86.312 0 0 1-49.72 18.23 88.506 88.506 0 0 1-50.02-18.54c-4.9-3.51-9.6-7.29-14.07-11.33l-34.38 18.69a93.086 93.086 0 0 1 3.86 24.07c-0.09 44.9-32.67 83.14-76.99 90.35a231.08 231.08 0 0 0-2.23 24.78c0.29 8.07 1 16.12 2.13 24.12 44.22 7.06 76.81 45.12 76.99 89.89a96.4 96.4 0 0 1-3.86 24.68l31.44 17.37c4.41-4.18 9.1-8.06 14.02-11.63a88.345 88.345 0 0 1 50.79-19.2c18.81 0.83 36.86 7.66 51.49 19.5v0.07z m-606.45 43.63c-18.56-0.03-33.59-15.06-33.62-33.62 0.03-18.56 15.06-33.59 33.62-33.62h250.63c18.57 0 33.64 15.04 33.67 33.62-0.03 18.58-15.1 33.62-33.67 33.62h-250.63z m555.92-104.57a84.095 84.095 0 0 1-59.37-24.38 83.929 83.929 0 0 1-25.39-59.67 84.132 84.132 0 0 1 24.38-59.98 83.975 83.975 0 0 1 59.68-25.39c46.71 0.08 84.63 37.8 84.96 84.51-0.11 46.47-37.54 84.21-84 84.71l-0.26 0.2z m0-119.81a35.48 35.48 0 0 0-24.93 10.16 35.085 35.085 0 0 0-10.51 24.84 35.102 35.102 0 0 0 10.15 24.94 35.122 35.122 0 0 0 24.79 10.46c19.53 0 35.41-15.76 35.55-35.3-0.08-19.29-15.65-34.93-34.94-35.1h-0.11z m-528.95 48.55c-37.88 0.1-74.26-14.78-101.22-41.39-27-26.46-42.46-62.5-43.02-100.3v-335.6c-0.11-37.86 14.8-74.22 41.44-101.12a143.417 143.417 0 0 1 100.35-42.91h557.33c78.53 0.29 142.5 63.13 144.18 141.64v195.38c-0.03 18.56-15.06 33.59-33.62 33.62-18.21-0.16-33.05-14.66-33.62-32.86v-193.75c-0.21-41.88-33.8-75.94-75.67-76.74h-556.15c-41.89 0.13-76.03 33.64-76.94 75.52v334.43c0.27 41.86 33.82 75.89 75.67 76.74h208.73c18.56 0.03 33.59 15.06 33.62 33.62 0.14 18.31-14.5 33.31-32.81 33.62h-208.22l-0.05 0.1z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="SettingsUserData">
|
||||||
|
M452.87552 32c115.2 0 208 92.8 208 208s-92.8 208-208 208-208-92.8-208-208 92.8-208 208-208m0-32c-132.8 0-240 107.2-240 240s107.2 240 240 240 240-107.2 240-240-107.2-240-240-240z M588.87552 491.2l4.8-24-4.8-3.2s-145.6-19.2-305.6 0c-11.2 3.2-148.8 43.2-236.8 184-68.8 118.4-108.8 328 236.8 347.2h217.6l6.4-33.6H289.67552C-55.92448 942.4 9.67552 768 78.47552 649.6c88-140.8 212.8-161.6 225.6-164.8 160-19.2 280 3.2 280 3.2l4.8 3.2zM750.47552 689.6c35.2 0 64 28.8 64 64s-28.8 64-64 64-64-28.8-64-64 28.8-64 64-64m0-32c-52.8 0-96 43.2-96 96s43.2 96 96 96 96-43.2 96-96-43.2-96-96-96z M692.87552 520c1.6 3.2 3.2 8 4.8 9.6 11.2 27.2 24 54.4 52.8 54.4 4.8 0 8 0 12.8-1.6 19.2-6.4 32-25.6 43.2-44.8 3.2-4.8 6.4-11.2 9.6-14.4l35.2 12.8c-1.6 3.2-3.2 8-3.2 11.2-8 25.6-22.4 68.8 16 83.2 4.8 1.6 11.2 3.2 16 3.2 14.4 0 32-6.4 48-12.8 4.8-1.6 11.2-4.8 17.6-6.4l12.8 22.4 9.6 17.6c-4.8 3.2-9.6 6.4-12.8 8-27.2 17.6-52.8 33.6-49.6 60.8 1.6 32 38.4 41.6 67.2 49.6 4.8 1.6 9.6 3.2 16 4.8l-1.6 4.8-4.8 33.6c-4.8 0-11.2 0-17.6-1.6-9.6 0-19.2-1.6-28.8-1.6-40 0-51.2 17.6-54.4 33.6-3.2 22.4 9.6 43.2 22.4 65.6l9.6 14.4-14.4 12.8-12.8 9.6-9.6-9.6c-19.2-17.6-35.2-33.6-56-33.6-9.6 0-17.6 3.2-24 9.6-16 12.8-20.8 36.8-25.6 57.6-1.6 4.8-1.6 11.2-3.2 16h-30.4c-1.6-6.4-3.2-12.8-3.2-19.2-4.8-32-12.8-72-48-72-24 0-41.6 17.6-60.8 35.2-3.2 3.2-8 8-12.8 11.2-3.2-3.2-8-6.4-11.2-9.6l-9.6-9.6c1.6-3.2 4.8-6.4 6.4-9.6 16-22.4 40-57.6 11.2-81.6-14.4-12.8-36.8-14.4-64-16-8 0-19.2-1.6-27.2-1.6l-6.4-41.6c6.4-1.6 14.4-4.8 20.8-6.4 33.6-9.6 76.8-20.8 70.4-59.2-4.8-24-25.6-40-46.4-56-4.8-3.2-11.2-8-16-12.8l11.2-19.2 8-12.8c6.4 3.2 11.2 4.8 16 8 25.6 11.2 41.6 19.2 56 19.2s25.6-6.4 32-17.6c11.2-20.8 6.4-44.8 0-68.8-1.6-4.8-1.6-9.6-3.2-14.4l19.2-6.4 12.8-8m11.2-36.8c-24 8-44.8 16-67.2 24-24 8 12.8 72 0 92.8-1.6 1.6-3.2 1.6-4.8 1.6-16 0-67.2-32-83.2-32-1.6 0-4.8 0-4.8 1.6-12.8 22.4-25.6 43.2-38.4 65.6-12.8 22.4 62.4 52.8 65.6 76.8 3.2 19.2-97.6 27.2-94.4 46.4 4.8 25.6 9.6 51.2 14.4 78.4 3.2 19.2 84.8 9.6 99.2 22.4 12.8 11.2-46.4 67.2-35.2 76.8 19.2 16 28.8 27.2 48 43.2 1.6 1.6 4.8 1.6 6.4 1.6 20.8 0 54.4-51.2 76.8-51.2 19.2 0 16 91.2 33.6 91.2h62.4c22.4 0 17.6-67.2 35.2-81.6 1.6-1.6 3.2-1.6 4.8-1.6 16 0 51.2 48 67.2 48 1.6 0 3.2 0 4.8-1.6 17.6-14.4 35.2-30.4 54.4-44.8 17.6-14.4-36.8-65.6-33.6-89.6 1.6-6.4 9.6-8 22.4-8 16 0 36.8 3.2 51.2 3.2 12.8 0 20.8-1.6 22.4-8 4.8-25.6 9.6-51.2 12.8-76.8 3.2-20.8-86.4-22.4-86.4-43.2-3.2-16 78.4-48 70.4-64-14.4-24-28.8-48-41.6-73.6-1.6-3.2-4.8-4.8-9.6-4.8-19.2 0-57.6 20.8-76.8 20.8h-4.8c-17.6-6.4 25.6-80 6.4-88l-67.2-24h-3.2c-19.2 0-40 59.2-59.2 65.6h-1.6c-16 3.2-32-67.2-46.4-67.2z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="SettingsLogData">
|
||||||
|
M676.620428 204.009192h124.69063c17.831817 0 22.686033-11.458593 11.326505-24.304106L653.934395 14.331497c-12.944578-12.878534-19.416867-8.189427-19.416867 9.477281v131.625224c0 27.243053 14.529628 48.542168 42.069878 48.542168z m-218.836017 517.122685H204.011242a54.651216 54.651216 0 0 1-54.88237-54.387041c0-30.049913 24.568281-54.387041 54.88237-54.387041h295.0175a313.839974 313.839974 0 0 1 262.788144-136.413398c21.794443 1.717138 43.456798 4.887239 64.854978 9.47728V303.438081c0-27.243053-47.88173-45.008826-75.455003-45.008826h-162.005357c-27.507228 0-37.149618-24.039931-37.149618-51.282984v-160.486351C552.061886 19.350823 517.950281 0 492.028103 0H86.882623C33.519261 0 16.512991 41.805704 16.512991 94.739782v770.499591c0 52.934078 17.00627 87.045683 70.402654 87.045683h428.194754a303.008797 303.008797 0 0 1-61.949052-179.639042 275.930853 275.930853 0 0 1 4.457954-51.514137h0.16511z m-253.60806-340.125393h416.571051c19.614998 0 37.744012 10.368871 47.551512 27.177009 9.807499 16.84116 9.807499 37.578903 0 54.420063s-27.936513 27.210031-47.551512 27.210032H204.011242a54.651216 54.651216 0 0 1-54.88237-54.420063c0-30.049913 24.568281-54.387041 54.88237-54.387041h0.132087z m553.578843 150.84396a247.499012 247.499012 0 0 0-177.195423 70.831939 242.90897 242.90897 0 0 0-72.78023 175.049001c2.212466 135.488785 112.934839 244.560064 249.711478 245.913961a247.499012 247.499012 0 0 0 176.997292-70.931004 242.90897 242.90897 0 0 0 72.648142-174.982957c-0.52835-136.116201-112.109292-246.112093-249.480324-245.946983l0.099065 0.066043z m144.503759 270.878505h-150.910004c-15.124022 0-20.638677-1.58505-20.638677-16.378854v-149.853304a27.408163 27.408163 0 0 1 27.54025-27.276075c15.190066 0 27.507228 12.218097 27.507229 27.276075v111.779073h116.765377c15.157044 0 27.408163 12.185075 27.408163 27.210032a27.342119 27.342119 0 0 1-27.408163 27.210031h-0.264175z
|
||||||
|
</Geometry>
|
||||||
|
|
||||||
|
<Geometry x:Key="ToolExpandData">
|
||||||
|
M490.666667 601.6L797.866667 298.666667l59.733333 59.733333-302.933333 302.933333-59.733334 64-59.733333-59.733333L128 358.4 187.733333 298.666667l302.933334 302.933333z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="ToolCollapseData">
|
||||||
|
M533.333333 465.066667L358.4 640 298.666667 580.266667l234.666666-234.666667 234.666667 234.666667-64 59.733333-170.666667-174.933333z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="ToolExpandAllData">
|
||||||
|
M707.84 238.506667 768 298.666667 512 554.666667 256 298.666667 316.16 238.506667 512 433.92 707.84 238.506667M707.84 494.506667 768 554.666667 512 810.666667 256 554.666667 316.16 494.506667 512 689.92 707.84 494.506667Z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="ToolCollapseAllData">
|
||||||
|
M316.16 785.493333 256 725.333333 512 469.333333 768 725.333333 707.84 785.493333 512 590.08 316.16 785.493333M316.16 529.493333 256 469.333333 512 213.333333 768 469.333333 707.84 529.493333 512 334.08 316.16 529.493333Z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="ToolGoBackData">
|
||||||
|
M164.42 335.086667a21.333333 21.333333 0 0 1-30.173333 0l-85.333334-85.333334a21.333333 21.333333 0 0 1 0-30.173333l85.333334-85.333333a21.333333 21.333333 0 0 1 30.173333 30.173333L115.506667 213.333333H682.666667a298.666667 298.666667 0 1 1 0 597.333334H106.666667a21.333333 21.333333 0 0 1 0-42.666667h576c141.16 0 256-114.84 256-256s-114.84-256-256-256H115.506667l48.913333 48.913333a21.333333 21.333333 0 0 1 0 30.173334z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="ToolRunData">
|
||||||
|
M341.333333 219.306667 341.333333 816.64 810.666667 517.973333 341.333333 219.306667Z
|
||||||
|
</Geometry>
|
||||||
|
|
||||||
|
<Geometry x:Key="ListData">
|
||||||
|
M917.44 160H64v672a32 32 0 0 0 32 32h832a32 32 0 0 0 32-32V160zM336 800H128v-148.8h208z m0-212.8H128v-131.84h208z m288 212.8h-224v-148.8h224z m0-212.8h-224v-131.84h224zM896 800h-208v-148.8H896z m0-212.8h-208v-131.84H896z m0-195.84H128V224h768z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="ListRegionData">
|
||||||
|
M886.533333 722.133333c-4.533333 0-8.8 1.733333-12 4.4-10.666667-23.6-34.4-40-62-40-17.866667 0-34.133333 6.933333-46.266666 18.133334L676.133333 638.666667c3.6-8.4 5.733333-17.6 5.733334-27.2 0-20.533333-9.2-39.066667-23.6-51.466667l152-209.733333c7.733333 3.066667 16.133333 4.666667 24.8 4.666666 37.6 0 68.133333-30.533333 68.133333-68.133333 0-37.6-30.533333-68.133333-68.133333-68.133333-37.6 0-68.133333 30.533333-68.133334 68.133333 0 20.266667 8.8 38.4 22.8 50.8L637.6 547.733333c-7.466667-2.8-15.466667-4.266667-23.866667-4.266666-37.6 0-68.133333 30.533333-68.133333 68.133333 0 37.6 30.533333 68.133333 68.133333 68.133333 19.333333 0 36.8-8 49.066667-20.933333l88.933333 65.066667c-4.8 9.333333-7.466667 19.733333-7.466666 30.933333 0 37.6 30.533333 68.133333 68.133333 68.133333 36.4 0 66.133333-28.533333 68-64.533333 1.866667 0.666667 4 1.066667 6 1.066667 10.266667 0 18.533333-8.266667 18.533333-18.533334 0.133333-10.4-8.133333-18.8-18.4-18.8z m-51.466666-483.333333c26.533333 0 48.133333 21.6 48.133333 48.133333 0 26.533333-21.6 48.133333-48.133333 48.133334-26.533333 0-48.133333-21.6-48.133334-48.133334 0-26.666667 21.6-48.133333 48.133334-48.133333z m-221.333334 420.933333c-26.533333 0-48.133333-21.6-48.133333-48.133333s21.6-48.133333 48.133333-48.133333 48.133333 21.6 48.133334 48.133333-21.466667 48.133333-48.133334 48.133333z m198.8 143.066667c-26.533333 0-48.133333-21.6-48.133333-48.133333 0-26.533333 21.6-48.133333 48.133333-48.133334s48.133333 21.6 48.133334 48.133334c0 26.533333-21.6 48.133333-48.133334 48.133333z m168.133334-66.666667c0 10.266667-8.266667 18.533333-18.533334 18.533334s-18.533333-8.266667-18.533333-18.533334 8.266667-18.533333 18.533333-18.533333 18.533333 8.266667 18.533334 18.533333z m69.6-10c0 10.266667-8.266667 18.533333-18.533334 18.533334s-18.533333-8.266667-18.533333-18.533334 8.266667-18.533333 18.533333-18.533333c10.133333 0 18.533333 8.266667 18.533334 18.533333z m69.466666-8.533333c0 10.266667-8.266667 18.533333-18.533333 18.533333s-18.533333-8.266667-18.533333-18.533333 8.266667-18.533333 18.533333-18.533333 18.533333 8.266667 18.533333 18.533333zM1082.666667 646.533333c0 10.266667-8.266667 18.533333-18.533334 18.533334s-18.533333-8.266667-18.533333-18.533334 8.266667-18.533333 18.533333-18.533333 18.533333 8.266667 18.533334 18.533333z m-74.133334-64.666666c0-10.266667 8.266667-18.533333 18.533334-18.533334s18.533333 8.266667 18.533333 18.533334-8.266667 18.533333-18.533333 18.533333-18.533333-8.266667-18.533334-18.533333z m0.8-64.533334c0 10.266667-8.266667 18.533333-18.533333 18.533334s-18.533333-8.266667-18.533333-18.533334 8.266667-18.533333 18.533333-18.533333S1009.333333 507.066667 1009.333333 517.333333z m-58.8-47.6c-10.266667 0-18.533333-8.266667-18.533333-18.533333s8.266667-18.533333 18.533333-18.533333 18.533333 8.266667 18.533334 18.533333-8.266667 18.533333-18.533334 18.533333z m-23.733333-83.6c0 10.266667-8.266667 18.533333-18.533333 18.533334s-18.533333-8.266667-18.533334-18.533334 8.266667-18.533333 18.533334-18.533333 18.533333 8.266667 18.533333 18.533333z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="ListDeviceData">
|
||||||
|
M713 702.1l244.1-180.4L263.7 68.8h-84.9L69.1 245.7v56.6L660 702.1h53zM104.5 255.8l94-151.6h54.7l642.1 419.3-193.9 143.2h-30.6L104.5 283.5v-27.7z M735.9 732.1l-32.3 32.3-592.1-389.3c-8.2-5.4-19.1-3.1-24.5 5.1-5.4 8.2-3.1 19.1 5.1 24.5l604.1 397.2c3 2 6.4 2.9 9.7 2.9 4.6 0 9.1-1.8 12.5-5.2l42.5-42.5c6.9-6.9 6.9-18.1 0-25-6.9-6.9-18.1-6.9-25 0zM336.4 573.7c-8.4-5-19.3-2.2-24.2 6.2l-62.1 104.6H100.9V511.1c0-9.8-7.9-17.7-17.7-17.7s-17.7 7.9-17.7 17.7v381c0 9.8 7.9 17.7 17.7 17.7s17.7-7.9 17.7-17.7V719.8h159.2c6.2 0 12-3.3 15.2-8.7l67.2-113.2c5.1-8.4 2.3-19.2-6.1-24.2z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="ListDeviceSelectedData">
|
||||||
|
M345.28 466.88c6.592 0 10.304 8.96 5.632 13.632L211.84 619.648a136 136 0 0 0 192.32 192.32l139.072-139.072c4.672-4.672 13.696-0.96 13.696 5.632v95.232a23.104 23.104 0 0 1-7.04 16.96l-83.52 83.456a224 224 0 0 1-316.8-316.8l83.52-83.456a23.104 23.104 0 0 1 16.96-7.04h95.232z m237.056-87.68a16 16 0 0 1 22.656 0l39.616 39.616a16 16 0 0 1 0 22.592L440.96 645.12a16 16 0 0 1-22.656 0l-39.616-39.552a16 16 0 0 1 0-22.656z m-25.408-229.12a224 224 0 0 1 316.8 316.8L790.144 550.4a23.104 23.104 0 0 1-16.96 7.04h-95.232c-6.592 0-10.304-8.96-5.632-13.632l139.072-139.072a136 136 0 0 0-192.32-192.32L480 351.36c-4.736 4.672-13.696 0.96-13.696-5.696V250.56c0-6.656 2.368-12.288 7.04-16.96z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="ListEventData">
|
||||||
|
M876.730316 807.253932h-175.469366a132.787628 132.787628 0 0 1-103.716623-215.305655A132.408235 132.408235 0 0 1 597.544327 426.817377a132.787628 132.787628 0 0 1 103.906319-215.305655h50.554148a370.240362 370.240362 0 0 0-242.669391-136.960954V47.424153a47.424153 47.424153 0 0 0-94.848306 0v27.363736C232.805167 99.590721 91.291494 264.769046 91.291494 452.568692v352.314032h-30.920547a64.876241 64.876241 0 0 0 0 129.420514h294.029748A109.075552 109.075552 0 0 0 569.089836 934.255813h294.029748a64.828817 64.828817 0 0 0 13.326187-127.049305z M1053.053317 471.443505h-351.602671a37.939322 37.939322 0 0 0 0 75.878644h351.602671a37.939322 37.939322 0 0 0 0-75.878644zM663.511324 344.299351a37.939322 37.939322 0 0 0 37.939322 37.939322h351.602671a37.939322 37.939322 0 0 0 0-75.878645h-351.602671a37.939322 37.939322 0 0 0-37.939322 37.939323zM1053.053317 636.526981h-351.602671a37.939322 37.939322 0 0 0 0 75.878645h351.602671a37.939322 37.939322 0 0 0 0-75.878645z
|
||||||
|
</Geometry>
|
||||||
|
|
||||||
|
|
||||||
|
<Geometry x:Key="NoneData">
|
||||||
|
M1005.714286 0a14.628571 14.628571 0 0 1 10.883657 4.842057l319.400228 361.6768A14.628571 14.628571 0 0 1 1345.828571 380.342857v570.514286a73.142857 73.142857 0 0 1-73.142857 73.142857H73.142857a73.142857 73.142857 0 0 1-73.142857-73.142857V380.342857a14.628571 14.628571 0 0 1 7.460571-12.756114L325.485714 4.944457A14.628571 14.628571 0 0 1 336.457143 0h669.257143zM320.994743 394.971429H29.257143v555.885714a43.885714 43.885714 0 0 0 43.885714 43.885714h1199.542857a43.885714 43.885714 0 0 0 43.885715-43.885714V394.971429H1011.039086a43.885714 43.885714 0 0 0-40.565029 27.121371l-0.980114 2.633143-31.509943 92.554971a73.142857 73.142857 0 0 1-69.251657 49.576229h-403.456a73.142857 73.142857 0 0 1-68.768914-48.274286l-34.230858-94.646857A43.885714 43.885714 0 0 0 320.980114 394.971429zM999.189943 29.257143H343.054629L48.040229 365.714286h272.969142a73.142857 73.142857 0 0 1 67.584 45.202285l1.184915 3.072 34.230857 94.646858a43.885714 43.885714 0 0 0 41.281828 28.964571h403.456a43.885714 43.885714 0 0 0 41.545143-29.739886l31.495315-92.5696A73.142857 73.142857 0 0 1 1011.039086 365.714286H1296.237714L999.189943 29.257143z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="Layout1Data">
|
||||||
|
M85.333333 85.333333h853.333334v853.333334H85.333333z M1024 1024H0V0h1024v1024zM42.666667 981.333333h938.666666V42.666667H42.666667v938.666666z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="Layout4Data">
|
||||||
|
M85.333333 85.333333h405.333334v405.333334H85.333333V85.333333z m448 0h405.333334v405.333334H533.333333V85.333333z m0 448h405.333334v405.333334H533.333333V533.333333zM85.333333 533.333333h405.333334v405.333334H85.333333V533.333333z M1024 1024H0V0h1024v1024zM42.666667 981.333333h938.666666V42.666667H42.666667v938.666666z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="Layout6Data">
|
||||||
|
M1024 1024H0V0h1024v1024zM42.666667 981.333333h938.666666V42.666667H42.666667v938.666666z M682.666667 682.666667h256v256H682.666667zM384 682.666667h256v256H384zM85.333333 682.666667h256v256H85.333333zM682.666667 384h256v256H682.666667zM682.666667 85.333333h256v256H682.666667zM85.333333 85.333333h554.666667v554.666667H85.333333z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="Layout8Data">
|
||||||
|
M1024 1024H0V0h1024v1024zM42.666667 981.333333h938.666666V42.666667H42.666667v938.666666z M757.333333 757.333333H938.666667V938.666667h-181.333334zM533.333333 757.333333h181.333334V938.666667H533.333333zM309.333333 757.333333H490.666667V938.666667h-181.333334zM757.333333 533.333333H938.666667v181.333334h-181.333334zM757.333333 309.333333H938.666667V490.666667h-181.333334zM757.333333 85.333333H938.666667v181.333334h-181.333334zM85.333333 757.333333h181.333334V938.666667H85.333333zM85.333333 85.333333h629.333334v629.333334H85.333333z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="Layout9Data">
|
||||||
|
M1024 1024H0V0h1024v1024zM42.666667 981.333333h938.666666V42.666667H42.666667v938.666666z M682.666667 682.666667h256v256H682.666667zM384 682.666667h256v256H384zM384 384h256v256H384zM384 85.333333h256v256H384zM85.333333 682.666667h256v256H85.333333zM85.333333 384h256v256H85.333333zM85.333333 85.333333h256v256H85.333333zM682.666667 384h256v256H682.666667zM682.666667 85.333333h256v256H682.666667z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="Layout16Data">
|
||||||
|
M1024 1024H0V0h1024v1024zM42.666667 981.333333h938.666666V42.666667H42.666667v938.666666z M757.333333 757.333333H938.666667V938.666667h-181.333334zM533.333333 757.333333h181.333334V938.666667H533.333333zM309.333333 757.333333H490.666667V938.666667h-181.333334zM757.333333 533.333333H938.666667v181.333334h-181.333334zM533.333333 533.333333h181.333334v181.333334H533.333333zM309.333333 533.333333H490.666667v181.333334h-181.333334zM85.333333 533.333333h181.333334v181.333334H85.333333zM757.333333 309.333333H938.666667V490.666667h-181.333334zM533.333333 309.333333h181.333334V490.666667H533.333333zM309.333333 309.333333H490.666667V490.666667h-181.333334zM85.333333 309.333333h181.333334V490.666667H85.333333zM757.333333 85.333333H938.666667v181.333334h-181.333334zM533.333333 85.333333h181.333334v181.333334H533.333333zM309.333333 85.333333H490.666667v181.333334h-181.333334zM85.333333 85.333333h181.333334v181.333334H85.333333zM85.333333 757.333333h181.333334V938.666667H85.333333z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="ZoomOutData">
|
||||||
|
M193.349046 210.094207v130.995519c0.084979 23.530755-6.042025 43.024996-29.555785 43.105726h-0.195452c-23.649726-0.025494-33.940714-19.53673-34.004448-43.207701v-169.916017a41.580349 41.580349 0 0 1 41.784299-41.809792h170.328166c23.641228 0.029743 42.795552 10.707386 42.825294 34.36561 0.029743 23.535004-19.009859 29.445311-42.523618 29.475054H210.344896a16.995851 16.995851 0 0 0-16.99585 16.99585z m620.306058-16.99585H681.992498c-23.513759-0.025494-42.549112-5.935801-42.523618-29.470805 0.029743-23.662473 19.184066-34.335867 42.825294-34.369859H852.78805a41.580349 41.580349 0 0 1 41.618589 41.809792v169.920266c-0.063734 23.666722-10.354722 43.182207-34.000199 43.21195h-0.199701c-23.513759-0.084979-29.636515-19.57922-29.555785-43.109975v-130.995519a16.995851 16.995851 0 0 0-16.99585-16.99585zM210.344896 830.09434H342.007502c23.513759 0.025494 42.553361 5.94005 42.523618 29.470805-0.029743 23.662473-19.184066 34.335867-42.825294 34.369859H171.21195a41.580349 41.580349 0 0 1-41.618589-41.809792v-169.916017c0.063734-23.670971 10.354722-43.186456 34.004448-43.21195h0.195452c23.513759 0.084979 29.636515 19.574971 29.555785 43.105726v130.995519a16.995851 16.995851 0 0 0 16.99585 16.99585z m620.306058-16.859884v-130.991269c-0.084979-23.535004 6.042025-43.024996 29.555785-43.109975h0.199701c23.645477 0.029743 33.936465 19.545228 34.000199 43.21195v169.916016a41.580349 41.580349 0 0 1-41.784299 41.809793h-170.328166c-23.641228-0.029743-42.795552-10.707386-42.825294-34.36561-0.025494-23.535004 19.009859-29.445311 42.523618-29.475054H813.655104a16.995851 16.995851 0 0 0 16.99585-16.995851z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="DeleteData">
|
||||||
|
M768.426667 940.462933H256c-43.093333 0-81.493333-37.546667-84.906667-84.053333V215.1296h682.666667l0.426667 640v1.28c-3.84 46.506667-42.24 84.053333-85.76 84.053333zM234.666667 852.5696c1.706667 13.226667 12.8 23.893333 21.333333 23.893333h512.853333c8.533333 0 19.626667-10.666667 21.333334-23.893333l-0.853334-573.44H234.666667v573.44z M117.333333 215.1296h789.333334c17.493333 0 32 14.506667 32 32s-14.506667 32-32 32h-789.333334c-17.493333 0-32-14.506667-32-32s14.506667-32 32-32zM373.333333 87.1296h277.333334c17.493333 0 32 14.506667 32 32s-14.506667 32-32 32h-277.333334c-17.493333 0-32-14.506667-32-32s14.506667-32 32-32zM469.333333 503.1296v149.333333c0 17.493333-14.506667 32-32 32s-32-14.506667-32-32v-149.333333c0-17.493333 14.506667-32 32-32s32 14.506667 32 32zM618.666667 503.1296v149.333333c0 17.493333-14.506667 32-32 32s-32-14.506667-32-32v-149.333333c0-17.493333 14.506667-32 32-32s32 14.506667 32 32z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="VideoListData">
|
||||||
|
M196.182442 0.61174a39.031688 39.031688 0 0 0-39.11813 39.111481v39.11813h704.053194V39.723221A39.031688 39.031688 0 0 0 821.992727 0.61174H196.182442z m-78.229611 156.459221a39.031688 39.031688 0 0 0-39.11148 39.111481v39.11148h860.499117v-39.11148a39.031688 39.031688 0 0 0-39.111481-39.11813H117.952831zM978.451948 313.516883a39.031688 39.031688 0 0 1 39.11813 39.11813v625.823584a39.031688 39.031688 0 0 1-39.111481 39.111481H39.723221a39.031688 39.031688 0 0 1-39.111481-39.111481V352.635013a39.031688 39.031688 0 0 1 39.111481-39.111481zM391.753143 469.976104v391.141402l312.918441-195.570701-312.918441-195.570701z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="PictureListData">
|
||||||
|
M738.133333 285.866667V960H64V285.866667h674.133333m0-64H64c-36.266667 0-64 27.733333-64 64V960c0 36.266667 27.733333 64 64 64h674.133333c36.266667 0 64-27.733333 64-64V285.866667c0-36.266667-27.733333-64-64-64z M876.8 846.933333c-17.066667 0-32-14.933333-32-32V232.533333c0-29.866667-23.466667-53.333333-53.333333-53.333333H209.066667c-17.066667 0-32-14.933333-32-32S192 115.2 209.066667 115.2h582.4c66.133333 0 117.333333 53.333333 117.333333 117.333333v582.4c0 17.066667-12.8 32-32 32z M992 693.333333c-17.066667 0-32-14.933333-32-32V149.333333c0-46.933333-38.4-85.333333-85.333333-85.333333H362.666667c-17.066667 0-32-14.933333-32-32S345.6 0 362.666667 0h512c83.2 0 149.333333 66.133333 149.333333 149.333333v512c0 17.066667-14.933333 32-32 32zM134.4 836.266667c-8.533333 0-17.066667-2.133333-23.466667-8.533334-12.8-12.8-12.8-32 0-44.8l151.466667-151.466666c12.8-12.8 34.133333-12.8 44.8 0l72.533333 72.533333 136.533334-136.533333c12.8-12.8 32-12.8 44.8 0l125.866666 125.866666c12.8 12.8 12.8 32 0 44.8-12.8 12.8-32 12.8-44.8 0l-104.533333-104.533333-132.266667 136.533333c-12.8 12.8-32 12.8-44.8 0l-72.533333-72.533333-130.133333 130.133333c-6.4 4.266667-14.933333 8.533333-23.466667 8.533334z M181.333333 518.4m-51.2 0a51.2 51.2 0 1 0 102.4 0 51.2 51.2 0 1 0-102.4 0Z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="SearchData">
|
||||||
|
M909.632 854.528l-259.712-259.712c40.32-52.096 62.08-115.84 62.08-182.848a298.24 298.24 0 0 0-87.872-212.096A297.792 297.792 0 0 0 411.968 112a298.24 298.24 0 0 0-212.096 87.872A297.6 297.6 0 0 0 112 411.968c0 80.128 31.36 155.52 87.872 212.16a297.6 297.6 0 0 0 212.096 87.872 297.6 297.6 0 0 0 182.72-62.016l259.712 259.584a8.128 8.128 0 0 0 8.96 1.792 8.128 8.128 0 0 0 2.624-1.728l43.584-43.52a8.128 8.128 0 0 0 1.792-8.96 8.192 8.192 0 0 0-1.728-2.624z m-339.2-284.16a222.784 222.784 0 0 1-158.464 65.6 222.784 222.784 0 0 1-158.4-65.536 222.784 222.784 0 0 1-65.6-158.464c0-59.776 23.36-116.096 65.664-158.4a222.784 222.784 0 0 1 158.336-65.6c59.84 0 116.16 23.232 158.4 65.664a222.784 222.784 0 0 1 65.6 158.336c0 59.84-23.296 116.16-65.536 158.4z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="ResetData">
|
||||||
|
M909.12 209.28l-56.448 44.16a419.008 419.008 0 0 0-330.752-161.408 419.712 419.712 0 0 0-419.904 419.456 419.904 419.904 0 0 0 814.464 144.384 8 8 0 0 0-4.864-10.24l-56.704-19.52a8 8 0 0 0-10.112 4.8 342.144 342.144 0 0 1-79.616 124.288 341.76 341.76 0 0 1-243.072 100.8A341.376 341.376 0 0 1 278.976 755.2a342.144 342.144 0 0 1-100.672-243.328 342.208 342.208 0 0 1 100.672-243.264 341.76 341.76 0 0 1 243.072-100.8 341.376 341.376 0 0 1 243.2 100.8c9.856 9.92 19.2 20.416 27.776 31.36l-60.224 47.04a8 8 0 0 0 3.008 14.08l175.616 43.008a8 8 0 0 0 9.856-7.68l0.832-180.928a8.064 8.064 0 0 0-12.992-6.208z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="ViewData">
|
||||||
|
M912.213333 498.773333c-7.253333-9.386667-180.48-230.4-400.213333-230.4s-392.533333 221.013333-400.213333 230.4l-9.813334 13.226667 10.24 13.226667c7.253333 9.386667 180.48 230.4 400.213334 230.4s392.96-221.013333 400.213333-230.4l9.386667-13.226667-9.813334-13.226667zM512 712.533333c-169.386667 0-315.306667-154.026667-355.413333-200.533333 40.106667-46.506667 185.6-200.533333 355.413333-200.533333 169.386667 0 315.306667 154.026667 355.413333 200.533333-40.106667 46.506667-186.026667 200.533333-355.413333 200.533333z M512 384.853333c-69.973333 0-127.146667 57.173333-127.146667 127.146667s57.173333 127.146667 127.146667 127.146667 127.146667-57.173333 127.146667-127.146667S581.973333 384.853333 512 384.853333z m0 211.626667c-46.506667 0-84.48-37.973333-84.48-84.48 0-46.506667 37.973333-84.48 84.48-84.48s84.48 37.973333 84.48 84.48c0 46.506667-37.973333 84.48-84.48 84.48z
|
||||||
|
</Geometry>
|
||||||
|
|
||||||
|
<Geometry x:Key="VideoPlayData">
|
||||||
|
M642.048 474.112L447.488 337.92c-7.168-6.144-14.336-8.192-19.456-9.216h-1.024c-13.312-2.048-26.624 0-37.888 8.192s-19.456 19.456-21.504 32.768c-9.216 46.08-13.312 95.232-13.312 146.432 0 49.152 4.096 95.232 13.312 139.264 1.024 7.168 3.072 13.312 7.168 20.48l1.024 1.024c10.24 13.312 26.624 21.504 41.984 21.504 9.216 0 19.456-3.072 28.672-8.192l196.608-131.072c2.048-1.024 9.216-6.144 13.312-13.312 8.192-11.264 11.264-25.6 8.192-38.912-4.096-15.36-11.264-26.624-22.528-32.768z m-20.48 47.104l-2.048 2.048-196.608 131.072c-5.12 3.072-11.264 1.024-15.36-3.072v-1.024-3.072c-8.192-40.96-13.312-84.992-13.312-132.096 0-48.128 4.096-94.208 13.312-138.24 1.024-3.072 3.072-5.12 4.096-6.144 1.024-1.024 4.096-2.048 7.168-2.048 1.024 0 1.024 0 2.048 1.024 1.024 0 1.024 1.024 2.048 1.024l197.632 136.192c2.048 2.048 3.072 4.096 4.096 5.12-1.024 4.096-1.024 7.168-3.072 9.216zM512 51.2C258.048 51.2 51.2 258.048 51.2 512s206.848 460.8 460.8 460.8 460.8-206.848 460.8-460.8S765.952 51.2 512 51.2z m0 880.64C280.576 931.84 92.16 743.424 92.16 512S280.576 92.16 512 92.16s419.84 188.416 419.84 419.84-188.416 419.84-419.84 419.84z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="VideoStopData">
|
||||||
|
M648.192 332.8c-44.032-7.168-89.088-10.24-136.192-10.24-48.128 0-93.184 3.072-136.192 10.24-22.528 3.072-38.912 20.48-43.008 43.008-7.168 44.032-10.24 89.088-10.24 136.192 0 48.128 3.072 93.184 10.24 136.192v2.048c5.12 19.456 20.48 35.84 40.96 40.96h2.048c44.032 7.168 89.088 10.24 136.192 10.24 48.128 0 93.184-3.072 136.192-10.24 22.528-3.072 38.912-20.48 43.008-43.008 7.168-44.032 10.24-89.088 10.24-136.192 0-48.128-3.072-93.184-10.24-136.192-4.096-22.528-20.48-38.912-43.008-43.008z m2.048 309.248c-1.024 4.096-4.096 8.192-8.192 8.192-40.96 7.168-83.968 10.24-130.048 10.24-45.056 0-87.04-3.072-129.024-10.24-4.096-1.024-8.192-5.12-10.24-10.24-6.144-40.96-10.24-82.944-10.24-129.024 0-45.056 3.072-88.064 10.24-130.048 1.024-4.096 4.096-8.192 8.192-8.192 40.96-7.168 83.968-10.24 130.048-10.24 45.056 0 88.064 3.072 130.048 10.24 4.096 1.024 8.192 4.096 8.192 8.192 7.168 40.96 10.24 83.968 10.24 130.048 1.024 46.08-2.048 89.088-9.216 131.072zM512 51.2C258.048 51.2 51.2 258.048 51.2 512s206.848 460.8 460.8 460.8 460.8-206.848 460.8-460.8S765.952 51.2 512 51.2z m0 880.64C280.576 931.84 92.16 743.424 92.16 512S280.576 92.16 512 92.16s419.84 188.416 419.84 419.84-188.416 419.84-419.84 419.84z
|
||||||
|
</Geometry>
|
||||||
|
<Geometry x:Key="VideoPauseData">
|
||||||
|
M618.496 335.872c21.504 56.32 31.744 115.712 31.744 181.248 0 52.224-10.24 109.568-31.744 171.008-3.072 8.192-11.264 13.312-19.456 13.312-2.048 0-4.096 0-7.168-1.024-10.24-4.096-16.384-15.36-12.288-25.6 19.456-57.344 29.696-109.568 29.696-156.672 0-60.416-9.216-115.712-29.696-166.912-4.096-10.24 1.024-22.528 11.264-26.624 11.264-5.12 23.552 1.024 27.648 11.264z m-186.368-12.288c-10.24-4.096-22.528 1.024-26.624 11.264-21.504 56.32-31.744 115.712-31.744 181.248 0 52.224 10.24 109.568 31.744 171.008 3.072 8.192 11.264 13.312 19.456 13.312 2.048 0 4.096 0 7.168-1.024 10.24-4.096 16.384-15.36 12.288-25.6-19.456-57.344-29.696-109.568-29.696-156.672 0-60.416 9.216-115.712 29.696-166.912 4.096-10.24-2.048-22.528-12.288-26.624zM972.8 512c0 253.952-206.848 460.8-460.8 460.8S51.2 765.952 51.2 512 258.048 51.2 512 51.2s460.8 206.848 460.8 460.8z m-40.96 0C931.84 280.576 743.424 92.16 512 92.16S92.16 280.576 92.16 512s188.416 419.84 419.84 419.84 419.84-188.416 419.84-419.84z
|
||||||
|
</Geometry>
|
||||||
|
|
||||||
|
</Styles.Resources>
|
||||||
|
</Styles>
|
295
detect.gui/Assets/Expander.axaml
Normal file
295
detect.gui/Assets/Expander.axaml
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
<ResourceDictionary xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
x:ClassModifier="internal">
|
||||||
|
<!-- Shared header/content -->
|
||||||
|
<x:Double x:Key="ExpanderMinHeight">48</x:Double>
|
||||||
|
|
||||||
|
<!-- Header -->
|
||||||
|
<HorizontalAlignment x:Key="ExpanderHeaderHorizontalContentAlignment">Stretch</HorizontalAlignment>
|
||||||
|
<VerticalAlignment x:Key="ExpanderHeaderVerticalContentAlignment">Center</VerticalAlignment>
|
||||||
|
<Thickness x:Key="ExpanderHeaderPadding">16,0,0,0</Thickness>
|
||||||
|
<Thickness x:Key="ExpanderHeaderBorderThickness">1</Thickness>
|
||||||
|
<Thickness x:Key="ExpanderChevronBorderThickness">0</Thickness>
|
||||||
|
<Thickness x:Key="ExpanderChevronMargin">20,0,8,0</Thickness>
|
||||||
|
<x:Double x:Key="ExpanderChevronButtonSize">32</x:Double>
|
||||||
|
|
||||||
|
<SolidColorBrush x:Key="ExpanderHeaderBackground" Color="{StaticResource Light}" />
|
||||||
|
<SolidColorBrush x:Key="ExpanderHeaderForeground" Color="{StaticResource Dark}" />
|
||||||
|
<SolidColorBrush x:Key="ExpanderContentBackground" Color="{StaticResource Light}" />
|
||||||
|
<SolidColorBrush x:Key="ExpanderHeaderBackgroundPointerOver" Color="{StaticResource Light}" />
|
||||||
|
<SolidColorBrush x:Key="ExpanderChevronForegroundPointerOver" Color="{StaticResource Light}" />
|
||||||
|
<SolidColorBrush x:Key="ExpanderChevronBackgroundPointerOver" Color="{StaticResource Blue2}" Opacity="0.8" />
|
||||||
|
|
||||||
|
<!-- Content -->
|
||||||
|
<Thickness x:Key="ExpanderContentPadding">16</Thickness>
|
||||||
|
<Thickness x:Key="ExpanderContentLeftBorderThickness">1,1,0,1</Thickness>
|
||||||
|
<Thickness x:Key="ExpanderContentUpBorderThickness">1,1,1,0</Thickness>
|
||||||
|
<Thickness x:Key="ExpanderContentRightBorderThickness">0,1,1,1</Thickness>
|
||||||
|
<Thickness x:Key="ExpanderContentDownBorderThickness">1,0,1,1</Thickness>
|
||||||
|
|
||||||
|
<ControlTheme x:Key="FluentExpanderToggleButtonTheme" TargetType="ToggleButton">
|
||||||
|
<Setter Property="Background" Value="{DynamicResource ExpanderHeaderBackground}" />
|
||||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ExpanderHeaderBorderBrush}" />
|
||||||
|
<Setter Property="BorderThickness" Value="{DynamicResource ExpanderHeaderBorderThickness}" />
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource ExpanderHeaderForeground}" />
|
||||||
|
<Setter Property="Padding" Value="{StaticResource ExpanderHeaderPadding}" />
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
||||||
|
<Setter Property="HorizontalContentAlignment" Value="{StaticResource ExpanderHeaderHorizontalContentAlignment}" />
|
||||||
|
<Setter Property="VerticalContentAlignment" Value="{StaticResource ExpanderHeaderVerticalContentAlignment}" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate>
|
||||||
|
<Border x:Name="ToggleButtonBackground"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}"
|
||||||
|
Background="{TemplateBinding Background}"
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
|
BorderThickness="{TemplateBinding BorderThickness}">
|
||||||
|
<Grid x:Name="ToggleButtonGrid"
|
||||||
|
ColumnDefinitions="*,Auto">
|
||||||
|
<ContentPresenter x:Name="PART_ContentPresenter"
|
||||||
|
Content="{TemplateBinding Content}"
|
||||||
|
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||||
|
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||||
|
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||||
|
Foreground="{TemplateBinding Foreground}"
|
||||||
|
Margin="{TemplateBinding Padding}"/>
|
||||||
|
<Border x:Name="ExpandCollapseChevronBorder"
|
||||||
|
Grid.Column="1"
|
||||||
|
Width="{DynamicResource ExpanderChevronButtonSize}"
|
||||||
|
Height="{DynamicResource ExpanderChevronButtonSize}"
|
||||||
|
Margin="{DynamicResource ExpanderChevronMargin}"
|
||||||
|
CornerRadius="100"
|
||||||
|
BorderBrush="{DynamicResource ExpanderChevronBorderBrush}"
|
||||||
|
BorderThickness="{DynamicResource ExpanderChevronBorderThickness}"
|
||||||
|
Background="{DynamicResource ExpanderChevronBackground}">
|
||||||
|
<Path x:Name="ExpandCollapseChevron"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
RenderTransformOrigin="50%,50%"
|
||||||
|
Stretch="None"
|
||||||
|
Stroke="{DynamicResource ExpanderChevronForeground}"
|
||||||
|
StrokeThickness="1">
|
||||||
|
<Path.RenderTransform>
|
||||||
|
<RotateTransform />
|
||||||
|
</Path.RenderTransform>
|
||||||
|
</Path>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
|
||||||
|
<Style Selector="^:checked /template/ Path#ExpandCollapseChevron">
|
||||||
|
<Style.Animations>
|
||||||
|
<Animation FillMode="Both" Duration="0:0:0.0625">
|
||||||
|
<KeyFrame Cue="100%">
|
||||||
|
<Setter Property="RotateTransform.Angle" Value="180" />
|
||||||
|
</KeyFrame>
|
||||||
|
</Animation>
|
||||||
|
</Style.Animations>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="^:not(:checked) /template/ Path#ExpandCollapseChevron">
|
||||||
|
<Style.Animations>
|
||||||
|
<Animation FillMode="Both" Duration="0:0:0.0625">
|
||||||
|
<KeyFrame Cue="0%">
|
||||||
|
<Setter Property="RotateTransform.Angle" Value="180" />
|
||||||
|
</KeyFrame>
|
||||||
|
<KeyFrame Cue="100%">
|
||||||
|
<Setter Property="RotateTransform.Angle" Value="0" />
|
||||||
|
</KeyFrame>
|
||||||
|
</Animation>
|
||||||
|
</Style.Animations>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<!-- PointerOver -->
|
||||||
|
<Style Selector="^:pointerover /template/ Border#ToggleButtonBackground">
|
||||||
|
<Setter Property="Background" Value="{DynamicResource ExpanderHeaderBackgroundPointerOver}" />
|
||||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ExpanderHeaderBorderBrushPointerOver}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^:pointerover /template/ ContentPresenter#PART_ContentPresenter">
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource ExpanderHeaderForegroundPointerOver}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^:pointerover /template/ Border#ExpandCollapseChevronBorder">
|
||||||
|
<Setter Property="Background" Value="{DynamicResource ExpanderChevronBackgroundPointerOver}" />
|
||||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ExpanderChevronBorderBrushPointerOver}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^:pointerover /template/ Path#ExpandCollapseChevron">
|
||||||
|
<Setter Property="Stroke" Value="{DynamicResource ExpanderChevronForegroundPointerOver}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<!-- Pressed -->
|
||||||
|
<Style Selector="^:pressed /template/ Border#ToggleButtonBackground">
|
||||||
|
<Setter Property="Background" Value="{DynamicResource ExpanderHeaderBackgroundPressed}" />
|
||||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ExpanderHeaderBorderBrushPressed}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^:pressed /template/ ContentPresenter#PART_ContentPresenter">
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource ExpanderHeaderForegroundPressed}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^:pressed /template/ Border#ExpandCollapseChevronBorder">
|
||||||
|
<Setter Property="Background" Value="{DynamicResource ExpanderChevronBackgroundPressed}" />
|
||||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ExpanderChevronBorderBrushPressed}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^:pressed /template/ Path#ExpandCollapseChevron">
|
||||||
|
<Setter Property="Stroke" Value="{DynamicResource ExpanderChevronForegroundPressed}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<!-- Disabled -->
|
||||||
|
<Style Selector="^:disabled /template/ Border#ToggleButtonBackground">
|
||||||
|
<Setter Property="Background" Value="{DynamicResource ExpanderHeaderBackgroundDisabled}" />
|
||||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ExpanderHeaderBorderBrushDisabled}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^:disabled /template/ ContentPresenter#PART_ContentPresenter">
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource ExpanderHeaderForegroundDisabled}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^:disabled /template/ Border#ExpandCollapseChevronBorder">
|
||||||
|
<Setter Property="Background" Value="{DynamicResource ExpanderChevronBackgroundDisabled}" />
|
||||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ExpanderChevronBorderBrushDisabled}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^:disabled /template/ Path#ExpandCollapseChevron">
|
||||||
|
<Setter Property="Stroke" Value="{DynamicResource ExpanderChevronForegroundDisabled}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
</ControlTheme>
|
||||||
|
|
||||||
|
<ControlTheme x:Key="FluentExpanderToggleButtonUpTheme" TargetType="ToggleButton" BasedOn="{StaticResource FluentExpanderToggleButtonTheme}">
|
||||||
|
<Style Selector="^ /template/ Path#ExpandCollapseChevron">
|
||||||
|
<Setter Property="Data" Value="M 0 7 L 7 0 L 14 7" />
|
||||||
|
</Style>
|
||||||
|
</ControlTheme>
|
||||||
|
|
||||||
|
<ControlTheme x:Key="FluentExpanderToggleButtonDownTheme" TargetType="ToggleButton" BasedOn="{StaticResource FluentExpanderToggleButtonTheme}">
|
||||||
|
<Style Selector="^ /template/ Path#ExpandCollapseChevron">
|
||||||
|
<Setter Property="Data" Value="M 0 0 L 7 7 L 14 0" />
|
||||||
|
</Style>
|
||||||
|
</ControlTheme>
|
||||||
|
|
||||||
|
<ControlTheme x:Key="FluentExpanderToggleButtonLeftTheme" TargetType="ToggleButton" BasedOn="{StaticResource FluentExpanderToggleButtonTheme}">
|
||||||
|
<Style Selector="^ /template/ Path#ExpandCollapseChevron">
|
||||||
|
<Setter Property="Data" Value="M 7 0 L 0 7 L 7 14" />
|
||||||
|
</Style>
|
||||||
|
</ControlTheme>
|
||||||
|
|
||||||
|
<ControlTheme x:Key="FluentExpanderToggleButtonRightTheme" TargetType="ToggleButton" BasedOn="{StaticResource FluentExpanderToggleButtonTheme}">
|
||||||
|
<Style Selector="^ /template/ Path#ExpandCollapseChevron">
|
||||||
|
<Setter Property="Data" Value="M 0 0 L 7 7 L 0 14" />
|
||||||
|
</Style>
|
||||||
|
</ControlTheme>
|
||||||
|
|
||||||
|
<ControlTheme x:Key="{x:Type Expander}" TargetType="Expander">
|
||||||
|
<Setter Property="IsTabStop" Value="False"/>
|
||||||
|
<Setter Property="MinWidth" Value="{DynamicResource FlyoutThemeMinWidth}" />
|
||||||
|
<Setter Property="MinHeight" Value="{StaticResource ExpanderMinHeight}" />
|
||||||
|
<Setter Property="Background" Value="{DynamicResource ExpanderContentBackground}" />
|
||||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ExpanderContentBorderBrush}" />
|
||||||
|
<Setter Property="BorderThickness" Value="{DynamicResource ExpanderContentDownBorderThickness}" />
|
||||||
|
<Setter Property="Padding" Value="{StaticResource ExpanderContentPadding}" />
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Left" />
|
||||||
|
<Setter Property="VerticalAlignment" Value="Center" />
|
||||||
|
<Setter Property="CornerRadius" Value="{DynamicResource ControlCornerRadius}" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate>
|
||||||
|
<DockPanel MinWidth="{TemplateBinding MinWidth}"
|
||||||
|
MaxWidth="{TemplateBinding MaxWidth}">
|
||||||
|
<ToggleButton x:Name="ExpanderHeader"
|
||||||
|
MinHeight="{TemplateBinding MinHeight}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}"
|
||||||
|
IsEnabled="{TemplateBinding IsEnabled}"
|
||||||
|
Content="{TemplateBinding Header}"
|
||||||
|
ContentTemplate="{TemplateBinding HeaderTemplate}"
|
||||||
|
IsChecked="{TemplateBinding IsExpanded, Mode=TwoWay}" />
|
||||||
|
<Border x:Name="ExpanderContent"
|
||||||
|
IsVisible="{TemplateBinding IsExpanded, Mode=TwoWay}"
|
||||||
|
Background="{TemplateBinding Background}"
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
|
BorderThickness="{TemplateBinding BorderThickness}"
|
||||||
|
MinHeight="{TemplateBinding MinHeight}"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
Padding="{TemplateBinding Padding}">
|
||||||
|
<ContentPresenter x:Name="PART_ContentPresenter"
|
||||||
|
Content="{TemplateBinding Content}"
|
||||||
|
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||||
|
Foreground="{TemplateBinding Foreground}"
|
||||||
|
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||||
|
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" />
|
||||||
|
</Border>
|
||||||
|
</DockPanel>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
|
||||||
|
<Style Selector="^ /template/ ToggleButton#ExpanderHeader:pressed">
|
||||||
|
<Setter Property="RenderTransform" Value="{x:Null}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="^:left /template/ ToggleButton#ExpanderHeader, ^:right /template/ ToggleButton#ExpanderHeader">
|
||||||
|
<Setter Property="VerticalAlignment" Value="Stretch" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="^:not(:expanded) /template/ ToggleButton#ExpanderHeader">
|
||||||
|
<Setter Property="CornerRadius" Value="{Binding $parent[Expander].CornerRadius}" />
|
||||||
|
</Style>
|
||||||
|
<!-- <Style Selector="^:expanded:up /template/ ToggleButton#ExpanderHeader"> -->
|
||||||
|
<!-- <Setter Property="CornerRadius" Value="{TemplateBinding CornerRadius, Converter={StaticResource BottomCornerRadiusFilterConverter}}" /> -->
|
||||||
|
<!-- </Style> -->
|
||||||
|
<!-- <Style Selector="^:expanded:up /template/ Border#ExpanderContent"> -->
|
||||||
|
<!-- <Setter Property="CornerRadius" Value="{TemplateBinding CornerRadius, Converter={StaticResource TopCornerRadiusFilterConverter}}" /> -->
|
||||||
|
<!-- </Style> -->
|
||||||
|
<!-- <Style Selector="^:expanded:down /template/ ToggleButton#ExpanderHeader"> -->
|
||||||
|
<!-- <Setter Property="CornerRadius" Value="{TemplateBinding CornerRadius, Converter={StaticResource TopCornerRadiusFilterConverter}}" /> -->
|
||||||
|
<!-- </Style> -->
|
||||||
|
<!-- <Style Selector="^:expanded:down /template/ Border#ExpanderContent"> -->
|
||||||
|
<!-- <Setter Property="CornerRadius" Value="{TemplateBinding CornerRadius, Converter={StaticResource BottomCornerRadiusFilterConverter}}" /> -->
|
||||||
|
<!-- </Style> -->
|
||||||
|
<!-- <Style Selector="^:expanded:left /template/ ToggleButton#ExpanderHeader"> -->
|
||||||
|
<!-- <Setter Property="CornerRadius" Value="{TemplateBinding CornerRadius, Converter={StaticResource RightCornerRadiusFilterConverter}}" /> -->
|
||||||
|
<!-- </Style> -->
|
||||||
|
<!-- <Style Selector="^:expanded:left /template/ Border#ExpanderContent"> -->
|
||||||
|
<!-- <Setter Property="CornerRadius" Value="{TemplateBinding CornerRadius, Converter={StaticResource LeftCornerRadiusFilterConverter}}" /> -->
|
||||||
|
<!-- </Style> -->
|
||||||
|
<!-- <Style Selector="^:expanded:right /template/ ToggleButton#ExpanderHeader"> -->
|
||||||
|
<!-- <Setter Property="CornerRadius" Value="{TemplateBinding CornerRadius, Converter={StaticResource LeftCornerRadiusFilterConverter}}" /> -->
|
||||||
|
<!-- </Style> -->
|
||||||
|
<!-- <Style Selector="^:expanded:right /template/ Border#ExpanderContent"> -->
|
||||||
|
<!-- <Setter Property="CornerRadius" Value="{TemplateBinding CornerRadius, Converter={StaticResource RightCornerRadiusFilterConverter}}" /> -->
|
||||||
|
<!-- </Style> -->
|
||||||
|
|
||||||
|
<Style Selector="^:left /template/ ToggleButton#ExpanderHeader">
|
||||||
|
<Setter Property="DockPanel.Dock" Value="Right" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^:up /template/ ToggleButton#ExpanderHeader">
|
||||||
|
<Setter Property="DockPanel.Dock" Value="Bottom" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^:right /template/ ToggleButton#ExpanderHeader">
|
||||||
|
<Setter Property="DockPanel.Dock" Value="Left" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^:down /template/ ToggleButton#ExpanderHeader">
|
||||||
|
<Setter Property="DockPanel.Dock" Value="Top" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="^:left /template/ ToggleButton#ExpanderHeader">
|
||||||
|
<Setter Property="Theme" Value="{StaticResource FluentExpanderToggleButtonLeftTheme}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^:up /template/ ToggleButton#ExpanderHeader">
|
||||||
|
<Setter Property="Theme" Value="{StaticResource FluentExpanderToggleButtonUpTheme}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^:right /template/ ToggleButton#ExpanderHeader">
|
||||||
|
<Setter Property="Theme" Value="{StaticResource FluentExpanderToggleButtonRightTheme}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^:down /template/ ToggleButton#ExpanderHeader">
|
||||||
|
<Setter Property="Theme" Value="{StaticResource FluentExpanderToggleButtonDownTheme}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="^:left /template/ Border#ExpanderContent">
|
||||||
|
<Setter Property="BorderThickness" Value="{DynamicResource ExpanderContentLeftBorderThickness}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^:up /template/ Border#ExpanderContent">
|
||||||
|
<Setter Property="BorderThickness" Value="{DynamicResource ExpanderContentUpBorderThickness}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^:right /template/ Border#ExpanderContent">
|
||||||
|
<Setter Property="BorderThickness" Value="{DynamicResource ExpanderContentRightBorderThickness}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^:down /template/ Border#ExpanderContent">
|
||||||
|
<Setter Property="BorderThickness" Value="{DynamicResource ExpanderContentDownBorderThickness}" />
|
||||||
|
</Style>
|
||||||
|
</ControlTheme>
|
||||||
|
</ResourceDictionary>
|
34
detect.gui/Assets/FlyoutPresenter.axaml
Normal file
34
detect.gui/Assets/FlyoutPresenter.axaml
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<Styles xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||||
|
|
||||||
|
<!-- FlyoutPresenter -->
|
||||||
|
<Style Selector="FlyoutPresenter.image">
|
||||||
|
<Setter Property="Background" Value="{StaticResource Light}" />
|
||||||
|
<Setter Property="CornerRadius" Value="0" />
|
||||||
|
<Setter Property="MinWidth" Value="440" />
|
||||||
|
<Setter Property="MinHeight" Value="200" />
|
||||||
|
<Setter Property="MaxWidth" Value="1020" />
|
||||||
|
<Setter Property="MaxHeight" Value="680" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate>
|
||||||
|
<Border Name="LayoutRoot"
|
||||||
|
Margin="15"
|
||||||
|
Background="{TemplateBinding Background}"
|
||||||
|
Padding="{DynamicResource FlyoutBorderThemePadding}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}">
|
||||||
|
<Border BorderBrush="{StaticResource Light-Gray}"
|
||||||
|
BorderThickness="1"
|
||||||
|
BoxShadow="{StaticResource DialogShadows}">
|
||||||
|
<ContentPresenter Content="{TemplateBinding Content}"
|
||||||
|
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||||
|
Margin="{TemplateBinding Padding}"
|
||||||
|
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||||
|
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||||
|
HorizontalContentAlignment="Stretch"
|
||||||
|
VerticalContentAlignment="Stretch" />
|
||||||
|
</Border>
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
</Styles>
|
BIN
detect.gui/Assets/Fonts/NotoSansSC-Regular.otf
Normal file
BIN
detect.gui/Assets/Fonts/NotoSansSC-Regular.otf
Normal file
Binary file not shown.
BIN
detect.gui/Assets/Images/bg.png
Normal file
BIN
detect.gui/Assets/Images/bg.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 775 KiB |
BIN
detect.gui/Assets/Images/header.png
Normal file
BIN
detect.gui/Assets/Images/header.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 44 KiB |
BIN
detect.gui/Assets/Images/login.png
Normal file
BIN
detect.gui/Assets/Images/login.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 181 KiB |
62
detect.gui/Assets/ListBox.axaml
Normal file
62
detect.gui/Assets/ListBox.axaml
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<Styles xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||||
|
<Styles.Resources>
|
||||||
|
<SolidColorBrush x:Key="ListItemPointerOverBackground" Color="{StaticResource Blue9}" Opacity="0.3" />
|
||||||
|
<SolidColorBrush x:Key="ActionListBackground" Color="{StaticResource Blue2}" Opacity="0.6" />
|
||||||
|
</Styles.Resources>
|
||||||
|
|
||||||
|
<Style Selector="ListBox">
|
||||||
|
<Setter Property="BorderThickness" Value="0"/>
|
||||||
|
<Setter Property="Background" Value="Transparent"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="ListBox.region-list > ListBoxItem">
|
||||||
|
<Setter Property="Padding" Value="0"/>
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Dark}"/>
|
||||||
|
<Setter Property="BorderThickness" Value="0,0,0,1"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="ListBox.region-list > ListBoxItem:pointerover /template/ ContentPresenter">
|
||||||
|
<Setter Property="Background" Value="Transparent"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="ListBox.region-list > ListBoxItem:selected /template/ ContentPresenter">
|
||||||
|
<Setter Property="Background" Value="Transparent"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="ListBox.region-list > ListBoxItem:selected:focus /template/ ContentPresenter">
|
||||||
|
<Setter Property="Background" Value="Transparent"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="ListBox.region-list > ListBoxItem:selected:pointerover /template/ ContentPresenter">
|
||||||
|
<Setter Property="Background" Value="Transparent"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="ListBox.region-list > ListBoxItem:selected:focus:pointerover /template/ ContentPresenter">
|
||||||
|
<Setter Property="Background" Value="Transparent"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="ListBox.region-list > ListBoxItem:pressed /template/ ContentPresenter">
|
||||||
|
<Setter Property="Background" Value="Transparent"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="ListBox.device-list > ListBoxItem">
|
||||||
|
<Setter Property="Padding" Value="0"/>
|
||||||
|
<Setter Property="CornerRadius" Value="6"/>
|
||||||
|
<Setter Property="FontSize" Value="14"/>
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Light}"/>
|
||||||
|
<Setter Property="BorderThickness" Value="0,0,0,1"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="ListBox.device-list > ListBoxItem:pointerover /template/ ContentPresenter">
|
||||||
|
<Setter Property="Background" Value="{StaticResource ListItemPointerOverBackground}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="ListBox.device-list > ListBoxItem:selected /template/ ContentPresenter">
|
||||||
|
<Setter Property="Background" Value="Transparent"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="ListBox.device-list > ListBoxItem:selected:focus /template/ ContentPresenter">
|
||||||
|
<Setter Property="Background" Value="Transparent"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="ListBox.device-list > ListBoxItem:selected:pointerover /template/ ContentPresenter">
|
||||||
|
<Setter Property="Background" Value="{StaticResource ListItemPointerOverBackground}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="ListBox.device-list > ListBoxItem:selected:focus:pointerover /template/ ContentPresenter">
|
||||||
|
<Setter Property="Background" Value="{StaticResource ListItemPointerOverBackground}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="ListBox.device-list > ListBoxItem:pressed /template/ ContentPresenter">
|
||||||
|
<Setter Property="Background" Value="{StaticResource ListItemPointerOverBackground}"/>
|
||||||
|
</Style>
|
||||||
|
</Styles>
|
83
detect.gui/Assets/NotificationCard.axaml
Normal file
83
detect.gui/Assets/NotificationCard.axaml
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<Styles xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||||
|
<!-- NotificationCard -->
|
||||||
|
<Style Selector="NotificationCard">
|
||||||
|
<Setter Property="UseLayoutRounding" Value="False"/>
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Light}" />
|
||||||
|
<Setter Property="Background">
|
||||||
|
<Setter.Value>
|
||||||
|
<SolidColorBrush Color="#2D77F3" Opacity="0.6" />
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
<Setter Property="BorderThickness" Value="1" />
|
||||||
|
<Setter Property="BorderBrush">
|
||||||
|
<Setter.Value>
|
||||||
|
<SolidColorBrush Color="#2D77F3" Opacity="0.7" />
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
<Setter Property="CornerRadius" Value="6" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate TargetType="NotificationCard">
|
||||||
|
<LayoutTransformControl Name="PART_LayoutTransformControl"
|
||||||
|
UseRenderTransform="True">
|
||||||
|
<Border Background="{TemplateBinding Background}"
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
|
BorderThickness="{TemplateBinding BorderThickness}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}"
|
||||||
|
Margin="0,0,0,2">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Border Margin="20">
|
||||||
|
<PathIcon Name="NotificationIcon"
|
||||||
|
Width="36"
|
||||||
|
Height="36" />
|
||||||
|
</Border>
|
||||||
|
<ContentControl Name="PART_Content"
|
||||||
|
MinHeight="64"
|
||||||
|
Content="{TemplateBinding Content}"
|
||||||
|
Margin="0,20,20,20">
|
||||||
|
<ContentControl.DataTemplates>
|
||||||
|
<DataTemplate DataType="INotification">
|
||||||
|
<StackPanel VerticalAlignment="Center">
|
||||||
|
<TextBlock FontFamily="{StaticResource Font-Family}"
|
||||||
|
FontSize="14"
|
||||||
|
Foreground="#FFFFFF"
|
||||||
|
Text="{Binding Message}" />
|
||||||
|
</StackPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
</ContentControl.DataTemplates>
|
||||||
|
</ContentControl>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</LayoutTransformControl>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="NotificationCard:information /template/ PathIcon#NotificationIcon">
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource Info}"/>
|
||||||
|
<Setter Property="Data" Value="{DynamicResource NotificationInformationData}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="NotificationCard:error /template/ PathIcon#NotificationIcon">
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource Danger}"/>
|
||||||
|
<Setter Property="Data" Value="{DynamicResource NotificationErrorData}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="NotificationCard:success /template/ PathIcon#NotificationIcon">
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource Success}"/>
|
||||||
|
<Setter Property="Data" Value="{DynamicResource NotificationSuccessData}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="NotificationCard:warning /template/ PathIcon#NotificationIcon">
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource Warning}"/>
|
||||||
|
<Setter Property="Data" Value="{DynamicResource NotificationWaringData}"/>
|
||||||
|
</Style>
|
||||||
|
<!-- <Style Selector="NotificationCard:information"> -->
|
||||||
|
<!-- <Setter Property="BorderBrush" Value="{DynamicResource Info}"/> -->
|
||||||
|
<!-- </Style> -->
|
||||||
|
<!-- <Style Selector="NotificationCard:error"> -->
|
||||||
|
<!-- <Setter Property="BorderBrush" Value="{DynamicResource Danger}"/> -->
|
||||||
|
<!-- </Style> -->
|
||||||
|
<!-- <Style Selector="NotificationCard:success"> -->
|
||||||
|
<!-- <Setter Property="BorderBrush" Value="{DynamicResource Success}"/> -->
|
||||||
|
<!-- </Style> -->
|
||||||
|
<!-- <Style Selector="NotificationCard:warning"> -->
|
||||||
|
<!-- <Setter Property="BorderBrush" Value="{DynamicResource Warning}"/> -->
|
||||||
|
<!-- </Style> -->
|
||||||
|
</Styles>
|
34
detect.gui/Assets/PathIcon.axaml
Normal file
34
detect.gui/Assets/PathIcon.axaml
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<Styles xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||||
|
|
||||||
|
<Style Selector="PathIcon">
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Dark}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="PathIcon.logo">
|
||||||
|
<Setter Property="Data" Value="{StaticResource LogoData}" />
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Blue2}" />
|
||||||
|
<Setter Property="Width" Value="36" />
|
||||||
|
<Setter Property="Height" Value="36" />
|
||||||
|
</Style>
|
||||||
|
<!-- <Style Selector="PathIcon.logo.header"> -->
|
||||||
|
<!-- <Setter Property="Width" Value="32" /> -->
|
||||||
|
<!-- <Setter Property="Height" Value="32" /> -->
|
||||||
|
<!-- </Style> -->
|
||||||
|
<Style Selector="PathIcon.login-user">
|
||||||
|
<Setter Property="Data" Value="{StaticResource LoginData}" />
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Blue1}" />
|
||||||
|
<Setter Property="Width" Value="39" />
|
||||||
|
<Setter Property="Height" Value="39" />
|
||||||
|
</Style>
|
||||||
|
<!-- <Style Selector="PathIcon.account-icon"> -->
|
||||||
|
<!-- <Setter Property="Data" Value="{StaticResource LoginData}" /> -->
|
||||||
|
<!-- <Setter Property="Foreground" Value="{StaticResource Blue1}" /> -->
|
||||||
|
<!-- <Setter Property="Width" Value="19" /> -->
|
||||||
|
<!-- <Setter Property="Height" Value="19" /> -->
|
||||||
|
<!-- </Style> -->
|
||||||
|
<Style Selector="PathIcon.dialog-icon">
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Light}" />
|
||||||
|
<Setter Property="Width" Value="19" />
|
||||||
|
<Setter Property="Height" Value="19" />
|
||||||
|
</Style>
|
||||||
|
</Styles>
|
248
detect.gui/Assets/RadioButton.axaml
Normal file
248
detect.gui/Assets/RadioButton.axaml
Normal file
@ -0,0 +1,248 @@
|
|||||||
|
<Styles xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||||
|
|
||||||
|
<Styles.Resources>
|
||||||
|
<BoxShadows x:Key="TabRadioButtonShadows">inset 0 0 20 2 #006CC6</BoxShadows>
|
||||||
|
<SolidColorBrush x:Key="TabRadioButtonPointeroverBackgroundColor" Color="#45afdf" Opacity="0.8" />
|
||||||
|
<SolidColorBrush x:Key="TabRadioButtonCheckedBackgroundColor" Color="{StaticResource Blue1}" />
|
||||||
|
</Styles.Resources>
|
||||||
|
|
||||||
|
<Style Selector="RadioButton.sidebar">
|
||||||
|
<Setter Property="FontFamily" Value="{StaticResource Font-Family}" />
|
||||||
|
<Setter Property="FontSize" Value="18" />
|
||||||
|
<Setter Property="CornerRadius" Value="6" />
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Light}" />
|
||||||
|
<Setter Property="Height" Value="45" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate>
|
||||||
|
<Grid Height="{TemplateBinding Height}">
|
||||||
|
<Border IsVisible="{TemplateBinding IsChecked}"
|
||||||
|
Height="{TemplateBinding Height}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}"
|
||||||
|
BorderThickness="0"
|
||||||
|
BoxShadow="{StaticResource TabRadioButtonShadows}"
|
||||||
|
BorderBrush="{StaticResource Light}"
|
||||||
|
Background="{StaticResource TabRadioButtonCheckedBackgroundColor}"/>
|
||||||
|
<Border x:Name="BackBorder"
|
||||||
|
Height="{TemplateBinding Height}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}"
|
||||||
|
Padding="15,0"
|
||||||
|
BorderThickness="0"
|
||||||
|
BorderBrush="{StaticResource Light}"
|
||||||
|
Background="Transparent">
|
||||||
|
<Grid ColumnDefinitions="Auto, 10, Auto"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center">
|
||||||
|
<PathIcon Grid.Column="0"
|
||||||
|
x:Name="ButtonIcon"
|
||||||
|
Width="21"
|
||||||
|
Height="21"
|
||||||
|
Foreground="{TemplateBinding Foreground}" />
|
||||||
|
<TextBlock Grid.Column="2"
|
||||||
|
x:Name="ButtonText"
|
||||||
|
FontSize="{TemplateBinding FontSize}"
|
||||||
|
Foreground="{TemplateBinding Foreground}"
|
||||||
|
Text="{TemplateBinding Content}" />
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="RadioButton.sidebar:checked /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Light}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="RadioButton.sidebar:checked /template/ TextBlock#ButtonText">
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Light}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="RadioButton.sidebar:pointerover Border#BackBorder">
|
||||||
|
<Setter Property="BoxShadow" Value="{StaticResource TabRadioButtonShadows}" />
|
||||||
|
<Setter Property="Background" Value="{StaticResource TabRadioButtonPointeroverBackgroundColor}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="RadioButton.tab-left">
|
||||||
|
<Setter Property="FontFamily" Value="{StaticResource Font-Family}" />
|
||||||
|
<Setter Property="FontSize" Value="14" />
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Light}" />
|
||||||
|
<Setter Property="CornerRadius" Value="6" />
|
||||||
|
<Setter Property="Width" Value="80" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate>
|
||||||
|
<Grid Width="{TemplateBinding Width}">
|
||||||
|
<Border IsVisible="{TemplateBinding IsChecked}"
|
||||||
|
Width="{TemplateBinding Width}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}"
|
||||||
|
BorderThickness="0"
|
||||||
|
BoxShadow="{StaticResource TabRadioButtonShadows}"
|
||||||
|
BorderBrush="{StaticResource Light}"
|
||||||
|
Background="{StaticResource TabRadioButtonCheckedBackgroundColor}"/>
|
||||||
|
<Border x:Name="BackBorder"
|
||||||
|
Width="{TemplateBinding Width}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}"
|
||||||
|
Padding="10,10"
|
||||||
|
BorderThickness="0"
|
||||||
|
BorderBrush="{StaticResource Light}"
|
||||||
|
Background="Transparent">
|
||||||
|
<Grid RowDefinitions="Auto, 5, Auto"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center">
|
||||||
|
<PathIcon Grid.Row="0"
|
||||||
|
x:Name="ButtonIcon"
|
||||||
|
Width="21"
|
||||||
|
Height="21"
|
||||||
|
Foreground="{TemplateBinding Foreground}" />
|
||||||
|
<TextBlock Grid.Row="2"
|
||||||
|
x:Name="ButtonText"
|
||||||
|
FontSize="{TemplateBinding FontSize}"
|
||||||
|
Foreground="{TemplateBinding Foreground}"
|
||||||
|
Text="{TemplateBinding Content}" />
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="RadioButton.tab-left:checked /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Light}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="RadioButton.tab-left:checked /template/ TextBlock#ButtonText">
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Light}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="RadioButton.tab-left:pointerover Border#BackBorder">
|
||||||
|
<Setter Property="BoxShadow" Value="{StaticResource TabRadioButtonShadows}" />
|
||||||
|
<Setter Property="Background" Value="{StaticResource TabRadioButtonPointeroverBackgroundColor}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="RadioButton.layout">
|
||||||
|
<Setter Property="FontFamily" Value="{StaticResource Font-Family}" />
|
||||||
|
<Setter Property="FontSize" Value="14" />
|
||||||
|
<Setter Property="FontWeight" Value="Bold" />
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Light}" />
|
||||||
|
<Setter Property="CornerRadius" Value="6" />
|
||||||
|
<Setter Property="Width" Value="48" />
|
||||||
|
<Setter Property="Height" Value="48" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate>
|
||||||
|
<Grid Width="{TemplateBinding Width}">
|
||||||
|
<Border IsVisible="{TemplateBinding IsChecked}"
|
||||||
|
Width="{TemplateBinding Width}"
|
||||||
|
Height="{TemplateBinding Height}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}"
|
||||||
|
BorderThickness="0"
|
||||||
|
BoxShadow="{StaticResource TabRadioButtonShadows}"
|
||||||
|
BorderBrush="{StaticResource Light}"
|
||||||
|
Background="{StaticResource TabRadioButtonCheckedBackgroundColor}"/>
|
||||||
|
<Border x:Name="BackBorder"
|
||||||
|
Width="{TemplateBinding Width}"
|
||||||
|
Height="{TemplateBinding Height}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}"
|
||||||
|
Padding="10"
|
||||||
|
BorderThickness="0"
|
||||||
|
BorderBrush="{StaticResource Light}"
|
||||||
|
Background="Transparent">
|
||||||
|
<Grid HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center">
|
||||||
|
<PathIcon x:Name="ButtonIcon"
|
||||||
|
Width="30"
|
||||||
|
Height="30"
|
||||||
|
Foreground="{StaticResource Light}" />
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="RadioButton.layout:checked /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Light}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="RadioButton.layout:checked /template/ TextBlock#ButtonText">
|
||||||
|
<Setter Property="Foreground" Value="#3A62CB" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="RadioButton.layout:pointerover Border#BackBorder">
|
||||||
|
<Setter Property="BoxShadow" Value="{StaticResource TabRadioButtonShadows}" />
|
||||||
|
<Setter Property="Background" Value="{StaticResource TabRadioButtonPointeroverBackgroundColor}" />
|
||||||
|
</Style>
|
||||||
|
<!-- <Style Selector="RadioButton.tab-left.small"> -->
|
||||||
|
<!-- <Setter Property="FontSize" Value="12" /> -->
|
||||||
|
<!-- </Style> -->
|
||||||
|
<!-- <Style Selector="RadioButton.tab-left.small /template/ PathIcon#ButtonIcon"> -->
|
||||||
|
<!-- <Setter Property="Width" Value="15" /> -->
|
||||||
|
<!-- <Setter Property="Height" Value="15" /> -->
|
||||||
|
<!-- </Style> -->
|
||||||
|
<!-- <Style Selector="RadioButton.tab-left.small /template/ Border"> -->
|
||||||
|
<!-- <Setter Property="Height" Value="25" /> -->
|
||||||
|
<!-- </Style> -->
|
||||||
|
|
||||||
|
<Style Selector="RadioButton.home-app /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource HomeAppData}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="RadioButton.home-camera /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource HomeCameraData}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="RadioButton.home-center /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource HomeCenterData}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="RadioButton.home-statistics /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource HomeStatisticsData}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="RadioButton.home-analysis /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource HomeAnalysisData}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="RadioButton.home-apply /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource HomeApplyData}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="RadioButton.home-settings /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource HomeSettingsData}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="RadioButton.center-event /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource CenterEventData}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="RadioButton.center-video /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource CenterVideoData}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="RadioButton.center-picture /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource CenterPictureData}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="RadioButton.settings-region /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource SettingsRegionData}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="RadioButton.settings-device /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource SettingsDeviceData}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="RadioButton.settings-algo /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource SettingsAlgoData}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="RadioButton.settings-storage /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource SettingsStorageData}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="RadioButton.settings-config /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource SettingsConfigData}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="RadioButton.settings-user /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource SettingsUserData}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="RadioButton.settings-log /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource SettingsLogData}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="RadioButton.layout1 /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource Layout1Data}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="RadioButton.layout4 /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource Layout4Data}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="RadioButton.layout6 /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource Layout6Data}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="RadioButton.layout8 /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource Layout8Data}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="RadioButton.layout9 /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource Layout9Data}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="RadioButton.layout16 /template/ PathIcon#ButtonIcon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource Layout16Data}" />
|
||||||
|
</Style>
|
||||||
|
</Styles>
|
44
detect.gui/Assets/TextBlock.axaml
Normal file
44
detect.gui/Assets/TextBlock.axaml
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<Styles xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||||
|
<Style Selector="TextBlock">
|
||||||
|
<Setter Property="FontFamily" Value="{StaticResource Font-Family}" />
|
||||||
|
<Setter Property="VerticalAlignment" Value="Center" />
|
||||||
|
<Setter Property="FontSize" Value="12" />
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Dark}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="TextBlock.logo-text">
|
||||||
|
<Setter Property="TextAlignment" Value="Center" />
|
||||||
|
<Setter Property="FontSize" Value="40" />
|
||||||
|
<Setter Property="FontWeight" Value="Normal" />
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Light}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="TextBlock.small">
|
||||||
|
<Setter Property="FontSize" Value="9" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="TextBlock.login-header">
|
||||||
|
<Setter Property="FontSize" Value="28" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="TextBlock.dialog-title">
|
||||||
|
<Setter Property="FontSize" Value="14" />
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Light}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="TextBlock.home-title">
|
||||||
|
<Setter Property="FontSize" Value="20" />
|
||||||
|
<Setter Property="FontWeight" Value="Bold" />
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Dark}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="TextBlock.grid-header">
|
||||||
|
<Setter Property="FontSize" Value="14" />
|
||||||
|
<Setter Property="FontWeight" Value="Bold" />
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Light}" />
|
||||||
|
<Setter Property="Padding" Value="18, 0, 0, 0" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="TextBlock.grid-cell">
|
||||||
|
<Setter Property="FontSize" Value="14" />
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource Light}" />
|
||||||
|
<Setter Property="Padding" Value="5, 0, 5, 0" />
|
||||||
|
</Style>
|
||||||
|
</Styles>
|
166
detect.gui/Assets/TextBox.axaml
Normal file
166
detect.gui/Assets/TextBox.axaml
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
<Styles xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||||
|
<Style Selector="TextBox">
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource TextControlForeground}" />
|
||||||
|
<Setter Property="Background">
|
||||||
|
<Setter.Value>
|
||||||
|
<SolidColorBrush Color="#13265a" />
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
<Setter Property="BorderBrush">
|
||||||
|
<Setter.Value>
|
||||||
|
<SolidColorBrush Color="#183678" />
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
<Setter Property="SelectionBrush" Value="{DynamicResource TextControlSelectionHighlightColor}" />
|
||||||
|
<Setter Property="BorderThickness" Value="1" />
|
||||||
|
<Setter Property="CornerRadius" Value="6" />
|
||||||
|
<Setter Property="FontSize" Value="14" />
|
||||||
|
<Setter Property="MinHeight" Value="{DynamicResource TextControlThemeMinHeight}" />
|
||||||
|
<Setter Property="MinWidth" Value="{DynamicResource TextControlThemeMinWidth}" />
|
||||||
|
<Setter Property="Padding" Value="{DynamicResource TextControlThemePadding}" />
|
||||||
|
<Setter Property="FocusAdorner" Value="{x:Null}" />
|
||||||
|
<Setter Property="ScrollViewer.IsScrollChainingEnabled" Value="True" />
|
||||||
|
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate>
|
||||||
|
<DataValidationErrors>
|
||||||
|
<Panel>
|
||||||
|
<Border
|
||||||
|
Name="PART_BorderElement"
|
||||||
|
Background="{TemplateBinding Background}"
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
|
BorderThickness="{TemplateBinding BorderThickness}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}"
|
||||||
|
MinWidth="{TemplateBinding MinWidth}"
|
||||||
|
MinHeight="{TemplateBinding MinHeight}">
|
||||||
|
</Border>
|
||||||
|
<Border>
|
||||||
|
<Grid ColumnDefinitions="Auto,*,Auto" >
|
||||||
|
<ContentPresenter Grid.Column="0"
|
||||||
|
Grid.ColumnSpan="1"
|
||||||
|
Content="{TemplateBinding InnerLeftContent}"/>
|
||||||
|
<DockPanel x:Name="PART_InnerDockPanel"
|
||||||
|
Grid.Column="1"
|
||||||
|
Grid.ColumnSpan="1"
|
||||||
|
Cursor="IBeam"
|
||||||
|
Margin="{TemplateBinding Padding}">
|
||||||
|
<TextBlock Name="PART_FloatingWatermark"
|
||||||
|
Foreground="{DynamicResource SystemAccentColor}"
|
||||||
|
FontSize="{TemplateBinding FontSize}"
|
||||||
|
IsVisible="False"
|
||||||
|
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||||
|
Text="{TemplateBinding Watermark}"
|
||||||
|
DockPanel.Dock="Top" />
|
||||||
|
<ScrollViewer Name="PART_ScrollViewer"
|
||||||
|
HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
|
||||||
|
VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}"
|
||||||
|
IsScrollChainingEnabled="{TemplateBinding (ScrollViewer.IsScrollChainingEnabled)}"
|
||||||
|
AllowAutoHide="{TemplateBinding (ScrollViewer.AllowAutoHide)}"
|
||||||
|
BringIntoViewOnFocusChange="{TemplateBinding (ScrollViewer.BringIntoViewOnFocusChange)}">
|
||||||
|
<Panel>
|
||||||
|
<TextBlock Name="PART_Watermark"
|
||||||
|
Opacity="0.5"
|
||||||
|
Text="{TemplateBinding Watermark}"
|
||||||
|
TextAlignment="{TemplateBinding TextAlignment}"
|
||||||
|
TextWrapping="{TemplateBinding TextWrapping}"
|
||||||
|
IsVisible="{TemplateBinding Text, Converter={x:Static StringConverters.IsNullOrEmpty}}"
|
||||||
|
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||||
|
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
|
||||||
|
<TextPresenter Name="PART_TextPresenter"
|
||||||
|
Foreground="#ffffff"
|
||||||
|
Text="{TemplateBinding Text, Mode=TwoWay}"
|
||||||
|
CaretIndex="{TemplateBinding CaretIndex}"
|
||||||
|
SelectionStart="{TemplateBinding SelectionStart}"
|
||||||
|
SelectionEnd="{TemplateBinding SelectionEnd}"
|
||||||
|
TextAlignment="{TemplateBinding TextAlignment}"
|
||||||
|
TextWrapping="{TemplateBinding TextWrapping}"
|
||||||
|
LineHeight="{TemplateBinding LineHeight}"
|
||||||
|
LetterSpacing="{TemplateBinding LetterSpacing}"
|
||||||
|
PasswordChar="{TemplateBinding PasswordChar}"
|
||||||
|
RevealPassword="{TemplateBinding RevealPassword}"
|
||||||
|
SelectionBrush="{TemplateBinding SelectionBrush}"
|
||||||
|
SelectionForegroundBrush="{TemplateBinding SelectionForegroundBrush}"
|
||||||
|
CaretBrush="#FFFFFF"
|
||||||
|
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||||
|
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
|
||||||
|
</Panel>
|
||||||
|
</ScrollViewer>
|
||||||
|
</DockPanel>
|
||||||
|
<ContentPresenter Grid.Column="2" Grid.ColumnSpan="1" Content="{TemplateBinding InnerRightContent}"/>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</Panel>
|
||||||
|
</DataValidationErrors>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
<Style Selector="^ /template/ TextBlock#PART_Watermark">
|
||||||
|
<Setter Property="Foreground" Value="#aaaaaa" />
|
||||||
|
<Setter Property="FontSize" Value="14" />
|
||||||
|
</Style>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="TextBox:pointerover">
|
||||||
|
<Setter Property="BorderThickness" Value="1" />
|
||||||
|
<Style Selector="^ /template/ Border#PART_BorderElement">
|
||||||
|
<Setter Property="Background">
|
||||||
|
<Setter.Value>
|
||||||
|
<SolidColorBrush Color="#183171" />
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
<Setter Property="BorderBrush">
|
||||||
|
<Setter.Value>
|
||||||
|
<SolidColorBrush Color="#1d4492" />
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^ /template/ TextBlock#PART_Watermark">
|
||||||
|
<Setter Property="Foreground" Value="#aaaaaa" />
|
||||||
|
<Setter Property="FontSize" Value="14" />
|
||||||
|
</Style>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="TextBox:focus">
|
||||||
|
<Setter Property="BorderThickness" Value="1" />
|
||||||
|
<Style Selector="^ /template/ Border#PART_BorderElement">
|
||||||
|
<Setter Property="Background">
|
||||||
|
<Setter.Value>
|
||||||
|
<SolidColorBrush Color="#183171" />
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
<Setter Property="BorderBrush">
|
||||||
|
<Setter.Value>
|
||||||
|
<SolidColorBrush Color="#1d4492" />
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
<Setter Property="BorderThickness" Value="0" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^ /template/ TextBlock#PART_Watermark">
|
||||||
|
<Setter Property="Foreground" Value="#aaaaaa" />
|
||||||
|
<Setter Property="FontSize" Value="14" />
|
||||||
|
</Style>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="TextBox.login">
|
||||||
|
<Setter Property="BorderBrush">
|
||||||
|
<Setter.Value>
|
||||||
|
<SolidColorBrush Color="#3458b7" />
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
<Setter Property="FontSize" Value="14" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="TextBox.login:pointerover">
|
||||||
|
<Setter Property="BorderBrush">
|
||||||
|
<Setter.Value>
|
||||||
|
<SolidColorBrush Color="#375dc1" />
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
<Setter Property="FontSize" Value="14" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="TextBox.login:focus">
|
||||||
|
<Setter Property="BorderBrush">
|
||||||
|
<Setter.Value>
|
||||||
|
<SolidColorBrush Color="#375dc1" />
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
<Setter Property="FontSize" Value="14" />
|
||||||
|
</Style>
|
||||||
|
</Styles>
|
44
detect.gui/Assets/ToolTip.axaml
Normal file
44
detect.gui/Assets/ToolTip.axaml
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<Styles xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||||
|
<Style Selector="ToolTip">
|
||||||
|
<Setter Property="FontFamily" Value="{StaticResource Font-Family}" />
|
||||||
|
<Setter Property="FontSize" Value="12" />
|
||||||
|
<Setter Property="Foreground" Value="#FFFFFF" />
|
||||||
|
<Setter Property="Background">
|
||||||
|
<Setter.Value>
|
||||||
|
<SolidColorBrush Color="#2D77F3" Opacity="0.4" />
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
<Setter Property="CornerRadius" Value="6" />
|
||||||
|
<Setter Property="BorderThickness" Value="1" />
|
||||||
|
<Setter Property="BorderBrush">
|
||||||
|
<Setter.Value>
|
||||||
|
<SolidColorBrush Color="#2D77F3" Opacity="0.6" />
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate>
|
||||||
|
<Border Name="PART_LayoutRoot"
|
||||||
|
BorderThickness="{TemplateBinding BorderThickness}"
|
||||||
|
Background="{TemplateBinding Background}"
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
|
Padding="{TemplateBinding Padding}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}">
|
||||||
|
<ContentPresenter Name="PART_ContentPresenter"
|
||||||
|
MaxWidth="{TemplateBinding MaxWidth}"
|
||||||
|
Content="{TemplateBinding Content}"
|
||||||
|
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||||
|
TextBlock.TextWrapping="Wrap" >
|
||||||
|
<ContentPresenter.Styles>
|
||||||
|
<Style Selector="TextBlock">
|
||||||
|
<Setter Property="FontSize" Value="12" />
|
||||||
|
<Setter Property="Foreground" Value="#ffffff" />
|
||||||
|
<Setter Property="FontFamily" Value="{StaticResource Font-Family}" />
|
||||||
|
</Style>
|
||||||
|
</ContentPresenter.Styles>
|
||||||
|
</ContentPresenter>
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
</Styles>
|
12
detect.gui/Assets/WebView.axaml
Normal file
12
detect.gui/Assets/WebView.axaml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<Styles xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:webViewControl="clr-namespace:WebViewControl;assembly=WebViewControl.Avalonia">
|
||||||
|
|
||||||
|
<Styles.Resources>
|
||||||
|
|
||||||
|
</Styles.Resources>
|
||||||
|
|
||||||
|
<Style Selector="webViewControl|WebView">
|
||||||
|
|
||||||
|
</Style>
|
||||||
|
</Styles>
|
BIN
detect.gui/Assets/logo.ico
Normal file
BIN
detect.gui/Assets/logo.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 87 KiB |
37
detect.gui/Classes/NotificationService.cs
Normal file
37
detect.gui/Classes/NotificationService.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
using System;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Notifications;
|
||||||
|
|
||||||
|
namespace detect.gui.Classes;
|
||||||
|
|
||||||
|
public abstract class NotificationService
|
||||||
|
{
|
||||||
|
private static WindowNotificationManager? _notificationManager;
|
||||||
|
|
||||||
|
private const int NotificationTimeout = 4;
|
||||||
|
|
||||||
|
public static void SetHostWindow(TopLevel? window)
|
||||||
|
{
|
||||||
|
var notificationManager = new WindowNotificationManager(window)
|
||||||
|
{
|
||||||
|
Position = NotificationPosition.BottomRight,
|
||||||
|
MaxItems = 4
|
||||||
|
};
|
||||||
|
|
||||||
|
_notificationManager = notificationManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Show(string title, string? message, NotificationType type,Action? onClick = null)
|
||||||
|
{
|
||||||
|
if (_notificationManager is { } nm)
|
||||||
|
{
|
||||||
|
nm.Show(
|
||||||
|
new Notification(
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
type,
|
||||||
|
new TimeSpan(0,0,0, NotificationTimeout),
|
||||||
|
onClick));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
88
detect.gui/Classes/ViewModelItem.cs
Normal file
88
detect.gui/Classes/ViewModelItem.cs
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using detect.gui.Commons;
|
||||||
|
using ReactiveUI;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace detect.gui.Classes;
|
||||||
|
|
||||||
|
public class ViewModelItem
|
||||||
|
{
|
||||||
|
private readonly RoutingState _router;
|
||||||
|
|
||||||
|
public string? ViewModelTypeName { get; init; }
|
||||||
|
public string? Text { get; init; }
|
||||||
|
public ViewModelItem? Parent { get; init; }
|
||||||
|
public List<ViewModelItem> Children { get; set; } = new List<ViewModelItem>();
|
||||||
|
public bool IsAuto { get; init; }
|
||||||
|
public bool IsActived { get; set; }
|
||||||
|
|
||||||
|
public object? Param { get; set; }
|
||||||
|
|
||||||
|
public ViewModelItem(RoutingState router)
|
||||||
|
{
|
||||||
|
_router = router;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Goto(object? param = null)
|
||||||
|
{
|
||||||
|
var v = RouterHelper.GetViewModel(ViewModelTypeName!);
|
||||||
|
if (v == null) return;
|
||||||
|
|
||||||
|
IsActived = true;
|
||||||
|
|
||||||
|
Parent?.Children.ForEach(i =>
|
||||||
|
{
|
||||||
|
if (ViewModelTypeName != i.ViewModelTypeName)
|
||||||
|
i.IsActived = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
var count = _router.NavigationStack.Count(x => x.GetType().Name == ViewModelTypeName);
|
||||||
|
if (count != 0)
|
||||||
|
{
|
||||||
|
var r = _router.NavigationStack.First(x => x.GetType().Name == ViewModelTypeName);
|
||||||
|
v.GetType().GetProperty("Item")!.SetValue(r, param);
|
||||||
|
_router.NavigationStack.Remove(r);
|
||||||
|
_router.Navigate.Execute(r);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (Parent != null)
|
||||||
|
{
|
||||||
|
Parent.IsActived = true;
|
||||||
|
var parentType = Parent.ViewModelTypeName?.GetTypeByName();
|
||||||
|
var parentModel = parentType?.Assembly.CreateInstance(parentType.FullName!);
|
||||||
|
if (parentModel != null)
|
||||||
|
{
|
||||||
|
_router.NavigationStack.Add((IRoutableViewModel)parentModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
v.GetType().GetProperty("Item")!.SetValue(v, param);
|
||||||
|
_router.Navigate.Execute(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
var child = Children.FirstOrDefault(x => x.IsActived);
|
||||||
|
if (child == null && Children.Any() && IsAuto)
|
||||||
|
Children[0].Goto();
|
||||||
|
else if (child != null && Children.Any())
|
||||||
|
{
|
||||||
|
var c = _router.NavigationStack.First(x => x.GetType().Name == child.ViewModelTypeName);
|
||||||
|
_router.NavigationStack.Remove(c);
|
||||||
|
_router.Navigate.Execute(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Goback()
|
||||||
|
{
|
||||||
|
IsActived = false;
|
||||||
|
|
||||||
|
var r = _router.NavigationStack.First(x => x.GetType().Name == ViewModelTypeName);
|
||||||
|
_router.NavigationStack.Remove(r);
|
||||||
|
|
||||||
|
if (Parent == null) return;
|
||||||
|
|
||||||
|
var p = _router.NavigationStack.First(x => x.GetType().Name == Parent.ViewModelTypeName);
|
||||||
|
_router.NavigationStack.Remove(p);
|
||||||
|
_router.Navigate.Execute(p);
|
||||||
|
}
|
||||||
|
}
|
29
detect.gui/Classes/WindowBase.cs
Normal file
29
detect.gui/Classes/WindowBase.cs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
using System;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Platform;
|
||||||
|
using Avalonia.ReactiveUI;
|
||||||
|
using detect.gui.ViewModels;
|
||||||
|
using Avalonia;
|
||||||
|
|
||||||
|
namespace detect.gui.Classes;
|
||||||
|
|
||||||
|
public class WindowBase<T> : ReactiveWindow<T> where T : ViewModelBase
|
||||||
|
{
|
||||||
|
protected WindowBase()
|
||||||
|
{
|
||||||
|
Icon = new WindowIcon(AssetLoader.Open(new Uri("avares://detect.gui/Assets/logo.ico")));
|
||||||
|
Title = "AI智能视频分析平台";
|
||||||
|
ExtendClientAreaToDecorationsHint = true;
|
||||||
|
ExtendClientAreaChromeHints = ExtendClientAreaChromeHints.NoChrome;
|
||||||
|
ExtendClientAreaTitleBarHeightHint = -1;
|
||||||
|
// WindowStartupLocation = WindowStartupLocation.CenterScreen;
|
||||||
|
SystemDecorations = SystemDecorations.None;
|
||||||
|
Topmost = false;
|
||||||
|
CanResize = false;
|
||||||
|
|
||||||
|
if (Screens.Primary == null) return;
|
||||||
|
Width = Screens.Primary.Bounds.Width / Screens.Primary.Scaling;
|
||||||
|
Height = Screens.Primary.Bounds.Height / Screens.Primary.Scaling;
|
||||||
|
Position = new PixelPoint(0, 0);
|
||||||
|
}
|
||||||
|
}
|
108
detect.gui/Commons/ClassHelper.cs
Normal file
108
detect.gui/Commons/ClassHelper.cs
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
|
||||||
|
namespace detect.gui.Commons;
|
||||||
|
|
||||||
|
public static class ClassHelper
|
||||||
|
{
|
||||||
|
public static object? GetValue<S>(this S source, string name)
|
||||||
|
{
|
||||||
|
var sp = (source?.GetType().GetProperties() ?? throw new InvalidOperationException()).FirstOrDefault(p =>
|
||||||
|
p.Name == name);
|
||||||
|
return sp == null ? default : sp.GetValue(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetFullName(this string typeName)
|
||||||
|
{
|
||||||
|
return "detect.gui.ViewModels." + typeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Type? GetTypeByName(this string typeName)
|
||||||
|
{
|
||||||
|
Type? type = null;
|
||||||
|
var assemblyArray = AppDomain.CurrentDomain.GetAssemblies();
|
||||||
|
var assemblyArrayLength = assemblyArray.Length;
|
||||||
|
for (var i = 0; i < assemblyArrayLength; ++i)
|
||||||
|
{
|
||||||
|
type = assemblyArray[i].GetType(typeName);
|
||||||
|
if (type != null)
|
||||||
|
{
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; (i < assemblyArrayLength); ++i)
|
||||||
|
{
|
||||||
|
var typeArray = assemblyArray[i].GetTypes();
|
||||||
|
var typeArrayLength = typeArray.Length;
|
||||||
|
for (var j = 0; j < typeArrayLength; ++j)
|
||||||
|
{
|
||||||
|
if (typeArray[j].Name.Equals(typeName))
|
||||||
|
{
|
||||||
|
return typeArray[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T? GetParent<T>(this Control control, string? name = null) where T : Control
|
||||||
|
{
|
||||||
|
var parent = control.Parent;
|
||||||
|
while (parent != null)
|
||||||
|
{
|
||||||
|
if (parent is T)
|
||||||
|
{
|
||||||
|
if (name == null) break;
|
||||||
|
if (parent.Name == name) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent = parent.Parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (T?)parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T Convert<S, T>(this S source)
|
||||||
|
{
|
||||||
|
var target = Activator.CreateInstance<T>();
|
||||||
|
var sourcePropertyList = typeof(S).GetProperties().ToList();
|
||||||
|
var targetPropertyList = typeof(T).GetProperties().ToList();
|
||||||
|
foreach (var sourceProperty in sourcePropertyList)
|
||||||
|
{
|
||||||
|
if (sourceProperty.DeclaringType?.FullName == "ReactiveUI.ReactiveObject") continue;
|
||||||
|
var sp = (source?.GetType().GetProperties() ?? throw new InvalidOperationException()).FirstOrDefault(p =>
|
||||||
|
p.Name == sourceProperty.Name);
|
||||||
|
if (sp == null) continue;
|
||||||
|
var value = sp.GetValue(source);
|
||||||
|
if (value == null) continue;
|
||||||
|
var cnt = targetPropertyList.Count(p => p.Name == sp.Name);
|
||||||
|
if (cnt == 0) continue;
|
||||||
|
var targetProperty = targetPropertyList.Where(p => p.Name == sp.Name).ToArray()[0];
|
||||||
|
targetProperty.SetValue(target, value);
|
||||||
|
}
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object ConvertType(object? convertibleValue, Type type)
|
||||||
|
{
|
||||||
|
if (convertibleValue == null) throw new Exception("Value is null"); //convertibleValue));
|
||||||
|
if (!type.IsGenericType)
|
||||||
|
{
|
||||||
|
return System.Convert.ChangeType(convertibleValue, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
var genericTypeDefinition = type.GetGenericTypeDefinition();
|
||||||
|
if (genericTypeDefinition == typeof(Nullable<>))
|
||||||
|
{
|
||||||
|
var ddd = Nullable.GetUnderlyingType(type);
|
||||||
|
return System.Convert.ChangeType(convertibleValue,
|
||||||
|
Nullable.GetUnderlyingType(type) ?? throw new InvalidOperationException(
|
||||||
|
$"Invalid cast from type \"{convertibleValue.GetType().FullName}\" to type \"{type.FullName}\"."));
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidCastException(
|
||||||
|
$"Invalid cast from type \"{convertibleValue.GetType().FullName}\" to type \"{type.FullName}\".");
|
||||||
|
}
|
||||||
|
}
|
106
detect.gui/Commons/ConstantHelper.cs
Normal file
106
detect.gui/Commons/ConstantHelper.cs
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
using Avalonia.Collections;
|
||||||
|
using Avalonia.Media;
|
||||||
|
using ReactiveUI;
|
||||||
|
|
||||||
|
namespace detect.gui.Commons;
|
||||||
|
|
||||||
|
public static class ConstantHelper
|
||||||
|
{
|
||||||
|
|
||||||
|
public static AvaloniaList<SelectItem> YesNoList(bool hasAll = true)
|
||||||
|
{
|
||||||
|
var result = new AvaloniaList<SelectItem>
|
||||||
|
{
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Name = "全部"
|
||||||
|
},
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Name = "是",
|
||||||
|
Value = "YES"
|
||||||
|
},
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Name = "否",
|
||||||
|
Value = "NO"
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if (!hasAll) result.RemoveAt(0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AvaloniaList<SelectItem> MessageTypeList(bool hasAll = true)
|
||||||
|
{
|
||||||
|
var result = new AvaloniaList<SelectItem>
|
||||||
|
{
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Name = "全部"
|
||||||
|
},
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Name = "错误",
|
||||||
|
Value = "ERROR"
|
||||||
|
},
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Name = "警告",
|
||||||
|
Value = "WARING"
|
||||||
|
},
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Name = "通知",
|
||||||
|
Value = "NOTICE"
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if (!hasAll) result.RemoveAt(0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Brush Dark { get; set; } = new SolidColorBrush(Color.FromRgb(52,58,64));
|
||||||
|
public static Brush LightGray { get; set; } = new SolidColorBrush(Color.FromRgb(204,204,204));
|
||||||
|
public static Brush BrightGreen { get; set; } = new SolidColorBrush(Color.FromRgb(167,221,36));
|
||||||
|
public static Brush LightGreen { get; set; } = new SolidColorBrush(Color.FromRgb(65,156,16));
|
||||||
|
public static Brush Green { get; set; } = new SolidColorBrush(Color.FromRgb(40,167,69));
|
||||||
|
public static Brush Orange { get; set; } = new SolidColorBrush(Color.FromRgb(253,126,20));
|
||||||
|
public static Brush BrightRed { get; set; } = new SolidColorBrush(Color.FromRgb(250,153,169));
|
||||||
|
public static Brush Danger { get; set; } = new SolidColorBrush(Color.FromRgb(220,53,69));
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SelectItem : ReactiveObject
|
||||||
|
{
|
||||||
|
private string? _name;
|
||||||
|
|
||||||
|
public string? Name
|
||||||
|
{
|
||||||
|
get => _name;
|
||||||
|
set => this.RaiseAndSetIfChanged(ref _name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string? _value;
|
||||||
|
|
||||||
|
public string? Value
|
||||||
|
{
|
||||||
|
get => _value;
|
||||||
|
set => this.RaiseAndSetIfChanged(ref _value, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _isSelected;
|
||||||
|
|
||||||
|
public SelectItem()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public SelectItem(string? name, string? value)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsSelected
|
||||||
|
{
|
||||||
|
get => _isSelected;
|
||||||
|
set => this.RaiseAndSetIfChanged(ref _isSelected, value);
|
||||||
|
}
|
||||||
|
}
|
29
detect.gui/Commons/Enums.cs
Normal file
29
detect.gui/Commons/Enums.cs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
|
namespace detect.gui.Commons;
|
||||||
|
|
||||||
|
public enum AlarmTypeEnum
|
||||||
|
{
|
||||||
|
[Description("报警1")]
|
||||||
|
type1,
|
||||||
|
[Description("报警2")]
|
||||||
|
type2,
|
||||||
|
[Description("报警3")]
|
||||||
|
type3,
|
||||||
|
[Description("报警4")]
|
||||||
|
type4,
|
||||||
|
[Description("报警5")]
|
||||||
|
type5
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class EnumExtensions
|
||||||
|
{
|
||||||
|
public static string? GetDescription(this Enum val)
|
||||||
|
{
|
||||||
|
var field = val.GetType().GetField(val.ToString());
|
||||||
|
if (field == null) return null;
|
||||||
|
var customAttribute = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute));
|
||||||
|
return customAttribute == null ? val.ToString() : ((DescriptionAttribute)customAttribute).Description;
|
||||||
|
}
|
||||||
|
}
|
63
detect.gui/Commons/PredicateBuilder.cs
Normal file
63
detect.gui/Commons/PredicateBuilder.cs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
|
||||||
|
namespace detect.gui.Commons;
|
||||||
|
|
||||||
|
public static class PredicateBuilder
|
||||||
|
{
|
||||||
|
public static Expression<Func<T, bool>> GetTrue<T>()
|
||||||
|
{
|
||||||
|
return f => true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Expression<Func<T, bool>> GetFalse<T>()
|
||||||
|
{
|
||||||
|
return f => false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first,
|
||||||
|
Expression<Func<T, bool>> second)
|
||||||
|
{
|
||||||
|
return first.AndAlso(second, Expression.AndAlso);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first,
|
||||||
|
Expression<Func<T, bool>> second)
|
||||||
|
{
|
||||||
|
return first.AndAlso(second, Expression.OrElse);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Expression<Func<T, bool>> AndAlso<T>(this Expression<Func<T, bool>> expr1,
|
||||||
|
Expression<Func<T, bool>> expr2, Func<Expression, Expression, BinaryExpression> func)
|
||||||
|
{
|
||||||
|
var parameter = Expression.Parameter(typeof(T));
|
||||||
|
|
||||||
|
var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], parameter);
|
||||||
|
var left = leftVisitor.Visit(expr1.Body);
|
||||||
|
|
||||||
|
var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter);
|
||||||
|
var right = rightVisitor.Visit(expr2.Body);
|
||||||
|
|
||||||
|
if (left == null || right == null)
|
||||||
|
return Expression.Lambda<Func<T, bool>>(() => true);
|
||||||
|
|
||||||
|
return Expression.Lambda<Func<T, bool>>(func(left, right), parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ReplaceExpressionVisitor : ExpressionVisitor
|
||||||
|
{
|
||||||
|
private readonly Expression? _oldValue;
|
||||||
|
private readonly Expression? _newValue;
|
||||||
|
|
||||||
|
public ReplaceExpressionVisitor(Expression? oldValue, Expression? newValue)
|
||||||
|
{
|
||||||
|
_oldValue = oldValue;
|
||||||
|
_newValue = newValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Expression? Visit(Expression? node)
|
||||||
|
{
|
||||||
|
return node == _oldValue ? _newValue : base.Visit(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
19
detect.gui/Commons/RegexHelper.cs
Normal file
19
detect.gui/Commons/RegexHelper.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
namespace detect.gui.Commons;
|
||||||
|
|
||||||
|
public class RegexHelper
|
||||||
|
{
|
||||||
|
public const string Email = @"^(\w)+(\.\w)*@(\w)+((\.\w+)+)$"; // ^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$ , [A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}
|
||||||
|
public const string TelephoneNumber = @"(d+-)?(d{4}-?d{7}|d{3}-?d{8}|^d{7,8})(-d+)?"; //座机号码(中国大陆)
|
||||||
|
public const string MobilePhoneNumber = @"^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$"; //移动电话
|
||||||
|
public const string Age = @"^(?:[1-9][0-9]?|1[01][0-9]|120)$"; // 年龄 1-120 之间有效
|
||||||
|
public const string Birthday = @"^((?:19[2-9]\d{1})|(?:20(?:(?:0[0-9])|(?:1[0-8]))))((?:0?[1-9])|(?:1[0-2]))((?:0?[1-9])|(?:[1-2][0-9])|30|31)$";
|
||||||
|
public const string Timespan = @"^15|16|17\d{8,11}$"; // 目前时间戳是15开头,以后16、17等开头,长度 10 位是秒级时间戳的正则,13 位时间戳是到毫秒级的。
|
||||||
|
public const string IdentityCardNumber = @"^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$|^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$";
|
||||||
|
public const string IpV4 = @"^((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}$";
|
||||||
|
public const string IpV6 = @"^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$";
|
||||||
|
public const string Domain = @"^[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+\.?$";
|
||||||
|
public const string English = @"^[A-Za-z]+$";
|
||||||
|
public const string Chinese = @"^[\u4e00-\u9fa5]{0,}$";
|
||||||
|
public const string MacAddress = @"^([0-9A-F]{2})(-[0-9A-F]{2}){5}$";
|
||||||
|
public const string Url = @"^[a-zA-z]+://(\w+(-\w+)*)(\.(\w+(-\w+)*))*(\?\S*)?$";
|
||||||
|
}
|
48
detect.gui/Commons/RouterHelper.cs
Normal file
48
detect.gui/Commons/RouterHelper.cs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using detect.gui.Classes;
|
||||||
|
using ReactiveUI;
|
||||||
|
|
||||||
|
namespace detect.gui.Commons;
|
||||||
|
|
||||||
|
public static class RouterHelper
|
||||||
|
{
|
||||||
|
public static void Init(RoutingState _router)
|
||||||
|
{
|
||||||
|
Router = _router;
|
||||||
|
ViewModelList = new List<ViewModelItem>
|
||||||
|
{
|
||||||
|
new(Router) { ViewModelTypeName = "HomeViewModel", Text = "首页" },
|
||||||
|
new(Router) { ViewModelTypeName = "DetectTaskViewModel", Text = "任务管理" },
|
||||||
|
new(Router) { ViewModelTypeName = "DeviceViewModel", Text = "设备管理" },
|
||||||
|
new(Router) { ViewModelTypeName = "LogViewModel", Text = "日志查看" },
|
||||||
|
new(Router) { ViewModelTypeName = "UserViewModel", Text = "用户设置" }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<ViewModelItem> ViewModelList = null!;
|
||||||
|
private static RoutingState Router = null!;
|
||||||
|
|
||||||
|
public static IRoutableViewModel? GetViewModel(string viewModelTypeName)
|
||||||
|
{
|
||||||
|
var t = viewModelTypeName.GetTypeByName();
|
||||||
|
return (IRoutableViewModel?)t?.Assembly.CreateInstance(t.FullName!);
|
||||||
|
}
|
||||||
|
public static ViewModelItem? FindViewModelItem(List<ViewModelItem>? viewModelList,string viewModelTypeName, ViewModelItem? result)
|
||||||
|
{
|
||||||
|
foreach (var item in viewModelList!)
|
||||||
|
{
|
||||||
|
if (item.ViewModelTypeName == viewModelTypeName)
|
||||||
|
{
|
||||||
|
result = item;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (result == null && item.Children.Any())
|
||||||
|
{
|
||||||
|
result = FindViewModelItem(item.Children, viewModelTypeName, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
71
detect.gui/Context/DetectContext.cs
Normal file
71
detect.gui/Context/DetectContext.cs
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
using System.IO;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using detect.gui.Models.Entities;
|
||||||
|
using xwd.utils;
|
||||||
|
|
||||||
|
namespace detect.gui.Context;
|
||||||
|
|
||||||
|
public class DetectContext : DbContext
|
||||||
|
{
|
||||||
|
private readonly string _dbPath = AppSettingsManager.Manager().GetString("Database.Path");
|
||||||
|
|
||||||
|
public DetectContext()
|
||||||
|
{
|
||||||
|
// 创建数据库
|
||||||
|
if (base.Database.EnsureCreated())
|
||||||
|
InitData();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化
|
||||||
|
/// </summary>
|
||||||
|
private void InitData()
|
||||||
|
{
|
||||||
|
Set<AuthorityEntity>().Add(new AuthorityEntity { Id = 1, Name = "首页", ParentId = 0 });
|
||||||
|
Set<AuthorityEntity>().Add(new AuthorityEntity { Id = 2, Name = "任务管理", ParentId = 0 });
|
||||||
|
Set<AuthorityEntity>().Add(new AuthorityEntity { Id = 3, Name = "设备管理", ParentId = 0 });
|
||||||
|
Set<AuthorityEntity>().Add(new AuthorityEntity { Id = 4, Name = "日志查看", ParentId = 0 });
|
||||||
|
Set<AuthorityEntity>().Add(new AuthorityEntity { Id = 5, Name = "用户管理", ParentId = 0 });
|
||||||
|
|
||||||
|
Set<UserEntity>().Add(new UserEntity { Id = 1, Username = "admin", Password = "winner!", RealName = "管理员" });
|
||||||
|
|
||||||
|
Set<UserAuthorityEntity>().Add(new UserAuthorityEntity { Id = 1, UserId = 1, AuthorityId = 1 });
|
||||||
|
Set<UserAuthorityEntity>().Add(new UserAuthorityEntity { Id = 2, UserId = 1, AuthorityId = 2 });
|
||||||
|
Set<UserAuthorityEntity>().Add(new UserAuthorityEntity { Id = 3, UserId = 1, AuthorityId = 3 });
|
||||||
|
Set<UserAuthorityEntity>().Add(new UserAuthorityEntity { Id = 4, UserId = 1, AuthorityId = 4 });
|
||||||
|
|
||||||
|
Set<DeviceEntity>().Add(new DeviceEntity { Id = 1, Name = "设备11", DeviceIp = "192.168.101.253", DevicePort = "13000", CameraRtsp = "https://stream7.iqilu.com/10339/article/202002/17/4417a27b1a656f4779eaa005ecd1a1a0.mp4"});
|
||||||
|
Set<DeviceEntity>().Add(new DeviceEntity { Id = 2, Name = "设备12", CameraRtsp = "https://stream7.iqilu.com/10339/article/202002/17/4417a27b1a656f4779eaa005ecd1a1a0.mp4"});
|
||||||
|
Set<DeviceEntity>().Add(new DeviceEntity { Id = 3, Name = "设备13", CameraRtsp = "https://stream7.iqilu.com/10339/article/202002/17/4417a27b1a656f4779eaa005ecd1a1a0.mp4"});
|
||||||
|
|
||||||
|
SaveChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
|
{
|
||||||
|
optionsBuilder.UseSqlite($"Data Source={_dbPath}detect.db");
|
||||||
|
base.OnConfiguring(optionsBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
modelBuilder.Entity<UserEntity>().ToTable("User");
|
||||||
|
modelBuilder.Entity<AuthorityEntity>().ToTable("Authority");
|
||||||
|
modelBuilder.Entity<UserAuthorityEntity>().ToTable("UserAuthority");
|
||||||
|
modelBuilder.Entity<DetectTaskEntity>().ToTable("DetectTask");
|
||||||
|
modelBuilder.Entity<DeviceEntity>().ToTable("Device");
|
||||||
|
modelBuilder.Entity<LogEntity>().ToTable("Log");
|
||||||
|
|
||||||
|
modelBuilder.Entity<UserEntity>().Property(p => p.CreateTime).HasDefaultValueSql("datetime('now')");
|
||||||
|
modelBuilder.Entity<UserEntity>().Property(p => p.UpdateTime).HasDefaultValueSql("datetime('now')");
|
||||||
|
modelBuilder.Entity<AuthorityEntity>().Property(p => p.CreateTime).HasDefaultValueSql("datetime('now')");
|
||||||
|
modelBuilder.Entity<AuthorityEntity>().Property(p => p.UpdateTime).HasDefaultValueSql("datetime('now')");
|
||||||
|
modelBuilder.Entity<AuthorityEntity>().Property(p => p.ParentId).HasDefaultValueSql("0");
|
||||||
|
modelBuilder.Entity<DetectTaskEntity>().Property(p => p.CreateTime).HasDefaultValueSql("datetime('now')");
|
||||||
|
modelBuilder.Entity<DetectTaskEntity>().Property(p => p.UpdateTime).HasDefaultValueSql("datetime('now')");
|
||||||
|
modelBuilder.Entity<DeviceEntity>().Property(p => p.CreateTime).HasDefaultValueSql("datetime('now')");
|
||||||
|
modelBuilder.Entity<DeviceEntity>().Property(p => p.UpdateTime).HasDefaultValueSql("datetime('now')");
|
||||||
|
modelBuilder.Entity<LogEntity>().Property(p => p.CreateTime).HasDefaultValueSql("datetime('now')");
|
||||||
|
base.OnModelCreating(modelBuilder);
|
||||||
|
}
|
||||||
|
}
|
12
detect.gui/Controls/Arrow.axaml
Normal file
12
detect.gui/Controls/Arrow.axaml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:controls="clr-namespace:detect.gui.Controls"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="detect.gui.Controls.Arrow">
|
||||||
|
<Canvas Width="14" Height="30">
|
||||||
|
<Path Stroke="{Binding $parent[controls:Arrow].ItemColor}" StrokeThickness="2" Data="M2,20 L7,30 L12,20"/>
|
||||||
|
<Path Stroke="{Binding $parent[controls:Arrow].ItemColor}" StrokeThickness="2" Data="M7,0 L7,30"/>
|
||||||
|
</Canvas>
|
||||||
|
</UserControl>
|
27
detect.gui/Controls/Arrow.axaml.cs
Normal file
27
detect.gui/Controls/Arrow.axaml.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Avalonia.Media;
|
||||||
|
|
||||||
|
namespace detect.gui.Controls;
|
||||||
|
|
||||||
|
public partial class Arrow : UserControl
|
||||||
|
{
|
||||||
|
public static readonly StyledProperty<IBrush?> ItemColorProperty =
|
||||||
|
AvaloniaProperty.Register<Arrow, IBrush?>(nameof(ItemColor));
|
||||||
|
public IBrush? ItemColor
|
||||||
|
{
|
||||||
|
get => GetValue(ItemColorProperty);
|
||||||
|
set => SetValue(ItemColorProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Arrow()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
}
|
42
detect.gui/Controls/PageControl.axaml
Normal file
42
detect.gui/Controls/PageControl.axaml
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<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:viewModels="clr-namespace:detect.gui.ViewModels"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="detect.gui.Controls.PageControl"
|
||||||
|
x:DataType="viewModels:PageModelBase">
|
||||||
|
<Border Padding="10,5">
|
||||||
|
<StackPanel Orientation="Horizontal"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Spacing="0">
|
||||||
|
<StackPanel.Styles>
|
||||||
|
<Style Selector="TextBlock">
|
||||||
|
<Setter Property="Foreground" Value="#ffffff" />
|
||||||
|
<Setter Property="FontSize" Value="14" />
|
||||||
|
</Style>
|
||||||
|
</StackPanel.Styles>
|
||||||
|
<TextBlock Text="总记录数: " />
|
||||||
|
<TextBlock Text="{Binding RecordCount}" />
|
||||||
|
<TextBlock Width="20" />
|
||||||
|
<TextBlock Text="当前页: " />
|
||||||
|
<TextBlock Text="{Binding PageNum}" />
|
||||||
|
<TextBlock Text=" / " />
|
||||||
|
<TextBlock Text="总页数: " />
|
||||||
|
<TextBlock Text="{Binding PageCount}" />
|
||||||
|
<TextBlock Width="20" />
|
||||||
|
<Button Classes="icon-button circle first-page small page"
|
||||||
|
IsEnabled="{Binding CanFirst}"
|
||||||
|
Command="{Binding FirstCommand}" />
|
||||||
|
<Button Classes="icon-button circle left-page small page"
|
||||||
|
IsEnabled="{Binding CanLeft}"
|
||||||
|
Command="{Binding LeftCommand}" />
|
||||||
|
<Button Classes="icon-button circle right-page small page"
|
||||||
|
IsEnabled="{Binding CanRight}"
|
||||||
|
Command="{Binding RightCommand}" />
|
||||||
|
<Button Classes="icon-button circle last-page small page"
|
||||||
|
IsEnabled="{Binding CanLast}"
|
||||||
|
Command="{Binding LastCommand}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</UserControl>
|
17
detect.gui/Controls/PageControl.axaml.cs
Normal file
17
detect.gui/Controls/PageControl.axaml.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
|
||||||
|
namespace detect.gui.Controls;
|
||||||
|
|
||||||
|
public partial class PageControl : UserControl
|
||||||
|
{
|
||||||
|
public PageControl()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user