mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Merge branch 'dev' into 250320BookmarkFavicon
This commit is contained in:
commit
2c6fdc0b2e
135 changed files with 6308 additions and 3384 deletions
|
|
@ -1,14 +1,14 @@
|
||||||
using Flow.Launcher.Infrastructure.Logger;
|
using System;
|
||||||
using Flow.Launcher.Infrastructure.UserSettings;
|
|
||||||
using Flow.Launcher.Plugin;
|
|
||||||
using Flow.Launcher.Plugin.SharedCommands;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using Flow.Launcher.Core.Resource;
|
|
||||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
|
using Flow.Launcher.Infrastructure.Logger;
|
||||||
|
using Flow.Launcher.Infrastructure.UserSettings;
|
||||||
|
using Flow.Launcher.Plugin;
|
||||||
|
using Flow.Launcher.Plugin.SharedCommands;
|
||||||
|
|
||||||
namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
||||||
{
|
{
|
||||||
|
|
@ -42,8 +42,11 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
||||||
|
|
||||||
internal IEnumerable<PluginPair> Setup()
|
internal IEnumerable<PluginPair> Setup()
|
||||||
{
|
{
|
||||||
|
// If no plugin is using the language, return empty list
|
||||||
if (!PluginMetadataList.Any(o => o.Language.Equals(Language, StringComparison.OrdinalIgnoreCase)))
|
if (!PluginMetadataList.Any(o => o.Language.Equals(Language, StringComparison.OrdinalIgnoreCase)))
|
||||||
|
{
|
||||||
return new List<PluginPair>();
|
return new List<PluginPair>();
|
||||||
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(PluginsSettingsFilePath) && FilesFolders.FileExists(PluginsSettingsFilePath))
|
if (!string.IsNullOrEmpty(PluginsSettingsFilePath) && FilesFolders.FileExists(PluginsSettingsFilePath))
|
||||||
{
|
{
|
||||||
|
|
@ -55,24 +58,55 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
||||||
}
|
}
|
||||||
|
|
||||||
var noRuntimeMessage = string.Format(
|
var noRuntimeMessage = string.Format(
|
||||||
InternationalizationManager.Instance.GetTranslation("runtimePluginInstalledChooseRuntimePrompt"),
|
API.GetTranslation("runtimePluginInstalledChooseRuntimePrompt"),
|
||||||
Language,
|
Language,
|
||||||
EnvName,
|
EnvName,
|
||||||
Environment.NewLine
|
Environment.NewLine
|
||||||
);
|
);
|
||||||
if (API.ShowMsgBox(noRuntimeMessage, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No)
|
if (API.ShowMsgBox(noRuntimeMessage, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No)
|
||||||
{
|
{
|
||||||
var msg = string.Format(InternationalizationManager.Instance.GetTranslation("runtimePluginChooseRuntimeExecutable"), EnvName);
|
var msg = string.Format(API.GetTranslation("runtimePluginChooseRuntimeExecutable"), EnvName);
|
||||||
string selectedFile;
|
|
||||||
|
|
||||||
selectedFile = GetFileFromDialog(msg, FileDialogFilter);
|
var selectedFile = GetFileFromDialog(msg, FileDialogFilter);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(selectedFile))
|
if (!string.IsNullOrEmpty(selectedFile))
|
||||||
|
{
|
||||||
PluginsSettingsFilePath = selectedFile;
|
PluginsSettingsFilePath = selectedFile;
|
||||||
|
}
|
||||||
// Nothing selected because user pressed cancel from the file dialog window
|
// Nothing selected because user pressed cancel from the file dialog window
|
||||||
if (string.IsNullOrEmpty(selectedFile))
|
else
|
||||||
InstallEnvironment();
|
{
|
||||||
|
var forceDownloadMessage = string.Format(
|
||||||
|
API.GetTranslation("runtimeExecutableInvalidChooseDownload"),
|
||||||
|
Language,
|
||||||
|
EnvName,
|
||||||
|
Environment.NewLine
|
||||||
|
);
|
||||||
|
|
||||||
|
// Let users select valid path or choose to download
|
||||||
|
while (string.IsNullOrEmpty(selectedFile))
|
||||||
|
{
|
||||||
|
if (API.ShowMsgBox(forceDownloadMessage, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.Yes)
|
||||||
|
{
|
||||||
|
// Continue select file
|
||||||
|
selectedFile = GetFileFromDialog(msg, FileDialogFilter);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// User selected no, break the loop
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(selectedFile))
|
||||||
|
{
|
||||||
|
PluginsSettingsFilePath = selectedFile;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
InstallEnvironment();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -85,7 +119,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
API.ShowMsgBox(string.Format(InternationalizationManager.Instance.GetTranslation("runtimePluginUnableToSetExecutablePath"), Language));
|
API.ShowMsgBox(string.Format(API.GetTranslation("runtimePluginUnableToSetExecutablePath"), Language));
|
||||||
Log.Error("PluginsLoader",
|
Log.Error("PluginsLoader",
|
||||||
$"Not able to successfully set {EnvName} path, setting's plugin executable path variable is still an empty string.",
|
$"Not able to successfully set {EnvName} path, setting's plugin executable path variable is still an empty string.",
|
||||||
$"{Language}Environment");
|
$"{Language}Environment");
|
||||||
|
|
@ -98,13 +132,11 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
||||||
|
|
||||||
private void EnsureLatestInstalled(string expectedPath, string currentPath, string installedDirPath)
|
private void EnsureLatestInstalled(string expectedPath, string currentPath, string installedDirPath)
|
||||||
{
|
{
|
||||||
if (expectedPath == currentPath)
|
if (expectedPath == currentPath) return;
|
||||||
return;
|
|
||||||
|
|
||||||
FilesFolders.RemoveFolderIfExists(installedDirPath, (s) => API.ShowMsgBox(s));
|
FilesFolders.RemoveFolderIfExists(installedDirPath, (s) => API.ShowMsgBox(s));
|
||||||
|
|
||||||
InstallEnvironment();
|
InstallEnvironment();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal abstract PluginPair CreatePluginPair(string filePath, PluginMetadata metadata);
|
internal abstract PluginPair CreatePluginPair(string filePath, PluginMetadata metadata);
|
||||||
|
|
@ -116,13 +148,16 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
||||||
foreach (var metadata in PluginMetadataList)
|
foreach (var metadata in PluginMetadataList)
|
||||||
{
|
{
|
||||||
if (metadata.Language.Equals(languageToSet, StringComparison.OrdinalIgnoreCase))
|
if (metadata.Language.Equals(languageToSet, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
metadata.AssemblyName = string.Empty;
|
||||||
pluginPairs.Add(CreatePluginPair(filePath, metadata));
|
pluginPairs.Add(CreatePluginPair(filePath, metadata));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return pluginPairs;
|
return pluginPairs;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetFileFromDialog(string title, string filter = "")
|
private static string GetFileFromDialog(string title, string filter = "")
|
||||||
{
|
{
|
||||||
var dlg = new OpenFileDialog
|
var dlg = new OpenFileDialog
|
||||||
{
|
{
|
||||||
|
|
@ -136,7 +171,6 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
||||||
|
|
||||||
var result = dlg.ShowDialog();
|
var result = dlg.ShowDialog();
|
||||||
return result == DialogResult.OK ? dlg.FileName : string.Empty;
|
return result == DialogResult.OK ? dlg.FileName : string.Empty;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -179,31 +213,33 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (IsUsingPortablePath(settings.PluginSettings.PythonExecutablePath, DataLocation.PythonEnvironmentName))
|
if (IsUsingPortablePath(settings.PluginSettings.PythonExecutablePath, DataLocation.PythonEnvironmentName))
|
||||||
|
{
|
||||||
settings.PluginSettings.PythonExecutablePath
|
settings.PluginSettings.PythonExecutablePath
|
||||||
= GetUpdatedEnvironmentPath(settings.PluginSettings.PythonExecutablePath);
|
= GetUpdatedEnvironmentPath(settings.PluginSettings.PythonExecutablePath);
|
||||||
|
}
|
||||||
|
|
||||||
if (IsUsingPortablePath(settings.PluginSettings.NodeExecutablePath, DataLocation.NodeEnvironmentName))
|
if (IsUsingPortablePath(settings.PluginSettings.NodeExecutablePath, DataLocation.NodeEnvironmentName))
|
||||||
|
{
|
||||||
settings.PluginSettings.NodeExecutablePath
|
settings.PluginSettings.NodeExecutablePath
|
||||||
= GetUpdatedEnvironmentPath(settings.PluginSettings.NodeExecutablePath);
|
= GetUpdatedEnvironmentPath(settings.PluginSettings.NodeExecutablePath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsUsingPortablePath(string filePath, string pluginEnvironmentName)
|
private static bool IsUsingPortablePath(string filePath, string pluginEnvironmentName)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(filePath))
|
if (string.IsNullOrEmpty(filePath)) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
// DataLocation.PortableDataPath returns the current portable path, this determines if an out
|
// DataLocation.PortableDataPath returns the current portable path, this determines if an out
|
||||||
// of date path is also a portable path.
|
// of date path is also a portable path.
|
||||||
var portableAppEnvLocation = $"UserData\\{DataLocation.PluginEnvironments}\\{pluginEnvironmentName}";
|
var portableAppEnvLocation = Path.Combine("UserData", DataLocation.PluginEnvironments, pluginEnvironmentName);
|
||||||
|
|
||||||
return filePath.Contains(portableAppEnvLocation);
|
return filePath.Contains(portableAppEnvLocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsUsingRoamingPath(string filePath)
|
private static bool IsUsingRoamingPath(string filePath)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(filePath))
|
if (string.IsNullOrEmpty(filePath)) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
return filePath.StartsWith(DataLocation.RoamingDataPath);
|
return filePath.StartsWith(DataLocation.RoamingDataPath);
|
||||||
}
|
}
|
||||||
|
|
@ -213,8 +249,8 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
||||||
var index = filePath.IndexOf(DataLocation.PluginEnvironments);
|
var index = filePath.IndexOf(DataLocation.PluginEnvironments);
|
||||||
|
|
||||||
// get the substring after "Environments" because we can not determine it dynamically
|
// get the substring after "Environments" because we can not determine it dynamically
|
||||||
var ExecutablePathSubstring = filePath.Substring(index + DataLocation.PluginEnvironments.Count());
|
var executablePathSubstring = filePath[(index + DataLocation.PluginEnvironments.Length)..];
|
||||||
return $"{DataLocation.PluginEnvironmentsPath}{ExecutablePathSubstring}";
|
return $"{DataLocation.PluginEnvironmentsPath}{executablePathSubstring}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
using Droplex;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using Droplex;
|
||||||
using Flow.Launcher.Core.Plugin;
|
using Flow.Launcher.Core.Plugin;
|
||||||
using Flow.Launcher.Infrastructure.UserSettings;
|
using Flow.Launcher.Infrastructure.UserSettings;
|
||||||
using Flow.Launcher.Plugin;
|
using Flow.Launcher.Plugin;
|
||||||
using Flow.Launcher.Plugin.SharedCommands;
|
using Flow.Launcher.Plugin.SharedCommands;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
||||||
{
|
{
|
||||||
|
|
@ -22,7 +22,11 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
||||||
|
|
||||||
internal override string FileDialogFilter => "Python|pythonw.exe";
|
internal override string FileDialogFilter => "Python|pythonw.exe";
|
||||||
|
|
||||||
internal override string PluginsSettingsFilePath { get => PluginSettings.PythonExecutablePath; set => PluginSettings.PythonExecutablePath = value; }
|
internal override string PluginsSettingsFilePath
|
||||||
|
{
|
||||||
|
get => PluginSettings.PythonExecutablePath;
|
||||||
|
set => PluginSettings.PythonExecutablePath = value;
|
||||||
|
}
|
||||||
|
|
||||||
internal PythonEnvironment(List<PluginMetadata> pluginMetadataList, PluginsSettings pluginSettings) : base(pluginMetadataList, pluginSettings) { }
|
internal PythonEnvironment(List<PluginMetadata> pluginMetadataList, PluginsSettings pluginSettings) : base(pluginMetadataList, pluginSettings) { }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Droplex;
|
|
||||||
using Flow.Launcher.Infrastructure.UserSettings;
|
|
||||||
using Flow.Launcher.Plugin.SharedCommands;
|
|
||||||
using Flow.Launcher.Plugin;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using Droplex;
|
||||||
using Flow.Launcher.Core.Plugin;
|
using Flow.Launcher.Core.Plugin;
|
||||||
|
using Flow.Launcher.Infrastructure.UserSettings;
|
||||||
|
using Flow.Launcher.Plugin;
|
||||||
|
using Flow.Launcher.Plugin.SharedCommands;
|
||||||
|
|
||||||
namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
||||||
{
|
{
|
||||||
|
|
@ -19,7 +19,11 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
||||||
internal override string InstallPath => Path.Combine(EnvPath, "Node-v16.18.0");
|
internal override string InstallPath => Path.Combine(EnvPath, "Node-v16.18.0");
|
||||||
internal override string ExecutablePath => Path.Combine(InstallPath, "node-v16.18.0-win-x64\\node.exe");
|
internal override string ExecutablePath => Path.Combine(InstallPath, "node-v16.18.0-win-x64\\node.exe");
|
||||||
|
|
||||||
internal override string PluginsSettingsFilePath { get => PluginSettings.NodeExecutablePath; set => PluginSettings.NodeExecutablePath = value; }
|
internal override string PluginsSettingsFilePath
|
||||||
|
{
|
||||||
|
get => PluginSettings.NodeExecutablePath;
|
||||||
|
set => PluginSettings.NodeExecutablePath = value;
|
||||||
|
}
|
||||||
|
|
||||||
internal TypeScriptEnvironment(List<PluginMetadata> pluginMetadataList, PluginsSettings pluginSettings) : base(pluginMetadataList, pluginSettings) { }
|
internal TypeScriptEnvironment(List<PluginMetadata> pluginMetadataList, PluginsSettings pluginSettings) : base(pluginMetadataList, pluginSettings) { }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Droplex;
|
|
||||||
using Flow.Launcher.Infrastructure.UserSettings;
|
|
||||||
using Flow.Launcher.Plugin.SharedCommands;
|
|
||||||
using Flow.Launcher.Plugin;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using Droplex;
|
||||||
using Flow.Launcher.Core.Plugin;
|
using Flow.Launcher.Core.Plugin;
|
||||||
|
using Flow.Launcher.Infrastructure.UserSettings;
|
||||||
|
using Flow.Launcher.Plugin;
|
||||||
|
using Flow.Launcher.Plugin.SharedCommands;
|
||||||
|
|
||||||
namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
||||||
{
|
{
|
||||||
|
|
@ -19,7 +19,11 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
||||||
internal override string InstallPath => Path.Combine(EnvPath, "Node-v16.18.0");
|
internal override string InstallPath => Path.Combine(EnvPath, "Node-v16.18.0");
|
||||||
internal override string ExecutablePath => Path.Combine(InstallPath, "node-v16.18.0-win-x64\\node.exe");
|
internal override string ExecutablePath => Path.Combine(InstallPath, "node-v16.18.0-win-x64\\node.exe");
|
||||||
|
|
||||||
internal override string PluginsSettingsFilePath { get => PluginSettings.NodeExecutablePath; set => PluginSettings.NodeExecutablePath = value; }
|
internal override string PluginsSettingsFilePath
|
||||||
|
{
|
||||||
|
get => PluginSettings.NodeExecutablePath;
|
||||||
|
set => PluginSettings.NodeExecutablePath = value;
|
||||||
|
}
|
||||||
|
|
||||||
internal TypeScriptV2Environment(List<PluginMetadata> pluginMetadataList, PluginsSettings pluginSettings) : base(pluginMetadataList, pluginSettings) { }
|
internal TypeScriptV2Environment(List<PluginMetadata> pluginMetadataList, PluginsSettings pluginSettings) : base(pluginMetadataList, pluginSettings) { }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,16 @@
|
||||||
using Flow.Launcher.Core.Resource;
|
using Flow.Launcher.Core.Resource;
|
||||||
using Flow.Launcher.Infrastructure;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Flow.Launcher.Infrastructure.Logger;
|
using Flow.Launcher.Infrastructure.Logger;
|
||||||
using Flow.Launcher.Infrastructure.UserSettings;
|
|
||||||
using Flow.Launcher.Plugin;
|
using Flow.Launcher.Plugin;
|
||||||
using Microsoft.IO;
|
using Microsoft.IO;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
|
||||||
using YamlDotNet.Serialization;
|
|
||||||
using YamlDotNet.Serialization.NamingConventions;
|
|
||||||
using CheckBox = System.Windows.Controls.CheckBox;
|
|
||||||
using Control = System.Windows.Controls.Control;
|
|
||||||
using Orientation = System.Windows.Controls.Orientation;
|
|
||||||
using TextBox = System.Windows.Controls.TextBox;
|
|
||||||
using UserControl = System.Windows.Controls.UserControl;
|
|
||||||
using System.Windows.Documents;
|
|
||||||
|
|
||||||
namespace Flow.Launcher.Core.Plugin
|
namespace Flow.Launcher.Core.Plugin
|
||||||
{
|
{
|
||||||
|
|
@ -42,7 +30,7 @@ namespace Flow.Launcher.Core.Plugin
|
||||||
private int RequestId { get; set; }
|
private int RequestId { get; set; }
|
||||||
|
|
||||||
private string SettingConfigurationPath => Path.Combine(Context.CurrentPluginMetadata.PluginDirectory, "SettingsTemplate.yaml");
|
private string SettingConfigurationPath => Path.Combine(Context.CurrentPluginMetadata.PluginDirectory, "SettingsTemplate.yaml");
|
||||||
private string SettingPath => Path.Combine(DataLocation.PluginSettingsDirectory, Context.CurrentPluginMetadata.Name, "Settings.json");
|
private string SettingPath => Path.Combine(Context.CurrentPluginMetadata.PluginSettingsDirectoryPath, "Settings.json");
|
||||||
|
|
||||||
public override List<Result> LoadContextMenus(Result selectedResult)
|
public override List<Result> LoadContextMenus(Result selectedResult)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,15 @@
|
||||||
using Flow.Launcher.Core.Resource;
|
using Flow.Launcher.Core.Resource;
|
||||||
using Flow.Launcher.Infrastructure;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Flow.Launcher.Infrastructure.Logger;
|
|
||||||
using Flow.Launcher.Infrastructure.UserSettings;
|
|
||||||
using Flow.Launcher.Plugin;
|
using Flow.Launcher.Plugin;
|
||||||
using Microsoft.IO;
|
|
||||||
using System.Windows;
|
|
||||||
using System.Windows.Controls;
|
|
||||||
using YamlDotNet.Serialization;
|
using YamlDotNet.Serialization;
|
||||||
using YamlDotNet.Serialization.NamingConventions;
|
using YamlDotNet.Serialization.NamingConventions;
|
||||||
using CheckBox = System.Windows.Controls.CheckBox;
|
|
||||||
using Control = System.Windows.Controls.Control;
|
using Control = System.Windows.Controls.Control;
|
||||||
using Orientation = System.Windows.Controls.Orientation;
|
|
||||||
using TextBox = System.Windows.Controls.TextBox;
|
|
||||||
using UserControl = System.Windows.Controls.UserControl;
|
|
||||||
using System.Windows.Documents;
|
|
||||||
using static System.Windows.Forms.LinkLabel;
|
|
||||||
using Droplex;
|
|
||||||
using System.Windows.Forms;
|
|
||||||
using Microsoft.VisualStudio.Threading;
|
|
||||||
|
|
||||||
namespace Flow.Launcher.Core.Plugin
|
namespace Flow.Launcher.Core.Plugin
|
||||||
{
|
{
|
||||||
|
|
@ -44,8 +27,7 @@ namespace Flow.Launcher.Core.Plugin
|
||||||
private string SettingConfigurationPath =>
|
private string SettingConfigurationPath =>
|
||||||
Path.Combine(Context.CurrentPluginMetadata.PluginDirectory, "SettingsTemplate.yaml");
|
Path.Combine(Context.CurrentPluginMetadata.PluginDirectory, "SettingsTemplate.yaml");
|
||||||
|
|
||||||
private string SettingDirectory => Path.Combine(DataLocation.PluginSettingsDirectory,
|
private string SettingDirectory => Context.CurrentPluginMetadata.PluginSettingsDirectoryPath;
|
||||||
Context.CurrentPluginMetadata.Name);
|
|
||||||
|
|
||||||
private string SettingPath => Path.Combine(SettingDirectory, "Settings.json");
|
private string SettingPath => Path.Combine(SettingDirectory, "Settings.json");
|
||||||
|
|
||||||
|
|
@ -166,13 +148,5 @@ namespace Flow.Launcher.Core.Plugin
|
||||||
{
|
{
|
||||||
return Settings.CreateSettingPanel();
|
return Settings.CreateSettingPanel();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DeletePluginSettingsDirectory()
|
|
||||||
{
|
|
||||||
if (Directory.Exists(SettingDirectory))
|
|
||||||
{
|
|
||||||
Directory.Delete(SettingDirectory, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,14 @@
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Windows.Documents;
|
using System.Windows.Documents;
|
||||||
using System.Windows.Forms;
|
|
||||||
using Flow.Launcher.Infrastructure.Storage;
|
using Flow.Launcher.Infrastructure.Storage;
|
||||||
using Flow.Launcher.Plugin;
|
using Flow.Launcher.Plugin;
|
||||||
using CheckBox = System.Windows.Controls.CheckBox;
|
|
||||||
using ComboBox = System.Windows.Controls.ComboBox;
|
#nullable enable
|
||||||
using Control = System.Windows.Controls.Control;
|
|
||||||
using Orientation = System.Windows.Controls.Orientation;
|
|
||||||
using TextBox = System.Windows.Controls.TextBox;
|
|
||||||
using UserControl = System.Windows.Controls.UserControl;
|
|
||||||
|
|
||||||
namespace Flow.Launcher.Core.Plugin
|
namespace Flow.Launcher.Core.Plugin
|
||||||
{
|
{
|
||||||
|
|
@ -23,51 +19,72 @@ namespace Flow.Launcher.Core.Plugin
|
||||||
public required string SettingPath { get; init; }
|
public required string SettingPath { get; init; }
|
||||||
public Dictionary<string, FrameworkElement> SettingControls { get; } = new();
|
public Dictionary<string, FrameworkElement> SettingControls { get; } = new();
|
||||||
|
|
||||||
public IReadOnlyDictionary<string, object> Inner => Settings;
|
public IReadOnlyDictionary<string, object?> Inner => Settings;
|
||||||
protected ConcurrentDictionary<string, object> Settings { get; set; }
|
protected ConcurrentDictionary<string, object?> Settings { get; set; } = null!;
|
||||||
public required IPublicAPI API { get; init; }
|
public required IPublicAPI API { get; init; }
|
||||||
|
|
||||||
private JsonStorage<ConcurrentDictionary<string, object>> _storage;
|
private JsonStorage<ConcurrentDictionary<string, object?>> _storage = null!;
|
||||||
|
|
||||||
// maybe move to resource?
|
private static readonly Thickness SettingPanelMargin = (Thickness)Application.Current.FindResource("SettingPanelMargin");
|
||||||
private static readonly Thickness settingControlMargin = new(0, 9, 18, 9);
|
private static readonly Thickness SettingPanelItemLeftMargin = (Thickness)Application.Current.FindResource("SettingPanelItemLeftMargin");
|
||||||
private static readonly Thickness settingCheckboxMargin = new(0, 9, 9, 9);
|
private static readonly Thickness SettingPanelItemTopBottomMargin = (Thickness)Application.Current.FindResource("SettingPanelItemTopBottomMargin");
|
||||||
private static readonly Thickness settingPanelMargin = new(0, 0, 0, 0);
|
private static readonly Thickness SettingPanelItemLeftTopBottomMargin = (Thickness)Application.Current.FindResource("SettingPanelItemLeftTopBottomMargin");
|
||||||
private static readonly Thickness settingTextBlockMargin = new(70, 9, 18, 9);
|
private static readonly double SettingPanelTextBoxMinWidth = (double)Application.Current.FindResource("SettingPanelTextBoxMinWidth");
|
||||||
private static readonly Thickness settingLabelPanelMargin = new(70, 9, 18, 9);
|
private static readonly double SettingPanelPathTextBoxWidth = (double)Application.Current.FindResource("SettingPanelPathTextBoxWidth");
|
||||||
private static readonly Thickness settingLabelMargin = new(0, 0, 0, 0);
|
private static readonly double SettingPanelAreaTextBoxMinHeight = (double)Application.Current.FindResource("SettingPanelAreaTextBoxMinHeight");
|
||||||
private static readonly Thickness settingDescMargin = new(0, 2, 0, 0);
|
|
||||||
private static readonly Thickness settingSepMargin = new(0, 0, 0, 2);
|
|
||||||
|
|
||||||
public async Task InitializeAsync()
|
public async Task InitializeAsync()
|
||||||
{
|
{
|
||||||
_storage = new JsonStorage<ConcurrentDictionary<string, object>>(SettingPath);
|
if (Settings == null)
|
||||||
Settings = await _storage.LoadAsync();
|
|
||||||
|
|
||||||
if (Configuration == null)
|
|
||||||
{
|
{
|
||||||
return;
|
_storage = new JsonStorage<ConcurrentDictionary<string, object?>>(SettingPath);
|
||||||
|
Settings = await _storage.LoadAsync();
|
||||||
|
|
||||||
|
// Because value type of settings dictionary is object which causes them to be JsonElement when loading from json files,
|
||||||
|
// we need to convert it to the correct type
|
||||||
|
foreach (var (key, value) in Settings)
|
||||||
|
{
|
||||||
|
if (value is not JsonElement jsonElement) continue;
|
||||||
|
|
||||||
|
Settings[key] = jsonElement.ValueKind switch
|
||||||
|
{
|
||||||
|
JsonValueKind.String => jsonElement.GetString() ?? value,
|
||||||
|
JsonValueKind.True => jsonElement.GetBoolean(),
|
||||||
|
JsonValueKind.False => jsonElement.GetBoolean(),
|
||||||
|
JsonValueKind.Null => null,
|
||||||
|
_ => value
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Configuration == null) return;
|
||||||
|
|
||||||
foreach (var (type, attributes) in Configuration.Body)
|
foreach (var (type, attributes) in Configuration.Body)
|
||||||
{
|
{
|
||||||
if (attributes.Name == null)
|
// Skip if the setting does not have attributes or name
|
||||||
{
|
if (attributes?.Name == null) continue;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Settings.ContainsKey(attributes.Name))
|
// Skip if the setting does not have attributes or name
|
||||||
|
if (!NeedSaveInSettings(type)) continue;
|
||||||
|
|
||||||
|
// If need save in settings, we need to make sure the setting exists in the settings file
|
||||||
|
if (Settings.ContainsKey(attributes.Name)) continue;
|
||||||
|
|
||||||
|
if (type == "checkbox")
|
||||||
|
{
|
||||||
|
// If can parse the default value to bool, use it, otherwise use false
|
||||||
|
Settings[attributes.Name] = bool.TryParse(attributes.DefaultValue, out var value) && value;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
Settings[attributes.Name] = attributes.DefaultValue;
|
Settings[attributes.Name] = attributes.DefaultValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void UpdateSettings(IReadOnlyDictionary<string, object> settings)
|
public void UpdateSettings(IReadOnlyDictionary<string, object> settings)
|
||||||
{
|
{
|
||||||
if (settings == null || settings.Count == 0)
|
if (settings == null || settings.Count == 0) return;
|
||||||
return;
|
|
||||||
|
|
||||||
foreach (var (key, value) in settings)
|
foreach (var (key, value) in settings)
|
||||||
{
|
{
|
||||||
|
|
@ -78,19 +95,23 @@ namespace Flow.Launcher.Core.Plugin
|
||||||
switch (control)
|
switch (control)
|
||||||
{
|
{
|
||||||
case TextBox textBox:
|
case TextBox textBox:
|
||||||
textBox.Dispatcher.Invoke(() => textBox.Text = value as string ?? string.Empty);
|
var text = value as string ?? string.Empty;
|
||||||
|
textBox.Dispatcher.Invoke(() => textBox.Text = text);
|
||||||
break;
|
break;
|
||||||
case PasswordBox passwordBox:
|
case PasswordBox passwordBox:
|
||||||
passwordBox.Dispatcher.Invoke(() => passwordBox.Password = value as string ?? string.Empty);
|
var password = value as string ?? string.Empty;
|
||||||
|
passwordBox.Dispatcher.Invoke(() => passwordBox.Password = password);
|
||||||
break;
|
break;
|
||||||
case ComboBox comboBox:
|
case ComboBox comboBox:
|
||||||
comboBox.Dispatcher.Invoke(() => comboBox.SelectedItem = value);
|
comboBox.Dispatcher.Invoke(() => comboBox.SelectedItem = value);
|
||||||
break;
|
break;
|
||||||
case CheckBox checkBox:
|
case CheckBox checkBox:
|
||||||
checkBox.Dispatcher.Invoke(() =>
|
var isChecked = value is bool boolValue
|
||||||
checkBox.IsChecked = value is bool isChecked
|
? boolValue
|
||||||
? isChecked
|
// If can parse the default value to bool, use it, otherwise use false
|
||||||
: bool.Parse(value as string ?? string.Empty));
|
: value is string stringValue && bool.TryParse(stringValue, out var boolValueFromString)
|
||||||
|
&& boolValueFromString;
|
||||||
|
checkBox.Dispatcher.Invoke(() =>checkBox.IsChecked = isChecked);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -108,7 +129,7 @@ namespace Flow.Launcher.Core.Plugin
|
||||||
{
|
{
|
||||||
_storage.Save();
|
_storage.Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool NeedCreateSettingPanel()
|
public bool NeedCreateSettingPanel()
|
||||||
{
|
{
|
||||||
// If there are no settings or the settings configuration is empty, return null
|
// If there are no settings or the settings configuration is empty, return null
|
||||||
|
|
@ -118,329 +139,350 @@ namespace Flow.Launcher.Core.Plugin
|
||||||
public Control CreateSettingPanel()
|
public Control CreateSettingPanel()
|
||||||
{
|
{
|
||||||
// No need to check if NeedCreateSettingPanel is true because CreateSettingPanel will only be called if it's true
|
// No need to check if NeedCreateSettingPanel is true because CreateSettingPanel will only be called if it's true
|
||||||
|
// if (!NeedCreateSettingPanel()) return null;
|
||||||
|
|
||||||
var settingWindow = new UserControl();
|
// Create main grid with two columns (Column 1: Auto, Column 2: *)
|
||||||
var mainPanel = new Grid { Margin = settingPanelMargin, VerticalAlignment = VerticalAlignment.Center };
|
var mainPanel = new Grid { Margin = SettingPanelMargin, VerticalAlignment = VerticalAlignment.Center };
|
||||||
|
mainPanel.ColumnDefinitions.Add(new ColumnDefinition()
|
||||||
ColumnDefinition gridCol1 = new ColumnDefinition();
|
|
||||||
ColumnDefinition gridCol2 = new ColumnDefinition();
|
|
||||||
|
|
||||||
gridCol1.Width = new GridLength(70, GridUnitType.Star);
|
|
||||||
gridCol2.Width = new GridLength(30, GridUnitType.Star);
|
|
||||||
mainPanel.ColumnDefinitions.Add(gridCol1);
|
|
||||||
mainPanel.ColumnDefinitions.Add(gridCol2);
|
|
||||||
settingWindow.Content = mainPanel;
|
|
||||||
int rowCount = 0;
|
|
||||||
|
|
||||||
foreach (var (type, attribute) in Configuration.Body)
|
|
||||||
{
|
{
|
||||||
Separator sep = new Separator();
|
Width = new GridLength(0, GridUnitType.Auto)
|
||||||
sep.VerticalAlignment = VerticalAlignment.Top;
|
});
|
||||||
sep.Margin = settingSepMargin;
|
mainPanel.ColumnDefinitions.Add(new ColumnDefinition()
|
||||||
sep.SetResourceReference(Separator.BackgroundProperty, "Color03B"); /* for theme change */
|
{
|
||||||
var panel = new StackPanel
|
Width = new GridLength(1, GridUnitType.Star)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Iterate over each setting and create one row for it
|
||||||
|
var rowCount = 0;
|
||||||
|
foreach (var (type, attributes) in Configuration!.Body)
|
||||||
|
{
|
||||||
|
// Skip if the setting does not have attributes or name
|
||||||
|
if (attributes?.Name == null) continue;
|
||||||
|
|
||||||
|
// Add a new row to the main grid
|
||||||
|
mainPanel.RowDefinitions.Add(new RowDefinition()
|
||||||
{
|
{
|
||||||
Orientation = Orientation.Vertical,
|
Height = new GridLength(0, GridUnitType.Auto)
|
||||||
VerticalAlignment = VerticalAlignment.Center,
|
});
|
||||||
Margin = settingLabelPanelMargin
|
|
||||||
};
|
|
||||||
|
|
||||||
RowDefinition gridRow = new RowDefinition();
|
|
||||||
mainPanel.RowDefinitions.Add(gridRow);
|
|
||||||
var name = new TextBlock()
|
|
||||||
{
|
|
||||||
Text = attribute.Label,
|
|
||||||
VerticalAlignment = VerticalAlignment.Center,
|
|
||||||
Margin = settingLabelMargin,
|
|
||||||
TextWrapping = TextWrapping.WrapWithOverflow
|
|
||||||
};
|
|
||||||
|
|
||||||
var desc = new TextBlock()
|
|
||||||
{
|
|
||||||
Text = attribute.Description,
|
|
||||||
FontSize = 12,
|
|
||||||
VerticalAlignment = VerticalAlignment.Center,
|
|
||||||
Margin = settingDescMargin,
|
|
||||||
TextWrapping = TextWrapping.WrapWithOverflow
|
|
||||||
};
|
|
||||||
|
|
||||||
desc.SetResourceReference(TextBlock.ForegroundProperty, "Color04B");
|
|
||||||
|
|
||||||
if (attribute.Description == null) /* if no description, hide */
|
|
||||||
desc.Visibility = Visibility.Collapsed;
|
|
||||||
|
|
||||||
|
|
||||||
if (type != "textBlock") /* if textBlock, hide desc */
|
|
||||||
{
|
|
||||||
panel.Children.Add(name);
|
|
||||||
panel.Children.Add(desc);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Grid.SetColumn(panel, 0);
|
|
||||||
Grid.SetRow(panel, rowCount);
|
|
||||||
|
|
||||||
|
// State controls for column 0 and 1
|
||||||
|
StackPanel? panel = null;
|
||||||
FrameworkElement contentControl;
|
FrameworkElement contentControl;
|
||||||
|
|
||||||
|
// If the type is textBlock, separator, or checkbox, we do not need to create a panel
|
||||||
|
if (type != "textBlock" && type != "separator" && type != "checkbox")
|
||||||
|
{
|
||||||
|
// Create a panel to hold the label and description
|
||||||
|
panel = new StackPanel
|
||||||
|
{
|
||||||
|
Margin = SettingPanelItemTopBottomMargin,
|
||||||
|
Orientation = Orientation.Vertical,
|
||||||
|
VerticalAlignment = VerticalAlignment.Center
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create a text block for name
|
||||||
|
var name = new TextBlock()
|
||||||
|
{
|
||||||
|
Text = attributes.Label,
|
||||||
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
|
TextWrapping = TextWrapping.WrapWithOverflow
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create a text block for description
|
||||||
|
TextBlock? desc = null;
|
||||||
|
if (attributes.Description != null)
|
||||||
|
{
|
||||||
|
desc = new TextBlock()
|
||||||
|
{
|
||||||
|
Text = attributes.Description,
|
||||||
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
|
TextWrapping = TextWrapping.WrapWithOverflow
|
||||||
|
};
|
||||||
|
|
||||||
|
desc.SetResourceReference(TextBlock.StyleProperty, "SettingPanelTextBlockDescriptionStyle"); // for theme change
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the name and description to the panel
|
||||||
|
panel.Children.Add(name);
|
||||||
|
if (desc != null) panel.Children.Add(desc);
|
||||||
|
}
|
||||||
|
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case "textBlock":
|
case "textBlock":
|
||||||
{
|
|
||||||
contentControl = new TextBlock
|
|
||||||
{
|
{
|
||||||
Text = attribute.Description.Replace("\\r\\n", "\r\n"),
|
contentControl = new TextBlock
|
||||||
Margin = settingTextBlockMargin,
|
{
|
||||||
Padding = new Thickness(0, 0, 0, 0),
|
Text = attributes.Description?.Replace("\\r\\n", "\r\n") ?? string.Empty,
|
||||||
HorizontalAlignment = System.Windows.HorizontalAlignment.Left,
|
HorizontalAlignment = HorizontalAlignment.Left,
|
||||||
TextAlignment = TextAlignment.Left,
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
TextWrapping = TextWrapping.Wrap
|
Margin = SettingPanelItemTopBottomMargin,
|
||||||
};
|
TextAlignment = TextAlignment.Left,
|
||||||
|
TextWrapping = TextWrapping.Wrap
|
||||||
|
};
|
||||||
|
|
||||||
Grid.SetColumn(contentControl, 0);
|
break;
|
||||||
Grid.SetColumnSpan(contentControl, 2);
|
}
|
||||||
Grid.SetRow(contentControl, rowCount);
|
|
||||||
if (rowCount != 0)
|
|
||||||
mainPanel.Children.Add(sep);
|
|
||||||
|
|
||||||
Grid.SetRow(sep, rowCount);
|
|
||||||
Grid.SetColumn(sep, 0);
|
|
||||||
Grid.SetColumnSpan(sep, 2);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "input":
|
case "input":
|
||||||
{
|
|
||||||
var textBox = new TextBox()
|
|
||||||
{
|
{
|
||||||
Text = Settings[attribute.Name] as string ?? string.Empty,
|
var textBox = new TextBox()
|
||||||
Margin = settingControlMargin,
|
{
|
||||||
HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch,
|
MinWidth = SettingPanelTextBoxMinWidth,
|
||||||
ToolTip = attribute.Description
|
HorizontalAlignment = HorizontalAlignment.Left,
|
||||||
};
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
|
Margin = SettingPanelItemLeftTopBottomMargin,
|
||||||
|
Text = Settings[attributes.Name] as string ?? string.Empty,
|
||||||
|
ToolTip = attributes.Description
|
||||||
|
};
|
||||||
|
|
||||||
textBox.TextChanged += (_, _) =>
|
textBox.TextChanged += (_, _) =>
|
||||||
{
|
{
|
||||||
Settings[attribute.Name] = textBox.Text;
|
Settings[attributes.Name] = textBox.Text;
|
||||||
};
|
};
|
||||||
|
|
||||||
contentControl = textBox;
|
contentControl = textBox;
|
||||||
Grid.SetColumn(contentControl, 1);
|
|
||||||
Grid.SetRow(contentControl, rowCount);
|
|
||||||
if (rowCount != 0)
|
|
||||||
mainPanel.Children.Add(sep);
|
|
||||||
|
|
||||||
Grid.SetRow(sep, rowCount);
|
break;
|
||||||
Grid.SetColumn(sep, 0);
|
}
|
||||||
Grid.SetColumnSpan(sep, 2);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "inputWithFileBtn":
|
case "inputWithFileBtn":
|
||||||
case "inputWithFolderBtn":
|
case "inputWithFolderBtn":
|
||||||
{
|
|
||||||
var textBox = new TextBox()
|
|
||||||
{
|
{
|
||||||
Margin = new Thickness(10, 0, 0, 0),
|
var textBox = new TextBox()
|
||||||
Text = Settings[attribute.Name] as string ?? string.Empty,
|
|
||||||
HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch,
|
|
||||||
ToolTip = attribute.Description
|
|
||||||
};
|
|
||||||
|
|
||||||
textBox.TextChanged += (_, _) =>
|
|
||||||
{
|
|
||||||
Settings[attribute.Name] = textBox.Text;
|
|
||||||
};
|
|
||||||
|
|
||||||
var Btn = new System.Windows.Controls.Button()
|
|
||||||
{
|
|
||||||
Margin = new Thickness(10, 0, 0, 0), Content = "Browse"
|
|
||||||
};
|
|
||||||
|
|
||||||
Btn.Click += (_, _) =>
|
|
||||||
{
|
|
||||||
using CommonDialog dialog = type switch
|
|
||||||
{
|
{
|
||||||
"inputWithFolderBtn" => new FolderBrowserDialog(),
|
Width = SettingPanelPathTextBoxWidth,
|
||||||
_ => new OpenFileDialog(),
|
HorizontalAlignment = HorizontalAlignment.Left,
|
||||||
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
|
Margin = SettingPanelItemLeftMargin,
|
||||||
|
Text = Settings[attributes.Name] as string ?? string.Empty,
|
||||||
|
ToolTip = attributes.Description
|
||||||
};
|
};
|
||||||
if (dialog.ShowDialog() != DialogResult.OK) return;
|
|
||||||
|
|
||||||
var path = dialog switch
|
textBox.TextChanged += (_, _) =>
|
||||||
{
|
{
|
||||||
FolderBrowserDialog folderDialog => folderDialog.SelectedPath,
|
Settings[attributes.Name] = textBox.Text;
|
||||||
OpenFileDialog fileDialog => fileDialog.FileName,
|
|
||||||
};
|
};
|
||||||
textBox.Text = path;
|
|
||||||
Settings[attribute.Name] = path;
|
|
||||||
};
|
|
||||||
|
|
||||||
var dockPanel = new DockPanel() { Margin = settingControlMargin };
|
var Btn = new Button()
|
||||||
|
{
|
||||||
|
HorizontalAlignment = HorizontalAlignment.Left,
|
||||||
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
|
Margin = SettingPanelItemLeftMargin,
|
||||||
|
Content = API.GetTranslation("select")
|
||||||
|
};
|
||||||
|
|
||||||
DockPanel.SetDock(Btn, Dock.Right);
|
Btn.Click += (_, _) =>
|
||||||
dockPanel.Children.Add(Btn);
|
{
|
||||||
dockPanel.Children.Add(textBox);
|
using System.Windows.Forms.CommonDialog dialog = type switch
|
||||||
contentControl = dockPanel;
|
{
|
||||||
Grid.SetColumn(contentControl, 1);
|
"inputWithFolderBtn" => new System.Windows.Forms.FolderBrowserDialog(),
|
||||||
Grid.SetRow(contentControl, rowCount);
|
_ => new System.Windows.Forms.OpenFileDialog(),
|
||||||
if (rowCount != 0)
|
};
|
||||||
mainPanel.Children.Add(sep);
|
|
||||||
|
|
||||||
Grid.SetRow(sep, rowCount);
|
if (dialog.ShowDialog() != System.Windows.Forms.DialogResult.OK)
|
||||||
Grid.SetColumn(sep, 0);
|
{
|
||||||
Grid.SetColumnSpan(sep, 2);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
var path = dialog switch
|
||||||
}
|
{
|
||||||
|
System.Windows.Forms.FolderBrowserDialog folderDialog => folderDialog.SelectedPath,
|
||||||
|
System.Windows.Forms.OpenFileDialog fileDialog => fileDialog.FileName,
|
||||||
|
_ => throw new System.NotImplementedException()
|
||||||
|
};
|
||||||
|
|
||||||
|
textBox.Text = path;
|
||||||
|
Settings[attributes.Name] = path;
|
||||||
|
};
|
||||||
|
|
||||||
|
var stackPanel = new StackPanel()
|
||||||
|
{
|
||||||
|
HorizontalAlignment = HorizontalAlignment.Left,
|
||||||
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
|
Margin = SettingPanelItemTopBottomMargin,
|
||||||
|
Orientation = Orientation.Horizontal
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create a stack panel to wrap the button and text box
|
||||||
|
stackPanel.Children.Add(textBox);
|
||||||
|
stackPanel.Children.Add(Btn);
|
||||||
|
|
||||||
|
contentControl = stackPanel;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
case "textarea":
|
case "textarea":
|
||||||
{
|
|
||||||
var textBox = new TextBox()
|
|
||||||
{
|
{
|
||||||
Height = 120,
|
var textBox = new TextBox()
|
||||||
Margin = settingControlMargin,
|
{
|
||||||
VerticalAlignment = VerticalAlignment.Center,
|
MinHeight = SettingPanelAreaTextBoxMinHeight,
|
||||||
TextWrapping = TextWrapping.WrapWithOverflow,
|
HorizontalAlignment = HorizontalAlignment.Stretch,
|
||||||
AcceptsReturn = true,
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch,
|
Margin = SettingPanelItemLeftTopBottomMargin,
|
||||||
Text = Settings[attribute.Name] as string ?? string.Empty,
|
TextWrapping = TextWrapping.WrapWithOverflow,
|
||||||
ToolTip = attribute.Description
|
AcceptsReturn = true,
|
||||||
};
|
Text = Settings[attributes.Name] as string ?? string.Empty,
|
||||||
|
ToolTip = attributes.Description
|
||||||
|
};
|
||||||
|
|
||||||
textBox.TextChanged += (sender, _) =>
|
textBox.TextChanged += (sender, _) =>
|
||||||
{
|
{
|
||||||
Settings[attribute.Name] = ((TextBox)sender).Text;
|
Settings[attributes.Name] = ((TextBox)sender).Text;
|
||||||
};
|
};
|
||||||
|
|
||||||
contentControl = textBox;
|
contentControl = textBox;
|
||||||
Grid.SetColumn(contentControl, 1);
|
|
||||||
Grid.SetRow(contentControl, rowCount);
|
|
||||||
if (rowCount != 0)
|
|
||||||
mainPanel.Children.Add(sep);
|
|
||||||
|
|
||||||
Grid.SetRow(sep, rowCount);
|
break;
|
||||||
Grid.SetColumn(sep, 0);
|
}
|
||||||
Grid.SetColumnSpan(sep, 2);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "passwordBox":
|
case "passwordBox":
|
||||||
{
|
|
||||||
var passwordBox = new PasswordBox()
|
|
||||||
{
|
{
|
||||||
Margin = settingControlMargin,
|
var passwordBox = new PasswordBox()
|
||||||
Password = Settings[attribute.Name] as string ?? string.Empty,
|
{
|
||||||
PasswordChar = attribute.passwordChar == default ? '*' : attribute.passwordChar,
|
MinWidth = SettingPanelTextBoxMinWidth,
|
||||||
HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch,
|
HorizontalAlignment = HorizontalAlignment.Left,
|
||||||
ToolTip = attribute.Description
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
};
|
Margin = SettingPanelItemLeftTopBottomMargin,
|
||||||
|
Password = Settings[attributes.Name] as string ?? string.Empty,
|
||||||
|
PasswordChar = attributes.passwordChar == default ? '*' : attributes.passwordChar,
|
||||||
|
ToolTip = attributes.Description,
|
||||||
|
};
|
||||||
|
|
||||||
passwordBox.PasswordChanged += (sender, _) =>
|
passwordBox.PasswordChanged += (sender, _) =>
|
||||||
{
|
{
|
||||||
Settings[attribute.Name] = ((PasswordBox)sender).Password;
|
Settings[attributes.Name] = ((PasswordBox)sender).Password;
|
||||||
};
|
};
|
||||||
|
|
||||||
contentControl = passwordBox;
|
contentControl = passwordBox;
|
||||||
Grid.SetColumn(contentControl, 1);
|
|
||||||
Grid.SetRow(contentControl, rowCount);
|
|
||||||
if (rowCount != 0)
|
|
||||||
mainPanel.Children.Add(sep);
|
|
||||||
|
|
||||||
Grid.SetRow(sep, rowCount);
|
break;
|
||||||
Grid.SetColumn(sep, 0);
|
}
|
||||||
Grid.SetColumnSpan(sep, 2);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "dropdown":
|
case "dropdown":
|
||||||
{
|
|
||||||
var comboBox = new System.Windows.Controls.ComboBox()
|
|
||||||
{
|
{
|
||||||
ItemsSource = attribute.Options,
|
var comboBox = new ComboBox()
|
||||||
SelectedItem = Settings[attribute.Name],
|
{
|
||||||
Margin = settingControlMargin,
|
ItemsSource = attributes.Options,
|
||||||
HorizontalAlignment = System.Windows.HorizontalAlignment.Right,
|
SelectedItem = Settings[attributes.Name],
|
||||||
ToolTip = attribute.Description
|
HorizontalAlignment = HorizontalAlignment.Left,
|
||||||
};
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
|
Margin = SettingPanelItemLeftTopBottomMargin,
|
||||||
|
ToolTip = attributes.Description
|
||||||
|
};
|
||||||
|
|
||||||
comboBox.SelectionChanged += (sender, _) =>
|
comboBox.SelectionChanged += (sender, _) =>
|
||||||
{
|
{
|
||||||
Settings[attribute.Name] = (string)((System.Windows.Controls.ComboBox)sender).SelectedItem;
|
Settings[attributes.Name] = (string)((ComboBox)sender).SelectedItem;
|
||||||
};
|
};
|
||||||
|
|
||||||
contentControl = comboBox;
|
contentControl = comboBox;
|
||||||
Grid.SetColumn(contentControl, 1);
|
|
||||||
Grid.SetRow(contentControl, rowCount);
|
|
||||||
if (rowCount != 0)
|
|
||||||
mainPanel.Children.Add(sep);
|
|
||||||
|
|
||||||
Grid.SetRow(sep, rowCount);
|
break;
|
||||||
Grid.SetColumn(sep, 0);
|
}
|
||||||
Grid.SetColumnSpan(sep, 2);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "checkbox":
|
case "checkbox":
|
||||||
var checkBox = new CheckBox
|
|
||||||
{
|
{
|
||||||
IsChecked =
|
// If can parse the default value to bool, use it, otherwise use false
|
||||||
Settings[attribute.Name] is bool isChecked
|
var defaultValue = bool.TryParse(attributes.DefaultValue, out var value) && value;
|
||||||
|
var checkBox = new CheckBox
|
||||||
|
{
|
||||||
|
IsChecked =
|
||||||
|
Settings[attributes.Name] is bool isChecked
|
||||||
? isChecked
|
? isChecked
|
||||||
: bool.Parse(attribute.DefaultValue),
|
: defaultValue,
|
||||||
Margin = settingCheckboxMargin,
|
HorizontalAlignment = HorizontalAlignment.Left,
|
||||||
HorizontalAlignment = System.Windows.HorizontalAlignment.Right,
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
ToolTip = attribute.Description
|
Margin = SettingPanelItemTopBottomMargin,
|
||||||
};
|
Content = attributes.Label,
|
||||||
|
ToolTip = attributes.Description
|
||||||
|
};
|
||||||
|
|
||||||
checkBox.Click += (sender, _) =>
|
checkBox.Click += (sender, _) =>
|
||||||
{
|
{
|
||||||
Settings[attribute.Name] = ((CheckBox)sender).IsChecked;
|
Settings[attributes.Name] = ((CheckBox)sender).IsChecked ?? defaultValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
contentControl = checkBox;
|
contentControl = checkBox;
|
||||||
Grid.SetColumn(contentControl, 1);
|
|
||||||
Grid.SetRow(contentControl, rowCount);
|
|
||||||
if (rowCount != 0)
|
|
||||||
mainPanel.Children.Add(sep);
|
|
||||||
|
|
||||||
Grid.SetRow(sep, rowCount);
|
break;
|
||||||
Grid.SetColumn(sep, 0);
|
}
|
||||||
Grid.SetColumnSpan(sep, 2);
|
|
||||||
|
|
||||||
break;
|
|
||||||
case "hyperlink":
|
case "hyperlink":
|
||||||
var hyperlink = new Hyperlink { ToolTip = attribute.Description, NavigateUri = attribute.url };
|
|
||||||
|
|
||||||
var linkbtn = new System.Windows.Controls.Button
|
|
||||||
{
|
{
|
||||||
HorizontalAlignment = System.Windows.HorizontalAlignment.Right,
|
var hyperlink = new Hyperlink
|
||||||
Margin = settingControlMargin
|
{
|
||||||
};
|
ToolTip = attributes.Description,
|
||||||
|
NavigateUri = attributes.url
|
||||||
|
};
|
||||||
|
|
||||||
linkbtn.Content = attribute.urlLabel;
|
hyperlink.Inlines.Add(attributes.urlLabel);
|
||||||
|
hyperlink.RequestNavigate += (sender, e) =>
|
||||||
|
{
|
||||||
|
API.OpenUrl(e.Uri);
|
||||||
|
e.Handled = true;
|
||||||
|
};
|
||||||
|
|
||||||
contentControl = linkbtn;
|
var textBlock = new TextBlock()
|
||||||
Grid.SetColumn(contentControl, 1);
|
{
|
||||||
Grid.SetRow(contentControl, rowCount);
|
HorizontalAlignment = HorizontalAlignment.Left,
|
||||||
if (rowCount != 0)
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
mainPanel.Children.Add(sep);
|
Margin = SettingPanelItemLeftTopBottomMargin,
|
||||||
|
TextAlignment = TextAlignment.Left,
|
||||||
|
TextWrapping = TextWrapping.Wrap
|
||||||
|
};
|
||||||
|
textBlock.Inlines.Add(hyperlink);
|
||||||
|
|
||||||
Grid.SetRow(sep, rowCount);
|
contentControl = textBlock;
|
||||||
Grid.SetColumn(sep, 0);
|
|
||||||
Grid.SetColumnSpan(sep, 2);
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
case "separator":
|
||||||
|
{
|
||||||
|
var sep = new Separator();
|
||||||
|
|
||||||
|
sep.SetResourceReference(Separator.StyleProperty, "SettingPanelSeparatorStyle");
|
||||||
|
|
||||||
|
contentControl = sep;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type != "textBlock")
|
// If type is textBlock or separator, we just add the content control to the main grid
|
||||||
SettingControls[attribute.Name] = contentControl;
|
if (panel == null)
|
||||||
|
{
|
||||||
|
// Add the content control to the column 0, row rowCount and columnSpan 2 of the main grid
|
||||||
|
mainPanel.Children.Add(contentControl);
|
||||||
|
Grid.SetColumn(contentControl, 0);
|
||||||
|
Grid.SetColumnSpan(contentControl, 2);
|
||||||
|
Grid.SetRow(contentControl, rowCount);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Add the panel to the column 0 and row rowCount of the main grid
|
||||||
|
mainPanel.Children.Add(panel);
|
||||||
|
Grid.SetColumn(panel, 0);
|
||||||
|
Grid.SetRow(panel, rowCount);
|
||||||
|
|
||||||
|
// Add the content control to the column 1 and row rowCount of the main grid
|
||||||
|
mainPanel.Children.Add(contentControl);
|
||||||
|
Grid.SetColumn(contentControl, 1);
|
||||||
|
Grid.SetRow(contentControl, rowCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add into SettingControls for settings storage if need
|
||||||
|
if (NeedSaveInSettings(type)) SettingControls[attributes.Name] = contentControl;
|
||||||
|
|
||||||
mainPanel.Children.Add(panel);
|
|
||||||
mainPanel.Children.Add(contentControl);
|
|
||||||
rowCount++;
|
rowCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return settingWindow;
|
// Wrap the main grid in a user control
|
||||||
|
return new UserControl()
|
||||||
|
{
|
||||||
|
Content = mainPanel
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool NeedSaveInSettings(string type)
|
||||||
|
{
|
||||||
|
return type != "textBlock" && type != "separator" && type != "hyperlink";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
@ -9,7 +9,6 @@ using System.Text.Json;
|
||||||
|
|
||||||
namespace Flow.Launcher.Core.Plugin
|
namespace Flow.Launcher.Core.Plugin
|
||||||
{
|
{
|
||||||
|
|
||||||
internal abstract class PluginConfig
|
internal abstract class PluginConfig
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -112,7 +111,7 @@ namespace Flow.Launcher.Core.Plugin
|
||||||
metadata = JsonSerializer.Deserialize<PluginMetadata>(File.ReadAllText(configPath));
|
metadata = JsonSerializer.Deserialize<PluginMetadata>(File.ReadAllText(configPath));
|
||||||
metadata.PluginDirectory = pluginDirectory;
|
metadata.PluginDirectory = pluginDirectory;
|
||||||
// for plugins which doesn't has ActionKeywords key
|
// for plugins which doesn't has ActionKeywords key
|
||||||
metadata.ActionKeywords = metadata.ActionKeywords ?? new List<string> { metadata.ActionKeyword };
|
metadata.ActionKeywords ??= new List<string> { metadata.ActionKeyword };
|
||||||
// for plugin still use old ActionKeyword
|
// for plugin still use old ActionKeyword
|
||||||
metadata.ActionKeyword = metadata.ActionKeywords?[0];
|
metadata.ActionKeyword = metadata.ActionKeywords?[0];
|
||||||
}
|
}
|
||||||
|
|
@ -137,4 +136,4 @@ namespace Flow.Launcher.Core.Plugin
|
||||||
return metadata;
|
return metadata;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,19 @@
|
||||||
using Flow.Launcher.Core.ExternalPlugins;
|
using System;
|
||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text.Json;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
|
using Flow.Launcher.Core.ExternalPlugins;
|
||||||
using Flow.Launcher.Infrastructure;
|
using Flow.Launcher.Infrastructure;
|
||||||
using Flow.Launcher.Infrastructure.Logger;
|
using Flow.Launcher.Infrastructure.Logger;
|
||||||
using Flow.Launcher.Infrastructure.UserSettings;
|
using Flow.Launcher.Infrastructure.UserSettings;
|
||||||
using Flow.Launcher.Plugin;
|
using Flow.Launcher.Plugin;
|
||||||
using ISavable = Flow.Launcher.Plugin.ISavable;
|
|
||||||
using Flow.Launcher.Plugin.SharedCommands;
|
using Flow.Launcher.Plugin.SharedCommands;
|
||||||
using System.Text.Json;
|
using ISavable = Flow.Launcher.Plugin.ISavable;
|
||||||
using Flow.Launcher.Core.Resource;
|
|
||||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
|
||||||
|
|
||||||
namespace Flow.Launcher.Core.Plugin
|
namespace Flow.Launcher.Core.Plugin
|
||||||
{
|
{
|
||||||
|
|
@ -35,7 +34,7 @@ namespace Flow.Launcher.Core.Plugin
|
||||||
|
|
||||||
private static PluginsSettings Settings;
|
private static PluginsSettings Settings;
|
||||||
private static List<PluginMetadata> _metadatas;
|
private static List<PluginMetadata> _metadatas;
|
||||||
private static List<string> _modifiedPlugins = new List<string>();
|
private static List<string> _modifiedPlugins = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Directories that will hold Flow Launcher plugin directory
|
/// Directories that will hold Flow Launcher plugin directory
|
||||||
|
|
@ -72,15 +71,20 @@ namespace Flow.Launcher.Core.Plugin
|
||||||
{
|
{
|
||||||
foreach (var pluginPair in AllPlugins)
|
foreach (var pluginPair in AllPlugins)
|
||||||
{
|
{
|
||||||
switch (pluginPair.Plugin)
|
await DisposePluginAsync(pluginPair);
|
||||||
{
|
}
|
||||||
case IDisposable disposable:
|
}
|
||||||
disposable.Dispose();
|
|
||||||
break;
|
private static async Task DisposePluginAsync(PluginPair pluginPair)
|
||||||
case IAsyncDisposable asyncDisposable:
|
{
|
||||||
await asyncDisposable.DisposeAsync();
|
switch (pluginPair.Plugin)
|
||||||
break;
|
{
|
||||||
}
|
case IDisposable disposable:
|
||||||
|
disposable.Dispose();
|
||||||
|
break;
|
||||||
|
case IAsyncDisposable asyncDisposable:
|
||||||
|
await asyncDisposable.DisposeAsync();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -155,6 +159,25 @@ namespace Flow.Launcher.Core.Plugin
|
||||||
Settings = settings;
|
Settings = settings;
|
||||||
Settings.UpdatePluginSettings(_metadatas);
|
Settings.UpdatePluginSettings(_metadatas);
|
||||||
AllPlugins = PluginsLoader.Plugins(_metadatas, Settings);
|
AllPlugins = PluginsLoader.Plugins(_metadatas, Settings);
|
||||||
|
// Since dotnet plugins need to get assembly name first, we should update plugin directory after loading plugins
|
||||||
|
UpdatePluginDirectory(_metadatas);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void UpdatePluginDirectory(List<PluginMetadata> metadatas)
|
||||||
|
{
|
||||||
|
foreach (var metadata in metadatas)
|
||||||
|
{
|
||||||
|
if (AllowedLanguage.IsDotNet(metadata.Language))
|
||||||
|
{
|
||||||
|
metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, metadata.AssemblyName);
|
||||||
|
metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, metadata.AssemblyName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, metadata.Name);
|
||||||
|
metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, metadata.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -205,9 +228,6 @@ namespace Flow.Launcher.Core.Plugin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
InternationalizationManager.Instance.AddPluginLanguageDirectories(GetPluginsForInterface<IPluginI18n>());
|
|
||||||
InternationalizationManager.Instance.ChangeLanguage(Ioc.Default.GetRequiredService<Settings>().Language);
|
|
||||||
|
|
||||||
if (failedPlugins.Any())
|
if (failedPlugins.Any())
|
||||||
{
|
{
|
||||||
var failed = string.Join(",", failedPlugins.Select(x => x.Metadata.Name));
|
var failed = string.Join(",", failedPlugins.Select(x => x.Metadata.Name));
|
||||||
|
|
@ -228,11 +248,9 @@ namespace Flow.Launcher.Core.Plugin
|
||||||
if (query is null)
|
if (query is null)
|
||||||
return Array.Empty<PluginPair>();
|
return Array.Empty<PluginPair>();
|
||||||
|
|
||||||
if (!NonGlobalPlugins.ContainsKey(query.ActionKeyword))
|
if (!NonGlobalPlugins.TryGetValue(query.ActionKeyword, out var plugin))
|
||||||
return GlobalPlugins;
|
return GlobalPlugins;
|
||||||
|
|
||||||
|
|
||||||
var plugin = NonGlobalPlugins[query.ActionKeyword];
|
|
||||||
return new List<PluginPair>
|
return new List<PluginPair>
|
||||||
{
|
{
|
||||||
plugin
|
plugin
|
||||||
|
|
@ -367,7 +385,16 @@ namespace Flow.Launcher.Core.Plugin
|
||||||
NonGlobalPlugins[newActionKeyword] = plugin;
|
NonGlobalPlugins[newActionKeyword] = plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update action keywords and action keyword in plugin metadata
|
||||||
plugin.Metadata.ActionKeywords.Add(newActionKeyword);
|
plugin.Metadata.ActionKeywords.Add(newActionKeyword);
|
||||||
|
if (plugin.Metadata.ActionKeywords.Count > 0)
|
||||||
|
{
|
||||||
|
plugin.Metadata.ActionKeyword = plugin.Metadata.ActionKeywords[0];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
plugin.Metadata.ActionKeyword = string.Empty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -388,16 +415,15 @@ namespace Flow.Launcher.Core.Plugin
|
||||||
if (oldActionkeyword != Query.GlobalPluginWildcardSign)
|
if (oldActionkeyword != Query.GlobalPluginWildcardSign)
|
||||||
NonGlobalPlugins.Remove(oldActionkeyword);
|
NonGlobalPlugins.Remove(oldActionkeyword);
|
||||||
|
|
||||||
|
// Update action keywords and action keyword in plugin metadata
|
||||||
plugin.Metadata.ActionKeywords.Remove(oldActionkeyword);
|
plugin.Metadata.ActionKeywords.Remove(oldActionkeyword);
|
||||||
}
|
if (plugin.Metadata.ActionKeywords.Count > 0)
|
||||||
|
|
||||||
public static void ReplaceActionKeyword(string id, string oldActionKeyword, string newActionKeyword)
|
|
||||||
{
|
|
||||||
if (oldActionKeyword != newActionKeyword)
|
|
||||||
{
|
{
|
||||||
AddActionKeyword(id, newActionKeyword);
|
plugin.Metadata.ActionKeyword = plugin.Metadata.ActionKeywords[0];
|
||||||
RemoveActionKeyword(id, oldActionKeyword);
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
plugin.Metadata.ActionKeyword = string.Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -438,10 +464,10 @@ namespace Flow.Launcher.Core.Plugin
|
||||||
/// Update a plugin to new version, from a zip file. By default will remove the zip file if update is via url,
|
/// Update a plugin to new version, from a zip file. By default will remove the zip file if update is via url,
|
||||||
/// unless it's a local path installation
|
/// unless it's a local path installation
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static void UpdatePlugin(PluginMetadata existingVersion, UserPlugin newVersion, string zipFilePath)
|
public static async Task UpdatePluginAsync(PluginMetadata existingVersion, UserPlugin newVersion, string zipFilePath)
|
||||||
{
|
{
|
||||||
InstallPlugin(newVersion, zipFilePath, checkModified:false);
|
InstallPlugin(newVersion, zipFilePath, checkModified:false);
|
||||||
UninstallPlugin(existingVersion, removePluginFromSettings:false, removePluginSettings:false, checkModified: false);
|
await UninstallPluginAsync(existingVersion, removePluginFromSettings:false, removePluginSettings:false, checkModified: false);
|
||||||
_modifiedPlugins.Add(existingVersion.ID);
|
_modifiedPlugins.Add(existingVersion.ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -456,9 +482,9 @@ namespace Flow.Launcher.Core.Plugin
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Uninstall a plugin.
|
/// Uninstall a plugin.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static void UninstallPlugin(PluginMetadata plugin, bool removePluginFromSettings = true, bool removePluginSettings = false)
|
public static async Task UninstallPluginAsync(PluginMetadata plugin, bool removePluginFromSettings = true, bool removePluginSettings = false)
|
||||||
{
|
{
|
||||||
UninstallPlugin(plugin, removePluginFromSettings, removePluginSettings, true);
|
await UninstallPluginAsync(plugin, removePluginFromSettings, removePluginSettings, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
@ -539,64 +565,63 @@ namespace Flow.Launcher.Core.Plugin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void UninstallPlugin(PluginMetadata plugin, bool removePluginFromSettings, bool removePluginSettings, bool checkModified)
|
internal static async Task UninstallPluginAsync(PluginMetadata plugin, bool removePluginFromSettings, bool removePluginSettings, bool checkModified)
|
||||||
{
|
{
|
||||||
if (checkModified && PluginModified(plugin.ID))
|
if (checkModified && PluginModified(plugin.ID))
|
||||||
{
|
{
|
||||||
throw new ArgumentException($"Plugin {plugin.Name} has been modified");
|
throw new ArgumentException($"Plugin {plugin.Name} has been modified");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (removePluginSettings || removePluginFromSettings)
|
||||||
|
{
|
||||||
|
// If we want to remove plugin from AllPlugins,
|
||||||
|
// we need to dispose them so that they can release file handles
|
||||||
|
// which can help FL to delete the plugin settings & cache folders successfully
|
||||||
|
var pluginPairs = AllPlugins.FindAll(p => p.Metadata.ID == plugin.ID);
|
||||||
|
foreach (var pluginPair in pluginPairs)
|
||||||
|
{
|
||||||
|
await DisposePluginAsync(pluginPair);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (removePluginSettings)
|
if (removePluginSettings)
|
||||||
{
|
{
|
||||||
if (AllowedLanguage.IsDotNet(plugin.Language)) // for the plugin in .NET, we can use assembly loader
|
// For dotnet plugins, we need to remove their PluginJsonStorage instance
|
||||||
|
if (AllowedLanguage.IsDotNet(plugin.Language))
|
||||||
{
|
{
|
||||||
var assemblyLoader = new PluginAssemblyLoader(plugin.ExecuteFilePath);
|
|
||||||
var assembly = assemblyLoader.LoadAssemblyAndDependencies();
|
|
||||||
var assemblyName = assembly.GetName().Name;
|
|
||||||
|
|
||||||
// if user want to remove the plugin settings, we cannot call save method for the plugin json storage instance of this plugin
|
|
||||||
// so we need to remove it from the api instance
|
|
||||||
var method = API.GetType().GetMethod("RemovePluginSettings");
|
var method = API.GetType().GetMethod("RemovePluginSettings");
|
||||||
var pluginJsonStorage = method?.Invoke(API, new object[] { assemblyName });
|
method?.Invoke(API, new object[] { plugin.AssemblyName });
|
||||||
|
|
||||||
// if there exists a json storage for current plugin, we need to delete the directory path
|
|
||||||
if (pluginJsonStorage != null)
|
|
||||||
{
|
|
||||||
var deleteMethod = pluginJsonStorage.GetType().GetMethod("DeleteDirectory");
|
|
||||||
try
|
|
||||||
{
|
|
||||||
deleteMethod?.Invoke(pluginJsonStorage, null);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Log.Exception($"|PluginManager.UninstallPlugin|Failed to delete plugin json folder for {plugin.Name}", e);
|
|
||||||
API.ShowMsg(API.GetTranslation("failedToRemovePluginSettingsTitle"),
|
|
||||||
string.Format(API.GetTranslation("failedToRemovePluginSettingsMessage"), plugin.Name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else // the plugin with json prc interface
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
var pluginPair = AllPlugins.FirstOrDefault(p => p.Metadata.ID == plugin.ID);
|
var pluginSettingsDirectory = plugin.PluginSettingsDirectoryPath;
|
||||||
if (pluginPair != null && pluginPair.Plugin is JsonRPCPlugin jsonRpcPlugin)
|
if (Directory.Exists(pluginSettingsDirectory))
|
||||||
{
|
Directory.Delete(pluginSettingsDirectory, true);
|
||||||
try
|
}
|
||||||
{
|
catch (Exception e)
|
||||||
jsonRpcPlugin.DeletePluginSettingsDirectory();
|
{
|
||||||
}
|
Log.Exception($"|PluginManager.UninstallPlugin|Failed to delete plugin settings folder for {plugin.Name}", e);
|
||||||
catch (Exception e)
|
API.ShowMsg(API.GetTranslation("failedToRemovePluginSettingsTitle"),
|
||||||
{
|
string.Format(API.GetTranslation("failedToRemovePluginSettingsMessage"), plugin.Name));
|
||||||
Log.Exception($"|PluginManager.UninstallPlugin|Failed to delete plugin json folder for {plugin.Name}", e);
|
|
||||||
API.ShowMsg(API.GetTranslation("failedToRemovePluginSettingsTitle"),
|
|
||||||
string.Format(API.GetTranslation("failedToRemovePluginSettingsMessage"), plugin.Name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (removePluginFromSettings)
|
if (removePluginFromSettings)
|
||||||
{
|
{
|
||||||
Settings.Plugins.Remove(plugin.ID);
|
try
|
||||||
|
{
|
||||||
|
var pluginCacheDirectory = plugin.PluginCacheDirectoryPath;
|
||||||
|
if (Directory.Exists(pluginCacheDirectory))
|
||||||
|
Directory.Delete(pluginCacheDirectory, true);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Exception($"|PluginManager.UninstallPlugin|Failed to delete plugin cache folder for {plugin.Name}", e);
|
||||||
|
API.ShowMsg(API.GetTranslation("failedToRemovePluginCacheTitle"),
|
||||||
|
string.Format(API.GetTranslation("failedToRemovePluginCacheMessage"), plugin.Name));
|
||||||
|
}
|
||||||
|
Settings.RemovePluginSettings(plugin.ID);
|
||||||
AllPlugins.RemoveAll(p => p.Metadata.ID == plugin.ID);
|
AllPlugins.RemoveAll(p => p.Metadata.ID == plugin.ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ namespace Flow.Launcher.Core.Plugin
|
||||||
return plugins;
|
return plugins;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IEnumerable<PluginPair> DotNetPlugins(List<PluginMetadata> source)
|
private static IEnumerable<PluginPair> DotNetPlugins(List<PluginMetadata> source)
|
||||||
{
|
{
|
||||||
var erroredPlugins = new List<string>();
|
var erroredPlugins = new List<string>();
|
||||||
|
|
||||||
|
|
@ -74,9 +74,11 @@ namespace Flow.Launcher.Core.Plugin
|
||||||
typeof(IAsyncPlugin));
|
typeof(IAsyncPlugin));
|
||||||
|
|
||||||
plugin = Activator.CreateInstance(type) as IAsyncPlugin;
|
plugin = Activator.CreateInstance(type) as IAsyncPlugin;
|
||||||
|
|
||||||
|
metadata.AssemblyName = assembly.GetName().Name;
|
||||||
}
|
}
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
catch (Exception e)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
@ -112,7 +114,7 @@ namespace Flow.Launcher.Core.Plugin
|
||||||
|
|
||||||
if (erroredPlugins.Count > 0)
|
if (erroredPlugins.Count > 0)
|
||||||
{
|
{
|
||||||
var errorPluginString = String.Join(Environment.NewLine, erroredPlugins);
|
var errorPluginString = string.Join(Environment.NewLine, erroredPlugins);
|
||||||
|
|
||||||
var errorMessage = "The following "
|
var errorMessage = "The following "
|
||||||
+ (erroredPlugins.Count > 1 ? "plugins have " : "plugin has ")
|
+ (erroredPlugins.Count > 1 ? "plugins have " : "plugin has ")
|
||||||
|
|
@ -130,23 +132,31 @@ namespace Flow.Launcher.Core.Plugin
|
||||||
return plugins;
|
return plugins;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IEnumerable<PluginPair> ExecutablePlugins(IEnumerable<PluginMetadata> source)
|
private static IEnumerable<PluginPair> ExecutablePlugins(IEnumerable<PluginMetadata> source)
|
||||||
{
|
{
|
||||||
return source
|
return source
|
||||||
.Where(o => o.Language.Equals(AllowedLanguage.Executable, StringComparison.OrdinalIgnoreCase))
|
.Where(o => o.Language.Equals(AllowedLanguage.Executable, StringComparison.OrdinalIgnoreCase))
|
||||||
.Select(metadata => new PluginPair
|
.Select(metadata =>
|
||||||
{
|
{
|
||||||
Plugin = new ExecutablePlugin(metadata.ExecuteFilePath), Metadata = metadata
|
return new PluginPair
|
||||||
|
{
|
||||||
|
Plugin = new ExecutablePlugin(metadata.ExecuteFilePath),
|
||||||
|
Metadata = metadata
|
||||||
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IEnumerable<PluginPair> ExecutableV2Plugins(IEnumerable<PluginMetadata> source)
|
private static IEnumerable<PluginPair> ExecutableV2Plugins(IEnumerable<PluginMetadata> source)
|
||||||
{
|
{
|
||||||
return source
|
return source
|
||||||
.Where(o => o.Language.Equals(AllowedLanguage.ExecutableV2, StringComparison.OrdinalIgnoreCase))
|
.Where(o => o.Language.Equals(AllowedLanguage.ExecutableV2, StringComparison.OrdinalIgnoreCase))
|
||||||
.Select(metadata => new PluginPair
|
.Select(metadata =>
|
||||||
{
|
{
|
||||||
Plugin = new ExecutablePluginV2(metadata.ExecuteFilePath), Metadata = metadata
|
return new PluginPair
|
||||||
|
{
|
||||||
|
Plugin = new ExecutablePlugin(metadata.ExecuteFilePath),
|
||||||
|
Metadata = metadata
|
||||||
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,9 +67,9 @@ namespace Flow.Launcher.Core.Resource
|
||||||
return DefaultLanguageCode;
|
return DefaultLanguageCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void AddPluginLanguageDirectories(IEnumerable<PluginPair> plugins)
|
private void AddPluginLanguageDirectories()
|
||||||
{
|
{
|
||||||
foreach (var plugin in plugins)
|
foreach (var plugin in PluginManager.GetPluginsForInterface<IPluginI18n>())
|
||||||
{
|
{
|
||||||
var location = Assembly.GetAssembly(plugin.Plugin.GetType()).Location;
|
var location = Assembly.GetAssembly(plugin.Plugin.GetType()).Location;
|
||||||
var dir = Path.GetDirectoryName(location);
|
var dir = Path.GetDirectoryName(location);
|
||||||
|
|
@ -96,6 +96,32 @@ namespace Flow.Launcher.Core.Resource
|
||||||
_oldResources.Clear();
|
_oldResources.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize language. Will change app language and plugin language based on settings.
|
||||||
|
/// </summary>
|
||||||
|
public async Task InitializeLanguageAsync()
|
||||||
|
{
|
||||||
|
// Get actual language
|
||||||
|
var languageCode = _settings.Language;
|
||||||
|
if (languageCode == Constant.SystemLanguageCode)
|
||||||
|
{
|
||||||
|
languageCode = SystemLanguageCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get language by language code and change language
|
||||||
|
var language = GetLanguageByLanguageCode(languageCode);
|
||||||
|
|
||||||
|
// Add plugin language directories first so that we can load language files from plugins
|
||||||
|
AddPluginLanguageDirectories();
|
||||||
|
|
||||||
|
// Change language
|
||||||
|
await ChangeLanguageAsync(language);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Change language during runtime. Will change app language and plugin language & save settings.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="languageCode"></param>
|
||||||
public void ChangeLanguage(string languageCode)
|
public void ChangeLanguage(string languageCode)
|
||||||
{
|
{
|
||||||
languageCode = languageCode.NonNull();
|
languageCode = languageCode.NonNull();
|
||||||
|
|
@ -110,7 +136,12 @@ namespace Flow.Launcher.Core.Resource
|
||||||
|
|
||||||
// Get language by language code and change language
|
// Get language by language code and change language
|
||||||
var language = GetLanguageByLanguageCode(languageCode);
|
var language = GetLanguageByLanguageCode(languageCode);
|
||||||
ChangeLanguage(language, isSystem);
|
|
||||||
|
// Change language
|
||||||
|
_ = ChangeLanguageAsync(language);
|
||||||
|
|
||||||
|
// Save settings
|
||||||
|
_settings.Language = isSystem ? Constant.SystemLanguageCode : language.LanguageCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Language GetLanguageByLanguageCode(string languageCode)
|
private Language GetLanguageByLanguageCode(string languageCode)
|
||||||
|
|
@ -128,26 +159,22 @@ namespace Flow.Launcher.Core.Resource
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ChangeLanguage(Language language, bool isSystem)
|
private async Task ChangeLanguageAsync(Language language)
|
||||||
{
|
{
|
||||||
language = language.NonNull();
|
// Remove old language files and load language
|
||||||
|
|
||||||
RemoveOldLanguageFiles();
|
RemoveOldLanguageFiles();
|
||||||
if (language != AvailableLanguages.English)
|
if (language != AvailableLanguages.English)
|
||||||
{
|
{
|
||||||
LoadLanguage(language);
|
LoadLanguage(language);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Culture of main thread
|
// Culture of main thread
|
||||||
// Use CreateSpecificCulture to preserve possible user-override settings in Windows, if Flow's language culture is the same as Windows's
|
// Use CreateSpecificCulture to preserve possible user-override settings in Windows, if Flow's language culture is the same as Windows's
|
||||||
CultureInfo.CurrentCulture = CultureInfo.CreateSpecificCulture(language.LanguageCode);
|
CultureInfo.CurrentCulture = CultureInfo.CreateSpecificCulture(language.LanguageCode);
|
||||||
CultureInfo.CurrentUICulture = CultureInfo.CurrentCulture;
|
CultureInfo.CurrentUICulture = CultureInfo.CurrentCulture;
|
||||||
|
|
||||||
// Raise event after culture is set
|
// Raise event for plugins after culture is set
|
||||||
_settings.Language = isSystem ? Constant.SystemLanguageCode : language.LanguageCode;
|
await Task.Run(UpdatePluginMetadataTranslations);
|
||||||
_ = Task.Run(() =>
|
|
||||||
{
|
|
||||||
UpdatePluginMetadataTranslations();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool PromptShouldUsePinyin(string languageCodeToSet)
|
public bool PromptShouldUsePinyin(string languageCodeToSet)
|
||||||
|
|
|
||||||
|
|
@ -3,21 +3,29 @@ using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Controls.Primitives;
|
||||||
using System.Windows.Markup;
|
using System.Windows.Markup;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using System.Windows.Media.Effects;
|
using System.Windows.Media.Effects;
|
||||||
using System.Windows.Shell;
|
using System.Windows.Shell;
|
||||||
|
using System.Windows.Threading;
|
||||||
using Flow.Launcher.Infrastructure;
|
using Flow.Launcher.Infrastructure;
|
||||||
using Flow.Launcher.Infrastructure.Logger;
|
using Flow.Launcher.Infrastructure.Logger;
|
||||||
using Flow.Launcher.Infrastructure.UserSettings;
|
using Flow.Launcher.Infrastructure.UserSettings;
|
||||||
using Flow.Launcher.Plugin;
|
using Flow.Launcher.Plugin;
|
||||||
|
using Microsoft.Win32;
|
||||||
|
|
||||||
namespace Flow.Launcher.Core.Resource
|
namespace Flow.Launcher.Core.Resource
|
||||||
{
|
{
|
||||||
public class Theme
|
public class Theme
|
||||||
{
|
{
|
||||||
|
#region Properties & Fields
|
||||||
|
|
||||||
|
public bool BlurEnabled { get; private set; }
|
||||||
|
|
||||||
private const string ThemeMetadataNamePrefix = "Name:";
|
private const string ThemeMetadataNamePrefix = "Name:";
|
||||||
private const string ThemeMetadataIsDarkPrefix = "IsDark:";
|
private const string ThemeMetadataIsDarkPrefix = "IsDark:";
|
||||||
private const string ThemeMetadataHasBlurPrefix = "HasBlur:";
|
private const string ThemeMetadataHasBlurPrefix = "HasBlur:";
|
||||||
|
|
@ -31,14 +39,14 @@ namespace Flow.Launcher.Core.Resource
|
||||||
private string _oldTheme;
|
private string _oldTheme;
|
||||||
private const string Folder = Constant.Themes;
|
private const string Folder = Constant.Themes;
|
||||||
private const string Extension = ".xaml";
|
private const string Extension = ".xaml";
|
||||||
private string DirectoryPath => Path.Combine(Constant.ProgramDirectory, Folder);
|
private static string DirectoryPath => Path.Combine(Constant.ProgramDirectory, Folder);
|
||||||
private string UserDirectoryPath => Path.Combine(DataLocation.DataDirectory(), Folder);
|
private static string UserDirectoryPath => Path.Combine(DataLocation.DataDirectory(), Folder);
|
||||||
|
|
||||||
public string CurrentTheme => _settings.Theme;
|
private Thickness _themeResizeBorderThickness;
|
||||||
|
|
||||||
public bool BlurEnabled { get; set; }
|
#endregion
|
||||||
|
|
||||||
private double mainWindowWidth;
|
#region Constructor
|
||||||
|
|
||||||
public Theme(IPublicAPI publicAPI, Settings settings)
|
public Theme(IPublicAPI publicAPI, Settings settings)
|
||||||
{
|
{
|
||||||
|
|
@ -50,20 +58,32 @@ namespace Flow.Launcher.Core.Resource
|
||||||
MakeSureThemeDirectoriesExist();
|
MakeSureThemeDirectoriesExist();
|
||||||
|
|
||||||
var dicts = Application.Current.Resources.MergedDictionaries;
|
var dicts = Application.Current.Resources.MergedDictionaries;
|
||||||
_oldResource = dicts.First(d =>
|
_oldResource = dicts.FirstOrDefault(d =>
|
||||||
{
|
{
|
||||||
if (d.Source == null)
|
if (d.Source == null) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
var p = d.Source.AbsolutePath;
|
var p = d.Source.AbsolutePath;
|
||||||
var dir = Path.GetDirectoryName(p).NonNull();
|
return p.Contains(Folder) && Path.GetExtension(p) == Extension;
|
||||||
var info = new DirectoryInfo(dir);
|
|
||||||
var f = info.Name;
|
|
||||||
var e = Path.GetExtension(p);
|
|
||||||
var found = f == Folder && e == Extension;
|
|
||||||
return found;
|
|
||||||
});
|
});
|
||||||
_oldTheme = Path.GetFileNameWithoutExtension(_oldResource.Source.AbsolutePath);
|
|
||||||
|
if (_oldResource != null)
|
||||||
|
{
|
||||||
|
_oldTheme = Path.GetFileNameWithoutExtension(_oldResource.Source.AbsolutePath);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.Error("Current theme resource not found. Initializing with default theme.");
|
||||||
|
_oldTheme = Constant.DefaultTheme;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Theme Resources
|
||||||
|
|
||||||
|
public string GetCurrentTheme()
|
||||||
|
{
|
||||||
|
return _settings.Theme;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void MakeSureThemeDirectoriesExist()
|
private void MakeSureThemeDirectoriesExist()
|
||||||
|
|
@ -81,68 +101,154 @@ namespace Flow.Launcher.Core.Resource
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ChangeTheme(string theme)
|
|
||||||
{
|
|
||||||
const string defaultTheme = Constant.DefaultTheme;
|
|
||||||
|
|
||||||
string path = GetThemePath(theme);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(path))
|
|
||||||
throw new DirectoryNotFoundException("Theme path can't be found <{path}>");
|
|
||||||
|
|
||||||
// reload all resources even if the theme itself hasn't changed in order to pickup changes
|
|
||||||
// to things like fonts
|
|
||||||
UpdateResourceDictionary(GetResourceDictionary(theme));
|
|
||||||
|
|
||||||
_settings.Theme = theme;
|
|
||||||
|
|
||||||
|
|
||||||
//always allow re-loading default theme, in case of failure of switching to a new theme from default theme
|
|
||||||
if (_oldTheme != theme || theme == defaultTheme)
|
|
||||||
{
|
|
||||||
_oldTheme = Path.GetFileNameWithoutExtension(_oldResource.Source.AbsolutePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
BlurEnabled = Win32Helper.IsBlurTheme();
|
|
||||||
|
|
||||||
if (_settings.UseDropShadowEffect && !BlurEnabled)
|
|
||||||
AddDropShadowEffectToCurrentTheme();
|
|
||||||
|
|
||||||
Win32Helper.SetBlurForWindow(Application.Current.MainWindow, BlurEnabled);
|
|
||||||
}
|
|
||||||
catch (DirectoryNotFoundException)
|
|
||||||
{
|
|
||||||
Log.Error($"|Theme.ChangeTheme|Theme <{theme}> path can't be found");
|
|
||||||
if (theme != defaultTheme)
|
|
||||||
{
|
|
||||||
_api.ShowMsgBox(string.Format(InternationalizationManager.Instance.GetTranslation("theme_load_failure_path_not_exists"), theme));
|
|
||||||
ChangeTheme(defaultTheme);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
catch (XamlParseException)
|
|
||||||
{
|
|
||||||
Log.Error($"|Theme.ChangeTheme|Theme <{theme}> fail to parse");
|
|
||||||
if (theme != defaultTheme)
|
|
||||||
{
|
|
||||||
_api.ShowMsgBox(string.Format(InternationalizationManager.Instance.GetTranslation("theme_load_failure_parse_error"), theme));
|
|
||||||
ChangeTheme(defaultTheme);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateResourceDictionary(ResourceDictionary dictionaryToUpdate)
|
private void UpdateResourceDictionary(ResourceDictionary dictionaryToUpdate)
|
||||||
{
|
{
|
||||||
var dicts = Application.Current.Resources.MergedDictionaries;
|
// Add new resources
|
||||||
|
if (!Application.Current.Resources.MergedDictionaries.Contains(dictionaryToUpdate))
|
||||||
|
{
|
||||||
|
Application.Current.Resources.MergedDictionaries.Add(dictionaryToUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove old resources
|
||||||
|
if (_oldResource != null && _oldResource != dictionaryToUpdate &&
|
||||||
|
Application.Current.Resources.MergedDictionaries.Contains(_oldResource))
|
||||||
|
{
|
||||||
|
Application.Current.Resources.MergedDictionaries.Remove(_oldResource);
|
||||||
|
}
|
||||||
|
|
||||||
dicts.Remove(_oldResource);
|
|
||||||
dicts.Add(dictionaryToUpdate);
|
|
||||||
_oldResource = dictionaryToUpdate;
|
_oldResource = dictionaryToUpdate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates only the font settings and refreshes the UI.
|
||||||
|
/// </summary>
|
||||||
|
public void UpdateFonts()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Load a ResourceDictionary for the specified theme.
|
||||||
|
var themeName = GetCurrentTheme();
|
||||||
|
var dict = GetThemeResourceDictionary(themeName);
|
||||||
|
|
||||||
|
// Apply font settings to the theme resource.
|
||||||
|
ApplyFontSettings(dict);
|
||||||
|
UpdateResourceDictionary(dict);
|
||||||
|
|
||||||
|
// Must apply blur and drop shadow effects
|
||||||
|
_ = RefreshFrameAsync();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Exception("Error occurred while updating theme fonts", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads and applies font settings to the theme resource.
|
||||||
|
/// </summary>
|
||||||
|
private void ApplyFontSettings(ResourceDictionary dict)
|
||||||
|
{
|
||||||
|
if (dict["QueryBoxStyle"] is Style queryBoxStyle &&
|
||||||
|
dict["QuerySuggestionBoxStyle"] is Style querySuggestionBoxStyle)
|
||||||
|
{
|
||||||
|
var fontFamily = new FontFamily(_settings.QueryBoxFont);
|
||||||
|
var fontStyle = FontHelper.GetFontStyleFromInvariantStringOrNormal(_settings.QueryBoxFontStyle);
|
||||||
|
var fontWeight = FontHelper.GetFontWeightFromInvariantStringOrNormal(_settings.QueryBoxFontWeight);
|
||||||
|
var fontStretch = FontHelper.GetFontStretchFromInvariantStringOrNormal(_settings.QueryBoxFontStretch);
|
||||||
|
|
||||||
|
SetFontProperties(queryBoxStyle, fontFamily, fontStyle, fontWeight, fontStretch, true);
|
||||||
|
SetFontProperties(querySuggestionBoxStyle, fontFamily, fontStyle, fontWeight, fontStretch, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dict["ItemTitleStyle"] is Style resultItemStyle &&
|
||||||
|
dict["ItemTitleSelectedStyle"] is Style resultItemSelectedStyle &&
|
||||||
|
dict["ItemHotkeyStyle"] is Style resultHotkeyItemStyle &&
|
||||||
|
dict["ItemHotkeySelectedStyle"] is Style resultHotkeyItemSelectedStyle)
|
||||||
|
{
|
||||||
|
var fontFamily = new FontFamily(_settings.ResultFont);
|
||||||
|
var fontStyle = FontHelper.GetFontStyleFromInvariantStringOrNormal(_settings.ResultFontStyle);
|
||||||
|
var fontWeight = FontHelper.GetFontWeightFromInvariantStringOrNormal(_settings.ResultFontWeight);
|
||||||
|
var fontStretch = FontHelper.GetFontStretchFromInvariantStringOrNormal(_settings.ResultFontStretch);
|
||||||
|
|
||||||
|
SetFontProperties(resultItemStyle, fontFamily, fontStyle, fontWeight, fontStretch, false);
|
||||||
|
SetFontProperties(resultItemSelectedStyle, fontFamily, fontStyle, fontWeight, fontStretch, false);
|
||||||
|
SetFontProperties(resultHotkeyItemStyle, fontFamily, fontStyle, fontWeight, fontStretch, false);
|
||||||
|
SetFontProperties(resultHotkeyItemSelectedStyle, fontFamily, fontStyle, fontWeight, fontStretch, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dict["ItemSubTitleStyle"] is Style resultSubItemStyle &&
|
||||||
|
dict["ItemSubTitleSelectedStyle"] is Style resultSubItemSelectedStyle)
|
||||||
|
{
|
||||||
|
var fontFamily = new FontFamily(_settings.ResultSubFont);
|
||||||
|
var fontStyle = FontHelper.GetFontStyleFromInvariantStringOrNormal(_settings.ResultSubFontStyle);
|
||||||
|
var fontWeight = FontHelper.GetFontWeightFromInvariantStringOrNormal(_settings.ResultSubFontWeight);
|
||||||
|
var fontStretch = FontHelper.GetFontStretchFromInvariantStringOrNormal(_settings.ResultSubFontStretch);
|
||||||
|
|
||||||
|
SetFontProperties(resultSubItemStyle, fontFamily, fontStyle, fontWeight, fontStretch, false);
|
||||||
|
SetFontProperties(resultSubItemSelectedStyle, fontFamily, fontStyle, fontWeight, fontStretch, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Applies font properties to a Style.
|
||||||
|
/// </summary>
|
||||||
|
private static void SetFontProperties(Style style, FontFamily fontFamily, FontStyle fontStyle, FontWeight fontWeight, FontStretch fontStretch, bool isTextBox)
|
||||||
|
{
|
||||||
|
// Remove existing font-related setters
|
||||||
|
if (isTextBox)
|
||||||
|
{
|
||||||
|
// First, find the setters to remove and store them in a list
|
||||||
|
var settersToRemove = style.Setters
|
||||||
|
.OfType<Setter>()
|
||||||
|
.Where(setter =>
|
||||||
|
setter.Property == Control.FontFamilyProperty ||
|
||||||
|
setter.Property == Control.FontStyleProperty ||
|
||||||
|
setter.Property == Control.FontWeightProperty ||
|
||||||
|
setter.Property == Control.FontStretchProperty)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// Remove each found setter one by one
|
||||||
|
foreach (var setter in settersToRemove)
|
||||||
|
{
|
||||||
|
style.Setters.Remove(setter);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add New font setter
|
||||||
|
style.Setters.Add(new Setter(Control.FontFamilyProperty, fontFamily));
|
||||||
|
style.Setters.Add(new Setter(Control.FontStyleProperty, fontStyle));
|
||||||
|
style.Setters.Add(new Setter(Control.FontWeightProperty, fontWeight));
|
||||||
|
style.Setters.Add(new Setter(Control.FontStretchProperty, fontStretch));
|
||||||
|
|
||||||
|
// Set caret brush (retain existing logic)
|
||||||
|
var caretBrushPropertyValue = style.Setters.OfType<Setter>().Any(x => x.Property.Name == "CaretBrush");
|
||||||
|
var foregroundPropertyValue = style.Setters.OfType<Setter>().Where(x => x.Property.Name == "Foreground")
|
||||||
|
.Select(x => x.Value).FirstOrDefault();
|
||||||
|
if (!caretBrushPropertyValue && foregroundPropertyValue != null)
|
||||||
|
style.Setters.Add(new Setter(TextBoxBase.CaretBrushProperty, foregroundPropertyValue));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var settersToRemove = style.Setters
|
||||||
|
.OfType<Setter>()
|
||||||
|
.Where(setter =>
|
||||||
|
setter.Property == TextBlock.FontFamilyProperty ||
|
||||||
|
setter.Property == TextBlock.FontStyleProperty ||
|
||||||
|
setter.Property == TextBlock.FontWeightProperty ||
|
||||||
|
setter.Property == TextBlock.FontStretchProperty)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
foreach (var setter in settersToRemove)
|
||||||
|
{
|
||||||
|
style.Setters.Remove(setter);
|
||||||
|
}
|
||||||
|
|
||||||
|
style.Setters.Add(new Setter(TextBlock.FontFamilyProperty, fontFamily));
|
||||||
|
style.Setters.Add(new Setter(TextBlock.FontStyleProperty, fontStyle));
|
||||||
|
style.Setters.Add(new Setter(TextBlock.FontWeightProperty, fontWeight));
|
||||||
|
style.Setters.Add(new Setter(TextBlock.FontStretchProperty, fontStretch));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private ResourceDictionary GetThemeResourceDictionary(string theme)
|
private ResourceDictionary GetThemeResourceDictionary(string theme)
|
||||||
{
|
{
|
||||||
var uri = GetThemePath(theme);
|
var uri = GetThemePath(theme);
|
||||||
|
|
@ -154,9 +260,7 @@ namespace Flow.Launcher.Core.Resource
|
||||||
return dict;
|
return dict;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResourceDictionary CurrentThemeResourceDictionary() => GetThemeResourceDictionary(_settings.Theme);
|
private ResourceDictionary GetResourceDictionary(string theme)
|
||||||
|
|
||||||
public ResourceDictionary GetResourceDictionary(string theme)
|
|
||||||
{
|
{
|
||||||
var dict = GetThemeResourceDictionary(theme);
|
var dict = GetThemeResourceDictionary(theme);
|
||||||
|
|
||||||
|
|
@ -168,22 +272,22 @@ namespace Flow.Launcher.Core.Resource
|
||||||
var fontWeight = FontHelper.GetFontWeightFromInvariantStringOrNormal(_settings.QueryBoxFontWeight);
|
var fontWeight = FontHelper.GetFontWeightFromInvariantStringOrNormal(_settings.QueryBoxFontWeight);
|
||||||
var fontStretch = FontHelper.GetFontStretchFromInvariantStringOrNormal(_settings.QueryBoxFontStretch);
|
var fontStretch = FontHelper.GetFontStretchFromInvariantStringOrNormal(_settings.QueryBoxFontStretch);
|
||||||
|
|
||||||
queryBoxStyle.Setters.Add(new Setter(TextBox.FontFamilyProperty, fontFamily));
|
queryBoxStyle.Setters.Add(new Setter(Control.FontFamilyProperty, fontFamily));
|
||||||
queryBoxStyle.Setters.Add(new Setter(TextBox.FontStyleProperty, fontStyle));
|
queryBoxStyle.Setters.Add(new Setter(Control.FontStyleProperty, fontStyle));
|
||||||
queryBoxStyle.Setters.Add(new Setter(TextBox.FontWeightProperty, fontWeight));
|
queryBoxStyle.Setters.Add(new Setter(Control.FontWeightProperty, fontWeight));
|
||||||
queryBoxStyle.Setters.Add(new Setter(TextBox.FontStretchProperty, fontStretch));
|
queryBoxStyle.Setters.Add(new Setter(Control.FontStretchProperty, fontStretch));
|
||||||
|
|
||||||
var caretBrushPropertyValue = queryBoxStyle.Setters.OfType<Setter>().Any(x => x.Property.Name == "CaretBrush");
|
var caretBrushPropertyValue = queryBoxStyle.Setters.OfType<Setter>().Any(x => x.Property.Name == "CaretBrush");
|
||||||
var foregroundPropertyValue = queryBoxStyle.Setters.OfType<Setter>().Where(x => x.Property.Name == "Foreground")
|
var foregroundPropertyValue = queryBoxStyle.Setters.OfType<Setter>().Where(x => x.Property.Name == "Foreground")
|
||||||
.Select(x => x.Value).FirstOrDefault();
|
.Select(x => x.Value).FirstOrDefault();
|
||||||
if (!caretBrushPropertyValue && foregroundPropertyValue != null) //otherwise BaseQueryBoxStyle will handle styling
|
if (!caretBrushPropertyValue && foregroundPropertyValue != null) //otherwise BaseQueryBoxStyle will handle styling
|
||||||
queryBoxStyle.Setters.Add(new Setter(TextBox.CaretBrushProperty, foregroundPropertyValue));
|
queryBoxStyle.Setters.Add(new Setter(TextBoxBase.CaretBrushProperty, foregroundPropertyValue));
|
||||||
|
|
||||||
// Query suggestion box's font style is aligned with query box
|
// Query suggestion box's font style is aligned with query box
|
||||||
querySuggestionBoxStyle.Setters.Add(new Setter(TextBox.FontFamilyProperty, fontFamily));
|
querySuggestionBoxStyle.Setters.Add(new Setter(Control.FontFamilyProperty, fontFamily));
|
||||||
querySuggestionBoxStyle.Setters.Add(new Setter(TextBox.FontStyleProperty, fontStyle));
|
querySuggestionBoxStyle.Setters.Add(new Setter(Control.FontStyleProperty, fontStyle));
|
||||||
querySuggestionBoxStyle.Setters.Add(new Setter(TextBox.FontWeightProperty, fontWeight));
|
querySuggestionBoxStyle.Setters.Add(new Setter(Control.FontWeightProperty, fontWeight));
|
||||||
querySuggestionBoxStyle.Setters.Add(new Setter(TextBox.FontStretchProperty, fontStretch));
|
querySuggestionBoxStyle.Setters.Add(new Setter(Control.FontStretchProperty, fontStretch));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dict["ItemTitleStyle"] is Style resultItemStyle &&
|
if (dict["ItemTitleStyle"] is Style resultItemStyle &&
|
||||||
|
|
@ -213,36 +317,20 @@ namespace Flow.Launcher.Core.Resource
|
||||||
|
|
||||||
Setter[] setters = { fontFamily, fontStyle, fontWeight, fontStretch };
|
Setter[] setters = { fontFamily, fontStyle, fontWeight, fontStretch };
|
||||||
Array.ForEach(
|
Array.ForEach(
|
||||||
new[] { resultSubItemStyle,resultSubItemSelectedStyle}, o
|
new[] { resultSubItemStyle, resultSubItemSelectedStyle }, o
|
||||||
=> Array.ForEach(setters, p => o.Setters.Add(p)));
|
=> Array.ForEach(setters, p => o.Setters.Add(p)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ignore Theme Window Width and use setting */
|
/* Ignore Theme Window Width and use setting */
|
||||||
var windowStyle = dict["WindowStyle"] as Style;
|
var windowStyle = dict["WindowStyle"] as Style;
|
||||||
var width = _settings.WindowSize;
|
var width = _settings.WindowSize;
|
||||||
windowStyle.Setters.Add(new Setter(Window.WidthProperty, width));
|
windowStyle.Setters.Add(new Setter(FrameworkElement.WidthProperty, width));
|
||||||
mainWindowWidth = (double)width;
|
|
||||||
return dict;
|
return dict;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResourceDictionary GetCurrentResourceDictionary( )
|
private ResourceDictionary GetCurrentResourceDictionary()
|
||||||
{
|
{
|
||||||
return GetResourceDictionary(_settings.Theme);
|
return GetResourceDictionary(GetCurrentTheme());
|
||||||
}
|
|
||||||
|
|
||||||
public List<ThemeData> LoadAvailableThemes()
|
|
||||||
{
|
|
||||||
List<ThemeData> themes = new List<ThemeData>();
|
|
||||||
foreach (var themeDirectory in _themeDirectories)
|
|
||||||
{
|
|
||||||
var filePaths = Directory
|
|
||||||
.GetFiles(themeDirectory)
|
|
||||||
.Where(filePath => filePath.EndsWith(Extension) && !filePath.EndsWith("Base.xaml"))
|
|
||||||
.Select(GetThemeDataFromPath);
|
|
||||||
themes.AddRange(filePaths);
|
|
||||||
}
|
|
||||||
|
|
||||||
return themes.OrderBy(o => o.Name).ToList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ThemeData GetThemeDataFromPath(string path)
|
private ThemeData GetThemeDataFromPath(string path)
|
||||||
|
|
@ -264,15 +352,15 @@ namespace Flow.Launcher.Core.Resource
|
||||||
{
|
{
|
||||||
if (line.StartsWith(ThemeMetadataNamePrefix, StringComparison.OrdinalIgnoreCase))
|
if (line.StartsWith(ThemeMetadataNamePrefix, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
name = line.Remove(0, ThemeMetadataNamePrefix.Length).Trim();
|
name = line[ThemeMetadataNamePrefix.Length..].Trim();
|
||||||
}
|
}
|
||||||
else if (line.StartsWith(ThemeMetadataIsDarkPrefix, StringComparison.OrdinalIgnoreCase))
|
else if (line.StartsWith(ThemeMetadataIsDarkPrefix, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
isDark = bool.Parse(line.Remove(0, ThemeMetadataIsDarkPrefix.Length).Trim());
|
isDark = bool.Parse(line[ThemeMetadataIsDarkPrefix.Length..].Trim());
|
||||||
}
|
}
|
||||||
else if (line.StartsWith(ThemeMetadataHasBlurPrefix, StringComparison.OrdinalIgnoreCase))
|
else if (line.StartsWith(ThemeMetadataHasBlurPrefix, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
hasBlur = bool.Parse(line.Remove(0, ThemeMetadataHasBlurPrefix.Length).Trim());
|
hasBlur = bool.Parse(line[ThemeMetadataHasBlurPrefix.Length..].Trim());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -293,6 +381,82 @@ namespace Flow.Launcher.Core.Resource
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Load & Change
|
||||||
|
|
||||||
|
public List<ThemeData> LoadAvailableThemes()
|
||||||
|
{
|
||||||
|
List<ThemeData> themes = new List<ThemeData>();
|
||||||
|
foreach (var themeDirectory in _themeDirectories)
|
||||||
|
{
|
||||||
|
var filePaths = Directory
|
||||||
|
.GetFiles(themeDirectory)
|
||||||
|
.Where(filePath => filePath.EndsWith(Extension) && !filePath.EndsWith("Base.xaml"))
|
||||||
|
.Select(GetThemeDataFromPath);
|
||||||
|
themes.AddRange(filePaths);
|
||||||
|
}
|
||||||
|
|
||||||
|
return themes.OrderBy(o => o.Name).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ChangeTheme(string theme = null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(theme))
|
||||||
|
theme = GetCurrentTheme();
|
||||||
|
|
||||||
|
string path = GetThemePath(theme);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(path))
|
||||||
|
throw new DirectoryNotFoundException($"Theme path can't be found <{path}>");
|
||||||
|
|
||||||
|
// Retrieve theme resource – always use the resource with font settings applied.
|
||||||
|
var resourceDict = GetResourceDictionary(theme);
|
||||||
|
|
||||||
|
UpdateResourceDictionary(resourceDict);
|
||||||
|
|
||||||
|
_settings.Theme = theme;
|
||||||
|
|
||||||
|
//always allow re-loading default theme, in case of failure of switching to a new theme from default theme
|
||||||
|
if (_oldTheme != theme || theme == Constant.DefaultTheme)
|
||||||
|
{
|
||||||
|
_oldTheme = Path.GetFileNameWithoutExtension(_oldResource.Source.AbsolutePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
BlurEnabled = IsBlurTheme();
|
||||||
|
|
||||||
|
// Can only apply blur but here also apply drop shadow effect to avoid possible drop shadow effect issues
|
||||||
|
_ = RefreshFrameAsync();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (DirectoryNotFoundException)
|
||||||
|
{
|
||||||
|
Log.Error($"|Theme.ChangeTheme|Theme <{theme}> path can't be found");
|
||||||
|
if (theme != Constant.DefaultTheme)
|
||||||
|
{
|
||||||
|
_api.ShowMsgBox(string.Format(_api.GetTranslation("theme_load_failure_path_not_exists"), theme));
|
||||||
|
ChangeTheme(Constant.DefaultTheme);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch (XamlParseException)
|
||||||
|
{
|
||||||
|
Log.Error($"|Theme.ChangeTheme|Theme <{theme}> fail to parse");
|
||||||
|
if (theme != Constant.DefaultTheme)
|
||||||
|
{
|
||||||
|
_api.ShowMsgBox(string.Format(_api.GetTranslation("theme_load_failure_parse_error"), theme));
|
||||||
|
ChangeTheme(Constant.DefaultTheme);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Shadow Effect
|
||||||
|
|
||||||
public void AddDropShadowEffectToCurrentTheme()
|
public void AddDropShadowEffectToCurrentTheme()
|
||||||
{
|
{
|
||||||
var dict = GetCurrentResourceDictionary();
|
var dict = GetCurrentResourceDictionary();
|
||||||
|
|
@ -301,7 +465,7 @@ namespace Flow.Launcher.Core.Resource
|
||||||
|
|
||||||
var effectSetter = new Setter
|
var effectSetter = new Setter
|
||||||
{
|
{
|
||||||
Property = Border.EffectProperty,
|
Property = UIElement.EffectProperty,
|
||||||
Value = new DropShadowEffect
|
Value = new DropShadowEffect
|
||||||
{
|
{
|
||||||
Opacity = 0.3,
|
Opacity = 0.3,
|
||||||
|
|
@ -311,13 +475,12 @@ namespace Flow.Launcher.Core.Resource
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var marginSetter = windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == Border.MarginProperty) as Setter;
|
if (windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == FrameworkElement.MarginProperty) is not Setter marginSetter)
|
||||||
if (marginSetter == null)
|
|
||||||
{
|
{
|
||||||
var margin = new Thickness(ShadowExtraMargin, 12, ShadowExtraMargin, ShadowExtraMargin);
|
var margin = new Thickness(ShadowExtraMargin, 12, ShadowExtraMargin, ShadowExtraMargin);
|
||||||
marginSetter = new Setter()
|
marginSetter = new Setter()
|
||||||
{
|
{
|
||||||
Property = Border.MarginProperty,
|
Property = FrameworkElement.MarginProperty,
|
||||||
Value = margin,
|
Value = margin,
|
||||||
};
|
};
|
||||||
windowBorderStyle.Setters.Add(marginSetter);
|
windowBorderStyle.Setters.Add(marginSetter);
|
||||||
|
|
@ -347,14 +510,12 @@ namespace Flow.Launcher.Core.Resource
|
||||||
var dict = GetCurrentResourceDictionary();
|
var dict = GetCurrentResourceDictionary();
|
||||||
var windowBorderStyle = dict["WindowBorderStyle"] as Style;
|
var windowBorderStyle = dict["WindowBorderStyle"] as Style;
|
||||||
|
|
||||||
var effectSetter = windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == Border.EffectProperty) as Setter;
|
if (windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == UIElement.EffectProperty) is Setter effectSetter)
|
||||||
var marginSetter = windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == Border.MarginProperty) as Setter;
|
|
||||||
|
|
||||||
if (effectSetter != null)
|
|
||||||
{
|
{
|
||||||
windowBorderStyle.Setters.Remove(effectSetter);
|
windowBorderStyle.Setters.Remove(effectSetter);
|
||||||
}
|
}
|
||||||
if (marginSetter != null)
|
|
||||||
|
if (windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == FrameworkElement.MarginProperty) is Setter marginSetter)
|
||||||
{
|
{
|
||||||
var currentMargin = (Thickness)marginSetter.Value;
|
var currentMargin = (Thickness)marginSetter.Value;
|
||||||
var newMargin = new Thickness(
|
var newMargin = new Thickness(
|
||||||
|
|
@ -370,31 +531,378 @@ namespace Flow.Launcher.Core.Resource
|
||||||
UpdateResourceDictionary(dict);
|
UpdateResourceDictionary(dict);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetResizeBorderThickness(WindowChrome windowChrome, bool fixedWindowSize)
|
||||||
|
{
|
||||||
|
if (fixedWindowSize)
|
||||||
|
{
|
||||||
|
windowChrome.ResizeBorderThickness = new Thickness(0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
windowChrome.ResizeBorderThickness = _themeResizeBorderThickness;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// because adding drop shadow effect will change the margin of the window,
|
// because adding drop shadow effect will change the margin of the window,
|
||||||
// we need to update the window chrome thickness to correct set the resize border
|
// we need to update the window chrome thickness to correct set the resize border
|
||||||
private static void SetResizeBoarderThickness(Thickness? effectMargin)
|
private void SetResizeBoarderThickness(Thickness? effectMargin)
|
||||||
{
|
{
|
||||||
var window = Application.Current.MainWindow;
|
var window = Application.Current.MainWindow;
|
||||||
if (WindowChrome.GetWindowChrome(window) is WindowChrome windowChrome)
|
if (WindowChrome.GetWindowChrome(window) is WindowChrome windowChrome)
|
||||||
{
|
{
|
||||||
Thickness thickness;
|
// Save the theme resize border thickness so that we can restore it if we change ResizeWindow setting
|
||||||
if (effectMargin == null)
|
if (effectMargin == null)
|
||||||
{
|
{
|
||||||
thickness = SystemParameters.WindowResizeBorderThickness;
|
_themeResizeBorderThickness = SystemParameters.WindowResizeBorderThickness;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
thickness = new Thickness(
|
_themeResizeBorderThickness = new Thickness(
|
||||||
effectMargin.Value.Left + SystemParameters.WindowResizeBorderThickness.Left,
|
effectMargin.Value.Left + SystemParameters.WindowResizeBorderThickness.Left,
|
||||||
effectMargin.Value.Top + SystemParameters.WindowResizeBorderThickness.Top,
|
effectMargin.Value.Top + SystemParameters.WindowResizeBorderThickness.Top,
|
||||||
effectMargin.Value.Right + SystemParameters.WindowResizeBorderThickness.Right,
|
effectMargin.Value.Right + SystemParameters.WindowResizeBorderThickness.Right,
|
||||||
effectMargin.Value.Bottom + SystemParameters.WindowResizeBorderThickness.Bottom);
|
effectMargin.Value.Bottom + SystemParameters.WindowResizeBorderThickness.Bottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
windowChrome.ResizeBorderThickness = thickness;
|
// Apply the resize border thickness to the window chrome
|
||||||
|
SetResizeBorderThickness(windowChrome, _settings.KeepMaxResults);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Blur Handling
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Refreshes the frame to apply the current theme settings.
|
||||||
|
/// </summary>
|
||||||
|
public async Task RefreshFrameAsync()
|
||||||
|
{
|
||||||
|
await Application.Current.Dispatcher.InvokeAsync(() =>
|
||||||
|
{
|
||||||
|
// Get the actual backdrop type and drop shadow effect settings
|
||||||
|
var (backdropType, useDropShadowEffect) = GetActualValue();
|
||||||
|
|
||||||
|
// Remove OS minimizing/maximizing animation
|
||||||
|
// Methods.SetWindowAttribute(new WindowInteropHelper(mainWindow).Handle, DWMWINDOWATTRIBUTE.DWMWA_TRANSITIONS_FORCEDISABLED, 3);
|
||||||
|
|
||||||
|
// The timing of adding the shadow effect should vary depending on whether the theme is transparent.
|
||||||
|
if (BlurEnabled)
|
||||||
|
{
|
||||||
|
AutoDropShadow(useDropShadowEffect);
|
||||||
|
}
|
||||||
|
SetBlurForWindow(GetCurrentTheme(), backdropType);
|
||||||
|
|
||||||
|
if (!BlurEnabled)
|
||||||
|
{
|
||||||
|
AutoDropShadow(useDropShadowEffect);
|
||||||
|
}
|
||||||
|
}, DispatcherPriority.Render);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the blur for a window via SetWindowCompositionAttribute
|
||||||
|
/// </summary>
|
||||||
|
public async Task SetBlurForWindowAsync()
|
||||||
|
{
|
||||||
|
await Application.Current.Dispatcher.InvokeAsync(() =>
|
||||||
|
{
|
||||||
|
// Get the actual backdrop type and drop shadow effect settings
|
||||||
|
var (backdropType, _) = GetActualValue();
|
||||||
|
|
||||||
|
SetBlurForWindow(GetCurrentTheme(), backdropType);
|
||||||
|
}, DispatcherPriority.Render);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the actual backdrop type and drop shadow effect settings based on the current theme status.
|
||||||
|
/// </summary>
|
||||||
|
public (BackdropTypes BackdropType, bool UseDropShadowEffect) GetActualValue()
|
||||||
|
{
|
||||||
|
var backdropType = _settings.BackdropType;
|
||||||
|
var useDropShadowEffect = _settings.UseDropShadowEffect;
|
||||||
|
|
||||||
|
// When changed non-blur theme, change to backdrop to none
|
||||||
|
if (!BlurEnabled)
|
||||||
|
{
|
||||||
|
backdropType = BackdropTypes.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dropshadow on and control disabled.(user can't change dropshadow with blur theme)
|
||||||
|
if (BlurEnabled)
|
||||||
|
{
|
||||||
|
useDropShadowEffect = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (backdropType, useDropShadowEffect);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetBlurForWindow(string theme, BackdropTypes backdropType)
|
||||||
|
{
|
||||||
|
var dict = GetResourceDictionary(theme);
|
||||||
|
if (dict == null) return;
|
||||||
|
|
||||||
|
var windowBorderStyle = dict.Contains("WindowBorderStyle") ? dict["WindowBorderStyle"] as Style : null;
|
||||||
|
if (windowBorderStyle == null) return;
|
||||||
|
|
||||||
|
var mainWindow = Application.Current.MainWindow;
|
||||||
|
if (mainWindow == null) return;
|
||||||
|
|
||||||
|
// Check if the theme supports blur
|
||||||
|
bool hasBlur = dict.Contains("ThemeBlurEnabled") && dict["ThemeBlurEnabled"] is bool b && b;
|
||||||
|
if (BlurEnabled && hasBlur && Win32Helper.IsBackdropSupported())
|
||||||
|
{
|
||||||
|
// If the BackdropType is Mica or MicaAlt, set the windowborderstyle's background to transparent
|
||||||
|
if (backdropType == BackdropTypes.Mica || backdropType == BackdropTypes.MicaAlt)
|
||||||
|
{
|
||||||
|
windowBorderStyle.Setters.Remove(windowBorderStyle.Setters.OfType<Setter>().FirstOrDefault(x => x.Property.Name == "Background"));
|
||||||
|
windowBorderStyle.Setters.Add(new Setter(Border.BackgroundProperty, new SolidColorBrush(Color.FromArgb(1, 0, 0, 0))));
|
||||||
|
}
|
||||||
|
else if (backdropType == BackdropTypes.Acrylic)
|
||||||
|
{
|
||||||
|
windowBorderStyle.Setters.Remove(windowBorderStyle.Setters.OfType<Setter>().FirstOrDefault(x => x.Property.Name == "Background"));
|
||||||
|
windowBorderStyle.Setters.Add(new Setter(Border.BackgroundProperty, new SolidColorBrush(Colors.Transparent)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the blur effect
|
||||||
|
Win32Helper.DWMSetBackdropForWindow(mainWindow, backdropType);
|
||||||
|
ColorizeWindow(theme, backdropType);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Apply default style when Blur is disabled
|
||||||
|
Win32Helper.DWMSetBackdropForWindow(mainWindow, BackdropTypes.None);
|
||||||
|
ColorizeWindow(theme, backdropType);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateResourceDictionary(dict);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AutoDropShadow(bool useDropShadowEffect)
|
||||||
|
{
|
||||||
|
SetWindowCornerPreference("Default");
|
||||||
|
RemoveDropShadowEffectFromCurrentTheme();
|
||||||
|
if (useDropShadowEffect)
|
||||||
|
{
|
||||||
|
if (BlurEnabled && Win32Helper.IsBackdropSupported())
|
||||||
|
{
|
||||||
|
SetWindowCornerPreference("Round");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SetWindowCornerPreference("Default");
|
||||||
|
AddDropShadowEffectToCurrentTheme();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (BlurEnabled && Win32Helper.IsBackdropSupported())
|
||||||
|
{
|
||||||
|
SetWindowCornerPreference("Default");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RemoveDropShadowEffectFromCurrentTheme();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetWindowCornerPreference(string cornerType)
|
||||||
|
{
|
||||||
|
Window mainWindow = Application.Current.MainWindow;
|
||||||
|
if (mainWindow == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Win32Helper.DWMSetCornerPreferenceForWindow(mainWindow, cornerType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get Background Color from WindowBorderStyle when there not color for BG.
|
||||||
|
// for theme has not "LightBG" or "DarkBG" case.
|
||||||
|
private Color GetWindowBorderStyleBackground(string theme)
|
||||||
|
{
|
||||||
|
var Resources = GetThemeResourceDictionary(theme);
|
||||||
|
var windowBorderStyle = (Style)Resources["WindowBorderStyle"];
|
||||||
|
|
||||||
|
var backgroundSetter = windowBorderStyle.Setters
|
||||||
|
.OfType<Setter>()
|
||||||
|
.FirstOrDefault(s => s.Property == Border.BackgroundProperty);
|
||||||
|
|
||||||
|
if (backgroundSetter != null)
|
||||||
|
{
|
||||||
|
// Background's Value is DynamicColor Case
|
||||||
|
var backgroundValue = backgroundSetter.Value;
|
||||||
|
|
||||||
|
if (backgroundValue is SolidColorBrush solidColorBrush)
|
||||||
|
{
|
||||||
|
return solidColorBrush.Color; // Return SolidColorBrush's Color
|
||||||
|
}
|
||||||
|
else if (backgroundValue is DynamicResourceExtension dynamicResource)
|
||||||
|
{
|
||||||
|
// When DynamicResource Extension it is, Key is resource's name.
|
||||||
|
var resourceKey = backgroundSetter.Value.ToString();
|
||||||
|
|
||||||
|
// find key in resource and return color.
|
||||||
|
if (Resources.Contains(resourceKey))
|
||||||
|
{
|
||||||
|
var colorResource = Resources[resourceKey];
|
||||||
|
if (colorResource is SolidColorBrush colorBrush)
|
||||||
|
{
|
||||||
|
return colorBrush.Color;
|
||||||
|
}
|
||||||
|
else if (colorResource is Color color)
|
||||||
|
{
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Colors.Transparent; // Default is transparent
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyPreviewBackground(Color? bgColor = null)
|
||||||
|
{
|
||||||
|
if (bgColor == null) return;
|
||||||
|
|
||||||
|
// Copy the existing WindowBorderStyle
|
||||||
|
var previewStyle = new Style(typeof(Border));
|
||||||
|
if (Application.Current.Resources.Contains("WindowBorderStyle"))
|
||||||
|
{
|
||||||
|
if (Application.Current.Resources["WindowBorderStyle"] is Style originalStyle)
|
||||||
|
{
|
||||||
|
foreach (var setter in originalStyle.Setters.OfType<Setter>())
|
||||||
|
{
|
||||||
|
previewStyle.Setters.Add(new Setter(setter.Property, setter.Value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply background color (remove transparency in color)
|
||||||
|
// WPF does not allow the use of an acrylic brush within the window's internal area,
|
||||||
|
// so transparency effects are not applied to the preview.
|
||||||
|
Color backgroundColor = Color.FromRgb(bgColor.Value.R, bgColor.Value.G, bgColor.Value.B);
|
||||||
|
previewStyle.Setters.Add(new Setter(Border.BackgroundProperty, new SolidColorBrush(backgroundColor)));
|
||||||
|
|
||||||
|
// The blur theme keeps the corner round fixed (applying DWM code to modify it causes rendering issues).
|
||||||
|
// The non-blur theme retains the previously set WindowBorderStyle.
|
||||||
|
if (BlurEnabled)
|
||||||
|
{
|
||||||
|
previewStyle.Setters.Add(new Setter(Border.CornerRadiusProperty, new CornerRadius(5)));
|
||||||
|
previewStyle.Setters.Add(new Setter(Border.BorderThicknessProperty, new Thickness(1)));
|
||||||
|
}
|
||||||
|
Application.Current.Resources["PreviewWindowBorderStyle"] = previewStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ColorizeWindow(string theme, BackdropTypes backdropType)
|
||||||
|
{
|
||||||
|
var dict = GetThemeResourceDictionary(theme);
|
||||||
|
if (dict == null) return;
|
||||||
|
|
||||||
|
var mainWindow = Application.Current.MainWindow;
|
||||||
|
if (mainWindow == null) return;
|
||||||
|
|
||||||
|
// Check if the theme supports blur
|
||||||
|
bool hasBlur = dict.Contains("ThemeBlurEnabled") && dict["ThemeBlurEnabled"] is bool b && b;
|
||||||
|
|
||||||
|
// SystemBG value check (Auto, Light, Dark)
|
||||||
|
string systemBG = dict.Contains("SystemBG") ? dict["SystemBG"] as string : "Auto"; // 기본값 Auto
|
||||||
|
|
||||||
|
// Check the user's ColorScheme setting
|
||||||
|
string colorScheme = _settings.ColorScheme;
|
||||||
|
|
||||||
|
// Check system dark mode setting (read AppsUseLightTheme value)
|
||||||
|
int themeValue = (int)Registry.GetValue(@"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize", "AppsUseLightTheme", 1);
|
||||||
|
bool isSystemDark = themeValue == 0;
|
||||||
|
|
||||||
|
// Final decision on whether to use dark mode
|
||||||
|
bool useDarkMode = false;
|
||||||
|
|
||||||
|
// If systemBG is not "Auto", prioritize it over ColorScheme and set the mode based on systemBG value
|
||||||
|
if (systemBG == "Dark")
|
||||||
|
{
|
||||||
|
useDarkMode = true; // Dark
|
||||||
|
}
|
||||||
|
else if (systemBG == "Light")
|
||||||
|
{
|
||||||
|
useDarkMode = false; // Light
|
||||||
|
}
|
||||||
|
else if (systemBG == "Auto")
|
||||||
|
{
|
||||||
|
// If systemBG is "Auto", decide based on ColorScheme
|
||||||
|
if (colorScheme == "Dark")
|
||||||
|
useDarkMode = true;
|
||||||
|
else if (colorScheme == "Light")
|
||||||
|
useDarkMode = false;
|
||||||
|
else
|
||||||
|
useDarkMode = isSystemDark; // Auto (based on system setting)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply DWM Dark Mode
|
||||||
|
Win32Helper.DWMSetDarkModeForWindow(mainWindow, useDarkMode);
|
||||||
|
|
||||||
|
Color LightBG;
|
||||||
|
Color DarkBG;
|
||||||
|
|
||||||
|
// Retrieve LightBG value (fallback to WindowBorderStyle background color if not found)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
LightBG = dict.Contains("LightBG") ? (Color)dict["LightBG"] : GetWindowBorderStyleBackground(theme);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
LightBG = GetWindowBorderStyleBackground(theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve DarkBG value (fallback to LightBG if not found)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DarkBG = dict.Contains("DarkBG") ? (Color)dict["DarkBG"] : LightBG;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
DarkBG = LightBG;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select background color based on ColorScheme and SystemBG
|
||||||
|
Color selectedBG = useDarkMode ? DarkBG : LightBG;
|
||||||
|
ApplyPreviewBackground(selectedBG);
|
||||||
|
|
||||||
|
bool isBlurAvailable = hasBlur && Win32Helper.IsBackdropSupported(); // Windows 11 미만이면 hasBlur를 강제 false
|
||||||
|
|
||||||
|
if (!isBlurAvailable)
|
||||||
|
{
|
||||||
|
mainWindow.Background = Brushes.Transparent;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Only set the background to transparent if the theme supports blur
|
||||||
|
if (backdropType == BackdropTypes.Mica || backdropType == BackdropTypes.MicaAlt)
|
||||||
|
{
|
||||||
|
mainWindow.Background = new SolidColorBrush(Color.FromArgb(1, 0, 0, 0));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mainWindow.Background = new SolidColorBrush(selectedBG);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsBlurTheme()
|
||||||
|
{
|
||||||
|
if (!Win32Helper.IsBackdropSupported()) // Windows 11 미만이면 무조건 false
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var resource = Application.Current.TryFindResource("ThemeBlurEnabled");
|
||||||
|
|
||||||
|
return resource is bool b && b;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Classes
|
||||||
|
|
||||||
public record ThemeData(string FileNameWithoutExtension, string Name, bool? IsDark = null, bool? HasBlur = null);
|
public record ThemeData(string FileNameWithoutExtension, string Name, bool? IsDark = null, bool? HasBlur = null);
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
using System;
|
|
||||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
|
||||||
|
|
||||||
namespace Flow.Launcher.Core.Resource
|
|
||||||
{
|
|
||||||
[Obsolete("ThemeManager.Instance is obsolete. Use Ioc.Default.GetRequiredService<Theme>() instead.")]
|
|
||||||
public class ThemeManager
|
|
||||||
{
|
|
||||||
public static Theme Instance
|
|
||||||
=> Ioc.Default.GetRequiredService<Theme>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -32,6 +32,7 @@ namespace Flow.Launcher.Infrastructure
|
||||||
public static readonly string MissingImgIcon = Path.Combine(ImagesDirectory, "app_missing_img.png");
|
public static readonly string MissingImgIcon = Path.Combine(ImagesDirectory, "app_missing_img.png");
|
||||||
public static readonly string LoadingImgIcon = Path.Combine(ImagesDirectory, "loading.png");
|
public static readonly string LoadingImgIcon = Path.Combine(ImagesDirectory, "loading.png");
|
||||||
public static readonly string ImageIcon = Path.Combine(ImagesDirectory, "image.png");
|
public static readonly string ImageIcon = Path.Combine(ImagesDirectory, "image.png");
|
||||||
|
public static readonly string HistoryIcon = Path.Combine(ImagesDirectory, "history.png");
|
||||||
|
|
||||||
public static string PythonPath;
|
public static string PythonPath;
|
||||||
public static string NodePath;
|
public static string NodePath;
|
||||||
|
|
@ -47,6 +48,7 @@ namespace Flow.Launcher.Infrastructure
|
||||||
public const string Themes = "Themes";
|
public const string Themes = "Themes";
|
||||||
public const string Settings = "Settings";
|
public const string Settings = "Settings";
|
||||||
public const string Logs = "Logs";
|
public const string Logs = "Logs";
|
||||||
|
public const string Cache = "Cache";
|
||||||
|
|
||||||
public const string Website = "https://flowlauncher.com";
|
public const string Website = "https://flowlauncher.com";
|
||||||
public const string SponsorPage = "https://github.com/sponsors/Flow-Launcher";
|
public const string SponsorPage = "https://github.com/sponsors/Flow-Launcher";
|
||||||
|
|
|
||||||
|
|
@ -67,8 +67,6 @@ namespace Flow.Launcher.Infrastructure
|
||||||
return explorerWindows.Zip(zOrders).MinBy(x => x.Second).First;
|
return explorerWindows.Zip(zOrders).MinBy(x => x.Second).First;
|
||||||
}
|
}
|
||||||
|
|
||||||
private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the z-order for one or more windows atomically with respect to each other. In Windows, smaller z-order is higher. If the window is not top level, the z order is returned as -1.
|
/// Gets the z-order for one or more windows atomically with respect to each other. In Windows, smaller z-order is higher. If the window is not top level, the z order is returned as -1.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -12,13 +12,13 @@ namespace Flow.Launcher.Infrastructure.Logger
|
||||||
{
|
{
|
||||||
public static class Log
|
public static class Log
|
||||||
{
|
{
|
||||||
public const string DirectoryName = "Logs";
|
public const string DirectoryName = Constant.Logs;
|
||||||
|
|
||||||
public static string CurrentLogDirectory { get; }
|
public static string CurrentLogDirectory { get; }
|
||||||
|
|
||||||
static Log()
|
static Log()
|
||||||
{
|
{
|
||||||
CurrentLogDirectory = Path.Combine(DataLocation.DataDirectory(), DirectoryName, Constant.Version);
|
CurrentLogDirectory = DataLocation.VersionLogDirectory;
|
||||||
if (!Directory.Exists(CurrentLogDirectory))
|
if (!Directory.Exists(CurrentLogDirectory))
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(CurrentLogDirectory);
|
Directory.CreateDirectory(CurrentLogDirectory);
|
||||||
|
|
|
||||||
123
Flow.Launcher.Infrastructure/MonitorInfo.cs
Normal file
123
Flow.Launcher.Infrastructure/MonitorInfo.cs
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Windows;
|
||||||
|
using Windows.Win32;
|
||||||
|
using Windows.Win32.Foundation;
|
||||||
|
using Windows.Win32.Graphics.Gdi;
|
||||||
|
using Windows.Win32.UI.WindowsAndMessaging;
|
||||||
|
|
||||||
|
namespace Flow.Launcher.Infrastructure;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Contains full information about a display monitor.
|
||||||
|
/// Codes are edited from: <see href="https://github.com/Jack251970/DesktopWidgets3">.
|
||||||
|
/// </summary>
|
||||||
|
internal class MonitorInfo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the display monitors (including invisible pseudo-monitors associated with the mirroring drivers).
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A list of display monitors</returns>
|
||||||
|
public static unsafe IList<MonitorInfo> GetDisplayMonitors()
|
||||||
|
{
|
||||||
|
var monitorCount = PInvoke.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CMONITORS);
|
||||||
|
var list = new List<MonitorInfo>(monitorCount);
|
||||||
|
var callback = new MONITORENUMPROC((HMONITOR monitor, HDC deviceContext, RECT* rect, LPARAM data) =>
|
||||||
|
{
|
||||||
|
list.Add(new MonitorInfo(monitor, rect));
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
var dwData = new LPARAM();
|
||||||
|
var hdc = new HDC();
|
||||||
|
bool ok = PInvoke.EnumDisplayMonitors(hdc, (RECT?)null, callback, dwData);
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
Marshal.ThrowExceptionForHR(Marshal.GetLastWin32Error());
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the display monitor that is nearest to a given window.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hwnd">Window handle</param>
|
||||||
|
/// <returns>The display monitor that is nearest to a given window, or null if no monitor is found.</returns>
|
||||||
|
public static unsafe MonitorInfo GetNearestDisplayMonitor(HWND hwnd)
|
||||||
|
{
|
||||||
|
var nearestMonitor = PInvoke.MonitorFromWindow(hwnd, MONITOR_FROM_FLAGS.MONITOR_DEFAULTTONEAREST);
|
||||||
|
MonitorInfo nearestMonitorInfo = null;
|
||||||
|
var callback = new MONITORENUMPROC((HMONITOR monitor, HDC deviceContext, RECT* rect, LPARAM data) =>
|
||||||
|
{
|
||||||
|
if (monitor == nearestMonitor)
|
||||||
|
{
|
||||||
|
nearestMonitorInfo = new MonitorInfo(monitor, rect);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
var dwData = new LPARAM();
|
||||||
|
var hdc = new HDC();
|
||||||
|
bool ok = PInvoke.EnumDisplayMonitors(hdc, (RECT?)null, callback, dwData);
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
Marshal.ThrowExceptionForHR(Marshal.GetLastWin32Error());
|
||||||
|
}
|
||||||
|
return nearestMonitorInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly HMONITOR _monitor;
|
||||||
|
|
||||||
|
internal unsafe MonitorInfo(HMONITOR monitor, RECT* rect)
|
||||||
|
{
|
||||||
|
RectMonitor =
|
||||||
|
new Rect(new Point(rect->left, rect->top),
|
||||||
|
new Point(rect->right, rect->bottom));
|
||||||
|
_monitor = monitor;
|
||||||
|
var info = new MONITORINFOEXW() { monitorInfo = new MONITORINFO() { cbSize = (uint)sizeof(MONITORINFOEXW) } };
|
||||||
|
GetMonitorInfo(monitor, ref info);
|
||||||
|
RectWork =
|
||||||
|
new Rect(new Point(info.monitorInfo.rcWork.left, info.monitorInfo.rcWork.top),
|
||||||
|
new Point(info.monitorInfo.rcWork.right, info.monitorInfo.rcWork.bottom));
|
||||||
|
Name = new string(info.szDevice.AsSpan()).Replace("\0", "").Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the name of the display.
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the display monitor rectangle, expressed in virtual-screen coordinates.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// <note>If the monitor is not the primary display monitor, some of the rectangle's coordinates may be negative values.</note>
|
||||||
|
/// </remarks>
|
||||||
|
public Rect RectMonitor { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the work area rectangle of the display monitor, expressed in virtual-screen coordinates.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// <note>If the monitor is not the primary display monitor, some of the rectangle's coordinates may be negative values.</note>
|
||||||
|
/// </remarks>
|
||||||
|
public Rect RectWork { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets if the monitor is the the primary display monitor.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsPrimary => _monitor == PInvoke.MonitorFromWindow(new(IntPtr.Zero), MONITOR_FROM_FLAGS.MONITOR_DEFAULTTOPRIMARY);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string ToString() => $"{Name} {RectMonitor.Width}x{RectMonitor.Height}";
|
||||||
|
|
||||||
|
private static unsafe bool GetMonitorInfo(HMONITOR hMonitor, ref MONITORINFOEXW lpmi)
|
||||||
|
{
|
||||||
|
fixed (MONITORINFOEXW* lpmiLocal = &lpmi)
|
||||||
|
{
|
||||||
|
var lpmiBase = (MONITORINFO*)lpmiLocal;
|
||||||
|
var __result = PInvoke.GetMonitorInfo(hMonitor, lpmiBase);
|
||||||
|
return __result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -16,4 +16,46 @@ WM_KEYUP
|
||||||
WM_SYSKEYDOWN
|
WM_SYSKEYDOWN
|
||||||
WM_SYSKEYUP
|
WM_SYSKEYUP
|
||||||
|
|
||||||
EnumWindows
|
EnumWindows
|
||||||
|
|
||||||
|
DwmSetWindowAttribute
|
||||||
|
DWM_SYSTEMBACKDROP_TYPE
|
||||||
|
DWM_WINDOW_CORNER_PREFERENCE
|
||||||
|
|
||||||
|
MAX_PATH
|
||||||
|
SystemParametersInfo
|
||||||
|
|
||||||
|
SetForegroundWindow
|
||||||
|
|
||||||
|
GetWindowLong
|
||||||
|
GetForegroundWindow
|
||||||
|
GetDesktopWindow
|
||||||
|
GetShellWindow
|
||||||
|
GetWindowRect
|
||||||
|
GetClassName
|
||||||
|
FindWindowEx
|
||||||
|
WINDOW_STYLE
|
||||||
|
|
||||||
|
SetLastError
|
||||||
|
WINDOW_EX_STYLE
|
||||||
|
|
||||||
|
GetSystemMetrics
|
||||||
|
EnumDisplayMonitors
|
||||||
|
MonitorFromWindow
|
||||||
|
GetMonitorInfo
|
||||||
|
MONITORINFOEXW
|
||||||
|
|
||||||
|
WM_ENTERSIZEMOVE
|
||||||
|
WM_EXITSIZEMOVE
|
||||||
|
|
||||||
|
GetKeyboardLayout
|
||||||
|
GetWindowThreadProcessId
|
||||||
|
ActivateKeyboardLayout
|
||||||
|
GetKeyboardLayoutList
|
||||||
|
PostMessage
|
||||||
|
WM_INPUTLANGCHANGEREQUEST
|
||||||
|
INPUTLANGCHANGE_FORWARD
|
||||||
|
LOCALE_TRANSIENT_KEYBOARD1
|
||||||
|
LOCALE_TRANSIENT_KEYBOARD2
|
||||||
|
LOCALE_TRANSIENT_KEYBOARD3
|
||||||
|
LOCALE_TRANSIENT_KEYBOARD4
|
||||||
25
Flow.Launcher.Infrastructure/PInvokeExtensions.cs
Normal file
25
Flow.Launcher.Infrastructure/PInvokeExtensions.cs
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using Windows.Win32.Foundation;
|
||||||
|
using Windows.Win32.UI.WindowsAndMessaging;
|
||||||
|
|
||||||
|
namespace Windows.Win32;
|
||||||
|
|
||||||
|
// Edited from: https://github.com/files-community/Files
|
||||||
|
internal static partial class PInvoke
|
||||||
|
{
|
||||||
|
[DllImport("User32", EntryPoint = "SetWindowLongW", ExactSpelling = true)]
|
||||||
|
static extern int _SetWindowLong(HWND hWnd, int nIndex, int dwNewLong);
|
||||||
|
|
||||||
|
[DllImport("User32", EntryPoint = "SetWindowLongPtrW", ExactSpelling = true)]
|
||||||
|
static extern nint _SetWindowLongPtr(HWND hWnd, int nIndex, nint dwNewLong);
|
||||||
|
|
||||||
|
// NOTE:
|
||||||
|
// CsWin32 doesn't generate SetWindowLong on other than x86 and vice versa.
|
||||||
|
// For more info, visit https://github.com/microsoft/CsWin32/issues/882
|
||||||
|
public static unsafe nint SetWindowLongPtr(HWND hWnd, WINDOW_LONG_PTR_INDEX nIndex, nint dwNewLong)
|
||||||
|
{
|
||||||
|
return sizeof(nint) is 4
|
||||||
|
? _SetWindowLong(hWnd, (int)nIndex, (int)dwNewLong)
|
||||||
|
: _SetWindowLongPtr(hWnd, (int)nIndex, dwNewLong);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,4 @@
|
||||||
using System;
|
using System.IO;
|
||||||
using System.IO;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Runtime.Serialization;
|
|
||||||
using System.Runtime.Serialization.Formatters;
|
|
||||||
using System.Runtime.Serialization.Formatters.Binary;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Flow.Launcher.Infrastructure.Logger;
|
using Flow.Launcher.Infrastructure.Logger;
|
||||||
using Flow.Launcher.Infrastructure.UserSettings;
|
using Flow.Launcher.Infrastructure.UserSettings;
|
||||||
|
|
@ -16,18 +11,16 @@ namespace Flow.Launcher.Infrastructure.Storage
|
||||||
/// Normally, it has better performance, but not readable
|
/// Normally, it has better performance, but not readable
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// It utilize MemoryPack, which means the object must be MemoryPackSerializable
|
/// It utilize MemoryPack, which means the object must be MemoryPackSerializable <see href="https://github.com/Cysharp/MemoryPack"/>
|
||||||
/// https://github.com/Cysharp/MemoryPack
|
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public class BinaryStorage<T>
|
public class BinaryStorage<T>
|
||||||
{
|
{
|
||||||
const string DirectoryName = "Cache";
|
public const string FileSuffix = ".cache";
|
||||||
|
|
||||||
const string FileSuffix = ".cache";
|
// Let the derived class to set the file path
|
||||||
|
public BinaryStorage(string filename, string directoryPath = null)
|
||||||
public BinaryStorage(string filename)
|
|
||||||
{
|
{
|
||||||
var directoryPath = Path.Combine(DataLocation.DataDirectory(), DirectoryName);
|
directoryPath ??= DataLocation.CacheDirectory;
|
||||||
Helper.ValidateDirectory(directoryPath);
|
Helper.ValidateDirectory(directoryPath);
|
||||||
|
|
||||||
FilePath = Path.Combine(directoryPath, $"{filename}{FileSuffix}");
|
FilePath = Path.Combine(directoryPath, $"{filename}{FileSuffix}");
|
||||||
|
|
@ -58,14 +51,14 @@ namespace Flow.Launcher.Infrastructure.Storage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async ValueTask<T> DeserializeAsync(Stream stream, T defaultData)
|
private static async ValueTask<T> DeserializeAsync(Stream stream, T defaultData)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var t = await MemoryPackSerializer.DeserializeAsync<T>(stream);
|
var t = await MemoryPackSerializer.DeserializeAsync<T>(stream);
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
catch (System.Exception e)
|
catch (System.Exception)
|
||||||
{
|
{
|
||||||
// Log.Exception($"|BinaryStorage.Deserialize|Deserialize error for file <{FilePath}>", e);
|
// Log.Exception($"|BinaryStorage.Deserialize|Deserialize error for file <{FilePath}>", e);
|
||||||
return defaultData;
|
return defaultData;
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ namespace Flow.Launcher.Infrastructure.Storage
|
||||||
protected T? Data;
|
protected T? Data;
|
||||||
|
|
||||||
// need a new directory name
|
// need a new directory name
|
||||||
public const string DirectoryName = "Settings";
|
public const string DirectoryName = Constant.Settings;
|
||||||
public const string FileSuffix = ".json";
|
public const string FileSuffix = ".json";
|
||||||
|
|
||||||
protected string FilePath { get; init; } = null!;
|
protected string FilePath { get; init; } = null!;
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ namespace Flow.Launcher.Infrastructure.Storage
|
||||||
// C# related, add python related below
|
// C# related, add python related below
|
||||||
var dataType = typeof(T);
|
var dataType = typeof(T);
|
||||||
AssemblyName = dataType.Assembly.GetName().Name;
|
AssemblyName = dataType.Assembly.GetName().Name;
|
||||||
DirectoryPath = Path.Combine(DataLocation.DataDirectory(), DirectoryName, Constant.Plugins, AssemblyName);
|
DirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, AssemblyName);
|
||||||
Helper.ValidateDirectory(DirectoryPath);
|
Helper.ValidateDirectory(DirectoryPath);
|
||||||
|
|
||||||
FilePath = Path.Combine(DirectoryPath, $"{dataType.Name}{FileSuffix}");
|
FilePath = Path.Combine(DirectoryPath, $"{dataType.Name}{FileSuffix}");
|
||||||
|
|
@ -23,13 +23,5 @@ namespace Flow.Launcher.Infrastructure.Storage
|
||||||
{
|
{
|
||||||
Data = data;
|
Data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DeleteDirectory()
|
|
||||||
{
|
|
||||||
if (Directory.Exists(DirectoryPath))
|
|
||||||
{
|
|
||||||
Directory.Delete(DirectoryPath, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,16 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string VersionLogDirectory => Path.Combine(LogDirectory, Constant.Version);
|
||||||
|
public static string LogDirectory => Path.Combine(DataDirectory(), Constant.Logs);
|
||||||
|
|
||||||
|
public static readonly string CacheDirectory = Path.Combine(DataDirectory(), Constant.Cache);
|
||||||
|
public static readonly string SettingsDirectory = Path.Combine(DataDirectory(), Constant.Settings);
|
||||||
public static readonly string PluginsDirectory = Path.Combine(DataDirectory(), Constant.Plugins);
|
public static readonly string PluginsDirectory = Path.Combine(DataDirectory(), Constant.Plugins);
|
||||||
public static readonly string PluginSettingsDirectory = Path.Combine(DataDirectory(), "Settings", Constant.Plugins);
|
public static readonly string ThemesDirectory = Path.Combine(DataDirectory(), Constant.Themes);
|
||||||
|
|
||||||
|
public static readonly string PluginSettingsDirectory = Path.Combine(SettingsDirectory, Constant.Plugins);
|
||||||
|
public static readonly string PluginCacheDirectory = Path.Combine(DataDirectory(), Constant.Cache, Constant.Plugins);
|
||||||
|
|
||||||
public const string PythonEnvironmentName = "Python";
|
public const string PythonEnvironmentName = "Python";
|
||||||
public const string NodeEnvironmentName = "Node.js";
|
public const string NodeEnvironmentName = "Node.js";
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
using Flow.Launcher.Plugin;
|
using Flow.Launcher.Plugin;
|
||||||
|
|
||||||
namespace Flow.Launcher.Infrastructure.UserSettings
|
namespace Flow.Launcher.Infrastructure.UserSettings
|
||||||
|
|
@ -6,8 +7,9 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
||||||
public class PluginsSettings : BaseModel
|
public class PluginsSettings : BaseModel
|
||||||
{
|
{
|
||||||
private string pythonExecutablePath = string.Empty;
|
private string pythonExecutablePath = string.Empty;
|
||||||
public string PythonExecutablePath {
|
public string PythonExecutablePath
|
||||||
get { return pythonExecutablePath; }
|
{
|
||||||
|
get => pythonExecutablePath;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
pythonExecutablePath = value;
|
pythonExecutablePath = value;
|
||||||
|
|
@ -18,7 +20,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
||||||
private string nodeExecutablePath = string.Empty;
|
private string nodeExecutablePath = string.Empty;
|
||||||
public string NodeExecutablePath
|
public string NodeExecutablePath
|
||||||
{
|
{
|
||||||
get { return nodeExecutablePath; }
|
get => nodeExecutablePath;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
nodeExecutablePath = value;
|
nodeExecutablePath = value;
|
||||||
|
|
@ -26,19 +28,32 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Dictionary<string, Plugin> Plugins { get; set; } = new Dictionary<string, Plugin>();
|
/// <summary>
|
||||||
|
/// Only used for serialization
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<string, Plugin> Plugins { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update plugin settings with metadata.
|
||||||
|
/// FL will get default values from metadata first and then load settings to metadata
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="metadatas">Parsed plugin metadatas</param>
|
||||||
public void UpdatePluginSettings(List<PluginMetadata> metadatas)
|
public void UpdatePluginSettings(List<PluginMetadata> metadatas)
|
||||||
{
|
{
|
||||||
foreach (var metadata in metadatas)
|
foreach (var metadata in metadatas)
|
||||||
{
|
{
|
||||||
if (Plugins.ContainsKey(metadata.ID))
|
if (Plugins.TryGetValue(metadata.ID, out var settings))
|
||||||
{
|
{
|
||||||
var settings = Plugins[metadata.ID];
|
// If settings exist, update settings & metadata value
|
||||||
|
// update settings values with metadata
|
||||||
if (string.IsNullOrEmpty(settings.Version))
|
if (string.IsNullOrEmpty(settings.Version))
|
||||||
|
{
|
||||||
settings.Version = metadata.Version;
|
settings.Version = metadata.Version;
|
||||||
|
}
|
||||||
|
settings.DefaultActionKeywords = metadata.ActionKeywords; // metadata provides default values
|
||||||
|
settings.DefaultSearchDelayTime = metadata.SearchDelayTime; // metadata provides default values
|
||||||
|
|
||||||
|
// update metadata values with settings
|
||||||
if (settings.ActionKeywords?.Count > 0)
|
if (settings.ActionKeywords?.Count > 0)
|
||||||
{
|
{
|
||||||
metadata.ActionKeywords = settings.ActionKeywords;
|
metadata.ActionKeywords = settings.ActionKeywords;
|
||||||
|
|
@ -51,30 +66,65 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
||||||
}
|
}
|
||||||
metadata.Disabled = settings.Disabled;
|
metadata.Disabled = settings.Disabled;
|
||||||
metadata.Priority = settings.Priority;
|
metadata.Priority = settings.Priority;
|
||||||
|
metadata.SearchDelayTime = settings.SearchDelayTime;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// If settings does not exist, create a new one
|
||||||
Plugins[metadata.ID] = new Plugin
|
Plugins[metadata.ID] = new Plugin
|
||||||
{
|
{
|
||||||
ID = metadata.ID,
|
ID = metadata.ID,
|
||||||
Name = metadata.Name,
|
Name = metadata.Name,
|
||||||
Version = metadata.Version,
|
Version = metadata.Version,
|
||||||
ActionKeywords = metadata.ActionKeywords,
|
DefaultActionKeywords = metadata.ActionKeywords, // metadata provides default values
|
||||||
|
ActionKeywords = metadata.ActionKeywords, // use default value
|
||||||
Disabled = metadata.Disabled,
|
Disabled = metadata.Disabled,
|
||||||
Priority = metadata.Priority
|
Priority = metadata.Priority,
|
||||||
|
DefaultSearchDelayTime = metadata.SearchDelayTime, // metadata provides default values
|
||||||
|
SearchDelayTime = metadata.SearchDelayTime, // use default value
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Plugin GetPluginSettings(string id)
|
||||||
|
{
|
||||||
|
if (Plugins.TryGetValue(id, out var plugin))
|
||||||
|
{
|
||||||
|
return plugin;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Plugin RemovePluginSettings(string id)
|
||||||
|
{
|
||||||
|
Plugins.Remove(id, out var plugin);
|
||||||
|
return plugin;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Plugin
|
public class Plugin
|
||||||
{
|
{
|
||||||
public string ID { get; set; }
|
public string ID { get; set; }
|
||||||
|
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
|
||||||
public string Version { get; set; }
|
public string Version { get; set; }
|
||||||
public List<string> ActionKeywords { get; set; } // a reference of the action keywords from plugin manager
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public List<string> DefaultActionKeywords { get; set; }
|
||||||
|
|
||||||
|
// a reference of the action keywords from plugin manager
|
||||||
|
public List<string> ActionKeywords { get; set; }
|
||||||
|
|
||||||
public int Priority { get; set; }
|
public int Priority { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public SearchDelayTime? DefaultSearchDelayTime { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||||
|
public SearchDelayTime? SearchDelayTime { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used only to save the state of the plugin in settings
|
/// Used only to save the state of the plugin in settings
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -68,14 +68,16 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
||||||
get => _theme;
|
get => _theme;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (value == _theme)
|
if (value != _theme)
|
||||||
return;
|
{
|
||||||
_theme = value;
|
_theme = value;
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
OnPropertyChanged(nameof(MaxResultsToShow));
|
OnPropertyChanged(nameof(MaxResultsToShow));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public bool UseDropShadowEffect { get; set; } = true;
|
public bool UseDropShadowEffect { get; set; } = true;
|
||||||
|
public BackdropTypes BackdropType{ get; set; } = BackdropTypes.None;
|
||||||
|
|
||||||
/* Appearance Settings. It should be separated from the setting later.*/
|
/* Appearance Settings. It should be separated from the setting later.*/
|
||||||
public double WindowHeightSize { get; set; } = 42;
|
public double WindowHeightSize { get; set; } = 42;
|
||||||
|
|
@ -110,7 +112,34 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
||||||
public double SettingWindowHeight { get; set; } = 700;
|
public double SettingWindowHeight { get; set; } = 700;
|
||||||
public double? SettingWindowTop { get; set; } = null;
|
public double? SettingWindowTop { get; set; } = null;
|
||||||
public double? SettingWindowLeft { get; set; } = null;
|
public double? SettingWindowLeft { get; set; } = null;
|
||||||
public System.Windows.WindowState SettingWindowState { get; set; } = WindowState.Normal;
|
public WindowState SettingWindowState { get; set; } = WindowState.Normal;
|
||||||
|
|
||||||
|
bool _showPlaceholder { get; set; } = false;
|
||||||
|
public bool ShowPlaceholder
|
||||||
|
{
|
||||||
|
get => _showPlaceholder;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_showPlaceholder != value)
|
||||||
|
{
|
||||||
|
_showPlaceholder = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
string _placeholderText { get; set; } = string.Empty;
|
||||||
|
public string PlaceholderText
|
||||||
|
{
|
||||||
|
get => _placeholderText;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_placeholderText != value)
|
||||||
|
{
|
||||||
|
_placeholderText = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public int CustomExplorerIndex { get; set; } = 0;
|
public int CustomExplorerIndex { get; set; } = 0;
|
||||||
|
|
||||||
|
|
@ -240,10 +269,26 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double CustomWindowTop { get; set; } = 0;
|
public double CustomWindowTop { get; set; } = 0;
|
||||||
|
|
||||||
public bool KeepMaxResults { get; set; } = false;
|
/// <summary>
|
||||||
public int MaxResultsToShow { get; set; } = 5;
|
/// Fixed window size
|
||||||
public int ActivateTimes { get; set; }
|
/// </summary>
|
||||||
|
private bool _keepMaxResults { get; set; } = false;
|
||||||
|
public bool KeepMaxResults
|
||||||
|
{
|
||||||
|
get => _keepMaxResults;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_keepMaxResults != value)
|
||||||
|
{
|
||||||
|
_keepMaxResults = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int MaxResultsToShow { get; set; } = 5;
|
||||||
|
|
||||||
|
public int ActivateTimes { get; set; }
|
||||||
|
|
||||||
public ObservableCollection<CustomPluginHotkey> CustomPluginHotkeys { get; set; } = new ObservableCollection<CustomPluginHotkey>();
|
public ObservableCollection<CustomPluginHotkey> CustomPluginHotkeys { get; set; } = new ObservableCollection<CustomPluginHotkey>();
|
||||||
|
|
||||||
|
|
@ -265,7 +310,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
||||||
bool _hideNotifyIcon { get; set; }
|
bool _hideNotifyIcon { get; set; }
|
||||||
public bool HideNotifyIcon
|
public bool HideNotifyIcon
|
||||||
{
|
{
|
||||||
get { return _hideNotifyIcon; }
|
get => _hideNotifyIcon;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_hideNotifyIcon = value;
|
_hideNotifyIcon = value;
|
||||||
|
|
@ -275,6 +320,11 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
||||||
public bool LeaveCmdOpen { get; set; }
|
public bool LeaveCmdOpen { get; set; }
|
||||||
public bool HideWhenDeactivated { get; set; } = true;
|
public bool HideWhenDeactivated { get; set; } = true;
|
||||||
|
|
||||||
|
public bool SearchQueryResultsWithDelay { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||||
|
public SearchDelayTime SearchDelayTime { get; set; } = SearchDelayTime.Normal;
|
||||||
|
|
||||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||||
public SearchWindowScreens SearchWindowScreen { get; set; } = SearchWindowScreens.Cursor;
|
public SearchWindowScreens SearchWindowScreen { get; set; } = SearchWindowScreens.Cursor;
|
||||||
|
|
||||||
|
|
@ -297,7 +347,6 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public bool WMPInstalled { get; set; } = true;
|
public bool WMPInstalled { get; set; } = true;
|
||||||
|
|
||||||
|
|
||||||
// This needs to be loaded last by staying at the bottom
|
// This needs to be loaded last by staying at the bottom
|
||||||
public PluginsSettings PluginSettings { get; set; } = new PluginsSettings();
|
public PluginsSettings PluginSettings { get; set; } = new PluginsSettings();
|
||||||
|
|
||||||
|
|
@ -430,4 +479,12 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
||||||
Fast,
|
Fast,
|
||||||
Custom
|
Custom
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum BackdropTypes
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Acrylic,
|
||||||
|
Mica,
|
||||||
|
MicaAlt
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,18 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Globalization;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Windows.Interop;
|
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
using System.Windows.Interop;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using Flow.Launcher.Infrastructure.UserSettings;
|
||||||
|
using Microsoft.Win32;
|
||||||
|
using Windows.Win32;
|
||||||
|
using Windows.Win32.Foundation;
|
||||||
|
using Windows.Win32.Graphics.Dwm;
|
||||||
|
using Windows.Win32.UI.Input.KeyboardAndMouse;
|
||||||
|
using Windows.Win32.UI.WindowsAndMessaging;
|
||||||
|
using Point = System.Windows.Point;
|
||||||
|
|
||||||
namespace Flow.Launcher.Infrastructure
|
namespace Flow.Launcher.Infrastructure
|
||||||
{
|
{
|
||||||
|
|
@ -9,93 +20,473 @@ namespace Flow.Launcher.Infrastructure
|
||||||
{
|
{
|
||||||
#region Blur Handling
|
#region Blur Handling
|
||||||
|
|
||||||
/*
|
public static bool IsBackdropSupported()
|
||||||
Found on https://github.com/riverar/sample-win10-aeroglass
|
|
||||||
*/
|
|
||||||
|
|
||||||
private enum AccentState
|
|
||||||
{
|
{
|
||||||
ACCENT_DISABLED = 0,
|
// Mica and Acrylic only supported Windows 11 22000+
|
||||||
ACCENT_ENABLE_GRADIENT = 1,
|
return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) &&
|
||||||
ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,
|
Environment.OSVersion.Version.Build >= 22000;
|
||||||
ACCENT_ENABLE_BLURBEHIND = 3,
|
|
||||||
ACCENT_INVALID_STATE = 4
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
public static unsafe bool DWMSetCloakForWindow(Window window, bool cloak)
|
||||||
private struct AccentPolicy
|
|
||||||
{
|
{
|
||||||
public AccentState AccentState;
|
var cloaked = cloak ? 1 : 0;
|
||||||
public int AccentFlags;
|
|
||||||
public int GradientColor;
|
return PInvoke.DwmSetWindowAttribute(
|
||||||
public int AnimationId;
|
GetWindowHandle(window),
|
||||||
|
DWMWINDOWATTRIBUTE.DWMWA_CLOAK,
|
||||||
|
&cloaked,
|
||||||
|
(uint)Marshal.SizeOf<int>()).Succeeded;
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
public static unsafe bool DWMSetBackdropForWindow(Window window, BackdropTypes backdrop)
|
||||||
private struct WindowCompositionAttributeData
|
|
||||||
{
|
{
|
||||||
public WindowCompositionAttribute Attribute;
|
var backdropType = backdrop switch
|
||||||
public IntPtr Data;
|
{
|
||||||
public int SizeOfData;
|
BackdropTypes.Acrylic => DWM_SYSTEMBACKDROP_TYPE.DWMSBT_TRANSIENTWINDOW,
|
||||||
|
BackdropTypes.Mica => DWM_SYSTEMBACKDROP_TYPE.DWMSBT_MAINWINDOW,
|
||||||
|
BackdropTypes.MicaAlt => DWM_SYSTEMBACKDROP_TYPE.DWMSBT_TABBEDWINDOW,
|
||||||
|
_ => DWM_SYSTEMBACKDROP_TYPE.DWMSBT_AUTO
|
||||||
|
};
|
||||||
|
|
||||||
|
return PInvoke.DwmSetWindowAttribute(
|
||||||
|
GetWindowHandle(window),
|
||||||
|
DWMWINDOWATTRIBUTE.DWMWA_SYSTEMBACKDROP_TYPE,
|
||||||
|
&backdropType,
|
||||||
|
(uint)Marshal.SizeOf<int>()).Succeeded;
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum WindowCompositionAttribute
|
public static unsafe bool DWMSetDarkModeForWindow(Window window, bool useDarkMode)
|
||||||
{
|
{
|
||||||
WCA_ACCENT_POLICY = 19
|
var darkMode = useDarkMode ? 1 : 0;
|
||||||
}
|
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
return PInvoke.DwmSetWindowAttribute(
|
||||||
private static extern int SetWindowCompositionAttribute(IntPtr hwnd, ref WindowCompositionAttributeData data);
|
GetWindowHandle(window),
|
||||||
|
DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE,
|
||||||
|
&darkMode,
|
||||||
|
(uint)Marshal.SizeOf<int>()).Succeeded;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if the blur theme is enabled
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static bool IsBlurTheme()
|
/// <param name="window"></param>
|
||||||
|
/// <param name="cornerType">DoNotRound, Round, RoundSmall, Default</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static unsafe bool DWMSetCornerPreferenceForWindow(Window window, string cornerType)
|
||||||
{
|
{
|
||||||
if (Environment.OSVersion.Version >= new Version(6, 2))
|
var preference = cornerType switch
|
||||||
{
|
{
|
||||||
var resource = Application.Current.TryFindResource("ThemeBlurEnabled");
|
"DoNotRound" => DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_DONOTROUND,
|
||||||
|
"Round" => DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUND,
|
||||||
|
"RoundSmall" => DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUNDSMALL,
|
||||||
|
"Default" => DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_DEFAULT,
|
||||||
|
_ => throw new InvalidOperationException("Invalid corner type")
|
||||||
|
};
|
||||||
|
|
||||||
if (resource is bool b)
|
return PInvoke.DwmSetWindowAttribute(
|
||||||
return b;
|
GetWindowHandle(window),
|
||||||
|
DWMWINDOWATTRIBUTE.DWMWA_WINDOW_CORNER_PREFERENCE,
|
||||||
|
&preference,
|
||||||
|
(uint)Marshal.SizeOf<int>()).Succeeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Wallpaper
|
||||||
|
|
||||||
|
public static unsafe string GetWallpaperPath()
|
||||||
|
{
|
||||||
|
var wallpaperPtr = stackalloc char[(int)PInvoke.MAX_PATH];
|
||||||
|
PInvoke.SystemParametersInfo(SYSTEM_PARAMETERS_INFO_ACTION.SPI_GETDESKWALLPAPER, PInvoke.MAX_PATH,
|
||||||
|
wallpaperPtr,
|
||||||
|
0);
|
||||||
|
var wallpaper = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(wallpaperPtr);
|
||||||
|
|
||||||
|
return wallpaper.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Window Foreground
|
||||||
|
|
||||||
|
public static nint GetForegroundWindow()
|
||||||
|
{
|
||||||
|
return PInvoke.GetForegroundWindow().Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool SetForegroundWindow(Window window)
|
||||||
|
{
|
||||||
|
return PInvoke.SetForegroundWindow(GetWindowHandle(window));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool SetForegroundWindow(nint handle)
|
||||||
|
{
|
||||||
|
return PInvoke.SetForegroundWindow(new(handle));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Task Switching
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hide windows in the Alt+Tab window list
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="window">To hide a window</param>
|
||||||
|
public static void HideFromAltTab(Window window)
|
||||||
|
{
|
||||||
|
var hwnd = GetWindowHandle(window);
|
||||||
|
|
||||||
|
var exStyle = GetWindowStyle(hwnd, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE);
|
||||||
|
|
||||||
|
// Add TOOLWINDOW style, remove APPWINDOW style
|
||||||
|
var newExStyle = ((uint)exStyle | (uint)WINDOW_EX_STYLE.WS_EX_TOOLWINDOW) & ~(uint)WINDOW_EX_STYLE.WS_EX_APPWINDOW;
|
||||||
|
|
||||||
|
SetWindowStyle(hwnd, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE, (int)newExStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Restore window display in the Alt+Tab window list.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="window">To restore the displayed window</param>
|
||||||
|
public static void ShowInAltTab(Window window)
|
||||||
|
{
|
||||||
|
var hwnd = GetWindowHandle(window);
|
||||||
|
|
||||||
|
var exStyle = GetWindowStyle(hwnd, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE);
|
||||||
|
|
||||||
|
// Remove the TOOLWINDOW style and add the APPWINDOW style.
|
||||||
|
var newExStyle = ((uint)exStyle & ~(uint)WINDOW_EX_STYLE.WS_EX_TOOLWINDOW) | (uint)WINDOW_EX_STYLE.WS_EX_APPWINDOW;
|
||||||
|
|
||||||
|
SetWindowStyle(GetWindowHandle(window), WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE, (int)newExStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disable windows toolbar's control box
|
||||||
|
/// This will also disable system menu with Alt+Space hotkey
|
||||||
|
/// </summary>
|
||||||
|
public static void DisableControlBox(Window window)
|
||||||
|
{
|
||||||
|
var hwnd = GetWindowHandle(window);
|
||||||
|
|
||||||
|
var style = GetWindowStyle(hwnd, WINDOW_LONG_PTR_INDEX.GWL_STYLE);
|
||||||
|
|
||||||
|
style &= ~(int)WINDOW_STYLE.WS_SYSMENU;
|
||||||
|
|
||||||
|
SetWindowStyle(hwnd, WINDOW_LONG_PTR_INDEX.GWL_STYLE, style);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetWindowStyle(HWND hWnd, WINDOW_LONG_PTR_INDEX nIndex)
|
||||||
|
{
|
||||||
|
var style = PInvoke.GetWindowLong(hWnd, nIndex);
|
||||||
|
if (style == 0 && Marshal.GetLastPInvokeError() != 0)
|
||||||
|
{
|
||||||
|
throw new Win32Exception(Marshal.GetLastPInvokeError());
|
||||||
|
}
|
||||||
|
return style;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static nint SetWindowStyle(HWND hWnd, WINDOW_LONG_PTR_INDEX nIndex, int dwNewLong)
|
||||||
|
{
|
||||||
|
PInvoke.SetLastError(WIN32_ERROR.NO_ERROR); // Clear any existing error
|
||||||
|
|
||||||
|
var result = PInvoke.SetWindowLongPtr(hWnd, nIndex, dwNewLong);
|
||||||
|
if (result == 0 && Marshal.GetLastPInvokeError() != 0)
|
||||||
|
{
|
||||||
|
throw new Win32Exception(Marshal.GetLastPInvokeError());
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Window Fullscreen
|
||||||
|
|
||||||
|
private const string WINDOW_CLASS_CONSOLE = "ConsoleWindowClass";
|
||||||
|
private const string WINDOW_CLASS_WINTAB = "Flip3D";
|
||||||
|
private const string WINDOW_CLASS_PROGMAN = "Progman";
|
||||||
|
private const string WINDOW_CLASS_WORKERW = "WorkerW";
|
||||||
|
|
||||||
|
private static HWND _hwnd_shell;
|
||||||
|
private static HWND HWND_SHELL =>
|
||||||
|
_hwnd_shell != HWND.Null ? _hwnd_shell : _hwnd_shell = PInvoke.GetShellWindow();
|
||||||
|
|
||||||
|
private static HWND _hwnd_desktop;
|
||||||
|
private static HWND HWND_DESKTOP =>
|
||||||
|
_hwnd_desktop != HWND.Null ? _hwnd_desktop : _hwnd_desktop = PInvoke.GetDesktopWindow();
|
||||||
|
|
||||||
|
public static unsafe bool IsForegroundWindowFullscreen()
|
||||||
|
{
|
||||||
|
// Get current active window
|
||||||
|
var hWnd = PInvoke.GetForegroundWindow();
|
||||||
|
if (hWnd.Equals(HWND.Null))
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
// If current active window is desktop or shell, exit early
|
||||||
|
if (hWnd.Equals(HWND_DESKTOP) || hWnd.Equals(HWND_SHELL))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
string windowClass;
|
||||||
|
const int capacity = 256;
|
||||||
|
Span<char> buffer = stackalloc char[capacity];
|
||||||
|
int validLength;
|
||||||
|
fixed (char* pBuffer = buffer)
|
||||||
|
{
|
||||||
|
validLength = PInvoke.GetClassName(hWnd, pBuffer, capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
windowClass = buffer[..validLength].ToString();
|
||||||
|
|
||||||
|
// For Win+Tab (Flip3D)
|
||||||
|
if (windowClass == WINDOW_CLASS_WINTAB)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PInvoke.GetWindowRect(hWnd, out var appBounds);
|
||||||
|
|
||||||
|
// For console (ConsoleWindowClass), we have to check for negative dimensions
|
||||||
|
if (windowClass == WINDOW_CLASS_CONSOLE)
|
||||||
|
{
|
||||||
|
return appBounds.top < 0 && appBounds.bottom < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For desktop (Progman or WorkerW, depends on the system), we have to check
|
||||||
|
if (windowClass is WINDOW_CLASS_PROGMAN or WINDOW_CLASS_WORKERW)
|
||||||
|
{
|
||||||
|
var hWndDesktop = PInvoke.FindWindowEx(hWnd, HWND.Null, "SHELLDLL_DefView", null);
|
||||||
|
hWndDesktop = PInvoke.FindWindowEx(hWndDesktop, HWND.Null, "SysListView32", "FolderView");
|
||||||
|
if (hWndDesktop.Value != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var monitorInfo = MonitorInfo.GetNearestDisplayMonitor(hWnd);
|
||||||
|
return (appBounds.bottom - appBounds.top) == monitorInfo.RectMonitor.Height &&
|
||||||
|
(appBounds.right - appBounds.left) == monitorInfo.RectMonitor.Width;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Pixel to DIP
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Transforms pixels to Device Independent Pixels used by WPF
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="visual">current window, required to get presentation source</param>
|
||||||
|
/// <param name="unitX">horizontal position in pixels</param>
|
||||||
|
/// <param name="unitY">vertical position in pixels</param>
|
||||||
|
/// <returns>point containing device independent pixels</returns>
|
||||||
|
public static Point TransformPixelsToDIP(Visual visual, double unitX, double unitY)
|
||||||
|
{
|
||||||
|
Matrix matrix;
|
||||||
|
var source = PresentationSource.FromVisual(visual);
|
||||||
|
if (source is not null)
|
||||||
|
{
|
||||||
|
matrix = source.CompositionTarget.TransformFromDevice;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
using var src = new HwndSource(new HwndSourceParameters());
|
||||||
|
matrix = src.CompositionTarget.TransformFromDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Point((int)(matrix.M11 * unitX), (int)(matrix.M22 * unitY));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region WndProc
|
||||||
|
|
||||||
|
public const int WM_ENTERSIZEMOVE = (int)PInvoke.WM_ENTERSIZEMOVE;
|
||||||
|
public const int WM_EXITSIZEMOVE = (int)PInvoke.WM_EXITSIZEMOVE;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Window Handle
|
||||||
|
|
||||||
|
internal static HWND GetWindowHandle(Window window, bool ensure = false)
|
||||||
|
{
|
||||||
|
var windowHelper = new WindowInteropHelper(window);
|
||||||
|
if (ensure)
|
||||||
|
{
|
||||||
|
windowHelper.EnsureHandle();
|
||||||
|
}
|
||||||
|
return new(windowHelper.Handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Keyboard Layout
|
||||||
|
|
||||||
|
private const string UserProfileRegistryPath = @"Control Panel\International\User Profile";
|
||||||
|
|
||||||
|
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-lcid/70feba9f-294e-491e-b6eb-56532684c37f
|
||||||
|
private const string EnglishLanguageTag = "en";
|
||||||
|
|
||||||
|
private static readonly string[] ImeLanguageTags =
|
||||||
|
{
|
||||||
|
"zh", // Chinese
|
||||||
|
"ja", // Japanese
|
||||||
|
"ko", // Korean
|
||||||
|
};
|
||||||
|
|
||||||
|
private const uint KeyboardLayoutLoWord = 0xFFFF;
|
||||||
|
|
||||||
|
// Store the previous keyboard layout
|
||||||
|
private static HKL _previousLayout;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Switches the keyboard layout to English if available.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="backupPrevious">If true, the current keyboard layout will be stored for later restoration.</param>
|
||||||
|
/// <exception cref="Win32Exception">Thrown when there's an error getting the window thread process ID.</exception>
|
||||||
|
public static unsafe void SwitchToEnglishKeyboardLayout(bool backupPrevious)
|
||||||
|
{
|
||||||
|
// Find an installed English layout
|
||||||
|
var enHKL = FindEnglishKeyboardLayout();
|
||||||
|
|
||||||
|
// No installed English layout found
|
||||||
|
if (enHKL == HKL.Null) return;
|
||||||
|
|
||||||
|
// Get the current foreground window
|
||||||
|
var hwnd = PInvoke.GetForegroundWindow();
|
||||||
|
if (hwnd == HWND.Null) return;
|
||||||
|
|
||||||
|
// Get the current foreground window thread ID
|
||||||
|
var threadId = PInvoke.GetWindowThreadProcessId(hwnd);
|
||||||
|
if (threadId == 0) throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||||
|
|
||||||
|
// If the current layout has an IME mode, disable it without switching to another layout.
|
||||||
|
// This is needed because for languages with IME mode, Flow Launcher just temporarily disables
|
||||||
|
// the IME mode instead of switching to another layout.
|
||||||
|
var currentLayout = PInvoke.GetKeyboardLayout(threadId);
|
||||||
|
var currentLangId = (uint)currentLayout.Value & KeyboardLayoutLoWord;
|
||||||
|
foreach (var langTag in ImeLanguageTags)
|
||||||
|
{
|
||||||
|
if (GetLanguageTag(currentLangId).StartsWith(langTag, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backup current keyboard layout
|
||||||
|
if (backupPrevious) _previousLayout = currentLayout;
|
||||||
|
|
||||||
|
// Switch to English layout
|
||||||
|
PInvoke.ActivateKeyboardLayout(enHKL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the blur for a window via SetWindowCompositionAttribute
|
/// Restores the previously backed-up keyboard layout.
|
||||||
|
/// If it wasn't backed up or has already been restored, this method does nothing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static void SetBlurForWindow(Window w, bool blur)
|
public static void RestorePreviousKeyboardLayout()
|
||||||
{
|
{
|
||||||
SetWindowAccent(w, blur ? AccentState.ACCENT_ENABLE_BLURBEHIND : AccentState.ACCENT_DISABLED);
|
if (_previousLayout == HKL.Null) return;
|
||||||
|
|
||||||
|
var hwnd = PInvoke.GetForegroundWindow();
|
||||||
|
if (hwnd == HWND.Null) return;
|
||||||
|
|
||||||
|
PInvoke.PostMessage(
|
||||||
|
hwnd,
|
||||||
|
PInvoke.WM_INPUTLANGCHANGEREQUEST,
|
||||||
|
PInvoke.INPUTLANGCHANGE_FORWARD,
|
||||||
|
_previousLayout.Value
|
||||||
|
);
|
||||||
|
|
||||||
|
_previousLayout = HKL.Null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SetWindowAccent(Window w, AccentState state)
|
/// <summary>
|
||||||
|
/// Finds an installed English keyboard layout.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="Win32Exception"></exception>
|
||||||
|
private static unsafe HKL FindEnglishKeyboardLayout()
|
||||||
{
|
{
|
||||||
var windowHelper = new WindowInteropHelper(w);
|
// Get the number of keyboard layouts
|
||||||
|
int count = PInvoke.GetKeyboardLayoutList(0, null);
|
||||||
|
if (count <= 0) return HKL.Null;
|
||||||
|
|
||||||
windowHelper.EnsureHandle();
|
// Get all keyboard layouts
|
||||||
|
var handles = new HKL[count];
|
||||||
var accent = new AccentPolicy { AccentState = state };
|
fixed (HKL* h = handles)
|
||||||
var accentStructSize = Marshal.SizeOf(accent);
|
|
||||||
|
|
||||||
var accentPtr = Marshal.AllocHGlobal(accentStructSize);
|
|
||||||
Marshal.StructureToPtr(accent, accentPtr, false);
|
|
||||||
|
|
||||||
var data = new WindowCompositionAttributeData
|
|
||||||
{
|
{
|
||||||
Attribute = WindowCompositionAttribute.WCA_ACCENT_POLICY,
|
var result = PInvoke.GetKeyboardLayoutList(count, h);
|
||||||
SizeOfData = accentStructSize,
|
if (result == 0) throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||||
Data = accentPtr
|
}
|
||||||
};
|
|
||||||
|
|
||||||
SetWindowCompositionAttribute(windowHelper.Handle, ref data);
|
// Look for any English keyboard layout
|
||||||
|
foreach (var hkl in handles)
|
||||||
|
{
|
||||||
|
// The lower word contains the language identifier
|
||||||
|
var langId = (uint)hkl.Value & KeyboardLayoutLoWord;
|
||||||
|
var langTag = GetLanguageTag(langId);
|
||||||
|
|
||||||
Marshal.FreeHGlobal(accentPtr);
|
// Check if it's an English layout
|
||||||
|
if (langTag.StartsWith(EnglishLanguageTag, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return hkl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return HKL.Null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the
|
||||||
|
/// <see href="https://learn.microsoft.com/globalization/locale/standard-locale-names">
|
||||||
|
/// BCP 47 language tag
|
||||||
|
/// </see>
|
||||||
|
/// of the current input language.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Edited from: https://github.com/dotnet/winforms
|
||||||
|
/// </remarks>
|
||||||
|
private static string GetLanguageTag(uint langId)
|
||||||
|
{
|
||||||
|
// We need to convert the language identifier to a language tag, because they are deprecated and may have a
|
||||||
|
// transient value.
|
||||||
|
// https://learn.microsoft.com/globalization/locale/other-locale-names#lcid
|
||||||
|
// https://learn.microsoft.com/windows/win32/winmsg/wm-inputlangchange#remarks
|
||||||
|
//
|
||||||
|
// It turns out that the LCIDToLocaleName API, which is used inside CultureInfo, may return incorrect
|
||||||
|
// language tags for transient language identifiers. For example, it returns "nqo-GN" and "jv-Java-ID"
|
||||||
|
// instead of the "nqo" and "jv-Java" (as seen in the Get-WinUserLanguageList PowerShell cmdlet).
|
||||||
|
//
|
||||||
|
// Try to extract proper language tag from registry as a workaround approved by a Windows team.
|
||||||
|
// https://github.com/dotnet/winforms/pull/8573#issuecomment-1542600949
|
||||||
|
//
|
||||||
|
// NOTE: this logic may break in future versions of Windows since it is not documented.
|
||||||
|
if (langId is PInvoke.LOCALE_TRANSIENT_KEYBOARD1
|
||||||
|
or PInvoke.LOCALE_TRANSIENT_KEYBOARD2
|
||||||
|
or PInvoke.LOCALE_TRANSIENT_KEYBOARD3
|
||||||
|
or PInvoke.LOCALE_TRANSIENT_KEYBOARD4)
|
||||||
|
{
|
||||||
|
using var key = Registry.CurrentUser.OpenSubKey(UserProfileRegistryPath);
|
||||||
|
if (key?.GetValue("Languages") is string[] languages)
|
||||||
|
{
|
||||||
|
foreach (string language in languages)
|
||||||
|
{
|
||||||
|
using var subKey = key.OpenSubKey(language);
|
||||||
|
if (subKey?.GetValue("TransientLangId") is int transientLangId
|
||||||
|
&& transientLangId == langId)
|
||||||
|
{
|
||||||
|
return language;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return CultureInfo.GetCultureInfo((int)langId).Name;
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,9 @@ namespace Flow.Launcher.Plugin
|
||||||
(WinPressed ? ModifierKeys.Windows : ModifierKeys.None);
|
(WinPressed ? ModifierKeys.Windows : ModifierKeys.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default <see cref="SpecialKeyState"/> object with all keys not pressed.
|
||||||
|
/// </summary>
|
||||||
public static readonly SpecialKeyState Default = new () {
|
public static readonly SpecialKeyState Default = new () {
|
||||||
CtrlPressed = false,
|
CtrlPressed = false,
|
||||||
ShiftPressed = false,
|
ShiftPressed = false,
|
||||||
|
|
|
||||||
|
|
@ -152,7 +152,6 @@ namespace Flow.Launcher.Plugin
|
||||||
/// <param name="callback"></param>
|
/// <param name="callback"></param>
|
||||||
public void RemoveGlobalKeyboardCallback(Func<int, int, SpecialKeyState, bool> callback);
|
public void RemoveGlobalKeyboardCallback(Func<int, int, SpecialKeyState, bool> callback);
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fuzzy Search the string with the given query. This is the core search mechanism Flow uses
|
/// Fuzzy Search the string with the given query. This is the core search mechanism Flow uses
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -191,14 +190,15 @@ namespace Flow.Launcher.Plugin
|
||||||
Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath, Action<double> reportProgress = null, CancellationToken token = default);
|
Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath, Action<double> reportProgress = null, CancellationToken token = default);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Add ActionKeyword for specific plugin
|
/// Add ActionKeyword and update action keyword metadata for specific plugin
|
||||||
|
/// Before adding, please check if action keyword is already assigned by <see cref="ActionKeywordAssigned"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="pluginId">ID for plugin that needs to add action keyword</param>
|
/// <param name="pluginId">ID for plugin that needs to add action keyword</param>
|
||||||
/// <param name="newActionKeyword">The actionkeyword that is supposed to be added</param>
|
/// <param name="newActionKeyword">The actionkeyword that is supposed to be added</param>
|
||||||
void AddActionKeyword(string pluginId, string newActionKeyword);
|
void AddActionKeyword(string pluginId, string newActionKeyword);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Remove ActionKeyword for specific plugin
|
/// Remove ActionKeyword and update action keyword metadata for specific plugin
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="pluginId">ID for plugin that needs to remove action keyword</param>
|
/// <param name="pluginId">ID for plugin that needs to remove action keyword</param>
|
||||||
/// <param name="oldActionKeyword">The actionkeyword that is supposed to be removed</param>
|
/// <param name="oldActionKeyword">The actionkeyword that is supposed to be removed</param>
|
||||||
|
|
|
||||||
|
|
@ -4,17 +4,42 @@ using System.Threading;
|
||||||
|
|
||||||
namespace Flow.Launcher.Plugin
|
namespace Flow.Launcher.Plugin
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interface for plugins that want to manually update their results
|
||||||
|
/// </summary>
|
||||||
public interface IResultUpdated : IFeatures
|
public interface IResultUpdated : IFeatures
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Event that is triggered when the results are updated
|
||||||
|
/// </summary>
|
||||||
event ResultUpdatedEventHandler ResultsUpdated;
|
event ResultUpdatedEventHandler ResultsUpdated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Delegate for the ResultsUpdated event
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender"></param>
|
||||||
|
/// <param name="e"></param>
|
||||||
public delegate void ResultUpdatedEventHandler(IResultUpdated sender, ResultUpdatedEventArgs e);
|
public delegate void ResultUpdatedEventHandler(IResultUpdated sender, ResultUpdatedEventArgs e);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event arguments for the ResultsUpdated event
|
||||||
|
/// </summary>
|
||||||
public class ResultUpdatedEventArgs : EventArgs
|
public class ResultUpdatedEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// List of results that should be displayed
|
||||||
|
/// </summary>
|
||||||
public List<Result> Results;
|
public List<Result> Results;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Query that triggered the update
|
||||||
|
/// </summary>
|
||||||
public Query Query;
|
public Query Query;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Token that can be used to cancel the update
|
||||||
|
/// </summary>
|
||||||
public CancellationToken Token { get; init; }
|
public CancellationToken Token { get; init; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,15 @@
|
||||||
|
|
||||||
namespace Flow.Launcher.Plugin
|
namespace Flow.Launcher.Plugin
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This interface is used to create settings panel for .Net plugins
|
||||||
|
/// </summary>
|
||||||
public interface ISettingProvider
|
public interface ISettingProvider
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Create settings panel control for .Net plugins
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
Control CreateSettingPanel();
|
Control CreateSettingPanel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,18 @@
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class PluginInitContext
|
public class PluginInitContext
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor.
|
||||||
|
/// </summary>
|
||||||
public PluginInitContext()
|
public PluginInitContext()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="currentPluginMetadata"></param>
|
||||||
|
/// <param name="api"></param>
|
||||||
public PluginInitContext(PluginMetadata currentPluginMetadata, IPublicAPI api)
|
public PluginInitContext(PluginMetadata currentPluginMetadata, IPublicAPI api)
|
||||||
{
|
{
|
||||||
CurrentPluginMetadata = currentPluginMetadata;
|
CurrentPluginMetadata = currentPluginMetadata;
|
||||||
|
|
|
||||||
|
|
@ -4,24 +4,77 @@ using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Flow.Launcher.Plugin
|
namespace Flow.Launcher.Plugin
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Plugin metadata
|
||||||
|
/// </summary>
|
||||||
public class PluginMetadata : BaseModel
|
public class PluginMetadata : BaseModel
|
||||||
{
|
{
|
||||||
private string _pluginDirectory;
|
/// <summary>
|
||||||
|
/// Plugin ID.
|
||||||
|
/// </summary>
|
||||||
public string ID { get; set; }
|
public string ID { get; set; }
|
||||||
public string Name { get; set; }
|
|
||||||
public string Author { get; set; }
|
|
||||||
public string Version { get; set; }
|
|
||||||
public string Language { get; set; }
|
|
||||||
public string Description { get; set; }
|
|
||||||
public string Website { get; set; }
|
|
||||||
public bool Disabled { get; set; }
|
|
||||||
public string ExecuteFilePath { get; private set;}
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Plugin name.
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Plugin author.
|
||||||
|
/// </summary>
|
||||||
|
public string Author { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Plugin version.
|
||||||
|
/// </summary>
|
||||||
|
public string Version { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Plugin language.
|
||||||
|
/// See <see cref="AllowedLanguage"/>
|
||||||
|
/// </summary>
|
||||||
|
public string Language { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Plugin description.
|
||||||
|
/// </summary>
|
||||||
|
public string Description { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Plugin website.
|
||||||
|
/// </summary>
|
||||||
|
public string Website { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether plugin is disabled.
|
||||||
|
/// </summary>
|
||||||
|
public bool Disabled { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Plugin execute file path.
|
||||||
|
/// </summary>
|
||||||
|
public string ExecuteFilePath { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Plugin execute file name.
|
||||||
|
/// </summary>
|
||||||
public string ExecuteFileName { get; set; }
|
public string ExecuteFileName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Plugin assembly name.
|
||||||
|
/// Only available for .Net plugins.
|
||||||
|
/// </summary>
|
||||||
|
[JsonIgnore]
|
||||||
|
public string AssemblyName { get; internal set; }
|
||||||
|
|
||||||
|
private string _pluginDirectory;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Plugin source directory.
|
||||||
|
/// </summary>
|
||||||
public string PluginDirectory
|
public string PluginDirectory
|
||||||
{
|
{
|
||||||
get { return _pluginDirectory; }
|
get => _pluginDirectory;
|
||||||
internal set
|
internal set
|
||||||
{
|
{
|
||||||
_pluginDirectory = value;
|
_pluginDirectory = value;
|
||||||
|
|
@ -30,28 +83,78 @@ namespace Flow.Launcher.Plugin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The first action keyword of plugin.
|
||||||
|
/// </summary>
|
||||||
public string ActionKeyword { get; set; }
|
public string ActionKeyword { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// All action keywords of plugin.
|
||||||
|
/// </summary>
|
||||||
public List<string> ActionKeywords { get; set; }
|
public List<string> ActionKeywords { get; set; }
|
||||||
|
|
||||||
public string IcoPath { get; set;}
|
/// <summary>
|
||||||
|
/// Hide plugin keyword setting panel.
|
||||||
public override string ToString()
|
/// </summary>
|
||||||
{
|
public bool HideActionKeywordPanel { get; set; }
|
||||||
return Name;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Plugin search delay time. Null means use default search delay time.
|
||||||
|
/// </summary>
|
||||||
|
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||||
|
public SearchDelayTime? SearchDelayTime { get; set; } = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Plugin icon path.
|
||||||
|
/// </summary>
|
||||||
|
public string IcoPath { get; set;}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Plugin priority.
|
||||||
|
/// </summary>
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public int Priority { get; set; }
|
public int Priority { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Init time include both plugin load time and init time
|
/// Init time include both plugin load time and init time.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public long InitTime { get; set; }
|
public long InitTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Average query time.
|
||||||
|
/// </summary>
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public long AvgQueryTime { get; set; }
|
public long AvgQueryTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Query count.
|
||||||
|
/// </summary>
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public int QueryCount { get; set; }
|
public int QueryCount { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The path to the plugin settings directory which is not validated.
|
||||||
|
/// It is used to store plugin settings files and data files.
|
||||||
|
/// When plugin is deleted, FL will ask users whether to keep its settings.
|
||||||
|
/// If users do not want to keep, this directory will be deleted.
|
||||||
|
/// </summary>
|
||||||
|
public string PluginSettingsDirectoryPath { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The path to the plugin cache directory which is not validated.
|
||||||
|
/// It is used to store cache files.
|
||||||
|
/// When plugin is deleted, this directory will be deleted as well.
|
||||||
|
/// </summary>
|
||||||
|
public string PluginCacheDirectoryPath { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert <see cref="PluginMetadata"/> to string.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,37 @@
|
||||||
namespace Flow.Launcher.Plugin
|
namespace Flow.Launcher.Plugin
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Plugin instance and plugin metadata
|
||||||
|
/// </summary>
|
||||||
public class PluginPair
|
public class PluginPair
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Plugin instance
|
||||||
|
/// </summary>
|
||||||
public IAsyncPlugin Plugin { get; internal set; }
|
public IAsyncPlugin Plugin { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Plugin metadata
|
||||||
|
/// </summary>
|
||||||
public PluginMetadata Metadata { get; internal set; }
|
public PluginMetadata Metadata { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert to string
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return Metadata.Name;
|
return Metadata.Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Compare by plugin metadata ID
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj"></param>
|
||||||
|
/// <returns></returns>
|
||||||
public override bool Equals(object obj)
|
public override bool Equals(object obj)
|
||||||
{
|
{
|
||||||
PluginPair r = obj as PluginPair;
|
if (obj is PluginPair r)
|
||||||
if (r != null)
|
|
||||||
{
|
{
|
||||||
return string.Equals(r.Metadata.ID, Metadata.ID);
|
return string.Equals(r.Metadata.ID, Metadata.ID);
|
||||||
}
|
}
|
||||||
|
|
@ -25,6 +41,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get hash code
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
var hashcode = Metadata.ID?.GetHashCode() ?? 0;
|
var hashcode = Metadata.ID?.GetHashCode() ?? 0;
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,11 @@
|
||||||
|
|
||||||
namespace Flow.Launcher.Plugin
|
namespace Flow.Launcher.Plugin
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a query that is sent to a plugin.
|
||||||
|
/// </summary>
|
||||||
public class Query
|
public class Query
|
||||||
{
|
{
|
||||||
public Query() { }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Raw query, this includes action keyword if it has
|
/// Raw query, this includes action keyword if it has
|
||||||
/// We didn't recommend use this property directly. You should always use Search property.
|
/// We didn't recommend use this property directly. You should always use Search property.
|
||||||
|
|
@ -39,10 +40,9 @@ namespace Flow.Launcher.Plugin
|
||||||
public const string TermSeparator = " ";
|
public const string TermSeparator = " ";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// User can set multiple action keywords seperated by ';'
|
/// User can set multiple action keywords seperated by whitespace
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string ActionKeywordSeparator = ";";
|
public const string ActionKeywordSeparator = TermSeparator;
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Wildcard action keyword. Plugins using this value will be queried on every search.
|
/// Wildcard action keyword. Plugins using this value will be queried on every search.
|
||||||
|
|
@ -55,13 +55,13 @@ namespace Flow.Launcher.Plugin
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ActionKeyword { get; init; }
|
public string ActionKeyword { get; init; }
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Splits <see cref="SearchTerms"/> by spaces and returns the first item.
|
/// Splits <see cref="SearchTerms"/> by spaces and returns the first item.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// returns an empty string when <see cref="SearchTerms"/> does not have enough items.
|
/// returns an empty string when <see cref="SearchTerms"/> does not have enough items.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
|
[JsonIgnore]
|
||||||
public string FirstSearch => SplitSearch(0);
|
public string FirstSearch => SplitSearch(0);
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -13,7 +12,6 @@ namespace Flow.Launcher.Plugin
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Result
|
public class Result
|
||||||
{
|
{
|
||||||
|
|
||||||
private string _pluginDirectory;
|
private string _pluginDirectory;
|
||||||
|
|
||||||
private string _icoPath;
|
private string _icoPath;
|
||||||
|
|
|
||||||
32
Flow.Launcher.Plugin/SearchDelayTime.cs
Normal file
32
Flow.Launcher.Plugin/SearchDelayTime.cs
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
namespace Flow.Launcher.Plugin;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enum for search delay time
|
||||||
|
/// </summary>
|
||||||
|
public enum SearchDelayTime
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Very long search delay time. 250ms.
|
||||||
|
/// </summary>
|
||||||
|
VeryLong,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Long search delay time. 200ms.
|
||||||
|
/// </summary>
|
||||||
|
Long,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Normal search delay time. 150ms. Default value.
|
||||||
|
/// </summary>
|
||||||
|
Normal,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Short search delay time. 100ms.
|
||||||
|
/// </summary>
|
||||||
|
Short,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Very short search delay time. 50ms.
|
||||||
|
/// </summary>
|
||||||
|
VeryShort
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,9 @@ using System.Linq;
|
||||||
|
|
||||||
namespace Flow.Launcher.Plugin.SharedCommands
|
namespace Flow.Launcher.Plugin.SharedCommands
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Contains methods to open a search in a new browser window or tab.
|
||||||
|
/// </summary>
|
||||||
public static class SearchWeb
|
public static class SearchWeb
|
||||||
{
|
{
|
||||||
private static string GetDefaultBrowserPath()
|
private static string GetDefaultBrowserPath()
|
||||||
|
|
@ -106,4 +109,4 @@ namespace Flow.Launcher.Plugin.SharedCommands
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,26 @@ using Windows.Win32.Foundation;
|
||||||
|
|
||||||
namespace Flow.Launcher.Plugin.SharedCommands
|
namespace Flow.Launcher.Plugin.SharedCommands
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Contains methods for running shell commands
|
||||||
|
/// </summary>
|
||||||
public static class ShellCommand
|
public static class ShellCommand
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Delegate for EnumThreadWindows
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hwnd"></param>
|
||||||
|
/// <param name="lParam"></param>
|
||||||
|
/// <returns></returns>
|
||||||
public delegate bool EnumThreadDelegate(IntPtr hwnd, IntPtr lParam);
|
public delegate bool EnumThreadDelegate(IntPtr hwnd, IntPtr lParam);
|
||||||
|
|
||||||
private static bool containsSecurityWindow;
|
private static bool containsSecurityWindow;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Runs a windows command using the provided ProcessStartInfo
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="processStartInfo"></param>
|
||||||
|
/// <returns></returns>
|
||||||
public static Process RunAsDifferentUser(ProcessStartInfo processStartInfo)
|
public static Process RunAsDifferentUser(ProcessStartInfo processStartInfo)
|
||||||
{
|
{
|
||||||
processStartInfo.Verb = "RunAsUser";
|
processStartInfo.Verb = "RunAsUser";
|
||||||
|
|
@ -65,6 +79,15 @@ namespace Flow.Launcher.Plugin.SharedCommands
|
||||||
return buffer[..length].ToString();
|
return buffer[..length].ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Runs a windows command using the provided ProcessStartInfo
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fileName"></param>
|
||||||
|
/// <param name="workingDirectory"></param>
|
||||||
|
/// <param name="arguments"></param>
|
||||||
|
/// <param name="verb"></param>
|
||||||
|
/// <param name="createNoWindow"></param>
|
||||||
|
/// <returns></returns>
|
||||||
public static ProcessStartInfo SetProcessStartInfo(this string fileName, string workingDirectory = "",
|
public static ProcessStartInfo SetProcessStartInfo(this string fileName, string workingDirectory = "",
|
||||||
string arguments = "", string verb = "", bool createNoWindow = false)
|
string arguments = "", string verb = "", bool createNoWindow = false)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,29 @@
|
||||||
|
|
||||||
namespace Flow.Launcher.Plugin.SharedModels
|
namespace Flow.Launcher.Plugin.SharedModels
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the result of a match operation.
|
||||||
|
/// </summary>
|
||||||
public class MatchResult
|
public class MatchResult
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="MatchResult"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="success"></param>
|
||||||
|
/// <param name="searchPrecision"></param>
|
||||||
public MatchResult(bool success, SearchPrecisionScore searchPrecision)
|
public MatchResult(bool success, SearchPrecisionScore searchPrecision)
|
||||||
{
|
{
|
||||||
Success = success;
|
Success = success;
|
||||||
SearchPrecision = searchPrecision;
|
SearchPrecision = searchPrecision;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="MatchResult"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="success"></param>
|
||||||
|
/// <param name="searchPrecision"></param>
|
||||||
|
/// <param name="matchData"></param>
|
||||||
|
/// <param name="rawScore"></param>
|
||||||
public MatchResult(bool success, SearchPrecisionScore searchPrecision, List<int> matchData, int rawScore)
|
public MatchResult(bool success, SearchPrecisionScore searchPrecision, List<int> matchData, int rawScore)
|
||||||
{
|
{
|
||||||
Success = success;
|
Success = success;
|
||||||
|
|
@ -18,6 +33,9 @@ namespace Flow.Launcher.Plugin.SharedModels
|
||||||
RawScore = rawScore;
|
RawScore = rawScore;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the match operation was successful.
|
||||||
|
/// </summary>
|
||||||
public bool Success { get; set; }
|
public bool Success { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -30,6 +48,9 @@ namespace Flow.Launcher.Plugin.SharedModels
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private int _rawScore;
|
private int _rawScore;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The raw calculated search score without any search precision filtering applied.
|
||||||
|
/// </summary>
|
||||||
public int RawScore
|
public int RawScore
|
||||||
{
|
{
|
||||||
get { return _rawScore; }
|
get { return _rawScore; }
|
||||||
|
|
@ -45,8 +66,15 @@ namespace Flow.Launcher.Plugin.SharedModels
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<int> MatchData { get; set; }
|
public List<int> MatchData { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The search precision score used to filter the search results.
|
||||||
|
/// </summary>
|
||||||
public SearchPrecisionScore SearchPrecision { get; set; }
|
public SearchPrecisionScore SearchPrecision { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if the search precision score is met.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
public bool IsSearchPrecisionScoreMet()
|
public bool IsSearchPrecisionScoreMet()
|
||||||
{
|
{
|
||||||
return IsSearchPrecisionScoreMet(_rawScore);
|
return IsSearchPrecisionScoreMet(_rawScore);
|
||||||
|
|
@ -63,10 +91,24 @@ namespace Flow.Launcher.Plugin.SharedModels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the search precision score used to filter search results.
|
||||||
|
/// </summary>
|
||||||
public enum SearchPrecisionScore
|
public enum SearchPrecisionScore
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The highest search precision score.
|
||||||
|
/// </summary>
|
||||||
Regular = 50,
|
Regular = 50,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The medium search precision score.
|
||||||
|
/// </summary>
|
||||||
Low = 20,
|
Low = 20,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The lowest search precision score.
|
||||||
|
/// </summary>
|
||||||
None = 0
|
None = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ using Flow.Launcher.Core.Resource;
|
||||||
using Flow.Launcher.Plugin;
|
using Flow.Launcher.Plugin;
|
||||||
using Flow.Launcher.ViewModel;
|
using Flow.Launcher.ViewModel;
|
||||||
using Flow.Launcher.Core;
|
using Flow.Launcher.Core;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Flow.Launcher
|
namespace Flow.Launcher
|
||||||
{
|
{
|
||||||
|
|
@ -32,14 +34,37 @@ namespace Flow.Launcher
|
||||||
|
|
||||||
private void btnDone_OnClick(object sender, RoutedEventArgs _)
|
private void btnDone_OnClick(object sender, RoutedEventArgs _)
|
||||||
{
|
{
|
||||||
var oldActionKeyword = plugin.Metadata.ActionKeywords[0];
|
var oldActionKeywords = plugin.Metadata.ActionKeywords;
|
||||||
var newActionKeyword = tbAction.Text.Trim();
|
|
||||||
newActionKeyword = newActionKeyword.Length > 0 ? newActionKeyword : "*";
|
var newActionKeywords = tbAction.Text.Split(Query.ActionKeywordSeparator).ToList();
|
||||||
|
newActionKeywords.RemoveAll(string.IsNullOrEmpty);
|
||||||
if (!PluginViewModel.IsActionKeywordRegistered(newActionKeyword))
|
newActionKeywords = newActionKeywords.Distinct().ToList();
|
||||||
|
|
||||||
|
newActionKeywords = newActionKeywords.Count > 0 ? newActionKeywords : new() { Query.GlobalPluginWildcardSign };
|
||||||
|
|
||||||
|
var addedActionKeywords = newActionKeywords.Except(oldActionKeywords).ToList();
|
||||||
|
var removedActionKeywords = oldActionKeywords.Except(newActionKeywords).ToList();
|
||||||
|
if (!addedActionKeywords.Any(App.API.ActionKeywordAssigned))
|
||||||
{
|
{
|
||||||
pluginViewModel.ChangeActionKeyword(newActionKeyword, oldActionKeyword);
|
if (oldActionKeywords.Count != newActionKeywords.Count)
|
||||||
Close();
|
{
|
||||||
|
ReplaceActionKeyword(plugin.Metadata.ID, removedActionKeywords, addedActionKeywords);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var sortedOldActionKeywords = oldActionKeywords.OrderBy(s => s).ToList();
|
||||||
|
var sortedNewActionKeywords = newActionKeywords.OrderBy(s => s).ToList();
|
||||||
|
|
||||||
|
if (sortedOldActionKeywords.SequenceEqual(sortedNewActionKeywords))
|
||||||
|
{
|
||||||
|
// User just changes the sequence of action keywords
|
||||||
|
var msg = translater.GetTranslation("newActionKeywordsSameAsOld");
|
||||||
|
MessageBoxEx.Show(msg);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ReplaceActionKeyword(plugin.Metadata.ID, removedActionKeywords, addedActionKeywords);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -47,5 +72,21 @@ namespace Flow.Launcher
|
||||||
App.API.ShowMsgBox(msg);
|
App.API.ShowMsgBox(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ReplaceActionKeyword(string id, IReadOnlyList<string> removedActionKeywords, IReadOnlyList<string> addedActionKeywords)
|
||||||
|
{
|
||||||
|
foreach (var actionKeyword in removedActionKeywords)
|
||||||
|
{
|
||||||
|
App.API.RemoveActionKeyword(id, actionKeyword);
|
||||||
|
}
|
||||||
|
foreach (var actionKeyword in addedActionKeywords)
|
||||||
|
{
|
||||||
|
App.API.AddActionKeyword(id, actionKeyword);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update action keywords text and close window
|
||||||
|
pluginViewModel.OnActionKeywordsChanged();
|
||||||
|
Close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:ui="http://schemas.modernwpf.com/2019"
|
xmlns:ui="http://schemas.modernwpf.com/2019"
|
||||||
ShutdownMode="OnMainWindowClose"
|
ShutdownMode="OnMainWindowClose"
|
||||||
Startup="OnStartupAsync">
|
Startup="OnStartup">
|
||||||
<Application.Resources>
|
<Application.Resources>
|
||||||
<ResourceDictionary>
|
<ResourceDictionary>
|
||||||
<ResourceDictionary.MergedDictionaries>
|
<ResourceDictionary.MergedDictionaries>
|
||||||
|
|
|
||||||
|
|
@ -27,11 +27,26 @@ namespace Flow.Launcher
|
||||||
{
|
{
|
||||||
public partial class App : IDisposable, ISingleInstanceApp
|
public partial class App : IDisposable, ISingleInstanceApp
|
||||||
{
|
{
|
||||||
|
#region Public Properties
|
||||||
|
|
||||||
public static IPublicAPI API { get; private set; }
|
public static IPublicAPI API { get; private set; }
|
||||||
private const string Unique = "Flow.Launcher_Unique_Application_Mutex";
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Private Fields
|
||||||
|
|
||||||
private static bool _disposed;
|
private static bool _disposed;
|
||||||
|
private MainWindow _mainWindow;
|
||||||
|
private readonly MainViewModel _mainVM;
|
||||||
private readonly Settings _settings;
|
private readonly Settings _settings;
|
||||||
|
|
||||||
|
// To prevent two disposals running at the same time.
|
||||||
|
private static readonly object _disposingLock = new();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructor
|
||||||
|
|
||||||
public App()
|
public App()
|
||||||
{
|
{
|
||||||
// Initialize settings
|
// Initialize settings
|
||||||
|
|
@ -79,40 +94,55 @@ namespace Flow.Launcher
|
||||||
{
|
{
|
||||||
API = Ioc.Default.GetRequiredService<IPublicAPI>();
|
API = Ioc.Default.GetRequiredService<IPublicAPI>();
|
||||||
_settings.Initialize();
|
_settings.Initialize();
|
||||||
|
_mainVM = Ioc.Default.GetRequiredService<MainViewModel>();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
ShowErrorMsgBoxAndFailFast("Cannot initialize api and settings, please open new issue in Flow.Launcher", e);
|
ShowErrorMsgBoxAndFailFast("Cannot initialize api and settings, please open new issue in Flow.Launcher", e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Local function
|
||||||
|
static void ShowErrorMsgBoxAndFailFast(string message, Exception e)
|
||||||
|
{
|
||||||
|
// Firstly show users the message
|
||||||
|
MessageBox.Show(e.ToString(), message, MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
|
||||||
|
// Flow cannot construct its App instance, so ensure Flow crashes w/ the exception info.
|
||||||
|
Environment.FailFast(message, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ShowErrorMsgBoxAndFailFast(string message, Exception e)
|
#endregion
|
||||||
{
|
|
||||||
// Firstly show users the message
|
|
||||||
MessageBox.Show(e.ToString(), message, MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
|
|
||||||
// Flow cannot construct its App instance, so ensure Flow crashes w/ the exception info.
|
#region Main
|
||||||
Environment.FailFast(message, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
[STAThread]
|
[STAThread]
|
||||||
public static void Main()
|
public static void Main()
|
||||||
{
|
{
|
||||||
if (SingleInstance<App>.InitializeAsFirstInstance(Unique))
|
if (SingleInstance<App>.InitializeAsFirstInstance())
|
||||||
{
|
{
|
||||||
using (var application = new App())
|
using var application = new App();
|
||||||
{
|
application.InitializeComponent();
|
||||||
application.InitializeComponent();
|
application.Run();
|
||||||
application.Run();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void OnStartupAsync(object sender, StartupEventArgs e)
|
#endregion
|
||||||
|
|
||||||
|
#region App Events
|
||||||
|
|
||||||
|
#pragma warning disable VSTHRD100 // Avoid async void methods
|
||||||
|
|
||||||
|
private async void OnStartup(object sender, StartupEventArgs e)
|
||||||
{
|
{
|
||||||
await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () =>
|
await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () =>
|
||||||
{
|
{
|
||||||
|
// Because new message box api uses MessageBoxEx window,
|
||||||
|
// if it is created and closed before main window is created, it will cause the application to exit.
|
||||||
|
// So set to OnExplicitShutdown to prevent the application from shutting down before main window is created
|
||||||
|
Current.ShutdownMode = ShutdownMode.OnExplicitShutdown;
|
||||||
|
|
||||||
Log.SetLogLevel(_settings.LogLevel);
|
Log.SetLogLevel(_settings.LogLevel);
|
||||||
|
|
||||||
Ioc.Default.GetRequiredService<Portable>().PreStartCleanUpAfterPortabilityUpdate();
|
Ioc.Default.GetRequiredService<Portable>().PreStartCleanUpAfterPortabilityUpdate();
|
||||||
|
|
@ -127,29 +157,32 @@ namespace Flow.Launcher
|
||||||
|
|
||||||
AbstractPluginEnvironment.PreStartPluginExecutablePathUpdate(_settings);
|
AbstractPluginEnvironment.PreStartPluginExecutablePathUpdate(_settings);
|
||||||
|
|
||||||
// TODO: Clean InternationalizationManager.Instance and InternationalizationManager.Instance.GetTranslation in future
|
|
||||||
InternationalizationManager.Instance.ChangeLanguage(_settings.Language);
|
|
||||||
|
|
||||||
PluginManager.LoadPlugins(_settings.PluginSettings);
|
PluginManager.LoadPlugins(_settings.PluginSettings);
|
||||||
|
|
||||||
|
// Register ResultsUpdated event after all plugins are loaded
|
||||||
|
Ioc.Default.GetRequiredService<MainViewModel>().RegisterResultsUpdatedEvent();
|
||||||
|
|
||||||
Http.Proxy = _settings.Proxy;
|
Http.Proxy = _settings.Proxy;
|
||||||
|
|
||||||
await PluginManager.InitializePluginsAsync();
|
await PluginManager.InitializePluginsAsync();
|
||||||
|
|
||||||
|
// Change language after all plugins are initialized because we need to update plugin title based on their api
|
||||||
|
// TODO: Clean InternationalizationManager.Instance and InternationalizationManager.Instance.GetTranslation in future
|
||||||
|
await Ioc.Default.GetRequiredService<Internationalization>().InitializeLanguageAsync();
|
||||||
|
|
||||||
await imageLoadertask;
|
await imageLoadertask;
|
||||||
|
|
||||||
var mainVM = Ioc.Default.GetRequiredService<MainViewModel>();
|
_mainWindow = new MainWindow();
|
||||||
var window = new MainWindow(_settings, mainVM);
|
|
||||||
|
|
||||||
Log.Info($"|App.OnStartup|Dependencies Info:{ErrorReporting.DependenciesInfo()}");
|
Log.Info($"|App.OnStartup|Dependencies Info:{ErrorReporting.DependenciesInfo()}");
|
||||||
|
|
||||||
Current.MainWindow = window;
|
Current.MainWindow = _mainWindow;
|
||||||
Current.MainWindow.Title = Constant.FlowLauncher;
|
Current.MainWindow.Title = Constant.FlowLauncher;
|
||||||
|
|
||||||
HotKeyMapper.Initialize();
|
HotKeyMapper.Initialize();
|
||||||
|
|
||||||
// main windows needs initialized before theme change because of blur settings
|
// main windows needs initialized before theme change because of blur settings
|
||||||
// TODO: Clean ThemeManager.Instance in future
|
Ioc.Default.GetRequiredService<Theme>().ChangeTheme();
|
||||||
ThemeManager.Instance.ChangeTheme(_settings.Theme);
|
|
||||||
|
|
||||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||||
|
|
||||||
|
|
@ -159,11 +192,12 @@ namespace Flow.Launcher
|
||||||
AutoUpdates();
|
AutoUpdates();
|
||||||
|
|
||||||
API.SaveAppAllSettings();
|
API.SaveAppAllSettings();
|
||||||
Log.Info(
|
Log.Info("|App.OnStartup|End Flow Launcher startup ----------------------------------------------------");
|
||||||
"|App.OnStartup|End Flow Launcher startup ---------------------------------------------------- ");
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma warning restore VSTHRD100 // Avoid async void methods
|
||||||
|
|
||||||
private void AutoStartup()
|
private void AutoStartup()
|
||||||
{
|
{
|
||||||
// we try to enable auto-startup on first launch, or reenable if it was removed
|
// we try to enable auto-startup on first launch, or reenable if it was removed
|
||||||
|
|
@ -186,13 +220,11 @@ namespace Flow.Launcher
|
||||||
// but if it fails (permissions, etc) then don't keep retrying
|
// but if it fails (permissions, etc) then don't keep retrying
|
||||||
// this also gives the user a visual indication in the Settings widget
|
// this also gives the user a visual indication in the Settings widget
|
||||||
_settings.StartFlowLauncherOnSystemStartup = false;
|
_settings.StartFlowLauncherOnSystemStartup = false;
|
||||||
Notification.Show(InternationalizationManager.Instance.GetTranslation("setAutoStartFailed"),
|
API.ShowMsg(API.GetTranslation("setAutoStartFailed"), e.Message);
|
||||||
e.Message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//[Conditional("RELEASE")]
|
|
||||||
private void AutoUpdates()
|
private void AutoUpdates()
|
||||||
{
|
{
|
||||||
_ = Task.Run(async () =>
|
_ = Task.Run(async () =>
|
||||||
|
|
@ -210,11 +242,29 @@ namespace Flow.Launcher
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Register Events
|
||||||
|
|
||||||
private void RegisterExitEvents()
|
private void RegisterExitEvents()
|
||||||
{
|
{
|
||||||
AppDomain.CurrentDomain.ProcessExit += (s, e) => Dispose();
|
AppDomain.CurrentDomain.ProcessExit += (s, e) =>
|
||||||
Current.Exit += (s, e) => Dispose();
|
{
|
||||||
Current.SessionEnding += (s, e) => Dispose();
|
Log.Info("|App.RegisterExitEvents|Process Exit");
|
||||||
|
Dispose();
|
||||||
|
};
|
||||||
|
|
||||||
|
Current.Exit += (s, e) =>
|
||||||
|
{
|
||||||
|
Log.Info("|App.RegisterExitEvents|Application Exit");
|
||||||
|
Dispose();
|
||||||
|
};
|
||||||
|
|
||||||
|
Current.SessionEnding += (s, e) =>
|
||||||
|
{
|
||||||
|
Log.Info("|App.RegisterExitEvents|Session Ending");
|
||||||
|
Dispose();
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -235,20 +285,60 @@ namespace Flow.Launcher
|
||||||
AppDomain.CurrentDomain.UnhandledException += ErrorReporting.UnhandledExceptionHandle;
|
AppDomain.CurrentDomain.UnhandledException += ErrorReporting.UnhandledExceptionHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
#endregion
|
||||||
|
|
||||||
|
#region IDisposable
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
// if sessionending is called, exit proverbially be called when log off / shutdown
|
// Prevent two disposes at the same time.
|
||||||
// but if sessionending is not called, exit won't be called when log off / shutdown
|
lock (_disposingLock)
|
||||||
if (!_disposed)
|
|
||||||
{
|
{
|
||||||
API.SaveAppAllSettings();
|
if (!disposing)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_disposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Stopwatch.Normal("|App.Dispose|Dispose cost", () =>
|
||||||
|
{
|
||||||
|
Log.Info("|App.Dispose|Begin Flow Launcher dispose ----------------------------------------------------");
|
||||||
|
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
// Dispose needs to be called on the main Windows thread,
|
||||||
|
// since some resources owned by the thread need to be disposed.
|
||||||
|
_mainWindow?.Dispatcher.Invoke(_mainWindow.Dispose);
|
||||||
|
_mainVM?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Info("|App.Dispose|End Flow Launcher dispose ----------------------------------------------------");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||||
|
Dispose(disposing: true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ISingleInstanceApp
|
||||||
|
|
||||||
public void OnSecondAppStarted()
|
public void OnSecondAppStarted()
|
||||||
{
|
{
|
||||||
Ioc.Default.GetRequiredService<MainViewModel>().Show();
|
Ioc.Default.GetRequiredService<MainViewModel>().Show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -94,10 +94,6 @@
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
|
||||||
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.3" />
|
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.3" />
|
||||||
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.106">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
<!-- ModernWpfUI v0.9.5 introduced WinRT changes that causes Notification platform unavailable error on some machines -->
|
<!-- ModernWpfUI v0.9.5 introduced WinRT changes that causes Notification platform unavailable error on some machines -->
|
||||||
<!-- https://github.com/Flow-Launcher/Flow.Launcher/issues/1772#issuecomment-1502440801 -->
|
<!-- https://github.com/Flow-Launcher/Flow.Launcher/issues/1772#issuecomment-1502440801 -->
|
||||||
<PackageReference Include="ModernWpfUI" Version="0.9.4" />
|
<PackageReference Include="ModernWpfUI" Version="0.9.4" />
|
||||||
|
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Windows;
|
|
||||||
using System.Windows.Interop;
|
|
||||||
using Windows.Win32;
|
|
||||||
using Windows.Win32.Foundation;
|
|
||||||
using Windows.Win32.Graphics.Dwm;
|
|
||||||
using Windows.Win32.UI.Controls;
|
|
||||||
|
|
||||||
namespace Flow.Launcher.Helper;
|
|
||||||
|
|
||||||
public class DwmDropShadow
|
|
||||||
{
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Drops a standard shadow to a WPF Window, even if the window isborderless. Only works with DWM (Vista and Seven).
|
|
||||||
/// This method is much more efficient than setting AllowsTransparency to true and using the DropShadow effect,
|
|
||||||
/// as AllowsTransparency involves a huge permormance issue (hardware acceleration is turned off for all the window).
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="window">Window to which the shadow will be applied</param>
|
|
||||||
public static void DropShadowToWindow(Window window)
|
|
||||||
{
|
|
||||||
if (!DropShadow(window))
|
|
||||||
{
|
|
||||||
window.SourceInitialized += window_SourceInitialized;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void window_SourceInitialized(object sender, EventArgs e) //fixed typo
|
|
||||||
{
|
|
||||||
Window window = (Window)sender;
|
|
||||||
|
|
||||||
DropShadow(window);
|
|
||||||
|
|
||||||
window.SourceInitialized -= window_SourceInitialized;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The actual method that makes API calls to drop the shadow to the window
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="window">Window to which the shadow will be applied</param>
|
|
||||||
/// <returns>True if the method succeeded, false if not</returns>
|
|
||||||
private static unsafe bool DropShadow(Window window)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
WindowInteropHelper helper = new WindowInteropHelper(window);
|
|
||||||
int val = 2;
|
|
||||||
var ret1 = PInvoke.DwmSetWindowAttribute(new (helper.Handle), DWMWINDOWATTRIBUTE.DWMWA_NCRENDERING_POLICY, &val, 4);
|
|
||||||
|
|
||||||
if (ret1 == HRESULT.S_OK)
|
|
||||||
{
|
|
||||||
var m = new MARGINS { cyBottomHeight = 0, cxLeftWidth = 0, cxRightWidth = 0, cyTopHeight = 0 };
|
|
||||||
var ret2 = PInvoke.DwmExtendFrameIntoClientArea(new(helper.Handle), &m);
|
|
||||||
return ret2 == HRESULT.S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
// Probably dwmapi.dll not found (incompatible OS)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -8,10 +8,10 @@ using System.Windows;
|
||||||
// modified to allow single instace restart
|
// modified to allow single instace restart
|
||||||
namespace Flow.Launcher.Helper
|
namespace Flow.Launcher.Helper
|
||||||
{
|
{
|
||||||
public interface ISingleInstanceApp
|
public interface ISingleInstanceApp
|
||||||
{
|
{
|
||||||
void OnSecondAppStarted();
|
void OnSecondAppStarted();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This class checks to make sure that only one instance of
|
/// This class checks to make sure that only one instance of
|
||||||
|
|
@ -24,9 +24,7 @@ namespace Flow.Launcher.Helper
|
||||||
/// running as Administrator, can activate it with command line arguments.
|
/// running as Administrator, can activate it with command line arguments.
|
||||||
/// For most apps, this will not be much of an issue.
|
/// For most apps, this will not be much of an issue.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public static class SingleInstance<TApplication>
|
public static class SingleInstance<TApplication> where TApplication : Application, ISingleInstanceApp
|
||||||
where TApplication: Application , ISingleInstanceApp
|
|
||||||
|
|
||||||
{
|
{
|
||||||
#region Private Fields
|
#region Private Fields
|
||||||
|
|
||||||
|
|
@ -39,11 +37,12 @@ namespace Flow.Launcher.Helper
|
||||||
/// Suffix to the channel name.
|
/// Suffix to the channel name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const string ChannelNameSuffix = "SingeInstanceIPCChannel";
|
private const string ChannelNameSuffix = "SingeInstanceIPCChannel";
|
||||||
|
private const string InstanceMutexName = "Flow.Launcher_Unique_Application_Mutex";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Application mutex.
|
/// Application mutex.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static Mutex singleInstanceMutex;
|
internal static Mutex SingleInstanceMutex { get; set; }
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
@ -54,24 +53,23 @@ namespace Flow.Launcher.Helper
|
||||||
/// If not, activates the first instance.
|
/// If not, activates the first instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>True if this is the first instance of the application.</returns>
|
/// <returns>True if this is the first instance of the application.</returns>
|
||||||
public static bool InitializeAsFirstInstance( string uniqueName )
|
public static bool InitializeAsFirstInstance()
|
||||||
{
|
{
|
||||||
// Build unique application Id and the IPC channel name.
|
// Build unique application Id and the IPC channel name.
|
||||||
string applicationIdentifier = uniqueName + Environment.UserName;
|
string applicationIdentifier = InstanceMutexName + Environment.UserName;
|
||||||
|
|
||||||
string channelName = String.Concat(applicationIdentifier, Delimiter, ChannelNameSuffix);
|
string channelName = string.Concat(applicationIdentifier, Delimiter, ChannelNameSuffix);
|
||||||
|
|
||||||
// Create mutex based on unique application Id to check if this is the first instance of the application.
|
// Create mutex based on unique application Id to check if this is the first instance of the application.
|
||||||
bool firstInstance;
|
SingleInstanceMutex = new Mutex(true, applicationIdentifier, out var firstInstance);
|
||||||
singleInstanceMutex = new Mutex(true, applicationIdentifier, out firstInstance);
|
|
||||||
if (firstInstance)
|
if (firstInstance)
|
||||||
{
|
{
|
||||||
_ = CreateRemoteService(channelName);
|
_ = CreateRemoteServiceAsync(channelName);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_ = SignalFirstInstance(channelName);
|
_ = SignalFirstInstanceAsync(channelName);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -81,7 +79,7 @@ namespace Flow.Launcher.Helper
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static void Cleanup()
|
public static void Cleanup()
|
||||||
{
|
{
|
||||||
singleInstanceMutex?.ReleaseMutex();
|
SingleInstanceMutex?.ReleaseMutex();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
@ -93,22 +91,19 @@ namespace Flow.Launcher.Helper
|
||||||
/// Once receives signal from client, will activate first instance.
|
/// Once receives signal from client, will activate first instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="channelName">Application's IPC channel name.</param>
|
/// <param name="channelName">Application's IPC channel name.</param>
|
||||||
private static async Task CreateRemoteService(string channelName)
|
private static async Task CreateRemoteServiceAsync(string channelName)
|
||||||
{
|
{
|
||||||
using (NamedPipeServerStream pipeServer = new NamedPipeServerStream(channelName, PipeDirection.In))
|
using NamedPipeServerStream pipeServer = new NamedPipeServerStream(channelName, PipeDirection.In);
|
||||||
|
while (true)
|
||||||
{
|
{
|
||||||
while(true)
|
// Wait for connection to the pipe
|
||||||
{
|
await pipeServer.WaitForConnectionAsync();
|
||||||
// Wait for connection to the pipe
|
|
||||||
await pipeServer.WaitForConnectionAsync();
|
// Do an asynchronous call to ActivateFirstInstance function
|
||||||
if (Application.Current != null)
|
Application.Current?.Dispatcher.Invoke(ActivateFirstInstance);
|
||||||
{
|
|
||||||
// Do an asynchronous call to ActivateFirstInstance function
|
// Disconect client
|
||||||
Application.Current.Dispatcher.Invoke(ActivateFirstInstance);
|
pipeServer.Disconnect();
|
||||||
}
|
|
||||||
// Disconect client
|
|
||||||
pipeServer.Disconnect();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -119,25 +114,13 @@ namespace Flow.Launcher.Helper
|
||||||
/// <param name="args">
|
/// <param name="args">
|
||||||
/// Command line arguments for the second instance, passed to the first instance to take appropriate action.
|
/// Command line arguments for the second instance, passed to the first instance to take appropriate action.
|
||||||
/// </param>
|
/// </param>
|
||||||
private static async Task SignalFirstInstance(string channelName)
|
private static async Task SignalFirstInstanceAsync(string channelName)
|
||||||
{
|
{
|
||||||
// Create a client pipe connected to server
|
// Create a client pipe connected to server
|
||||||
using (NamedPipeClientStream pipeClient = new NamedPipeClientStream(".", channelName, PipeDirection.Out))
|
using NamedPipeClientStream pipeClient = new NamedPipeClientStream(".", channelName, PipeDirection.Out);
|
||||||
{
|
|
||||||
// Connect to the available pipe
|
|
||||||
await pipeClient.ConnectAsync(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
// Connect to the available pipe
|
||||||
/// Callback for activating first instance of the application.
|
await pipeClient.ConnectAsync(0);
|
||||||
/// </summary>
|
|
||||||
/// <param name="arg">Callback argument.</param>
|
|
||||||
/// <returns>Always null.</returns>
|
|
||||||
private static object ActivateFirstInstanceCallback(object o)
|
|
||||||
{
|
|
||||||
ActivateFirstInstance();
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -2,22 +2,19 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using System.Windows.Media.Imaging;
|
using System.Windows.Media.Imaging;
|
||||||
|
using Flow.Launcher.Infrastructure;
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
using Windows.Win32;
|
|
||||||
using Windows.Win32.UI.WindowsAndMessaging;
|
|
||||||
|
|
||||||
namespace Flow.Launcher.Helper;
|
namespace Flow.Launcher.Helper;
|
||||||
|
|
||||||
public static class WallpaperPathRetrieval
|
public static class WallpaperPathRetrieval
|
||||||
{
|
{
|
||||||
private static readonly int MAX_PATH = 260;
|
private const int MaxCacheSize = 3;
|
||||||
private static readonly int MAX_CACHE_SIZE = 3;
|
private static readonly Dictionary<(string, DateTime), ImageBrush> WallpaperCache = new();
|
||||||
|
private static readonly object CacheLock = new();
|
||||||
private static readonly Dictionary<(string, DateTime), ImageBrush> wallpaperCache = new();
|
|
||||||
|
|
||||||
public static Brush GetWallpaperBrush()
|
public static Brush GetWallpaperBrush()
|
||||||
{
|
{
|
||||||
|
|
@ -29,47 +26,72 @@ public static class WallpaperPathRetrieval
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var wallpaperPath = GetWallpaperPath();
|
var wallpaperPath = Win32Helper.GetWallpaperPath();
|
||||||
if (wallpaperPath is not null && File.Exists(wallpaperPath))
|
if (string.IsNullOrEmpty(wallpaperPath) || !File.Exists(wallpaperPath))
|
||||||
{
|
{
|
||||||
// Since the wallpaper file name can be the same (TranscodedWallpaper),
|
App.API.LogInfo(nameof(WallpaperPathRetrieval), $"Wallpaper path is invalid: {wallpaperPath}");
|
||||||
// we need to add the last modified date to differentiate them
|
var wallpaperColor = GetWallpaperColor();
|
||||||
var dateModified = File.GetLastWriteTime(wallpaperPath);
|
return new SolidColorBrush(wallpaperColor);
|
||||||
wallpaperCache.TryGetValue((wallpaperPath, dateModified), out var cachedWallpaper);
|
}
|
||||||
|
|
||||||
|
// Since the wallpaper file name can be the same (TranscodedWallpaper),
|
||||||
|
// we need to add the last modified date to differentiate them
|
||||||
|
var dateModified = File.GetLastWriteTime(wallpaperPath);
|
||||||
|
lock (CacheLock)
|
||||||
|
{
|
||||||
|
WallpaperCache.TryGetValue((wallpaperPath, dateModified), out var cachedWallpaper);
|
||||||
if (cachedWallpaper != null)
|
if (cachedWallpaper != null)
|
||||||
{
|
{
|
||||||
return cachedWallpaper;
|
return cachedWallpaper;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using var fileStream = File.OpenRead(wallpaperPath);
|
||||||
|
var decoder = BitmapDecoder.Create(fileStream, BitmapCreateOptions.DelayCreation, BitmapCacheOption.None);
|
||||||
|
var frame = decoder.Frames[0];
|
||||||
|
var originalWidth = frame.PixelWidth;
|
||||||
|
var originalHeight = frame.PixelHeight;
|
||||||
|
|
||||||
// We should not dispose the memory stream since the bitmap is still in use
|
if (originalWidth == 0 || originalHeight == 0)
|
||||||
var memStream = new MemoryStream(File.ReadAllBytes(wallpaperPath));
|
{
|
||||||
var bitmap = new BitmapImage();
|
App.API.LogInfo(nameof(WallpaperPathRetrieval), $"Failed to load bitmap: Width={originalWidth}, Height={originalHeight}");
|
||||||
bitmap.BeginInit();
|
return new SolidColorBrush(Colors.Transparent);
|
||||||
bitmap.StreamSource = memStream;
|
}
|
||||||
bitmap.DecodePixelWidth = 800;
|
|
||||||
bitmap.DecodePixelHeight = 600;
|
|
||||||
bitmap.EndInit();
|
|
||||||
bitmap.Freeze(); // Make the bitmap thread-safe
|
|
||||||
var wallpaperBrush = new ImageBrush(bitmap) { Stretch = Stretch.UniformToFill };
|
|
||||||
wallpaperBrush.Freeze(); // Make the brush thread-safe
|
|
||||||
|
|
||||||
// Manage cache size
|
// Calculate the scaling factor to fit the image within 800x600 while preserving aspect ratio
|
||||||
if (wallpaperCache.Count >= MAX_CACHE_SIZE)
|
var widthRatio = 800.0 / originalWidth;
|
||||||
|
var heightRatio = 600.0 / originalHeight;
|
||||||
|
var scaleFactor = Math.Min(widthRatio, heightRatio);
|
||||||
|
var decodedPixelWidth = (int)(originalWidth * scaleFactor);
|
||||||
|
var decodedPixelHeight = (int)(originalHeight * scaleFactor);
|
||||||
|
|
||||||
|
// Set DecodePixelWidth and DecodePixelHeight to resize the image while preserving aspect ratio
|
||||||
|
var bitmap = new BitmapImage();
|
||||||
|
bitmap.BeginInit();
|
||||||
|
bitmap.UriSource = new Uri(wallpaperPath);
|
||||||
|
bitmap.DecodePixelWidth = decodedPixelWidth;
|
||||||
|
bitmap.DecodePixelHeight = decodedPixelHeight;
|
||||||
|
bitmap.EndInit();
|
||||||
|
bitmap.Freeze(); // Make the bitmap thread-safe
|
||||||
|
var wallpaperBrush = new ImageBrush(bitmap) { Stretch = Stretch.UniformToFill };
|
||||||
|
wallpaperBrush.Freeze(); // Make the brush thread-safe
|
||||||
|
|
||||||
|
// Manage cache size
|
||||||
|
lock (CacheLock)
|
||||||
|
{
|
||||||
|
if (WallpaperCache.Count >= MaxCacheSize)
|
||||||
{
|
{
|
||||||
// Remove the oldest wallpaper from the cache
|
// Remove the oldest wallpaper from the cache
|
||||||
var oldestCache = wallpaperCache.Keys.OrderBy(k => k.Item2).FirstOrDefault();
|
var oldestCache = WallpaperCache.Keys.OrderBy(k => k.Item2).FirstOrDefault();
|
||||||
if (oldestCache != default)
|
if (oldestCache != default)
|
||||||
{
|
{
|
||||||
wallpaperCache.Remove(oldestCache);
|
WallpaperCache.Remove(oldestCache);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wallpaperCache.Add((wallpaperPath, dateModified), wallpaperBrush);
|
WallpaperCache.Add((wallpaperPath, dateModified), wallpaperBrush);
|
||||||
return wallpaperBrush;
|
return wallpaperBrush;
|
||||||
}
|
}
|
||||||
|
|
||||||
var wallpaperColor = GetWallpaperColor();
|
|
||||||
return new SolidColorBrush(wallpaperColor);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
@ -78,20 +100,9 @@ public static class WallpaperPathRetrieval
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static unsafe string GetWallpaperPath()
|
|
||||||
{
|
|
||||||
var wallpaperPtr = stackalloc char[MAX_PATH];
|
|
||||||
PInvoke.SystemParametersInfo(SYSTEM_PARAMETERS_INFO_ACTION.SPI_GETDESKWALLPAPER, (uint)MAX_PATH,
|
|
||||||
wallpaperPtr,
|
|
||||||
0);
|
|
||||||
var wallpaper = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(wallpaperPtr);
|
|
||||||
|
|
||||||
return wallpaper.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Color GetWallpaperColor()
|
private static Color GetWallpaperColor()
|
||||||
{
|
{
|
||||||
RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Control Panel\Colors", true);
|
RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Control Panel\Colors", false);
|
||||||
var result = key?.GetValue("Background", null);
|
var result = key?.GetValue("Background", null);
|
||||||
if (result is string strResult)
|
if (result is string strResult)
|
||||||
{
|
{
|
||||||
|
|
@ -100,8 +111,9 @@ public static class WallpaperPathRetrieval
|
||||||
var parts = strResult.Trim().Split(new[] { ' ' }, 3).Select(byte.Parse).ToList();
|
var parts = strResult.Trim().Split(new[] { ' ' }, 3).Select(byte.Parse).ToList();
|
||||||
return Color.FromRgb(parts[0], parts[1], parts[2]);
|
return Color.FromRgb(parts[0], parts[1], parts[2]);
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
App.API.LogException(nameof(WallpaperPathRetrieval), "Error parsing wallpaper color", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,211 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Drawing;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Windows;
|
|
||||||
using System.Windows.Forms;
|
|
||||||
using System.Windows.Interop;
|
|
||||||
using System.Windows.Media;
|
|
||||||
using Windows.Win32;
|
|
||||||
using Windows.Win32.Foundation;
|
|
||||||
using Windows.Win32.UI.WindowsAndMessaging;
|
|
||||||
using Point = System.Windows.Point;
|
|
||||||
|
|
||||||
namespace Flow.Launcher.Helper;
|
|
||||||
|
|
||||||
public class WindowsInteropHelper
|
|
||||||
{
|
|
||||||
private static HWND _hwnd_shell;
|
|
||||||
private static HWND _hwnd_desktop;
|
|
||||||
|
|
||||||
//Accessors for shell and desktop handlers
|
|
||||||
//Will set the variables once and then will return them
|
|
||||||
private static HWND HWND_SHELL
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _hwnd_shell != HWND.Null ? _hwnd_shell : _hwnd_shell = PInvoke.GetShellWindow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static HWND HWND_DESKTOP
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _hwnd_desktop != HWND.Null ? _hwnd_desktop : _hwnd_desktop = PInvoke.GetDesktopWindow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const string WINDOW_CLASS_CONSOLE = "ConsoleWindowClass";
|
|
||||||
const string WINDOW_CLASS_WINTAB = "Flip3D";
|
|
||||||
const string WINDOW_CLASS_PROGMAN = "Progman";
|
|
||||||
const string WINDOW_CLASS_WORKERW = "WorkerW";
|
|
||||||
|
|
||||||
public unsafe static bool IsWindowFullscreen()
|
|
||||||
{
|
|
||||||
//get current active window
|
|
||||||
var hWnd = PInvoke.GetForegroundWindow();
|
|
||||||
|
|
||||||
if (hWnd.Equals(HWND.Null))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//if current active window is desktop or shell, exit early
|
|
||||||
if (hWnd.Equals(HWND_DESKTOP) || hWnd.Equals(HWND_SHELL))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
string windowClass;
|
|
||||||
const int capacity = 256;
|
|
||||||
Span<char> buffer = stackalloc char[capacity];
|
|
||||||
int validLength;
|
|
||||||
fixed (char* pBuffer = buffer)
|
|
||||||
{
|
|
||||||
validLength = PInvoke.GetClassName(hWnd, pBuffer, capacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
windowClass = buffer[..validLength].ToString();
|
|
||||||
|
|
||||||
|
|
||||||
//for Win+Tab (Flip3D)
|
|
||||||
if (windowClass == WINDOW_CLASS_WINTAB)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
PInvoke.GetWindowRect(hWnd, out var appBounds);
|
|
||||||
|
|
||||||
//for console (ConsoleWindowClass), we have to check for negative dimensions
|
|
||||||
if (windowClass == WINDOW_CLASS_CONSOLE)
|
|
||||||
{
|
|
||||||
return appBounds.top < 0 && appBounds.bottom < 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//for desktop (Progman or WorkerW, depends on the system), we have to check
|
|
||||||
if (windowClass is WINDOW_CLASS_PROGMAN or WINDOW_CLASS_WORKERW)
|
|
||||||
{
|
|
||||||
var hWndDesktop = PInvoke.FindWindowEx(hWnd, HWND.Null, "SHELLDLL_DefView", null);
|
|
||||||
hWndDesktop = PInvoke.FindWindowEx(hWndDesktop, HWND.Null, "SysListView32", "FolderView");
|
|
||||||
if (hWndDesktop.Value != (IntPtr.Zero))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle screenBounds = Screen.FromHandle(hWnd).Bounds;
|
|
||||||
return (appBounds.bottom - appBounds.top) == screenBounds.Height &&
|
|
||||||
(appBounds.right - appBounds.left) == screenBounds.Width;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// disable windows toolbar's control box
|
|
||||||
/// this will also disable system menu with Alt+Space hotkey
|
|
||||||
/// </summary>
|
|
||||||
public static void DisableControlBox(Window win)
|
|
||||||
{
|
|
||||||
var hwnd = new HWND(new WindowInteropHelper(win).Handle);
|
|
||||||
|
|
||||||
var style = PInvoke.GetWindowLong(hwnd, WINDOW_LONG_PTR_INDEX.GWL_STYLE);
|
|
||||||
|
|
||||||
if (style == 0)
|
|
||||||
{
|
|
||||||
throw new Win32Exception(Marshal.GetLastPInvokeError());
|
|
||||||
}
|
|
||||||
|
|
||||||
style &= ~(int)WINDOW_STYLE.WS_SYSMENU;
|
|
||||||
|
|
||||||
var previousStyle = PInvoke.SetWindowLong(hwnd, WINDOW_LONG_PTR_INDEX.GWL_STYLE,
|
|
||||||
style);
|
|
||||||
|
|
||||||
if (previousStyle == 0)
|
|
||||||
{
|
|
||||||
throw new Win32Exception(Marshal.GetLastPInvokeError());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Transforms pixels to Device Independent Pixels used by WPF
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="visual">current window, required to get presentation source</param>
|
|
||||||
/// <param name="unitX">horizontal position in pixels</param>
|
|
||||||
/// <param name="unitY">vertical position in pixels</param>
|
|
||||||
/// <returns>point containing device independent pixels</returns>
|
|
||||||
public static Point TransformPixelsToDIP(Visual visual, double unitX, double unitY)
|
|
||||||
{
|
|
||||||
Matrix matrix;
|
|
||||||
var source = PresentationSource.FromVisual(visual);
|
|
||||||
if (source is not null)
|
|
||||||
{
|
|
||||||
matrix = source.CompositionTarget.TransformFromDevice;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
using var src = new HwndSource(new HwndSourceParameters());
|
|
||||||
matrix = src.CompositionTarget.TransformFromDevice;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Point((int)(matrix.M11 * unitX), (int)(matrix.M22 * unitY));
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Alt Tab
|
|
||||||
|
|
||||||
private static int SetWindowLong(HWND hWnd, WINDOW_LONG_PTR_INDEX nIndex, int dwNewLong)
|
|
||||||
{
|
|
||||||
PInvoke.SetLastError(WIN32_ERROR.NO_ERROR); // Clear any existing error
|
|
||||||
|
|
||||||
var result = PInvoke.SetWindowLong(hWnd, nIndex, dwNewLong);
|
|
||||||
if (result == 0 && Marshal.GetLastPInvokeError() != 0)
|
|
||||||
{
|
|
||||||
throw new Win32Exception(Marshal.GetLastPInvokeError());
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Hide windows in the Alt+Tab window list
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="window">To hide a window</param>
|
|
||||||
public static void HideFromAltTab(Window window)
|
|
||||||
{
|
|
||||||
var exStyle = GetCurrentWindowStyle(window);
|
|
||||||
|
|
||||||
// Add TOOLWINDOW style, remove APPWINDOW style
|
|
||||||
var newExStyle = ((uint)exStyle | (uint)WINDOW_EX_STYLE.WS_EX_TOOLWINDOW) & ~(uint)WINDOW_EX_STYLE.WS_EX_APPWINDOW;
|
|
||||||
|
|
||||||
SetWindowLong(new(new WindowInteropHelper(window).Handle), WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE, (int)newExStyle);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Restore window display in the Alt+Tab window list.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="window">To restore the displayed window</param>
|
|
||||||
public static void ShowInAltTab(Window window)
|
|
||||||
{
|
|
||||||
var exStyle = GetCurrentWindowStyle(window);
|
|
||||||
|
|
||||||
// Remove the TOOLWINDOW style and add the APPWINDOW style.
|
|
||||||
var newExStyle = ((uint)exStyle & ~(uint)WINDOW_EX_STYLE.WS_EX_TOOLWINDOW) | (uint)WINDOW_EX_STYLE.WS_EX_APPWINDOW;
|
|
||||||
|
|
||||||
SetWindowLong(new(new WindowInteropHelper(window).Handle), WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE, (int)newExStyle);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// To obtain the current overridden style of a window.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="window">To obtain the style dialog window</param>
|
|
||||||
/// <returns>current extension style value</returns>
|
|
||||||
private static int GetCurrentWindowStyle(Window window)
|
|
||||||
{
|
|
||||||
var style = PInvoke.GetWindowLong(new(new WindowInteropHelper(window).Handle), WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE);
|
|
||||||
if (style == 0 && Marshal.GetLastPInvokeError() != 0)
|
|
||||||
{
|
|
||||||
throw new Win32Exception(Marshal.GetLastPInvokeError());
|
|
||||||
}
|
|
||||||
return style;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
|
|
@ -9,6 +9,11 @@
|
||||||
Click no if it's already installed, and you will be prompted to select the folder that contains the {1} executable
|
Click no if it's already installed, and you will be prompted to select the folder that contains the {1} executable
|
||||||
</system:String>
|
</system:String>
|
||||||
<system:String x:Key="runtimePluginChooseRuntimeExecutable">Please select the {0} executable</system:String>
|
<system:String x:Key="runtimePluginChooseRuntimeExecutable">Please select the {0} executable</system:String>
|
||||||
|
<system:String x:Key="runtimeExecutableInvalidChooseDownload">
|
||||||
|
Your selected {0} executable is invalid.
|
||||||
|
{2}{2}
|
||||||
|
Click yes if you would like select the {0} executable agian. Click no if you would like to download {1}
|
||||||
|
</system:String>
|
||||||
<system:String x:Key="runtimePluginUnableToSetExecutablePath">Unable to set {0} executable path, please try from Flow's settings (scroll down to the bottom).</system:String>
|
<system:String x:Key="runtimePluginUnableToSetExecutablePath">Unable to set {0} executable path, please try from Flow's settings (scroll down to the bottom).</system:String>
|
||||||
<system:String x:Key="failedToInitializePluginsTitle">Fail to Init Plugins</system:String>
|
<system:String x:Key="failedToInitializePluginsTitle">Fail to Init Plugins</system:String>
|
||||||
<system:String x:Key="failedToInitializePluginsMessage">Plugins: {0} - fail to load and would be disabled, please contact plugin creator for help</system:String>
|
<system:String x:Key="failedToInitializePluginsMessage">Plugins: {0} - fail to load and would be disabled, please contact plugin creator for help</system:String>
|
||||||
|
|
@ -39,7 +44,7 @@
|
||||||
<system:String x:Key="GameMode">Game Mode</system:String>
|
<system:String x:Key="GameMode">Game Mode</system:String>
|
||||||
<system:String x:Key="GameModeToolTip">Suspend the use of Hotkeys.</system:String>
|
<system:String x:Key="GameModeToolTip">Suspend the use of Hotkeys.</system:String>
|
||||||
<system:String x:Key="PositionReset">Position Reset</system:String>
|
<system:String x:Key="PositionReset">Position Reset</system:String>
|
||||||
<system:String x:Key="PositionResetToolTip">Reset search window position</system:String>
|
<system:String x:Key="queryTextBoxPlaceholder">Type here to search</system:String>
|
||||||
|
|
||||||
<!-- Setting General -->
|
<!-- Setting General -->
|
||||||
<system:String x:Key="flowlauncher_settings">Settings</system:String>
|
<system:String x:Key="flowlauncher_settings">Settings</system:String>
|
||||||
|
|
@ -72,8 +77,6 @@
|
||||||
<system:String x:Key="LastQueryEmpty">Empty last Query</system:String>
|
<system:String x:Key="LastQueryEmpty">Empty last Query</system:String>
|
||||||
<system:String x:Key="LastQueryActionKeywordPreserved">Preserve Last Action Keyword</system:String>
|
<system:String x:Key="LastQueryActionKeywordPreserved">Preserve Last Action Keyword</system:String>
|
||||||
<system:String x:Key="LastQueryActionKeywordSelected">Select Last Action Keyword</system:String>
|
<system:String x:Key="LastQueryActionKeywordSelected">Select Last Action Keyword</system:String>
|
||||||
<system:String x:Key="KeepMaxResults">Fixed Window Height</system:String>
|
|
||||||
<system:String x:Key="KeepMaxResultsToolTip">The window height is not adjustable by dragging.</system:String>
|
|
||||||
<system:String x:Key="maxShowResults">Maximum results shown</system:String>
|
<system:String x:Key="maxShowResults">Maximum results shown</system:String>
|
||||||
<system:String x:Key="maxShowResultsToolTip">You can also quickly adjust this by using CTRL+Plus and CTRL+Minus.</system:String>
|
<system:String x:Key="maxShowResultsToolTip">You can also quickly adjust this by using CTRL+Plus and CTRL+Minus.</system:String>
|
||||||
<system:String x:Key="ignoreHotkeysOnFullscreen">Ignore hotkeys in fullscreen mode</system:String>
|
<system:String x:Key="ignoreHotkeysOnFullscreen">Ignore hotkeys in fullscreen mode</system:String>
|
||||||
|
|
@ -104,6 +107,15 @@
|
||||||
<system:String x:Key="AlwaysPreview">Always Preview</system:String>
|
<system:String x:Key="AlwaysPreview">Always Preview</system:String>
|
||||||
<system:String x:Key="AlwaysPreviewToolTip">Always open preview panel when Flow activates. Press {0} to toggle preview.</system:String>
|
<system:String x:Key="AlwaysPreviewToolTip">Always open preview panel when Flow activates. Press {0} to toggle preview.</system:String>
|
||||||
<system:String x:Key="shadowEffectNotAllowed">Shadow effect is not allowed while current theme has blur effect enabled</system:String>
|
<system:String x:Key="shadowEffectNotAllowed">Shadow effect is not allowed while current theme has blur effect enabled</system:String>
|
||||||
|
<system:String x:Key="searchDelay">Search Delay</system:String>
|
||||||
|
<system:String x:Key="searchDelayToolTip">Delay for a while to search when typing. This reduces interface jumpiness and result load.</system:String>
|
||||||
|
<system:String x:Key="searchDelayTime">Default Search Delay Time</system:String>
|
||||||
|
<system:String x:Key="searchDelayTimeToolTip">Plugin default delay time after which search results appear when typing is stopped.</system:String>
|
||||||
|
<system:String x:Key="SearchDelayTimeVeryLong">Very long</system:String>
|
||||||
|
<system:String x:Key="SearchDelayTimeLong">Long</system:String>
|
||||||
|
<system:String x:Key="SearchDelayTimeNormal">Normal</system:String>
|
||||||
|
<system:String x:Key="SearchDelayTimeShort">Short</system:String>
|
||||||
|
<system:String x:Key="SearchDelayTimeVeryShort">Very short</system:String>
|
||||||
|
|
||||||
<!-- Setting Plugin -->
|
<!-- Setting Plugin -->
|
||||||
<system:String x:Key="searchplugin">Search Plugin</system:String>
|
<system:String x:Key="searchplugin">Search Plugin</system:String>
|
||||||
|
|
@ -120,6 +132,8 @@
|
||||||
<system:String x:Key="currentActionKeywords">Current action keyword</system:String>
|
<system:String x:Key="currentActionKeywords">Current action keyword</system:String>
|
||||||
<system:String x:Key="newActionKeyword">New action keyword</system:String>
|
<system:String x:Key="newActionKeyword">New action keyword</system:String>
|
||||||
<system:String x:Key="actionKeywordsTooltip">Change Action Keywords</system:String>
|
<system:String x:Key="actionKeywordsTooltip">Change Action Keywords</system:String>
|
||||||
|
<system:String x:Key="pluginSearchDelayTime">Plugin seach delay time</system:String>
|
||||||
|
<system:String x:Key="pluginSearchDelayTimeTooltip">Change Plugin Seach Delay Time</system:String>
|
||||||
<system:String x:Key="currentPriority">Current Priority</system:String>
|
<system:String x:Key="currentPriority">Current Priority</system:String>
|
||||||
<system:String x:Key="newPriority">New Priority</system:String>
|
<system:String x:Key="newPriority">New Priority</system:String>
|
||||||
<system:String x:Key="priority">Priority</system:String>
|
<system:String x:Key="priority">Priority</system:String>
|
||||||
|
|
@ -133,6 +147,9 @@
|
||||||
<system:String x:Key="plugin_uninstall">Uninstall</system:String>
|
<system:String x:Key="plugin_uninstall">Uninstall</system:String>
|
||||||
<system:String x:Key="failedToRemovePluginSettingsTitle">Fail to remove plugin settings</system:String>
|
<system:String x:Key="failedToRemovePluginSettingsTitle">Fail to remove plugin settings</system:String>
|
||||||
<system:String x:Key="failedToRemovePluginSettingsMessage">Plugins: {0} - Fail to remove plugin settings files, please remove them manually</system:String>
|
<system:String x:Key="failedToRemovePluginSettingsMessage">Plugins: {0} - Fail to remove plugin settings files, please remove them manually</system:String>
|
||||||
|
<system:String x:Key="failedToRemovePluginCacheTitle">Fail to remove plugin cache</system:String>
|
||||||
|
<system:String x:Key="failedToRemovePluginCacheMessage">Plugins: {0} - Fail to remove plugin cache files, please remove them manually</system:String>
|
||||||
|
<system:String x:Key="default">Default</system:String>
|
||||||
|
|
||||||
<!-- Setting Plugin Store -->
|
<!-- Setting Plugin Store -->
|
||||||
<system:String x:Key="pluginStore">Plugin Store</system:String>
|
<system:String x:Key="pluginStore">Plugin Store</system:String>
|
||||||
|
|
@ -195,8 +212,20 @@
|
||||||
<system:String x:Key="AnimationSpeedCustom">Custom</system:String>
|
<system:String x:Key="AnimationSpeedCustom">Custom</system:String>
|
||||||
<system:String x:Key="Clock">Clock</system:String>
|
<system:String x:Key="Clock">Clock</system:String>
|
||||||
<system:String x:Key="Date">Date</system:String>
|
<system:String x:Key="Date">Date</system:String>
|
||||||
|
<system:String x:Key="BackdropType">Backdrop Type</system:String>
|
||||||
|
<system:String x:Key="BackdropTypeDisabledToolTip">Backdrop supported starting from Windows 11 build 22000 and above</system:String>
|
||||||
|
<system:String x:Key="BackdropTypesNone">None</system:String>
|
||||||
|
<system:String x:Key="BackdropTypesAcrylic">Acrylic</system:String>
|
||||||
|
<system:String x:Key="BackdropTypesMica">Mica</system:String>
|
||||||
|
<system:String x:Key="BackdropTypesMicaAlt">Mica Alt</system:String>
|
||||||
<system:String x:Key="TypeIsDarkToolTip">This theme supports two(light/dark) modes.</system:String>
|
<system:String x:Key="TypeIsDarkToolTip">This theme supports two(light/dark) modes.</system:String>
|
||||||
<system:String x:Key="TypeHasBlurToolTip">This theme supports Blur Transparent Background.</system:String>
|
<system:String x:Key="TypeHasBlurToolTip">This theme supports Blur Transparent Background.</system:String>
|
||||||
|
<system:String x:Key="ShowPlaceholder">Show placeholder</system:String>
|
||||||
|
<system:String x:Key="ShowPlaceholderTip">Display placeholder when query is empty</system:String>
|
||||||
|
<system:String x:Key="PlaceholderText">Placeholder text</system:String>
|
||||||
|
<system:String x:Key="PlaceholderTextTip">Change placeholder text. Input empty will use: {0}</system:String>
|
||||||
|
<system:String x:Key="KeepMaxResults">Fixed Window Size</system:String>
|
||||||
|
<system:String x:Key="KeepMaxResultsToolTip">The window size is not adjustable by dragging.</system:String>
|
||||||
|
|
||||||
<!-- Setting Hotkey -->
|
<!-- Setting Hotkey -->
|
||||||
<system:String x:Key="hotkey">Hotkey</system:String>
|
<system:String x:Key="hotkey">Hotkey</system:String>
|
||||||
|
|
@ -296,6 +325,9 @@
|
||||||
<system:String x:Key="logfolder">Log Folder</system:String>
|
<system:String x:Key="logfolder">Log Folder</system:String>
|
||||||
<system:String x:Key="clearlogfolder">Clear Logs</system:String>
|
<system:String x:Key="clearlogfolder">Clear Logs</system:String>
|
||||||
<system:String x:Key="clearlogfolderMessage">Are you sure you want to delete all logs?</system:String>
|
<system:String x:Key="clearlogfolderMessage">Are you sure you want to delete all logs?</system:String>
|
||||||
|
<system:String x:Key="clearcachefolder">Clear Caches</system:String>
|
||||||
|
<system:String x:Key="clearcachefolderMessage">Are you sure you want to delete all caches?</system:String>
|
||||||
|
<system:String x:Key="clearfolderfailMessage">Failed to clear part of folders and files. Please see log file for more information</system:String>
|
||||||
<system:String x:Key="welcomewindow">Wizard</system:String>
|
<system:String x:Key="welcomewindow">Wizard</system:String>
|
||||||
<system:String x:Key="userdatapath">User Data Location</system:String>
|
<system:String x:Key="userdatapath">User Data Location</system:String>
|
||||||
<system:String x:Key="userdatapathToolTip">User settings and installed plugins are saved in the user data folder. This location may vary depending on whether it's in portable mode or not.</system:String>
|
<system:String x:Key="userdatapathToolTip">User settings and installed plugins are saved in the user data folder. This location may vary depending on whether it's in portable mode or not.</system:String>
|
||||||
|
|
@ -337,9 +369,16 @@
|
||||||
<system:String x:Key="cannotFindSpecifiedPlugin">Can't find specified plugin</system:String>
|
<system:String x:Key="cannotFindSpecifiedPlugin">Can't find specified plugin</system:String>
|
||||||
<system:String x:Key="newActionKeywordsCannotBeEmpty">New Action Keyword can't be empty</system:String>
|
<system:String x:Key="newActionKeywordsCannotBeEmpty">New Action Keyword can't be empty</system:String>
|
||||||
<system:String x:Key="newActionKeywordsHasBeenAssigned">This new Action Keyword is already assigned to another plugin, please choose a different one</system:String>
|
<system:String x:Key="newActionKeywordsHasBeenAssigned">This new Action Keyword is already assigned to another plugin, please choose a different one</system:String>
|
||||||
|
<system:String x:Key="newActionKeywordsSameAsOld">This new Action Keyword is the same as old, please choose a different one</system:String>
|
||||||
<system:String x:Key="success">Success</system:String>
|
<system:String x:Key="success">Success</system:String>
|
||||||
<system:String x:Key="completedSuccessfully">Completed successfully</system:String>
|
<system:String x:Key="completedSuccessfully">Completed successfully</system:String>
|
||||||
<system:String x:Key="actionkeyword_tips">Enter the action keyword you like to use to start the plugin. Use * if you don't want to specify any, and the plugin will be triggered without any action keywords.</system:String>
|
<system:String x:Key="actionkeyword_tips">Enter the action keywords you like to use to start the plugin and use whitespace to divide them. Use * if you don't want to specify any, and the plugin will be triggered without any action keywords.</system:String>
|
||||||
|
|
||||||
|
<!-- Search Delay Settings Dialog -->
|
||||||
|
<system:String x:Key="searchDelayTimeTitle">Search Delay Time Setting</system:String>
|
||||||
|
<system:String x:Key="searchDelayTime_tips">Select the search delay time you like to use for the plugin. Select "{0}" if you don't want to specify any, and the plugin will use default search delay time.</system:String>
|
||||||
|
<system:String x:Key="currentSearchDelayTime">Current search delay time</system:String>
|
||||||
|
<system:String x:Key="newSearchDelayTime">New search delay time</system:String>
|
||||||
|
|
||||||
<!-- Custom Query Hotkey Dialog -->
|
<!-- Custom Query Hotkey Dialog -->
|
||||||
<system:String x:Key="customeQueryHotkeyTitle">Custom Query Hotkey</system:String>
|
<system:String x:Key="customeQueryHotkeyTitle">Custom Query Hotkey</system:String>
|
||||||
|
|
|
||||||
|
|
@ -17,20 +17,20 @@
|
||||||
AllowDrop="True"
|
AllowDrop="True"
|
||||||
AllowsTransparency="True"
|
AllowsTransparency="True"
|
||||||
Background="Transparent"
|
Background="Transparent"
|
||||||
|
Closed="OnClosed"
|
||||||
Closing="OnClosing"
|
Closing="OnClosing"
|
||||||
Deactivated="OnDeactivated"
|
Deactivated="OnDeactivated"
|
||||||
Icon="Images/app.png"
|
Icon="Images/app.png"
|
||||||
SourceInitialized="OnSourceInitialized"
|
|
||||||
Initialized="OnInitialized"
|
|
||||||
Left="{Binding Settings.WindowLeft, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
Left="{Binding Settings.WindowLeft, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||||
Loaded="OnLoaded"
|
Loaded="OnLoaded"
|
||||||
LocationChanged="OnLocationChanged"
|
LocationChanged="OnLocationChanged"
|
||||||
Opacity="{Binding MainWindowOpacity, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
|
|
||||||
PreviewKeyDown="OnKeyDown"
|
PreviewKeyDown="OnKeyDown"
|
||||||
PreviewKeyUp="OnKeyUp"
|
PreviewKeyUp="OnKeyUp"
|
||||||
|
PreviewMouseMove="OnPreviewMouseMove"
|
||||||
ResizeMode="CanResize"
|
ResizeMode="CanResize"
|
||||||
ShowInTaskbar="False"
|
ShowInTaskbar="False"
|
||||||
SizeToContent="Height"
|
SizeToContent="Height"
|
||||||
|
SourceInitialized="OnSourceInitialized"
|
||||||
Topmost="True"
|
Topmost="True"
|
||||||
Visibility="{Binding MainWindowVisibility, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
Visibility="{Binding MainWindowVisibility, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||||
WindowStartupLocation="Manual"
|
WindowStartupLocation="Manual"
|
||||||
|
|
@ -215,9 +215,17 @@
|
||||||
|
|
||||||
<Border MouseDown="OnMouseDown" Style="{DynamicResource WindowBorderStyle}">
|
<Border MouseDown="OnMouseDown" Style="{DynamicResource WindowBorderStyle}">
|
||||||
<StackPanel Orientation="Vertical">
|
<StackPanel Orientation="Vertical">
|
||||||
<Grid>
|
<Grid x:Name="QueryBoxArea">
|
||||||
<Border MinHeight="30" Style="{DynamicResource QueryBoxBgStyle}">
|
<Border MinHeight="30" Style="{DynamicResource QueryBoxBgStyle}">
|
||||||
<Grid>
|
<Grid>
|
||||||
|
<TextBox
|
||||||
|
x:Name="QueryTextPlaceholderBox"
|
||||||
|
Height="{Binding MainWindowHeight, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||||
|
FontSize="{Binding QueryBoxFontSize, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||||
|
IsEnabled="False"
|
||||||
|
Style="{DynamicResource QuerySuggestionBoxStyle}"
|
||||||
|
Text="{Binding PlaceholderText, Mode=OneWay}"
|
||||||
|
Visibility="Collapsed" />
|
||||||
<TextBox
|
<TextBox
|
||||||
x:Name="QueryTextSuggestionBox"
|
x:Name="QueryTextSuggestionBox"
|
||||||
Height="{Binding MainWindowHeight, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
Height="{Binding MainWindowHeight, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||||
|
|
@ -240,14 +248,15 @@
|
||||||
FontSize="{Binding QueryBoxFontSize, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
FontSize="{Binding QueryBoxFontSize, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||||
InputMethod.PreferredImeConversionMode="{Binding StartWithEnglishMode, Converter={StaticResource BoolToIMEConversionModeConverter}}"
|
InputMethod.PreferredImeConversionMode="{Binding StartWithEnglishMode, Converter={StaticResource BoolToIMEConversionModeConverter}}"
|
||||||
InputMethod.PreferredImeState="{Binding StartWithEnglishMode, Converter={StaticResource BoolToIMEStateConverter}}"
|
InputMethod.PreferredImeState="{Binding StartWithEnglishMode, Converter={StaticResource BoolToIMEStateConverter}}"
|
||||||
PreviewDragOver="OnPreviewDragOver"
|
PreviewDragOver="QueryTextBox_OnPreviewDragOver"
|
||||||
PreviewKeyUp="QueryTextBox_KeyUp"
|
PreviewKeyUp="QueryTextBox_KeyUp"
|
||||||
Style="{DynamicResource QueryBoxStyle}"
|
Style="{DynamicResource QueryBoxStyle}"
|
||||||
Text="{Binding QueryText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
Text="{Binding QueryText, Mode=OneWay}"
|
||||||
|
TextChanged="QueryTextBox_TextChanged1"
|
||||||
Visibility="Visible"
|
Visibility="Visible"
|
||||||
WindowChrome.IsHitTestVisibleInChrome="True">
|
WindowChrome.IsHitTestVisibleInChrome="True">
|
||||||
<TextBox.CommandBindings>
|
<TextBox.CommandBindings>
|
||||||
<CommandBinding Command="ApplicationCommands.Copy" Executed="OnCopy" />
|
<CommandBinding Command="ApplicationCommands.Copy" Executed="QueryTextBox_OnCopy" />
|
||||||
</TextBox.CommandBindings>
|
</TextBox.CommandBindings>
|
||||||
<TextBox.ContextMenu>
|
<TextBox.ContextMenu>
|
||||||
<ContextMenu MinWidth="160">
|
<ContextMenu MinWidth="160">
|
||||||
|
|
@ -289,7 +298,9 @@
|
||||||
<StackPanel
|
<StackPanel
|
||||||
x:Name="ClockPanel"
|
x:Name="ClockPanel"
|
||||||
IsHitTestVisible="False"
|
IsHitTestVisible="False"
|
||||||
Style="{DynamicResource ClockPanel}">
|
Opacity="{Binding ClockPanelOpacity}"
|
||||||
|
Style="{DynamicResource ClockPanel}"
|
||||||
|
Visibility="{Binding ClockPanelVisibility}">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
x:Name="ClockBox"
|
x:Name="ClockBox"
|
||||||
Style="{DynamicResource ClockBox}"
|
Style="{DynamicResource ClockBox}"
|
||||||
|
|
@ -317,6 +328,7 @@
|
||||||
Name="SearchIcon"
|
Name="SearchIcon"
|
||||||
Margin="0"
|
Margin="0"
|
||||||
Data="{DynamicResource SearchIconImg}"
|
Data="{DynamicResource SearchIconImg}"
|
||||||
|
Opacity="{Binding SearchIconOpacity}"
|
||||||
Stretch="Fill"
|
Stretch="Fill"
|
||||||
Style="{DynamicResource SearchIconStyle}"
|
Style="{DynamicResource SearchIconStyle}"
|
||||||
Visibility="{Binding SearchIconVisibility}" />
|
Visibility="{Binding SearchIconVisibility}" />
|
||||||
|
|
@ -338,7 +350,7 @@
|
||||||
Y2="0" />
|
Y2="0" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid ClipToBounds="True">
|
<Grid x:Name="MiddleSeparatorArea" ClipToBounds="True">
|
||||||
<ContentControl>
|
<ContentControl>
|
||||||
<ContentControl.Style>
|
<ContentControl.Style>
|
||||||
<Style TargetType="ContentControl">
|
<Style TargetType="ContentControl">
|
||||||
|
|
@ -377,8 +389,8 @@
|
||||||
Style="{DynamicResource SeparatorStyle}" />
|
Style="{DynamicResource SeparatorStyle}" />
|
||||||
</ContentControl>
|
</ContentControl>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Border Style="{DynamicResource WindowRadius}">
|
<Border x:Name="ResultPreviewAreaBoarder" Style="{DynamicResource WindowRadius}">
|
||||||
<Border.Clip>
|
<Border.Clip>
|
||||||
<MultiBinding Converter="{StaticResource BorderClipConverter}">
|
<MultiBinding Converter="{StaticResource BorderClipConverter}">
|
||||||
<Binding Path="ActualWidth" RelativeSource="{RelativeSource Self}" />
|
<Binding Path="ActualWidth" RelativeSource="{RelativeSource Self}" />
|
||||||
|
|
@ -386,12 +398,14 @@
|
||||||
<Binding Path="CornerRadius" RelativeSource="{RelativeSource Self}" />
|
<Binding Path="CornerRadius" RelativeSource="{RelativeSource Self}" />
|
||||||
</MultiBinding>
|
</MultiBinding>
|
||||||
</Border.Clip>
|
</Border.Clip>
|
||||||
<Grid>
|
|
||||||
|
<Grid x:Name="ResultPreviewArea">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="*" MinWidth="80" />
|
<ColumnDefinition Width="*" MinWidth="80" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
<ColumnDefinition Width="0.85*" MinWidth="244" />
|
<ColumnDefinition Width="0.85*" MinWidth="244" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
<StackPanel
|
<StackPanel
|
||||||
x:Name="ResultArea"
|
x:Name="ResultArea"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
|
|
@ -418,7 +432,9 @@
|
||||||
RightClickResultCommand="{Binding RightClickResultCommand}" />
|
RightClickResultCommand="{Binding RightClickResultCommand}" />
|
||||||
</ContentControl>
|
</ContentControl>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<GridSplitter
|
<GridSplitter
|
||||||
|
x:Name="PreviewMiddleSeparator"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Margin="0"
|
Margin="0"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
|
|
@ -432,6 +448,7 @@
|
||||||
</ControlTemplate>
|
</ControlTemplate>
|
||||||
</GridSplitter.Template>
|
</GridSplitter.Template>
|
||||||
</GridSplitter>
|
</GridSplitter>
|
||||||
|
|
||||||
<Grid
|
<Grid
|
||||||
x:Name="Preview"
|
x:Name="Preview"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
|
|
@ -441,7 +458,7 @@
|
||||||
<Border
|
<Border
|
||||||
MinHeight="380"
|
MinHeight="380"
|
||||||
d:DataContext="{d:DesignInstance vm:ResultViewModel}"
|
d:DataContext="{d:DesignInstance vm:ResultViewModel}"
|
||||||
DataContext="{Binding SelectedItem, ElementName=ResultListBox}"
|
DataContext="{Binding PreviewSelectedItem, Mode=OneWay}"
|
||||||
Visibility="{Binding ShowDefaultPreview}">
|
Visibility="{Binding ShowDefaultPreview}">
|
||||||
<Grid
|
<Grid
|
||||||
Margin="0 0 10 5"
|
Margin="0 0 10 5"
|
||||||
|
|
@ -518,7 +535,7 @@
|
||||||
MaxHeight="{Binding ElementName=ResultListBox, Path=ActualHeight}"
|
MaxHeight="{Binding ElementName=ResultListBox, Path=ActualHeight}"
|
||||||
Padding="0 0 10 10"
|
Padding="0 0 10 10"
|
||||||
d:DataContext="{d:DesignInstance vm:ResultViewModel}"
|
d:DataContext="{d:DesignInstance vm:ResultViewModel}"
|
||||||
DataContext="{Binding SelectedItem, ElementName=ResultListBox}"
|
DataContext="{Binding PreviewSelectedItem, Mode=OneWay}"
|
||||||
Visibility="{Binding ShowCustomizedPreview}">
|
Visibility="{Binding ShowCustomizedPreview}">
|
||||||
<ContentControl Content="{Binding Result.PreviewPanel.Value}" />
|
<ContentControl Content="{Binding Result.PreviewPanel.Value}" />
|
||||||
</Border>
|
</Border>
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,10 +1,9 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using System.Windows.Media.Animation;
|
using System.Windows.Media.Animation;
|
||||||
using Flow.Launcher.Helper;
|
|
||||||
using Flow.Launcher.Infrastructure;
|
using Flow.Launcher.Infrastructure;
|
||||||
using Flow.Launcher.Infrastructure.Image;
|
using Flow.Launcher.Infrastructure.Image;
|
||||||
|
|
||||||
|
|
@ -12,14 +11,14 @@ namespace Flow.Launcher
|
||||||
{
|
{
|
||||||
public partial class Msg : Window
|
public partial class Msg : Window
|
||||||
{
|
{
|
||||||
Storyboard fadeOutStoryboard = new Storyboard();
|
private readonly Storyboard fadeOutStoryboard = new();
|
||||||
private bool closing;
|
private bool closing;
|
||||||
|
|
||||||
public Msg()
|
public Msg()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position);
|
var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position);
|
||||||
var dipWorkingArea = WindowsInteropHelper.TransformPixelsToDIP(this,
|
var dipWorkingArea = Win32Helper.TransformPixelsToDIP(this,
|
||||||
screen.WorkingArea.Width,
|
screen.WorkingArea.Width,
|
||||||
screen.WorkingArea.Height);
|
screen.WorkingArea.Height);
|
||||||
Left = dipWorkingArea.X - Width;
|
Left = dipWorkingArea.X - Width;
|
||||||
|
|
@ -82,13 +81,13 @@ namespace Flow.Launcher
|
||||||
Show();
|
Show();
|
||||||
|
|
||||||
await Dispatcher.InvokeAsync(async () =>
|
await Dispatcher.InvokeAsync(async () =>
|
||||||
{
|
{
|
||||||
if (!closing)
|
if (!closing)
|
||||||
{
|
{
|
||||||
closing = true;
|
closing = true;
|
||||||
await Dispatcher.InvokeAsync(fadeOutStoryboard.Begin);
|
await Dispatcher.InvokeAsync(fadeOutStoryboard.Begin);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
DwmSetWindowAttribute
|
|
||||||
DwmExtendFrameIntoClientArea
|
|
||||||
SystemParametersInfo
|
|
||||||
SetForegroundWindow
|
|
||||||
|
|
||||||
GetWindowLong
|
|
||||||
SetWindowLong
|
|
||||||
GetForegroundWindow
|
|
||||||
GetDesktopWindow
|
|
||||||
GetShellWindow
|
|
||||||
GetWindowRect
|
|
||||||
GetClassName
|
|
||||||
FindWindowEx
|
|
||||||
WINDOW_STYLE
|
|
||||||
|
|
||||||
WM_ENTERSIZEMOVE
|
|
||||||
WM_EXITSIZEMOVE
|
|
||||||
|
|
||||||
SetLastError
|
|
||||||
WINDOW_EX_STYLE
|
|
||||||
|
|
@ -1,46 +1,48 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
using Squirrel;
|
using Squirrel;
|
||||||
|
using Flow.Launcher.Core;
|
||||||
using Flow.Launcher.Core.Plugin;
|
using Flow.Launcher.Core.Plugin;
|
||||||
using Flow.Launcher.Core.Resource;
|
|
||||||
using Flow.Launcher.Helper;
|
using Flow.Launcher.Helper;
|
||||||
using Flow.Launcher.Infrastructure;
|
using Flow.Launcher.Infrastructure;
|
||||||
|
using Flow.Launcher.Infrastructure.Http;
|
||||||
using Flow.Launcher.Infrastructure.Hotkey;
|
using Flow.Launcher.Infrastructure.Hotkey;
|
||||||
using Flow.Launcher.Infrastructure.Image;
|
using Flow.Launcher.Infrastructure.Image;
|
||||||
using Flow.Launcher.Plugin;
|
|
||||||
using Flow.Launcher.ViewModel;
|
|
||||||
using Flow.Launcher.Plugin.SharedModels;
|
|
||||||
using Flow.Launcher.Plugin.SharedCommands;
|
|
||||||
using System.Threading;
|
|
||||||
using System.IO;
|
|
||||||
using Flow.Launcher.Infrastructure.Http;
|
|
||||||
using JetBrains.Annotations;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using Flow.Launcher.Infrastructure.Logger;
|
using Flow.Launcher.Infrastructure.Logger;
|
||||||
using Flow.Launcher.Infrastructure.Storage;
|
using Flow.Launcher.Infrastructure.Storage;
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Collections.Specialized;
|
|
||||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
|
||||||
using Flow.Launcher.Core;
|
|
||||||
using Flow.Launcher.Infrastructure.UserSettings;
|
using Flow.Launcher.Infrastructure.UserSettings;
|
||||||
|
using Flow.Launcher.Plugin;
|
||||||
|
using Flow.Launcher.Plugin.SharedModels;
|
||||||
|
using Flow.Launcher.Plugin.SharedCommands;
|
||||||
|
using Flow.Launcher.ViewModel;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Flow.Launcher.Core.Resource;
|
||||||
|
|
||||||
namespace Flow.Launcher
|
namespace Flow.Launcher
|
||||||
{
|
{
|
||||||
public class PublicAPIInstance : IPublicAPI
|
public class PublicAPIInstance : IPublicAPI
|
||||||
{
|
{
|
||||||
private readonly Settings _settings;
|
private readonly Settings _settings;
|
||||||
|
private readonly Internationalization _translater;
|
||||||
private readonly MainViewModel _mainVM;
|
private readonly MainViewModel _mainVM;
|
||||||
|
|
||||||
#region Constructor
|
#region Constructor
|
||||||
|
|
||||||
public PublicAPIInstance(Settings settings, MainViewModel mainVM)
|
public PublicAPIInstance(Settings settings, Internationalization translater, MainViewModel mainVM)
|
||||||
{
|
{
|
||||||
_settings = settings;
|
_settings = settings;
|
||||||
|
_translater = translater;
|
||||||
_mainVM = mainVM;
|
_mainVM = mainVM;
|
||||||
GlobalHotkey.hookedKeyboardCallback = KListener_hookedKeyboardCallback;
|
GlobalHotkey.hookedKeyboardCallback = KListener_hookedKeyboardCallback;
|
||||||
WebRequest.RegisterPrefix("data", new DataWebRequestFactory());
|
WebRequest.RegisterPrefix("data", new DataWebRequestFactory());
|
||||||
|
|
@ -153,17 +155,17 @@ namespace Flow.Launcher
|
||||||
|
|
||||||
public void StopLoadingBar() => _mainVM.ProgressBarVisibility = Visibility.Collapsed;
|
public void StopLoadingBar() => _mainVM.ProgressBarVisibility = Visibility.Collapsed;
|
||||||
|
|
||||||
public string GetTranslation(string key) => InternationalizationManager.Instance.GetTranslation(key);
|
public string GetTranslation(string key) => _translater.GetTranslation(key);
|
||||||
|
|
||||||
public List<PluginPair> GetAllPlugins() => PluginManager.AllPlugins.ToList();
|
public List<PluginPair> GetAllPlugins() => PluginManager.AllPlugins.ToList();
|
||||||
|
|
||||||
public MatchResult FuzzySearch(string query, string stringToCompare) =>
|
public MatchResult FuzzySearch(string query, string stringToCompare) =>
|
||||||
StringMatcher.FuzzySearch(query, stringToCompare);
|
StringMatcher.FuzzySearch(query, stringToCompare);
|
||||||
|
|
||||||
public Task<string> HttpGetStringAsync(string url, CancellationToken token = default) => Http.GetAsync(url);
|
public Task<string> HttpGetStringAsync(string url, CancellationToken token = default) => Http.GetAsync(url, token);
|
||||||
|
|
||||||
public Task<Stream> HttpGetStreamAsync(string url, CancellationToken token = default) =>
|
public Task<Stream> HttpGetStreamAsync(string url, CancellationToken token = default) =>
|
||||||
Http.GetStreamAsync(url);
|
Http.GetStreamAsync(url, token);
|
||||||
|
|
||||||
public Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath, Action<double> reportProgress = null,
|
public Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath, Action<double> reportProgress = null,
|
||||||
CancellationToken token = default) => Http.DownloadAsync(url, filePath, reportProgress, token);
|
CancellationToken token = default) => Http.DownloadAsync(url, filePath, reportProgress, token);
|
||||||
|
|
@ -190,7 +192,7 @@ namespace Flow.Launcher
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<Type, object> _pluginJsonStorages = new();
|
private readonly ConcurrentDictionary<Type, object> _pluginJsonStorages = new();
|
||||||
|
|
||||||
public object RemovePluginSettings(string assemblyName)
|
public void RemovePluginSettings(string assemblyName)
|
||||||
{
|
{
|
||||||
foreach (var keyValuePair in _pluginJsonStorages)
|
foreach (var keyValuePair in _pluginJsonStorages)
|
||||||
{
|
{
|
||||||
|
|
@ -200,11 +202,8 @@ namespace Flow.Launcher
|
||||||
if (name == assemblyName)
|
if (name == assemblyName)
|
||||||
{
|
{
|
||||||
_pluginJsonStorages.Remove(key, out var pluginJsonStorage);
|
_pluginJsonStorages.Remove(key, out var pluginJsonStorage);
|
||||||
return pluginJsonStorage;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -329,7 +328,6 @@ namespace Flow.Launcher
|
||||||
return _mainVM.GameModeStatus;
|
return _mainVM.GameModeStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private readonly List<Func<int, int, SpecialKeyState, bool>> _globalKeyboardHandlers = new();
|
private readonly List<Func<int, int, SpecialKeyState, bool>> _globalKeyboardHandlers = new();
|
||||||
|
|
||||||
public void RegisterGlobalKeyboardCallback(Func<int, int, SpecialKeyState, bool> callback) => _globalKeyboardHandlers.Add(callback);
|
public void RegisterGlobalKeyboardCallback(Func<int, int, SpecialKeyState, bool> callback) => _globalKeyboardHandlers.Add(callback);
|
||||||
|
|
|
||||||
|
|
@ -20,21 +20,21 @@
|
||||||
<Setter Property="BorderThickness" Value="1" />
|
<Setter Property="BorderThickness" Value="1" />
|
||||||
<Setter Property="CornerRadius" Value="5" />
|
<Setter Property="CornerRadius" Value="5" />
|
||||||
<Setter Property="MinHeight" Value="68" />
|
<Setter Property="MinHeight" Value="68" />
|
||||||
<Setter Property="Padding" Value="0,15,0,15" />
|
<Setter Property="Padding" Value="0 15 0 15" />
|
||||||
<Setter Property="Margin" Value="0,4,0,0" />
|
<Setter Property="Margin" Value="0 4 0 0" />
|
||||||
<Setter Property="SnapsToDevicePixels" Value="True" />
|
<Setter Property="SnapsToDevicePixels" Value="True" />
|
||||||
<Style.Triggers>
|
<Style.Triggers>
|
||||||
<DataTrigger Binding="{Binding Type, RelativeSource={RelativeSource AncestorType=local:Card}}" Value="Inside">
|
<DataTrigger Binding="{Binding Type, RelativeSource={RelativeSource AncestorType=local:Card}}" Value="Inside">
|
||||||
<Setter Property="BorderThickness" Value="0,1,0,0" />
|
<Setter Property="BorderThickness" Value="0 1 0 0" />
|
||||||
<Setter Property="CornerRadius" Value="0" />
|
<Setter Property="CornerRadius" Value="0" />
|
||||||
<Setter Property="Margin" Value="0,0,0,0" />
|
<Setter Property="Margin" Value="0 0 0 0" />
|
||||||
<Setter Property="Background" Value="Transparent" />
|
<Setter Property="Background" Value="Transparent" />
|
||||||
</DataTrigger>
|
</DataTrigger>
|
||||||
<DataTrigger Binding="{Binding Type, RelativeSource={RelativeSource AncestorType=local:Card}}" Value="InsideFit">
|
<DataTrigger Binding="{Binding Type, RelativeSource={RelativeSource AncestorType=local:Card}}" Value="InsideFit">
|
||||||
<Setter Property="BorderThickness" Value="0,1,0,0" />
|
<Setter Property="BorderThickness" Value="0 1 0 0" />
|
||||||
<Setter Property="CornerRadius" Value="0" />
|
<Setter Property="CornerRadius" Value="0" />
|
||||||
<Setter Property="Margin" Value="0,0,0,0" />
|
<Setter Property="Margin" Value="0 0 0 0" />
|
||||||
<Setter Property="Padding" Value="38,0,26,0" />
|
<Setter Property="Padding" Value="35 0 26 0" />
|
||||||
<Setter Property="Background" Value="Transparent" />
|
<Setter Property="Background" Value="Transparent" />
|
||||||
</DataTrigger>
|
</DataTrigger>
|
||||||
|
|
||||||
|
|
@ -73,7 +73,7 @@
|
||||||
<ContentControl
|
<ContentControl
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Margin="0,0,16,0"
|
Margin="0 0 16 0"
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Content="{TemplateBinding Content}" />
|
Content="{TemplateBinding Content}" />
|
||||||
|
|
@ -91,7 +91,7 @@
|
||||||
<TextBlock.Style>
|
<TextBlock.Style>
|
||||||
<Style TargetType="{x:Type TextBlock}">
|
<Style TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="{DynamicResource Color05B}" />
|
<Setter Property="Foreground" Value="{DynamicResource Color05B}" />
|
||||||
<Setter Property="Margin" Value="0,0,0,0" />
|
<Setter Property="Margin" Value="0 0 0 0" />
|
||||||
<Setter Property="TextWrapping" Value="Wrap" />
|
<Setter Property="TextWrapping" Value="Wrap" />
|
||||||
<Setter Property="VerticalAlignment" Value="Center" />
|
<Setter Property="VerticalAlignment" Value="Center" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
@ -107,8 +107,8 @@
|
||||||
</Style.Triggers>
|
</Style.Triggers>
|
||||||
<Setter Property="Foreground" Value="{DynamicResource Color04B}" />
|
<Setter Property="Foreground" Value="{DynamicResource Color04B}" />
|
||||||
<Setter Property="FontSize" Value="12" />
|
<Setter Property="FontSize" Value="12" />
|
||||||
<Setter Property="Margin" Value="0,0,0,0" />
|
<Setter Property="Margin" Value="0 0 0 0" />
|
||||||
<Setter Property="Padding" Value="0,0,24,0" />
|
<Setter Property="Padding" Value="0 0 24 0" />
|
||||||
<Setter Property="TextWrapping" Value="WrapWithOverflow" />
|
<Setter Property="TextWrapping" Value="WrapWithOverflow" />
|
||||||
</Style>
|
</Style>
|
||||||
</TextBlock.Style>
|
</TextBlock.Style>
|
||||||
|
|
@ -120,11 +120,11 @@
|
||||||
<Style TargetType="{x:Type TextBlock}">
|
<Style TargetType="{x:Type TextBlock}">
|
||||||
<Style.Triggers>
|
<Style.Triggers>
|
||||||
<DataTrigger Binding="{Binding ElementName=ItemIcon, Path=Text}" Value="{x:Static sys:String.Empty}">
|
<DataTrigger Binding="{Binding ElementName=ItemIcon, Path=Text}" Value="{x:Static sys:String.Empty}">
|
||||||
<Setter Property="Margin" Value="24,0,0,0" />
|
<Setter Property="Margin" Value="24 0 0 0" />
|
||||||
</DataTrigger>
|
</DataTrigger>
|
||||||
</Style.Triggers>
|
</Style.Triggers>
|
||||||
<Setter Property="Grid.Column" Value="0" />
|
<Setter Property="Grid.Column" Value="0" />
|
||||||
<Setter Property="Margin" Value="24,0,16,0" />
|
<Setter Property="Margin" Value="24 0 16 0" />
|
||||||
<Setter Property="VerticalAlignment" Value="Center" />
|
<Setter Property="VerticalAlignment" Value="Center" />
|
||||||
<Setter Property="FontSize" Value="20" />
|
<Setter Property="FontSize" Value="20" />
|
||||||
<Setter Property="FontFamily" Value="/Resources/#Segoe Fluent Icons" />
|
<Setter Property="FontFamily" Value="/Resources/#Segoe Fluent Icons" />
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,16 @@
|
||||||
<UserControl x:Class="Flow.Launcher.Resources.Controls.InstalledPluginDisplay"
|
<UserControl
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
x:Class="Flow.Launcher.Resources.Controls.InstalledPluginDisplay"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:cc="clr-namespace:Flow.Launcher.Resources.Controls"
|
||||||
xmlns:ui="http://schemas.modernwpf.com/2019"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:viewModel="clr-namespace:Flow.Launcher.ViewModel"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:cc="clr-namespace:Flow.Launcher.Resources.Controls"
|
xmlns:ui="http://schemas.modernwpf.com/2019"
|
||||||
mc:Ignorable="d"
|
xmlns:viewModel="clr-namespace:Flow.Launcher.ViewModel"
|
||||||
d:DataContext="{d:DesignInstance viewModel:PluginViewModel}"
|
d:DataContext="{d:DesignInstance viewModel:PluginViewModel}"
|
||||||
d:DesignHeight="300" d:DesignWidth="300">
|
d:DesignHeight="300"
|
||||||
|
d:DesignWidth="300"
|
||||||
|
mc:Ignorable="d">
|
||||||
<Expander
|
<Expander
|
||||||
Padding="0"
|
Padding="0"
|
||||||
BorderThickness="0"
|
BorderThickness="0"
|
||||||
|
|
@ -28,7 +30,7 @@
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Width="32"
|
Width="32"
|
||||||
Height="32"
|
Height="32"
|
||||||
Source="{Binding Image, IsAsync=True}" />
|
Source="{Binding Image, Mode=OneWay, IsAsync=True}" />
|
||||||
<StackPanel Grid.Column="1" Margin="16 0 14 0">
|
<StackPanel Grid.Column="1" Margin="16 0 14 0">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Foreground="{DynamicResource Color05B}"
|
Foreground="{DynamicResource Color05B}"
|
||||||
|
|
@ -37,12 +39,15 @@
|
||||||
ToolTip="{Binding PluginPair.Metadata.Version}" />
|
ToolTip="{Binding PluginPair.Metadata.Version}" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="0 2 0 0"
|
Margin="0 2 0 0"
|
||||||
Foreground="{DynamicResource Color04B}"
|
|
||||||
FontSize="12"
|
FontSize="12"
|
||||||
|
Foreground="{DynamicResource Color04B}"
|
||||||
Text="{Binding PluginPair.Metadata.Description}"
|
Text="{Binding PluginPair.Metadata.Description}"
|
||||||
TextWrapping="WrapWithOverflow" />
|
TextWrapping="WrapWithOverflow" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Grid.Column="2" Orientation="Horizontal" HorizontalAlignment="Right">
|
<StackPanel
|
||||||
|
Grid.Column="2"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Orientation="Horizontal">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="0 0 8 0"
|
Margin="0 0 8 0"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
|
|
@ -70,9 +75,7 @@
|
||||||
<Setter Property="FontWeight" Value="DemiBold" />
|
<Setter Property="FontWeight" Value="DemiBold" />
|
||||||
<Setter Property="Foreground" Value="{DynamicResource Color05B}" />
|
<Setter Property="Foreground" Value="{DynamicResource Color05B}" />
|
||||||
<Style.Triggers>
|
<Style.Triggers>
|
||||||
<DataTrigger
|
<DataTrigger Binding="{Binding ElementName=PriorityButton, UpdateSourceTrigger=PropertyChanged, Path=Content}" Value="0">
|
||||||
Binding="{Binding ElementName=PriorityButton, UpdateSourceTrigger=PropertyChanged, Path=Content}"
|
|
||||||
Value="0">
|
|
||||||
<Setter Property="Foreground" Value="{DynamicResource Color08B}" />
|
<Setter Property="Foreground" Value="{DynamicResource Color08B}" />
|
||||||
<Setter Property="FontWeight" Value="Normal" />
|
<Setter Property="FontWeight" Value="Normal" />
|
||||||
</DataTrigger>
|
</DataTrigger>
|
||||||
|
|
@ -95,10 +98,12 @@
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<ContentControl Content="{Binding BottomPart1}" />
|
<ContentControl Content="{Binding BottomPart1}" />
|
||||||
|
|
||||||
|
<ContentControl Content="{Binding BottomPart2}" />
|
||||||
|
|
||||||
<Border
|
<Border
|
||||||
BorderThickness="0 1 0 0"
|
Background="{DynamicResource Color00B}"
|
||||||
BorderBrush="{DynamicResource Color03B}"
|
BorderBrush="{DynamicResource Color03B}"
|
||||||
Background="{DynamicResource Color00B}">
|
BorderThickness="0 1 0 0">
|
||||||
<Border.Style>
|
<Border.Style>
|
||||||
<Style TargetType="Border">
|
<Style TargetType="Border">
|
||||||
<Style.Triggers>
|
<Style.Triggers>
|
||||||
|
|
@ -115,7 +120,7 @@
|
||||||
Content="{Binding SettingControl}" />
|
Content="{Binding SettingControl}" />
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<ContentControl Content="{Binding BottomPart2}" />
|
<ContentControl Content="{Binding BottomPart3}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Expander>
|
</Expander>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|
|
||||||
|
|
@ -11,30 +11,33 @@
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
<Border
|
<Border
|
||||||
Width="Auto"
|
Width="Auto"
|
||||||
Height="52"
|
Height="Auto"
|
||||||
Margin="0"
|
Margin="0"
|
||||||
Padding="0"
|
Padding="0"
|
||||||
BorderThickness="0 1 0 0"
|
BorderThickness="0 1 0 0"
|
||||||
CornerRadius="0"
|
CornerRadius="0"
|
||||||
Style="{DynamicResource SettingGroupBox}"
|
Style="{DynamicResource SettingGroupBox}"
|
||||||
Visibility="{Binding ActionKeywordsVisibility}">
|
Visibility="{Binding ActionKeywordsVisibility}">
|
||||||
<DockPanel Margin="22 0 18 0" VerticalAlignment="Center">
|
<DockPanel Margin="{StaticResource SettingPanelMargin}">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="48 0 10 0"
|
Margin="{StaticResource SettingPanelItemRightMargin}"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Center"
|
||||||
DockPanel.Dock="Left"
|
DockPanel.Dock="Left"
|
||||||
Style="{StaticResource Glyph}">
|
Style="{StaticResource Glyph}">
|
||||||

|

|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
|
Margin="{StaticResource SettingPanelItemRightMargin}"
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
DockPanel.Dock="Left"
|
DockPanel.Dock="Left"
|
||||||
Style="{DynamicResource SettingTitleLabel}"
|
Style="{DynamicResource SettingTitleLabel}"
|
||||||
Text="{DynamicResource actionKeywords}" />
|
Text="{DynamicResource actionKeywords}" />
|
||||||
|
<!-- Here Margin="0 -4.5 0 -4.5" is to remove redundant top bottom margin from Margin="{StaticResource SettingPanelMargin}" -->
|
||||||
<Button
|
<Button
|
||||||
Width="100"
|
Width="100"
|
||||||
Height="34"
|
Margin="0 -4.5 0 -4.5"
|
||||||
Margin="5 0 0 0"
|
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
Command="{Binding SetActionKeywordsCommand}"
|
Command="{Binding SetActionKeywordsCommand}"
|
||||||
Content="{Binding ActionKeywordsText}"
|
Content="{Binding ActionKeywordsText}"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
<UserControl
|
||||||
|
x:Class="Flow.Launcher.Resources.Controls.InstalledPluginSearchDelay"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:viewModel="clr-namespace:Flow.Launcher.ViewModel"
|
||||||
|
d:DataContext="{d:DesignInstance viewModel:PluginViewModel}"
|
||||||
|
d:DesignHeight="300"
|
||||||
|
d:DesignWidth="300"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
<Border
|
||||||
|
Width="Auto"
|
||||||
|
Height="Auto"
|
||||||
|
Margin="0"
|
||||||
|
Padding="0"
|
||||||
|
BorderThickness="0 1 0 0"
|
||||||
|
CornerRadius="0"
|
||||||
|
Style="{DynamicResource SettingGroupBox}">
|
||||||
|
<DockPanel Margin="{StaticResource SettingPanelMargin}">
|
||||||
|
<TextBlock
|
||||||
|
Margin="{StaticResource SettingPanelItemRightMargin}"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
DockPanel.Dock="Left"
|
||||||
|
Style="{StaticResource Glyph}">
|
||||||
|

|
||||||
|
</TextBlock>
|
||||||
|
<TextBlock
|
||||||
|
Margin="{StaticResource SettingPanelItemRightMargin}"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
DockPanel.Dock="Left"
|
||||||
|
Style="{DynamicResource SettingTitleLabel}"
|
||||||
|
Text="{DynamicResource pluginSearchDelayTime}" />
|
||||||
|
<!-- Here Margin="0 -4.5 0 -4.5" is to remove redundant top bottom margin from Margin="{StaticResource SettingPanelMargin}" -->
|
||||||
|
<Button
|
||||||
|
Width="100"
|
||||||
|
Margin="0 -4.5 0 -4.5"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Command="{Binding SetSearchDelayTimeCommand}"
|
||||||
|
Content="{Binding SearchDelayTimeText}"
|
||||||
|
Cursor="Hand"
|
||||||
|
DockPanel.Dock="Right"
|
||||||
|
FontWeight="Bold"
|
||||||
|
ToolTip="{DynamicResource pluginSearchDelayTimeTooltip}" />
|
||||||
|
</DockPanel>
|
||||||
|
</Border>
|
||||||
|
</UserControl>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
using System.Windows.Controls;
|
||||||
|
|
||||||
|
namespace Flow.Launcher.Resources.Controls;
|
||||||
|
|
||||||
|
public partial class InstalledPluginSearchDelay : UserControl
|
||||||
|
{
|
||||||
|
public InstalledPluginSearchDelay()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5585,4 +5585,31 @@
|
||||||
</Setter.Value>
|
</Setter.Value>
|
||||||
</Setter>
|
</Setter>
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
|
<!-- Settings Panel -->
|
||||||
|
<Thickness x:Key="SettingPanelMargin">70,13.5,18,13.5</Thickness>
|
||||||
|
|
||||||
|
<Thickness x:Key="SettingPanelItemLeftMargin">9,0,0,0</Thickness>
|
||||||
|
<Thickness x:Key="SettingPanelItemRightMargin">0,0,9,0</Thickness>
|
||||||
|
<Thickness x:Key="SettingPanelItemTopBottomMargin">0,4.5,0,4.5</Thickness>
|
||||||
|
|
||||||
|
<Thickness x:Key="SettingPanelItemLeftTopBottomMargin">9,4.5,0,4.5</Thickness>
|
||||||
|
<Thickness x:Key="SettingPanelItemRightTopBottomMargin">0,4.5,9,4.5</Thickness>
|
||||||
|
|
||||||
|
<Style x:Key="SettingPanelSeparatorStyle" TargetType="Separator">
|
||||||
|
<Setter Property="Margin" Value="-70 13.5 -18 13.5" />
|
||||||
|
<Setter Property="Height" Value="1" />
|
||||||
|
<Setter Property="VerticalAlignment" Value="Top" />
|
||||||
|
<Setter Property="Background" Value="{DynamicResource Color03B}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<system:Double x:Key="SettingPanelTextBoxMinWidth">180</system:Double>
|
||||||
|
<system:Double x:Key="SettingPanelPathTextBoxWidth">240</system:Double>
|
||||||
|
<system:Double x:Key="SettingPanelAreaTextBoxMinHeight">150</system:Double>
|
||||||
|
|
||||||
|
<Style x:Key="SettingPanelTextBlockDescriptionStyle" TargetType="TextBlock">
|
||||||
|
<Setter Property="FontSize" Value="12" />
|
||||||
|
<Setter Property="Margin" Value="0 2 0 0" />
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource Color04B}" />
|
||||||
|
</Style>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
|
|
|
||||||
|
|
@ -7,17 +7,17 @@
|
||||||
xmlns:sys="clr-namespace:System;assembly=mscorlib">
|
xmlns:sys="clr-namespace:System;assembly=mscorlib">
|
||||||
|
|
||||||
<SolidColorBrush x:Key="SystemThemeBorder" Color="#3f3f3f" />
|
<SolidColorBrush x:Key="SystemThemeBorder" Color="#3f3f3f" />
|
||||||
<SolidColorBrush x:Key="QuerySuggestionBoxForeground" Color="#4d4d4d" />
|
<SolidColorBrush x:Key="QuerySuggestionBoxForeground" Color="#33FFFFFF" />
|
||||||
<SolidColorBrush x:Key="QuerySelectionBrush" Color="#4a5459" />
|
<SolidColorBrush x:Key="QuerySelectionBrush" Color="#4a5459" />
|
||||||
<SolidColorBrush x:Key="SubTitleForeground" Color="#7b7b7b" />
|
<SolidColorBrush x:Key="SubTitleForeground" Color="#33FFFFFF" />
|
||||||
<SolidColorBrush x:Key="SubTitleSelectedForeground" Color="#7b7b7b" />
|
<SolidColorBrush x:Key="SubTitleSelectedForeground" Color="#33FFFFFF" />
|
||||||
<SolidColorBrush x:Key="SearchIconForeground" Color="#4d4d4d" />
|
<SolidColorBrush x:Key="SearchIconForeground" Color="#33FFFFFF" />
|
||||||
<SolidColorBrush x:Key="SeparatorForeground" Color="#14ffffff" />
|
<SolidColorBrush x:Key="SeparatorForeground" Color="#24FFFFFF" />
|
||||||
<SolidColorBrush x:Key="ThumbColor" Color="#9a9a9a" />
|
<SolidColorBrush x:Key="ThumbColor" Color="#9a9a9a" />
|
||||||
<SolidColorBrush x:Key="InlineHighlight" Color="#0078d7" />
|
<SolidColorBrush x:Key="InlineHighlight" Color="#0078d7" />
|
||||||
<SolidColorBrush x:Key="HotkeyForeground" Color="#7b7b7b" />
|
<SolidColorBrush x:Key="HotkeyForeground" Color="#33FFFFFF" />
|
||||||
<SolidColorBrush x:Key="HotkeySelectedForeground" Color="#7b7b7b" />
|
<SolidColorBrush x:Key="HotkeySelectedForeground" Color="#33FFFFFF" />
|
||||||
<SolidColorBrush x:Key="ClockDateForeground" Color="#4d4d4d" />
|
<SolidColorBrush x:Key="ClockDateForeground" Color="#26FFFFFF" />
|
||||||
<Color x:Key="ItemSelectedBackgroundColorBrush">#198F8F8F</Color>
|
<Color x:Key="ItemSelectedBackgroundColorBrush">#198F8F8F</Color>
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -58,6 +58,9 @@
|
||||||
<SolidColorBrush x:Key="BasicSeparatorColor" Color="#cecece" />
|
<SolidColorBrush x:Key="BasicSeparatorColor" Color="#cecece" />
|
||||||
<SolidColorBrush x:Key="BasicLineColor" Color="#cecece" />
|
<SolidColorBrush x:Key="BasicLineColor" Color="#cecece" />
|
||||||
|
|
||||||
|
<SolidColorBrush x:Key="NewHotkeyForeground" Color="#44FFFFFF" />
|
||||||
|
<SolidColorBrush x:Key="BasicHotkeyBGColor" Color="#13FFFFFF" />
|
||||||
|
|
||||||
<SolidColorBrush x:Key="ThemeHoverButton" Color="#3c3c3c" />
|
<SolidColorBrush x:Key="ThemeHoverButton" Color="#3c3c3c" />
|
||||||
<SolidColorBrush x:Key="PopuBGColor" Color="#202020" />
|
<SolidColorBrush x:Key="PopuBGColor" Color="#202020" />
|
||||||
<SolidColorBrush x:Key="PopupBGColor" Color="#202020" />
|
<SolidColorBrush x:Key="PopupBGColor" Color="#202020" />
|
||||||
|
|
@ -116,6 +119,8 @@
|
||||||
<SolidColorBrush x:Key="InfoBarWarningBG" Color="#433519" />
|
<SolidColorBrush x:Key="InfoBarWarningBG" Color="#433519" />
|
||||||
<SolidColorBrush x:Key="InfoBarBD" Color="#19000000" />
|
<SolidColorBrush x:Key="InfoBarBD" Color="#19000000" />
|
||||||
|
|
||||||
|
<SolidColorBrush x:Key="MouseOverWindowCloseButtonForegroundBrush" Color="#ffffff" />
|
||||||
|
|
||||||
<SolidColorBrush x:Key="ButtonOutBorder" Color="Transparent" />
|
<SolidColorBrush x:Key="ButtonOutBorder" Color="Transparent" />
|
||||||
<SolidColorBrush x:Key="ButtonInsideBorder" Color="#3f3f3f" />
|
<SolidColorBrush x:Key="ButtonInsideBorder" Color="#3f3f3f" />
|
||||||
<SolidColorBrush x:Key="ButtonBackgroundColor" Color="#373737" />
|
<SolidColorBrush x:Key="ButtonBackgroundColor" Color="#373737" />
|
||||||
|
|
|
||||||
|
|
@ -7,18 +7,18 @@
|
||||||
xmlns:sys="clr-namespace:System;assembly=mscorlib">
|
xmlns:sys="clr-namespace:System;assembly=mscorlib">
|
||||||
|
|
||||||
<SolidColorBrush x:Key="SystemThemeBorder" Color="#aaaaaa" />
|
<SolidColorBrush x:Key="SystemThemeBorder" Color="#aaaaaa" />
|
||||||
<SolidColorBrush x:Key="QuerySuggestionBoxForeground" Color="#d8d2d0" />
|
<SolidColorBrush x:Key="QuerySuggestionBoxForeground" Color="#51000000" />
|
||||||
<SolidColorBrush x:Key="QuerySelectionBrush" Color="#0a68d8" />
|
<SolidColorBrush x:Key="QuerySelectionBrush" Color="#0a68d8" />
|
||||||
<SolidColorBrush x:Key="SubTitleForeground" Color="#818181" />
|
<SolidColorBrush x:Key="SubTitleForeground" Color="#818181" />
|
||||||
<SolidColorBrush x:Key="SubTitleSelectedForeground" Color="#72767d" />
|
<SolidColorBrush x:Key="SubTitleSelectedForeground" Color="#72767d" />
|
||||||
<SolidColorBrush x:Key="SearchIconForeground" Color="#d3d3d3" />
|
<SolidColorBrush x:Key="SearchIconForeground" Color="#20000000" />
|
||||||
<SolidColorBrush x:Key="SeparatorForeground" Color="#14000000" />
|
<SolidColorBrush x:Key="SeparatorForeground" Color="#20000000" />
|
||||||
<SolidColorBrush x:Key="ThumbColor" Color="#868686" />
|
<SolidColorBrush x:Key="ThumbColor" Color="#868686" />
|
||||||
<SolidColorBrush x:Key="InlineHighlight" Color="#0078d7" />
|
<SolidColorBrush x:Key="InlineHighlight" Color="#0078d7" />
|
||||||
<SolidColorBrush x:Key="HotkeyForeground" Color="#acacac" />
|
<SolidColorBrush x:Key="HotkeyForeground" Color="#51000000" />
|
||||||
<SolidColorBrush x:Key="HotkeySelectedForeground" Color="#acacac" />
|
<SolidColorBrush x:Key="HotkeySelectedForeground" Color="#51000000" />
|
||||||
<SolidColorBrush x:Key="ClockDateForeground" Color="#d3d3d3" />
|
<SolidColorBrush x:Key="ClockDateForeground" Color="#44000000" />
|
||||||
<Color x:Key="ItemSelectedBackgroundColorBrush">#198F8F8F</Color>
|
<Color x:Key="ItemSelectedBackgroundColorBrush">#0C000000</Color>
|
||||||
|
|
||||||
<SolidColorBrush x:Key="Color00B" Color="#fbfbfb" />
|
<SolidColorBrush x:Key="Color00B" Color="#fbfbfb" />
|
||||||
<SolidColorBrush x:Key="Color01B" Color="#f3f3f3" />
|
<SolidColorBrush x:Key="Color01B" Color="#f3f3f3" />
|
||||||
|
|
@ -51,6 +51,9 @@
|
||||||
<SolidColorBrush x:Key="PluginInfoColor" Color="#8f8f8f" />
|
<SolidColorBrush x:Key="PluginInfoColor" Color="#8f8f8f" />
|
||||||
<SolidColorBrush x:Key="BasicLabelColor" Color="#1b1b1b" />
|
<SolidColorBrush x:Key="BasicLabelColor" Color="#1b1b1b" />
|
||||||
|
|
||||||
|
<SolidColorBrush x:Key="NewHotkeyForeground" Color="#5E000000" />
|
||||||
|
<SolidColorBrush x:Key="BasicHotkeyBGColor" Color="#0A000000" />
|
||||||
|
|
||||||
<SolidColorBrush x:Key="ThemeHoverButton" Color="#f6f6f6" />
|
<SolidColorBrush x:Key="ThemeHoverButton" Color="#f6f6f6" />
|
||||||
<!-- Typo -->
|
<!-- Typo -->
|
||||||
<SolidColorBrush x:Key="PopuBGColor" Color="#ffffff" />
|
<SolidColorBrush x:Key="PopuBGColor" Color="#ffffff" />
|
||||||
|
|
@ -107,6 +110,7 @@
|
||||||
<SolidColorBrush x:Key="InfoBarWarningBG" Color="#FFF4CE" />
|
<SolidColorBrush x:Key="InfoBarWarningBG" Color="#FFF4CE" />
|
||||||
<SolidColorBrush x:Key="InfoBarBD" Color="#0F000000" />
|
<SolidColorBrush x:Key="InfoBarBD" Color="#0F000000" />
|
||||||
|
|
||||||
|
<SolidColorBrush x:Key="MouseOverWindowCloseButtonForegroundBrush" Color="#ffffff" />
|
||||||
|
|
||||||
<SolidColorBrush x:Key="ButtonOutBorder" Color="#e5e5e5" />
|
<SolidColorBrush x:Key="ButtonOutBorder" Color="#e5e5e5" />
|
||||||
<SolidColorBrush x:Key="ButtonInsideBorder" Color="#d3d3d3" />
|
<SolidColorBrush x:Key="ButtonInsideBorder" Color="#d3d3d3" />
|
||||||
|
|
|
||||||
|
|
@ -110,8 +110,8 @@
|
||||||
<Border.Background>
|
<Border.Background>
|
||||||
<LinearGradientBrush StartPoint="0 0" EndPoint="1 1">
|
<LinearGradientBrush StartPoint="0 0" EndPoint="1 1">
|
||||||
<LinearGradientBrush.GradientStops>
|
<LinearGradientBrush.GradientStops>
|
||||||
<GradientStop Offset="0.0" Color="#1494df" />
|
<GradientStop Offset="0.0" Color="#2A4D8C" />
|
||||||
<GradientStop Offset="1.0" Color="#1073bd" />
|
<GradientStop Offset="1.0" Color="#1E3160" />
|
||||||
</LinearGradientBrush.GradientStops>
|
</LinearGradientBrush.GradientStops>
|
||||||
</LinearGradientBrush>
|
</LinearGradientBrush>
|
||||||
</Border.Background>
|
</Border.Background>
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Orientation="Horizontal">
|
Orientation="Horizontal">
|
||||||
<Border Width="450" Style="{DynamicResource WindowBorderStyle}">
|
<Border Width="450" Style="{DynamicResource PreviewWindowBorderStyle}">
|
||||||
<Border Style="{DynamicResource WindowRadius}">
|
<Border Style="{DynamicResource WindowRadius}">
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
|
|
|
||||||
|
|
@ -58,10 +58,10 @@
|
||||||
|
|
||||||
<Border Grid.Row="0" HorizontalAlignment="Stretch">
|
<Border Grid.Row="0" HorizontalAlignment="Stretch">
|
||||||
<Border.Background>
|
<Border.Background>
|
||||||
<LinearGradientBrush StartPoint="0 0" EndPoint="1 1">
|
<LinearGradientBrush StartPoint="0 1" EndPoint="0 0">
|
||||||
<LinearGradientBrush.GradientStops>
|
<LinearGradientBrush.GradientStops>
|
||||||
<GradientStop Offset="0.0" Color="#7b83eb" />
|
<GradientStop Offset="0.0" Color="#E5F3F7" />
|
||||||
<GradientStop Offset="1.0" Color="#555dc0" />
|
<GradientStop Offset="1.0" Color="#FAFAFD" />
|
||||||
</LinearGradientBrush.GradientStops>
|
</LinearGradientBrush.GradientStops>
|
||||||
</LinearGradientBrush>
|
</LinearGradientBrush>
|
||||||
</Border.Background>
|
</Border.Background>
|
||||||
|
|
|
||||||
|
|
@ -65,23 +65,24 @@
|
||||||
Margin="0 0 10 0"
|
Margin="0 0 10 0"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Visibility="{Binding ShowOpenResultHotkey}">
|
Visibility="{Binding ShowOpenResultHotkey}">
|
||||||
<TextBlock
|
<Border x:Name="HotkeyBG" Style="{DynamicResource ItemHotkeyBGStyle}">
|
||||||
x:Name="Hotkey"
|
<Border.Visibility>
|
||||||
Margin="12 0 12 0"
|
|
||||||
Padding="0 0 0 0"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Style="{DynamicResource ItemHotkeyStyle}">
|
|
||||||
<TextBlock.Visibility>
|
|
||||||
<Binding Converter="{StaticResource ResourceKey=OpenResultHotkeyVisibilityConverter}" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=ListBoxItem}" />
|
<Binding Converter="{StaticResource ResourceKey=OpenResultHotkeyVisibilityConverter}" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=ListBoxItem}" />
|
||||||
</TextBlock.Visibility>
|
</Border.Visibility>
|
||||||
<TextBlock.Text>
|
<TextBlock
|
||||||
<MultiBinding StringFormat="{}{0}+{1}">
|
x:Name="Hotkey"
|
||||||
<Binding Path="OpenResultModifiers" />
|
Padding="0 0 0 0"
|
||||||
<Binding Converter="{StaticResource ResourceKey=OrdinalConverter}" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=ListBoxItem}" />
|
HorizontalAlignment="Right"
|
||||||
</MultiBinding>
|
VerticalAlignment="Center"
|
||||||
</TextBlock.Text>
|
Style="{DynamicResource ItemHotkeyStyle}">
|
||||||
</TextBlock>
|
<TextBlock.Text>
|
||||||
|
<MultiBinding StringFormat="{}{0}+{1}">
|
||||||
|
<Binding Path="OpenResultModifiers" />
|
||||||
|
<Binding Converter="{StaticResource ResourceKey=OrdinalConverter}" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=ListBoxItem}" />
|
||||||
|
</MultiBinding>
|
||||||
|
</TextBlock.Text>
|
||||||
|
</TextBlock>
|
||||||
|
</Border>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<Grid Grid.Column="0">
|
<Grid Grid.Column="0">
|
||||||
|
|
@ -214,6 +215,7 @@
|
||||||
<Setter TargetName="Title" Property="Style" Value="{DynamicResource ItemTitleSelectedStyle}" />
|
<Setter TargetName="Title" Property="Style" Value="{DynamicResource ItemTitleSelectedStyle}" />
|
||||||
<Setter TargetName="SubTitle" Property="Style" Value="{DynamicResource ItemSubTitleSelectedStyle}" />
|
<Setter TargetName="SubTitle" Property="Style" Value="{DynamicResource ItemSubTitleSelectedStyle}" />
|
||||||
<Setter TargetName="Hotkey" Property="Style" Value="{DynamicResource ItemHotkeySelectedStyle}" />
|
<Setter TargetName="Hotkey" Property="Style" Value="{DynamicResource ItemHotkeySelectedStyle}" />
|
||||||
|
<Setter TargetName="HotkeyBG" Property="Style" Value="{DynamicResource ItemHotkeyBGSelectedStyle}" />
|
||||||
<Setter TargetName="GlyphIcon" Property="Style" Value="{DynamicResource ItemGlyphSelectedStyle}" />
|
<Setter TargetName="GlyphIcon" Property="Style" Value="{DynamicResource ItemGlyphSelectedStyle}" />
|
||||||
</DataTrigger>
|
</DataTrigger>
|
||||||
</DataTemplate.Triggers>
|
</DataTemplate.Triggers>
|
||||||
|
|
|
||||||
138
Flow.Launcher/SearchDelayTimeWindow.xaml
Normal file
138
Flow.Launcher/SearchDelayTimeWindow.xaml
Normal file
|
|
@ -0,0 +1,138 @@
|
||||||
|
<Window
|
||||||
|
x:Class="Flow.Launcher.SearchDelayTimeWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Title="{DynamicResource searchDelayTimeTitle}"
|
||||||
|
Width="450"
|
||||||
|
Background="{DynamicResource PopuBGColor}"
|
||||||
|
Foreground="{DynamicResource PopupTextColor}"
|
||||||
|
Icon="Images\app.png"
|
||||||
|
Loaded="SearchDelayTimeWindow_OnLoaded"
|
||||||
|
ResizeMode="NoResize"
|
||||||
|
SizeToContent="Height"
|
||||||
|
WindowStartupLocation="CenterScreen">
|
||||||
|
<WindowChrome.WindowChrome>
|
||||||
|
<WindowChrome CaptionHeight="32" ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
|
||||||
|
</WindowChrome.WindowChrome>
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition />
|
||||||
|
<RowDefinition Height="80" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid>
|
||||||
|
<StackPanel Grid.Row="0">
|
||||||
|
<StackPanel>
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Button
|
||||||
|
Grid.Column="4"
|
||||||
|
Click="BtnCancel_OnClick"
|
||||||
|
Style="{StaticResource TitleBarCloseButtonStyle}">
|
||||||
|
<Path
|
||||||
|
Width="46"
|
||||||
|
Height="32"
|
||||||
|
Data="M 18,11 27,20 M 18,20 27,11"
|
||||||
|
Stroke="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}"
|
||||||
|
StrokeThickness="1">
|
||||||
|
<Path.Style>
|
||||||
|
<Style TargetType="Path">
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding Path=IsActive, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" Value="False">
|
||||||
|
<Setter Property="Opacity" Value="0.5" />
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</Path.Style>
|
||||||
|
</Path>
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Margin="26 12 26 0">
|
||||||
|
<StackPanel Grid.Row="0" Margin="0 0 0 12">
|
||||||
|
<TextBlock
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="0 0 0 0"
|
||||||
|
FontSize="20"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Text="{DynamicResource searchDelayTimeTitle}"
|
||||||
|
TextAlignment="Left" />
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock
|
||||||
|
x:Name="tbSearchDelayTimeTips"
|
||||||
|
FontSize="14"
|
||||||
|
TextAlignment="Left"
|
||||||
|
TextWrapping="WrapWithOverflow" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel Margin="0 18 0 0" Orientation="Horizontal">
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="1"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
FontSize="14"
|
||||||
|
Text="{DynamicResource currentSearchDelayTime}" />
|
||||||
|
<TextBlock
|
||||||
|
x:Name="tbOldSearchDelayTime"
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="14 10 10 10"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
FontSize="14"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Foreground="{DynamicResource Color05B}" />
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Margin="0 0 0 10" Orientation="Horizontal">
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="1"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
FontSize="14"
|
||||||
|
Text="{DynamicResource newSearchDelayTime}" />
|
||||||
|
<ComboBox
|
||||||
|
x:Name="cbDelay"
|
||||||
|
MaxWidth="200"
|
||||||
|
Margin="10 10 15 10"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
DisplayMemberPath="Display"
|
||||||
|
SelectedValuePath="Value" />
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
<Border
|
||||||
|
Grid.Row="1"
|
||||||
|
Background="{DynamicResource PopupButtonAreaBGColor}"
|
||||||
|
BorderBrush="{DynamicResource PopupButtonAreaBorderColor}"
|
||||||
|
BorderThickness="0 1 0 0">
|
||||||
|
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
|
||||||
|
<Button
|
||||||
|
x:Name="btnCancel"
|
||||||
|
Width="145"
|
||||||
|
Height="30"
|
||||||
|
Margin="10 0 5 0"
|
||||||
|
Click="BtnCancel_OnClick"
|
||||||
|
Content="{DynamicResource cancel}" />
|
||||||
|
<Button
|
||||||
|
x:Name="btnDone"
|
||||||
|
Width="145"
|
||||||
|
Height="30"
|
||||||
|
Margin="5 0 10 0"
|
||||||
|
Click="btnDone_OnClick"
|
||||||
|
Style="{StaticResource AccentButtonStyle}">
|
||||||
|
<TextBlock x:Name="lblAdd" Text="{DynamicResource done}" />
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
57
Flow.Launcher/SearchDelayTimeWindow.xaml.cs
Normal file
57
Flow.Launcher/SearchDelayTimeWindow.xaml.cs
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows;
|
||||||
|
using Flow.Launcher.Plugin;
|
||||||
|
using Flow.Launcher.SettingPages.ViewModels;
|
||||||
|
using Flow.Launcher.ViewModel;
|
||||||
|
using static Flow.Launcher.SettingPages.ViewModels.SettingsPaneGeneralViewModel;
|
||||||
|
|
||||||
|
namespace Flow.Launcher;
|
||||||
|
|
||||||
|
public partial class SearchDelayTimeWindow : Window
|
||||||
|
{
|
||||||
|
private readonly PluginViewModel _pluginViewModel;
|
||||||
|
|
||||||
|
public SearchDelayTimeWindow(PluginViewModel pluginViewModel)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
_pluginViewModel = pluginViewModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SearchDelayTimeWindow_OnLoaded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
tbSearchDelayTimeTips.Text = string.Format(App.API.GetTranslation("searchDelayTime_tips"),
|
||||||
|
App.API.GetTranslation("default"));
|
||||||
|
tbOldSearchDelayTime.Text = _pluginViewModel.SearchDelayTimeText;
|
||||||
|
var searchDelayTimes = DropdownDataGeneric<SearchDelayTime>.GetValues<SearchDelayTimeData>("SearchDelayTime");
|
||||||
|
SearchDelayTimeData selected = null;
|
||||||
|
// Because default value is SearchDelayTime.VeryShort, we need to get selected value before adding default value
|
||||||
|
if (_pluginViewModel.PluginSearchDelayTime != null)
|
||||||
|
{
|
||||||
|
selected = searchDelayTimes.FirstOrDefault(x => x.Value == _pluginViewModel.PluginSearchDelayTime);
|
||||||
|
}
|
||||||
|
// Add default value to the beginning of the list
|
||||||
|
// When _pluginViewModel.PluginSearchDelayTime equals null, we will select this
|
||||||
|
searchDelayTimes.Insert(0, new SearchDelayTimeData { Display = App.API.GetTranslation("default"), LocalizationKey = "default" });
|
||||||
|
selected ??= searchDelayTimes.FirstOrDefault();
|
||||||
|
cbDelay.ItemsSource = searchDelayTimes;
|
||||||
|
cbDelay.SelectedItem = selected;
|
||||||
|
cbDelay.Focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BtnCancel_OnClick(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btnDone_OnClick(object sender, RoutedEventArgs _)
|
||||||
|
{
|
||||||
|
// Update search delay time
|
||||||
|
var selected = cbDelay.SelectedItem as SearchDelayTimeData;
|
||||||
|
SearchDelayTime? changedValue = selected?.LocalizationKey != "default" ? selected.Value : null;
|
||||||
|
_pluginViewModel.PluginSearchDelayTime = changedValue;
|
||||||
|
|
||||||
|
// Update search delay time text and close window
|
||||||
|
_pluginViewModel.OnSearchDelayTimeChanged();
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Flow.Launcher.Core.Resource;
|
|
||||||
using Flow.Launcher.Plugin;
|
using Flow.Launcher.Plugin;
|
||||||
|
|
||||||
namespace Flow.Launcher.SettingPages.ViewModels;
|
namespace Flow.Launcher.SettingPages.ViewModels;
|
||||||
|
|
@ -9,7 +8,7 @@ public class DropdownDataGeneric<TValue> : BaseModel where TValue : Enum
|
||||||
{
|
{
|
||||||
public string Display { get; set; }
|
public string Display { get; set; }
|
||||||
public TValue Value { get; private init; }
|
public TValue Value { get; private init; }
|
||||||
private string LocalizationKey { get; init; }
|
public string LocalizationKey { get; set; }
|
||||||
|
|
||||||
public static List<TR> GetValues<TR>(string keyPrefix) where TR : DropdownDataGeneric<TValue>, new()
|
public static List<TR> GetValues<TR>(string keyPrefix) where TR : DropdownDataGeneric<TValue>, new()
|
||||||
{
|
{
|
||||||
|
|
@ -19,7 +18,7 @@ public class DropdownDataGeneric<TValue> : BaseModel where TValue : Enum
|
||||||
foreach (var value in enumValues)
|
foreach (var value in enumValues)
|
||||||
{
|
{
|
||||||
var key = keyPrefix + value;
|
var key = keyPrefix + value;
|
||||||
var display = InternationalizationManager.Instance.GetTranslation(key);
|
var display = App.API.GetTranslation(key);
|
||||||
data.Add(new TR { Display = display, Value = value, LocalizationKey = key });
|
data.Add(new TR { Display = display, Value = value, LocalizationKey = key });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -30,7 +29,7 @@ public class DropdownDataGeneric<TValue> : BaseModel where TValue : Enum
|
||||||
{
|
{
|
||||||
foreach (var item in options)
|
foreach (var item in options)
|
||||||
{
|
{
|
||||||
item.Display = InternationalizationManager.Instance.GetTranslation(item.LocalizationKey);
|
item.Display = App.API.GetTranslation(item.LocalizationKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
using Flow.Launcher.Core;
|
using Flow.Launcher.Core;
|
||||||
using Flow.Launcher.Core.Resource;
|
|
||||||
using Flow.Launcher.Infrastructure;
|
using Flow.Launcher.Infrastructure;
|
||||||
using Flow.Launcher.Infrastructure.Logger;
|
using Flow.Launcher.Infrastructure.Logger;
|
||||||
using Flow.Launcher.Infrastructure.UserSettings;
|
using Flow.Launcher.Infrastructure.UserSettings;
|
||||||
|
|
@ -16,6 +15,8 @@ namespace Flow.Launcher.SettingPages.ViewModels;
|
||||||
|
|
||||||
public partial class SettingsPaneAboutViewModel : BaseModel
|
public partial class SettingsPaneAboutViewModel : BaseModel
|
||||||
{
|
{
|
||||||
|
private static readonly string ClassName = nameof(SettingsPaneAboutViewModel);
|
||||||
|
|
||||||
private readonly Settings _settings;
|
private readonly Settings _settings;
|
||||||
private readonly Updater _updater;
|
private readonly Updater _updater;
|
||||||
|
|
||||||
|
|
@ -24,7 +25,16 @@ public partial class SettingsPaneAboutViewModel : BaseModel
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var size = GetLogFiles().Sum(file => file.Length);
|
var size = GetLogFiles().Sum(file => file.Length);
|
||||||
return $"{InternationalizationManager.Instance.GetTranslation("clearlogfolder")} ({BytesToReadableString(size)})";
|
return $"{App.API.GetTranslation("clearlogfolder")} ({BytesToReadableString(size)})";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string CacheFolderSize
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var size = GetCacheFiles().Sum(file => file.Length);
|
||||||
|
return $"{App.API.GetTranslation("clearcachefolder")} ({BytesToReadableString(size)})";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -42,7 +52,7 @@ public partial class SettingsPaneAboutViewModel : BaseModel
|
||||||
};
|
};
|
||||||
|
|
||||||
public string ActivatedTimes => string.Format(
|
public string ActivatedTimes => string.Format(
|
||||||
InternationalizationManager.Instance.GetTranslation("about_activate_times"),
|
App.API.GetTranslation("about_activate_times"),
|
||||||
_settings.ActivateTimes
|
_settings.ActivateTimes
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -88,32 +98,52 @@ public partial class SettingsPaneAboutViewModel : BaseModel
|
||||||
private void AskClearLogFolderConfirmation()
|
private void AskClearLogFolderConfirmation()
|
||||||
{
|
{
|
||||||
var confirmResult = App.API.ShowMsgBox(
|
var confirmResult = App.API.ShowMsgBox(
|
||||||
InternationalizationManager.Instance.GetTranslation("clearlogfolderMessage"),
|
App.API.GetTranslation("clearlogfolderMessage"),
|
||||||
InternationalizationManager.Instance.GetTranslation("clearlogfolder"),
|
App.API.GetTranslation("clearlogfolder"),
|
||||||
MessageBoxButton.YesNo
|
MessageBoxButton.YesNo
|
||||||
);
|
);
|
||||||
|
|
||||||
if (confirmResult == MessageBoxResult.Yes)
|
if (confirmResult == MessageBoxResult.Yes)
|
||||||
{
|
{
|
||||||
ClearLogFolder();
|
if (!ClearLogFolder())
|
||||||
|
{
|
||||||
|
App.API.ShowMsgBox(App.API.GetTranslation("clearfolderfailMessage"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private void AskClearCacheFolderConfirmation()
|
||||||
|
{
|
||||||
|
var confirmResult = App.API.ShowMsgBox(
|
||||||
|
App.API.GetTranslation("clearcachefolderMessage"),
|
||||||
|
App.API.GetTranslation("clearcachefolder"),
|
||||||
|
MessageBoxButton.YesNo
|
||||||
|
);
|
||||||
|
|
||||||
|
if (confirmResult == MessageBoxResult.Yes)
|
||||||
|
{
|
||||||
|
if (!ClearCacheFolder())
|
||||||
|
{
|
||||||
|
App.API.ShowMsgBox(App.API.GetTranslation("clearfolderfailMessage"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private void OpenSettingsFolder()
|
private void OpenSettingsFolder()
|
||||||
{
|
{
|
||||||
App.API.OpenDirectory(Path.Combine(DataLocation.DataDirectory(), Constant.Settings));
|
App.API.OpenDirectory(DataLocation.SettingsDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private void OpenParentOfSettingsFolder(object parameter)
|
private void OpenParentOfSettingsFolder(object parameter)
|
||||||
{
|
{
|
||||||
string settingsFolderPath = Path.Combine(DataLocation.DataDirectory(), Constant.Settings);
|
string settingsFolderPath = Path.Combine(DataLocation.SettingsDirectory);
|
||||||
string parentFolderPath = Path.GetDirectoryName(settingsFolderPath);
|
string parentFolderPath = Path.GetDirectoryName(settingsFolderPath);
|
||||||
App.API.OpenDirectory(parentFolderPath);
|
App.API.OpenDirectory(parentFolderPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private void OpenLogsFolder()
|
private void OpenLogsFolder()
|
||||||
{
|
{
|
||||||
|
|
@ -121,26 +151,52 @@ public partial class SettingsPaneAboutViewModel : BaseModel
|
||||||
}
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private Task UpdateApp() => _updater.UpdateAppAsync(false);
|
private Task UpdateAppAsync() => _updater.UpdateAppAsync(false);
|
||||||
|
|
||||||
private void ClearLogFolder()
|
private bool ClearLogFolder()
|
||||||
{
|
{
|
||||||
|
var success = true;
|
||||||
var logDirectory = GetLogDir();
|
var logDirectory = GetLogDir();
|
||||||
var logFiles = GetLogFiles();
|
var logFiles = GetLogFiles();
|
||||||
|
|
||||||
logFiles.ForEach(f => f.Delete());
|
logFiles.ForEach(f =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
f.Delete();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
App.API.LogException(ClassName, $"Failed to delete log file: {f.Name}", e);
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
logDirectory.EnumerateDirectories("*", SearchOption.TopDirectoryOnly)
|
logDirectory.EnumerateDirectories("*", SearchOption.TopDirectoryOnly)
|
||||||
|
// Do not clean log files of current version
|
||||||
.Where(dir => !Constant.Version.Equals(dir.Name))
|
.Where(dir => !Constant.Version.Equals(dir.Name))
|
||||||
.ToList()
|
.ToList()
|
||||||
.ForEach(dir => dir.Delete());
|
.ForEach(dir =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dir.Delete(true);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
App.API.LogException(ClassName, $"Failed to delete log directory: {dir.Name}", e);
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
OnPropertyChanged(nameof(LogFolderSize));
|
OnPropertyChanged(nameof(LogFolderSize));
|
||||||
|
|
||||||
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DirectoryInfo GetLogDir(string version = "")
|
private static DirectoryInfo GetLogDir(string version = "")
|
||||||
{
|
{
|
||||||
return new DirectoryInfo(Path.Combine(DataLocation.DataDirectory(), Constant.Logs, version));
|
return new DirectoryInfo(Path.Combine(DataLocation.LogDirectory, version));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<FileInfo> GetLogFiles(string version = "")
|
private static List<FileInfo> GetLogFiles(string version = "")
|
||||||
|
|
@ -148,6 +204,55 @@ public partial class SettingsPaneAboutViewModel : BaseModel
|
||||||
return GetLogDir(version).EnumerateFiles("*", SearchOption.AllDirectories).ToList();
|
return GetLogDir(version).EnumerateFiles("*", SearchOption.AllDirectories).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool ClearCacheFolder()
|
||||||
|
{
|
||||||
|
var success = true;
|
||||||
|
var cacheDirectory = GetCacheDir();
|
||||||
|
var cacheFiles = GetCacheFiles();
|
||||||
|
|
||||||
|
cacheFiles.ForEach(f =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
f.Delete();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
App.API.LogException(ClassName, $"Failed to delete cache file: {f.Name}", e);
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cacheDirectory.EnumerateDirectories("*", SearchOption.TopDirectoryOnly)
|
||||||
|
.ToList()
|
||||||
|
.ForEach(dir =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dir.Delete(true);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
App.API.LogException(ClassName, $"Failed to delete cache directory: {dir.Name}", e);
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
OnPropertyChanged(nameof(CacheFolderSize));
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DirectoryInfo GetCacheDir()
|
||||||
|
{
|
||||||
|
return new DirectoryInfo(DataLocation.CacheDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<FileInfo> GetCacheFiles()
|
||||||
|
{
|
||||||
|
return GetCacheDir().EnumerateFiles("*", SearchOption.AllDirectories).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
private static string BytesToReadableString(long bytes)
|
private static string BytesToReadableString(long bytes)
|
||||||
{
|
{
|
||||||
const int scale = 1024;
|
const int scale = 1024;
|
||||||
|
|
@ -156,8 +261,7 @@ public partial class SettingsPaneAboutViewModel : BaseModel
|
||||||
|
|
||||||
foreach (string order in orders)
|
foreach (string order in orders)
|
||||||
{
|
{
|
||||||
if (bytes > max)
|
if (bytes > max) return $"{decimal.Divide(bytes, max):##.##} {order}";
|
||||||
return $"{decimal.Divide(bytes, max):##.##} {order}";
|
|
||||||
|
|
||||||
max /= scale;
|
max /= scale;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
using Flow.Launcher.Core;
|
using Flow.Launcher.Core;
|
||||||
|
|
@ -30,6 +31,7 @@ public partial class SettingsPaneGeneralViewModel : BaseModel
|
||||||
public class SearchWindowAlignData : DropdownDataGeneric<SearchWindowAligns> { }
|
public class SearchWindowAlignData : DropdownDataGeneric<SearchWindowAligns> { }
|
||||||
public class SearchPrecisionData : DropdownDataGeneric<SearchPrecisionScore> { }
|
public class SearchPrecisionData : DropdownDataGeneric<SearchPrecisionScore> { }
|
||||||
public class LastQueryModeData : DropdownDataGeneric<LastQueryMode> { }
|
public class LastQueryModeData : DropdownDataGeneric<LastQueryMode> { }
|
||||||
|
public class SearchDelayTimeData : DropdownDataGeneric<SearchDelayTime> { }
|
||||||
|
|
||||||
public bool StartFlowLauncherOnSystemStartup
|
public bool StartFlowLauncherOnSystemStartup
|
||||||
{
|
{
|
||||||
|
|
@ -142,12 +144,33 @@ public partial class SettingsPaneGeneralViewModel : BaseModel
|
||||||
public List<LastQueryModeData> LastQueryModes { get; } =
|
public List<LastQueryModeData> LastQueryModes { get; } =
|
||||||
DropdownDataGeneric<LastQueryMode>.GetValues<LastQueryModeData>("LastQuery");
|
DropdownDataGeneric<LastQueryMode>.GetValues<LastQueryModeData>("LastQuery");
|
||||||
|
|
||||||
|
public List<SearchDelayTimeData> SearchDelayTimes { get; } =
|
||||||
|
DropdownDataGeneric<SearchDelayTime>.GetValues<SearchDelayTimeData>("SearchDelayTime");
|
||||||
|
|
||||||
|
public SearchDelayTimeData SearchDelayTime
|
||||||
|
{
|
||||||
|
get => SearchDelayTimes.FirstOrDefault(x => x.Value == Settings.SearchDelayTime) ??
|
||||||
|
SearchDelayTimes.FirstOrDefault(x => x.Value == Plugin.SearchDelayTime.Normal) ??
|
||||||
|
SearchDelayTimes.FirstOrDefault();
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (Settings.SearchDelayTime != value.Value)
|
||||||
|
{
|
||||||
|
Settings.SearchDelayTime = value.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void UpdateEnumDropdownLocalizations()
|
private void UpdateEnumDropdownLocalizations()
|
||||||
{
|
{
|
||||||
DropdownDataGeneric<SearchWindowScreens>.UpdateLabels(SearchWindowScreens);
|
DropdownDataGeneric<SearchWindowScreens>.UpdateLabels(SearchWindowScreens);
|
||||||
DropdownDataGeneric<SearchWindowAligns>.UpdateLabels(SearchWindowAligns);
|
DropdownDataGeneric<SearchWindowAligns>.UpdateLabels(SearchWindowAligns);
|
||||||
DropdownDataGeneric<SearchPrecisionScore>.UpdateLabels(SearchPrecisionScores);
|
DropdownDataGeneric<SearchPrecisionScore>.UpdateLabels(SearchPrecisionScores);
|
||||||
DropdownDataGeneric<LastQueryMode>.UpdateLabels(LastQueryModes);
|
DropdownDataGeneric<LastQueryMode>.UpdateLabels(LastQueryModes);
|
||||||
|
DropdownDataGeneric<SearchDelayTime>.UpdateLabels(SearchDelayTimes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Language
|
public string Language
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,9 @@ public class SettingsPanePluginsViewModel : BaseModel
|
||||||
.Select(plugin => new PluginViewModel
|
.Select(plugin => new PluginViewModel
|
||||||
{
|
{
|
||||||
PluginPair = plugin,
|
PluginPair = plugin,
|
||||||
PluginSettingsObject = _settings.PluginSettings.Plugins[plugin.Metadata.ID]
|
PluginSettingsObject = _settings.PluginSettings.GetPluginSettings(plugin.Metadata.ID)
|
||||||
})
|
})
|
||||||
|
.Where(plugin => plugin.PluginSettingsObject != null)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
public List<PluginViewModel> FilteredPluginViewModels => PluginViewModels
|
public List<PluginViewModel> FilteredPluginViewModels => PluginViewModels
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
using Flow.Launcher.Core.Resource;
|
using Flow.Launcher.Core.Resource;
|
||||||
using Flow.Launcher.Helper;
|
using Flow.Launcher.Helper;
|
||||||
|
|
@ -13,7 +14,6 @@ using Flow.Launcher.Infrastructure.UserSettings;
|
||||||
using Flow.Launcher.Plugin;
|
using Flow.Launcher.Plugin;
|
||||||
using Flow.Launcher.ViewModel;
|
using Flow.Launcher.ViewModel;
|
||||||
using ModernWpf;
|
using ModernWpf;
|
||||||
using ThemeManager = Flow.Launcher.Core.Resource.ThemeManager;
|
|
||||||
using ThemeManagerForColorSchemeSwitch = ModernWpf.ThemeManager;
|
using ThemeManagerForColorSchemeSwitch = ModernWpf.ThemeManager;
|
||||||
|
|
||||||
namespace Flow.Launcher.SettingPages.ViewModels;
|
namespace Flow.Launcher.SettingPages.ViewModels;
|
||||||
|
|
@ -21,46 +21,70 @@ namespace Flow.Launcher.SettingPages.ViewModels;
|
||||||
public partial class SettingsPaneThemeViewModel : BaseModel
|
public partial class SettingsPaneThemeViewModel : BaseModel
|
||||||
{
|
{
|
||||||
private const string DefaultFont = "Segoe UI";
|
private const string DefaultFont = "Segoe UI";
|
||||||
|
public string BackdropSubText => !Win32Helper.IsBackdropSupported() ? App.API.GetTranslation("BackdropTypeDisabledToolTip") : "";
|
||||||
public Settings Settings { get; }
|
public Settings Settings { get; }
|
||||||
|
private readonly Theme _theme = Ioc.Default.GetRequiredService<Theme>();
|
||||||
|
|
||||||
public static string LinkHowToCreateTheme => @"https://flowlauncher.com/docs/#/how-to-create-a-theme";
|
public static string LinkHowToCreateTheme => @"https://www.flowlauncher.com/theme-builder/";
|
||||||
public static string LinkThemeGallery => "https://github.com/Flow-Launcher/Flow.Launcher/discussions/1438";
|
public static string LinkThemeGallery => "https://github.com/Flow-Launcher/Flow.Launcher/discussions/1438";
|
||||||
|
|
||||||
|
private List<Theme.ThemeData> _themes;
|
||||||
|
public List<Theme.ThemeData> Themes => _themes ??= _theme.LoadAvailableThemes();
|
||||||
|
|
||||||
private Theme.ThemeData _selectedTheme;
|
private Theme.ThemeData _selectedTheme;
|
||||||
public Theme.ThemeData SelectedTheme
|
public Theme.ThemeData SelectedTheme
|
||||||
{
|
{
|
||||||
get => _selectedTheme ??= Themes.Find(v => v.FileNameWithoutExtension == Settings.Theme);
|
get => _selectedTheme ??= Themes.Find(v => v.FileNameWithoutExtension == _theme.GetCurrentTheme());
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_selectedTheme = value;
|
_selectedTheme = value;
|
||||||
ThemeManager.Instance.ChangeTheme(value.FileNameWithoutExtension);
|
_theme.ChangeTheme(value.FileNameWithoutExtension);
|
||||||
|
|
||||||
if (ThemeManager.Instance.BlurEnabled && Settings.UseDropShadowEffect)
|
// Update UI state
|
||||||
DropShadowEffect = false;
|
OnPropertyChanged(nameof(BackdropType));
|
||||||
|
OnPropertyChanged(nameof(IsBackdropEnabled));
|
||||||
|
OnPropertyChanged(nameof(IsDropShadowEnabled));
|
||||||
|
OnPropertyChanged(nameof(DropShadowEffect));
|
||||||
|
|
||||||
|
_ = _theme.RefreshFrameAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsBackdropEnabled
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!Win32Helper.IsBackdropSupported()) return false;
|
||||||
|
return SelectedTheme?.HasBlur ?? false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsDropShadowEnabled => !_theme.BlurEnabled;
|
||||||
|
|
||||||
public bool DropShadowEffect
|
public bool DropShadowEffect
|
||||||
{
|
{
|
||||||
get => Settings.UseDropShadowEffect;
|
get => Settings.UseDropShadowEffect;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (ThemeManager.Instance.BlurEnabled && value)
|
if (_theme.BlurEnabled)
|
||||||
{
|
{
|
||||||
App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("shadowEffectNotAllowed"));
|
// Always DropShadowEffect = true with blur theme
|
||||||
|
Settings.UseDropShadowEffect = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// User can change shadow with non-blur theme.
|
||||||
if (value)
|
if (value)
|
||||||
{
|
{
|
||||||
ThemeManager.Instance.AddDropShadowEffectToCurrentTheme();
|
_theme.AddDropShadowEffectToCurrentTheme();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ThemeManager.Instance.RemoveDropShadowEffectFromCurrentTheme();
|
_theme.RemoveDropShadowEffectFromCurrentTheme();
|
||||||
}
|
}
|
||||||
|
|
||||||
Settings.UseDropShadowEffect = value;
|
Settings.UseDropShadowEffect = value;
|
||||||
|
OnPropertyChanged(nameof(DropShadowEffect));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -94,12 +118,25 @@ public partial class SettingsPaneThemeViewModel : BaseModel
|
||||||
set => Settings.ResultSubItemFontSize = value;
|
set => Settings.ResultSubItemFontSize = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Theme.ThemeData> _themes;
|
|
||||||
public List<Theme.ThemeData> Themes => _themes ??= ThemeManager.Instance.LoadAvailableThemes();
|
|
||||||
|
|
||||||
public class ColorSchemeData : DropdownDataGeneric<ColorSchemes> { }
|
public class ColorSchemeData : DropdownDataGeneric<ColorSchemes> { }
|
||||||
|
|
||||||
public List<ColorSchemeData> ColorSchemes { get; } = DropdownDataGeneric<ColorSchemes>.GetValues<ColorSchemeData>("ColorScheme");
|
public List<ColorSchemeData> ColorSchemes { get; } = DropdownDataGeneric<ColorSchemes>.GetValues<ColorSchemeData>("ColorScheme");
|
||||||
|
public string ColorScheme
|
||||||
|
{
|
||||||
|
get => Settings.ColorScheme;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
ThemeManagerForColorSchemeSwitch.Current.ApplicationTheme = value switch
|
||||||
|
{
|
||||||
|
Constant.Light => ApplicationTheme.Light,
|
||||||
|
Constant.Dark => ApplicationTheme.Dark,
|
||||||
|
Constant.System => null,
|
||||||
|
_ => ThemeManagerForColorSchemeSwitch.Current.ApplicationTheme
|
||||||
|
};
|
||||||
|
Settings.ColorScheme = value;
|
||||||
|
_ = _theme.RefreshFrameAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public List<string> TimeFormatList { get; } = new()
|
public List<string> TimeFormatList { get; } = new()
|
||||||
{
|
{
|
||||||
|
|
@ -174,6 +211,33 @@ public partial class SettingsPaneThemeViewModel : BaseModel
|
||||||
public class AnimationSpeedData : DropdownDataGeneric<AnimationSpeeds> { }
|
public class AnimationSpeedData : DropdownDataGeneric<AnimationSpeeds> { }
|
||||||
public List<AnimationSpeedData> AnimationSpeeds { get; } = DropdownDataGeneric<AnimationSpeeds>.GetValues<AnimationSpeedData>("AnimationSpeed");
|
public List<AnimationSpeedData> AnimationSpeeds { get; } = DropdownDataGeneric<AnimationSpeeds>.GetValues<AnimationSpeedData>("AnimationSpeed");
|
||||||
|
|
||||||
|
public class BackdropTypeData : DropdownDataGeneric<BackdropTypes> { }
|
||||||
|
|
||||||
|
public List<BackdropTypeData> BackdropTypesList { get; } =
|
||||||
|
DropdownDataGeneric<BackdropTypes>.GetValues<BackdropTypeData>("BackdropTypes");
|
||||||
|
|
||||||
|
public BackdropTypes BackdropType
|
||||||
|
{
|
||||||
|
get => Enum.IsDefined(typeof(BackdropTypes), Settings.BackdropType)
|
||||||
|
? Settings.BackdropType
|
||||||
|
: BackdropTypes.None;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (!Enum.IsDefined(typeof(BackdropTypes), value))
|
||||||
|
{
|
||||||
|
value = BackdropTypes.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Settings.BackdropType = value;
|
||||||
|
|
||||||
|
// Can only apply blur because drop shadow effect is not supported with backdrop
|
||||||
|
// So drop shadow effect has been disabled
|
||||||
|
_ = _theme.SetBlurForWindowAsync();
|
||||||
|
|
||||||
|
OnPropertyChanged(nameof(IsDropShadowEnabled));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool UseSound
|
public bool UseSound
|
||||||
{
|
{
|
||||||
get => Settings.UseSound;
|
get => Settings.UseSound;
|
||||||
|
|
@ -196,6 +260,23 @@ public partial class SettingsPaneThemeViewModel : BaseModel
|
||||||
set => Settings.SoundVolume = value;
|
set => Settings.SoundVolume = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool ShowPlaceholder
|
||||||
|
{
|
||||||
|
get => Settings.ShowPlaceholder;
|
||||||
|
set => Settings.ShowPlaceholder = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string PlaceholderTextTip
|
||||||
|
{
|
||||||
|
get => string.Format(App.API.GetTranslation("PlaceholderTextTip"), App.API.GetTranslation("queryTextBoxPlaceholder"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public string PlaceholderText
|
||||||
|
{
|
||||||
|
get => Settings.PlaceholderText;
|
||||||
|
set => Settings.PlaceholderText = value;
|
||||||
|
}
|
||||||
|
|
||||||
public bool UseClock
|
public bool UseClock
|
||||||
{
|
{
|
||||||
get => Settings.UseClock;
|
get => Settings.UseClock;
|
||||||
|
|
@ -219,37 +300,37 @@ public partial class SettingsPaneThemeViewModel : BaseModel
|
||||||
{
|
{
|
||||||
var results = new List<Result>
|
var results = new List<Result>
|
||||||
{
|
{
|
||||||
new Result
|
new()
|
||||||
{
|
{
|
||||||
Title = InternationalizationManager.Instance.GetTranslation("SampleTitleExplorer"),
|
Title = App.API.GetTranslation("SampleTitleExplorer"),
|
||||||
SubTitle = InternationalizationManager.Instance.GetTranslation("SampleSubTitleExplorer"),
|
SubTitle = App.API.GetTranslation("SampleSubTitleExplorer"),
|
||||||
IcoPath = Path.Combine(
|
IcoPath = Path.Combine(
|
||||||
Constant.ProgramDirectory,
|
Constant.ProgramDirectory,
|
||||||
@"Plugins\Flow.Launcher.Plugin.Explorer\Images\explorer.png"
|
@"Plugins\Flow.Launcher.Plugin.Explorer\Images\explorer.png"
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
new Result
|
new()
|
||||||
{
|
{
|
||||||
Title = InternationalizationManager.Instance.GetTranslation("SampleTitleWebSearch"),
|
Title = App.API.GetTranslation("SampleTitleWebSearch"),
|
||||||
SubTitle = InternationalizationManager.Instance.GetTranslation("SampleSubTitleWebSearch"),
|
SubTitle = App.API.GetTranslation("SampleSubTitleWebSearch"),
|
||||||
IcoPath = Path.Combine(
|
IcoPath = Path.Combine(
|
||||||
Constant.ProgramDirectory,
|
Constant.ProgramDirectory,
|
||||||
@"Plugins\Flow.Launcher.Plugin.WebSearch\Images\web_search.png"
|
@"Plugins\Flow.Launcher.Plugin.WebSearch\Images\web_search.png"
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
new Result
|
new()
|
||||||
{
|
{
|
||||||
Title = InternationalizationManager.Instance.GetTranslation("SampleTitleProgram"),
|
Title = App.API.GetTranslation("SampleTitleProgram"),
|
||||||
SubTitle = InternationalizationManager.Instance.GetTranslation("SampleSubTitleProgram"),
|
SubTitle = App.API.GetTranslation("SampleSubTitleProgram"),
|
||||||
IcoPath = Path.Combine(
|
IcoPath = Path.Combine(
|
||||||
Constant.ProgramDirectory,
|
Constant.ProgramDirectory,
|
||||||
@"Plugins\Flow.Launcher.Plugin.Program\Images\program.png"
|
@"Plugins\Flow.Launcher.Plugin.Program\Images\program.png"
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
new Result
|
new()
|
||||||
{
|
{
|
||||||
Title = InternationalizationManager.Instance.GetTranslation("SampleTitleProcessKiller"),
|
Title = App.API.GetTranslation("SampleTitleProcessKiller"),
|
||||||
SubTitle = InternationalizationManager.Instance.GetTranslation("SampleSubTitleProcessKiller"),
|
SubTitle = App.API.GetTranslation("SampleSubTitleProcessKiller"),
|
||||||
IcoPath = Path.Combine(
|
IcoPath = Path.Combine(
|
||||||
Constant.ProgramDirectory,
|
Constant.ProgramDirectory,
|
||||||
@"Plugins\Flow.Launcher.Plugin.ProcessKiller\Images\app.png"
|
@"Plugins\Flow.Launcher.Plugin.ProcessKiller\Images\app.png"
|
||||||
|
|
@ -281,7 +362,7 @@ public partial class SettingsPaneThemeViewModel : BaseModel
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
Settings.QueryBoxFont = value.ToString();
|
Settings.QueryBoxFont = value.ToString();
|
||||||
ThemeManager.Instance.ChangeTheme(Settings.Theme);
|
_theme.UpdateFonts();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -303,7 +384,7 @@ public partial class SettingsPaneThemeViewModel : BaseModel
|
||||||
Settings.QueryBoxFontStretch = value.Stretch.ToString();
|
Settings.QueryBoxFontStretch = value.Stretch.ToString();
|
||||||
Settings.QueryBoxFontWeight = value.Weight.ToString();
|
Settings.QueryBoxFontWeight = value.Weight.ToString();
|
||||||
Settings.QueryBoxFontStyle = value.Style.ToString();
|
Settings.QueryBoxFontStyle = value.Style.ToString();
|
||||||
ThemeManager.Instance.ChangeTheme(Settings.Theme);
|
_theme.UpdateFonts();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -325,7 +406,7 @@ public partial class SettingsPaneThemeViewModel : BaseModel
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
Settings.ResultFont = value.ToString();
|
Settings.ResultFont = value.ToString();
|
||||||
ThemeManager.Instance.ChangeTheme(Settings.Theme);
|
_theme.UpdateFonts();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -347,7 +428,7 @@ public partial class SettingsPaneThemeViewModel : BaseModel
|
||||||
Settings.ResultFontStretch = value.Stretch.ToString();
|
Settings.ResultFontStretch = value.Stretch.ToString();
|
||||||
Settings.ResultFontWeight = value.Weight.ToString();
|
Settings.ResultFontWeight = value.Weight.ToString();
|
||||||
Settings.ResultFontStyle = value.Style.ToString();
|
Settings.ResultFontStyle = value.Style.ToString();
|
||||||
ThemeManager.Instance.ChangeTheme(Settings.Theme);
|
_theme.UpdateFonts();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -355,9 +436,9 @@ public partial class SettingsPaneThemeViewModel : BaseModel
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (Fonts.SystemFontFamilies.Count(o =>
|
if (Fonts.SystemFontFamilies.Any(o =>
|
||||||
o.FamilyNames.Values != null &&
|
o.FamilyNames.Values != null &&
|
||||||
o.FamilyNames.Values.Contains(Settings.ResultSubFont)) > 0)
|
o.FamilyNames.Values.Contains(Settings.ResultSubFont)))
|
||||||
{
|
{
|
||||||
var font = new FontFamily(Settings.ResultSubFont);
|
var font = new FontFamily(Settings.ResultSubFont);
|
||||||
return font;
|
return font;
|
||||||
|
|
@ -371,7 +452,7 @@ public partial class SettingsPaneThemeViewModel : BaseModel
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
Settings.ResultSubFont = value.ToString();
|
Settings.ResultSubFont = value.ToString();
|
||||||
ThemeManager.Instance.ChangeTheme(Settings.Theme);
|
_theme.UpdateFonts();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -392,34 +473,23 @@ public partial class SettingsPaneThemeViewModel : BaseModel
|
||||||
Settings.ResultSubFontStretch = value.Stretch.ToString();
|
Settings.ResultSubFontStretch = value.Stretch.ToString();
|
||||||
Settings.ResultSubFontWeight = value.Weight.ToString();
|
Settings.ResultSubFontWeight = value.Weight.ToString();
|
||||||
Settings.ResultSubFontStyle = value.Style.ToString();
|
Settings.ResultSubFontStyle = value.Style.ToString();
|
||||||
ThemeManager.Instance.ChangeTheme(Settings.Theme);
|
_theme.UpdateFonts();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ThemeImage => Constant.QueryTextBoxIconImagePath;
|
public string ThemeImage => Constant.QueryTextBoxIconImagePath;
|
||||||
|
|
||||||
[RelayCommand]
|
|
||||||
private void OpenThemesFolder()
|
|
||||||
{
|
|
||||||
App.API.OpenDirectory(Path.Combine(DataLocation.DataDirectory(), Constant.Themes));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateColorScheme()
|
|
||||||
{
|
|
||||||
ThemeManagerForColorSchemeSwitch.Current.ApplicationTheme = Settings.ColorScheme switch
|
|
||||||
{
|
|
||||||
Constant.Light => ApplicationTheme.Light,
|
|
||||||
Constant.Dark => ApplicationTheme.Dark,
|
|
||||||
Constant.System => null,
|
|
||||||
_ => ThemeManagerForColorSchemeSwitch.Current.ApplicationTheme
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public SettingsPaneThemeViewModel(Settings settings)
|
public SettingsPaneThemeViewModel(Settings settings)
|
||||||
{
|
{
|
||||||
Settings = settings;
|
Settings = settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private void OpenThemesFolder()
|
||||||
|
{
|
||||||
|
App.API.OpenDirectory(DataLocation.ThemesDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
public void Reset()
|
public void Reset()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,10 @@
|
||||||
Margin="0 12 0 0"
|
Margin="0 12 0 0"
|
||||||
Icon="">
|
Icon="">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Button
|
||||||
|
Margin="0 0 12 0"
|
||||||
|
Command="{Binding AskClearCacheFolderConfirmationCommand}"
|
||||||
|
Content="{Binding CacheFolderSize, Mode=OneWay}" />
|
||||||
<Button
|
<Button
|
||||||
Margin="0 0 12 0"
|
Margin="0 0 12 0"
|
||||||
Command="{Binding AskClearLogFolderConfirmationCommand}"
|
Command="{Binding AskClearLogFolderConfirmationCommand}"
|
||||||
|
|
|
||||||
|
|
@ -28,21 +28,26 @@
|
||||||
Style="{StaticResource PageTitle}"
|
Style="{StaticResource PageTitle}"
|
||||||
Text="{DynamicResource general}"
|
Text="{DynamicResource general}"
|
||||||
TextAlignment="left" />
|
TextAlignment="left" />
|
||||||
|
<cc:ExCard
|
||||||
<cc:Card Title="{DynamicResource startFlowLauncherOnSystemStartup}" Icon="">
|
Title="{DynamicResource startFlowLauncherOnSystemStartup}"
|
||||||
<ui:ToggleSwitch
|
Margin="0 8 0 0"
|
||||||
IsOn="{Binding StartFlowLauncherOnSystemStartup}"
|
Icon="">
|
||||||
OffContent="{DynamicResource disable}"
|
<cc:ExCard.SideContent>
|
||||||
OnContent="{DynamicResource enable}" />
|
<ui:ToggleSwitch
|
||||||
</cc:Card>
|
IsOn="{Binding StartFlowLauncherOnSystemStartup}"
|
||||||
|
OffContent="{DynamicResource disable}"
|
||||||
<cc:Card Title="{DynamicResource useLogonTaskForStartup}" Sub="{DynamicResource useLogonTaskForStartupTooltip}">
|
OnContent="{DynamicResource enable}" />
|
||||||
<ui:ToggleSwitch
|
</cc:ExCard.SideContent>
|
||||||
IsOn="{Binding UseLogonTaskForStartup}"
|
<cc:Card
|
||||||
OffContent="{DynamicResource disable}"
|
Title="{DynamicResource useLogonTaskForStartup}"
|
||||||
OnContent="{DynamicResource enable}" />
|
Sub="{DynamicResource useLogonTaskForStartupTooltip}"
|
||||||
</cc:Card>
|
Type="InsideFit">
|
||||||
|
<ui:ToggleSwitch
|
||||||
|
IsOn="{Binding UseLogonTaskForStartup}"
|
||||||
|
OffContent="{DynamicResource disable}"
|
||||||
|
OnContent="{DynamicResource enable}" />
|
||||||
|
</cc:Card>
|
||||||
|
</cc:ExCard>
|
||||||
<cc:Card
|
<cc:Card
|
||||||
Title="{DynamicResource hideOnStartup}"
|
Title="{DynamicResource hideOnStartup}"
|
||||||
Icon=""
|
Icon=""
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@
|
||||||
VirtualizingStackPanel.IsVirtualizing="True"
|
VirtualizingStackPanel.IsVirtualizing="True"
|
||||||
VirtualizingStackPanel.ScrollUnit="Pixel">
|
VirtualizingStackPanel.ScrollUnit="Pixel">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
|
|
||||||
<!-- Page title -->
|
<!-- Page title -->
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="5 23 0 5"
|
Margin="5 23 0 5"
|
||||||
|
|
@ -38,6 +39,7 @@
|
||||||
Text="{DynamicResource appearance}"
|
Text="{DynamicResource appearance}"
|
||||||
TextAlignment="left"
|
TextAlignment="left"
|
||||||
Visibility="Collapsed" />
|
Visibility="Collapsed" />
|
||||||
|
|
||||||
<!-- Theme Preview and Editor -->
|
<!-- Theme Preview and Editor -->
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.Style>
|
<Grid.Style>
|
||||||
|
|
@ -68,6 +70,7 @@
|
||||||
<ColumnDefinition Width="8*" />
|
<ColumnDefinition Width="8*" />
|
||||||
<ColumnDefinition MaxWidth="250" Style="{StaticResource SecondColumnStyle}" />
|
<ColumnDefinition MaxWidth="250" Style="{StaticResource SecondColumnStyle}" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
<!-- Theme Size Editor -->
|
<!-- Theme Size Editor -->
|
||||||
<Border
|
<Border
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
|
|
@ -260,7 +263,8 @@
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</Border>
|
</Border>
|
||||||
<!-- Theme Preview -->
|
|
||||||
|
<!-- Theme preview -->
|
||||||
<Border
|
<Border
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Background="{Binding PreviewBackground}"
|
Background="{Binding PreviewBackground}"
|
||||||
|
|
@ -303,7 +307,7 @@
|
||||||
Width="400"
|
Width="400"
|
||||||
Margin="40 30 0 30"
|
Margin="40 30 0 30"
|
||||||
SnapsToDevicePixels="True"
|
SnapsToDevicePixels="True"
|
||||||
Style="{DynamicResource WindowBorderStyle}">
|
Style="{DynamicResource PreviewWindowBorderStyle}">
|
||||||
<Border BorderThickness="0" Style="{DynamicResource WindowRadius}">
|
<Border BorderThickness="0" Style="{DynamicResource WindowRadius}">
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
|
|
@ -370,17 +374,6 @@
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
<!-- Drop shadow effect -->
|
|
||||||
<cc:Card
|
|
||||||
Title="{DynamicResource queryWindowShadowEffect}"
|
|
||||||
Margin="0 8 0 0"
|
|
||||||
Icon=""
|
|
||||||
Sub="{DynamicResource shadowEffectCPUUsage}">
|
|
||||||
<ui:ToggleSwitch
|
|
||||||
IsOn="{Binding DropShadowEffect}"
|
|
||||||
OffContent="{DynamicResource disable}"
|
|
||||||
OnContent="{DynamicResource enable}" />
|
|
||||||
</cc:Card>
|
|
||||||
|
|
||||||
<!-- Theme -->
|
<!-- Theme -->
|
||||||
<cc:ExCard
|
<cc:ExCard
|
||||||
|
|
@ -469,42 +462,67 @@
|
||||||
</ListBox.Template>
|
</ListBox.Template>
|
||||||
</ListBox>
|
</ListBox>
|
||||||
</cc:ExCard>
|
</cc:ExCard>
|
||||||
|
|
||||||
|
<cc:CardGroup Margin="0 10 0 0">
|
||||||
|
<!-- Backdrop effect -->
|
||||||
|
<cc:Card
|
||||||
|
Title="{DynamicResource BackdropType}"
|
||||||
|
Margin="0 0 0 0"
|
||||||
|
Icon=""
|
||||||
|
Sub="{Binding BackdropSubText}">
|
||||||
|
<ComboBox
|
||||||
|
MinWidth="160"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
DisplayMemberPath="Display"
|
||||||
|
FontSize="14"
|
||||||
|
IsEnabled="{Binding IsBackdropEnabled}"
|
||||||
|
ItemsSource="{Binding BackdropTypesList}"
|
||||||
|
SelectedValue="{Binding BackdropType, Mode=TwoWay}"
|
||||||
|
SelectedValuePath="Value" />
|
||||||
|
</cc:Card>
|
||||||
|
|
||||||
|
<!-- Drop shadow effect -->
|
||||||
|
<cc:Card
|
||||||
|
Title="{DynamicResource queryWindowShadowEffect}"
|
||||||
|
Margin="0 0 0 0"
|
||||||
|
Icon="">
|
||||||
|
<ui:ToggleSwitch
|
||||||
|
IsEnabled="{Binding IsDropShadowEnabled}"
|
||||||
|
IsOn="{Binding DropShadowEffect}"
|
||||||
|
OffContent="{DynamicResource disable}"
|
||||||
|
OnContent="{DynamicResource enable}" />
|
||||||
|
</cc:Card>
|
||||||
|
</cc:CardGroup>
|
||||||
<cc:HyperLink
|
<cc:HyperLink
|
||||||
Margin="10"
|
Margin="10"
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
Text="{DynamicResource browserMoreThemes}"
|
Text="{DynamicResource browserMoreThemes}"
|
||||||
Uri="{Binding LinkThemeGallery}" />
|
Uri="{Binding LinkThemeGallery}" />
|
||||||
|
|
||||||
<!-- Fixed Height -->
|
<!-- Fixed size -->
|
||||||
<cc:CardGroup Margin="0 20 0 0">
|
<cc:ExCard
|
||||||
<cc:Card
|
Title="{DynamicResource KeepMaxResults}"
|
||||||
Title="{DynamicResource KeepMaxResults}"
|
Margin="0 20 0 0"
|
||||||
Icon=""
|
Icon=""
|
||||||
Sub="{DynamicResource KeepMaxResultsToolTip}">
|
Sub="{DynamicResource KeepMaxResultsToolTip}">
|
||||||
<ui:ToggleSwitch IsOn="{Binding KeepMaxResults}" />
|
<cc:ExCard.SideContent>
|
||||||
</cc:Card>
|
<StackPanel VerticalAlignment="Center" Orientation="Horizontal">
|
||||||
|
<ui:ToggleSwitch
|
||||||
|
IsOn="{Binding KeepMaxResults}"
|
||||||
|
OffContent="{DynamicResource disable}"
|
||||||
|
OnContent="{DynamicResource enable}" />
|
||||||
|
</StackPanel>
|
||||||
|
</cc:ExCard.SideContent>
|
||||||
<cc:Card
|
<cc:Card
|
||||||
Title="{DynamicResource maxShowResults}"
|
Title="{DynamicResource maxShowResults}"
|
||||||
Sub="{DynamicResource maxShowResultsToolTip}"
|
Sub="{DynamicResource maxShowResultsToolTip}"
|
||||||
Visibility="{Binding KeepMaxResults, Converter={StaticResource BoolToVisibilityConverter}}">
|
Type="InsideFit">
|
||||||
<ComboBox
|
<ComboBox
|
||||||
Width="100"
|
Width="100"
|
||||||
ItemsSource="{Binding MaxResultsRange}"
|
ItemsSource="{Binding MaxResultsRange}"
|
||||||
SelectedItem="{Binding Settings.MaxResultsToShow}" />
|
SelectedItem="{Binding Settings.MaxResultsToShow}" />
|
||||||
</cc:Card>
|
</cc:Card>
|
||||||
</cc:CardGroup>
|
</cc:ExCard>
|
||||||
|
|
||||||
<!-- Fonts and icons -->
|
|
||||||
<cc:Card
|
|
||||||
Title="{DynamicResource useGlyphUI}"
|
|
||||||
Margin="0 14 0 0"
|
|
||||||
Icon=""
|
|
||||||
Sub="{DynamicResource useGlyphUIEffect}">
|
|
||||||
<ui:ToggleSwitch
|
|
||||||
IsOn="{Binding UseGlyphIcons}"
|
|
||||||
OffContent="{DynamicResource disable}"
|
|
||||||
OnContent="{DynamicResource enable}" />
|
|
||||||
</cc:Card>
|
|
||||||
|
|
||||||
<!-- Time and date -->
|
<!-- Time and date -->
|
||||||
<cc:CardGroup Margin="0 14 0 0">
|
<cc:CardGroup Margin="0 14 0 0">
|
||||||
|
|
@ -551,22 +569,45 @@
|
||||||
</cc:Card>
|
</cc:Card>
|
||||||
</cc:CardGroup>
|
</cc:CardGroup>
|
||||||
|
|
||||||
<!-- Animation -->
|
<!-- Placeholder text -->
|
||||||
<cc:CardGroup Margin="0 14 0 0">
|
<cc:ExCard
|
||||||
|
Title="{DynamicResource ShowPlaceholder}"
|
||||||
|
Margin="0 4 0 0"
|
||||||
|
Icon=""
|
||||||
|
Sub="{DynamicResource ShowPlaceholderTip}">
|
||||||
|
<cc:ExCard.SideContent>
|
||||||
|
<ui:ToggleSwitch
|
||||||
|
IsOn="{Binding ShowPlaceholder}"
|
||||||
|
OffContent="{DynamicResource disable}"
|
||||||
|
OnContent="{DynamicResource enable}" />
|
||||||
|
</cc:ExCard.SideContent>
|
||||||
<cc:Card
|
<cc:Card
|
||||||
Title="{DynamicResource Animation}"
|
Title="{DynamicResource PlaceholderText}"
|
||||||
Icon=""
|
Sub="{Binding PlaceholderTextTip}"
|
||||||
Sub="{DynamicResource AnimationTip}">
|
Type="InsideFit">
|
||||||
|
<TextBox
|
||||||
|
MinWidth="150"
|
||||||
|
Text="{Binding PlaceholderText}"
|
||||||
|
TextWrapping="NoWrap" />
|
||||||
|
</cc:Card>
|
||||||
|
</cc:ExCard>
|
||||||
|
|
||||||
|
<!-- Animation -->
|
||||||
|
<cc:ExCard
|
||||||
|
Title="{DynamicResource Animation}"
|
||||||
|
Margin="0 18 0 0"
|
||||||
|
Icon=""
|
||||||
|
Sub="{DynamicResource AnimationTip}">
|
||||||
|
<cc:ExCard.SideContent>
|
||||||
<ui:ToggleSwitch
|
<ui:ToggleSwitch
|
||||||
IsOn="{Binding UseAnimation}"
|
IsOn="{Binding UseAnimation}"
|
||||||
OffContent="{DynamicResource disable}"
|
OffContent="{DynamicResource disable}"
|
||||||
OnContent="{DynamicResource enable}" />
|
OnContent="{DynamicResource enable}" />
|
||||||
</cc:Card>
|
</cc:ExCard.SideContent>
|
||||||
<cc:Card
|
<cc:Card
|
||||||
Title="{DynamicResource AnimationSpeed}"
|
Title="{DynamicResource AnimationSpeed}"
|
||||||
Icon=""
|
|
||||||
Sub="{DynamicResource AnimationSpeedTip}"
|
Sub="{DynamicResource AnimationSpeedTip}"
|
||||||
Visibility="{Binding UseAnimation, Converter={StaticResource BoolToVisibilityConverter}}">
|
Type="InsideFit">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<ComboBox
|
<ComboBox
|
||||||
MinWidth="160"
|
MinWidth="160"
|
||||||
|
|
@ -586,25 +627,25 @@
|
||||||
IsEqualTo={x:Static userSettings:AnimationSpeeds.Custom}}" />
|
IsEqualTo={x:Static userSettings:AnimationSpeeds.Custom}}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</cc:Card>
|
</cc:Card>
|
||||||
</cc:CardGroup>
|
</cc:ExCard>
|
||||||
|
|
||||||
<!-- SFX -->
|
<!-- SFX -->
|
||||||
<cc:CardGroup Margin="0 14 0 0">
|
<cc:ExCard
|
||||||
<cc:Card
|
Title="{DynamicResource SoundEffect}"
|
||||||
Title="{DynamicResource SoundEffect}"
|
Margin="0 4 0 0"
|
||||||
Icon=""
|
Icon=""
|
||||||
Sub="{DynamicResource SoundEffectTip}">
|
Sub="{DynamicResource SoundEffectTip}">
|
||||||
|
<cc:ExCard.SideContent>
|
||||||
<ui:ToggleSwitch
|
<ui:ToggleSwitch
|
||||||
IsOn="{Binding UseSound}"
|
IsOn="{Binding UseSound}"
|
||||||
OffContent="{DynamicResource disable}"
|
OffContent="{DynamicResource disable}"
|
||||||
OnContent="{DynamicResource enable}" />
|
OnContent="{DynamicResource enable}" />
|
||||||
</cc:Card>
|
</cc:ExCard.SideContent>
|
||||||
<cc:Card
|
<cc:Card
|
||||||
Title="{DynamicResource SoundEffectVolume}"
|
Title="{DynamicResource SoundEffectVolume}"
|
||||||
Icon=""
|
|
||||||
IsEnabled="{Binding EnableVolumeAdjustment}"
|
IsEnabled="{Binding EnableVolumeAdjustment}"
|
||||||
Sub="{DynamicResource SoundEffectVolumeTip}"
|
Sub="{DynamicResource SoundEffectVolumeTip}"
|
||||||
Visibility="{Binding UseSound, Converter={StaticResource BoolToVisibilityConverter}}">
|
Type="InsideFit">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="0 0 8 0"
|
Margin="0 0 8 0"
|
||||||
|
|
@ -621,7 +662,7 @@
|
||||||
Value="{Binding SoundEffectVolume}" />
|
Value="{Binding SoundEffectVolume}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</cc:Card>
|
</cc:Card>
|
||||||
</cc:CardGroup>
|
</cc:ExCard>
|
||||||
<Border
|
<Border
|
||||||
Name="WMPWarning"
|
Name="WMPWarning"
|
||||||
Padding="0 10"
|
Padding="0 10"
|
||||||
|
|
@ -657,6 +698,18 @@
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Fonts and icons -->
|
||||||
|
<cc:Card
|
||||||
|
Title="{DynamicResource useGlyphUI}"
|
||||||
|
Margin="0 0 0 0"
|
||||||
|
Icon=""
|
||||||
|
Sub="{DynamicResource useGlyphUIEffect}">
|
||||||
|
<ui:ToggleSwitch
|
||||||
|
IsOn="{Binding UseGlyphIcons}"
|
||||||
|
OffContent="{DynamicResource disable}"
|
||||||
|
OnContent="{DynamicResource enable}" />
|
||||||
|
</cc:Card>
|
||||||
<!-- Settings color scheme -->
|
<!-- Settings color scheme -->
|
||||||
<cc:Card
|
<cc:Card
|
||||||
Title="{DynamicResource ColorScheme}"
|
Title="{DynamicResource ColorScheme}"
|
||||||
|
|
@ -667,9 +720,8 @@
|
||||||
DisplayMemberPath="Display"
|
DisplayMemberPath="Display"
|
||||||
FontSize="14"
|
FontSize="14"
|
||||||
ItemsSource="{Binding ColorSchemes}"
|
ItemsSource="{Binding ColorSchemes}"
|
||||||
SelectedValue="{Binding Settings.ColorScheme}"
|
SelectedValue="{Binding ColorScheme, Mode=TwoWay}"
|
||||||
SelectedValuePath="Value"
|
SelectedValuePath="Value" />
|
||||||
SelectionChanged="Selector_OnSelectionChanged" />
|
|
||||||
</cc:Card>
|
</cc:Card>
|
||||||
|
|
||||||
<!-- Theme folder -->
|
<!-- Theme folder -->
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
using System.Windows.Controls;
|
|
||||||
using System.Windows.Navigation;
|
using System.Windows.Navigation;
|
||||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
using Flow.Launcher.SettingPages.ViewModels;
|
using Flow.Launcher.SettingPages.ViewModels;
|
||||||
|
|
@ -23,9 +22,4 @@ public partial class SettingsPaneTheme : Page
|
||||||
|
|
||||||
base.OnNavigatedTo(e);
|
base.OnNavigatedTo(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Selector_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
|
|
||||||
{
|
|
||||||
_viewModel.UpdateColorScheme();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@
|
||||||
Loaded="OnLoaded"
|
Loaded="OnLoaded"
|
||||||
MouseDown="window_MouseDown"
|
MouseDown="window_MouseDown"
|
||||||
ResizeMode="CanResize"
|
ResizeMode="CanResize"
|
||||||
|
SnapsToDevicePixels="True"
|
||||||
|
UseLayoutRounding="True"
|
||||||
StateChanged="Window_StateChanged"
|
StateChanged="Window_StateChanged"
|
||||||
Top="{Binding SettingWindowTop, Mode=TwoWay}"
|
Top="{Binding SettingWindowTop, Mode=TwoWay}"
|
||||||
WindowStartupLocation="Manual"
|
WindowStartupLocation="Manual"
|
||||||
|
|
@ -54,6 +56,7 @@
|
||||||
Width="16"
|
Width="16"
|
||||||
Height="16"
|
Height="16"
|
||||||
Margin="10 4 4 4"
|
Margin="10 4 4 4"
|
||||||
|
RenderOptions.BitmapScalingMode="HighQuality"
|
||||||
Source="/Images/app.png" />
|
Source="/Images/app.png" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ using System.Windows.Forms;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using System.Windows.Interop;
|
using System.Windows.Interop;
|
||||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
using Flow.Launcher.Helper;
|
using Flow.Launcher.Infrastructure;
|
||||||
using Flow.Launcher.Infrastructure.UserSettings;
|
using Flow.Launcher.Infrastructure.UserSettings;
|
||||||
using Flow.Launcher.Plugin;
|
using Flow.Launcher.Plugin;
|
||||||
using Flow.Launcher.SettingPages.Views;
|
using Flow.Launcher.SettingPages.Views;
|
||||||
|
|
@ -92,13 +92,13 @@ public partial class SettingWindow
|
||||||
{
|
{
|
||||||
if (WindowState == WindowState.Maximized)
|
if (WindowState == WindowState.Maximized)
|
||||||
{
|
{
|
||||||
MaximizeButton.Visibility = Visibility.Collapsed;
|
MaximizeButton.Visibility = Visibility.Hidden;
|
||||||
RestoreButton.Visibility = Visibility.Visible;
|
RestoreButton.Visibility = Visibility.Visible;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
MaximizeButton.Visibility = Visibility.Visible;
|
MaximizeButton.Visibility = Visibility.Visible;
|
||||||
RestoreButton.Visibility = Visibility.Collapsed;
|
RestoreButton.Visibility = Visibility.Hidden;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -143,8 +143,8 @@ public partial class SettingWindow
|
||||||
private double WindowLeft()
|
private double WindowLeft()
|
||||||
{
|
{
|
||||||
var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position);
|
var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position);
|
||||||
var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0);
|
var dip1 = Win32Helper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0);
|
||||||
var dip2 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.Width, 0);
|
var dip2 = Win32Helper.TransformPixelsToDIP(this, screen.WorkingArea.Width, 0);
|
||||||
var left = (dip2.X - ActualWidth) / 2 + dip1.X;
|
var left = (dip2.X - ActualWidth) / 2 + dip1.X;
|
||||||
return left;
|
return left;
|
||||||
}
|
}
|
||||||
|
|
@ -152,8 +152,8 @@ public partial class SettingWindow
|
||||||
private double WindowTop()
|
private double WindowTop()
|
||||||
{
|
{
|
||||||
var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position);
|
var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position);
|
||||||
var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Y);
|
var dip1 = Win32Helper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Y);
|
||||||
var dip2 = WindowsInteropHelper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Height);
|
var dip2 = Win32Helper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Height);
|
||||||
var top = (dip2.Y - ActualHeight) / 2 + dip1.Y - 20;
|
var top = (dip2.Y - ActualHeight) / 2 + dip1.Y - 20;
|
||||||
return top;
|
return top;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@
|
||||||
<Style x:Key="BaseQueryBoxStyle" TargetType="{x:Type TextBox}">
|
<Style x:Key="BaseQueryBoxStyle" TargetType="{x:Type TextBox}">
|
||||||
<Setter Property="BorderThickness" Value="0" />
|
<Setter Property="BorderThickness" Value="0" />
|
||||||
<Setter Property="FontSize" Value="28" />
|
<Setter Property="FontSize" Value="28" />
|
||||||
<Setter Property="FontWeight" Value="Regular" />
|
|
||||||
<Setter Property="Margin" Value="16 7 0 7" />
|
<Setter Property="Margin" Value="16 7 0 7" />
|
||||||
<Setter Property="Padding" Value="0 0 68 0" />
|
<Setter Property="Padding" Value="0 0 68 0" />
|
||||||
<Setter Property="Background" Value="Transparent" />
|
<Setter Property="Background" Value="Transparent" />
|
||||||
|
|
@ -107,7 +106,7 @@
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<Style x:Key="BasePendingLineStyle" TargetType="{x:Type Line}">
|
<Style x:Key="BasePendingLineStyle" TargetType="{x:Type Line}">
|
||||||
<Setter Property="Stroke" Value="Blue" />
|
<Setter Property="Stroke" Value="{StaticResource SystemAccentColorLight1Brush}" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<Style x:Key="BaseClockPanelPosition" TargetType="{x:Type Canvas}" />
|
<Style x:Key="BaseClockPanelPosition" TargetType="{x:Type Canvas}" />
|
||||||
|
|
@ -165,29 +164,6 @@
|
||||||
BasedOn="{StaticResource BaseClockPanel}"
|
BasedOn="{StaticResource BaseClockPanel}"
|
||||||
TargetType="{x:Type StackPanel}">
|
TargetType="{x:Type StackPanel}">
|
||||||
<Setter Property="Orientation" Value="Vertical" />
|
<Setter Property="Orientation" Value="Vertical" />
|
||||||
<Setter Property="Visibility" Value="Collapsed" />
|
|
||||||
<Style.Triggers>
|
|
||||||
<MultiDataTrigger>
|
|
||||||
<MultiDataTrigger.Conditions>
|
|
||||||
<Condition Binding="{Binding ElementName=QueryTextBox, UpdateSourceTrigger=PropertyChanged, Path=Text.Length}" Value="0" />
|
|
||||||
<Condition Binding="{Binding ElementName=ContextMenu, Path=Visibility}" Value="Collapsed" />
|
|
||||||
<Condition Binding="{Binding ElementName=History, Path=Visibility}" Value="Collapsed" />
|
|
||||||
</MultiDataTrigger.Conditions>
|
|
||||||
<Setter Property="Visibility" Value="Visible" />
|
|
||||||
<MultiDataTrigger.EnterActions>
|
|
||||||
<BeginStoryboard>
|
|
||||||
<Storyboard>
|
|
||||||
<DoubleAnimation
|
|
||||||
Storyboard.TargetProperty="Opacity"
|
|
||||||
From="0.0"
|
|
||||||
To="1"
|
|
||||||
Duration="0:0:0.25" />
|
|
||||||
</Storyboard>
|
|
||||||
</BeginStoryboard>
|
|
||||||
</MultiDataTrigger.EnterActions>
|
|
||||||
</MultiDataTrigger>
|
|
||||||
</Style.Triggers>
|
|
||||||
|
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<!-- Item Style -->
|
<!-- Item Style -->
|
||||||
|
|
@ -204,12 +180,10 @@
|
||||||
<Style x:Key="BaseItemTitleStyle" TargetType="{x:Type TextBlock}">
|
<Style x:Key="BaseItemTitleStyle" TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#FFFFF8" />
|
<Setter Property="Foreground" Value="#FFFFF8" />
|
||||||
<Setter Property="FontSize" Value="16" />
|
<Setter Property="FontSize" Value="16" />
|
||||||
<Setter Property="FontWeight" Value="Medium" />
|
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="BaseItemSubTitleStyle" TargetType="{x:Type TextBlock}">
|
<Style x:Key="BaseItemSubTitleStyle" TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#D9D9D4" />
|
<Setter Property="Foreground" Value="#D9D9D4" />
|
||||||
<Setter Property="FontSize" Value="13" />
|
<Setter Property="FontSize" Value="13" />
|
||||||
<Setter Property="FontWeight" Value="Normal" />
|
|
||||||
<Style.Triggers>
|
<Style.Triggers>
|
||||||
<DataTrigger Binding="{Binding ElementName=SubTitle, UpdateSourceTrigger=PropertyChanged, Path=Text.Length}" Value="0">
|
<DataTrigger Binding="{Binding ElementName=SubTitle, UpdateSourceTrigger=PropertyChanged, Path=Text.Length}" Value="0">
|
||||||
<Setter Property="Height" Value="0" />
|
<Setter Property="Height" Value="0" />
|
||||||
|
|
@ -241,7 +215,6 @@
|
||||||
<Style x:Key="BaseItemTitleSelectedStyle" TargetType="{x:Type TextBlock}">
|
<Style x:Key="BaseItemTitleSelectedStyle" TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#FFFFF8" />
|
<Setter Property="Foreground" Value="#FFFFF8" />
|
||||||
<Setter Property="FontSize" Value="16" />
|
<Setter Property="FontSize" Value="16" />
|
||||||
<Setter Property="FontWeight" Value="Normal" />
|
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="BaseItemSubTitleSelectedStyle" TargetType="{x:Type TextBlock}">
|
<Style x:Key="BaseItemSubTitleSelectedStyle" TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#D9D9D4" />
|
<Setter Property="Foreground" Value="#D9D9D4" />
|
||||||
|
|
@ -378,17 +351,41 @@
|
||||||
<Setter Property="Inline.FontWeight" Value="Bold" />
|
<Setter Property="Inline.FontWeight" Value="Bold" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="BaseItemHotkeyStyle" TargetType="{x:Type TextBlock}">
|
<Style x:Key="BaseItemHotkeyStyle" TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="FontSize" Value="15" />
|
<Setter Property="FontSize" Value="12" />
|
||||||
<Setter Property="Foreground" Value="#8f8f8f" />
|
<Setter Property="Foreground" Value="#8f8f8f" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="BaseItemHotkeySelectedStyle" TargetType="{x:Type TextBlock}">
|
<Style x:Key="BaseItemHotkeySelectedStyle" TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="FontSize" Value="15" />
|
<Setter Property="FontSize" Value="12" />
|
||||||
<Setter Property="Foreground" Value="#8f8f8f" />
|
<Setter Property="Foreground" Value="#8f8f8f" />
|
||||||
</Style>
|
</Style>
|
||||||
|
<Style x:Key="BaseItemHotkeyBGStyle" TargetType="{x:Type Border}">
|
||||||
|
<Setter Property="Margin" Value="12 0 12 0" />
|
||||||
|
<Setter Property="Padding" Value="6 4 6 4" />
|
||||||
|
<Setter Property="Background" Value="#138A8A8A" />
|
||||||
|
<Setter Property="CornerRadius" Value="4" />
|
||||||
|
</Style>
|
||||||
|
<Style x:Key="BaseItemHotkeyBGSelectedStyle" TargetType="{x:Type Border}">
|
||||||
|
<Setter Property="Margin" Value="12 0 12 0" />
|
||||||
|
<Setter Property="Padding" Value="6 4 6 4" />
|
||||||
|
<Setter Property="CornerRadius" Value="4" />
|
||||||
|
<Setter Property="Background" Value="#138A8A8A" />
|
||||||
|
</Style>
|
||||||
|
<Style
|
||||||
|
x:Key="ItemHotkeyBGStyle"
|
||||||
|
BasedOn="{StaticResource BaseItemHotkeyBGStyle}"
|
||||||
|
TargetType="{x:Type Border}">
|
||||||
|
<Setter Property="Margin" Value="12 0 12 0" />
|
||||||
|
<Setter Property="Padding" Value="6 4 6 4" />
|
||||||
|
<Setter Property="CornerRadius" Value="4" />
|
||||||
|
</Style>
|
||||||
|
<Style
|
||||||
|
x:Key="ItemHotkeyBGSelectedStyle"
|
||||||
|
BasedOn="{StaticResource BaseItemHotkeyBGSelectedStyle}"
|
||||||
|
TargetType="{x:Type Border}" />
|
||||||
|
|
||||||
<!-- DO NOT USE THIS KEY. this key for themes with wrong typo. This key should be removed. Right key is BaseItemHotkeySelectedStyle. -->
|
<!-- DO NOT USE THIS KEY. this key for themes with wrong typo. This key should be removed. Right key is BaseItemHotkeySelectedStyle. -->
|
||||||
<Style x:Key="BaseItemHotkeySelecetedStyle" TargetType="{x:Type TextBlock}">
|
<Style x:Key="BaseItemHotkeySelecetedStyle" TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="FontSize" Value="15" />
|
<Setter Property="FontSize" Value="12" />
|
||||||
<Setter Property="Foreground" Value="#8f8f8f" />
|
<Setter Property="Foreground" Value="#8f8f8f" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
|
|
@ -417,6 +414,7 @@
|
||||||
<Style.Triggers>
|
<Style.Triggers>
|
||||||
<MultiDataTrigger>
|
<MultiDataTrigger>
|
||||||
<MultiDataTrigger.Conditions>
|
<MultiDataTrigger.Conditions>
|
||||||
|
<Condition Binding="{Binding ElementName=History, Path=Visibility}" Value="Collapsed" />
|
||||||
<Condition Binding="{Binding ElementName=ResultListBox, Path=Items.Count}" Value="0" />
|
<Condition Binding="{Binding ElementName=ResultListBox, Path=Items.Count}" Value="0" />
|
||||||
</MultiDataTrigger.Conditions>
|
</MultiDataTrigger.Conditions>
|
||||||
<MultiDataTrigger.Setters>
|
<MultiDataTrigger.Setters>
|
||||||
|
|
@ -458,12 +456,12 @@
|
||||||
</DataTrigger>
|
</DataTrigger>
|
||||||
</Style.Triggers>
|
</Style.Triggers>
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<Style
|
<Style
|
||||||
x:Key="PreviewBorderStyle"
|
x:Key="PreviewBorderStyle"
|
||||||
BasedOn="{StaticResource BasePreviewBorderStyle}"
|
BasedOn="{StaticResource BasePreviewBorderStyle}"
|
||||||
TargetType="{x:Type Border}">
|
TargetType="{x:Type Border}">
|
||||||
<Setter Property="BorderBrush" Value="Gray" />
|
<Setter Property="BorderBrush" Value="Gray" />
|
||||||
|
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<Style x:Key="PreviewArea" TargetType="{x:Type Grid}">
|
<Style x:Key="PreviewArea" TargetType="{x:Type Grid}">
|
||||||
|
|
@ -473,8 +471,8 @@
|
||||||
<MultiDataTrigger.Conditions>
|
<MultiDataTrigger.Conditions>
|
||||||
<!--
|
<!--
|
||||||
<Condition Binding="{Binding ElementName=ResultListBox, Path=Visibility}" Value="Collapsed" />
|
<Condition Binding="{Binding ElementName=ResultListBox, Path=Visibility}" Value="Collapsed" />
|
||||||
<Condition Binding="{Binding ElementName=ContextMenu, Path=Visibility}" Value="Collapsed" />
|
<Condition Binding="{Binding ElementName=ContextMenu, Path=Visibility}" Value="Collapsed" />-->
|
||||||
<Condition Binding="{Binding ElementName=History, Path=Visibility}" Value="Collapsed" />-->
|
<Condition Binding="{Binding ElementName=History, Path=Visibility}" Value="Collapsed" />
|
||||||
<Condition Binding="{Binding ElementName=ResultListBox, Path=Items.Count}" Value="0" />
|
<Condition Binding="{Binding ElementName=ResultListBox, Path=Items.Count}" Value="0" />
|
||||||
</MultiDataTrigger.Conditions>
|
</MultiDataTrigger.Conditions>
|
||||||
<MultiDataTrigger.Setters>
|
<MultiDataTrigger.Setters>
|
||||||
|
|
@ -514,7 +512,6 @@
|
||||||
x:Key="ItemHotkeyStyle"
|
x:Key="ItemHotkeyStyle"
|
||||||
BasedOn="{StaticResource BaseItemHotkeyStyle}"
|
BasedOn="{StaticResource BaseItemHotkeyStyle}"
|
||||||
TargetType="{x:Type TextBlock}">
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="FontSize" Value="15" />
|
|
||||||
<Setter Property="Foreground" Value="#8f8f8f" />
|
<Setter Property="Foreground" Value="#8f8f8f" />
|
||||||
<Setter Property="Opacity" Value="0.5" />
|
<Setter Property="Opacity" Value="0.5" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
@ -522,7 +519,6 @@
|
||||||
x:Key="ItemHotkeySelectedStyle"
|
x:Key="ItemHotkeySelectedStyle"
|
||||||
BasedOn="{StaticResource BaseItemHotkeySelectedStyle}"
|
BasedOn="{StaticResource BaseItemHotkeySelectedStyle}"
|
||||||
TargetType="{x:Type TextBlock}">
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="FontSize" Value="15" />
|
|
||||||
<Setter Property="Foreground" Value="#8f8f8f" />
|
<Setter Property="Foreground" Value="#8f8f8f" />
|
||||||
<Setter Property="Opacity" Value="0.5" />
|
<Setter Property="Opacity" Value="0.5" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
@ -557,6 +553,14 @@
|
||||||
<Setter Property="TextWrapping" Value="Wrap" />
|
<Setter Property="TextWrapping" Value="Wrap" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
|
<Style x:Key="PreviewWindowBorderStyle" TargetType="{x:Type Border}">
|
||||||
|
<Setter Property="BorderThickness" Value="0" />
|
||||||
|
<Setter Property="Background" Value="#2F2F2F" />
|
||||||
|
<Setter Property="Padding" Value="0 0 0 0" />
|
||||||
|
<Setter Property="CornerRadius" Value="5" />
|
||||||
|
<Setter Property="UseLayoutRounding" Value="True" />
|
||||||
|
<Setter Property="SnapsToDevicePixels" Value="True" />
|
||||||
|
</Style>
|
||||||
<!-- Style for old version themes -->
|
<!-- Style for old version themes -->
|
||||||
<Style
|
<Style
|
||||||
x:Key="PreviewItemTitleStyle"
|
x:Key="PreviewItemTitleStyle"
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,10 @@
|
||||||
</ResourceDictionary.MergedDictionaries>
|
</ResourceDictionary.MergedDictionaries>
|
||||||
|
|
||||||
<system:Boolean x:Key="ThemeBlurEnabled">True</system:Boolean>
|
<system:Boolean x:Key="ThemeBlurEnabled">True</system:Boolean>
|
||||||
|
<system:String x:Key="SystemBG">Dark</system:String>
|
||||||
|
<Color x:Key="LightBG">#C7000000</Color>
|
||||||
|
<Color x:Key="DarkBG">#C7000000</Color>
|
||||||
|
<Thickness x:Key="ResultMargin">0 0 0 8</Thickness>
|
||||||
|
|
||||||
<Style x:Key="WindowRadius" TargetType="{x:Type Border}">
|
<Style x:Key="WindowRadius" TargetType="{x:Type Border}">
|
||||||
<Setter Property="CornerRadius" Value="0" />
|
<Setter Property="CornerRadius" Value="0" />
|
||||||
|
|
@ -51,7 +55,6 @@
|
||||||
</Setter.Value>
|
</Setter.Value>
|
||||||
</Setter>
|
</Setter>
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<Style
|
<Style
|
||||||
x:Key="SeparatorStyle"
|
x:Key="SeparatorStyle"
|
||||||
BasedOn="{StaticResource BaseSeparatorStyle}"
|
BasedOn="{StaticResource BaseSeparatorStyle}"
|
||||||
|
|
@ -139,17 +142,21 @@
|
||||||
BasedOn="{StaticResource BaseSearchIconStyle}"
|
BasedOn="{StaticResource BaseSearchIconStyle}"
|
||||||
TargetType="{x:Type Path}">
|
TargetType="{x:Type Path}">
|
||||||
<Setter Property="Fill" Value="#ffffff" />
|
<Setter Property="Fill" Value="#ffffff" />
|
||||||
<Setter Property="Width" Value="32" />
|
<Setter Property="Width" Value="30" />
|
||||||
<Setter Property="Height" Value="32" />
|
<Setter Property="Height" Value="30" />
|
||||||
<Setter Property="Opacity" Value="0.2" />
|
<Setter Property="Opacity" Value="0.2" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="ItemHotkeyStyle" TargetType="{x:Type TextBlock}">
|
<Style
|
||||||
<Setter Property="FontSize" Value="14" />
|
x:Key="ItemHotkeyStyle"
|
||||||
|
BasedOn="{StaticResource BaseItemHotkeyStyle}"
|
||||||
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#ffffff" />
|
<Setter Property="Foreground" Value="#ffffff" />
|
||||||
<Setter Property="Opacity" Value="0.2" />
|
<Setter Property="Opacity" Value="0.2" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="ItemHotkeySelectedStyle" TargetType="{x:Type TextBlock}">
|
<Style
|
||||||
<Setter Property="FontSize" Value="14" />
|
x:Key="ItemHotkeySelectedStyle"
|
||||||
|
BasedOn="{StaticResource BaseItemHotkeySelectedStyle}"
|
||||||
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#ffffff" />
|
<Setter Property="Foreground" Value="#ffffff" />
|
||||||
<Setter Property="Opacity" Value="0.2" />
|
<Setter Property="Opacity" Value="0.2" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,12 @@
|
||||||
<ResourceDictionary.MergedDictionaries>
|
<ResourceDictionary.MergedDictionaries>
|
||||||
<ResourceDictionary Source="pack://application:,,,/Themes/Base.xaml" />
|
<ResourceDictionary Source="pack://application:,,,/Themes/Base.xaml" />
|
||||||
</ResourceDictionary.MergedDictionaries>
|
</ResourceDictionary.MergedDictionaries>
|
||||||
|
|
||||||
<system:Boolean x:Key="ThemeBlurEnabled">True</system:Boolean>
|
<system:Boolean x:Key="ThemeBlurEnabled">True</system:Boolean>
|
||||||
|
<system:String x:Key="SystemBG">Dark</system:String>
|
||||||
|
<Color x:Key="LightBG">#B0000000</Color>
|
||||||
|
<Color x:Key="DarkBG">#B6000000</Color>
|
||||||
|
<Thickness x:Key="ResultMargin">0 0 0 8</Thickness>
|
||||||
|
|
||||||
<Style x:Key="WindowRadius" TargetType="{x:Type Border}">
|
<Style x:Key="WindowRadius" TargetType="{x:Type Border}">
|
||||||
<Setter Property="CornerRadius" Value="0" />
|
<Setter Property="CornerRadius" Value="0" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
@ -135,17 +139,21 @@
|
||||||
BasedOn="{StaticResource BaseSearchIconStyle}"
|
BasedOn="{StaticResource BaseSearchIconStyle}"
|
||||||
TargetType="{x:Type Path}">
|
TargetType="{x:Type Path}">
|
||||||
<Setter Property="Fill" Value="#ffffff" />
|
<Setter Property="Fill" Value="#ffffff" />
|
||||||
<Setter Property="Width" Value="32" />
|
<Setter Property="Width" Value="30" />
|
||||||
<Setter Property="Height" Value="32" />
|
<Setter Property="Height" Value="30" />
|
||||||
<Setter Property="Opacity" Value="0.5" />
|
<Setter Property="Opacity" Value="0.5" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="ItemHotkeyStyle" TargetType="{x:Type TextBlock}">
|
<Style
|
||||||
<Setter Property="FontSize" Value="14" />
|
x:Key="ItemHotkeyStyle"
|
||||||
|
BasedOn="{StaticResource BaseItemHotkeyStyle}"
|
||||||
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#ffffff" />
|
<Setter Property="Foreground" Value="#ffffff" />
|
||||||
<Setter Property="Opacity" Value="0.5" />
|
<Setter Property="Opacity" Value="0.5" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="ItemHotkeySelectedStyle" TargetType="{x:Type TextBlock}">
|
<Style
|
||||||
<Setter Property="FontSize" Value="14" />
|
x:Key="ItemHotkeySelectedStyle"
|
||||||
|
BasedOn="{StaticResource BaseItemHotkeySelectedStyle}"
|
||||||
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#ffffff" />
|
<Setter Property="Foreground" Value="#ffffff" />
|
||||||
<Setter Property="Opacity" Value="0.5" />
|
<Setter Property="Opacity" Value="0.5" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
|
||||||
|
|
@ -10,14 +10,26 @@
|
||||||
<ResourceDictionary.MergedDictionaries>
|
<ResourceDictionary.MergedDictionaries>
|
||||||
<ResourceDictionary Source="pack://application:,,,/Themes/Base.xaml" />
|
<ResourceDictionary Source="pack://application:,,,/Themes/Base.xaml" />
|
||||||
</ResourceDictionary.MergedDictionaries>
|
</ResourceDictionary.MergedDictionaries>
|
||||||
|
|
||||||
<system:Boolean x:Key="ThemeBlurEnabled">True</system:Boolean>
|
<system:Boolean x:Key="ThemeBlurEnabled">True</system:Boolean>
|
||||||
|
<system:String x:Key="SystemBG">Light</system:String>
|
||||||
|
<Color x:Key="LightBG">#BFFAFAFA</Color>
|
||||||
|
<Color x:Key="DarkBG">#BFFAFAFA</Color>
|
||||||
|
<Thickness x:Key="ResultMargin">0 0 0 8</Thickness>
|
||||||
|
|
||||||
<Style
|
<Style
|
||||||
x:Key="ItemGlyph"
|
x:Key="ItemGlyph"
|
||||||
BasedOn="{StaticResource BaseGlyphStyle}"
|
BasedOn="{StaticResource BaseGlyphStyle}"
|
||||||
TargetType="{x:Type TextBlock}">
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#FF000000" />
|
<Setter Property="Foreground" Value="#FF000000" />
|
||||||
</Style>
|
</Style>
|
||||||
|
<Style x:Key="ItemGlyphSelectedStyle" TargetType="{x:Type TextBlock}">
|
||||||
|
<Setter Property="Foreground" Value="#FF000000" />
|
||||||
|
<Setter Property="VerticalAlignment" Value="Center" />
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Center" />
|
||||||
|
<Setter Property="Width" Value="25" />
|
||||||
|
<Setter Property="Height" Value="25" />
|
||||||
|
<Setter Property="FontSize" Value="25" />
|
||||||
|
</Style>
|
||||||
<Style x:Key="WindowRadius" TargetType="{x:Type Border}">
|
<Style x:Key="WindowRadius" TargetType="{x:Type Border}">
|
||||||
<Setter Property="CornerRadius" Value="0" />
|
<Setter Property="CornerRadius" Value="0" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
@ -33,7 +45,9 @@
|
||||||
<Style
|
<Style
|
||||||
x:Key="QuerySuggestionBoxStyle"
|
x:Key="QuerySuggestionBoxStyle"
|
||||||
BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}"
|
BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}"
|
||||||
TargetType="{x:Type TextBox}" />
|
TargetType="{x:Type TextBox}">
|
||||||
|
<Setter Property="Foreground" Value="#39000000" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
<Style
|
<Style
|
||||||
x:Key="WindowBorderStyle"
|
x:Key="WindowBorderStyle"
|
||||||
|
|
@ -76,7 +90,7 @@
|
||||||
x:Key="ItemSubTitleStyle"
|
x:Key="ItemSubTitleStyle"
|
||||||
BasedOn="{StaticResource BaseItemSubTitleStyle}"
|
BasedOn="{StaticResource BaseItemSubTitleStyle}"
|
||||||
TargetType="{x:Type TextBlock}">
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#818181" />
|
<Setter Property="Foreground" Value="#85000000" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="ItemTitleSelectedStyle"
|
x:Key="ItemTitleSelectedStyle"
|
||||||
|
|
@ -88,18 +102,18 @@
|
||||||
x:Key="ItemSubTitleSelectedStyle"
|
x:Key="ItemSubTitleSelectedStyle"
|
||||||
BasedOn="{StaticResource BaseItemSubTitleSelectedStyle}"
|
BasedOn="{StaticResource BaseItemSubTitleSelectedStyle}"
|
||||||
TargetType="{x:Type TextBlock}">
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#72767d" />
|
<Setter Property="Foreground" Value="#85000000" />
|
||||||
</Style>
|
</Style>
|
||||||
<SolidColorBrush
|
<SolidColorBrush
|
||||||
x:Key="ItemSelectedBackgroundColor"
|
x:Key="ItemSelectedBackgroundColor"
|
||||||
Opacity="0.5"
|
Opacity="0.6"
|
||||||
Color="#ccd0d4" />
|
Color="#33000000" />
|
||||||
|
|
||||||
<Style
|
<Style
|
||||||
x:Key="SeparatorStyle"
|
x:Key="SeparatorStyle"
|
||||||
BasedOn="{StaticResource BaseSeparatorStyle}"
|
BasedOn="{StaticResource BaseSeparatorStyle}"
|
||||||
TargetType="{x:Type Rectangle}">
|
TargetType="{x:Type Rectangle}">
|
||||||
<Setter Property="Fill" Value="#c6c6c6" />
|
<Setter Property="Fill" Value="#22000000" />
|
||||||
<Setter Property="Height" Value="1" />
|
<Setter Property="Height" Value="1" />
|
||||||
<Setter Property="Margin" Value="12 0 12 8" />
|
<Setter Property="Margin" Value="12 0 12 8" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
@ -112,7 +126,7 @@
|
||||||
<Setter.Value>
|
<Setter.Value>
|
||||||
<ControlTemplate TargetType="{x:Type Thumb}">
|
<ControlTemplate TargetType="{x:Type Thumb}">
|
||||||
<Border
|
<Border
|
||||||
Background="#bebebe"
|
Background="#3C000000"
|
||||||
BorderBrush="Transparent"
|
BorderBrush="Transparent"
|
||||||
BorderThickness="0"
|
BorderThickness="0"
|
||||||
CornerRadius="2"
|
CornerRadius="2"
|
||||||
|
|
@ -132,17 +146,21 @@
|
||||||
BasedOn="{StaticResource BaseSearchIconStyle}"
|
BasedOn="{StaticResource BaseSearchIconStyle}"
|
||||||
TargetType="{x:Type Path}">
|
TargetType="{x:Type Path}">
|
||||||
<Setter Property="Fill" Value="#000000" />
|
<Setter Property="Fill" Value="#000000" />
|
||||||
<Setter Property="Width" Value="32" />
|
<Setter Property="Width" Value="30" />
|
||||||
<Setter Property="Height" Value="32" />
|
<Setter Property="Height" Value="30" />
|
||||||
<Setter Property="Opacity" Value="0.2" />
|
<Setter Property="Opacity" Value="0.2" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="ItemHotkeyStyle" TargetType="{x:Type TextBlock}">
|
<Style
|
||||||
<Setter Property="FontSize" Value="14" />
|
x:Key="ItemHotkeyStyle"
|
||||||
|
BasedOn="{StaticResource BaseItemHotkeyStyle}"
|
||||||
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#000000" />
|
<Setter Property="Foreground" Value="#000000" />
|
||||||
<Setter Property="Opacity" Value="0.2" />
|
<Setter Property="Opacity" Value="0.2" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="ItemHotkeySelectedStyle" TargetType="{x:Type TextBlock}">
|
<Style
|
||||||
<Setter Property="FontSize" Value="14" />
|
x:Key="ItemHotkeySelectedStyle"
|
||||||
|
BasedOn="{StaticResource BaseItemHotkeySelectedStyle}"
|
||||||
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#000000" />
|
<Setter Property="Foreground" Value="#000000" />
|
||||||
<Setter Property="Opacity" Value="0.2" />
|
<Setter Property="Opacity" Value="0.2" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<!--
|
<!--
|
||||||
Name: Circle
|
Name: Circle
|
||||||
IsDark: True
|
IsDark: True
|
||||||
HasBlur: False
|
HasBlur: True
|
||||||
-->
|
-->
|
||||||
<ResourceDictionary
|
<ResourceDictionary
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
|
@ -12,6 +12,10 @@
|
||||||
<ResourceDictionary.MergedDictionaries>
|
<ResourceDictionary.MergedDictionaries>
|
||||||
<ResourceDictionary Source="pack://application:,,,/Themes/Base.xaml" />
|
<ResourceDictionary Source="pack://application:,,,/Themes/Base.xaml" />
|
||||||
</ResourceDictionary.MergedDictionaries>
|
</ResourceDictionary.MergedDictionaries>
|
||||||
|
<system:Boolean x:Key="ThemeBlurEnabled">True</system:Boolean>
|
||||||
|
<system:String x:Key="SystemBG">Auto</system:String>
|
||||||
|
<Color x:Key="LightBG">#E5E6E6E6</Color>
|
||||||
|
<Color x:Key="DarkBG">#DF1F1F1F</Color>
|
||||||
<Style
|
<Style
|
||||||
x:Key="ItemGlyph"
|
x:Key="ItemGlyph"
|
||||||
BasedOn="{StaticResource BaseGlyphStyle}"
|
BasedOn="{StaticResource BaseGlyphStyle}"
|
||||||
|
|
@ -22,8 +26,8 @@
|
||||||
x:Key="QueryBoxStyle"
|
x:Key="QueryBoxStyle"
|
||||||
BasedOn="{StaticResource BaseQueryBoxStyle}"
|
BasedOn="{StaticResource BaseQueryBoxStyle}"
|
||||||
TargetType="{x:Type TextBox}">
|
TargetType="{x:Type TextBox}">
|
||||||
<Setter Property="Padding" Value="0 4 50 0" />
|
<Setter Property="Padding" Value="0 0 50 0" />
|
||||||
<Setter Property="Foreground" Value="{m:DynamicColor SystemControlPageTextBaseHighBrush}" />
|
<Setter Property="Foreground" Value="{m:DynamicColor SystemControlHighlightBaseHighBrush}" />
|
||||||
<Setter Property="FontSize" Value="18" />
|
<Setter Property="FontSize" Value="18" />
|
||||||
<Setter Property="Height" Value="38" />
|
<Setter Property="Height" Value="38" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
@ -32,7 +36,7 @@
|
||||||
x:Key="QuerySuggestionBoxStyle"
|
x:Key="QuerySuggestionBoxStyle"
|
||||||
BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}"
|
BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}"
|
||||||
TargetType="{x:Type TextBox}">
|
TargetType="{x:Type TextBox}">
|
||||||
<Setter Property="Padding" Value="0 4 50 0" />
|
<Setter Property="Padding" Value="0 0 50 0" />
|
||||||
<Setter Property="Background" Value="Transparent" />
|
<Setter Property="Background" Value="Transparent" />
|
||||||
<Setter Property="Height" Value="38" />
|
<Setter Property="Height" Value="38" />
|
||||||
<Setter Property="FontSize" Value="18" />
|
<Setter Property="FontSize" Value="18" />
|
||||||
|
|
@ -75,7 +79,7 @@
|
||||||
x:Key="SeparatorStyle"
|
x:Key="SeparatorStyle"
|
||||||
BasedOn="{StaticResource BaseSeparatorStyle}"
|
BasedOn="{StaticResource BaseSeparatorStyle}"
|
||||||
TargetType="{x:Type Rectangle}">
|
TargetType="{x:Type Rectangle}">
|
||||||
<Setter Property="Fill" Value="{m:DynamicColor SystemControlBackgroundChromeMediumBrush}" />
|
<Setter Property="Fill" Value="{DynamicResource SeparatorForeground}" />
|
||||||
<Setter Property="Height" Value="1" />
|
<Setter Property="Height" Value="1" />
|
||||||
<Setter Property="Margin" Value="0 0 0 8" />
|
<Setter Property="Margin" Value="0 0 0 8" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@
|
||||||
x:Key="QueryBoxStyle"
|
x:Key="QueryBoxStyle"
|
||||||
BasedOn="{StaticResource BaseQueryBoxStyle}"
|
BasedOn="{StaticResource BaseQueryBoxStyle}"
|
||||||
TargetType="{x:Type TextBox}">
|
TargetType="{x:Type TextBox}">
|
||||||
<Setter Property="Padding" Value="0,4,50,0" />
|
<Setter Property="Padding" Value="0,0,50,0" />
|
||||||
<Setter Property="CaretBrush" Value="#336766" />
|
<Setter Property="CaretBrush" Value="#336766" />
|
||||||
<Setter Property="Foreground" Value="#e7e9eb" />
|
<Setter Property="Foreground" Value="#e7e9eb" />
|
||||||
<Setter Property="FontSize" Value="18" />
|
<Setter Property="FontSize" Value="18" />
|
||||||
|
|
@ -44,7 +44,7 @@
|
||||||
x:Key="QuerySuggestionBoxStyle"
|
x:Key="QuerySuggestionBoxStyle"
|
||||||
BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}"
|
BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}"
|
||||||
TargetType="{x:Type TextBox}">
|
TargetType="{x:Type TextBox}">
|
||||||
<Setter Property="Padding" Value="0,4,50,0" />
|
<Setter Property="Padding" Value="0,0,50,0" />
|
||||||
<Setter Property="Background" Value="Transparent" />
|
<Setter Property="Background" Value="Transparent" />
|
||||||
<Setter Property="Height" Value="38" />
|
<Setter Property="Height" Value="38" />
|
||||||
<Setter Property="FontSize" Value="18" />
|
<Setter Property="FontSize" Value="18" />
|
||||||
|
|
@ -56,7 +56,7 @@
|
||||||
BasedOn="{StaticResource BaseWindowBorderStyle}"
|
BasedOn="{StaticResource BaseWindowBorderStyle}"
|
||||||
TargetType="{x:Type Border}">
|
TargetType="{x:Type Border}">
|
||||||
<Setter Property="BorderThickness" Value="1" />
|
<Setter Property="BorderThickness" Value="1" />
|
||||||
<Setter Property="BorderBrush" Value="#09181e" />
|
<Setter Property="BorderBrush" Value="#1e292f" />
|
||||||
<Setter Property="Background" Value="#0f1f26" />
|
<Setter Property="Background" Value="#0f1f26" />
|
||||||
<Setter Property="CornerRadius" Value="6" />
|
<Setter Property="CornerRadius" Value="6" />
|
||||||
<Setter Property="UseLayoutRounding" Value="True" />
|
<Setter Property="UseLayoutRounding" Value="True" />
|
||||||
|
|
|
||||||
|
|
@ -1,58 +1,57 @@
|
||||||
|
<!--
|
||||||
|
Name: Rider
|
||||||
|
IsDark: False
|
||||||
|
HasBlur: False
|
||||||
|
-->
|
||||||
<ResourceDictionary
|
<ResourceDictionary
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
||||||
|
|
||||||
<ResourceDictionary.MergedDictionaries>
|
<ResourceDictionary.MergedDictionaries>
|
||||||
<ResourceDictionary Source="pack://application:,,,/Themes/Base.xaml" />
|
<ResourceDictionary Source="pack://application:,,,/Themes/Base.xaml" />
|
||||||
</ResourceDictionary.MergedDictionaries>
|
</ResourceDictionary.MergedDictionaries>
|
||||||
<Thickness x:Key="ResultMargin">0 0 0 8</Thickness>
|
|
||||||
<Style
|
<Style
|
||||||
x:Key="ItemGlyph"
|
x:Key="ItemGlyph"
|
||||||
BasedOn="{StaticResource BaseGlyphStyle}"
|
BasedOn="{StaticResource BaseGlyphStyle}"
|
||||||
TargetType="{x:Type TextBlock}">
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#ebebeb" />
|
<Setter Property="Foreground" Value="#dfe1e5" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="QueryBoxStyle"
|
x:Key="QueryBoxStyle"
|
||||||
BasedOn="{StaticResource BaseQueryBoxStyle}"
|
BasedOn="{StaticResource BaseQueryBoxStyle}"
|
||||||
TargetType="{x:Type TextBox}">
|
TargetType="{x:Type TextBox}">
|
||||||
<Setter Property="Foreground" Value="#ebebeb" />
|
<Setter Property="Padding" Value="0 0 50 0" />
|
||||||
<Setter Property="Background" Value="Transparent" />
|
<Setter Property="Foreground" Value="#dfe1e5" />
|
||||||
|
<Setter Property="FontSize" Value="18" />
|
||||||
|
<Setter Property="Height" Value="38" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<Style
|
<Style
|
||||||
x:Key="QuerySuggestionBoxStyle"
|
x:Key="QuerySuggestionBoxStyle"
|
||||||
BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}"
|
BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}"
|
||||||
TargetType="{x:Type TextBox}">
|
TargetType="{x:Type TextBox}">
|
||||||
<Setter Property="Foreground" Value="#ebebeb" />
|
<Setter Property="Padding" Value="0 0 50 0" />
|
||||||
<Setter Property="Opacity" Value="0.2" />
|
<Setter Property="Background" Value="Transparent" />
|
||||||
|
<Setter Property="Height" Value="38" />
|
||||||
|
<Setter Property="FontSize" Value="18" />
|
||||||
|
<Setter Property="Foreground" Value="#24DFE1E5" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<Style
|
<Style
|
||||||
x:Key="WindowBorderStyle"
|
x:Key="WindowBorderStyle"
|
||||||
BasedOn="{StaticResource BaseWindowBorderStyle}"
|
BasedOn="{StaticResource BaseWindowBorderStyle}"
|
||||||
TargetType="{x:Type Border}">
|
TargetType="{x:Type Border}">
|
||||||
<Setter Property="CornerRadius" Value="5" />
|
<Setter Property="BorderThickness" Value="1" />
|
||||||
<Setter Property="BorderThickness" Value="1,1,0,0" />
|
<Setter Property="BorderBrush" Value="#393b40" />
|
||||||
<Setter Property="BorderBrush" Value="#666666" />
|
<Setter Property="Background" Value="#2b2d30" />
|
||||||
<Setter Property="Background">
|
<Setter Property="CornerRadius" Value="8" />
|
||||||
<Setter.Value>
|
<Setter Property="UseLayoutRounding" Value="True" />
|
||||||
<SolidColorBrush Opacity="0.95" Color="#333333" />
|
|
||||||
</Setter.Value>
|
|
||||||
</Setter>
|
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<Style
|
<Style
|
||||||
x:Key="WindowStyle"
|
x:Key="WindowStyle"
|
||||||
BasedOn="{StaticResource BaseWindowStyle}"
|
BasedOn="{StaticResource BaseWindowStyle}"
|
||||||
TargetType="{x:Type Window}">
|
TargetType="{x:Type Window}" />
|
||||||
<Setter Property="Background">
|
|
||||||
<Setter.Value>
|
|
||||||
<SolidColorBrush Opacity="0.5" Color="White" />
|
|
||||||
</Setter.Value>
|
|
||||||
</Setter>
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style
|
<Style
|
||||||
x:Key="PendingLineStyle"
|
x:Key="PendingLineStyle"
|
||||||
BasedOn="{StaticResource BasePendingLineStyle}"
|
BasedOn="{StaticResource BasePendingLineStyle}"
|
||||||
|
|
@ -63,27 +62,36 @@
|
||||||
x:Key="ItemTitleStyle"
|
x:Key="ItemTitleStyle"
|
||||||
BasedOn="{StaticResource BaseItemTitleStyle}"
|
BasedOn="{StaticResource BaseItemTitleStyle}"
|
||||||
TargetType="{x:Type TextBlock}">
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#ebebeb" />
|
<Setter Property="Foreground" Value="#dfe1e5" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="ItemSubTitleStyle"
|
x:Key="ItemSubTitleStyle"
|
||||||
BasedOn="{StaticResource BaseItemSubTitleStyle}"
|
BasedOn="{StaticResource BaseItemSubTitleStyle}"
|
||||||
TargetType="{x:Type TextBlock}">
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#787878" />
|
<Setter Property="Foreground" Value="#6f737a" />
|
||||||
</Style>
|
</Style>
|
||||||
|
<Style
|
||||||
|
x:Key="SeparatorStyle"
|
||||||
|
BasedOn="{StaticResource BaseSeparatorStyle}"
|
||||||
|
TargetType="{x:Type Rectangle}">
|
||||||
|
<Setter Property="Fill" Value="#393b40" />
|
||||||
|
<Setter Property="Height" Value="1" />
|
||||||
|
<Setter Property="Margin" Value="0 0 0 8" />
|
||||||
|
</Style>
|
||||||
|
<Style x:Key="HighlightStyle" />
|
||||||
<Style
|
<Style
|
||||||
x:Key="ItemTitleSelectedStyle"
|
x:Key="ItemTitleSelectedStyle"
|
||||||
BasedOn="{StaticResource BaseItemTitleSelectedStyle}"
|
BasedOn="{StaticResource BaseItemTitleSelectedStyle}"
|
||||||
TargetType="{x:Type TextBlock}">
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#ffffff" />
|
<Setter Property="Foreground" Value="#dfe1e5" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="ItemSubTitleSelectedStyle"
|
x:Key="ItemSubTitleSelectedStyle"
|
||||||
BasedOn="{StaticResource BaseItemSubTitleSelectedStyle}"
|
BasedOn="{StaticResource BaseItemSubTitleSelectedStyle}"
|
||||||
TargetType="{x:Type TextBlock}">
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#949494" />
|
<Setter Property="Foreground" Value="#dfe1e5" />
|
||||||
</Style>
|
</Style>
|
||||||
<SolidColorBrush x:Key="ItemSelectedBackgroundColor">#545454</SolidColorBrush>
|
<SolidColorBrush x:Key="ItemSelectedBackgroundColor">#2e436e</SolidColorBrush>
|
||||||
|
|
||||||
<!-- button style in the middle of the scrollbar -->
|
<!-- button style in the middle of the scrollbar -->
|
||||||
<Style
|
<Style
|
||||||
|
|
@ -94,7 +102,7 @@
|
||||||
<Setter.Value>
|
<Setter.Value>
|
||||||
<ControlTemplate TargetType="{x:Type Thumb}">
|
<ControlTemplate TargetType="{x:Type Thumb}">
|
||||||
<Border
|
<Border
|
||||||
Background="#525252"
|
Background="#393b40"
|
||||||
BorderBrush="Transparent"
|
BorderBrush="Transparent"
|
||||||
BorderThickness="0"
|
BorderThickness="0"
|
||||||
CornerRadius="2"
|
CornerRadius="2"
|
||||||
|
|
@ -106,69 +114,83 @@
|
||||||
<Style
|
<Style
|
||||||
x:Key="ScrollBarStyle"
|
x:Key="ScrollBarStyle"
|
||||||
BasedOn="{StaticResource BaseScrollBarStyle}"
|
BasedOn="{StaticResource BaseScrollBarStyle}"
|
||||||
TargetType="{x:Type ScrollBar}">
|
TargetType="{x:Type ScrollBar}" />
|
||||||
<Setter Property="Background" Value="#a0a0a0" />
|
|
||||||
</Style>
|
|
||||||
<Style
|
<Style
|
||||||
x:Key="SearchIconStyle"
|
x:Key="SearchIconStyle"
|
||||||
BasedOn="{StaticResource BaseSearchIconStyle}"
|
BasedOn="{StaticResource BaseSearchIconStyle}"
|
||||||
TargetType="{x:Type Path}">
|
TargetType="{x:Type Path}">
|
||||||
<Setter Property="Fill" Value="#FFFFFF" />
|
<Setter Property="Fill" Value="#6f737a" />
|
||||||
|
<Setter Property="Width" Value="24" />
|
||||||
|
<Setter Property="Height" Value="24" />
|
||||||
|
</Style>
|
||||||
|
<Style x:Key="SearchIconPosition" TargetType="{x:Type Canvas}">
|
||||||
|
<Setter Property="Background" Value="#2b2d30" />
|
||||||
<Setter Property="Width" Value="32" />
|
<Setter Property="Width" Value="32" />
|
||||||
<Setter Property="Height" Value="32" />
|
<Setter Property="Height" Value="32" />
|
||||||
<Setter Property="Opacity" Value="0.2" />
|
<Setter Property="Margin" Value="0 8 8 0" />
|
||||||
</Style>
|
<Setter Property="HorizontalAlignment" Value="Right" />
|
||||||
<Style
|
|
||||||
x:Key="SeparatorStyle"
|
|
||||||
BasedOn="{StaticResource BaseSeparatorStyle}"
|
|
||||||
TargetType="{x:Type Rectangle}">
|
|
||||||
<Setter Property="Fill" Value="#474747" />
|
|
||||||
<Setter Property="Height" Value="1" />
|
|
||||||
<Setter Property="Margin" Value="0,0,0,8" />
|
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<Style x:Key="ItemHotkeyStyle" TargetType="{x:Type TextBlock}">
|
<Style x:Key="ItemHotkeyStyle" TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="FontSize" Value="14" />
|
<Setter Property="FontSize" Value="12" />
|
||||||
<Setter Property="Foreground" Value="#787878" />
|
<Setter Property="Foreground" Value="#6f737a" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="ItemHotkeySelectedStyle" TargetType="{x:Type TextBlock}">
|
<Style x:Key="ItemHotkeySelectedStyle" TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="FontSize" Value="14" />
|
<Setter Property="FontSize" Value="12" />
|
||||||
<Setter Property="Foreground" Value="#787878" />
|
<Setter Property="Foreground" Value="#6f737a" />
|
||||||
|
</Style>
|
||||||
|
<Style x:Key="ItemGlyphSelectedStyle" TargetType="{x:Type TextBlock}">
|
||||||
|
<Setter Property="Foreground" Value="#dfe1e5" />
|
||||||
|
<Setter Property="VerticalAlignment" Value="Center" />
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Center" />
|
||||||
|
<Setter Property="Width" Value="25" />
|
||||||
|
<Setter Property="Height" Value="25" />
|
||||||
|
<Setter Property="FontSize" Value="25" />
|
||||||
|
</Style>
|
||||||
|
<CornerRadius x:Key="ItemRadius">8</CornerRadius>
|
||||||
|
<Thickness x:Key="ItemMargin">10 0 10 0</Thickness>
|
||||||
|
<Thickness x:Key="ResultMargin">0 0 0 10</Thickness>
|
||||||
|
<Style
|
||||||
|
x:Key="ClockPanel"
|
||||||
|
BasedOn="{StaticResource BaseClockPanel}"
|
||||||
|
TargetType="{x:Type StackPanel}">
|
||||||
|
<Setter Property="Margin" Value="0 0 54 0" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="ClockBox"
|
x:Key="ClockBox"
|
||||||
BasedOn="{StaticResource BaseClockBox}"
|
BasedOn="{StaticResource BaseClockBox}"
|
||||||
TargetType="{x:Type TextBlock}">
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#787878" />
|
<Setter Property="Foreground" Value="#6f737a" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="DateBox"
|
x:Key="DateBox"
|
||||||
BasedOn="{StaticResource BaseDateBox}"
|
BasedOn="{StaticResource BaseDateBox}"
|
||||||
TargetType="{x:Type TextBlock}">
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#787878" />
|
<Setter Property="Foreground" Value="#6f737a" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="PreviewBorderStyle"
|
x:Key="PreviewBorderStyle"
|
||||||
BasedOn="{StaticResource BasePreviewBorderStyle}"
|
BasedOn="{StaticResource BasePreviewBorderStyle}"
|
||||||
TargetType="{x:Type Border}">
|
TargetType="{x:Type Border}">
|
||||||
<Setter Property="BorderBrush" Value="#474747" />
|
<Setter Property="Margin" Value="0 0 10 8" />
|
||||||
|
<Setter Property="BorderBrush" Value="#393b40" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<Style
|
<Style
|
||||||
x:Key="PreviewItemTitleStyle"
|
x:Key="PreviewItemTitleStyle"
|
||||||
BasedOn="{StaticResource BasePreviewItemTitleStyle}"
|
BasedOn="{StaticResource BasePreviewItemTitleStyle}"
|
||||||
TargetType="{x:Type TextBlock}">
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#ebebeb" />
|
<Setter Property="Foreground" Value="#6f737a" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="PreviewItemSubTitleStyle"
|
x:Key="PreviewItemSubTitleStyle"
|
||||||
BasedOn="{StaticResource BasePreviewItemSubTitleStyle}"
|
BasedOn="{StaticResource BasePreviewItemSubTitleStyle}"
|
||||||
TargetType="{x:Type TextBlock}">
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#787878" />
|
<Setter Property="Foreground" Value="#6f737a" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="PreviewGlyph"
|
x:Key="PreviewGlyph"
|
||||||
BasedOn="{StaticResource BasePreviewGlyph}"
|
BasedOn="{StaticResource BasePreviewGlyph}"
|
||||||
TargetType="{x:Type TextBlock}">
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#ebebeb" />
|
<Setter Property="Foreground" Value="#dfe1e5" />
|
||||||
</Style>
|
</Style>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,9 @@
|
||||||
<Style
|
<Style
|
||||||
x:Key="WindowBorderStyle"
|
x:Key="WindowBorderStyle"
|
||||||
BasedOn="{StaticResource BaseWindowBorderStyle}"
|
BasedOn="{StaticResource BaseWindowBorderStyle}"
|
||||||
TargetType="{x:Type Border}" />
|
TargetType="{x:Type Border}">
|
||||||
|
<Setter Property="Background" Value="#2F2F2F" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
<Style
|
<Style
|
||||||
x:Key="WindowStyle"
|
x:Key="WindowStyle"
|
||||||
|
|
@ -89,7 +91,7 @@
|
||||||
TargetType="{x:Type Rectangle}">
|
TargetType="{x:Type Rectangle}">
|
||||||
<Setter Property="Fill" Value="#3b3b3b" />
|
<Setter Property="Fill" Value="#3b3b3b" />
|
||||||
<Setter Property="Height" Value="1" />
|
<Setter Property="Height" Value="1" />
|
||||||
<Setter Property="Margin" Value="8,0,8,8" />
|
<Setter Property="Margin" Value="8 0 8 8" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="PreviewBorderStyle"
|
x:Key="PreviewBorderStyle"
|
||||||
|
|
|
||||||
|
|
@ -21,26 +21,25 @@
|
||||||
<Setter Property="SelectionBrush" Value="#0a68d8" />
|
<Setter Property="SelectionBrush" Value="#0a68d8" />
|
||||||
<Setter Property="Height" Value="42" />
|
<Setter Property="Height" Value="42" />
|
||||||
<Setter Property="FontSize" Value="24" />
|
<Setter Property="FontSize" Value="24" />
|
||||||
<Setter Property="Padding" Value="0,0,66,0" />
|
<Setter Property="Padding" Value="0 0 66 0" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="QuerySuggestionBoxStyle"
|
x:Key="QuerySuggestionBoxStyle"
|
||||||
BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}"
|
BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}"
|
||||||
TargetType="{x:Type TextBox}">
|
TargetType="{x:Type TextBox}">
|
||||||
<Setter Property="Foreground" Value="#72767d" />
|
<Setter Property="Foreground" Value="#72767d" />
|
||||||
<Setter Property="Background" Value="#36393f" />
|
|
||||||
<Setter Property="Height" Value="42" />
|
<Setter Property="Height" Value="42" />
|
||||||
<Setter Property="FontSize" Value="24" />
|
<Setter Property="FontSize" Value="24" />
|
||||||
<Setter Property="Padding" Value="0,0,66,0" />
|
<Setter Property="Padding" Value="0 0 66 0" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="WindowBorderStyle"
|
x:Key="WindowBorderStyle"
|
||||||
BasedOn="{StaticResource BaseWindowBorderStyle}"
|
BasedOn="{StaticResource BaseWindowBorderStyle}"
|
||||||
TargetType="{x:Type Border}">
|
TargetType="{x:Type Border}">
|
||||||
<Setter Property="BorderThickness" Value="2" />
|
<Setter Property="BorderThickness" Value="2" />
|
||||||
<Setter Property="BorderBrush" Value="#2f3136" />
|
<Setter Property="BorderBrush" Value="#3a3a41" />
|
||||||
<Setter Property="CornerRadius" Value="4" />
|
<Setter Property="CornerRadius" Value="4" />
|
||||||
<Setter Property="Background" Value="#36393f" />
|
<Setter Property="Background" Value="#2e2e34" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="WindowStyle"
|
x:Key="WindowStyle"
|
||||||
|
|
@ -91,23 +90,25 @@
|
||||||
<Setter Property="Cursor" Value="Arrow" />
|
<Setter Property="Cursor" Value="Arrow" />
|
||||||
<Setter Property="Foreground" Value="#72767d" />
|
<Setter Property="Foreground" Value="#72767d" />
|
||||||
</Style>
|
</Style>
|
||||||
<SolidColorBrush x:Key="ItemSelectedBackgroundColor">#49443c</SolidColorBrush>
|
<SolidColorBrush x:Key="ItemSelectedBackgroundColor">#36363d</SolidColorBrush>
|
||||||
<Style
|
<Style
|
||||||
x:Key="ItemImageSelectedStyle"
|
x:Key="ItemImageSelectedStyle"
|
||||||
BasedOn="{StaticResource BaseItemImageSelectedStyle}"
|
BasedOn="{StaticResource BaseItemImageSelectedStyle}"
|
||||||
TargetType="{x:Type Image}">
|
TargetType="{x:Type Image}">
|
||||||
<Setter Property="Cursor" Value="Arrow" />
|
<Setter Property="Cursor" Value="Arrow" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="HighlightStyle">
|
<Style x:Key="HighlightStyle" />
|
||||||
<Setter Property="Inline.FontWeight" Value="Bold" />
|
<Style
|
||||||
</Style>
|
x:Key="ItemHotkeyStyle"
|
||||||
<Style x:Key="ItemHotkeyStyle" TargetType="{x:Type TextBlock}">
|
BasedOn="{StaticResource BaseItemHotkeyStyle}"
|
||||||
<Setter Property="FontSize" Value="15" />
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#72767d" />
|
<Setter Property="Foreground" Value="#72767d" />
|
||||||
<Setter Property="Opacity" Value="0.8" />
|
<Setter Property="Opacity" Value="0.8" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="ItemHotkeySelectedStyle" TargetType="{x:Type TextBlock}">
|
<Style
|
||||||
<Setter Property="FontSize" Value="15" />
|
x:Key="ItemHotkeySelectedStyle"
|
||||||
|
BasedOn="{StaticResource BaseItemHotkeySelectedStyle}"
|
||||||
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#72767d" />
|
<Setter Property="Foreground" Value="#72767d" />
|
||||||
<Setter Property="Opacity" Value="0.8" />
|
<Setter Property="Opacity" Value="0.8" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
@ -124,7 +125,7 @@
|
||||||
<Setter.Value>
|
<Setter.Value>
|
||||||
<ControlTemplate TargetType="{x:Type Thumb}">
|
<ControlTemplate TargetType="{x:Type Thumb}">
|
||||||
<Border
|
<Border
|
||||||
Background="#202225"
|
Background="#787881"
|
||||||
BorderBrush="Transparent"
|
BorderBrush="Transparent"
|
||||||
BorderThickness="0"
|
BorderThickness="0"
|
||||||
CornerRadius="2"
|
CornerRadius="2"
|
||||||
|
|
@ -141,9 +142,9 @@
|
||||||
x:Key="SeparatorStyle"
|
x:Key="SeparatorStyle"
|
||||||
BasedOn="{StaticResource BaseSeparatorStyle}"
|
BasedOn="{StaticResource BaseSeparatorStyle}"
|
||||||
TargetType="{x:Type Rectangle}">
|
TargetType="{x:Type Rectangle}">
|
||||||
<Setter Property="Fill" Value="#42454a" />
|
<Setter Property="Fill" Value="#3a3a41" />
|
||||||
<Setter Property="Height" Value="1" />
|
<Setter Property="Height" Value="1" />
|
||||||
<Setter Property="Margin" Value="12,0,12,6" />
|
<Setter Property="Margin" Value="0 0 0 0" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="SearchIconStyle"
|
x:Key="SearchIconStyle"
|
||||||
|
|
@ -170,6 +171,7 @@
|
||||||
BasedOn="{StaticResource BasePreviewBorderStyle}"
|
BasedOn="{StaticResource BasePreviewBorderStyle}"
|
||||||
TargetType="{x:Type Border}">
|
TargetType="{x:Type Border}">
|
||||||
<Setter Property="BorderBrush" Value="#42454a" />
|
<Setter Property="BorderBrush" Value="#42454a" />
|
||||||
|
<Setter Property="Margin" Value="0 0 0 0" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="PreviewItemTitleStyle"
|
x:Key="PreviewItemTitleStyle"
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
<Setter Property="Foreground" Value="#f8f8f2" />
|
<Setter Property="Foreground" Value="#f8f8f2" />
|
||||||
<Setter Property="CaretBrush" Value="#ffb86c" />
|
<Setter Property="CaretBrush" Value="#ffb86c" />
|
||||||
<Setter Property="FontSize" Value="26" />
|
<Setter Property="FontSize" Value="26" />
|
||||||
<Setter Property="Padding" Value="0,4,66,0" />
|
<Setter Property="Padding" Value="0,0,66,0" />
|
||||||
<Setter Property="Height" Value="42" />
|
<Setter Property="Height" Value="42" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
TargetType="{x:Type TextBox}">
|
TargetType="{x:Type TextBox}">
|
||||||
<Setter Property="Foreground" Value="#6272a4" />
|
<Setter Property="Foreground" Value="#6272a4" />
|
||||||
<Setter Property="FontSize" Value="26" />
|
<Setter Property="FontSize" Value="26" />
|
||||||
<Setter Property="Padding" Value="0,4,66,0" />
|
<Setter Property="Padding" Value="0,0,66,0" />
|
||||||
<Setter Property="Height" Value="42" />
|
<Setter Property="Height" Value="42" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
TargetType="{x:Type TextBox}">
|
TargetType="{x:Type TextBox}">
|
||||||
<Setter Property="Foreground" Value="#b88f3a" />
|
<Setter Property="Foreground" Value="#b88f3a" />
|
||||||
<Setter Property="CaretBrush" Value="#b88f3a" />
|
<Setter Property="CaretBrush" Value="#b88f3a" />
|
||||||
<Setter Property="Padding" Value="0,0,66,0" />
|
<Setter Property="Padding" Value="0 0 66 0" />
|
||||||
<Setter Property="Height" Value="42" />
|
<Setter Property="Height" Value="42" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
|
|
@ -24,9 +24,8 @@
|
||||||
x:Key="QuerySuggestionBoxStyle"
|
x:Key="QuerySuggestionBoxStyle"
|
||||||
BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}"
|
BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}"
|
||||||
TargetType="{x:Type TextBox}">
|
TargetType="{x:Type TextBox}">
|
||||||
<Setter Property="Background" Value="#161614" />
|
|
||||||
<Setter Property="Foreground" Value="#a09b8c" />
|
<Setter Property="Foreground" Value="#a09b8c" />
|
||||||
<Setter Property="Padding" Value="0,0,66,0" />
|
<Setter Property="Padding" Value="0 0 66 0" />
|
||||||
<Setter Property="Height" Value="42" />
|
<Setter Property="Height" Value="42" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
|
|
@ -57,7 +56,7 @@
|
||||||
TargetType="{x:Type Rectangle}">
|
TargetType="{x:Type Rectangle}">
|
||||||
<Setter Property="Fill" Value="#785a28" />
|
<Setter Property="Fill" Value="#785a28" />
|
||||||
<Setter Property="Height" Value="1" />
|
<Setter Property="Height" Value="1" />
|
||||||
<Setter Property="Margin" Value="0,0,0,0" />
|
<Setter Property="Margin" Value="0 0 0 0" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="ItemTitleStyle"
|
x:Key="ItemTitleStyle"
|
||||||
|
|
@ -111,12 +110,16 @@
|
||||||
<Style x:Key="HighlightStyle">
|
<Style x:Key="HighlightStyle">
|
||||||
<Setter Property="Inline.FontWeight" Value="Bold" />
|
<Setter Property="Inline.FontWeight" Value="Bold" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="ItemHotkeyStyle" TargetType="{x:Type TextBlock}">
|
<Style
|
||||||
<Setter Property="FontSize" Value="15" />
|
x:Key="ItemHotkeyStyle"
|
||||||
|
BasedOn="{StaticResource BaseItemHotkeyStyle}"
|
||||||
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#5b5a56" />
|
<Setter Property="Foreground" Value="#5b5a56" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="ItemHotkeySelectedStyle" TargetType="{x:Type TextBlock}">
|
<Style
|
||||||
<Setter Property="FontSize" Value="15" />
|
x:Key="ItemHotkeySelectedStyle"
|
||||||
|
BasedOn="{StaticResource BaseItemHotkeySelectedStyle}"
|
||||||
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#5b5a56" />
|
<Setter Property="Foreground" Value="#5b5a56" />
|
||||||
</Style>
|
</Style>
|
||||||
<Geometry x:Key="SearchIconImg">F1 M12000,12000z M0,0z M10354,10962C10326,10951 10279,10927 10249,10907 10216,10886 9476,10153 8370,9046 7366,8042 6541,7220 6536,7220 6532,7220 6498,7242 6461,7268 6213,7447 5883,7619 5592,7721 5194,7860 4802,7919 4360,7906 3612,7886 2953,7647 2340,7174 2131,7013 1832,6699 1664,6465 1394,6088 1188,5618 1097,5170 1044,4909 1030,4764 1030,4470 1030,4130 1056,3914 1135,3609 1263,3110 1511,2633 1850,2235 1936,2134 2162,1911 2260,1829 2781,1395 3422,1120 4090,1045 4271,1025 4667,1025 4848,1045 5505,1120 6100,1368 6630,1789 6774,1903 7081,2215 7186,2355 7362,2588 7467,2759 7579,2990 7802,3455 7911,3937 7911,4460 7911,4854 7861,5165 7737,5542 7684,5702 7675,5724 7602,5885 7517,6071 7390,6292 7270,6460 7242,6499 7220,6533 7220,6538 7220,6542 8046,7371 9055,8380 10441,9766 10898,10229 10924,10274 10945,10308 10966,10364 10976,10408 10990,10472 10991,10493 10980,10554 10952,10717 10840,10865 10690,10937 10621,10971 10607,10974 10510,10977 10425,10980 10395,10977 10354,10962z M4685,7050C5214,7001 5694,6809 6100,6484 6209,6396 6396,6209 6484,6100 7151,5267 7246,4110 6721,3190 6369,2571 5798,2137 5100,1956 4706,1855 4222,1855 3830,1957 3448,2056 3140,2210 2838,2453 2337,2855 2010,3427 1908,4080 1877,4274 1877,4656 1908,4850 1948,5105 2028,5370 2133,5590 2459,6272 3077,6782 3810,6973 3967,7014 4085,7034 4290,7053 4371,7061 4583,7059 4685,7050z</Geometry>
|
<Geometry x:Key="SearchIconImg">F1 M12000,12000z M0,0z M10354,10962C10326,10951 10279,10927 10249,10907 10216,10886 9476,10153 8370,9046 7366,8042 6541,7220 6536,7220 6532,7220 6498,7242 6461,7268 6213,7447 5883,7619 5592,7721 5194,7860 4802,7919 4360,7906 3612,7886 2953,7647 2340,7174 2131,7013 1832,6699 1664,6465 1394,6088 1188,5618 1097,5170 1044,4909 1030,4764 1030,4470 1030,4130 1056,3914 1135,3609 1263,3110 1511,2633 1850,2235 1936,2134 2162,1911 2260,1829 2781,1395 3422,1120 4090,1045 4271,1025 4667,1025 4848,1045 5505,1120 6100,1368 6630,1789 6774,1903 7081,2215 7186,2355 7362,2588 7467,2759 7579,2990 7802,3455 7911,3937 7911,4460 7911,4854 7861,5165 7737,5542 7684,5702 7675,5724 7602,5885 7517,6071 7390,6292 7270,6460 7242,6499 7220,6533 7220,6538 7220,6542 8046,7371 9055,8380 10441,9766 10898,10229 10924,10274 10945,10308 10966,10364 10976,10408 10990,10472 10991,10493 10980,10554 10952,10717 10840,10865 10690,10937 10621,10971 10607,10974 10510,10977 10425,10980 10395,10977 10354,10962z M4685,7050C5214,7001 5694,6809 6100,6484 6209,6396 6396,6209 6484,6100 7151,5267 7246,4110 6721,3190 6369,2571 5798,2137 5100,1956 4706,1855 4222,1855 3830,1957 3448,2056 3140,2210 2838,2453 2337,2855 2010,3427 1908,4080 1877,4274 1877,4656 1908,4850 1948,5105 2028,5370 2133,5590 2459,6272 3077,6782 3810,6973 3967,7014 4085,7034 4290,7053 4371,7061 4583,7059 4685,7050z</Geometry>
|
||||||
|
|
@ -141,7 +144,7 @@
|
||||||
x:Key="PreviewBorderStyle"
|
x:Key="PreviewBorderStyle"
|
||||||
BasedOn="{StaticResource BasePreviewBorderStyle}"
|
BasedOn="{StaticResource BasePreviewBorderStyle}"
|
||||||
TargetType="{x:Type Border}">
|
TargetType="{x:Type Border}">
|
||||||
<Setter Property="Margin" Value="0,0,10,0" />
|
<Setter Property="Margin" Value="0 0 10 0" />
|
||||||
<Setter Property="BorderBrush" Value="#785a28" />
|
<Setter Property="BorderBrush" Value="#785a28" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
x:Key="QueryBoxStyle"
|
x:Key="QueryBoxStyle"
|
||||||
BasedOn="{StaticResource BaseQueryBoxStyle}"
|
BasedOn="{StaticResource BaseQueryBoxStyle}"
|
||||||
TargetType="{x:Type TextBox}">
|
TargetType="{x:Type TextBox}">
|
||||||
<Setter Property="Padding" Value="0,4,50,0" />
|
<Setter Property="Padding" Value="0 0 50 0" />
|
||||||
<Setter Property="Foreground" Value="#fbfdff" />
|
<Setter Property="Foreground" Value="#fbfdff" />
|
||||||
<Setter Property="FontSize" Value="18" />
|
<Setter Property="FontSize" Value="18" />
|
||||||
<Setter Property="Height" Value="38" />
|
<Setter Property="Height" Value="38" />
|
||||||
|
|
@ -26,7 +26,7 @@
|
||||||
x:Key="QuerySuggestionBoxStyle"
|
x:Key="QuerySuggestionBoxStyle"
|
||||||
BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}"
|
BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}"
|
||||||
TargetType="{x:Type TextBox}">
|
TargetType="{x:Type TextBox}">
|
||||||
<Setter Property="Padding" Value="0,4,50,0" />
|
<Setter Property="Padding" Value="0 0 50 0" />
|
||||||
<Setter Property="Background" Value="Transparent" />
|
<Setter Property="Background" Value="Transparent" />
|
||||||
<Setter Property="Height" Value="38" />
|
<Setter Property="Height" Value="38" />
|
||||||
<Setter Property="FontSize" Value="18" />
|
<Setter Property="FontSize" Value="18" />
|
||||||
|
|
@ -38,7 +38,7 @@
|
||||||
BasedOn="{StaticResource BaseWindowBorderStyle}"
|
BasedOn="{StaticResource BaseWindowBorderStyle}"
|
||||||
TargetType="{x:Type Border}">
|
TargetType="{x:Type Border}">
|
||||||
<Setter Property="BorderThickness" Value="1" />
|
<Setter Property="BorderThickness" Value="1" />
|
||||||
<Setter Property="BorderBrush" Value="#111828" />
|
<Setter Property="BorderBrush" Value="#202938" />
|
||||||
<Setter Property="Background" Value="#111828" />
|
<Setter Property="Background" Value="#111828" />
|
||||||
<Setter Property="CornerRadius" Value="8" />
|
<Setter Property="CornerRadius" Value="8" />
|
||||||
<Setter Property="UseLayoutRounding" Value="True" />
|
<Setter Property="UseLayoutRounding" Value="True" />
|
||||||
|
|
@ -71,7 +71,7 @@
|
||||||
TargetType="{x:Type Rectangle}">
|
TargetType="{x:Type Rectangle}">
|
||||||
<Setter Property="Fill" Value="#202938" />
|
<Setter Property="Fill" Value="#202938" />
|
||||||
<Setter Property="Height" Value="1" />
|
<Setter Property="Height" Value="1" />
|
||||||
<Setter Property="Margin" Value="0,0,0,8" />
|
<Setter Property="Margin" Value="0 0 0 8" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="HighlightStyle" />
|
<Style x:Key="HighlightStyle" />
|
||||||
<Style
|
<Style
|
||||||
|
|
@ -122,7 +122,7 @@
|
||||||
<Setter Property="Background" Value="#111828" />
|
<Setter Property="Background" Value="#111828" />
|
||||||
<Setter Property="Width" Value="32" />
|
<Setter Property="Width" Value="32" />
|
||||||
<Setter Property="Height" Value="32" />
|
<Setter Property="Height" Value="32" />
|
||||||
<Setter Property="Margin" Value="0,8,8,0" />
|
<Setter Property="Margin" Value="0 8 8 0" />
|
||||||
<Setter Property="HorizontalAlignment" Value="Right" />
|
<Setter Property="HorizontalAlignment" Value="Right" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
|
|
@ -149,7 +149,7 @@
|
||||||
x:Key="ClockPanel"
|
x:Key="ClockPanel"
|
||||||
BasedOn="{StaticResource ClockPanel}"
|
BasedOn="{StaticResource ClockPanel}"
|
||||||
TargetType="{x:Type StackPanel}">
|
TargetType="{x:Type StackPanel}">
|
||||||
<Setter Property="Margin" Value="0,0,54,0" />
|
<Setter Property="Margin" Value="0 0 54 0" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="ClockBox"
|
x:Key="ClockBox"
|
||||||
|
|
@ -167,7 +167,7 @@
|
||||||
x:Key="PreviewBorderStyle"
|
x:Key="PreviewBorderStyle"
|
||||||
BasedOn="{StaticResource BasePreviewBorderStyle}"
|
BasedOn="{StaticResource BasePreviewBorderStyle}"
|
||||||
TargetType="{x:Type Border}">
|
TargetType="{x:Type Border}">
|
||||||
<Setter Property="Margin" Value="0,0,10,8" />
|
<Setter Property="Margin" Value="0 0 10 8" />
|
||||||
<Setter Property="BorderBrush" Value="#202938" />
|
<Setter Property="BorderBrush" Value="#202938" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@
|
||||||
TargetType="{x:Type Rectangle}">
|
TargetType="{x:Type Rectangle}">
|
||||||
<Setter Property="Fill" Value="#4c566a" />
|
<Setter Property="Fill" Value="#4c566a" />
|
||||||
<Setter Property="Height" Value="1" />
|
<Setter Property="Height" Value="1" />
|
||||||
<Setter Property="Margin" Value="12,0,12,8" />
|
<Setter Property="Margin" Value="12 0 12 8" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="WindowStyle"
|
x:Key="WindowStyle"
|
||||||
|
|
@ -116,12 +116,16 @@
|
||||||
<Setter Property="Width" Value="32" />
|
<Setter Property="Width" Value="32" />
|
||||||
<Setter Property="Height" Value="32" />
|
<Setter Property="Height" Value="32" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="ItemHotkeyStyle" TargetType="{x:Type TextBlock}">
|
<Style
|
||||||
<Setter Property="FontSize" Value="15" />
|
x:Key="ItemHotkeyStyle"
|
||||||
|
BasedOn="{StaticResource BaseItemHotkeyStyle}"
|
||||||
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#8391AB" />
|
<Setter Property="Foreground" Value="#8391AB" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="ItemHotkeySelectedStyle" TargetType="{x:Type TextBlock}">
|
<Style
|
||||||
<Setter Property="FontSize" Value="15" />
|
x:Key="ItemHotkeySelectedStyle"
|
||||||
|
BasedOn="{StaticResource BaseItemHotkeySelectedStyle}"
|
||||||
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#8391AB" />
|
<Setter Property="Foreground" Value="#8391AB" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
|
|
|
||||||
|
|
@ -2,46 +2,44 @@
|
||||||
<ResourceDictionary.MergedDictionaries>
|
<ResourceDictionary.MergedDictionaries>
|
||||||
<ResourceDictionary Source="pack://application:,,,/Themes/Base.xaml" />
|
<ResourceDictionary Source="pack://application:,,,/Themes/Base.xaml" />
|
||||||
</ResourceDictionary.MergedDictionaries>
|
</ResourceDictionary.MergedDictionaries>
|
||||||
<Thickness x:Key="ResultMargin">0 0 0 0</Thickness>
|
<Thickness x:Key="ResultMargin">0 0 0 4</Thickness>
|
||||||
<Style
|
<Style
|
||||||
x:Key="ItemGlyph"
|
x:Key="ItemGlyph"
|
||||||
BasedOn="{StaticResource BaseGlyphStyle}"
|
BasedOn="{StaticResource BaseGlyphStyle}"
|
||||||
TargetType="{x:Type TextBlock}">
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#0e172c" />
|
<Setter Property="Foreground" Value="#ffffff" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="QueryBoxStyle"
|
x:Key="QueryBoxStyle"
|
||||||
BasedOn="{StaticResource BaseQueryBoxStyle}"
|
BasedOn="{StaticResource BaseQueryBoxStyle}"
|
||||||
TargetType="{x:Type TextBox}">
|
TargetType="{x:Type TextBox}">
|
||||||
<Setter Property="Foreground" Value="#0E172C" />
|
<Setter Property="Foreground" Value="#cc1081" />
|
||||||
<Setter Property="CaretBrush" Value="#0E172C" />
|
<Setter Property="CaretBrush" Value="#cc1081" />
|
||||||
<Setter Property="SelectionBrush" Value="#a786df" />
|
<Setter Property="SelectionBrush" Value="#e564b1" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<Style
|
<Style
|
||||||
x:Key="QuerySuggestionBoxStyle"
|
x:Key="QuerySuggestionBoxStyle"
|
||||||
BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}"
|
BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}"
|
||||||
TargetType="{x:Type TextBox}">
|
TargetType="{x:Type TextBox}">
|
||||||
<Setter Property="Background" Value="Transparent" />
|
<Setter Property="Foreground" Value="#71114b" />
|
||||||
<Setter Property="Foreground" Value="#E9819F" />
|
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<Style
|
<Style
|
||||||
x:Key="WindowBorderStyle"
|
x:Key="WindowBorderStyle"
|
||||||
BasedOn="{StaticResource BaseWindowBorderStyle}"
|
BasedOn="{StaticResource BaseWindowBorderStyle}"
|
||||||
TargetType="{x:Type Border}">
|
TargetType="{x:Type Border}">
|
||||||
<Setter Property="CornerRadius" Value="8" />
|
<Setter Property="Background" Value="#1f1d1f" />
|
||||||
<Setter Property="Background" Value="#fec7d7" />
|
<Setter Property="BorderThickness" Value="2" />
|
||||||
<Setter Property="BorderThickness" Value="3" />
|
<Setter Property="BorderBrush" Value="#000000" />
|
||||||
<Setter Property="BorderBrush" Value="#0e172c" />
|
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="SeparatorStyle"
|
x:Key="SeparatorStyle"
|
||||||
BasedOn="{StaticResource BaseSeparatorStyle}"
|
BasedOn="{StaticResource BaseSeparatorStyle}"
|
||||||
TargetType="{x:Type Rectangle}">
|
TargetType="{x:Type Rectangle}">
|
||||||
<Setter Property="Fill" Value="#0e172c" />
|
<Setter Property="Fill" Value="#000000" />
|
||||||
<Setter Property="Height" Value="3" />
|
<Setter Property="Height" Value="1" />
|
||||||
<Setter Property="Margin" Value="0 0 0 0" />
|
<Setter Property="Margin" Value="12 0 12 4" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="WindowStyle"
|
x:Key="WindowStyle"
|
||||||
|
|
@ -51,33 +49,34 @@
|
||||||
x:Key="PendingLineStyle"
|
x:Key="PendingLineStyle"
|
||||||
BasedOn="{StaticResource BasePendingLineStyle}"
|
BasedOn="{StaticResource BasePendingLineStyle}"
|
||||||
TargetType="{x:Type Line}">
|
TargetType="{x:Type Line}">
|
||||||
<Setter Property="Stroke" Value="#a786df" />
|
<Setter Property="Stroke" Value="#cc1081" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="ItemTitleStyle"
|
x:Key="ItemTitleStyle"
|
||||||
BasedOn="{StaticResource BaseItemTitleStyle}"
|
BasedOn="{StaticResource BaseItemTitleStyle}"
|
||||||
TargetType="{x:Type TextBlock}">
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#0e172c" />
|
<Setter Property="Foreground" Value="#f5f5f5" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="ItemSubTitleStyle"
|
x:Key="ItemSubTitleStyle"
|
||||||
BasedOn="{StaticResource BaseItemSubTitleStyle}"
|
BasedOn="{StaticResource BaseItemSubTitleStyle}"
|
||||||
TargetType="{x:Type TextBlock}">
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#0e172c" />
|
<Setter Property="Foreground" Value="#c2c2c2" />
|
||||||
|
<Setter Property="Opacity" Value="0.5" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="ItemTitleSelectedStyle"
|
x:Key="ItemTitleSelectedStyle"
|
||||||
BasedOn="{StaticResource BaseItemTitleSelectedStyle}"
|
BasedOn="{StaticResource BaseItemTitleSelectedStyle}"
|
||||||
TargetType="{x:Type TextBlock}">
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#fffffe" />
|
<Setter Property="Foreground" Value="#f5f5f5" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="ItemSubTitleSelectedStyle"
|
x:Key="ItemSubTitleSelectedStyle"
|
||||||
BasedOn="{StaticResource BaseItemSubTitleSelectedStyle}"
|
BasedOn="{StaticResource BaseItemSubTitleSelectedStyle}"
|
||||||
TargetType="{x:Type TextBlock}">
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#fffffe" />
|
<Setter Property="Foreground" Value="#ed92c9" />
|
||||||
</Style>
|
</Style>
|
||||||
<SolidColorBrush x:Key="ItemSelectedBackgroundColor">#0e172c</SolidColorBrush>
|
<SolidColorBrush x:Key="ItemSelectedBackgroundColor">#cc1081</SolidColorBrush>
|
||||||
<Style
|
<Style
|
||||||
x:Key="ThumbStyle"
|
x:Key="ThumbStyle"
|
||||||
BasedOn="{StaticResource BaseThumbStyle}"
|
BasedOn="{StaticResource BaseThumbStyle}"
|
||||||
|
|
@ -86,7 +85,7 @@
|
||||||
<Setter.Value>
|
<Setter.Value>
|
||||||
<ControlTemplate TargetType="{x:Type Thumb}">
|
<ControlTemplate TargetType="{x:Type Thumb}">
|
||||||
<Border
|
<Border
|
||||||
Background="#E9819F"
|
Background="#e564b1"
|
||||||
BorderBrush="Transparent"
|
BorderBrush="Transparent"
|
||||||
BorderThickness="0"
|
BorderThickness="0"
|
||||||
CornerRadius="2"
|
CornerRadius="2"
|
||||||
|
|
@ -109,56 +108,57 @@
|
||||||
<Setter Property="HorizontalAlignment" Value="Right" />
|
<Setter Property="HorizontalAlignment" Value="Right" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="SearchIconStyle" TargetType="{x:Type Path}">
|
<Style x:Key="SearchIconStyle" TargetType="{x:Type Path}">
|
||||||
<Setter Property="Fill" Value="#E9819F" />
|
<Setter Property="Fill" Value="#cc1081" />
|
||||||
<Setter Property="Width" Value="28" />
|
<Setter Property="Width" Value="28" />
|
||||||
<Setter Property="Height" Value="28" />
|
<Setter Property="Height" Value="28" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="ItemHotkeyStyle" TargetType="{x:Type TextBlock}">
|
<Style
|
||||||
<Setter Property="FontSize" Value="11" />
|
x:Key="ItemHotkeyStyle"
|
||||||
<Setter Property="Foreground" Value="#E9819F" />
|
BasedOn="{StaticResource BaseItemHotkeyStyle}"
|
||||||
|
TargetType="{x:Type TextBlock}">
|
||||||
|
<Setter Property="Foreground" Value="#ed92c9" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="ItemHotkeySelectedStyle" TargetType="{x:Type TextBlock}">
|
<Style
|
||||||
<Setter Property="FontSize" Value="11" />
|
x:Key="ItemHotkeySelectedStyle"
|
||||||
<Setter Property="Foreground" Value="#E9819F" />
|
BasedOn="{StaticResource BaseItemHotkeySelectedStyle}"
|
||||||
|
TargetType="{x:Type TextBlock}">
|
||||||
|
<Setter Property="Foreground" Value="#ed92c9" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="ClockBox"
|
x:Key="ClockBox"
|
||||||
BasedOn="{StaticResource BaseClockBox}"
|
BasedOn="{StaticResource BaseClockBox}"
|
||||||
TargetType="{x:Type TextBlock}">
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#E9819F" />
|
<Setter Property="Foreground" Value="#71114b" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="DateBox"
|
x:Key="DateBox"
|
||||||
BasedOn="{StaticResource BaseDateBox}"
|
BasedOn="{StaticResource BaseDateBox}"
|
||||||
TargetType="{x:Type TextBlock}">
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="FontWeight" Value="Bold" />
|
<Setter Property="FontWeight" Value="Bold" />
|
||||||
<Setter Property="Foreground" Value="#E9819F" />
|
<Setter Property="Foreground" Value="#71114b" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="PreviewBorderStyle"
|
x:Key="PreviewBorderStyle"
|
||||||
BasedOn="{StaticResource BasePreviewBorderStyle}"
|
BasedOn="{StaticResource BasePreviewBorderStyle}"
|
||||||
TargetType="{x:Type Border}">
|
TargetType="{x:Type Border}">
|
||||||
<Setter Property="Margin" Value="0 0 10 0" />
|
<Setter Property="BorderBrush" Value="#000000" />
|
||||||
<Setter Property="Width" Value="5" />
|
|
||||||
<Setter Property="BorderThickness" Value="3 0 0 0" />
|
|
||||||
<Setter Property="BorderBrush" Value="#0e172c" />
|
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="PreviewItemTitleStyle"
|
x:Key="PreviewItemTitleStyle"
|
||||||
BasedOn="{StaticResource BasePreviewItemTitleStyle}"
|
BasedOn="{StaticResource BasePreviewItemTitleStyle}"
|
||||||
TargetType="{x:Type TextBlock}">
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#0E172C" />
|
<Setter Property="Foreground" Value="#f5f5f5" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="PreviewItemSubTitleStyle"
|
x:Key="PreviewItemSubTitleStyle"
|
||||||
BasedOn="{StaticResource BasePreviewItemSubTitleStyle}"
|
BasedOn="{StaticResource BasePreviewItemSubTitleStyle}"
|
||||||
TargetType="{x:Type TextBlock}">
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#0E172C" />
|
<Setter Property="Foreground" Value="#c2c2c2" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="PreviewGlyph"
|
x:Key="PreviewGlyph"
|
||||||
BasedOn="{StaticResource BasePreviewGlyph}"
|
BasedOn="{StaticResource BasePreviewGlyph}"
|
||||||
TargetType="{x:Type TextBlock}">
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="#0E172C" />
|
<Setter Property="Foreground" Value="#ffffff" />
|
||||||
</Style>
|
</Style>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
|
|
@ -27,7 +27,7 @@
|
||||||
x:Key="QueryBoxStyle"
|
x:Key="QueryBoxStyle"
|
||||||
BasedOn="{StaticResource BaseQueryBoxStyle}"
|
BasedOn="{StaticResource BaseQueryBoxStyle}"
|
||||||
TargetType="{x:Type TextBox}">
|
TargetType="{x:Type TextBox}">
|
||||||
<Setter Property="Padding" Value="0,2,42,0" />
|
<Setter Property="Padding" Value="0,0,42,0" />
|
||||||
<Setter Property="Foreground" Value="#282728" />
|
<Setter Property="Foreground" Value="#282728" />
|
||||||
<Setter Property="FontSize" Value="16" />
|
<Setter Property="FontSize" Value="16" />
|
||||||
<Setter Property="Height" Value="24" />
|
<Setter Property="Height" Value="24" />
|
||||||
|
|
@ -37,7 +37,7 @@
|
||||||
x:Key="QuerySuggestionBoxStyle"
|
x:Key="QuerySuggestionBoxStyle"
|
||||||
BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}"
|
BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}"
|
||||||
TargetType="{x:Type TextBox}">
|
TargetType="{x:Type TextBox}">
|
||||||
<Setter Property="Padding" Value="0,2,42,0" />
|
<Setter Property="Padding" Value="0,0,42,0" />
|
||||||
<Setter Property="Background" Value="Transparent" />
|
<Setter Property="Background" Value="Transparent" />
|
||||||
<Setter Property="Height" Value="24" />
|
<Setter Property="Height" Value="24" />
|
||||||
<Setter Property="FontSize" Value="16" />
|
<Setter Property="FontSize" Value="16" />
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
<Setter Property="CaretBrush" Value="#FFAA47" />
|
<Setter Property="CaretBrush" Value="#FFAA47" />
|
||||||
<Setter Property="FontSize" Value="26" />
|
<Setter Property="FontSize" Value="26" />
|
||||||
<Setter Property="Height" Value="42" />
|
<Setter Property="Height" Value="42" />
|
||||||
<Setter Property="Padding" Value="0,4,66,0" />
|
<Setter Property="Padding" Value="0,0,66,0" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="QuerySuggestionBoxStyle"
|
x:Key="QuerySuggestionBoxStyle"
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
<Setter Property="Foreground" Value="#798189" />
|
<Setter Property="Foreground" Value="#798189" />
|
||||||
<Setter Property="FontSize" Value="26" />
|
<Setter Property="FontSize" Value="26" />
|
||||||
<Setter Property="Height" Value="42" />
|
<Setter Property="Height" Value="42" />
|
||||||
<Setter Property="Padding" Value="0,4,66,0" />
|
<Setter Property="Padding" Value="0,0,66,0" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="WindowBorderStyle"
|
x:Key="WindowBorderStyle"
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@
|
||||||
x:Key="QueryBoxStyle"
|
x:Key="QueryBoxStyle"
|
||||||
BasedOn="{StaticResource BaseQueryBoxStyle}"
|
BasedOn="{StaticResource BaseQueryBoxStyle}"
|
||||||
TargetType="{x:Type TextBox}">
|
TargetType="{x:Type TextBox}">
|
||||||
<Setter Property="Padding" Value="0,4,50,0" />
|
<Setter Property="Padding" Value="0 0 50 0" />
|
||||||
<Setter Property="CaretBrush" Value="#fa7941" />
|
<Setter Property="CaretBrush" Value="#fa7941" />
|
||||||
<Setter Property="Foreground" Value="#ffffff" />
|
<Setter Property="Foreground" Value="#ffffff" />
|
||||||
<Setter Property="FontSize" Value="18" />
|
<Setter Property="FontSize" Value="18" />
|
||||||
|
|
@ -43,7 +43,7 @@
|
||||||
x:Key="QuerySuggestionBoxStyle"
|
x:Key="QuerySuggestionBoxStyle"
|
||||||
BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}"
|
BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}"
|
||||||
TargetType="{x:Type TextBox}">
|
TargetType="{x:Type TextBox}">
|
||||||
<Setter Property="Padding" Value="0,4,50,0" />
|
<Setter Property="Padding" Value="0 0 50 0" />
|
||||||
<Setter Property="Background" Value="Transparent" />
|
<Setter Property="Background" Value="Transparent" />
|
||||||
<Setter Property="Height" Value="38" />
|
<Setter Property="Height" Value="38" />
|
||||||
<Setter Property="FontSize" Value="18" />
|
<Setter Property="FontSize" Value="18" />
|
||||||
|
|
@ -56,15 +56,7 @@
|
||||||
TargetType="{x:Type Border}">
|
TargetType="{x:Type Border}">
|
||||||
<Setter Property="BorderThickness" Value="0" />
|
<Setter Property="BorderThickness" Value="0" />
|
||||||
<Setter Property="BorderBrush" Value="#e2e2e2" />
|
<Setter Property="BorderBrush" Value="#e2e2e2" />
|
||||||
<Setter Property="Background">
|
<Setter Property="Background" Value="#2e2e2e" />
|
||||||
<Setter.Value>
|
|
||||||
<LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
|
|
||||||
<GradientStop Offset="0.0" Color="#383838" />
|
|
||||||
<GradientStop Offset="0.9" Color="#2e2e2e" />
|
|
||||||
<GradientStop Offset="1.0" Color="#2e2e2e" />
|
|
||||||
</LinearGradientBrush>
|
|
||||||
</Setter.Value>
|
|
||||||
</Setter>
|
|
||||||
<Setter Property="CornerRadius" Value="8" />
|
<Setter Property="CornerRadius" Value="8" />
|
||||||
<Setter Property="UseLayoutRounding" Value="True" />
|
<Setter Property="UseLayoutRounding" Value="True" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
@ -96,7 +88,7 @@
|
||||||
TargetType="{x:Type Rectangle}">
|
TargetType="{x:Type Rectangle}">
|
||||||
<Setter Property="Fill" Value="#3b3b3b" />
|
<Setter Property="Fill" Value="#3b3b3b" />
|
||||||
<Setter Property="Height" Value="1" />
|
<Setter Property="Height" Value="1" />
|
||||||
<Setter Property="Margin" Value="0,0,0,8" />
|
<Setter Property="Margin" Value="0 0 0 8" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="HighlightStyle" />
|
<Style x:Key="HighlightStyle" />
|
||||||
<Style
|
<Style
|
||||||
|
|
@ -147,7 +139,7 @@
|
||||||
<Setter Property="Background" Value="Transparent" />
|
<Setter Property="Background" Value="Transparent" />
|
||||||
<Setter Property="Width" Value="32" />
|
<Setter Property="Width" Value="32" />
|
||||||
<Setter Property="Height" Value="32" />
|
<Setter Property="Height" Value="32" />
|
||||||
<Setter Property="Margin" Value="0,8,8,0" />
|
<Setter Property="Margin" Value="0 8 8 0" />
|
||||||
<Setter Property="HorizontalAlignment" Value="Right" />
|
<Setter Property="HorizontalAlignment" Value="Right" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
|
|
@ -174,7 +166,7 @@
|
||||||
x:Key="ClockPanel"
|
x:Key="ClockPanel"
|
||||||
BasedOn="{StaticResource ClockPanel}"
|
BasedOn="{StaticResource ClockPanel}"
|
||||||
TargetType="{x:Type StackPanel}">
|
TargetType="{x:Type StackPanel}">
|
||||||
<Setter Property="Margin" Value="0,0,54,2" />
|
<Setter Property="Margin" Value="0 0 54 2" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="ClockBox"
|
x:Key="ClockBox"
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,10 @@
|
||||||
<ResourceDictionary.MergedDictionaries>
|
<ResourceDictionary.MergedDictionaries>
|
||||||
<ResourceDictionary Source="pack://application:,,,/Themes/Base.xaml" />
|
<ResourceDictionary Source="pack://application:,,,/Themes/Base.xaml" />
|
||||||
</ResourceDictionary.MergedDictionaries>
|
</ResourceDictionary.MergedDictionaries>
|
||||||
|
<system:Boolean x:Key="ThemeBlurEnabled">False</system:Boolean>
|
||||||
|
<system:String x:Key="SystemBG">Auto</system:String>
|
||||||
|
<Color x:Key="LightBG">#FFFAFAFA</Color>
|
||||||
|
<Color x:Key="DarkBG">#FF202020</Color>
|
||||||
<Thickness x:Key="ResultMargin">0 0 0 8</Thickness>
|
<Thickness x:Key="ResultMargin">0 0 0 8</Thickness>
|
||||||
<Style
|
<Style
|
||||||
x:Key="ItemGlyph"
|
x:Key="ItemGlyph"
|
||||||
|
|
@ -32,7 +36,7 @@
|
||||||
<Setter Property="FontSize" Value="22" />
|
<Setter Property="FontSize" Value="22" />
|
||||||
<Setter Property="Foreground" Value="{DynamicResource Color05B}" />
|
<Setter Property="Foreground" Value="{DynamicResource Color05B}" />
|
||||||
<Setter Property="CaretBrush" Value="{DynamicResource Color05B}" />
|
<Setter Property="CaretBrush" Value="{DynamicResource Color05B}" />
|
||||||
<Setter Property="Padding" Value="0 4 66 0" />
|
<Setter Property="Padding" Value="0 0 66 0" />
|
||||||
<Setter Property="Height" Value="42" />
|
<Setter Property="Height" Value="42" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
|
|
@ -41,7 +45,7 @@
|
||||||
TargetType="{x:Type TextBox}">
|
TargetType="{x:Type TextBox}">
|
||||||
<Setter Property="Foreground" Value="{DynamicResource QuerySuggestionBoxForeground}" />
|
<Setter Property="Foreground" Value="{DynamicResource QuerySuggestionBoxForeground}" />
|
||||||
<Setter Property="FontSize" Value="22" />
|
<Setter Property="FontSize" Value="22" />
|
||||||
<Setter Property="Padding" Value="0 4 66 0" />
|
<Setter Property="Padding" Value="0 0 66 0" />
|
||||||
<Setter Property="Height" Value="42" />
|
<Setter Property="Height" Value="42" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
|
|
@ -109,15 +113,18 @@
|
||||||
<Style x:Key="HighlightStyle">
|
<Style x:Key="HighlightStyle">
|
||||||
<Setter Property="Inline.FontWeight" Value="SemiBold" />
|
<Setter Property="Inline.FontWeight" Value="SemiBold" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="ItemHotkeyStyle" TargetType="{x:Type TextBlock}">
|
<Style
|
||||||
<Setter Property="FontSize" Value="13" />
|
x:Key="ItemHotkeyStyle"
|
||||||
|
BasedOn="{StaticResource BaseItemHotkeyStyle}"
|
||||||
|
TargetType="{x:Type TextBlock}">
|
||||||
|
<Setter Property="FontSize" Value="11" />
|
||||||
<Setter Property="Foreground" Value="{DynamicResource HotkeyForeground}" />
|
<Setter Property="Foreground" Value="{DynamicResource HotkeyForeground}" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="ItemHotkeySelectedStyle"
|
x:Key="ItemHotkeySelectedStyle"
|
||||||
BasedOn="{StaticResource BaseItemHotkeySelectedStyle}"
|
BasedOn="{StaticResource BaseItemHotkeySelectedStyle}"
|
||||||
TargetType="{x:Type TextBlock}">
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="FontSize" Value="13" />
|
<Setter Property="FontSize" Value="11" />
|
||||||
<Setter Property="Foreground" Value="{DynamicResource HotkeySelectedForeground}" />
|
<Setter Property="Foreground" Value="{DynamicResource HotkeySelectedForeground}" />
|
||||||
</Style>
|
</Style>
|
||||||
<!-- button style in the middle of the scrollbar -->
|
<!-- button style in the middle of the scrollbar -->
|
||||||
|
|
@ -175,13 +182,13 @@
|
||||||
x:Key="ClockBox"
|
x:Key="ClockBox"
|
||||||
BasedOn="{StaticResource BaseClockBox}"
|
BasedOn="{StaticResource BaseClockBox}"
|
||||||
TargetType="{x:Type TextBlock}">
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="{DynamicResource ClockDateForeground}" />
|
<Setter Property="Foreground" Value="{DynamicResource SystemControlBackgroundBaseLowBrush}" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="DateBox"
|
x:Key="DateBox"
|
||||||
BasedOn="{StaticResource BaseDateBox}"
|
BasedOn="{StaticResource BaseDateBox}"
|
||||||
TargetType="{x:Type TextBlock}">
|
TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="{DynamicResource ClockDateForeground}" />
|
<Setter Property="Foreground" Value="{DynamicResource SystemControlBackgroundBaseLowBrush}" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="PreviewBorderStyle"
|
x:Key="PreviewBorderStyle"
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<!--
|
<!--
|
||||||
Name: Windows 11
|
Name: Windows 11
|
||||||
IsDark: True
|
IsDark: True
|
||||||
HasBlur: False
|
HasBlur: True
|
||||||
-->
|
-->
|
||||||
<ResourceDictionary
|
<ResourceDictionary
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
|
@ -12,6 +12,10 @@
|
||||||
<ResourceDictionary.MergedDictionaries>
|
<ResourceDictionary.MergedDictionaries>
|
||||||
<ResourceDictionary Source="pack://application:,,,/Themes/Base.xaml" />
|
<ResourceDictionary Source="pack://application:,,,/Themes/Base.xaml" />
|
||||||
</ResourceDictionary.MergedDictionaries>
|
</ResourceDictionary.MergedDictionaries>
|
||||||
|
<system:Boolean x:Key="ThemeBlurEnabled">True</system:Boolean>
|
||||||
|
<system:String x:Key="SystemBG">Auto</system:String>
|
||||||
|
<Color x:Key="LightBG">#BFFAFAFA</Color>
|
||||||
|
<Color x:Key="DarkBG">#DD202020</Color>
|
||||||
<Style
|
<Style
|
||||||
x:Key="BulletStyle"
|
x:Key="BulletStyle"
|
||||||
BasedOn="{StaticResource BaseBulletStyle}"
|
BasedOn="{StaticResource BaseBulletStyle}"
|
||||||
|
|
@ -152,13 +156,26 @@
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<Style x:Key="ItemHotkeyStyle" TargetType="{x:Type TextBlock}">
|
<Style x:Key="ItemHotkeyStyle" TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="FontSize" Value="12" />
|
<Setter Property="FontSize" Value="11" />
|
||||||
<Setter Property="Foreground" Value="{DynamicResource HotkeyForeground}" />
|
<Setter Property="Foreground" Value="{DynamicResource NewHotkeyForeground}" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="ItemHotkeySelectedStyle" TargetType="{x:Type TextBlock}">
|
<Style x:Key="ItemHotkeySelectedStyle" TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="FontSize" Value="12" />
|
<Setter Property="FontSize" Value="11" />
|
||||||
<Setter Property="Foreground" Value="{DynamicResource HotkeySelectedForeground}" />
|
<Setter Property="Foreground" Value="{DynamicResource NewHotkeyForeground}" />
|
||||||
</Style>
|
</Style>
|
||||||
|
<Style
|
||||||
|
x:Key="ItemHotkeyBGStyle"
|
||||||
|
BasedOn="{StaticResource BaseItemHotkeyBGStyle}"
|
||||||
|
TargetType="{x:Type Border}">
|
||||||
|
<Setter Property="Background" Value="{DynamicResource BasicHotkeyBGColor}" />
|
||||||
|
</Style>
|
||||||
|
<Style
|
||||||
|
x:Key="ItemHotkeyBGSelectedStyle"
|
||||||
|
BasedOn="{StaticResource BaseItemHotkeyBGSelectedStyle}"
|
||||||
|
TargetType="{x:Type Border}">
|
||||||
|
<Setter Property="Background" Value="{DynamicResource BasicHotkeyBGColor}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
<Style x:Key="ItemGlyphSelectedStyle" TargetType="{x:Type TextBlock}">
|
<Style x:Key="ItemGlyphSelectedStyle" TargetType="{x:Type TextBlock}">
|
||||||
<Setter Property="Foreground" Value="{DynamicResource Color05B}" />
|
<Setter Property="Foreground" Value="{DynamicResource Color05B}" />
|
||||||
<Setter Property="VerticalAlignment" Value="Center" />
|
<Setter Property="VerticalAlignment" Value="Center" />
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,12 +1,12 @@
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Media;
|
|
||||||
using Flow.Launcher.Plugin;
|
|
||||||
using Flow.Launcher.Infrastructure.Image;
|
|
||||||
using Flow.Launcher.Core.Plugin;
|
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Media;
|
||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
using Flow.Launcher.Core.Resource;
|
using Flow.Launcher.Core.Plugin;
|
||||||
|
using Flow.Launcher.Infrastructure.Image;
|
||||||
|
using Flow.Launcher.Plugin;
|
||||||
using Flow.Launcher.Resources.Controls;
|
using Flow.Launcher.Resources.Controls;
|
||||||
|
|
||||||
namespace Flow.Launcher.ViewModel
|
namespace Flow.Launcher.ViewModel
|
||||||
|
|
@ -28,7 +28,7 @@ namespace Flow.Launcher.ViewModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string PluginManagerActionKeyword
|
private static string PluginManagerActionKeyword
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
|
@ -43,10 +43,10 @@ namespace Flow.Launcher.ViewModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task LoadIconAsync()
|
||||||
private async void LoadIconAsync()
|
|
||||||
{
|
{
|
||||||
Image = await ImageLoader.LoadAsync(PluginPair.Metadata.IcoPath);
|
Image = await ImageLoader.LoadAsync(PluginPair.Metadata.IcoPath);
|
||||||
|
OnPropertyChanged(nameof(Image));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImageSource Image
|
public ImageSource Image
|
||||||
|
|
@ -54,12 +54,13 @@ namespace Flow.Launcher.ViewModel
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (_image == ImageLoader.MissingImage)
|
if (_image == ImageLoader.MissingImage)
|
||||||
LoadIconAsync();
|
_ = LoadIconAsync();
|
||||||
|
|
||||||
return _image;
|
return _image;
|
||||||
}
|
}
|
||||||
set => _image = value;
|
set => _image = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool PluginState
|
public bool PluginState
|
||||||
{
|
{
|
||||||
get => !PluginPair.Metadata.Disabled;
|
get => !PluginPair.Metadata.Disabled;
|
||||||
|
|
@ -69,6 +70,7 @@ namespace Flow.Launcher.ViewModel
|
||||||
PluginSettingsObject.Disabled = !value;
|
PluginSettingsObject.Disabled = !value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsExpanded
|
public bool IsExpanded
|
||||||
{
|
{
|
||||||
get => _isExpanded;
|
get => _isExpanded;
|
||||||
|
|
@ -81,6 +83,16 @@ namespace Flow.Launcher.ViewModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SearchDelayTime? PluginSearchDelayTime
|
||||||
|
{
|
||||||
|
get => PluginPair.Metadata.SearchDelayTime;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
PluginPair.Metadata.SearchDelayTime = value;
|
||||||
|
PluginSettingsObject.SearchDelayTime = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Control _settingControl;
|
private Control _settingControl;
|
||||||
private bool _isExpanded;
|
private bool _isExpanded;
|
||||||
|
|
||||||
|
|
@ -88,9 +100,13 @@ namespace Flow.Launcher.ViewModel
|
||||||
public Control BottomPart1 => IsExpanded ? _bottomPart1 ??= new InstalledPluginDisplayKeyword() : null;
|
public Control BottomPart1 => IsExpanded ? _bottomPart1 ??= new InstalledPluginDisplayKeyword() : null;
|
||||||
|
|
||||||
private Control _bottomPart2;
|
private Control _bottomPart2;
|
||||||
public Control BottomPart2 => IsExpanded ? _bottomPart2 ??= new InstalledPluginDisplayBottomData() : null;
|
public Control BottomPart2 => IsExpanded ? _bottomPart2 ??= new InstalledPluginSearchDelay() : null;
|
||||||
|
|
||||||
public bool HasSettingControl => PluginPair.Plugin is ISettingProvider && (PluginPair.Plugin is not JsonRPCPluginBase jsonRPCPluginBase || jsonRPCPluginBase.NeedCreateSettingPanel());
|
private Control _bottomPart3;
|
||||||
|
public Control BottomPart3 => IsExpanded ? _bottomPart3 ??= new InstalledPluginDisplayBottomData() : null;
|
||||||
|
|
||||||
|
public bool HasSettingControl => PluginPair.Plugin is ISettingProvider &&
|
||||||
|
(PluginPair.Plugin is not JsonRPCPluginBase jsonRPCPluginBase || jsonRPCPluginBase.NeedCreateSettingPanel());
|
||||||
public Control SettingControl
|
public Control SettingControl
|
||||||
=> IsExpanded
|
=> IsExpanded
|
||||||
? _settingControl
|
? _settingControl
|
||||||
|
|
@ -100,21 +116,33 @@ namespace Flow.Launcher.ViewModel
|
||||||
: null;
|
: null;
|
||||||
private ImageSource _image = ImageLoader.MissingImage;
|
private ImageSource _image = ImageLoader.MissingImage;
|
||||||
|
|
||||||
public Visibility ActionKeywordsVisibility => PluginPair.Metadata.ActionKeywords.Count == 1 ? Visibility.Visible : Visibility.Collapsed;
|
public Visibility ActionKeywordsVisibility => PluginPair.Metadata.HideActionKeywordPanel ?
|
||||||
public string InitilizaTime => PluginPair.Metadata.InitTime + "ms";
|
Visibility.Collapsed : Visibility.Visible;
|
||||||
|
public string InitializeTime => PluginPair.Metadata.InitTime + "ms";
|
||||||
public string QueryTime => PluginPair.Metadata.AvgQueryTime + "ms";
|
public string QueryTime => PluginPair.Metadata.AvgQueryTime + "ms";
|
||||||
public string Version => InternationalizationManager.Instance.GetTranslation("plugin_query_version") + " " + PluginPair.Metadata.Version;
|
public string Version => App.API.GetTranslation("plugin_query_version") + " " + PluginPair.Metadata.Version;
|
||||||
public string InitAndQueryTime => InternationalizationManager.Instance.GetTranslation("plugin_init_time") + " " + PluginPair.Metadata.InitTime + "ms, " + InternationalizationManager.Instance.GetTranslation("plugin_query_time") + " " + PluginPair.Metadata.AvgQueryTime + "ms";
|
public string InitAndQueryTime =>
|
||||||
|
App.API.GetTranslation("plugin_init_time") + " " +
|
||||||
|
PluginPair.Metadata.InitTime + "ms, " +
|
||||||
|
App.API.GetTranslation("plugin_query_time") + " " +
|
||||||
|
PluginPair.Metadata.AvgQueryTime + "ms";
|
||||||
public string ActionKeywordsText => string.Join(Query.ActionKeywordSeparator, PluginPair.Metadata.ActionKeywords);
|
public string ActionKeywordsText => string.Join(Query.ActionKeywordSeparator, PluginPair.Metadata.ActionKeywords);
|
||||||
public int Priority => PluginPair.Metadata.Priority;
|
public int Priority => PluginPair.Metadata.Priority;
|
||||||
public Infrastructure.UserSettings.Plugin PluginSettingsObject { get; set; }
|
public string SearchDelayTimeText => PluginPair.Metadata.SearchDelayTime == null ?
|
||||||
|
App.API.GetTranslation("default") :
|
||||||
|
App.API.GetTranslation($"SearchDelayTime{PluginPair.Metadata.SearchDelayTime}");
|
||||||
|
public Infrastructure.UserSettings.Plugin PluginSettingsObject{ get; init; }
|
||||||
|
|
||||||
public void ChangeActionKeyword(string newActionKeyword, string oldActionKeyword)
|
public void OnActionKeywordsChanged()
|
||||||
{
|
{
|
||||||
PluginManager.ReplaceActionKeyword(PluginPair.Metadata.ID, oldActionKeyword, newActionKeyword);
|
|
||||||
OnPropertyChanged(nameof(ActionKeywordsText));
|
OnPropertyChanged(nameof(ActionKeywordsText));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void OnSearchDelayTimeChanged()
|
||||||
|
{
|
||||||
|
OnPropertyChanged(nameof(SearchDelayTimeText));
|
||||||
|
}
|
||||||
|
|
||||||
public void ChangePriority(int newPriority)
|
public void ChangePriority(int newPriority)
|
||||||
{
|
{
|
||||||
PluginPair.Metadata.Priority = newPriority;
|
PluginPair.Metadata.Priority = newPriority;
|
||||||
|
|
@ -125,7 +153,7 @@ namespace Flow.Launcher.ViewModel
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private void EditPluginPriority()
|
private void EditPluginPriority()
|
||||||
{
|
{
|
||||||
PriorityChangeWindow priorityChangeWindow = new PriorityChangeWindow(PluginPair. Metadata.ID, this);
|
var priorityChangeWindow = new PriorityChangeWindow(PluginPair. Metadata.ID, this);
|
||||||
priorityChangeWindow.ShowDialog();
|
priorityChangeWindow.ShowDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -150,13 +178,18 @@ namespace Flow.Launcher.ViewModel
|
||||||
App.API.ShowMainWindow();
|
App.API.ShowMainWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsActionKeywordRegistered(string newActionKeyword) => PluginManager.ActionKeywordRegistered(newActionKeyword);
|
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private void SetActionKeywords()
|
private void SetActionKeywords()
|
||||||
{
|
{
|
||||||
ActionKeywords changeKeywordsWindow = new ActionKeywords(this);
|
var changeKeywordsWindow = new ActionKeywords(this);
|
||||||
changeKeywordsWindow.ShowDialog();
|
changeKeywordsWindow.ShowDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private void SetSearchDelayTime()
|
||||||
|
{
|
||||||
|
var searchDelayTimeWindow = new SearchDelayTimeWindow(this);
|
||||||
|
searchDelayTimeWindow.ShowDialog();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing.Text;
|
||||||
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
|
|
@ -6,25 +9,20 @@ using Flow.Launcher.Infrastructure.Image;
|
||||||
using Flow.Launcher.Infrastructure.Logger;
|
using Flow.Launcher.Infrastructure.Logger;
|
||||||
using Flow.Launcher.Infrastructure.UserSettings;
|
using Flow.Launcher.Infrastructure.UserSettings;
|
||||||
using Flow.Launcher.Plugin;
|
using Flow.Launcher.Plugin;
|
||||||
using System.IO;
|
|
||||||
using System.Drawing.Text;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Flow.Launcher.ViewModel
|
namespace Flow.Launcher.ViewModel
|
||||||
{
|
{
|
||||||
public class ResultViewModel : BaseModel
|
public class ResultViewModel : BaseModel
|
||||||
{
|
{
|
||||||
private static PrivateFontCollection fontCollection = new();
|
private static readonly PrivateFontCollection FontCollection = new();
|
||||||
private static Dictionary<string, string> fonts = new();
|
private static readonly Dictionary<string, string> Fonts = new();
|
||||||
|
|
||||||
public ResultViewModel(Result result, Settings settings)
|
public ResultViewModel(Result result, Settings settings)
|
||||||
{
|
{
|
||||||
Settings = settings;
|
Settings = settings;
|
||||||
|
|
||||||
if (result == null)
|
if (result == null) return;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Result = result;
|
Result = result;
|
||||||
|
|
||||||
if (Result.Glyph is { FontFamily: not null } glyph)
|
if (Result.Glyph is { FontFamily: not null } glyph)
|
||||||
|
|
@ -39,20 +37,20 @@ namespace Flow.Launcher.ViewModel
|
||||||
fontFamilyPath = Path.Combine(Result.PluginDirectory, fontFamilyPath);
|
fontFamilyPath = Path.Combine(Result.PluginDirectory, fontFamilyPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fonts.ContainsKey(fontFamilyPath))
|
if (Fonts.TryGetValue(fontFamilyPath, out var value))
|
||||||
{
|
{
|
||||||
Glyph = glyph with
|
Glyph = glyph with
|
||||||
{
|
{
|
||||||
FontFamily = fonts[fontFamilyPath]
|
FontFamily = value
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fontCollection.AddFontFile(fontFamilyPath);
|
FontCollection.AddFontFile(fontFamilyPath);
|
||||||
fonts[fontFamilyPath] = $"{Path.GetDirectoryName(fontFamilyPath)}/#{fontCollection.Families[^1].Name}";
|
Fonts[fontFamilyPath] = $"{Path.GetDirectoryName(fontFamilyPath)}/#{FontCollection.Families[^1].Name}";
|
||||||
Glyph = glyph with
|
Glyph = glyph with
|
||||||
{
|
{
|
||||||
FontFamily = fonts[fontFamilyPath]
|
FontFamily = Fonts[fontFamilyPath]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -61,7 +59,6 @@ namespace Flow.Launcher.ViewModel
|
||||||
Glyph = glyph;
|
Glyph = glyph;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Settings Settings { get; }
|
public Settings Settings { get; }
|
||||||
|
|
@ -95,14 +92,10 @@ namespace Flow.Launcher.ViewModel
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (PreviewImageAvailable)
|
if (PreviewImageAvailable)
|
||||||
{
|
|
||||||
return Visibility.Visible;
|
return Visibility.Visible;
|
||||||
}
|
|
||||||
else
|
// Fall back to icon
|
||||||
{
|
return ShowIcon;
|
||||||
// Fall back to icon
|
|
||||||
return ShowIcon;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -111,9 +104,8 @@ namespace Flow.Launcher.ViewModel
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (Result.RoundedIcon)
|
if (Result.RoundedIcon)
|
||||||
{
|
|
||||||
return IconXY / 2;
|
return IconXY / 2;
|
||||||
}
|
|
||||||
return IconXY;
|
return IconXY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -148,31 +140,40 @@ namespace Flow.Launcher.ViewModel
|
||||||
? Result.SubTitle
|
? Result.SubTitle
|
||||||
: Result.SubTitleToolTip;
|
: Result.SubTitleToolTip;
|
||||||
|
|
||||||
private volatile bool ImageLoaded;
|
private volatile bool _imageLoaded;
|
||||||
private volatile bool PreviewImageLoaded;
|
private volatile bool _previewImageLoaded;
|
||||||
|
|
||||||
private ImageSource image = ImageLoader.LoadingImage;
|
private ImageSource _image = ImageLoader.LoadingImage;
|
||||||
private ImageSource previewImage = ImageLoader.LoadingImage;
|
private ImageSource _previewImage = ImageLoader.LoadingImage;
|
||||||
|
|
||||||
public ImageSource Image
|
public ImageSource Image
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (!ImageLoaded)
|
if (!_imageLoaded)
|
||||||
{
|
{
|
||||||
ImageLoaded = true;
|
_imageLoaded = true;
|
||||||
_ = LoadImageAsync();
|
_ = LoadImageAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
return image;
|
return _image;
|
||||||
}
|
}
|
||||||
private set => image = value;
|
private set => _image = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImageSource PreviewImage
|
public ImageSource PreviewImage
|
||||||
{
|
{
|
||||||
get => previewImage;
|
get
|
||||||
private set => previewImage = value;
|
{
|
||||||
|
if (!_previewImageLoaded)
|
||||||
|
{
|
||||||
|
_previewImageLoaded = true;
|
||||||
|
_ = LoadPreviewImageAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _previewImage;
|
||||||
|
}
|
||||||
|
private set => _previewImage = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -188,8 +189,7 @@ namespace Flow.Launcher.ViewModel
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var image = icon();
|
return icon();
|
||||||
return image;
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|
@ -208,7 +208,7 @@ namespace Flow.Launcher.ViewModel
|
||||||
var iconDelegate = Result.Icon;
|
var iconDelegate = Result.Icon;
|
||||||
if (ImageLoader.TryGetValue(imagePath, false, out ImageSource img))
|
if (ImageLoader.TryGetValue(imagePath, false, out ImageSource img))
|
||||||
{
|
{
|
||||||
image = img;
|
_image = img;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -223,7 +223,7 @@ namespace Flow.Launcher.ViewModel
|
||||||
var iconDelegate = Result.Preview.PreviewDelegate ?? Result.Icon;
|
var iconDelegate = Result.Preview.PreviewDelegate ?? Result.Icon;
|
||||||
if (ImageLoader.TryGetValue(imagePath, true, out ImageSource img))
|
if (ImageLoader.TryGetValue(imagePath, true, out ImageSource img))
|
||||||
{
|
{
|
||||||
previewImage = img;
|
_previewImage = img;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -234,13 +234,10 @@ namespace Flow.Launcher.ViewModel
|
||||||
|
|
||||||
public void LoadPreviewImage()
|
public void LoadPreviewImage()
|
||||||
{
|
{
|
||||||
if (ShowDefaultPreview == Visibility.Visible)
|
if (ShowDefaultPreview == Visibility.Visible && !_previewImageLoaded && ShowPreviewImage == Visibility.Visible)
|
||||||
{
|
{
|
||||||
if (!PreviewImageLoaded && ShowPreviewImage == Visibility.Visible)
|
_previewImageLoaded = true;
|
||||||
{
|
_ = LoadPreviewImageAsync();
|
||||||
PreviewImageLoaded = true;
|
|
||||||
_ = LoadPreviewImageAsync();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue