Merge branch 'dev' into search_delay

This commit is contained in:
Jack Ye 2025-03-21 07:42:04 +08:00 committed by GitHub
commit 1e0118604c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
63 changed files with 3358 additions and 2475 deletions

View file

@ -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";
} }
} }
} }

View file

@ -231,7 +231,6 @@ namespace Flow.Launcher.Core.Plugin
if (!NonGlobalPlugins.ContainsKey(query.ActionKeyword)) if (!NonGlobalPlugins.ContainsKey(query.ActionKeyword))
return GlobalPlugins; return GlobalPlugins;
var plugin = NonGlobalPlugins[query.ActionKeyword]; var plugin = NonGlobalPlugins[query.ActionKeyword];
return new List<PluginPair> return new List<PluginPair>
{ {
@ -367,7 +366,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 +396,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;
} }
} }

View file

@ -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.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;
using TextBox = System.Windows.Controls.TextBox;
namespace Flow.Launcher.Core.Resource namespace Flow.Launcher.Core.Resource
{ {
public class Theme public class Theme
{ {
#region Properties & Fields
public bool BlurEnabled { get; 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,12 @@ 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; #endregion
public bool BlurEnabled { get; set; } #region Constructor
private double mainWindowWidth;
public Theme(IPublicAPI publicAPI, Settings settings) public Theme(IPublicAPI publicAPI, Settings settings)
{ {
@ -66,6 +72,15 @@ namespace Flow.Launcher.Core.Resource
_oldTheme = Path.GetFileNameWithoutExtension(_oldResource.Source.AbsolutePath); _oldTheme = Path.GetFileNameWithoutExtension(_oldResource.Source.AbsolutePath);
} }
#endregion
#region Theme Resources
public string GetCurrentTheme()
{
return _settings.Theme;
}
private void MakeSureThemeDirectoriesExist() private void MakeSureThemeDirectoriesExist()
{ {
foreach (var dir in _themeDirectories.Where(dir => !Directory.Exists(dir))) foreach (var dir in _themeDirectories.Where(dir => !Directory.Exists(dir)))
@ -81,59 +96,6 @@ 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; var dicts = Application.Current.Resources.MergedDictionaries;
@ -154,9 +116,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);
@ -213,7 +173,7 @@ 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)));
} }
@ -221,28 +181,12 @@ namespace Flow.Launcher.Core.Resource
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(Window.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 +208,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 +237,81 @@ 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}>");
// 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 == Constant.DefaultTheme)
{
_oldTheme = Path.GetFileNameWithoutExtension(_oldResource.Source.AbsolutePath);
}
BlurEnabled = IsBlurTheme();
//if (_settings.UseDropShadowEffect)
// AddDropShadowEffectToCurrentTheme();
//Win32Helper.SetBlurForWindow(Application.Current.MainWindow, BlurEnabled);
_ = SetBlurForWindowAsync();
}
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;
}
return true;
}
#endregion
#region Shadow Effect
public void AddDropShadowEffectToCurrentTheme() public void AddDropShadowEffectToCurrentTheme()
{ {
var dict = GetCurrentResourceDictionary(); var dict = GetCurrentResourceDictionary();
@ -311,8 +330,7 @@ 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 == Border.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()
@ -347,14 +365,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 == Border.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 == Border.MarginProperty) is Setter marginSetter)
{ {
var currentMargin = (Thickness)marginSetter.Value; var currentMargin = (Thickness)marginSetter.Value;
var newMargin = new Thickness( var newMargin = new Thickness(
@ -395,6 +411,343 @@ namespace Flow.Launcher.Core.Resource
} }
} }
#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.Normal);
}
/// <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.Normal);
}
/// <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 = GetThemeResourceDictionary(theme);
if (dict == null)
return;
var windowBorderStyle = dict.Contains("WindowBorderStyle") ? dict["WindowBorderStyle"] as Style : null;
if (windowBorderStyle == null)
return;
Window 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
} }
} }

View 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;
}
}
}

View file

@ -16,4 +16,34 @@ 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

View 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);
}
}

View file

@ -76,6 +76,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
} }
} }
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;
@ -457,4 +458,12 @@ namespace Flow.Launcher.Infrastructure.UserSettings
Fast, Fast,
Custom Custom
} }
public enum BackdropTypes
{
None,
Acrylic,
Mica,
MicaAlt
}
} }

View file

@ -1,7 +1,14 @@
using System; using System;
using System.ComponentModel;
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 Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.Graphics.Dwm;
using Windows.Win32.UI.WindowsAndMessaging;
using Flow.Launcher.Infrastructure.UserSettings;
namespace Flow.Launcher.Infrastructure namespace Flow.Launcher.Infrastructure
{ {
@ -9,93 +16,306 @@ 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> /// <summary>
/// Sets the blur for a window via SetWindowCompositionAttribute /// Transforms pixels to Device Independent Pixels used by WPF
/// </summary> /// </summary>
public static void SetBlurForWindow(Window w, bool blur) /// <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)
{ {
SetWindowAccent(w, blur ? AccentState.ACCENT_ENABLE_BLURBEHIND : AccentState.ACCENT_DISABLED); Matrix matrix;
} var source = PresentationSource.FromVisual(visual);
if (source is not null)
private static void SetWindowAccent(Window w, AccentState state)
{
var windowHelper = new WindowInteropHelper(w);
windowHelper.EnsureHandle();
var accent = new AccentPolicy { AccentState = state };
var accentStructSize = Marshal.SizeOf(accent);
var accentPtr = Marshal.AllocHGlobal(accentStructSize);
Marshal.StructureToPtr(accent, accentPtr, false);
var data = new WindowCompositionAttributeData
{ {
Attribute = WindowCompositionAttribute.WCA_ACCENT_POLICY, matrix = source.CompositionTarget.TransformFromDevice;
SizeOfData = accentStructSize, }
Data = accentPtr else
}; {
using var src = new HwndSource(new HwndSourceParameters());
matrix = src.CompositionTarget.TransformFromDevice;
}
SetWindowCompositionAttribute(windowHelper.Handle, ref data); return new Point((int)(matrix.M11 * unitX), (int)(matrix.M22 * unitY));
Marshal.FreeHGlobal(accentPtr);
} }
#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 #endregion
} }
} }

View file

@ -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>

View file

@ -33,7 +33,9 @@ namespace Flow.Launcher.Plugin
public string ActionKeyword { get; set; } public string ActionKeyword { get; set; }
public List<string> ActionKeywords { get; set; } public List<string> ActionKeywords { get; set; }
public bool HideActionKeywordPanel { get; set; }
public int SearchDelay { get; set; } public int SearchDelay { get; set; }
public string IcoPath { get; set;} public string IcoPath { get; set;}

View file

@ -39,10 +39,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.

View file

@ -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();
}
} }
} }

View file

@ -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>

View file

@ -101,15 +101,15 @@ namespace Flow.Launcher
{ {
if (SingleInstance<App>.InitializeAsFirstInstance(Unique)) if (SingleInstance<App>.InitializeAsFirstInstance(Unique))
{ {
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) #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 () =>
{ {
@ -128,7 +128,7 @@ namespace Flow.Launcher
AbstractPluginEnvironment.PreStartPluginExecutablePathUpdate(_settings); AbstractPluginEnvironment.PreStartPluginExecutablePathUpdate(_settings);
// TODO: Clean InternationalizationManager.Instance and InternationalizationManager.Instance.GetTranslation in future // TODO: Clean InternationalizationManager.Instance and InternationalizationManager.Instance.GetTranslation in future
InternationalizationManager.Instance.ChangeLanguage(_settings.Language); Ioc.Default.GetRequiredService<Internationalization>().ChangeLanguage(_settings.Language);
PluginManager.LoadPlugins(_settings.PluginSettings); PluginManager.LoadPlugins(_settings.PluginSettings);
@ -137,8 +137,7 @@ namespace Flow.Launcher
await PluginManager.InitializePluginsAsync(); await PluginManager.InitializePluginsAsync();
await imageLoadertask; await imageLoadertask;
var mainVM = Ioc.Default.GetRequiredService<MainViewModel>(); var window = new MainWindow();
var window = new MainWindow(_settings, mainVM);
Log.Info($"|App.OnStartup|Dependencies Info:{ErrorReporting.DependenciesInfo()}"); Log.Info($"|App.OnStartup|Dependencies Info:{ErrorReporting.DependenciesInfo()}");
@ -149,7 +148,7 @@ namespace Flow.Launcher
// 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 // TODO: Clean ThemeManager.Instance in future
ThemeManager.Instance.ChangeTheme(_settings.Theme); Ioc.Default.GetRequiredService<Theme>().ChangeTheme();
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
@ -164,6 +163,8 @@ namespace Flow.Launcher
}); });
} }
#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,8 +187,7 @@ 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);
} }
} }
} }

View file

@ -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" />

View file

@ -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;
}
}
}

View file

@ -2,19 +2,16 @@
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 static readonly int MAX_CACHE_SIZE = 3; private static readonly int MAX_CACHE_SIZE = 3;
private static readonly Dictionary<(string, DateTime), ImageBrush> wallpaperCache = new(); private static readonly Dictionary<(string, DateTime), ImageBrush> wallpaperCache = new();
@ -29,7 +26,7 @@ public static class WallpaperPathRetrieval
try try
{ {
var wallpaperPath = GetWallpaperPath(); var wallpaperPath = Win32Helper.GetWallpaperPath();
if (wallpaperPath is not null && File.Exists(wallpaperPath)) if (wallpaperPath is not null && File.Exists(wallpaperPath))
{ {
// Since the wallpaper file name can be the same (TranscodedWallpaper), // Since the wallpaper file name can be the same (TranscodedWallpaper),
@ -78,17 +75,6 @@ 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", true);

View file

@ -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
}

View file

@ -214,13 +214,14 @@ namespace Flow.Launcher
HotkeyList.ItemsSource = KeysToDisplay; HotkeyList.ItemsSource = KeysToDisplay;
RefreshHotkeyInterface(Hotkey); // We should not call RefreshHotkeyInterface here because DependencyProperty is not set yet
// And it will be called in OnHotkeyChanged event or Hotkey setter later
} }
private void RefreshHotkeyInterface(string hotkey) private void RefreshHotkeyInterface(string hotkey)
{ {
SetKeysToDisplay(new HotkeyModel(Hotkey)); SetKeysToDisplay(new HotkeyModel(hotkey));
CurrentHotkey = new HotkeyModel(Hotkey); CurrentHotkey = new HotkeyModel(hotkey);
} }
private static bool CheckHotkeyAvailability(HotkeyModel hotkey, bool validateKeyGesture) => private static bool CheckHotkeyAvailability(HotkeyModel hotkey, bool validateKeyGesture) =>

View file

@ -108,6 +108,11 @@
<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="BackdropType">Backdrop Type</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>
<!-- Setting Plugin --> <!-- Setting Plugin -->
<system:String x:Key="searchplugin">Search Plugin</system:String> <system:String x:Key="searchplugin">Search Plugin</system:String>
@ -342,9 +347,10 @@
<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>
<!-- 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>

View file

@ -20,17 +20,17 @@
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}" 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"
@ -240,14 +240,14 @@
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=OneWay}" Text="{Binding QueryText, Mode=OneWay}"
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">

File diff suppressed because it is too large Load diff

View file

@ -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);
} }
}); });
} }
} }
} }

View file

@ -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

View file

@ -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);
@ -329,7 +331,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);

View file

@ -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}">
&#xe819; &#xe819;
</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}"

View file

@ -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>

View file

@ -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>

View file

@ -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" />

View file

@ -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;
@ -22,45 +22,68 @@ public partial class SettingsPaneThemeViewModel : BaseModel
{ {
private const string DefaultFont = "Segoe UI"; private const string DefaultFont = "Segoe UI";
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://flowlauncher.com/docs/#/how-to-create-a-theme";
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 +117,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 +210,31 @@ 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;
_ = _theme.SetBlurForWindowAsync();
OnPropertyChanged(nameof(IsDropShadowEnabled));
}
}
public bool UseSound public bool UseSound
{ {
get => Settings.UseSound; get => Settings.UseSound;
@ -219,37 +280,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 +342,7 @@ public partial class SettingsPaneThemeViewModel : BaseModel
set set
{ {
Settings.QueryBoxFont = value.ToString(); Settings.QueryBoxFont = value.ToString();
ThemeManager.Instance.ChangeTheme(Settings.Theme); _theme.ChangeTheme();
} }
} }
@ -303,7 +364,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.ChangeTheme();
} }
} }
@ -325,7 +386,7 @@ public partial class SettingsPaneThemeViewModel : BaseModel
set set
{ {
Settings.ResultFont = value.ToString(); Settings.ResultFont = value.ToString();
ThemeManager.Instance.ChangeTheme(Settings.Theme); _theme.ChangeTheme();
} }
} }
@ -347,7 +408,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.ChangeTheme();
} }
} }
@ -355,9 +416,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 +432,7 @@ public partial class SettingsPaneThemeViewModel : BaseModel
set set
{ {
Settings.ResultSubFont = value.ToString(); Settings.ResultSubFont = value.ToString();
ThemeManager.Instance.ChangeTheme(Settings.Theme); _theme.ChangeTheme();
} }
} }
@ -392,34 +453,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.ChangeTheme();
} }
} }
public string ThemeImage => Constant.QueryTextBoxIconImagePath; public string ThemeImage => Constant.QueryTextBoxIconImagePath;
public SettingsPaneThemeViewModel(Settings settings)
{
Settings = settings;
}
[RelayCommand] [RelayCommand]
private void OpenThemesFolder() private void OpenThemesFolder()
{ {
App.API.OpenDirectory(Path.Combine(DataLocation.DataDirectory(), Constant.Themes)); 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)
{
Settings = settings;
}
[RelayCommand] [RelayCommand]
public void Reset() public void Reset()
{ {

View file

@ -303,7 +303,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 +370,6 @@
</DockPanel> </DockPanel>
</Border> </Border>
</Grid> </Grid>
<!-- Drop shadow effect -->
<cc:Card
Title="{DynamicResource queryWindowShadowEffect}"
Margin="0 8 0 0"
Icon="&#xeb91;"
Sub="{DynamicResource shadowEffectCPUUsage}">
<ui:ToggleSwitch
IsOn="{Binding DropShadowEffect}"
OffContent="{DynamicResource disable}"
OnContent="{DynamicResource enable}" />
</cc:Card>
<!-- Theme --> <!-- Theme -->
<cc:ExCard <cc:ExCard
@ -469,12 +458,46 @@
</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="&#xeb42;">
<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="&#xeb91;">
<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 Height -->
<cc:CardGroup Margin="0 20 0 0"> <cc:CardGroup Margin="0 20 0 0">
<cc:Card <cc:Card
@ -667,9 +690,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 -->

View file

@ -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();
}
} }

View file

@ -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;
} }

View file

@ -107,7 +107,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 +165,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 -->
@ -557,6 +534,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"

View file

@ -12,6 +12,9 @@
</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>
<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" />
@ -139,8 +142,8 @@
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 x:Key="ItemHotkeyStyle" TargetType="{x:Type TextBlock}">

View file

@ -10,8 +10,11 @@
<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>
<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,8 +138,8 @@
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 x:Key="ItemHotkeyStyle" TargetType="{x:Type TextBlock}">

View file

@ -10,14 +10,24 @@
<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>
<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 +43,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 +88,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 +100,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 +124,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,8 +144,8 @@
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 x:Key="ItemHotkeyStyle" TargetType="{x:Type TextBlock}">

View file

@ -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}"
@ -23,7 +27,7 @@
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 4 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>
@ -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>

View file

@ -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"

View file

@ -2,46 +2,45 @@
<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="Background" Value="#1f1d1f" />
<Setter Property="Foreground" Value="#E9819F" /> <Setter Property="Foreground" Value="#71114b" />
</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 +50,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 +86,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 +109,53 @@
<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 x:Key="ItemHotkeyStyle" TargetType="{x:Type TextBlock}">
<Setter Property="FontSize" Value="11" /> <Setter Property="FontSize" Value="14" />
<Setter Property="Foreground" Value="#E9819F" /> <Setter Property="Foreground" Value="#ed92c9" />
</Style> </Style>
<Style x:Key="ItemHotkeySelectedStyle" TargetType="{x:Type TextBlock}"> <Style x:Key="ItemHotkeySelectedStyle" TargetType="{x:Type TextBlock}">
<Setter Property="FontSize" Value="11" /> <Setter Property="FontSize" Value="14" />
<Setter Property="Foreground" Value="#E9819F" /> <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>

View file

@ -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 4 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 4 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"

View file

@ -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"
@ -175,13 +179,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"

View file

@ -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}"

View file

@ -1,31 +1,29 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Windows.Input;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Media;
using System.Windows.Threading;
using CommunityToolkit.Mvvm.DependencyInjection;
using CommunityToolkit.Mvvm.Input;
using Flow.Launcher.Core.Plugin; using Flow.Launcher.Core.Plugin;
using Flow.Launcher.Core.Resource;
using Flow.Launcher.Helper;
using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Hotkey; using Flow.Launcher.Infrastructure.Hotkey;
using Flow.Launcher.Infrastructure.Image;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.Storage; using Flow.Launcher.Infrastructure.Storage;
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 Flow.Launcher.Storage; using Flow.Launcher.Storage;
using Flow.Launcher.Infrastructure.Logger;
using Microsoft.VisualStudio.Threading; using Microsoft.VisualStudio.Threading;
using System.Text;
using System.Threading.Channels;
using ISavable = Flow.Launcher.Plugin.ISavable;
using CommunityToolkit.Mvvm.Input;
using System.Globalization;
using System.Windows.Input;
using System.ComponentModel;
using Flow.Launcher.Infrastructure.Image;
using System.Windows.Media;
using CommunityToolkit.Mvvm.DependencyInjection;
namespace Flow.Launcher.ViewModel namespace Flow.Launcher.ViewModel
{ {
@ -48,8 +46,6 @@ namespace Flow.Launcher.ViewModel
private CancellationTokenSource _updateSource; private CancellationTokenSource _updateSource;
private CancellationToken _updateToken; private CancellationToken _updateToken;
private readonly Internationalization _translator = InternationalizationManager.Instance;
private ChannelWriter<ResultsForUpdate> _resultsUpdateChannelWriter; private ChannelWriter<ResultsForUpdate> _resultsUpdateChannelWriter;
private Task _resultsViewUpdateTask; private Task _resultsViewUpdateTask;
@ -141,7 +137,6 @@ namespace Flow.Launcher.ViewModel
_userSelectedRecord = _userSelectedRecordStorage.Load(); _userSelectedRecord = _userSelectedRecordStorage.Load();
_topMostRecord = _topMostRecordStorage.Load(); _topMostRecord = _topMostRecordStorage.Load();
ContextMenu = new ResultsViewModel(Settings) ContextMenu = new ResultsViewModel(Settings)
{ {
LeftClickResultCommand = OpenResultCommand, LeftClickResultCommand = OpenResultCommand,
@ -182,9 +177,9 @@ namespace Flow.Launcher.ViewModel
var resultUpdateChannel = Channel.CreateUnbounded<ResultsForUpdate>(); var resultUpdateChannel = Channel.CreateUnbounded<ResultsForUpdate>();
_resultsUpdateChannelWriter = resultUpdateChannel.Writer; _resultsUpdateChannelWriter = resultUpdateChannel.Writer;
_resultsViewUpdateTask = _resultsViewUpdateTask =
Task.Run(updateAction).ContinueWith(continueAction, TaskContinuationOptions.OnlyOnFaulted); Task.Run(UpdateActionAsync).ContinueWith(continueAction, CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default);
async Task updateAction() async Task UpdateActionAsync()
{ {
var queue = new Dictionary<string, ResultsForUpdate>(); var queue = new Dictionary<string, ResultsForUpdate>();
var channelReader = resultUpdateChannel.Reader; var channelReader = resultUpdateChannel.Reader;
@ -213,7 +208,7 @@ namespace Flow.Launcher.ViewModel
#else #else
Log.Error($"Error happen in task dealing with viewupdate for results. {t.Exception}"); Log.Error($"Error happen in task dealing with viewupdate for results. {t.Exception}");
_resultsViewUpdateTask = _resultsViewUpdateTask =
Task.Run(updateAction).ContinueWith(continueAction, TaskContinuationOptions.OnlyOnFaulted); Task.Run(UpdateActionAsync).ContinueWith(continueAction, CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default);
#endif #endif
} }
} }
@ -245,14 +240,27 @@ namespace Flow.Launcher.ViewModel
} }
} }
private async Task RegisterClockAndDateUpdateAsync()
{
var timer = new PeriodicTimer(TimeSpan.FromSeconds(1));
// ReSharper disable once MethodSupportsCancellation
while (await timer.WaitForNextTickAsync().ConfigureAwait(false))
{
if (Settings.UseClock)
ClockText = DateTime.Now.ToString(Settings.TimeFormat, CultureInfo.CurrentCulture);
if (Settings.UseDate)
DateText = DateTime.Now.ToString(Settings.DateFormat, CultureInfo.CurrentCulture);
}
}
[RelayCommand] [RelayCommand]
private async Task ReloadPluginDataAsync() private async Task ReloadPluginDataAsync()
{ {
Hide(); Hide();
await PluginManager.ReloadDataAsync().ConfigureAwait(false); await PluginManager.ReloadDataAsync().ConfigureAwait(false);
Notification.Show(InternationalizationManager.Instance.GetTranslation("success"), Notification.Show(App.API.GetTranslation("success"),
InternationalizationManager.Instance.GetTranslation("completedSuccessfully")); App.API.GetTranslation("completedSuccessfully"));
} }
[RelayCommand] [RelayCommand]
@ -274,14 +282,14 @@ namespace Flow.Launcher.ViewModel
{ {
if (SelectedIsFromQueryResults()) if (SelectedIsFromQueryResults())
{ {
QueryResults(null, isReQuery: true); _ = QueryResultsAsync(null, isReQuery: true);
} }
} }
public void ReQuery(bool reselect) public void ReQuery(bool reselect)
{ {
BackToQueryResults(); BackToQueryResults();
QueryResults(null, isReQuery: true, reSelect: reselect); _ = QueryResultsAsync(null, isReQuery: true, reSelect: reselect);
} }
[RelayCommand] [RelayCommand]
@ -289,7 +297,7 @@ namespace Flow.Launcher.ViewModel
{ {
if (_history.Items.Count > 0) if (_history.Items.Count > 0)
{ {
ChangeQueryText(_history.Items[_history.Items.Count - lastHistoryIndex].Query.ToString()); ChangeQueryText(_history.Items[^lastHistoryIndex].Query.ToString());
if (lastHistoryIndex < _history.Items.Count) if (lastHistoryIndex < _history.Items.Count)
{ {
lastHistoryIndex++; lastHistoryIndex++;
@ -302,7 +310,7 @@ namespace Flow.Launcher.ViewModel
{ {
if (_history.Items.Count > 0) if (_history.Items.Count > 0)
{ {
ChangeQueryText(_history.Items[_history.Items.Count - lastHistoryIndex].Query.ToString()); ChangeQueryText(_history.Items[^lastHistoryIndex].Query.ToString());
if (lastHistoryIndex > 1) if (lastHistoryIndex > 1)
{ {
lastHistoryIndex--; lastHistoryIndex--;
@ -353,12 +361,12 @@ namespace Flow.Launcher.ViewModel
} }
else if (!string.IsNullOrEmpty(SelectedResults.SelectedItem?.QuerySuggestionText)) else if (!string.IsNullOrEmpty(SelectedResults.SelectedItem?.QuerySuggestionText))
{ {
var defaultSuggestion = SelectedResults.SelectedItem.QuerySuggestionText; //var defaultSuggestion = SelectedResults.SelectedItem.QuerySuggestionText;
// check if result.actionkeywordassigned is empty //// check if result.actionkeywordassigned is empty
if (!string.IsNullOrEmpty(result.ActionKeywordAssigned)) //if (!string.IsNullOrEmpty(result.ActionKeywordAssigned))
{ //{
autoCompleteText = $"{result.ActionKeywordAssigned} {defaultSuggestion}"; // autoCompleteText = $"{result.ActionKeywordAssigned} {defaultSuggestion}";
} //}
autoCompleteText = SelectedResults.SelectedItem.QuerySuggestionText; autoCompleteText = SelectedResults.SelectedItem.QuerySuggestionText;
} }
@ -389,11 +397,11 @@ namespace Flow.Launcher.ViewModel
} }
var hideWindow = await result.ExecuteAsync(new ActionContext var hideWindow = await result.ExecuteAsync(new ActionContext
{ {
// not null means pressing modifier key + number, should ignore the modifier key // not null means pressing modifier key + number, should ignore the modifier key
SpecialKeyState = index is not null ? SpecialKeyState.Default : GlobalHotkey.CheckModifiers() SpecialKeyState = index is not null ? SpecialKeyState.Default : GlobalHotkey.CheckModifiers()
}) })
.ConfigureAwait(false); .ConfigureAwait(false);
if (SelectedIsFromQueryResults()) if (SelectedIsFromQueryResults())
{ {
@ -457,7 +465,6 @@ namespace Flow.Launcher.ViewModel
SelectedResults.SelectLastResult(); SelectedResults.SelectLastResult();
} }
[RelayCommand] [RelayCommand]
private void SelectPrevPage() private void SelectPrevPage()
{ {
@ -484,7 +491,6 @@ namespace Flow.Launcher.ViewModel
{ {
SelectedResults.SelectPrevResult(); SelectedResults.SelectPrevResult();
} }
} }
[RelayCommand] [RelayCommand]
@ -539,19 +545,6 @@ namespace Flow.Launcher.ViewModel
public string ClockText { get; private set; } public string ClockText { get; private set; }
public string DateText { get; private set; } public string DateText { get; private set; }
private async Task RegisterClockAndDateUpdateAsync()
{
var timer = new PeriodicTimer(TimeSpan.FromSeconds(1));
// ReSharper disable once MethodSupportsCancellation
while (await timer.WaitForNextTickAsync().ConfigureAwait(false))
{
if (Settings.UseClock)
ClockText = DateTime.Now.ToString(Settings.TimeFormat, CultureInfo.CurrentCulture);
if (Settings.UseDate)
DateText = DateTime.Now.ToString(Settings.DateFormat, CultureInfo.CurrentCulture);
}
}
public ResultsViewModel Results { get; private set; } public ResultsViewModel Results { get; private set; }
public ResultsViewModel ContextMenu { get; private set; } public ResultsViewModel ContextMenu { get; private set; }
@ -561,7 +554,6 @@ namespace Flow.Launcher.ViewModel
public bool GameModeStatus { get; set; } = false; public bool GameModeStatus { get; set; } = false;
private string _queryText; private string _queryText;
public string QueryText public string QueryText
{ {
get => _queryText; get => _queryText;
@ -683,7 +675,6 @@ namespace Flow.Launcher.ViewModel
Results.Visibility = Visibility.Collapsed; Results.Visibility = Visibility.Collapsed;
_queryTextBeforeLeaveResults = QueryText; _queryTextBeforeLeaveResults = QueryText;
// Because of Fody's optimization // Because of Fody's optimization
// setter won't be called when property value is not changed. // setter won't be called when property value is not changed.
// so we need manually call Query() // so we need manually call Query()
@ -761,7 +752,7 @@ namespace Flow.Launcher.ViewModel
public string OpenResultCommandModifiers => Settings.OpenResultModifiers; public string OpenResultCommandModifiers => Settings.OpenResultModifiers;
public string VerifyOrSetDefaultHotkey(string hotkey, string defaultHotkey) private static string VerifyOrSetDefaultHotkey(string hotkey, string defaultHotkey)
{ {
try try
{ {
@ -790,9 +781,6 @@ namespace Flow.Launcher.ViewModel
public string CycleHistoryUpHotkey => VerifyOrSetDefaultHotkey(Settings.CycleHistoryUpHotkey, "Alt+Up"); public string CycleHistoryUpHotkey => VerifyOrSetDefaultHotkey(Settings.CycleHistoryUpHotkey, "Alt+Up");
public string CycleHistoryDownHotkey => VerifyOrSetDefaultHotkey(Settings.CycleHistoryDownHotkey, "Alt+Down"); public string CycleHistoryDownHotkey => VerifyOrSetDefaultHotkey(Settings.CycleHistoryDownHotkey, "Alt+Down");
public string Image => Constant.QueryTextBoxIconImagePath;
public bool StartWithEnglishMode => Settings.AlwaysStartEn; public bool StartWithEnglishMode => Settings.AlwaysStartEn;
#endregion #endregion
@ -812,8 +800,8 @@ namespace Flow.Launcher.ViewModel
throw new NotImplementedException("ResultAreaColumn should match ResultAreaColumnPreviewShown/ResultAreaColumnPreviewHidden value"); throw new NotImplementedException("ResultAreaColumn should match ResultAreaColumnPreviewShown/ResultAreaColumnPreviewHidden value");
#else #else
Log.Error("MainViewModel", "ResultAreaColumnPreviewHidden/ResultAreaColumnPreviewShown int value not implemented", "InternalPreviewVisible"); Log.Error("MainViewModel", "ResultAreaColumnPreviewHidden/ResultAreaColumnPreviewShown int value not implemented", "InternalPreviewVisible");
#endif
return false; return false;
#endif
} }
} }
@ -877,18 +865,6 @@ namespace Flow.Launcher.ViewModel
} }
} }
private void ToggleInternalPreview()
{
if (!InternalPreviewVisible)
{
ShowInternalPreview();
}
else
{
HideInternalPreview();
}
}
private void OpenExternalPreview(string path, bool sendFailToast = true) private void OpenExternalPreview(string path, bool sendFailToast = true)
{ {
_ = PluginManager.OpenExternalPreviewAsync(path, sendFailToast).ConfigureAwait(false); _ = PluginManager.OpenExternalPreviewAsync(path, sendFailToast).ConfigureAwait(false);
@ -901,7 +877,7 @@ namespace Flow.Launcher.ViewModel
ExternalPreviewVisible = false; ExternalPreviewVisible = false;
} }
private void SwitchExternalPreview(string path, bool sendFailToast = true) private static void SwitchExternalPreview(string path, bool sendFailToast = true)
{ {
_ = PluginManager.SwitchExternalPreviewAsync(path,sendFailToast).ConfigureAwait(false); _ = PluginManager.SwitchExternalPreviewAsync(path,sendFailToast).ConfigureAwait(false);
} }
@ -983,7 +959,7 @@ namespace Flow.Launcher.ViewModel
{ {
if (SelectedIsFromQueryResults()) if (SelectedIsFromQueryResults())
{ {
QueryResults(searchDelay, isReQuery); _ = QueryResultsAsync(searchDelay, isReQuery);
} }
else if (ContextMenuSelected()) else if (ContextMenuSelected())
{ {
@ -1054,8 +1030,8 @@ namespace Flow.Launcher.ViewModel
var results = new List<Result>(); var results = new List<Result>();
foreach (var h in _history.Items) foreach (var h in _history.Items)
{ {
var title = _translator.GetTranslation("executeQuery"); var title = App.API.GetTranslation("executeQuery");
var time = _translator.GetTranslation("lastExecuteTime"); var time = App.API.GetTranslation("lastExecuteTime");
var result = new Result var result = new Result
{ {
Title = string.Format(title, h.Query), Title = string.Format(title, h.Query),
@ -1088,8 +1064,8 @@ namespace Flow.Launcher.ViewModel
} }
private readonly IReadOnlyList<Result> _emptyResult = new List<Result>(); private readonly IReadOnlyList<Result> _emptyResult = new List<Result>();
private async void QueryResults(int? searchDelay, bool isReQuery = false, bool reSelect = true) private async Task QueryResultsAsync(int? searchDelay, bool isReQuery = false, bool reSelect = true)
{ {
System.Diagnostics.Debug.WriteLine("!!!QueryResults"); System.Diagnostics.Debug.WriteLine("!!!QueryResults");
@ -1111,8 +1087,7 @@ namespace Flow.Launcher.ViewModel
var currentUpdateSource = new CancellationTokenSource(); var currentUpdateSource = new CancellationTokenSource();
_updateSource = currentUpdateSource; _updateSource = currentUpdateSource;
var currentCancellationToken = _updateSource.Token; _updateToken = _updateSource.Token;
_updateToken = currentCancellationToken;
ProgressBarVisibility = Visibility.Hidden; ProgressBarVisibility = Visibility.Hidden;
_isQueryRunning = true; _isQueryRunning = true;
@ -1120,7 +1095,7 @@ namespace Flow.Launcher.ViewModel
// Switch to ThreadPool thread // Switch to ThreadPool thread
await TaskScheduler.Default; await TaskScheduler.Default;
if (currentCancellationToken.IsCancellationRequested) if (_updateSource.Token.IsCancellationRequested)
return; return;
// Update the query's IsReQuery property to true if this is a re-query // Update the query's IsReQuery property to true if this is a re-query
@ -1146,24 +1121,23 @@ namespace Flow.Launcher.ViewModel
SearchIconVisibility = Visibility.Visible; SearchIconVisibility = Visibility.Visible;
} }
if (query.ActionKeyword == Plugin.Query.GlobalPluginWildcardSign) if (query.ActionKeyword == Plugin.Query.GlobalPluginWildcardSign)
{ {
// Wait 45 millisecond for query change in global query // Wait 45 millisecond for query change in global query
// if query changes, return so that it won't be calculated // if query changes, return so that it won't be calculated
await Task.Delay(45, currentCancellationToken); await Task.Delay(45, _updateSource.Token);
if (currentCancellationToken.IsCancellationRequested) if (_updateSource.Token.IsCancellationRequested)
return; return;
} }
_ = Task.Delay(200, currentCancellationToken).ContinueWith(_ => _ = Task.Delay(200, _updateSource.Token).ContinueWith(_ =>
{ {
// start the progress bar if query takes more than 200 ms and this is the current running query and it didn't finish yet // start the progress bar if query takes more than 200 ms and this is the current running query and it didn't finish yet
if (!currentCancellationToken.IsCancellationRequested && _isQueryRunning) if (!_updateSource.Token.IsCancellationRequested && _isQueryRunning)
{ {
ProgressBarVisibility = Visibility.Visible; ProgressBarVisibility = Visibility.Visible;
} }
}, currentCancellationToken, TaskContinuationOptions.NotOnCanceled, TaskScheduler.Default); }, _updateSource.Token, TaskContinuationOptions.NotOnCanceled, TaskScheduler.Default);
// plugins is ICollection, meaning LINQ will get the Count and preallocate Array // plugins is ICollection, meaning LINQ will get the Count and preallocate Array
@ -1207,7 +1181,7 @@ namespace Flow.Launcher.ViewModel
} }
System.Diagnostics.Debug.Write("\n"); System.Diagnostics.Debug.Write("\n");
} }
try try
{ {
// Check the code, WhenAll will translate all type of IEnumerable or Collection to Array, so make an array at first // Check the code, WhenAll will translate all type of IEnumerable or Collection to Array, so make an array at first
@ -1218,29 +1192,29 @@ namespace Flow.Launcher.ViewModel
// nothing to do here // nothing to do here
} }
if (currentCancellationToken.IsCancellationRequested) if (_updateSource.Token.IsCancellationRequested)
return; return;
// this should happen once after all queries are done so progress bar should continue // this should happen once after all queries are done so progress bar should continue
// until the end of all querying // until the end of all querying
_isQueryRunning = false; _isQueryRunning = false;
if (!currentCancellationToken.IsCancellationRequested) if (!_updateSource.Token.IsCancellationRequested)
{ {
// update to hidden if this is still the current query // update to hidden if this is still the current query
ProgressBarVisibility = Visibility.Hidden; ProgressBarVisibility = Visibility.Hidden;
} }
// Local function // Local function
async Task QueryTask(PluginPair plugin, bool reSelect = true) async Task QueryTaskAsync(PluginPair plugin, bool reSelect = true)
{ {
// Since it is wrapped within a ThreadPool Thread, the synchronous context is null // Since it is wrapped within a ThreadPool Thread, the synchronous context is null
// Task.Yield will force it to run in ThreadPool // Task.Yield will force it to run in ThreadPool
await Task.Yield(); await Task.Yield();
IReadOnlyList<Result> results = IReadOnlyList<Result> results =
await PluginManager.QueryForPluginAsync(plugin, query, currentCancellationToken); await PluginManager.QueryForPluginAsync(plugin, query, _updateSource.Token);
currentCancellationToken.ThrowIfCancellationRequested(); _updateSource.Token.ThrowIfCancellationRequested();
IReadOnlyList<Result> resultsCopy; IReadOnlyList<Result> resultsCopy;
if (results == null) if (results == null)
@ -1254,7 +1228,7 @@ namespace Flow.Launcher.ViewModel
} }
if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(resultsCopy, plugin.Metadata, query, if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(resultsCopy, plugin.Metadata, query,
currentCancellationToken, reSelect))) _updateSource.Token, reSelect)))
{ {
Log.Error("MainViewModel", "Unable to add item to Result Update Queue"); Log.Error("MainViewModel", "Unable to add item to Result Update Queue");
} }
@ -1330,13 +1304,13 @@ namespace Flow.Launcher.ViewModel
{ {
menu = new Result menu = new Result
{ {
Title = InternationalizationManager.Instance.GetTranslation("cancelTopMostInThisQuery"), Title = App.API.GetTranslation("cancelTopMostInThisQuery"),
IcoPath = "Images\\down.png", IcoPath = "Images\\down.png",
PluginDirectory = Constant.ProgramDirectory, PluginDirectory = Constant.ProgramDirectory,
Action = _ => Action = _ =>
{ {
_topMostRecord.Remove(result); _topMostRecord.Remove(result);
App.API.ShowMsg(InternationalizationManager.Instance.GetTranslation("success")); App.API.ShowMsg(App.API.GetTranslation("success"));
App.API.ReQuery(); App.API.ReQuery();
return false; return false;
} }
@ -1346,14 +1320,14 @@ namespace Flow.Launcher.ViewModel
{ {
menu = new Result menu = new Result
{ {
Title = InternationalizationManager.Instance.GetTranslation("setAsTopMostInThisQuery"), Title = App.API.GetTranslation("setAsTopMostInThisQuery"),
IcoPath = "Images\\up.png", IcoPath = "Images\\up.png",
Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\xeac2"), Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\xeac2"),
PluginDirectory = Constant.ProgramDirectory, PluginDirectory = Constant.ProgramDirectory,
Action = _ => Action = _ =>
{ {
_topMostRecord.AddOrUpdate(result); _topMostRecord.AddOrUpdate(result);
App.API.ShowMsg(InternationalizationManager.Instance.GetTranslation("success")); App.API.ShowMsg(App.API.GetTranslation("success"));
App.API.ReQuery(); App.API.ReQuery();
return false; return false;
} }
@ -1363,10 +1337,10 @@ namespace Flow.Launcher.ViewModel
return menu; return menu;
} }
private Result ContextMenuPluginInfo(string id) private static Result ContextMenuPluginInfo(string id)
{ {
var metadata = PluginManager.GetPluginForId(id).Metadata; var metadata = PluginManager.GetPluginForId(id).Metadata;
var translator = InternationalizationManager.Instance; var translator = App.API;
var author = translator.GetTranslation("author"); var author = translator.GetTranslation("author");
var website = translator.GetTranslation("website"); var website = translator.GetTranslation("website");
@ -1429,70 +1403,108 @@ namespace Flow.Launcher.ViewModel
{ {
Application.Current.Dispatcher.Invoke(() => Application.Current.Dispatcher.Invoke(() =>
{ {
if (Application.Current.MainWindow is MainWindow mainWindow)
{
// 📌 Remove DWM Cloak (Make the window visible normally)
Win32Helper.DWMSetCloakForWindow(mainWindow, false);
// 📌 Restore UI elements
mainWindow.ClockPanel.Visibility = Visibility.Visible;
//mainWindow.SearchIcon.Visibility = Visibility.Visible;
SearchIconVisibility = Visibility.Visible;
}
// Update WPF properties
MainWindowVisibility = Visibility.Visible; MainWindowVisibility = Visibility.Visible;
MainWindowOpacity = 1; MainWindowOpacity = 1;
MainWindowVisibilityStatus = true; MainWindowVisibilityStatus = true;
VisibilityChanged?.Invoke(this, new VisibilityChangedEventArgs { IsVisible = true }); VisibilityChanged?.Invoke(this, new VisibilityChangedEventArgs { IsVisible = true });
}); });
} }
#pragma warning disable VSTHRD100 // Avoid async void methods
public async void Hide() public async void Hide()
{ {
lastHistoryIndex = 1; lastHistoryIndex = 1;
// Trick for no delay
MainWindowOpacity = 0;
if (ExternalPreviewVisible) if (ExternalPreviewVisible)
{
CloseExternalPreview(); CloseExternalPreview();
}
if (!SelectedIsFromQueryResults()) if (!SelectedIsFromQueryResults())
{ {
SelectedResults = Results; SelectedResults = Results;
} }
// 📌 Immediately apply text reset + force UI update
if (Settings.LastQueryMode == LastQueryMode.Empty)
{
ChangeQueryText(string.Empty);
await Task.Delay(1); // Wait for one frame to ensure UI reflects changes
Application.Current.Dispatcher.Invoke(() =>
{
Application.Current.MainWindow.UpdateLayout(); // Force UI update
});
}
switch (Settings.LastQueryMode) switch (Settings.LastQueryMode)
{ {
case LastQueryMode.Empty:
ChangeQueryText(string.Empty);
await Task.Delay(100); //Time for change to opacity
break;
case LastQueryMode.Preserved: case LastQueryMode.Preserved:
if (Settings.UseAnimation)
await Task.Delay(100);
LastQuerySelected = true;
break;
case LastQueryMode.Selected: case LastQueryMode.Selected:
if (Settings.UseAnimation) LastQuerySelected = (Settings.LastQueryMode == LastQueryMode.Preserved);
await Task.Delay(100);
LastQuerySelected = false;
break; break;
case LastQueryMode.ActionKeywordPreserved or LastQueryMode.ActionKeywordSelected:
case LastQueryMode.ActionKeywordPreserved:
case LastQueryMode.ActionKeywordSelected:
var newQuery = _lastQuery.ActionKeyword; var newQuery = _lastQuery.ActionKeyword;
if (!string.IsNullOrEmpty(newQuery)) if (!string.IsNullOrEmpty(newQuery))
newQuery += " "; newQuery += " ";
ChangeQueryText(newQuery); ChangeQueryText(newQuery);
if (Settings.UseAnimation)
await Task.Delay(100);
if (Settings.LastQueryMode == LastQueryMode.ActionKeywordSelected) if (Settings.LastQueryMode == LastQueryMode.ActionKeywordSelected)
LastQuerySelected = false; LastQuerySelected = false;
break; break;
default:
throw new ArgumentException($"wrong LastQueryMode: <{Settings.LastQueryMode}>");
} }
if (Application.Current.MainWindow is MainWindow mainWindow)
{
// 📌 Set Opacity of icon and clock to 0 and apply Visibility.Hidden
Application.Current.Dispatcher.Invoke(() =>
{
mainWindow.ClockPanel.Opacity = 0;
mainWindow.SearchIcon.Opacity = 0;
mainWindow.ClockPanel.Visibility = Visibility.Hidden;
//mainWindow.SearchIcon.Visibility = Visibility.Hidden;
SearchIconVisibility = Visibility.Hidden;
// Force UI update
mainWindow.ClockPanel.UpdateLayout();
mainWindow.SearchIcon.UpdateLayout();
}, DispatcherPriority.Render);
// 📌 Apply DWM Cloak (Completely hide the window)
Win32Helper.DWMSetCloakForWindow(mainWindow, true);
}
await Task.Delay(50);
// Update WPF properties
//MainWindowOpacity = 0;
MainWindowVisibilityStatus = false; MainWindowVisibilityStatus = false;
MainWindowVisibility = Visibility.Collapsed; MainWindowVisibility = Visibility.Collapsed;
VisibilityChanged?.Invoke(this, new VisibilityChangedEventArgs { IsVisible = false }); VisibilityChanged?.Invoke(this, new VisibilityChangedEventArgs { IsVisible = false });
} }
#pragma warning restore VSTHRD100 // Avoid async void methods
/// <summary> /// <summary>
/// Checks if Flow Launcher should ignore any hotkeys /// Checks if Flow Launcher should ignore any hotkeys
/// </summary> /// </summary>
public bool ShouldIgnoreHotkeys() public bool ShouldIgnoreHotkeys()
{ {
return Settings.IgnoreHotkeysOnFullscreen && WindowsInteropHelper.IsWindowFullscreen() || GameModeStatus; return Settings.IgnoreHotkeysOnFullscreen && Win32Helper.IsForegroundWindowFullscreen() || GameModeStatus;
} }
#endregion #endregion

View file

@ -46,7 +46,6 @@ namespace Flow.Launcher.ViewModel
} }
} }
private async void LoadIconAsync() private async void LoadIconAsync()
{ {
Image = await ImageLoader.LoadAsync(PluginPair.Metadata.IcoPath); Image = await ImageLoader.LoadAsync(PluginPair.Metadata.IcoPath);
@ -119,7 +118,7 @@ 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 ? Visibility.Collapsed : Visibility.Visible;
public string InitilizaTime => PluginPair.Metadata.InitTime + "ms"; public string InitilizaTime => 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 => InternationalizationManager.Instance.GetTranslation("plugin_query_version") + " " + PluginPair.Metadata.Version;
@ -128,9 +127,8 @@ namespace Flow.Launcher.ViewModel
public int Priority => PluginPair.Metadata.Priority; public int Priority => PluginPair.Metadata.Priority;
public Infrastructure.UserSettings.Plugin PluginSettingsObject { get; set; } public Infrastructure.UserSettings.Plugin PluginSettingsObject { get; set; }
public void ChangeActionKeyword(string newActionKeyword, string oldActionKeyword) public void OnActionKeywordsChanged()
{ {
PluginManager.ReplaceActionKeyword(PluginPair.Metadata.ID, oldActionKeyword, newActionKeyword);
OnPropertyChanged(nameof(ActionKeywordsText)); OnPropertyChanged(nameof(ActionKeywordsText));
} }
@ -169,8 +167,6 @@ 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()
{ {

View file

@ -8,77 +8,87 @@
d:DesignWidth="500" d:DesignWidth="500"
DataContext="{Binding RelativeSource={RelativeSource Self}}" DataContext="{Binding RelativeSource={RelativeSource Self}}"
mc:Ignorable="d"> mc:Ignorable="d">
<Grid Margin="60,0,10,0"> <Grid Margin="{StaticResource SettingPanelMargin}">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<StackPanel Margin="0,10,0,10" Orientation="Vertical"> <StackPanel
<StackPanel Orientation="Horizontal"> Grid.Row="0"
<TextBlock Margin="10" Text="{DynamicResource flowlauncher_plugin_browserbookmark_loadBrowserFrom}" /> Margin="{StaticResource SettingPanelItemTopBottomMargin}"
<CheckBox Orientation="Horizontal">
Margin="0,0,15,0" <TextBlock
Content="Chrome" Margin="{StaticResource SettingPanelItemRightMargin}"
IsChecked="{Binding LoadChromeBookmark}" /> VerticalAlignment="Center"
<CheckBox Text="{DynamicResource flowlauncher_plugin_browserbookmark_loadBrowserFrom}" />
Margin="0,0,15,0" <CheckBox
Content="Edge" Margin="{StaticResource SettingPanelItemRightMargin}"
IsChecked="{Binding LoadEdgeBookmark}" /> Content="Chrome"
<CheckBox IsChecked="{Binding LoadChromeBookmark}" />
Margin="0,0,15,0" <CheckBox
Content="Firefox" Margin="{StaticResource SettingPanelItemRightMargin}"
IsChecked="{Binding LoadFirefoxBookmark}" /> Content="Edge"
IsChecked="{Binding LoadEdgeBookmark}" />
<CheckBox
Margin="{StaticResource SettingPanelItemRightMargin}"
Content="Firefox"
IsChecked="{Binding LoadFirefoxBookmark}" />
<Button
Margin="{StaticResource SettingPanelItemRightMargin}"
Click="Others_Click"
Content="{DynamicResource flowlauncher_plugin_browserbookmark_others}" />
</StackPanel>
<StackPanel
Name="CustomBrowsersList"
Grid.Row="1"
Visibility="Collapsed">
<ListView
Name="CustomBrowsers"
Height="auto"
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
BorderBrush="DarkGray"
BorderThickness="1"
ItemsSource="{Binding Settings.CustomChromiumBrowsers}"
MouseDoubleClick="MouseDoubleClickOnSelectedCustomBrowser"
SelectedItem="{Binding SelectedCustomBrowser}"
Style="{StaticResource {x:Static GridView.GridViewStyleKey}}">
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding Name, Mode=OneWay}" Header="{DynamicResource flowlauncher_plugin_browserbookmark_browserName}" />
<GridViewColumn DisplayMemberBinding="{Binding DataDirectoryPath, Mode=OneWay}" Header="{DynamicResource flowlauncher_plugin_browserbookmark_browserBookmarkDataDirectory}" />
<GridViewColumn DisplayMemberBinding="{Binding BrowserType, Mode=OneWay}" Header="{DynamicResource flowlauncher_plugin_browserbookmark_browserEngine}" />
</GridView>
</ListView.View>
</ListView>
<StackPanel
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button <Button
Margin="0,0,15,0" Width="100"
Click="Others_Click" Click="NewCustomBrowser"
Content="{DynamicResource flowlauncher_plugin_browserbookmark_others}" /> Content="{DynamicResource flowlauncher_plugin_browserbookmark_addBrowserBookmark}" />
</StackPanel> <Button
<StackPanel Name="CustomBrowsersList" Visibility="Collapsed"> Width="100"
<ListView Margin="{StaticResource SettingPanelItemLeftMargin}"
Name="CustomBrowsers" Click="EditCustomBrowser"
Height="auto" Content="{DynamicResource flowlauncher_plugin_browserbookmark_editBrowserBookmark}">
Margin="10" <Button.Style>
BorderBrush="DarkGray" <Style BasedOn="{StaticResource DefaultButtonStyle}" TargetType="Button">
BorderThickness="1" <Setter Property="IsEnabled" Value="true" />
ItemsSource="{Binding Settings.CustomChromiumBrowsers}" <Style.Triggers>
MouseDoubleClick="MouseDoubleClickOnSelectedCustomBrowser" <DataTrigger Binding="{Binding ElementName=CustomBrowsers, Path=SelectedItems.Count}" Value="0">
SelectedItem="{Binding SelectedCustomBrowser}" <Setter Property="IsEnabled" Value="false" />
Style="{StaticResource {x:Static GridView.GridViewStyleKey}}"> </DataTrigger>
<ListView.View> </Style.Triggers>
<GridView> </Style>
<GridViewColumn DisplayMemberBinding="{Binding Name, Mode=OneWay}" Header="{DynamicResource flowlauncher_plugin_browserbookmark_browserName}" /> </Button.Style>
<GridViewColumn DisplayMemberBinding="{Binding DataDirectoryPath, Mode=OneWay}" Header="{DynamicResource flowlauncher_plugin_browserbookmark_browserBookmarkDataDirectory}" /> </Button>
<GridViewColumn DisplayMemberBinding="{Binding BrowserType, Mode=OneWay}" Header="{DynamicResource flowlauncher_plugin_browserbookmark_browserEngine}" /> <Button
</GridView> Width="100"
</ListView.View> Margin="{StaticResource SettingPanelItemLeftMargin}"
</ListView> Click="DeleteCustomBrowser"
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal"> Content="{DynamicResource flowlauncher_plugin_browserbookmark_removeBrowserBookmark}" />
<Button
MinWidth="130"
Margin="10"
Click="NewCustomBrowser"
Content="{DynamicResource flowlauncher_plugin_browserbookmark_addBrowserBookmark}" />
<Button
MinWidth="130"
Margin="10"
Click="EditCustomBrowser"
Content="{DynamicResource flowlauncher_plugin_browserbookmark_editBrowserBookmark}">
<Button.Style>
<Style BasedOn="{StaticResource DefaultButtonStyle}" TargetType="Button">
<Setter Property="IsEnabled" Value="true" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=CustomBrowsers, Path=SelectedItems.Count}" Value="0">
<Setter Property="IsEnabled" Value="false" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
<Button
MinWidth="120"
Margin="10"
Click="DeleteCustomBrowser"
Content="{DynamicResource flowlauncher_plugin_browserbookmark_removeBrowserBookmark}" />
</StackPanel>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</Grid> </Grid>

View file

@ -43,6 +43,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\Flow.Launcher.Core\Flow.Launcher.Core.csproj" /> <ProjectReference Include="..\..\Flow.Launcher.Core\Flow.Launcher.Core.csproj" />
<ProjectReference Include="..\..\Flow.Launcher.Plugin\Flow.Launcher.Plugin.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -155,16 +155,16 @@ namespace Flow.Launcher.Plugin.Calculator
return value.ToString(numberFormatInfo); return value.ToString(numberFormatInfo);
} }
private string GetDecimalSeparator() private static string GetDecimalSeparator()
{ {
string systemDecimalSeperator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator; string systemDecimalSeparator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
switch (_settings.DecimalSeparator) return _settings.DecimalSeparator switch
{ {
case DecimalSeparator.UseSystemLocale: return systemDecimalSeperator; DecimalSeparator.UseSystemLocale => systemDecimalSeparator,
case DecimalSeparator.Dot: return dot; DecimalSeparator.Dot => dot,
case DecimalSeparator.Comma: return comma; DecimalSeparator.Comma => comma,
default: return systemDecimalSeperator; _ => systemDecimalSeparator,
} };
} }
private bool IsBracketComplete(string query) private bool IsBracketComplete(string query)

View file

@ -17,7 +17,7 @@
<core:LocalizationConverter x:Key="LocalizationConverter" /> <core:LocalizationConverter x:Key="LocalizationConverter" />
</UserControl.Resources> </UserControl.Resources>
<Grid Margin="70,14,0,14"> <Grid Margin="{StaticResource SettingPanelMargin}">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="auto" /> <RowDefinition Height="auto" />
<RowDefinition Height="auto" /> <RowDefinition Height="auto" />
@ -30,7 +30,7 @@
<TextBlock <TextBlock
Grid.Row="0" Grid.Row="0"
Grid.Column="0" Grid.Column="0"
Margin="0,0,10,0" Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
VerticalAlignment="Center" VerticalAlignment="Center"
FontSize="14" FontSize="14"
Text="{DynamicResource flowlauncher_plugin_calculator_output_decimal_seperator}" /> Text="{DynamicResource flowlauncher_plugin_calculator_output_decimal_seperator}" />
@ -39,8 +39,9 @@
Grid.Row="0" Grid.Row="0"
Grid.Column="1" Grid.Column="1"
MaxWidth="300" MaxWidth="300"
Margin="10,5,0,5" Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Center"
ItemsSource="{Binding Source={ui:EnumBindingSource {x:Type calculator:DecimalSeparator}}}" ItemsSource="{Binding Source={ui:EnumBindingSource {x:Type calculator:DecimalSeparator}}}"
SelectedItem="{Binding Settings.DecimalSeparator}"> SelectedItem="{Binding Settings.DecimalSeparator}">
<ComboBox.ItemTemplate> <ComboBox.ItemTemplate>
@ -53,7 +54,7 @@
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
Margin="0,0,10,0" Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
VerticalAlignment="Center" VerticalAlignment="Center"
FontSize="14" FontSize="14"
Text="{DynamicResource flowlauncher_plugin_calculator_max_decimal_places}" /> Text="{DynamicResource flowlauncher_plugin_calculator_max_decimal_places}" />
@ -61,8 +62,9 @@
x:Name="MaxDecimalPlaces" x:Name="MaxDecimalPlaces"
Grid.Row="1" Grid.Row="1"
Grid.Column="1" Grid.Column="1"
Margin="10,5,0,5" Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Center"
ItemsSource="{Binding MaxDecimalPlacesRange}" ItemsSource="{Binding MaxDecimalPlacesRange}"
SelectedItem="{Binding Settings.MaxDecimalPlaces}" /> SelectedItem="{Binding Settings.MaxDecimalPlaces}" />

View file

@ -7,6 +7,7 @@
"*", "*",
"*" "*"
], ],
"HideActionKeywordPanel": true,
"Name": "Explorer", "Name": "Explorer",
"Description": "Find and manage files and folders via Windows Search or Everything", "Description": "Find and manage files and folders via Windows Search or Everything",
"Author": "Jeremy Wu", "Author": "Jeremy Wu",

View file

@ -19,7 +19,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj" /> <ProjectReference Include="..\..\Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj" />
<ProjectReference Include="..\..\Flow.Launcher.Core\Flow.Launcher.Core.csproj" /> <ProjectReference Include="..\..\Flow.Launcher.Core\Flow.Launcher.Core.csproj" />
<ProjectReference Include="..\..\Flow.Launcher.Plugin\Flow.Launcher.Plugin.csproj" /> <ProjectReference Include="..\..\Flow.Launcher.Plugin\Flow.Launcher.Plugin.csproj" />
</ItemGroup> </ItemGroup>

View file

@ -7,7 +7,7 @@
d:DesignHeight="450" d:DesignHeight="450"
d:DesignWidth="800" d:DesignWidth="800"
mc:Ignorable="d"> mc:Ignorable="d">
<Grid Margin="70 15 18 15"> <Grid Margin="{StaticResource SettingPanelMargin}">
<Grid.ColumnDefinitions /> <Grid.ColumnDefinitions />
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="auto" /> <RowDefinition Height="auto" />
@ -15,13 +15,12 @@
</Grid.RowDefinitions> </Grid.RowDefinitions>
<CheckBox <CheckBox
Grid.Row="0" Grid.Row="0"
Padding="8 0 0 0" Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
Content="{DynamicResource plugin_pluginsmanager_plugin_settings_unknown_source}" Content="{DynamicResource plugin_pluginsmanager_plugin_settings_unknown_source}"
IsChecked="{Binding WarnFromUnknownSource}" /> IsChecked="{Binding WarnFromUnknownSource}" />
<CheckBox <CheckBox
Grid.Row="1" Grid.Row="1"
Margin="0 10 0 0" Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
Padding="8 0 0 0"
Content="{DynamicResource plugin_pluginsmanager_plugin_settings_auto_restart}" Content="{DynamicResource plugin_pluginsmanager_plugin_settings_auto_restart}"
IsChecked="{Binding AutoRestartAfterChanging}" /> IsChecked="{Binding AutoRestartAfterChanging}" />
</Grid> </Grid>

View file

@ -10,124 +10,109 @@
<UserControl.Resources> <UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" /> <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</UserControl.Resources> </UserControl.Resources>
<Grid Margin="0"> <Grid Margin="{StaticResource SettingPanelMargin}">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<DockPanel <DockPanel HorizontalAlignment="Stretch" LastChildFill="True">
Margin="70 10 0 8"
HorizontalAlignment="Stretch"
LastChildFill="True">
<TextBlock <TextBlock
MinWidth="120" MinWidth="120"
Margin="0 5 10 0" Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
VerticalAlignment="Center"
Text="{DynamicResource flowlauncher_plugin_program_index_source}" /> Text="{DynamicResource flowlauncher_plugin_program_index_source}" />
<WrapPanel <WrapPanel
Width="Auto" Width="Auto"
Margin="0 0 14 0" Margin="{StaticResource SettingPanelItemTopBottomMargin}"
HorizontalAlignment="Right" HorizontalAlignment="Right"
DockPanel.Dock="Right"> DockPanel.Dock="Right">
<CheckBox <CheckBox
Name="UWPEnabled" Name="UWPEnabled"
Margin="12 0 12 0" Margin="{StaticResource SettingPanelItemRightMargin}"
Content="{DynamicResource flowlauncher_plugin_program_index_uwp}" Content="{DynamicResource flowlauncher_plugin_program_index_uwp}"
IsChecked="{Binding EnableUWP}" IsChecked="{Binding EnableUWP}"
ToolTip="{DynamicResource flowlauncher_plugin_program_index_uwp_tooltip}" ToolTip="{DynamicResource flowlauncher_plugin_program_index_uwp_tooltip}"
Visibility="{Binding ShowUWPCheckbox, Converter={StaticResource BooleanToVisibilityConverter}}" /> Visibility="{Binding ShowUWPCheckbox, Converter={StaticResource BooleanToVisibilityConverter}}" />
<CheckBox <CheckBox
Name="StartMenuEnabled" Name="StartMenuEnabled"
Margin="12 0 12 0" Margin="{StaticResource SettingPanelItemRightMargin}"
Content="{DynamicResource flowlauncher_plugin_program_index_start}" Content="{DynamicResource flowlauncher_plugin_program_index_start}"
IsChecked="{Binding EnableStartMenuSource}" IsChecked="{Binding EnableStartMenuSource}"
ToolTip="{DynamicResource flowlauncher_plugin_program_index_start_tooltip}" /> ToolTip="{DynamicResource flowlauncher_plugin_program_index_start_tooltip}" />
<CheckBox <CheckBox
Name="RegistryEnabled" Name="RegistryEnabled"
Margin="12 0 12 0" Margin="{StaticResource SettingPanelItemRightMargin}"
Content="{DynamicResource flowlauncher_plugin_program_index_registry}" Content="{DynamicResource flowlauncher_plugin_program_index_registry}"
IsChecked="{Binding EnableRegistrySource}" IsChecked="{Binding EnableRegistrySource}"
ToolTip="{DynamicResource flowlauncher_plugin_program_index_registry_tooltip}" /> ToolTip="{DynamicResource flowlauncher_plugin_program_index_registry_tooltip}" />
<CheckBox <CheckBox
Name="PATHEnabled" Name="PATHEnabled"
Margin="12 0 12 0"
Content="{DynamicResource flowlauncher_plugin_program_index_PATH}" Content="{DynamicResource flowlauncher_plugin_program_index_PATH}"
IsChecked="{Binding EnablePATHSource}" IsChecked="{Binding EnablePATHSource}"
ToolTip="{DynamicResource flowlauncher_plugin_program_index_PATH_tooltip}" /> ToolTip="{DynamicResource flowlauncher_plugin_program_index_PATH_tooltip}" />
</WrapPanel> </WrapPanel>
</DockPanel> </DockPanel>
<StackPanel <StackPanel
Grid.Row="1" Grid.Row="1"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Orientation="Vertical"> Orientation="Vertical">
<Separator <Separator Style="{StaticResource SettingPanelSeparatorStyle}" />
Height="1" <DockPanel HorizontalAlignment="Stretch" LastChildFill="True">
BorderBrush="{DynamicResource Color03B}"
BorderThickness="1" />
<DockPanel
Margin="70 10 0 8"
HorizontalAlignment="Stretch"
LastChildFill="True">
<TextBlock <TextBlock
MinWidth="120" MinWidth="120"
Margin="0 5 10 0" Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
VerticalAlignment="Center"
Text="{DynamicResource flowlauncher_plugin_program_index_option}" /> Text="{DynamicResource flowlauncher_plugin_program_index_option}" />
<WrapPanel <WrapPanel
Width="Auto" Width="Auto"
Margin="0 0 14 0"
HorizontalAlignment="Right" HorizontalAlignment="Right"
DockPanel.Dock="Right"> DockPanel.Dock="Right">
<CheckBox <CheckBox
Margin="12 0 12 0" Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
Content="{DynamicResource flowlauncher_plugin_program_enable_hidelnkpath}" Content="{DynamicResource flowlauncher_plugin_program_enable_hidelnkpath}"
IsChecked="{Binding HideAppsPath}" IsChecked="{Binding HideAppsPath}"
ToolTip="{DynamicResource flowlauncher_plugin_program_enable_hidelnkpath_tooltip}" /> ToolTip="{DynamicResource flowlauncher_plugin_program_enable_hidelnkpath_tooltip}" />
<CheckBox <CheckBox
Margin="12 0 12 0" Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
Content="{DynamicResource flowlauncher_plugin_program_enable_hideuninstallers}" Content="{DynamicResource flowlauncher_plugin_program_enable_hideuninstallers}"
IsChecked="{Binding HideUninstallers}" IsChecked="{Binding HideUninstallers}"
ToolTip="{DynamicResource flowlauncher_plugin_program_enable_hideuninstallers_tooltip}" /> ToolTip="{DynamicResource flowlauncher_plugin_program_enable_hideuninstallers_tooltip}" />
<CheckBox <CheckBox
Margin="12 0 12 0" Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
Content="{DynamicResource flowlauncher_plugin_program_enable_description}" Content="{DynamicResource flowlauncher_plugin_program_enable_description}"
IsChecked="{Binding EnableDescription}" IsChecked="{Binding EnableDescription}"
ToolTip="{DynamicResource flowlauncher_plugin_program_enable_description_tooltip}" /> ToolTip="{DynamicResource flowlauncher_plugin_program_enable_description_tooltip}" />
<CheckBox <CheckBox
Margin="12 0 12 0"
Content="{DynamicResource flowlauncher_plugin_program_enable_hideduplicatedwindowsapp}" Content="{DynamicResource flowlauncher_plugin_program_enable_hideduplicatedwindowsapp}"
IsChecked="{Binding HideDuplicatedWindowsApp}" IsChecked="{Binding HideDuplicatedWindowsApp}"
ToolTip="{DynamicResource flowlauncher_plugin_program_enable_hideduplicatedwindowsapp_tooltip}" /> ToolTip="{DynamicResource flowlauncher_plugin_program_enable_hideduplicatedwindowsapp_tooltip}" />
</WrapPanel> </WrapPanel>
</DockPanel> </DockPanel>
<Separator <Separator Style="{StaticResource SettingPanelSeparatorStyle}" />
Height="1"
BorderBrush="{DynamicResource Color03B}"
BorderThickness="1" />
<StackPanel <StackPanel
Margin="60 0 0 2"
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Center" VerticalAlignment="Center"
Orientation="Horizontal"> Orientation="Horizontal">
<Button <Button
x:Name="btnLoadAllProgramSource" x:Name="btnLoadAllProgramSource"
MinWidth="120" MinWidth="120"
Margin="10 10 5 10" Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
HorizontalAlignment="Right" HorizontalAlignment="Right"
Click="btnLoadAllProgramSource_OnClick" Click="btnLoadAllProgramSource_OnClick"
Content="{DynamicResource flowlauncher_plugin_program_all_programs}" /> Content="{DynamicResource flowlauncher_plugin_program_all_programs}" />
<Button <Button
x:Name="btnProgramSuffixes" x:Name="btnProgramSuffixes"
MinWidth="120" MinWidth="120"
Margin="5 10 5 10" Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
HorizontalAlignment="Right" HorizontalAlignment="Right"
Click="BtnProgramSuffixes_OnClick" Click="BtnProgramSuffixes_OnClick"
Content="{DynamicResource flowlauncher_plugin_program_suffixes}" /> Content="{DynamicResource flowlauncher_plugin_program_suffixes}" />
<Button <Button
x:Name="btnReindex" x:Name="btnReindex"
MinWidth="120" MinWidth="120"
Margin="5 10 5 10" Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
HorizontalAlignment="Right" HorizontalAlignment="Right"
Click="btnReindex_Click" Click="btnReindex_Click"
Content="{DynamicResource flowlauncher_plugin_program_reindex}" /> Content="{DynamicResource flowlauncher_plugin_program_reindex}" />
@ -140,12 +125,13 @@
x:Name="progressBarIndexing" x:Name="progressBarIndexing"
Width="80" Width="80"
Height="20" Height="20"
Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
IsIndeterminate="True" IsIndeterminate="True"
Maximum="100" Maximum="100"
Minimum="0" /> Minimum="0" />
<TextBlock <TextBlock
Height="20" Height="20"
Margin="10 0 0 0" Margin="{StaticResource SettingPanelItemTopBottomMargin}"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Text="{DynamicResource flowlauncher_plugin_program_indexing}" /> Text="{DynamicResource flowlauncher_plugin_program_indexing}" />
</StackPanel> </StackPanel>
@ -154,7 +140,7 @@
<ListView <ListView
x:Name="programSourceView" x:Name="programSourceView"
Grid.Row="2" Grid.Row="2"
Margin="70 0 20 0" Margin="{StaticResource SettingPanelItemTopBottomMargin}"
AllowDrop="True" AllowDrop="True"
BorderBrush="DarkGray" BorderBrush="DarkGray"
BorderThickness="1" BorderThickness="1"
@ -203,27 +189,24 @@
</GridView> </GridView>
</ListView.View> </ListView.View>
</ListView> </ListView>
<DockPanel <DockPanel Grid.Row="3">
Grid.Row="3"
Grid.RowSpan="1"
Margin="0 0 20 0">
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal"> <StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
<Button <Button
x:Name="btnProgramSourceStatus" x:Name="btnProgramSourceStatus"
MinWidth="100" MinWidth="100"
Margin="10" Margin="{StaticResource SettingPanelItemTopBottomMargin}"
Click="btnProgramSourceStatus_OnClick" Click="btnProgramSourceStatus_OnClick"
Content="{DynamicResource flowlauncher_plugin_program_disable}" /> Content="{DynamicResource flowlauncher_plugin_program_disable}" />
<Button <Button
x:Name="btnEditProgramSource" x:Name="btnEditProgramSource"
MinWidth="100" MinWidth="100"
Margin="10" Margin="{StaticResource SettingPanelItemLeftTopBottomMargin}"
Click="btnEditProgramSource_OnClick" Click="btnEditProgramSource_OnClick"
Content="{DynamicResource flowlauncher_plugin_program_edit}" /> Content="{DynamicResource flowlauncher_plugin_program_edit}" />
<Button <Button
x:Name="btnAddProgramSource" x:Name="btnAddProgramSource"
MinWidth="100" MinWidth="100"
Margin="10 10 0 10" Margin="{StaticResource SettingPanelItemLeftTopBottomMargin}"
Click="btnAddProgramSource_OnClick" Click="btnAddProgramSource_OnClick"
Content="{DynamicResource flowlauncher_plugin_program_add}" /> Content="{DynamicResource flowlauncher_plugin_program_add}" />
</StackPanel> </StackPanel>

View file

@ -8,7 +8,7 @@
d:DesignWidth="300" d:DesignWidth="300"
Loaded="CMDSetting_OnLoaded" Loaded="CMDSetting_OnLoaded"
mc:Ignorable="d"> mc:Ignorable="d">
<Grid Margin="60,10,10,20" VerticalAlignment="Top"> <Grid Margin="{StaticResource SettingPanelMargin}" VerticalAlignment="Top">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition /> <RowDefinition />
<RowDefinition /> <RowDefinition />
@ -21,37 +21,37 @@
<CheckBox <CheckBox
x:Name="ReplaceWinR" x:Name="ReplaceWinR"
Grid.Row="0" Grid.Row="0"
Margin="10,10,5,5" Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Content="{DynamicResource flowlauncher_plugin_cmd_relace_winr}" /> Content="{DynamicResource flowlauncher_plugin_cmd_relace_winr}" />
<CheckBox <CheckBox
x:Name="CloseShellAfterPress" x:Name="CloseShellAfterPress"
Grid.Row="1" Grid.Row="1"
Margin="10,5,5,5" Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Content="{DynamicResource flowlauncher_plugin_cmd_close_cmd_after_press}" /> Content="{DynamicResource flowlauncher_plugin_cmd_close_cmd_after_press}" />
<CheckBox <CheckBox
x:Name="LeaveShellOpen" x:Name="LeaveShellOpen"
Grid.Row="2" Grid.Row="2"
Margin="10,5,5,5" Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Content="{DynamicResource flowlauncher_plugin_cmd_leave_cmd_open}" /> Content="{DynamicResource flowlauncher_plugin_cmd_leave_cmd_open}" />
<CheckBox <CheckBox
x:Name="AlwaysRunAsAdministrator" x:Name="AlwaysRunAsAdministrator"
Grid.Row="3" Grid.Row="3"
Margin="10,5,5,5" Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Content="{DynamicResource flowlauncher_plugin_cmd_always_run_as_administrator}" /> Content="{DynamicResource flowlauncher_plugin_cmd_always_run_as_administrator}" />
<CheckBox <CheckBox
x:Name="UseWindowsTerminal" x:Name="UseWindowsTerminal"
Grid.Row="4" Grid.Row="4"
Margin="10,5,5,5" Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Content="{DynamicResource flowlauncher_plugin_cmd_use_windows_terminal}" /> Content="{DynamicResource flowlauncher_plugin_cmd_use_windows_terminal}" />
<ComboBox <ComboBox
x:Name="ShellComboBox" x:Name="ShellComboBox"
Grid.Row="5" Grid.Row="5"
Margin="10,5,5,5" Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
HorizontalAlignment="Left"> HorizontalAlignment="Left">
<ComboBoxItem>CMD</ComboBoxItem> <ComboBoxItem>CMD</ComboBoxItem>
<ComboBoxItem>PowerShell</ComboBoxItem> <ComboBoxItem>PowerShell</ComboBoxItem>
@ -61,11 +61,11 @@
<StackPanel Grid.Row="6" Orientation="Horizontal"> <StackPanel Grid.Row="6" Orientation="Horizontal">
<CheckBox <CheckBox
x:Name="ShowOnlyMostUsedCMDs" x:Name="ShowOnlyMostUsedCMDs"
Margin="10,5,5,5" Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
Content="{DynamicResource flowlauncher_plugin_cmd_history}" /> Content="{DynamicResource flowlauncher_plugin_cmd_history}" />
<ComboBox <ComboBox
x:Name="ShowOnlyMostUsedCMDsNumber" x:Name="ShowOnlyMostUsedCMDsNumber"
Margin="10,5,5,5" Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
HorizontalAlignment="Left" /> HorizontalAlignment="Left" />
</StackPanel> </StackPanel>
</Grid> </Grid>

View file

@ -9,7 +9,8 @@
d:DesignHeight="300" d:DesignHeight="300"
d:DesignWidth="300" d:DesignWidth="300"
mc:Ignorable="d"> mc:Ignorable="d">
<Grid Margin="70 18 18 18">
<Grid Margin="{StaticResource SettingPanelMargin}">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition /> <RowDefinition />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
@ -18,7 +19,7 @@
<ListView <ListView
x:Name="lbxCommands" x:Name="lbxCommands"
Grid.Row="0" Grid.Row="0"
Margin="0" Margin="{StaticResource SettingPanelItemTopBottomMargin}"
BorderBrush="DarkGray" BorderBrush="DarkGray"
BorderThickness="1" BorderThickness="1"
ItemsSource="{Binding Settings.Commands}" ItemsSource="{Binding Settings.Commands}"
@ -57,11 +58,12 @@
<StackPanel <StackPanel
Grid.Row="1" Grid.Row="1"
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
HorizontalAlignment="Right" HorizontalAlignment="Right"
Orientation="Horizontal"> Orientation="Horizontal">
<Button <Button
Width="100" Width="100"
Margin="10" Margin="{StaticResource SettingPanelItemLeftMargin}"
Click="OnEditCommandKeywordClick" Click="OnEditCommandKeywordClick"
Content="{DynamicResource flowlauncher_plugin_sys_edit}" /> Content="{DynamicResource flowlauncher_plugin_sys_edit}" />
</StackPanel> </StackPanel>

View file

@ -2,7 +2,6 @@
using System.Linq; using System.Linq;
using CommunityToolkit.Mvvm.DependencyInjection; using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Core.Resource; using Flow.Launcher.Core.Resource;
using FLSettings = Flow.Launcher.Infrastructure.UserSettings.Settings;
namespace Flow.Launcher.Plugin.Sys namespace Flow.Launcher.Plugin.Sys
{ {
@ -10,7 +9,6 @@ namespace Flow.Launcher.Plugin.Sys
{ {
public const string Keyword = "fltheme"; public const string Keyword = "fltheme";
private readonly FLSettings _settings;
private readonly Theme _theme; private readonly Theme _theme;
private readonly PluginInitContext _context; private readonly PluginInitContext _context;
@ -19,19 +17,15 @@ namespace Flow.Launcher.Plugin.Sys
// Theme select codes simplified from SettingsPaneThemeViewModel.cs // Theme select codes simplified from SettingsPaneThemeViewModel.cs
private Theme.ThemeData _selectedTheme; private Theme.ThemeData _selectedTheme;
private Theme.ThemeData SelectedTheme public Theme.ThemeData SelectedTheme
{ {
get => _selectedTheme ??= Themes.Find(v => v.FileNameWithoutExtension == _theme.CurrentTheme); get => _selectedTheme ??= Themes.Find(v => v.FileNameWithoutExtension == _theme.GetCurrentTheme());
set set
{ {
_selectedTheme = value; _selectedTheme = value;
_theme.ChangeTheme(value.FileNameWithoutExtension); _theme.ChangeTheme(value.FileNameWithoutExtension);
if (_theme.BlurEnabled && _settings.UseDropShadowEffect) _ = _theme.RefreshFrameAsync();
{
_theme.RemoveDropShadowEffectFromCurrentTheme();
_settings.UseDropShadowEffect = false;
}
} }
} }
@ -43,7 +37,6 @@ namespace Flow.Launcher.Plugin.Sys
{ {
_context = context; _context = context;
_theme = Ioc.Default.GetRequiredService<Theme>(); _theme = Ioc.Default.GetRequiredService<Theme>();
_settings = Ioc.Default.GetRequiredService<FLSettings>();
} }
public List<Result> Query(Query query) public List<Result> Query(Query query)

View file

@ -51,7 +51,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\Flow.Launcher.Core\Flow.Launcher.Core.csproj" />
<ProjectReference Include="..\..\Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj" /> <ProjectReference Include="..\..\Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj" />
<ProjectReference Include="..\..\Flow.Launcher.Plugin\Flow.Launcher.Plugin.csproj" /> <ProjectReference Include="..\..\Flow.Launcher.Plugin\Flow.Launcher.Plugin.csproj" />
</ItemGroup> </ItemGroup>

View file

@ -19,7 +19,7 @@
<system:String x:Key="flowlauncher_plugin_websearch_action_keyword">Action Keyword</system:String> <system:String x:Key="flowlauncher_plugin_websearch_action_keyword">Action Keyword</system:String>
<system:String x:Key="flowlauncher_plugin_websearch_url">URL</system:String> <system:String x:Key="flowlauncher_plugin_websearch_url">URL</system:String>
<system:String x:Key="flowlauncher_plugin_websearch_search">Search</system:String> <system:String x:Key="flowlauncher_plugin_websearch_search">Search</system:String>
<system:String x:Key="flowlauncher_plugin_websearch_enable_suggestion">Use Search Query Autocomplete:</system:String> <system:String x:Key="flowlauncher_plugin_websearch_enable_suggestion">Use Search Query Autocomplete</system:String>
<system:String x:Key="flowlauncher_plugin_websearch_enable_suggestion_provider">Autocomplete Data from:</system:String> <system:String x:Key="flowlauncher_plugin_websearch_enable_suggestion_provider">Autocomplete Data from:</system:String>
<system:String x:Key="flowlauncher_plugin_websearch_pls_select_web_search">Please select a web search</system:String> <system:String x:Key="flowlauncher_plugin_websearch_pls_select_web_search">Please select a web search</system:String>
<system:String x:Key="flowlauncher_plugin_websearch_delete_warning">Are you sure you want to delete {0}?</system:String> <system:String x:Key="flowlauncher_plugin_websearch_delete_warning">Are you sure you want to delete {0}?</system:String>

View file

@ -1,7 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Windows; using System.Windows;
using Microsoft.Win32; using Microsoft.Win32;
using Flow.Launcher.Core.Plugin;
namespace Flow.Launcher.Plugin.WebSearch namespace Flow.Launcher.Plugin.WebSearch
{ {
@ -16,7 +15,6 @@ namespace Flow.Launcher.Plugin.WebSearch
private SearchSourceViewModel _viewModel; private SearchSourceViewModel _viewModel;
private string selectedNewIconImageFullPath; private string selectedNewIconImageFullPath;
public SearchSourceSettingWindow(IList<SearchSource> sources, PluginInitContext context, SearchSource old) public SearchSourceSettingWindow(IList<SearchSource> sources, PluginInitContext context, SearchSource old)
{ {
_oldSearchSource = old; _oldSearchSource = old;
@ -80,10 +78,10 @@ namespace Flow.Launcher.Plugin.WebSearch
private void AddSearchSource() private void AddSearchSource()
{ {
var keyword = _searchSource.ActionKeyword; var keyword = _searchSource.ActionKeyword;
if (!PluginManager.ActionKeywordRegistered(keyword)) if (!_context.API.ActionKeywordAssigned(keyword))
{ {
var id = _context.CurrentPluginMetadata.ID; var id = _context.CurrentPluginMetadata.ID;
PluginManager.AddActionKeyword(id, keyword); _context.API.AddActionKeyword(id, keyword);
_searchSources.Add(_searchSource); _searchSources.Add(_searchSource);
@ -100,10 +98,11 @@ namespace Flow.Launcher.Plugin.WebSearch
{ {
var newKeyword = _searchSource.ActionKeyword; var newKeyword = _searchSource.ActionKeyword;
var oldKeyword = _oldSearchSource.ActionKeyword; var oldKeyword = _oldSearchSource.ActionKeyword;
if (!PluginManager.ActionKeywordRegistered(newKeyword) || oldKeyword == newKeyword) if (!_context.API.ActionKeywordAssigned(newKeyword) || oldKeyword == newKeyword)
{ {
var id = _context.CurrentPluginMetadata.ID; var id = _context.CurrentPluginMetadata.ID;
PluginManager.ReplaceActionKeyword(id, oldKeyword, newKeyword); _context.API.RemoveActionKeyword(id, oldKeyword);
_context.API.AddActionKeyword(id, newKeyword);
var index = _searchSources.IndexOf(_oldSearchSource); var index = _searchSources.IndexOf(_oldSearchSource);
_searchSources[index] = _searchSource; _searchSources[index] = _searchSource;

View file

@ -9,6 +9,7 @@
d:DesignHeight="300" d:DesignHeight="300"
d:DesignWidth="500" d:DesignWidth="500"
mc:Ignorable="d"> mc:Ignorable="d">
<UserControl.Resources> <UserControl.Resources>
<Style x:Key="BrowserPathBoxStyle" TargetType="TextBox"> <Style x:Key="BrowserPathBoxStyle" TargetType="TextBox">
<Setter Property="Height" Value="28" /> <Setter Property="Height" Value="28" />
@ -35,16 +36,19 @@
</DockPanel> </DockPanel>
</DataTemplate> </DataTemplate>
</UserControl.Resources> </UserControl.Resources>
<Grid Margin="0,4,0,0">
<Grid Margin="{StaticResource SettingPanelMargin}">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition /> <RowDefinition />
<RowDefinition Height="56" /> <RowDefinition Height="Auto" />
<RowDefinition Height="50" /> <RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<ListView <ListView
x:Name="SearchSourcesListView" x:Name="SearchSourcesListView"
Grid.Row="0" Grid.Row="0"
Margin="18,18,18,0" Margin="{StaticResource SettingPanelItemTopBottomMargin}"
BorderBrush="DarkGray" BorderBrush="DarkGray"
BorderThickness="1" BorderThickness="1"
GridViewColumnHeader.Click="SortByColumn" GridViewColumnHeader.Click="SortByColumn"
@ -60,7 +64,7 @@
<Image <Image
Width="20" Width="20"
Height="20" Height="20"
Margin="6,0,0,0" Margin="6 0 0 0"
Source="{Binding Path=IconPath}" /> Source="{Binding Path=IconPath}" />
</DataTemplate> </DataTemplate>
</GridViewColumn.CellTemplate> </GridViewColumn.CellTemplate>
@ -69,11 +73,11 @@
<GridViewColumn <GridViewColumn
Width="130" Width="130"
DisplayMemberBinding="{Binding ActionKeyword}" DisplayMemberBinding="{Binding ActionKeyword}"
Header="{DynamicResource flowlauncher_plugin_websearch_action_keyword}"/> Header="{DynamicResource flowlauncher_plugin_websearch_action_keyword}" />
<GridViewColumn <GridViewColumn
Width="350" Width="239"
DisplayMemberBinding="{Binding Title}" DisplayMemberBinding="{Binding Title}"
Header="{DynamicResource flowlauncher_plugin_websearch_title}"/> Header="{DynamicResource flowlauncher_plugin_websearch_title}" />
<GridViewColumn Width="140" Header="{DynamicResource flowlauncher_plugin_websearch_enable}"> <GridViewColumn Width="140" Header="{DynamicResource flowlauncher_plugin_websearch_enable}">
<GridViewColumn.CellTemplate> <GridViewColumn.CellTemplate>
<DataTemplate> <DataTemplate>
@ -95,59 +99,57 @@
</GridView> </GridView>
</ListView.View> </ListView.View>
</ListView> </ListView>
<StackPanel <StackPanel
Grid.Row="1" Grid.Row="1"
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
HorizontalAlignment="Right" HorizontalAlignment="Right"
Orientation="Horizontal"> Orientation="Horizontal">
<Button <Button
Width="100" Width="100"
Margin="10" Margin="{StaticResource SettingPanelItemLeftMargin}"
Click="OnDeleteSearchSearchClick" Click="OnDeleteSearchSearchClick"
Content="{DynamicResource flowlauncher_plugin_websearch_delete}" /> Content="{DynamicResource flowlauncher_plugin_websearch_delete}" />
<Button <Button
Width="100" Width="100"
Margin="10" Margin="{StaticResource SettingPanelItemLeftMargin}"
Click="OnEditSearchSourceClick" Click="OnEditSearchSourceClick"
Content="{DynamicResource flowlauncher_plugin_websearch_edit}" /> Content="{DynamicResource flowlauncher_plugin_websearch_edit}" />
<Button <Button
Width="100" Width="100"
Margin="10,10,18,10" Margin="{StaticResource SettingPanelItemLeftMargin}"
Click="OnAddSearchSearchClick" Click="OnAddSearchSearchClick"
Content="{DynamicResource flowlauncher_plugin_websearch_add}" /> Content="{DynamicResource flowlauncher_plugin_websearch_add}" />
</StackPanel> </StackPanel>
<Border
Grid.Row="2" <Separator Grid.Row="2" Style="{StaticResource SettingPanelSeparatorStyle}" />
Margin="0,0,0,0"
HorizontalAlignment="Stretch" <DockPanel
BorderBrush="{DynamicResource Color03B}" Grid.Row="3"
BorderThickness="0,1,0,0"> Margin="{StaticResource SettingPanelItemTopBottomMargin}"
<DockPanel HorizontalAlignment="Right"> HorizontalAlignment="Right">
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal"> <StackPanel DockPanel.Dock="Right" Orientation="Horizontal">
<Label <Label
Margin="14,0,10,0" HorizontalAlignment="Right"
HorizontalAlignment="Right" VerticalAlignment="Center"
VerticalAlignment="Center" Content="{DynamicResource flowlauncher_plugin_websearch_enable_suggestion_provider}" />
Content="{DynamicResource flowlauncher_plugin_websearch_enable_suggestion_provider}" /> <ComboBox
<ComboBox Height="30"
Height="30" Margin="{StaticResource SettingPanelItemLeftMargin}"
Margin="0,0,20,0" VerticalAlignment="Center"
VerticalAlignment="Center" FontSize="11"
FontSize="11" IsEnabled="{Binding ElementName=EnableSuggestion, Path=IsChecked}"
IsEnabled="{Binding ElementName=EnableSuggestion, Path=IsChecked}" ItemsSource="{Binding Settings.Suggestions}"
ItemsSource="{Binding Settings.Suggestions}" SelectedItem="{Binding Settings.SelectedSuggestion}" />
SelectedItem="{Binding Settings.SelectedSuggestion}" /> <CheckBox
<Label Name="EnableSuggestion"
Margin="0,0,10,0" Margin="{StaticResource SettingPanelItemLeftMargin}"
HorizontalAlignment="Right" HorizontalAlignment="Right"
VerticalAlignment="Center" VerticalAlignment="Center"
Content="{DynamicResource flowlauncher_plugin_websearch_enable_suggestion}" /> Content="{DynamicResource flowlauncher_plugin_websearch_enable_suggestion}"
<CheckBox IsChecked="{Binding Settings.EnableSuggestion}" />
Name="EnableSuggestion" </StackPanel>
Margin="0,0,8,0" <!-- Not sure why binding IsEnabled directly to Settings.EnableWebSearchSuggestion is not working -->
IsChecked="{Binding Settings.EnableSuggestion}" /> </DockPanel>
</StackPanel>
<!-- Not sure why binding IsEnabled directly to Settings.EnableWebSearchSuggestion is not working -->
</DockPanel>
</Border>
</Grid> </Grid>
</UserControl> </UserControl>

View file

@ -1,6 +1,5 @@
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using Flow.Launcher.Core.Plugin;
using System.ComponentModel; using System.ComponentModel;
using System.Windows.Data; using System.Windows.Data;
@ -40,7 +39,7 @@ namespace Flow.Launcher.Plugin.WebSearch
if (result == MessageBoxResult.Yes) if (result == MessageBoxResult.Yes)
{ {
var id = _context.CurrentPluginMetadata.ID; var id = _context.CurrentPluginMetadata.ID;
PluginManager.RemoveActionKeyword(id, selected.ActionKeyword); _context.API.RemoveActionKeyword(id, selected.ActionKeyword);
_settings.SearchSources.Remove(selected); _settings.SearchSources.Remove(selected);
} }
} }

View file

@ -23,6 +23,7 @@
"yahoo", "yahoo",
"bd" "bd"
], ],
"HideActionKeywordPanel": true,
"Name": "Web Searches", "Name": "Web Searches",
"Description": "Provide the web search ability", "Description": "Provide the web search ability",
"Author": "qianlifeng", "Author": "qianlifeng",