mirror of
https://github.com/9ParsonsB/Pulsar.git
synced 2025-04-04 09:19:38 -04:00
WIP: observatory UI overhaul
This commit is contained in:
parent
1d62a0ec1d
commit
d99a190869
4
.vscode/launch.json
vendored
4
.vscode/launch.json
vendored
@ -5,12 +5,12 @@
|
||||
// Use IntelliSense to find out which attributes exist for C# debugging
|
||||
// Use hover for the description of the existing attributes
|
||||
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
|
||||
"name": ".NET Core Launch (console)",
|
||||
"name": ".NET Core Launch (debug)",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
// If you have changed target frameworks, make sure to update the program path.
|
||||
"program": "${workspaceFolder}/ObservatoryCore/bin/Debug/net5.0/ObservatoryCore.dll",
|
||||
"program": "${workspaceFolder}/ObservatoryCore/bin/debug/net6.0-windows/ObservatoryCore.exe",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}/ObservatoryCore",
|
||||
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
||||
|
@ -5,6 +5,7 @@
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<SignAssembly>false</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>ObservatoryKey.snk</AssemblyOriginatorKeyFile>
|
||||
<Configurations>Debug;Release;Portable</Configurations>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
@ -15,25 +15,29 @@ namespace Observatory.NativeNotification
|
||||
public Guid InvokeNativeNotification(NotificationArgs notificationArgs)
|
||||
{
|
||||
var notificationGuid = Guid.NewGuid();
|
||||
var notification = new NotificationForm()
|
||||
Application.OpenForms[0].Invoke(() =>
|
||||
{
|
||||
Guid = notificationGuid
|
||||
};
|
||||
notification.Show();
|
||||
notifications.Add(notificationGuid, notification);
|
||||
|
||||
//TODO: Implement winform notification
|
||||
var notification = new NotificationForm(notificationGuid, notificationArgs);
|
||||
|
||||
notification.FormClosed += NotifyWindow_Closed;
|
||||
|
||||
notifications.Add(notificationGuid, notification);
|
||||
notification.Show();
|
||||
});
|
||||
|
||||
return notificationGuid;
|
||||
}
|
||||
|
||||
private void NotifyWindow_Closed(object sender, EventArgs e)
|
||||
private void NotifyWindow_Closed(object? sender, EventArgs e)
|
||||
{
|
||||
var currentNotification = (NotificationForm)sender;
|
||||
|
||||
if (notifications.ContainsKey(currentNotification.Guid))
|
||||
if (sender != null)
|
||||
{
|
||||
notifications.Remove(currentNotification.Guid);
|
||||
var currentNotification = (NotificationForm)sender;
|
||||
|
||||
if (notifications.ContainsKey(currentNotification.Guid))
|
||||
{
|
||||
notifications.Remove(currentNotification.Guid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,8 +53,7 @@ namespace Observatory.NativeNotification
|
||||
{
|
||||
if (notifications.ContainsKey(guid))
|
||||
{
|
||||
//TODO: Update notification content
|
||||
// notifications[guid].DataContext = new NotificationViewModel(notificationArgs);
|
||||
notifications[guid].Update(notificationArgs);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Observatory.PluginManagement;
|
||||
using Observatory.Utils;
|
||||
using System.Reflection.PortableExecutable;
|
||||
|
||||
namespace Observatory
|
||||
@ -11,6 +12,8 @@ namespace Observatory
|
||||
[STAThread]
|
||||
static void Main(string[] args)
|
||||
{
|
||||
SettingsManager.Load();
|
||||
|
||||
if (args.Length > 0 && File.Exists(args[0]))
|
||||
{
|
||||
var fileInfo = new FileInfo(args[0]);
|
||||
@ -27,14 +30,14 @@ namespace Observatory
|
||||
{
|
||||
try
|
||||
{
|
||||
Properties.Core.Default.Upgrade();
|
||||
// Properties.Core.Default.Upgrade();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Silently ignore properties upgrade failure.
|
||||
}
|
||||
Properties.Core.Default.CoreVersion = version;
|
||||
Properties.Core.Default.Save();
|
||||
SettingsManager.Save();
|
||||
}
|
||||
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<RootNamespace>Observatory</RootNamespace>
|
||||
<Configurations>Debug;Release;Portable</Configurations>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
@ -59,9 +60,9 @@
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
|
||||
<Exec Condition=" '$(OS)' == 'Windows_NT'" Command="if not exist "$(ProjectDir)..\ObservatoryFramework\bin\Release\net5.0\ObservatoryFramework.dll" dotnet build "$(ProjectDir)..\ObservatoryFramework\ObservatoryFramework.csproj" -c Release" />
|
||||
<Exec Condition=" '$(OS)' == 'Windows_NT'" Command="if not exist "$(ProjectDir)..\ObservatoryFramework\bin\Release\net6.0\ObservatoryFramework.dll" dotnet build "$(ProjectDir)..\ObservatoryFramework\ObservatoryFramework.csproj" -c Release" />
|
||||
<Exec Condition=" '$(OS)' == 'Windows_NT'" Command="if not exist "$(OutDir)plugins\ObservatoryExplorer.dll" dotnet build "$(ProjectDir)..\ObservatoryExplorer\ObservatoryExplorer.csproj" -c $(ConfigurationName)" />
|
||||
<Exec Condition=" '$(OS)' != 'Windows_NT'" Command="[ ! -e "$(ProjectDir)../ObservatoryFramework/bin/Release/net5.0/ObservatoryFramework.dll" ] && dotnet build "$(ProjectDir)../ObservatoryFramework/ObservatoryFramework.csproj" -c Release || echo No build necessary" />
|
||||
<Exec Condition=" '$(OS)' != 'Windows_NT'" Command="[ ! -e "$(ProjectDir)../ObservatoryFramework/bin/Release/net6.0/ObservatoryFramework.dll" ] && dotnet build "$(ProjectDir)../ObservatoryFramework/ObservatoryFramework.csproj" -c Release || echo No build necessary" />
|
||||
<Exec Condition=" '$(OS)' != 'Windows_NT'" Command="[ ! -e "$(ProjectDir)$(OutDir)plugins/ObservatoryExplorer.dll" ] && dotnet build "$(ProjectDir)../ObservatoryExplorer/ObservatoryExplorer.csproj" -c $(ConfigurationName) || echo No build necessary" />
|
||||
</Target>
|
||||
|
||||
|
@ -105,17 +105,22 @@ namespace Observatory.PluginManagement
|
||||
|
||||
public void AddGridItems(IObservatoryWorker worker, IEnumerable<object> items)
|
||||
{
|
||||
//TODO: Add to winform list
|
||||
//TODO: Use better bulk handling here.
|
||||
foreach (var item in items)
|
||||
{
|
||||
worker.PluginUI.DataGrid.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearGrid(IObservatoryWorker worker, object templateItem)
|
||||
{
|
||||
//TODO: Clear winform list
|
||||
worker.PluginUI.DataGrid.Clear();
|
||||
}
|
||||
|
||||
public void ExecuteOnUIThread(Action action)
|
||||
{
|
||||
//TODO: Execute action
|
||||
if (Application.OpenForms.Count > 0)
|
||||
Application.OpenForms[0].Invoke(action);
|
||||
}
|
||||
|
||||
public System.Net.Http.HttpClient HttpClient
|
||||
@ -140,7 +145,7 @@ namespace Observatory.PluginManagement
|
||||
get
|
||||
{
|
||||
var context = new System.Diagnostics.StackFrame(1).GetMethod();
|
||||
|
||||
#if DEBUG || RELEASE
|
||||
string folderLocation = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)
|
||||
+ $"{Path.DirectorySeparatorChar}ObservatoryCore{Path.DirectorySeparatorChar}{context?.DeclaringType?.Assembly.GetName().Name}{Path.DirectorySeparatorChar}";
|
||||
|
||||
@ -148,6 +153,11 @@ namespace Observatory.PluginManagement
|
||||
Directory.CreateDirectory(folderLocation);
|
||||
|
||||
return folderLocation;
|
||||
#elif PORTABLE
|
||||
string? observatoryLocation = System.Diagnostics.Process.GetCurrentProcess()?.MainModule?.FileName;
|
||||
var obsDir = new FileInfo(observatoryLocation ?? String.Empty).DirectoryName;
|
||||
return $"{obsDir}{Path.DirectorySeparatorChar}plugins{Path.DirectorySeparatorChar}{context?.DeclaringType?.Assembly.GetName().Name}-Data{Path.DirectorySeparatorChar}";
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ namespace Observatory.PluginManagement
|
||||
}
|
||||
|
||||
|
||||
public readonly List<(string error, string detail)> errorList;
|
||||
public readonly List<(string error, string? detail)> errorList;
|
||||
public readonly List<Panel> pluginPanels;
|
||||
public readonly List<DataTable> pluginTables;
|
||||
public readonly List<(IObservatoryWorker plugin, PluginStatus signed)> workerPlugins;
|
||||
@ -181,21 +181,22 @@ namespace Observatory.PluginManagement
|
||||
});
|
||||
|
||||
Properties.Core.Default.PluginSettings = newSettings;
|
||||
Properties.Core.Default.Save();
|
||||
SettingsManager.Save();
|
||||
}
|
||||
|
||||
private static List<(string, string)> LoadPlugins(out List<(IObservatoryWorker plugin, PluginStatus signed)> observatoryWorkers, out List<(IObservatoryNotifier plugin, PluginStatus signed)> observatoryNotifiers)
|
||||
private static List<(string, string?)> LoadPlugins(out List<(IObservatoryWorker plugin, PluginStatus signed)> observatoryWorkers, out List<(IObservatoryNotifier plugin, PluginStatus signed)> observatoryNotifiers)
|
||||
{
|
||||
observatoryWorkers = new();
|
||||
observatoryNotifiers = new();
|
||||
var errorList = new List<(string, string)>();
|
||||
var errorList = new List<(string, string?)>();
|
||||
|
||||
string pluginPath = $"{AppDomain.CurrentDomain.BaseDirectory}{Path.DirectorySeparatorChar}plugins";
|
||||
|
||||
string ownExe = System.Reflection.Assembly.GetExecutingAssembly().Location;
|
||||
|
||||
string? ownExe = System.Diagnostics.Process.GetCurrentProcess()?.MainModule?.FileName;
|
||||
FileSignatureInfo ownSig;
|
||||
|
||||
using (var stream = File.OpenRead(ownExe))
|
||||
// This will throw if ownExe is null, but that's an error condition regardless.
|
||||
using (var stream = File.OpenRead(ownExe ?? String.Empty))
|
||||
ownSig = FileSignatureInfo.GetFromFileStream(stream);
|
||||
|
||||
|
||||
@ -259,7 +260,7 @@ namespace Observatory.PluginManagement
|
||||
if (response == DialogResult.OK)
|
||||
{
|
||||
Properties.Core.Default.UnsignedAllowed.Add(pluginHash);
|
||||
Properties.Core.Default.Save();
|
||||
SettingsManager.Save();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -322,16 +323,16 @@ namespace Observatory.PluginManagement
|
||||
|
||||
System.Runtime.Loader.AssemblyLoadContext.Default.Resolving += (context, name) => {
|
||||
|
||||
if (name.Name.EndsWith("resources"))
|
||||
if ((name?.Name?.EndsWith("resources")).GetValueOrDefault(false))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Importing Observatory.Framework in the Explorer Lua scripts causes an attempt to reload
|
||||
// the assembly, just hand it back the one we already have.
|
||||
if (name.Name.StartsWith("Observatory.Framework") || name.Name == "ObservatoryFramework")
|
||||
if ((name?.Name?.StartsWith("Observatory.Framework")).GetValueOrDefault(false) || name?.Name == "ObservatoryFramework")
|
||||
{
|
||||
return context.Assemblies.Where(a => a.FullName.Contains("ObservatoryFramework")).First();
|
||||
return context.Assemblies.Where(a => (a.FullName?.Contains("ObservatoryFramework")).GetValueOrDefault(false)).First();
|
||||
}
|
||||
|
||||
var foundDlls = Directory.GetFileSystemEntries(new FileInfo($"{AppDomain.CurrentDomain.BaseDirectory}{Path.DirectorySeparatorChar}plugins{Path.DirectorySeparatorChar}deps").FullName, name.Name + ".dll", SearchOption.TopDirectoryOnly);
|
||||
@ -340,7 +341,7 @@ namespace Observatory.PluginManagement
|
||||
return context.LoadFromAssemblyPath(foundDlls[0]);
|
||||
}
|
||||
|
||||
if (name.Name != recursionGuard)
|
||||
if (name.Name != recursionGuard && name.Name != null)
|
||||
{
|
||||
recursionGuard = name.Name;
|
||||
return context.LoadFromAssemblyName(name);
|
||||
@ -361,37 +362,43 @@ namespace Observatory.PluginManagement
|
||||
}
|
||||
catch (ReflectionTypeLoadException ex)
|
||||
{
|
||||
types = ex.Types.Where(t => t != null).ToArray();
|
||||
types = ex.Types.OfType<Type>().ToArray();
|
||||
}
|
||||
catch
|
||||
{
|
||||
types = Array.Empty<Type>();
|
||||
}
|
||||
|
||||
var workerTypes = types.Where(t => t.IsAssignableTo(typeof(IObservatoryWorker)));
|
||||
foreach (var worker in workerTypes)
|
||||
IEnumerable<Type> workerTypes = types.Where(t => t.IsAssignableTo(typeof(IObservatoryWorker)));
|
||||
foreach (Type worker in workerTypes)
|
||||
{
|
||||
ConstructorInfo constructor = worker.GetConstructor(Array.Empty<Type>());
|
||||
object instance = constructor.Invoke(Array.Empty<object>());
|
||||
workers.Add((instance as IObservatoryWorker, pluginStatus));
|
||||
if (instance is IObservatoryNotifier)
|
||||
ConstructorInfo? constructor = worker.GetConstructor(Array.Empty<Type>());
|
||||
if (constructor != null)
|
||||
{
|
||||
// This is also a notifier; add to the notifier list as well, so the work and notifier are
|
||||
// the same instance and can share state.
|
||||
notifiers.Add((instance as IObservatoryNotifier, pluginStatus));
|
||||
object instance = constructor.Invoke(Array.Empty<object>());
|
||||
workers.Add(((instance as IObservatoryWorker)!, pluginStatus));
|
||||
if (instance is IObservatoryNotifier)
|
||||
{
|
||||
// This is also a notifier; add to the notifier list as well, so the work and notifier are
|
||||
// the same instance and can share state.
|
||||
notifiers.Add(((instance as IObservatoryNotifier)!, pluginStatus));
|
||||
}
|
||||
pluginCount++;
|
||||
}
|
||||
pluginCount++;
|
||||
}
|
||||
|
||||
// Filter out items which are also workers as we've already created them above.
|
||||
var notifyTypes = types.Where(t =>
|
||||
t.IsAssignableTo(typeof(IObservatoryNotifier)) && !t.IsAssignableTo(typeof(IObservatoryWorker)));
|
||||
foreach (var notifier in notifyTypes)
|
||||
foreach (Type notifier in notifyTypes)
|
||||
{
|
||||
ConstructorInfo constructor = notifier.GetConstructor(Array.Empty<Type>());
|
||||
object instance = constructor.Invoke(Array.Empty<object>());
|
||||
notifiers.Add((instance as IObservatoryNotifier, PluginStatus.Signed));
|
||||
pluginCount++;
|
||||
ConstructorInfo? constructor = notifier.GetConstructor(Array.Empty<Type>());
|
||||
if (constructor != null)
|
||||
{
|
||||
object instance = constructor.Invoke(Array.Empty<object>());
|
||||
notifiers.Add(((instance as IObservatoryNotifier)!, PluginStatus.Signed));
|
||||
pluginCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (pluginCount == 0)
|
||||
|
2
ObservatoryCore/UI/CoreForm.Designer.cs
generated
2
ObservatoryCore/UI/CoreForm.Designer.cs
generated
@ -360,6 +360,7 @@
|
||||
this.TestButton.TabIndex = 12;
|
||||
this.TestButton.Text = "Test";
|
||||
this.TestButton.UseVisualStyleBackColor = false;
|
||||
this.TestButton.Click += new System.EventHandler(this.TestButton_Click);
|
||||
//
|
||||
// ColourButton
|
||||
//
|
||||
@ -569,6 +570,7 @@
|
||||
this.ToggleMonitorButton.TabIndex = 3;
|
||||
this.ToggleMonitorButton.Text = "Start Monitor";
|
||||
this.ToggleMonitorButton.UseVisualStyleBackColor = false;
|
||||
this.ToggleMonitorButton.Click += new System.EventHandler(this.ToggleMonitorButton_Click);
|
||||
//
|
||||
// ClearButton
|
||||
//
|
||||
|
109
ObservatoryCore/UI/CoreForm.Plugins.cs
Normal file
109
ObservatoryCore/UI/CoreForm.Plugins.cs
Normal file
@ -0,0 +1,109 @@
|
||||
using Observatory.PluginManagement;
|
||||
using Observatory.Framework.Interfaces;
|
||||
|
||||
namespace Observatory.UI
|
||||
{
|
||||
partial class CoreForm
|
||||
{
|
||||
|
||||
private void PopulatePluginList()
|
||||
{
|
||||
List<IObservatoryPlugin> uniquePlugins = new();
|
||||
|
||||
foreach (var (plugin, signed) in PluginManager.GetInstance.workerPlugins)
|
||||
{
|
||||
if (!uniquePlugins.Contains(plugin))
|
||||
{
|
||||
uniquePlugins.Add(plugin);
|
||||
ListViewItem item = new ListViewItem(new[] { plugin.Name, "Worker", plugin.Version, PluginStatusString(signed) });
|
||||
PluginList.Items.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var (plugin, signed) in PluginManager.GetInstance.notifyPlugins)
|
||||
{
|
||||
if (!uniquePlugins.Contains(plugin))
|
||||
{
|
||||
uniquePlugins.Add(plugin);
|
||||
ListViewItem item = new ListViewItem(new[] { plugin.Name, "Notifier", plugin.Version, PluginStatusString(signed) });
|
||||
PluginList.Items.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string PluginStatusString(PluginManager.PluginStatus status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case PluginManager.PluginStatus.Signed:
|
||||
return "Signed";
|
||||
|
||||
case PluginManager.PluginStatus.Unsigned:
|
||||
return "Unsigned";
|
||||
|
||||
case PluginManager.PluginStatus.InvalidSignature:
|
||||
return "Invalid Signature";
|
||||
|
||||
case PluginManager.PluginStatus.InvalidPlugin:
|
||||
return "Invalid Plugin";
|
||||
|
||||
case PluginManager.PluginStatus.InvalidLibrary:
|
||||
return "Invalid File";
|
||||
|
||||
case PluginManager.PluginStatus.NoCert:
|
||||
return "Unsigned Observatory (Debug build)";
|
||||
|
||||
case PluginManager.PluginStatus.SigCheckDisabled:
|
||||
return "Signature Checks Disabled";
|
||||
|
||||
default:
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private void CreatePluginTabs()
|
||||
{
|
||||
var uiPlugins = PluginManager.GetInstance.workerPlugins.Where(p => p.plugin.PluginUI.PluginUIType != Framework.PluginUI.UIType.None);
|
||||
|
||||
PluginHelper.CreatePluginTabs(CoreMenu, uiPlugins, uiPanels);
|
||||
|
||||
foreach(ToolStripMenuItem item in CoreMenu.Items)
|
||||
{
|
||||
pluginList.Add(item.Text, item);
|
||||
}
|
||||
}
|
||||
|
||||
private void CreatePluginSettings()
|
||||
{
|
||||
foreach (var plugin in PluginManager.GetInstance.workerPlugins)
|
||||
{
|
||||
var pluginSettingsPanel = new SettingsPanel(plugin.plugin, AdjustPanelsBelow);
|
||||
AddSettingsPanel(pluginSettingsPanel);
|
||||
}
|
||||
foreach (var plugin in PluginManager.GetInstance.notifyPlugins)
|
||||
{
|
||||
var pluginSettingsPanel = new SettingsPanel(plugin.plugin, AdjustPanelsBelow);
|
||||
AddSettingsPanel(pluginSettingsPanel);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddSettingsPanel(SettingsPanel panel)
|
||||
{
|
||||
int lowestPoint = 0;
|
||||
foreach (Control control in CorePanel.Controls)
|
||||
{
|
||||
if (control.Location.Y + control.Height > lowestPoint)
|
||||
lowestPoint = control.Location.Y + control.Height;
|
||||
}
|
||||
DuplicateControlVisuals(PopupNotificationLabel, panel.Header);
|
||||
panel.Header.TextAlign = PopupNotificationLabel.TextAlign;
|
||||
panel.Header.Location = new Point(PopupNotificationLabel.Location.X, lowestPoint);
|
||||
|
||||
DuplicateControlVisuals(PopupSettingsPanel, panel, false);
|
||||
panel.Location = new Point(PopupSettingsPanel.Location.X, lowestPoint + panel.Header.Height);
|
||||
panel.Visible = false;
|
||||
CorePanel.Controls.Add(panel.Header);
|
||||
CorePanel.Controls.Add(panel);
|
||||
}
|
||||
}
|
||||
}
|
111
ObservatoryCore/UI/CoreForm.Settings.cs
Normal file
111
ObservatoryCore/UI/CoreForm.Settings.cs
Normal file
@ -0,0 +1,111 @@
|
||||
using Observatory.Utils;
|
||||
|
||||
namespace Observatory.UI
|
||||
{
|
||||
partial class CoreForm
|
||||
{
|
||||
private void ColourButton_Click(object _, EventArgs e)
|
||||
{
|
||||
var selectionResult = PopupColour.ShowDialog();
|
||||
if (selectionResult == DialogResult.OK)
|
||||
{
|
||||
ColourButton.BackColor = PopupColour.Color;
|
||||
Properties.Core.Default.NativeNotifyColour = (uint)PopupColour.Color.ToArgb();
|
||||
SettingsManager.Save();
|
||||
}
|
||||
}
|
||||
|
||||
private void PopupCheckbox_CheckedChanged(object _, EventArgs e)
|
||||
{
|
||||
Properties.Core.Default.NativeNotify = PopupCheckbox.Checked;
|
||||
SettingsManager.Save();
|
||||
}
|
||||
|
||||
private void DurationSpinner_ValueChanged(object _, EventArgs e)
|
||||
{
|
||||
Properties.Core.Default.NativeNotifyTimeout = (int)DurationSpinner.Value;
|
||||
SettingsManager.Save();
|
||||
}
|
||||
|
||||
private void ScaleSpinner_ValueChanged(object _, EventArgs e)
|
||||
{
|
||||
Properties.Core.Default.NativeNotifyScale = (int)ScaleSpinner.Value;
|
||||
SettingsManager.Save();
|
||||
}
|
||||
|
||||
private void FontDropdown_SelectedIndexChanged(object _, EventArgs e)
|
||||
{
|
||||
Properties.Core.Default.NativeNotifyFont = FontDropdown.SelectedItem.ToString();
|
||||
SettingsManager.Save();
|
||||
}
|
||||
|
||||
private void CornerDropdown_SelectedIndexChanged(object _, EventArgs e)
|
||||
{
|
||||
Properties.Core.Default.NativeNotifyCorner = CornerDropdown.SelectedIndex;
|
||||
SettingsManager.Save();
|
||||
}
|
||||
|
||||
private void DisplayDropdown_SelectedIndexChanged(object _, EventArgs e)
|
||||
{
|
||||
Properties.Core.Default.NativeNotifyScreen = DisplayDropdown.SelectedIndex - 1;
|
||||
SettingsManager.Save();
|
||||
}
|
||||
|
||||
private void VoiceVolumeSlider_Scroll(object _, EventArgs e)
|
||||
{
|
||||
Properties.Core.Default.VoiceVolume = VoiceVolumeSlider.Value;
|
||||
SettingsManager.Save();
|
||||
}
|
||||
|
||||
private void VoiceSpeedSlider_Scroll(object _, EventArgs e)
|
||||
{
|
||||
Properties.Core.Default.VoiceRate = VoiceSpeedSlider.Value;
|
||||
SettingsManager.Save();
|
||||
}
|
||||
|
||||
private void VoiceCheckbox_CheckedChanged(object _, EventArgs e)
|
||||
{
|
||||
Properties.Core.Default.VoiceNotify = VoiceCheckbox.Checked;
|
||||
SettingsManager.Save();
|
||||
}
|
||||
|
||||
private void VoiceDropdown_SelectedIndexChanged(object _, EventArgs e)
|
||||
{
|
||||
Properties.Core.Default.VoiceSelected = VoiceDropdown.SelectedItem.ToString();
|
||||
SettingsManager.Save();
|
||||
}
|
||||
|
||||
private void PopulateDropdownOptions()
|
||||
{
|
||||
var fonts = new System.Drawing.Text.InstalledFontCollection().Families;
|
||||
FontDropdown.Items.AddRange(fonts.Select(f => f.Name).ToArray());
|
||||
|
||||
DisplayDropdown.Items.Add("Primary");
|
||||
if (Screen.AllScreens.Length > 1)
|
||||
for (int i = 0; i < Screen.AllScreens.Length; i++)
|
||||
DisplayDropdown.Items.Add((i + 1).ToString());
|
||||
|
||||
var voices = new System.Speech.Synthesis.SpeechSynthesizer().GetInstalledVoices();
|
||||
foreach (var voice in voices.Select(v => v.VoiceInfo.Name))
|
||||
VoiceDropdown.Items.Add(voice);
|
||||
|
||||
}
|
||||
|
||||
private void PopulateNativeSettings()
|
||||
{
|
||||
var settings = Properties.Core.Default;
|
||||
|
||||
DisplayDropdown.SelectedIndex = settings.NativeNotifyScreen + 1;
|
||||
CornerDropdown.SelectedIndex = settings.NativeNotifyCorner;
|
||||
FontDropdown.SelectedItem = settings.NativeNotifyFont;
|
||||
ScaleSpinner.Value = settings.NativeNotifyScale;
|
||||
DurationSpinner.Value = settings.NativeNotifyTimeout;
|
||||
ColourButton.BackColor = Color.FromArgb((int)settings.NativeNotifyColour);
|
||||
PopupCheckbox.Checked = settings.NativeNotify;
|
||||
VoiceVolumeSlider.Value = settings.VoiceVolume;
|
||||
VoiceSpeedSlider.Value = settings.VoiceRate;
|
||||
VoiceDropdown.SelectedItem = settings.VoiceSelected;
|
||||
VoiceCheckbox.Checked = settings.VoiceNotify;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,9 @@
|
||||
using Observatory.Framework.Interfaces;
|
||||
using Observatory.Framework;
|
||||
using Observatory.Framework.Interfaces;
|
||||
using Observatory.PluginManagement;
|
||||
using Observatory.Utils;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Observatory.UI
|
||||
{
|
||||
@ -37,40 +40,6 @@ namespace Observatory.UI
|
||||
AdjustPanelsBelow(PopupSettingsPanel, AdjustmentDirection.Up);
|
||||
}
|
||||
|
||||
private void PopulateDropdownOptions()
|
||||
{
|
||||
var fonts = new System.Drawing.Text.InstalledFontCollection().Families;
|
||||
FontDropdown.Items.AddRange(fonts.Select(f => f.Name).ToArray());
|
||||
|
||||
DisplayDropdown.Items.Add("Primary");
|
||||
if (Screen.AllScreens.Length > 1)
|
||||
for (int i = 0; i < Screen.AllScreens.Length; i++)
|
||||
DisplayDropdown.Items.Add((i + 1).ToString());
|
||||
|
||||
var voices = new System.Speech.Synthesis.SpeechSynthesizer().GetInstalledVoices();
|
||||
foreach (var voice in voices.Select(v => v.VoiceInfo.Name))
|
||||
VoiceDropdown.Items.Add(voice);
|
||||
|
||||
}
|
||||
|
||||
private void PopulateNativeSettings()
|
||||
{
|
||||
var settings = Properties.Core.Default;
|
||||
|
||||
DisplayDropdown.SelectedIndex = settings.NativeNotifyScreen + 1;
|
||||
CornerDropdown.SelectedIndex = settings.NativeNotifyCorner;
|
||||
FontDropdown.SelectedItem = settings.NativeNotifyFont;
|
||||
ScaleSpinner.Value = settings.NativeNotifyScale;
|
||||
DurationSpinner.Value = settings.NativeNotifyTimeout;
|
||||
ColourButton.BackColor = Color.FromArgb((int)settings.NativeNotifyColour);
|
||||
PopupCheckbox.Checked = settings.NativeNotify;
|
||||
VoiceVolumeSlider.Value = settings.VoiceVolume;
|
||||
VoiceSpeedSlider.Value = settings.VoiceRate;
|
||||
VoiceDropdown.SelectedItem = settings.VoiceSelected;
|
||||
VoiceCheckbox.Checked = settings.VoiceNotify;
|
||||
}
|
||||
|
||||
|
||||
private void CoreMenu_SizeChanged(object? sender, EventArgs e)
|
||||
{
|
||||
CorePanel.Location = new Point(12 + CoreMenu.Width, 12);
|
||||
@ -80,95 +49,27 @@ namespace Observatory.UI
|
||||
|
||||
private Dictionary<string, ToolStripMenuItem> pluginList;
|
||||
|
||||
private void CreatePluginTabs()
|
||||
private static void DuplicateControlVisuals(Control source, Control target, bool applyHeight = true)
|
||||
{
|
||||
var uiPlugins = PluginManager.GetInstance.workerPlugins.Where(p => p.plugin.PluginUI.PluginUIType != Framework.PluginUI.UIType.None);
|
||||
|
||||
PluginHelper.CreatePluginTabs(CoreMenu, uiPlugins, uiPanels);
|
||||
|
||||
foreach(ToolStripMenuItem item in CoreMenu.Items)
|
||||
{
|
||||
pluginList.Add(item.Text, item);
|
||||
}
|
||||
if (applyHeight) target.Height = source.Height;
|
||||
target.Width = source.Width;
|
||||
target.Font = source.Font;
|
||||
target.ForeColor = source.ForeColor;
|
||||
target.BackColor = source.BackColor;
|
||||
target.Anchor = source.Anchor;
|
||||
}
|
||||
|
||||
private void CreatePluginSettings()
|
||||
private void ToggleMonitorButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
foreach (var plugin in PluginManager.GetInstance.workerPlugins)
|
||||
if ((LogMonitor.GetInstance.CurrentState & Framework.LogMonitorState.Realtime) == Framework.LogMonitorState.Realtime)
|
||||
{
|
||||
var pluginSettingsPanel = new SettingsPanel(plugin.plugin, AdjustPanelsBelow);
|
||||
AddSettingsPanel(pluginSettingsPanel);
|
||||
LogMonitor.GetInstance.Stop();
|
||||
ToggleMonitorButton.Text = "Start Monitor";
|
||||
}
|
||||
foreach (var plugin in PluginManager.GetInstance.notifyPlugins)
|
||||
else
|
||||
{
|
||||
var pluginSettingsPanel = new SettingsPanel(plugin.plugin, AdjustPanelsBelow);
|
||||
AddSettingsPanel(pluginSettingsPanel);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddSettingsPanel(SettingsPanel panel)
|
||||
{
|
||||
int lowestPoint = 0;
|
||||
foreach (Control control in CorePanel.Controls)
|
||||
{
|
||||
if (control.Location.Y + control.Height > lowestPoint)
|
||||
lowestPoint = control.Location.Y + control.Height;
|
||||
}
|
||||
panel.Header.Location = new Point(PopupNotificationLabel.Location.X, lowestPoint);
|
||||
panel.Header.Width = PopupNotificationLabel.Width;
|
||||
panel.Header.Font = PopupNotificationLabel.Font;
|
||||
panel.Header.ForeColor = PopupNotificationLabel.ForeColor;
|
||||
panel.Header.BackColor = PopupNotificationLabel.BackColor;
|
||||
panel.Header.TextAlign = PopupNotificationLabel.TextAlign;
|
||||
panel.Location = new Point(PopupNotificationLabel.Location.X, lowestPoint + panel.Header.Height);
|
||||
panel.Width = PopupSettingsPanel.Width;
|
||||
CorePanel.Controls.Add(panel.Header);
|
||||
CorePanel.Controls.Add(panel);
|
||||
}
|
||||
|
||||
private void PopulatePluginList()
|
||||
{
|
||||
List<IObservatoryPlugin> uniquePlugins = new();
|
||||
|
||||
|
||||
foreach (var (plugin, signed) in PluginManager.GetInstance.workerPlugins)
|
||||
{
|
||||
if (!uniquePlugins.Contains(plugin))
|
||||
{
|
||||
uniquePlugins.Add(plugin);
|
||||
ListViewItem item = new ListViewItem(new[] { plugin.Name, "Worker", plugin.Version, PluginStatusString(signed) });
|
||||
PluginList.Items.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string PluginStatusString(PluginManager.PluginStatus status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case PluginManager.PluginStatus.Signed:
|
||||
return "Signed";
|
||||
|
||||
case PluginManager.PluginStatus.Unsigned:
|
||||
return "Unsigned";
|
||||
|
||||
case PluginManager.PluginStatus.InvalidSignature:
|
||||
return "Invalid Signature";
|
||||
|
||||
case PluginManager.PluginStatus.InvalidPlugin:
|
||||
return "Invalid Plugin";
|
||||
|
||||
case PluginManager.PluginStatus.InvalidLibrary:
|
||||
return "Invalid File";
|
||||
|
||||
case PluginManager.PluginStatus.NoCert:
|
||||
return "Unsigned Observatory (Debug build)";
|
||||
|
||||
case PluginManager.PluginStatus.SigCheckDisabled:
|
||||
return "Signature Checks Disabled";
|
||||
|
||||
default:
|
||||
return string.Empty;
|
||||
LogMonitor.GetInstance.Start();
|
||||
ToggleMonitorButton.Text = "Stop Monitor";
|
||||
}
|
||||
}
|
||||
|
||||
@ -337,82 +238,16 @@ namespace Observatory.UI
|
||||
Up, Down
|
||||
}
|
||||
|
||||
#region Settings Changes
|
||||
|
||||
private void ColourButton_Click(object _, EventArgs e)
|
||||
private void TestButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
var selectionResult = PopupColour.ShowDialog();
|
||||
if (selectionResult == DialogResult.OK)
|
||||
NotificationArgs args = new()
|
||||
{
|
||||
ColourButton.BackColor = PopupColour.Color;
|
||||
Properties.Core.Default.NativeNotifyColour = (uint)PopupColour.Color.ToArgb();
|
||||
Properties.Core.Default.Save();
|
||||
}
|
||||
Title = "Test Notification",
|
||||
Detail = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec at elit maximus, ornare dui nec, accumsan velit. Vestibulum fringilla elit."
|
||||
};
|
||||
var testNotify = new NotificationForm(new Guid(), args);
|
||||
testNotify.Show();
|
||||
|
||||
}
|
||||
|
||||
private void PopupCheckbox_CheckedChanged(object _, EventArgs e)
|
||||
{
|
||||
Properties.Core.Default.NativeNotify = PopupCheckbox.Checked;
|
||||
Properties.Core.Default.Save();
|
||||
}
|
||||
|
||||
private void DurationSpinner_ValueChanged(object _, EventArgs e)
|
||||
{
|
||||
Properties.Core.Default.NativeNotifyTimeout = (int)DurationSpinner.Value;
|
||||
Properties.Core.Default.Save();
|
||||
}
|
||||
|
||||
private void ScaleSpinner_ValueChanged(object _, EventArgs e)
|
||||
{
|
||||
Properties.Core.Default.NativeNotifyScale = (int)ScaleSpinner.Value;
|
||||
Properties.Core.Default.Save();
|
||||
}
|
||||
|
||||
private void FontDropdown_SelectedIndexChanged(object _, EventArgs e)
|
||||
{
|
||||
Properties.Core.Default.NativeNotifyFont = FontDropdown.SelectedItem.ToString();
|
||||
Properties.Core.Default.Save();
|
||||
}
|
||||
|
||||
private void CornerDropdown_SelectedIndexChanged(object _, EventArgs e)
|
||||
{
|
||||
Properties.Core.Default.NativeNotifyCorner = CornerDropdown.SelectedIndex;
|
||||
Properties.Core.Default.Save();
|
||||
}
|
||||
|
||||
private void DisplayDropdown_SelectedIndexChanged(object _, EventArgs e)
|
||||
{
|
||||
Properties.Core.Default.NativeNotifyScreen = DisplayDropdown.SelectedIndex - 1;
|
||||
Properties.Core.Default.Save();
|
||||
}
|
||||
|
||||
private void VoiceVolumeSlider_Scroll(object _, EventArgs e)
|
||||
{
|
||||
Properties.Core.Default.VoiceVolume = VoiceVolumeSlider.Value;
|
||||
Properties.Core.Default.Save();
|
||||
}
|
||||
|
||||
private void VoiceSpeedSlider_Scroll(object _, EventArgs e)
|
||||
{
|
||||
Properties.Core.Default.VoiceRate = VoiceSpeedSlider.Value;
|
||||
Properties.Core.Default.Save();
|
||||
}
|
||||
|
||||
private void VoiceCheckbox_CheckedChanged(object _, EventArgs e)
|
||||
{
|
||||
Properties.Core.Default.VoiceNotify = VoiceCheckbox.Checked;
|
||||
Properties.Core.Default.Save();
|
||||
}
|
||||
|
||||
private void VoiceDropdown_SelectedIndexChanged(object _, EventArgs e)
|
||||
{
|
||||
Properties.Core.Default.VoiceSelected = VoiceDropdown.SelectedItem.ToString();
|
||||
Properties.Core.Default.Save();
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -3,10 +3,11 @@ using System.Collections;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Observatory.Framework.Interfaces;
|
||||
|
||||
namespace Observatory.UI
|
||||
{
|
||||
internal class DefaultSorter : IComparer
|
||||
internal class DefaultSorter : IObservatoryComparer
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the column to be sorted
|
||||
@ -15,7 +16,7 @@ namespace Observatory.UI
|
||||
/// <summary>
|
||||
/// Specifies the order in which to sort (i.e. 'Ascending').
|
||||
/// </summary>
|
||||
private SortOrder OrderOfSort;
|
||||
private int OrderOfSort;
|
||||
/// <summary>
|
||||
/// Case insensitive comparer object
|
||||
/// </summary>
|
||||
@ -30,7 +31,7 @@ namespace Observatory.UI
|
||||
ColumnToSort = 0;
|
||||
|
||||
// Initialize the sort order to 'none'
|
||||
OrderOfSort = SortOrder.None;
|
||||
OrderOfSort = 0;
|
||||
|
||||
// Initialize the CaseInsensitiveComparer object
|
||||
ObjectCompare = new CaseInsensitiveComparer();
|
||||
@ -49,25 +50,68 @@ namespace Observatory.UI
|
||||
ListViewItem? listviewX = (ListViewItem?)x;
|
||||
ListViewItem? listviewY = (ListViewItem?)y;
|
||||
|
||||
if (OrderOfSort == 0)
|
||||
return 0;
|
||||
|
||||
// Compare the two items
|
||||
compareResult = ObjectCompare.Compare(listviewX?.SubItems[ColumnToSort].Text, listviewY?.SubItems[ColumnToSort].Text);
|
||||
compareResult = NaturalCompare(listviewX?.SubItems[ColumnToSort].Text, listviewY?.SubItems[ColumnToSort].Text);
|
||||
|
||||
// Calculate correct return value based on object comparison
|
||||
if (OrderOfSort == SortOrder.Ascending)
|
||||
if (OrderOfSort == 1)
|
||||
{
|
||||
// Ascending sort is selected, return normal result of compare operation
|
||||
return compareResult;
|
||||
}
|
||||
else if (OrderOfSort == SortOrder.Descending)
|
||||
else
|
||||
{
|
||||
// Descending sort is selected, return negative result of compare operation
|
||||
return (-compareResult);
|
||||
}
|
||||
else
|
||||
}
|
||||
|
||||
private static int NaturalCompare(string? x, string? y)
|
||||
{
|
||||
for (int i = 0; i <= x?.Length && i <= y?.Length; i++)
|
||||
{
|
||||
// Return '0' to indicate they are equal
|
||||
return 0;
|
||||
// If we've reached the end of the string without finding a difference
|
||||
// the longer string is "greater".
|
||||
if (i == x.Length || i == y.Length)
|
||||
return x.Length > y.Length ? 1 : y.Length > x.Length ? -1 : 0;
|
||||
|
||||
// We've found a number in the same place in both strings.
|
||||
if (Char.IsDigit(x[i]) && Char.IsDigit(y[i]))
|
||||
{
|
||||
// Walk ahead and get the full numbers.
|
||||
string xNum = new(x[i..].TakeWhile(c => Char.IsDigit(c)).ToArray());
|
||||
string yNum = new(y[i..].TakeWhile(c => Char.IsDigit(c)).ToArray());
|
||||
|
||||
// Pad with zeroes to equal lengths.
|
||||
int numLength = Math.Max(xNum.Length, yNum.Length);
|
||||
string xNumPadded = xNum.PadLeft(numLength, '0');
|
||||
string yNumPadded = yNum.PadLeft(numLength, '0');
|
||||
|
||||
// Now that they're the same length a direct compare works.
|
||||
int result = xNumPadded.CompareTo(yNumPadded);
|
||||
if (result != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The numbers are identical, skip them and keep moving.
|
||||
i += numLength - 1;
|
||||
}
|
||||
}
|
||||
// Check if we have unequal letters.
|
||||
else if (x[i] != y[i])
|
||||
{
|
||||
// Straight compare and return.
|
||||
return x[i] > y[i] ? 1 : -1;
|
||||
}
|
||||
}
|
||||
|
||||
// If we somehow make it here, return equal result.
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -88,7 +132,7 @@ namespace Observatory.UI
|
||||
/// <summary>
|
||||
/// Gets or sets the order of sorting to apply (for example, 'Ascending' or 'Descending').
|
||||
/// </summary>
|
||||
public SortOrder Order
|
||||
public int Order
|
||||
{
|
||||
set
|
||||
{
|
||||
|
310
ObservatoryCore/UI/DwmHelper.cs
Normal file
310
ObservatoryCore/UI/DwmHelper.cs
Normal file
@ -0,0 +1,310 @@
|
||||
// Source: https://stackoverflow.com/questions/51578104/how-to-create-a-semi-transparent-or-blurred-backcolor-in-a-windows-form
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
|
||||
[SuppressUnmanagedCodeSecurity]
|
||||
public class DwmHelper
|
||||
{
|
||||
public const int WM_DWMCOMPOSITIONCHANGED = 0x031E;
|
||||
|
||||
public struct MARGINS
|
||||
{
|
||||
public int leftWidth;
|
||||
public int rightWidth;
|
||||
public int topHeight;
|
||||
public int bottomHeight;
|
||||
|
||||
public MARGINS(int LeftWidth, int RightWidth, int TopHeight, int BottomHeight)
|
||||
{
|
||||
leftWidth = LeftWidth;
|
||||
rightWidth = RightWidth;
|
||||
topHeight = TopHeight;
|
||||
bottomHeight = BottomHeight;
|
||||
}
|
||||
|
||||
public void NoMargins()
|
||||
{
|
||||
leftWidth = 0;
|
||||
rightWidth = 0;
|
||||
topHeight = 0;
|
||||
bottomHeight = 0;
|
||||
}
|
||||
|
||||
public void SheetOfGlass()
|
||||
{
|
||||
leftWidth = -1;
|
||||
rightWidth = -1;
|
||||
topHeight = -1;
|
||||
bottomHeight = -1;
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum DWM_BB
|
||||
{
|
||||
Enable = 1,
|
||||
BlurRegion = 2,
|
||||
TransitionOnMaximized = 4
|
||||
}
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
|
||||
public enum DWMWINDOWATTRIBUTE : uint
|
||||
{
|
||||
NCRenderingEnabled = 1, //Get atttribute
|
||||
NCRenderingPolicy, //Enable or disable non-client rendering
|
||||
TransitionsForceDisabled,
|
||||
AllowNCPaint,
|
||||
CaptionButtonBounds, //Get atttribute
|
||||
NonClientRtlLayout,
|
||||
ForceIconicRepresentation,
|
||||
Flip3DPolicy,
|
||||
ExtendedFrameBounds, //Get atttribute
|
||||
HasIconicBitmap,
|
||||
DisallowPeek,
|
||||
ExcludedFromPeek,
|
||||
Cloak,
|
||||
Cloaked, //Get atttribute. Returns a DWMCLOACKEDREASON
|
||||
FreezeRepresentation,
|
||||
PassiveUpdateMode,
|
||||
UseHostBackDropBrush,
|
||||
AccentPolicy = 19, // Win 10 (undocumented)
|
||||
ImmersiveDarkMode = 20, // Win 11 22000
|
||||
WindowCornerPreference = 33, // Win 11 22000
|
||||
BorderColor, // Win 11 22000
|
||||
CaptionColor, // Win 11 22000
|
||||
TextColor, // Win 11 22000
|
||||
VisibleFrameBorderThickness, // Win 11 22000
|
||||
SystemBackdropType // Win 11 22621
|
||||
}
|
||||
|
||||
public enum DWMCLOACKEDREASON : uint
|
||||
{
|
||||
DWM_CLOAKED_APP = 0x0000001, //cloaked by its owner application.
|
||||
DWM_CLOAKED_SHELL = 0x0000002, //cloaked by the Shell.
|
||||
DWM_CLOAKED_INHERITED = 0x0000004 //inherited from its owner window.
|
||||
}
|
||||
|
||||
public enum DWMNCRENDERINGPOLICY : uint
|
||||
{
|
||||
UseWindowStyle, // Enable/disable non-client rendering based on window style
|
||||
Disabled, // Disabled non-client rendering; window style is ignored
|
||||
Enabled, // Enabled non-client rendering; window style is ignored
|
||||
};
|
||||
|
||||
public enum DWMACCENTSTATE
|
||||
{
|
||||
ACCENT_DISABLED = 0,
|
||||
ACCENT_ENABLE_GRADIENT = 1,
|
||||
ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,
|
||||
ACCENT_ENABLE_BLURBEHIND = 3,
|
||||
ACCENT_INVALID_STATE = 4
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum CompositionAction : uint
|
||||
{
|
||||
DWM_EC_DISABLECOMPOSITION = 0,
|
||||
DWM_EC_ENABLECOMPOSITION = 1
|
||||
}
|
||||
|
||||
// Values designating how Flip3D treats a given window.
|
||||
enum DWMFLIP3DWINDOWPOLICY : uint
|
||||
{
|
||||
Default, // Hide or include the window in Flip3D based on window style and visibility.
|
||||
ExcludeBelow, // Display the window under Flip3D and disabled.
|
||||
ExcludeAbove, // Display the window above Flip3D and enabled.
|
||||
};
|
||||
|
||||
public enum ThumbProperties_dwFlags : uint
|
||||
{
|
||||
RectDestination = 0x00000001,
|
||||
RectSource = 0x00000002,
|
||||
Opacity = 0x00000004,
|
||||
Visible = 0x00000008,
|
||||
SourceClientAreaOnly = 0x00000010
|
||||
}
|
||||
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct AccentPolicy
|
||||
{
|
||||
public DWMACCENTSTATE AccentState;
|
||||
public int AccentFlags;
|
||||
public int GradientColor;
|
||||
public int AnimationId;
|
||||
|
||||
public AccentPolicy(DWMACCENTSTATE accentState, int accentFlags, int gradientColor, int animationId)
|
||||
{
|
||||
AccentState = accentState;
|
||||
AccentFlags = accentFlags;
|
||||
GradientColor = gradientColor;
|
||||
AnimationId = animationId;
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct DWM_BLURBEHIND
|
||||
{
|
||||
public DWM_BB dwFlags;
|
||||
public int fEnable;
|
||||
public IntPtr hRgnBlur;
|
||||
public int fTransitionOnMaximized;
|
||||
|
||||
public DWM_BLURBEHIND(bool enabled)
|
||||
{
|
||||
dwFlags = DWM_BB.Enable;
|
||||
fEnable = (enabled) ? 1 : 0;
|
||||
hRgnBlur = IntPtr.Zero;
|
||||
fTransitionOnMaximized = 0;
|
||||
}
|
||||
|
||||
public Region Region => Region.FromHrgn(hRgnBlur);
|
||||
|
||||
public bool TransitionOnMaximized
|
||||
{
|
||||
get => fTransitionOnMaximized > 0;
|
||||
set
|
||||
{
|
||||
fTransitionOnMaximized = (value) ? 1 : 0;
|
||||
dwFlags |= DWM_BB.TransitionOnMaximized;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetRegion(Graphics graphics, Region region)
|
||||
{
|
||||
hRgnBlur = region.GetHrgn(graphics);
|
||||
dwFlags |= DWM_BB.BlurRegion;
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct WinCompositionAttrData
|
||||
{
|
||||
public DWMWINDOWATTRIBUTE Attribute;
|
||||
public IntPtr Data; //Will point to an AccentPolicy struct, where Attribute will be DWMWINDOWATTRIBUTE.AccentPolicy
|
||||
public int SizeOfData;
|
||||
|
||||
public WinCompositionAttrData(DWMWINDOWATTRIBUTE attribute, IntPtr data, int sizeOfData)
|
||||
{
|
||||
Attribute = attribute;
|
||||
Data = data;
|
||||
SizeOfData = sizeOfData;
|
||||
}
|
||||
}
|
||||
|
||||
private static int GetBlurBehindPolicyAccentFlags()
|
||||
{
|
||||
int drawLeftBorder = 20;
|
||||
int drawTopBorder = 40;
|
||||
int drawRightBorder = 80;
|
||||
int drawBottomBorder = 100;
|
||||
return (drawLeftBorder | drawTopBorder | drawRightBorder | drawBottomBorder);
|
||||
}
|
||||
|
||||
//https://msdn.microsoft.com/en-us/library/windows/desktop/aa969508(v=vs.85).aspx
|
||||
[DllImport("dwmapi.dll")]
|
||||
internal static extern int DwmEnableBlurBehindWindow(IntPtr hwnd, ref DWM_BLURBEHIND blurBehind);
|
||||
|
||||
[DllImport("dwmapi.dll", PreserveSig = false)]
|
||||
public static extern void DwmEnableComposition(CompositionAction uCompositionAction);
|
||||
|
||||
//https://msdn.microsoft.com/it-it/library/windows/desktop/aa969512(v=vs.85).aspx
|
||||
[DllImport("dwmapi.dll")]
|
||||
internal static extern int DwmExtendFrameIntoClientArea(IntPtr hWnd, ref MARGINS pMarInset);
|
||||
|
||||
//https://msdn.microsoft.com/en-us/library/windows/desktop/aa969515(v=vs.85).aspx
|
||||
[DllImport("dwmapi.dll")]
|
||||
internal static extern int DwmGetWindowAttribute(IntPtr hwnd, DWMWINDOWATTRIBUTE attr, ref int attrValue, int attrSize);
|
||||
|
||||
//https://msdn.microsoft.com/en-us/library/windows/desktop/aa969524(v=vs.85).aspx
|
||||
[DllImport("dwmapi.dll")]
|
||||
internal static extern int DwmSetWindowAttribute(IntPtr hwnd, DWMWINDOWATTRIBUTE attr, ref int attrValue, int attrSize);
|
||||
|
||||
[DllImport("User32.dll", SetLastError = true)]
|
||||
internal static extern int SetWindowCompositionAttribute(IntPtr hwnd, ref WinCompositionAttrData data);
|
||||
|
||||
[DllImport("dwmapi.dll")]
|
||||
internal static extern int DwmIsCompositionEnabled(ref int pfEnabled);
|
||||
|
||||
public static bool IsCompositionEnabled()
|
||||
{
|
||||
int pfEnabled = 0;
|
||||
int result = DwmIsCompositionEnabled(ref pfEnabled);
|
||||
return (pfEnabled == 1) ? true : false;
|
||||
}
|
||||
|
||||
public static bool IsNonClientRenderingEnabled(IntPtr hWnd)
|
||||
{
|
||||
int gwaEnabled = 0;
|
||||
int result = DwmGetWindowAttribute(hWnd, DWMWINDOWATTRIBUTE.NCRenderingEnabled, ref gwaEnabled, sizeof(int));
|
||||
return gwaEnabled == 1;
|
||||
}
|
||||
|
||||
public static bool WindowSetAttribute(IntPtr hWnd, DWMWINDOWATTRIBUTE attribute, int attributeValue)
|
||||
{
|
||||
int result = DwmSetWindowAttribute(hWnd, attribute, ref attributeValue, sizeof(int));
|
||||
return (result == 0);
|
||||
}
|
||||
|
||||
public static void Windows10EnableBlurBehind(IntPtr hWnd)
|
||||
{
|
||||
DWMNCRENDERINGPOLICY policy = DWMNCRENDERINGPOLICY.Enabled;
|
||||
WindowSetAttribute(hWnd, DWMWINDOWATTRIBUTE.NCRenderingPolicy, (int)policy);
|
||||
|
||||
AccentPolicy accPolicy = new AccentPolicy()
|
||||
{
|
||||
AccentState = DWMACCENTSTATE.ACCENT_ENABLE_BLURBEHIND,
|
||||
};
|
||||
|
||||
int accentSize = Marshal.SizeOf(accPolicy);
|
||||
IntPtr accentPtr = Marshal.AllocHGlobal(accentSize);
|
||||
Marshal.StructureToPtr(accPolicy, accentPtr, false);
|
||||
var data = new WinCompositionAttrData(DWMWINDOWATTRIBUTE.AccentPolicy, accentPtr, accentSize);
|
||||
|
||||
SetWindowCompositionAttribute(hWnd, ref data);
|
||||
Marshal.FreeHGlobal(accentPtr);
|
||||
}
|
||||
|
||||
public static bool WindowEnableBlurBehind(IntPtr hWnd)
|
||||
{
|
||||
DWMNCRENDERINGPOLICY policy = DWMNCRENDERINGPOLICY.Enabled;
|
||||
WindowSetAttribute(hWnd, DWMWINDOWATTRIBUTE.NCRenderingPolicy, (int)policy);
|
||||
|
||||
DWM_BLURBEHIND dwm_BB = new DWM_BLURBEHIND(true);
|
||||
int result = DwmEnableBlurBehindWindow(hWnd, ref dwm_BB);
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
public static bool WindowExtendIntoClientArea(IntPtr hWnd, MARGINS margins)
|
||||
{
|
||||
// Extend frame on the bottom of client area
|
||||
int result = DwmExtendFrameIntoClientArea(hWnd, ref margins);
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
public static bool WindowBorderlessDropShadow(IntPtr hWnd, int shadowSize)
|
||||
{
|
||||
MARGINS margins = new MARGINS(0, shadowSize, 0, shadowSize);
|
||||
int result = DwmExtendFrameIntoClientArea(hWnd, ref margins);
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
public static bool WindowSheetOfGlass(IntPtr hWnd)
|
||||
{
|
||||
MARGINS margins = new MARGINS();
|
||||
|
||||
//Margins set to All:-1 - Sheet Of Glass effect
|
||||
margins.SheetOfGlass();
|
||||
int result = DwmExtendFrameIntoClientArea(hWnd, ref margins);
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
public static bool WindowDisableRendering(IntPtr hWnd)
|
||||
{
|
||||
int ncrp = (int)DWMNCRENDERINGPOLICY.Disabled;
|
||||
// Disable non-client area rendering on the window.
|
||||
int result = DwmSetWindowAttribute(hWnd, DWMWINDOWATTRIBUTE.NCRenderingPolicy, ref ncrp, sizeof(int));
|
||||
return result == 0;
|
||||
}
|
||||
}
|
52
ObservatoryCore/UI/NotificationForm.Designer.cs
generated
52
ObservatoryCore/UI/NotificationForm.Designer.cs
generated
@ -28,12 +28,60 @@
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.components = new System.ComponentModel.Container();
|
||||
this.Title = new System.Windows.Forms.Label();
|
||||
this.Body = new System.Windows.Forms.Label();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// Title
|
||||
//
|
||||
this.Title.Font = new System.Drawing.Font("Segoe UI", 24F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
|
||||
this.Title.ForeColor = System.Drawing.Color.OrangeRed;
|
||||
this.Title.Location = new System.Drawing.Point(5, 5);
|
||||
this.Title.MaximumSize = new System.Drawing.Size(355, 0);
|
||||
this.Title.Name = "Title";
|
||||
this.Title.Size = new System.Drawing.Size(338, 45);
|
||||
this.Title.TabIndex = 0;
|
||||
this.Title.Text = "Title";
|
||||
//
|
||||
// Body
|
||||
//
|
||||
this.Body.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.Body.AutoSize = true;
|
||||
this.Body.Font = new System.Drawing.Font("Segoe UI", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
|
||||
this.Body.ForeColor = System.Drawing.Color.OrangeRed;
|
||||
this.Body.Location = new System.Drawing.Point(12, 45);
|
||||
this.Body.MaximumSize = new System.Drawing.Size(320, 85);
|
||||
this.Body.Name = "Body";
|
||||
this.Body.Size = new System.Drawing.Size(51, 31);
|
||||
this.Body.TabIndex = 1;
|
||||
this.Body.Text = "Body";
|
||||
this.Body.UseCompatibleTextRendering = true;
|
||||
//
|
||||
// NotificationForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(800, 450);
|
||||
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
|
||||
this.ClientSize = new System.Drawing.Size(355, 145);
|
||||
this.ControlBox = false;
|
||||
this.Controls.Add(this.Body);
|
||||
this.Controls.Add(this.Title);
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
|
||||
this.MaximizeBox = false;
|
||||
this.MinimizeBox = false;
|
||||
this.Name = "NotificationForm";
|
||||
this.ShowIcon = false;
|
||||
this.ShowInTaskbar = false;
|
||||
this.Text = "NotificationForm";
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private Label Title;
|
||||
private Label Body;
|
||||
}
|
||||
}
|
@ -1,9 +1,12 @@
|
||||
using System;
|
||||
using Observatory.Framework;
|
||||
using Observatory.Framework.Files.Journal;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
@ -12,11 +15,206 @@ namespace Observatory.UI
|
||||
{
|
||||
public partial class NotificationForm : Form
|
||||
{
|
||||
public NotificationForm()
|
||||
private Color _color;
|
||||
private readonly Guid _guid;
|
||||
private readonly System.Timers.Timer _timer;
|
||||
private bool _defaultPosition = true;
|
||||
private Point _originalLocation;
|
||||
|
||||
protected override bool ShowWithoutActivation => true;
|
||||
protected override CreateParams CreateParams
|
||||
{
|
||||
get
|
||||
{
|
||||
CreateParams cp = base.CreateParams;
|
||||
cp.ExStyle |= 0x00000008; // WS_EX_TOPMOST
|
||||
return cp;
|
||||
}
|
||||
}
|
||||
|
||||
public NotificationForm(Guid guid, NotificationArgs args)
|
||||
{
|
||||
_guid = guid;
|
||||
_color = Color.FromArgb((int)Properties.Core.Default.NativeNotifyColour);
|
||||
InitializeComponent();
|
||||
|
||||
Title.Paint += DrawText;
|
||||
Body.Paint += DrawText;
|
||||
|
||||
if (System.Environment.OSVersion.Version.Major >= 6 && DwmHelper.IsCompositionEnabled())
|
||||
{
|
||||
if (Environment.OSVersion.Version.Major > 6)
|
||||
{
|
||||
DwmHelper.Windows10EnableBlurBehind(Handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
DwmHelper.WindowEnableBlurBehind(Handle);
|
||||
}
|
||||
|
||||
// For some reason this causes the window to become all white on my own
|
||||
// PC. Looks very similar to strange system-specific all-white behaviour
|
||||
// of Avalonia.
|
||||
// DwmHelper.WindowBorderlessDropShadow(Handle, 2);
|
||||
}
|
||||
|
||||
|
||||
Title.ForeColor = _color;
|
||||
Title.Text = args.Title;
|
||||
Title.Font = new Font(Properties.Core.Default.NativeNotifyFont, 24);
|
||||
Body.ForeColor = _color;
|
||||
Body.Text = args.Detail;
|
||||
Body.Font = new Font(Properties.Core.Default.NativeNotifyFont, 14);
|
||||
this.Paint += DrawBorder;
|
||||
|
||||
AdjustPosition(args.XPos / 100, args.YPos / 100);
|
||||
|
||||
_timer = new();
|
||||
_timer.Elapsed += CloseNotification;
|
||||
if (args.Timeout != 0)
|
||||
{
|
||||
_timer.Interval = args.Timeout == -1 ? Properties.Core.Default.NativeNotifyTimeout : args.Timeout;
|
||||
_timer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
public Guid Guid;
|
||||
public void Update(NotificationArgs notificationArgs)
|
||||
{
|
||||
Title.Text = notificationArgs.Title;
|
||||
Body.Text = notificationArgs.Detail;
|
||||
}
|
||||
|
||||
private void AdjustPosition(double x = -1.0, double y = -1.0)
|
||||
{
|
||||
int screen = Properties.Core.Default.NativeNotifyScreen;
|
||||
int corner = Properties.Core.Default.NativeNotifyCorner;
|
||||
Rectangle screenBounds;
|
||||
|
||||
|
||||
if (screen == -1 || screen > Screen.AllScreens.Length)
|
||||
if (Screen.AllScreens.Length == 1)
|
||||
screenBounds = Screen.GetBounds(this);
|
||||
else
|
||||
screenBounds = Screen.PrimaryScreen.Bounds;
|
||||
else
|
||||
screenBounds = Screen.AllScreens[screen - 1].Bounds;
|
||||
|
||||
if (x >= 0 && y >= 0)
|
||||
{
|
||||
_defaultPosition = false;
|
||||
int xLocation = Convert.ToInt32(screenBounds.Width * x);
|
||||
int yLocation = Convert.ToInt32(screenBounds.Height * x);
|
||||
Location = Point.Add(screenBounds.Location, new Size(xLocation, yLocation));
|
||||
}
|
||||
else
|
||||
{
|
||||
_defaultPosition = true;
|
||||
switch (corner)
|
||||
{
|
||||
default:
|
||||
case 0:
|
||||
Location = Point.Add(
|
||||
new Point(screenBounds.Right, screenBounds.Bottom),
|
||||
new Size(-(Width+50), -(Height+50)));
|
||||
break;
|
||||
case 1:
|
||||
Location = Point.Add(
|
||||
new Point(screenBounds.Left, screenBounds.Bottom),
|
||||
new Size(50, -(Height + 50)));
|
||||
break;
|
||||
case 2:
|
||||
Location = Point.Add(
|
||||
new Point(screenBounds.Right, screenBounds.Top),
|
||||
new Size(-(Width + 50), 50));
|
||||
break;
|
||||
case 3:
|
||||
Location = Point.Add(
|
||||
new Point(screenBounds.Left, screenBounds.Top),
|
||||
new Size(50, 00));
|
||||
break;
|
||||
}
|
||||
_originalLocation = new Point(Location.X, Location.Y);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawBorder(object? sender, PaintEventArgs e)
|
||||
{
|
||||
using (Pen pen = new Pen(_color))
|
||||
{
|
||||
pen.Width = 6;
|
||||
e.Graphics.DrawLine(pen, 0, 0, Width, 0);
|
||||
e.Graphics.DrawLine(pen, 0, 0, 0, Height);
|
||||
e.Graphics.DrawLine(pen, 0, Height, Width, Height);
|
||||
e.Graphics.DrawLine(pen, Width, 0, Width, Height);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void WndProc(ref Message m)
|
||||
{
|
||||
|
||||
switch (m.Msg)
|
||||
{
|
||||
case DwmHelper.WM_DWMCOMPOSITIONCHANGED:
|
||||
if (System.Environment.OSVersion.Version.Major >= 6 && DwmHelper.IsCompositionEnabled())
|
||||
{
|
||||
var policy = DwmHelper.DWMNCRENDERINGPOLICY.Enabled;
|
||||
DwmHelper.WindowSetAttribute(Handle, DwmHelper.DWMWINDOWATTRIBUTE.NCRenderingPolicy, (int)policy);
|
||||
DwmHelper.WindowBorderlessDropShadow(Handle, 2);
|
||||
m.Result = IntPtr.Zero;
|
||||
}
|
||||
break;
|
||||
case 0x0084:
|
||||
m.Result = (IntPtr)(-1);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
base.WndProc(ref m);
|
||||
}
|
||||
|
||||
private void DrawText(object? sender, PaintEventArgs e)
|
||||
{
|
||||
if (sender != null)
|
||||
{
|
||||
var label = (Label)sender;
|
||||
e.Graphics.Clear(Color.Transparent);
|
||||
using (var sf = new StringFormat())
|
||||
using (var brush = new SolidBrush(label.ForeColor))
|
||||
{
|
||||
sf.Alignment = sf.LineAlignment = StringAlignment.Near;
|
||||
e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
|
||||
e.Graphics.DrawString(label.Text, label.Font, brush, label.ClientRectangle, sf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Guid Guid { get => _guid; }
|
||||
|
||||
private void AdjustText()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void CloseNotification(object? sender, System.Timers.ElapsedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
Close();
|
||||
}
|
||||
catch
|
||||
{
|
||||
try
|
||||
{
|
||||
this.Invoke(() => Close());
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw new Exception("blah");
|
||||
}
|
||||
}
|
||||
|
||||
_timer.Stop();
|
||||
_timer.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,64 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
|
@ -1,11 +1,7 @@
|
||||
using Observatory.Framework.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Speech.Synthesis;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections;
|
||||
using Observatory.PluginManagement;
|
||||
using Observatory.Utils;
|
||||
|
||||
namespace Observatory.UI
|
||||
{
|
||||
@ -47,12 +43,20 @@ namespace Observatory.UI
|
||||
|
||||
if (plugin.PluginUI.PluginUIType == Framework.PluginUI.UIType.Basic)
|
||||
uiPanels.Add(newItem, CreateBasicUI(plugin));
|
||||
else if (plugin.PluginUI.PluginUIType == Framework.PluginUI.UIType.Panel)
|
||||
uiPanels.Add(newItem, (Panel)plugin.PluginUI.UI);
|
||||
}
|
||||
|
||||
private static Panel CreateBasicUI(IObservatoryPlugin plugin)
|
||||
{
|
||||
Panel panel = new();
|
||||
var columnSorter = new DefaultSorter();
|
||||
|
||||
IObservatoryComparer columnSorter;
|
||||
if (plugin.ColumnSorter != null)
|
||||
columnSorter = plugin.ColumnSorter;
|
||||
else
|
||||
columnSorter = new DefaultSorter();
|
||||
|
||||
ListView listView = new()
|
||||
{
|
||||
View = View.Details,
|
||||
@ -62,7 +66,8 @@ namespace Observatory.UI
|
||||
BackColor = Color.FromArgb(64, 64, 64),
|
||||
ForeColor = Color.LightGray,
|
||||
GridLines = true,
|
||||
ListViewItemSorter = columnSorter
|
||||
ListViewItemSorter = columnSorter,
|
||||
Font = new Font(new FontFamily("Segoe UI"), 10, FontStyle.Regular)
|
||||
};
|
||||
|
||||
foreach (var property in plugin.PluginUI.DataGrid.First().GetType().GetProperties())
|
||||
@ -75,20 +80,20 @@ namespace Observatory.UI
|
||||
if (e.Column == columnSorter.SortColumn)
|
||||
{
|
||||
// Reverse the current sort direction for this column.
|
||||
if (columnSorter.Order == SortOrder.Ascending)
|
||||
if (columnSorter.Order == 1)
|
||||
{
|
||||
columnSorter.Order = SortOrder.Descending;
|
||||
columnSorter.Order = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
columnSorter.Order = SortOrder.Ascending;
|
||||
columnSorter.Order = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set the column number that is to be sorted; default to ascending.
|
||||
columnSorter.SortColumn = e.Column;
|
||||
columnSorter.Order = SortOrder.Ascending;
|
||||
columnSorter.Order = 1;
|
||||
}
|
||||
listView.Sort();
|
||||
};
|
||||
@ -97,20 +102,58 @@ namespace Observatory.UI
|
||||
|
||||
plugin.PluginUI.DataGrid.CollectionChanged += (sender, e) =>
|
||||
{
|
||||
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add &&
|
||||
e.NewItems != null)
|
||||
listView.Invoke(() =>
|
||||
{
|
||||
foreach (var newItem in e.NewItems)
|
||||
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add &&
|
||||
e.NewItems != null)
|
||||
{
|
||||
ListViewItem newListItem = new();
|
||||
foreach (var property in newItem.GetType().GetProperties())
|
||||
foreach (var newItem in e.NewItems)
|
||||
{
|
||||
newListItem.SubItems.Add(property.GetValue(newItem)?.ToString());
|
||||
ListViewItem newListItem = new();
|
||||
foreach (var property in newItem.GetType().GetProperties())
|
||||
{
|
||||
newListItem.SubItems.Add(property.GetValue(newItem)?.ToString());
|
||||
}
|
||||
newListItem.SubItems.RemoveAt(0);
|
||||
listView.Items.Add(newListItem);
|
||||
}
|
||||
newListItem.SubItems.RemoveAt(0);
|
||||
listView.Items.Add(newListItem);
|
||||
}
|
||||
}
|
||||
|
||||
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove &&
|
||||
e.OldItems != null)
|
||||
{
|
||||
foreach (var oldItem in e.OldItems)
|
||||
{
|
||||
ListViewItem oldListItem = new();
|
||||
foreach (var property in oldItem.GetType().GetProperties())
|
||||
{
|
||||
oldListItem.SubItems.Add(property.GetValue(oldItem)?.ToString());
|
||||
}
|
||||
oldListItem.SubItems.RemoveAt(0);
|
||||
|
||||
var itemToRemove = listView.Items.Cast<ListViewItem>().Where(i => i.SubItems.Cast<string>().SequenceEqual(oldListItem.SubItems.Cast<string>())).First();
|
||||
if (itemToRemove != null)
|
||||
{
|
||||
listView.Items.Remove(itemToRemove);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Reset)
|
||||
{
|
||||
listView.Items.Clear();
|
||||
foreach (var item in plugin.PluginUI.DataGrid)
|
||||
{
|
||||
ListViewItem listItem = new();
|
||||
foreach (var property in item.GetType().GetProperties())
|
||||
{
|
||||
listItem.SubItems.Add(property.GetValue(item)?.ToString());
|
||||
}
|
||||
listItem.SubItems.RemoveAt(0);
|
||||
listView.Items.Add(listItem);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return panel;
|
||||
|
@ -2,10 +2,12 @@
|
||||
using Observatory.Framework.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms.VisualStyles;
|
||||
|
||||
namespace Observatory.UI
|
||||
{
|
||||
@ -22,7 +24,7 @@ namespace Observatory.UI
|
||||
_adjustPanelsBelow = adjustPanelsBelow;
|
||||
|
||||
// Filtered to only settings without SettingIgnore attribute
|
||||
var settings = PluginManagement.PluginManager.GetSettingDisplayNames(plugin).Where(s => !Attribute.IsDefined(s.Key, typeof (SettingIgnore)));
|
||||
var settings = PluginManagement.PluginManager.GetSettingDisplayNames(plugin.Settings).Where(s => !Attribute.IsDefined(s.Key, typeof (SettingIgnore)));
|
||||
CreateControls(settings);
|
||||
|
||||
}
|
||||
@ -30,35 +32,300 @@ namespace Observatory.UI
|
||||
private void CreateControls(IEnumerable<KeyValuePair<PropertyInfo, string>> settings)
|
||||
{
|
||||
int controlRow = 0;
|
||||
bool nextColumn = true;
|
||||
bool recentHalfCol = false;
|
||||
|
||||
// Handle bool (checkbox) settings first and keep them grouped together
|
||||
foreach (var setting in settings.Where(s => s.Key.PropertyType == typeof(bool)))
|
||||
foreach (var setting in settings)
|
||||
{
|
||||
CheckBox checkBox = new()
|
||||
// Reset the column tracking for checkboxes if this isn't a checkbox
|
||||
if (setting.Key.PropertyType.Name != "Boolean" && setting.Key.PropertyType.Name != "Button")
|
||||
recentHalfCol = false;
|
||||
|
||||
switch (setting.Key.GetValue(_plugin.Settings))
|
||||
{
|
||||
Text = setting.Value,
|
||||
Checked = (bool?)setting.Key.GetValue(_plugin.Settings) ?? false
|
||||
};
|
||||
case bool:
|
||||
var checkBox = CreateBoolSetting(setting);
|
||||
controlRow += recentHalfCol ? 0 : 1;
|
||||
checkBox.Location = GetSettingPosition(controlRow, recentHalfCol);
|
||||
|
||||
checkBox.CheckedChanged += (object? _, EventArgs _) =>
|
||||
{
|
||||
setting.Key.SetValue(_plugin.Settings, checkBox.Checked);
|
||||
PluginManagement.PluginManager.GetInstance.SaveSettings(_plugin, _plugin.Settings);
|
||||
};
|
||||
recentHalfCol = !recentHalfCol;
|
||||
|
||||
checkBox.Location = new Point(nextColumn ? 10 : 130, 3 + controlRow * 29);
|
||||
controlRow += nextColumn ? 0 : 1;
|
||||
nextColumn = !nextColumn;
|
||||
Controls.Add(checkBox);
|
||||
break;
|
||||
case string:
|
||||
var stringLabel = CreateSettingLabel(setting.Value);
|
||||
var textBox = CreateStringSetting(setting.Key);
|
||||
controlRow++;
|
||||
stringLabel.Location = GetSettingPosition(controlRow);
|
||||
textBox.Location = GetSettingPosition(controlRow, true);
|
||||
|
||||
Controls.Add(stringLabel);
|
||||
Controls.Add(textBox);
|
||||
|
||||
Controls.Add(checkBox);
|
||||
break;
|
||||
case FileInfo:
|
||||
var fileLabel = CreateSettingLabel(setting.Value);
|
||||
var pathTextBox = CreateFilePathSetting(setting.Key);
|
||||
var pathButton = CreateFileBrowseSetting(setting.Key, pathTextBox);
|
||||
|
||||
controlRow++;
|
||||
|
||||
fileLabel.Location = GetSettingPosition(controlRow);
|
||||
pathTextBox.Location = GetSettingPosition(controlRow, true);
|
||||
pathButton.Location = GetSettingPosition(++controlRow, true);
|
||||
|
||||
Controls.Add(fileLabel);
|
||||
Controls.Add(pathTextBox);
|
||||
Controls.Add(pathButton);
|
||||
|
||||
break;
|
||||
case int:
|
||||
// We have two options for integer values:
|
||||
// 1) A slider (explicit by way of the SettingIntegerUseSlider attribute and bounded to 0..100 by default)
|
||||
// 2) A numeric up/down (default otherwise, and is unbounded by default).
|
||||
// Bounds for both can be set via the SettingNumericBounds attribute, only the up/down uses Increment.
|
||||
var intLabel = CreateSettingLabel(setting.Value);
|
||||
Control intControl;
|
||||
controlRow++;
|
||||
if (System.Attribute.IsDefined(setting.Key, typeof(SettingNumericUseSlider)))
|
||||
{
|
||||
intControl = CreateSettingTrackbar(setting.Key);
|
||||
}
|
||||
else
|
||||
{
|
||||
intControl = CreateSettingNumericUpDown(setting.Key);
|
||||
}
|
||||
intLabel.Location = GetSettingPosition(controlRow);
|
||||
intControl.Location = GetSettingPosition(controlRow, true);
|
||||
|
||||
Controls.Add(intLabel);
|
||||
Controls.Add(intControl);
|
||||
break;
|
||||
case Action action:
|
||||
var button = CreateSettingButton(setting.Value, action);
|
||||
|
||||
controlRow += recentHalfCol ? 0 : 1;
|
||||
button.Location = GetSettingPosition(controlRow, recentHalfCol);
|
||||
recentHalfCol = !recentHalfCol;
|
||||
|
||||
Controls.Add(button);
|
||||
break;
|
||||
case Dictionary<string, object> dictSetting:
|
||||
var dictLabel = CreateSettingLabel(setting.Value);
|
||||
var dropdown = CreateSettingDropdown(setting.Key, dictSetting);
|
||||
controlRow++;
|
||||
|
||||
dictLabel.Location = GetSettingPosition(controlRow);
|
||||
dropdown.Location = GetSettingPosition(controlRow, true);
|
||||
Controls.Add(dictLabel);
|
||||
Controls.Add(dropdown);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
Height = 3 + controlRow * 29;
|
||||
}
|
||||
|
||||
private static Point GetSettingPosition(int rowNum, bool secondCol = false)
|
||||
{
|
||||
return new Point(10 + (secondCol ? 200 : 0), -26 + rowNum * 29);
|
||||
}
|
||||
|
||||
|
||||
private Label CreateSettingLabel(string settingName)
|
||||
{
|
||||
Label label = new()
|
||||
{
|
||||
Text = settingName + ": ",
|
||||
TextAlign = System.Drawing.ContentAlignment.MiddleRight,
|
||||
Width = 200,
|
||||
ForeColor = Color.LightGray
|
||||
};
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
private ComboBox CreateSettingDropdown(PropertyInfo setting, Dictionary<string, object> dropdownItems)
|
||||
{
|
||||
var backingValueName = (SettingBackingValue?)Attribute.GetCustomAttribute(setting, typeof(SettingBackingValue));
|
||||
|
||||
var backingValue = from s in PluginManagement.PluginManager.GetSettingDisplayNames(_plugin.Settings)
|
||||
where s.Value == backingValueName?.BackingProperty
|
||||
select s.Key;
|
||||
|
||||
if (backingValue.Count() != 1)
|
||||
throw new($"{_plugin.ShortName}: Dictionary settings must have exactly one backing value.");
|
||||
|
||||
ComboBox comboBox = new()
|
||||
{
|
||||
Width = 200,
|
||||
DropDownStyle = ComboBoxStyle.DropDownList
|
||||
};
|
||||
|
||||
comboBox.Items.AddRange(dropdownItems.OrderBy(s => s.Key).Select(s => s.Key).ToArray());
|
||||
|
||||
string? currentSelection = backingValue.First().GetValue(_plugin.Settings)?.ToString();
|
||||
|
||||
if (currentSelection?.Length > 0)
|
||||
{
|
||||
comboBox.SelectedItem = currentSelection;
|
||||
}
|
||||
|
||||
// Then the rest
|
||||
foreach (var setting in settings.Where(s => s.Key.PropertyType != typeof(bool)))
|
||||
comboBox.SelectedValueChanged += (sender, e) =>
|
||||
{
|
||||
backingValue.First().SetValue(_plugin.Settings, comboBox.SelectedItem.ToString());
|
||||
SaveSettings();
|
||||
};
|
||||
|
||||
return comboBox;
|
||||
}
|
||||
|
||||
private Button CreateSettingButton(string settingName, Action action)
|
||||
{
|
||||
Button button = new()
|
||||
{
|
||||
Text = settingName
|
||||
};
|
||||
|
||||
button.Click += (sender, e) =>
|
||||
{
|
||||
action.Invoke();
|
||||
SaveSettings();
|
||||
};
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
private TrackBar CreateSettingTrackbar(PropertyInfo setting)
|
||||
{
|
||||
SettingNumericBounds? bounds = (SettingNumericBounds?)System.Attribute.GetCustomAttribute(setting, typeof(SettingNumericBounds));
|
||||
TrackBar trackBar = new ()
|
||||
{
|
||||
Orientation = Orientation.Horizontal,
|
||||
TickStyle = TickStyle.Both,
|
||||
Width = 200,
|
||||
Minimum = Convert.ToInt32(bounds?.Minimum ?? 0),
|
||||
Maximum = Convert.ToInt32(bounds?.Maximum ?? 100)
|
||||
};
|
||||
|
||||
trackBar.Value = (int?)setting.GetValue(_plugin.Settings) ?? 0;
|
||||
|
||||
trackBar.ValueChanged += (sender, e) =>
|
||||
{
|
||||
setting.SetValue(_plugin.Settings, trackBar.Value);
|
||||
SaveSettings();
|
||||
};
|
||||
|
||||
return trackBar;
|
||||
}
|
||||
|
||||
private NumericUpDown CreateSettingNumericUpDown(PropertyInfo setting)
|
||||
{
|
||||
SettingNumericBounds? bounds = (SettingNumericBounds?)System.Attribute.GetCustomAttribute(setting, typeof(SettingNumericBounds));
|
||||
NumericUpDown numericUpDown = new()
|
||||
{
|
||||
|
||||
}
|
||||
Width = 200,
|
||||
Minimum = Convert.ToInt32(bounds?.Minimum ?? Int32.MinValue),
|
||||
Maximum = Convert.ToInt32(bounds?.Maximum ?? Int32.MaxValue),
|
||||
Increment = Convert.ToInt32(bounds?.Increment ?? 1)
|
||||
};
|
||||
|
||||
numericUpDown.Value = (int?)setting.GetValue(_plugin.Settings) ?? 0;
|
||||
|
||||
numericUpDown.ValueChanged += (sender, e) =>
|
||||
{
|
||||
setting.SetValue(_plugin.Settings, numericUpDown.Value);
|
||||
SaveSettings();
|
||||
};
|
||||
|
||||
return numericUpDown;
|
||||
}
|
||||
|
||||
private CheckBox CreateBoolSetting(KeyValuePair<PropertyInfo, string> setting)
|
||||
{
|
||||
CheckBox checkBox = new()
|
||||
{
|
||||
Text = setting.Value,
|
||||
TextAlign= System.Drawing.ContentAlignment.MiddleLeft,
|
||||
Checked = (bool?)setting.Key.GetValue(_plugin.Settings) ?? false,
|
||||
Width = 200,
|
||||
ForeColor = Color.LightGray
|
||||
};
|
||||
|
||||
checkBox.CheckedChanged += (sender, e) =>
|
||||
{
|
||||
setting.Key.SetValue(_plugin.Settings, checkBox.Checked);
|
||||
SaveSettings();
|
||||
};
|
||||
|
||||
return checkBox;
|
||||
}
|
||||
|
||||
private TextBox CreateStringSetting(PropertyInfo setting)
|
||||
{
|
||||
TextBox textBox = new()
|
||||
{
|
||||
Text = (setting.GetValue(_plugin.Settings) ?? String.Empty).ToString(),
|
||||
Width = 200
|
||||
};
|
||||
|
||||
textBox.TextChanged += (object? sender, EventArgs e) =>
|
||||
{
|
||||
setting.SetValue(_plugin.Settings, textBox.Text);
|
||||
SaveSettings();
|
||||
};
|
||||
|
||||
return textBox;
|
||||
}
|
||||
|
||||
private TextBox CreateFilePathSetting(PropertyInfo setting)
|
||||
{
|
||||
var fileInfo = (FileInfo?)setting.GetValue(_plugin.Settings);
|
||||
|
||||
TextBox textBox = new()
|
||||
{
|
||||
Text = fileInfo?.FullName ?? string.Empty,
|
||||
Width = 200
|
||||
};
|
||||
|
||||
textBox.TextChanged += (object? sender, EventArgs e) =>
|
||||
{
|
||||
setting.SetValue(_plugin.Settings, new FileInfo(textBox.Text));
|
||||
SaveSettings();
|
||||
};
|
||||
|
||||
return textBox;
|
||||
}
|
||||
|
||||
private Button CreateFileBrowseSetting(PropertyInfo setting, TextBox textBox)
|
||||
{
|
||||
Button button = new()
|
||||
{
|
||||
Text = "Browse"
|
||||
};
|
||||
|
||||
button.Click += (object? sender, EventArgs e) =>
|
||||
{
|
||||
var currentDir = ((FileInfo?)setting.GetValue(_plugin.Settings))?.DirectoryName;
|
||||
|
||||
OpenFileDialog ofd = new OpenFileDialog()
|
||||
{
|
||||
Title = "Select File...",
|
||||
Filter = "Lua files (*.lua)|*.lua|All files (*.*)|*.*",
|
||||
FilterIndex = 0,
|
||||
InitialDirectory = currentDir ?? Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
|
||||
};
|
||||
|
||||
var browseResult = ofd.ShowDialog();
|
||||
|
||||
if (browseResult == DialogResult.OK)
|
||||
{
|
||||
textBox.Text = ofd.FileName;
|
||||
}
|
||||
};
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
private Label CreateHeader(string pluginName)
|
||||
@ -92,5 +359,10 @@ namespace Observatory.UI
|
||||
}
|
||||
this.Parent?.ResumeLayout();
|
||||
}
|
||||
|
||||
private void SaveSettings()
|
||||
{
|
||||
PluginManagement.PluginManager.GetInstance.SaveSettings(_plugin, _plugin.Settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -284,7 +284,7 @@ namespace Observatory.Utils
|
||||
if (Properties.Core.Default.JournalFolder != path)
|
||||
{
|
||||
Properties.Core.Default.JournalFolder = path;
|
||||
Properties.Core.Default.Save();
|
||||
SettingsManager.Save();
|
||||
}
|
||||
|
||||
return logDirectory;
|
||||
|
67
ObservatoryCore/Utils/SettingsManager.cs
Normal file
67
ObservatoryCore/Utils/SettingsManager.cs
Normal file
@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Observatory.Utils
|
||||
{
|
||||
internal static class SettingsManager
|
||||
{
|
||||
internal static void Save()
|
||||
{
|
||||
#if DEBUG || RELEASE
|
||||
Properties.Core.Default.Save();
|
||||
#elif PORTABLE
|
||||
|
||||
Dictionary<string, object?> settings = new();
|
||||
|
||||
foreach (PropertyInfo property in Properties.Core.Default.GetType().GetProperties())
|
||||
{
|
||||
if (property.CanRead && property.CanWrite && !property.GetIndexParameters().Any())
|
||||
settings.Add(
|
||||
property.Name,
|
||||
property.GetValue(Properties.Core.Default)
|
||||
);
|
||||
}
|
||||
|
||||
string serializedSettings = JsonSerializer.Serialize(settings, new JsonSerializerOptions()
|
||||
{
|
||||
ReferenceHandler = System.Text.Json.Serialization.ReferenceHandler.Preserve,
|
||||
|
||||
});
|
||||
File.WriteAllText("Observatory.config", serializedSettings);
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static void Load()
|
||||
{
|
||||
#if PORTABLE
|
||||
if (File.Exists("Observatory.config"))
|
||||
{
|
||||
string savedSettings = File.ReadAllText("Observatory.config");
|
||||
Dictionary<string, object?>? settings = JsonSerializer.Deserialize<Dictionary<string, object?>>(savedSettings);
|
||||
if (settings != null)
|
||||
{
|
||||
var properties = Properties.Core.Default.GetType().GetProperties();
|
||||
|
||||
foreach (var savedProperty in settings)
|
||||
{
|
||||
|
||||
var currentProperty = properties.Where(p => p.Name == savedProperty.Key);
|
||||
if (currentProperty.Any())
|
||||
{
|
||||
JsonElement? value = (JsonElement?)savedProperty.Value;
|
||||
var deserializedValue = value?.Deserialize(currentProperty.First().PropertyType);
|
||||
currentProperty.First().SetValue(Properties.Core.Default, deserializedValue);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.30128.74
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.3.32922.545
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ObservatoryBotanist", "..\ObservatoryBotanist\ObservatoryBotanist.csproj", "{498F7360-D443-4D64-895C-9EAB5570D019}"
|
||||
EndProject
|
||||
@ -16,27 +16,38 @@ EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Portable|Any CPU = Portable|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{498F7360-D443-4D64-895C-9EAB5570D019}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{498F7360-D443-4D64-895C-9EAB5570D019}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{498F7360-D443-4D64-895C-9EAB5570D019}.Portable|Any CPU.ActiveCfg = Portable|Any CPU
|
||||
{498F7360-D443-4D64-895C-9EAB5570D019}.Portable|Any CPU.Build.0 = Portable|Any CPU
|
||||
{498F7360-D443-4D64-895C-9EAB5570D019}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{498F7360-D443-4D64-895C-9EAB5570D019}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0E1C4F16-858E-4E53-948A-77D81A8F3395}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0E1C4F16-858E-4E53-948A-77D81A8F3395}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0E1C4F16-858E-4E53-948A-77D81A8F3395}.Portable|Any CPU.ActiveCfg = Portable|Any CPU
|
||||
{0E1C4F16-858E-4E53-948A-77D81A8F3395}.Portable|Any CPU.Build.0 = Portable|Any CPU
|
||||
{0E1C4F16-858E-4E53-948A-77D81A8F3395}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0E1C4F16-858E-4E53-948A-77D81A8F3395}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E0FCF2A2-BF56-4F4D-836B-92A0E8269192}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E0FCF2A2-BF56-4F4D-836B-92A0E8269192}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E0FCF2A2-BF56-4F4D-836B-92A0E8269192}.Portable|Any CPU.ActiveCfg = Portable|Any CPU
|
||||
{E0FCF2A2-BF56-4F4D-836B-92A0E8269192}.Portable|Any CPU.Build.0 = Portable|Any CPU
|
||||
{E0FCF2A2-BF56-4F4D-836B-92A0E8269192}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E0FCF2A2-BF56-4F4D-836B-92A0E8269192}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{27ABA3B7-AB3C-465F-BA40-4F06BD803811}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{27ABA3B7-AB3C-465F-BA40-4F06BD803811}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{27ABA3B7-AB3C-465F-BA40-4F06BD803811}.Portable|Any CPU.ActiveCfg = Portable|Any CPU
|
||||
{27ABA3B7-AB3C-465F-BA40-4F06BD803811}.Portable|Any CPU.Build.0 = Portable|Any CPU
|
||||
{27ABA3B7-AB3C-465F-BA40-4F06BD803811}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{27ABA3B7-AB3C-465F-BA40-4F06BD803811}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{BC57225F-D89B-4853-A816-9AB4865E7AC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BC57225F-D89B-4853-A816-9AB4865E7AC5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BC57225F-D89B-4853-A816-9AB4865E7AC5}.Portable|Any CPU.ActiveCfg = Portable|Any CPU
|
||||
{BC57225F-D89B-4853-A816-9AB4865E7AC5}.Portable|Any CPU.Build.0 = Portable|Any CPU
|
||||
{BC57225F-D89B-4853-A816-9AB4865E7AC5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BC57225F-D89B-4853-A816-9AB4865E7AC5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
|
@ -16,6 +16,10 @@ namespace Observatory.Explorer
|
||||
|
||||
bool isRing = scan.BodyName.Contains("Ring");
|
||||
|
||||
#if DEBUG
|
||||
// results.Add("Test Scan Event", "Test Detail");
|
||||
#endif
|
||||
|
||||
#region Landable Checks
|
||||
if (scan.Landable)
|
||||
{
|
||||
|
@ -5,6 +5,7 @@
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<SignAssembly>false</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>ObservatoryKey.snk</AssemblyOriginatorKeyFile>
|
||||
<Configurations>Debug;Release;Portable</Configurations>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
@ -20,10 +21,10 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
<Exec Condition=" '$(OS)' == 'Windows_NT' " Command="xcopy "$(TargetPath)" "$(ProjectDir)..\ObservatoryCore\$(OutDir)plugins\" /y" />
|
||||
<Exec Condition=" '$(OS)' == 'Windows_NT' " Command="xcopy "$(TargetDir)NLua.dll" "$(ProjectDir)..\ObservatoryCore\$(OutDir)plugins\deps\" /y" />
|
||||
<Exec Condition=" '$(OS)' == 'Windows_NT' " Command="xcopy "$(TargetDir)KeraLua.dll" "$(ProjectDir)..\ObservatoryCore\$(OutDir)plugins\deps\" /y" />
|
||||
<Exec Condition=" '$(OS)' == 'Windows_NT' " Command="xcopy "$(TargetDir)runtimes\win-x64\native\lua54.dll" "$(ProjectDir)..\ObservatoryCore\$(OutDir)plugins\deps\" /y" />
|
||||
<Exec Condition=" '$(OS)' == 'Windows_NT' " Command="xcopy "$(TargetPath)" "$(ProjectDir)..\ObservatoryCore\$(OutDir)..\net6.0-windows\plugins\" /y" />
|
||||
<Exec Condition=" '$(OS)' == 'Windows_NT' " Command="xcopy "$(TargetDir)NLua.dll" "$(ProjectDir)..\ObservatoryCore\$(OutDir)..\net6.0-windows\plugins\deps\" /y" />
|
||||
<Exec Condition=" '$(OS)' == 'Windows_NT' " Command="xcopy "$(TargetDir)KeraLua.dll" "$(ProjectDir)..\ObservatoryCore\$(OutDir)..\net6.0-windows\plugins\deps\" /y" />
|
||||
<Exec Condition=" '$(OS)' == 'Windows_NT' " Command="xcopy "$(TargetDir)runtimes\win-x64\native\lua54.dll" "$(ProjectDir)..\ObservatoryCore\$(OutDir)..\net6.0-windows\plugins\deps\" /y" />
|
||||
<Exec Condition=" '$(OS)' != 'Windows_NT' " Command="[ ! -d "$(ProjectDir)../ObservatoryCore/$(OutDir)plugins/deps" ] && mkdir -p "$(ProjectDir)../ObservatoryCore/$(OutDir)plugins/deps" || echo Directory already exists" />
|
||||
<Exec Condition=" '$(OS)' != 'Windows_NT' " Command="cp "$(TargetPath)" "$(ProjectDir)../ObservatoryCore/$(OutDir)plugins/" -f" />
|
||||
<Exec Condition=" '$(OS)' != 'Windows_NT' " Command="cp "$(TargetDir)NLua.dll" "$(ProjectDir)../ObservatoryCore/$(OutDir)plugins/deps/" -f" />
|
||||
|
@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using Observatory.Framework.Files;
|
||||
using Observatory.Framework.Files;
|
||||
using Observatory.Framework.Files.Journal;
|
||||
using System.Xml.XPath;
|
||||
|
||||
namespace Observatory.Framework.Interfaces
|
||||
{
|
||||
@ -50,6 +49,13 @@ namespace Observatory.Framework.Interfaces
|
||||
/// </summary>
|
||||
public object Settings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para>Plugin-specific object implementing the IComparer interface which is used to sort columns in the basic UI datagrid.</para>
|
||||
/// <para>If omitted a basic numeric compare sorter is used.</para>
|
||||
/// </summary>
|
||||
public IObservatoryComparer ColumnSorter
|
||||
{ get => null; }
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -163,13 +169,6 @@ namespace Observatory.Framework.Interfaces
|
||||
/// <param name="items">Grid items to be added. Object types should match original template item used to create the grid.</param>
|
||||
public void AddGridItems(IObservatoryWorker worker, IEnumerable<object> items);
|
||||
|
||||
/// <summary>
|
||||
/// Add multiple items to the bottom of the basic UI grid.
|
||||
/// </summary>
|
||||
/// <param name="worker">Reference to the calling plugin's worker interface.</param>
|
||||
/// <param name="items">Grid items to be added. Object types should match original template item used to create the grid.</param>
|
||||
public void AddGridItems(IObservatoryWorker worker, IEnumerable<object> items);
|
||||
|
||||
/// <summary>
|
||||
/// Clears basic UI grid, removing all items.
|
||||
/// </summary>
|
||||
@ -221,4 +220,21 @@ namespace Observatory.Framework.Interfaces
|
||||
/// </summary>
|
||||
public string PluginStorageFolder { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extends the base IComparer interface with exposed values for the column ID and sort order to use.
|
||||
/// </summary>
|
||||
public interface IObservatoryComparer : System.Collections.IComparer
|
||||
{
|
||||
/// <summary>
|
||||
/// Column ID to be currently sorted by.
|
||||
/// </summary>
|
||||
public int SortColumn { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Current order of sorting. Ascending = 1, Descending = -1, No sorting = 0.
|
||||
/// </summary>
|
||||
public int Order { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<RootNamespace>Observatory.Framework</RootNamespace>
|
||||
<Configurations>Debug;Release;Portable</Configurations>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
@ -17,5 +18,9 @@
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<DocumentationFile>ObservatoryFramework.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Portable|AnyCPU'">
|
||||
<DocumentationFile>ObservatoryFramework.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
@ -1350,6 +1350,12 @@
|
||||
If a public property is necessary but not required to be user accessible the [SettingIgnore] property will suppress display.</para>
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Observatory.Framework.Interfaces.IObservatoryPlugin.ColumnSorter">
|
||||
<summary>
|
||||
Plugin-specific object implementing the IComparer interface which is used to sort columns in the basic UI datagrid.
|
||||
If omitted a basic string compare sorter is used.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Observatory.Framework.Interfaces.IObservatoryWorker">
|
||||
<summary>
|
||||
<para>Interface for worker plugins which process journal data to update their UI or send notifications.</para>
|
||||
@ -1504,6 +1510,21 @@
|
||||
Retrieves and ensures creation of a location which can be used by the plugin to store persistent data.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Observatory.Framework.Interfaces.IObservatoryComparer">
|
||||
<summary>
|
||||
Extends the base IComparer interface with exposed values for the column ID and sort order to use.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Observatory.Framework.Interfaces.IObservatoryComparer.SortColumn">
|
||||
<summary>
|
||||
Column ID to be currently sorted by.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Observatory.Framework.Interfaces.IObservatoryComparer.Order">
|
||||
<summary>
|
||||
Current order of sorting. Ascending = 1, Descending = -1, No sorting = 0.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Observatory.Framework.PluginUI">
|
||||
<summary>
|
||||
Class permitting plugins to provide their UI, if any, to Observatory Core.
|
||||
@ -1516,13 +1537,12 @@
|
||||
</member>
|
||||
<member name="F:Observatory.Framework.PluginUI.UI">
|
||||
<summary>
|
||||
<para>UI object used by plugins with UIType.Avalonia.</para>
|
||||
<para>(Untested/not implemented)</para>
|
||||
<para>UI object used by plugins with UIType.Panel.</para>
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:Observatory.Framework.PluginUI.DataGrid">
|
||||
<summary>
|
||||
<para>Collection bound to DataGrid used byu plugins with UIType.Basic.</para>
|
||||
<para>Collection bound to DataGrid used by plugins with UIType.Basic.</para>
|
||||
<para>Objects in collection should be of a class defined within the plugin consisting of string properties.<br/>Each object is a single row, and the property names are used as column headers.</para>
|
||||
</summary>
|
||||
</member>
|
||||
@ -1531,7 +1551,7 @@
|
||||
Instantiate PluginUI of UIType.Basic.
|
||||
</summary>
|
||||
<param name="DataGrid">
|
||||
<para>Collection bound to DataGrid used byu plugins with UIType.Basic.</para>
|
||||
<para>Collection bound to DataGrid used by plugins with UIType.Basic.</para>
|
||||
<para>Objects in collection should be of a class defined within the plugin consisting of string properties.<br/>Each object is a single row, and the property names are used as column headers.</para>
|
||||
</param>
|
||||
</member>
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@ -17,13 +18,12 @@ namespace Observatory.Framework
|
||||
public readonly UIType PluginUIType;
|
||||
|
||||
/// <summary>
|
||||
/// <para>UI object used by plugins with UIType.Avalonia.</para>
|
||||
/// <para>(Untested/not implemented)</para>
|
||||
/// <para>UI object used by plugins with UIType.Panel.</para>
|
||||
/// </summary>
|
||||
public object UI;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Collection bound to DataGrid used byu plugins with UIType.Basic.</para>
|
||||
/// <para>Collection bound to DataGrid used by plugins with UIType.Basic.</para>
|
||||
/// <para>Objects in collection should be of a class defined within the plugin consisting of string properties.<br/>Each object is a single row, and the property names are used as column headers.</para>
|
||||
/// </summary>
|
||||
public ObservableCollection<object> DataGrid;
|
||||
@ -32,7 +32,7 @@ namespace Observatory.Framework
|
||||
/// Instantiate PluginUI of UIType.Basic.
|
||||
/// </summary>
|
||||
/// <param name="DataGrid">
|
||||
/// <para>Collection bound to DataGrid used byu plugins with UIType.Basic.</para>
|
||||
/// <para>Collection bound to DataGrid used by plugins with UIType.Basic.</para>
|
||||
/// <para>Objects in collection should be of a class defined within the plugin consisting of string properties.<br/>Each object is a single row, and the property names are used as column headers.</para>
|
||||
/// </param>
|
||||
public PluginUI(ObservableCollection<object> DataGrid)
|
||||
|
@ -4,6 +4,7 @@
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
<Configurations>Debug;Release;Portable</Configurations>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
Loading…
x
Reference in New Issue
Block a user