mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Merge branch 'dev' into search_delay
This commit is contained in:
commit
1e0118604c
63 changed files with 3358 additions and 2475 deletions
|
|
@ -1,18 +1,14 @@
|
|||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Forms;
|
||||
using Flow.Launcher.Infrastructure.Storage;
|
||||
using Flow.Launcher.Plugin;
|
||||
using CheckBox = System.Windows.Controls.CheckBox;
|
||||
using ComboBox = System.Windows.Controls.ComboBox;
|
||||
using Control = System.Windows.Controls.Control;
|
||||
using Orientation = System.Windows.Controls.Orientation;
|
||||
using TextBox = System.Windows.Controls.TextBox;
|
||||
using UserControl = System.Windows.Controls.UserControl;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Flow.Launcher.Core.Plugin
|
||||
{
|
||||
|
|
@ -23,51 +19,72 @@ namespace Flow.Launcher.Core.Plugin
|
|||
public required string SettingPath { get; init; }
|
||||
public Dictionary<string, FrameworkElement> SettingControls { get; } = new();
|
||||
|
||||
public IReadOnlyDictionary<string, object> Inner => Settings;
|
||||
protected ConcurrentDictionary<string, object> Settings { get; set; }
|
||||
public IReadOnlyDictionary<string, object?> Inner => Settings;
|
||||
protected ConcurrentDictionary<string, object?> Settings { get; set; } = null!;
|
||||
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 settingControlMargin = new(0, 9, 18, 9);
|
||||
private static readonly Thickness settingCheckboxMargin = new(0, 9, 9, 9);
|
||||
private static readonly Thickness settingPanelMargin = new(0, 0, 0, 0);
|
||||
private static readonly Thickness settingTextBlockMargin = new(70, 9, 18, 9);
|
||||
private static readonly Thickness settingLabelPanelMargin = new(70, 9, 18, 9);
|
||||
private static readonly Thickness settingLabelMargin = new(0, 0, 0, 0);
|
||||
private static readonly Thickness settingDescMargin = new(0, 2, 0, 0);
|
||||
private static readonly Thickness settingSepMargin = new(0, 0, 0, 2);
|
||||
private static readonly Thickness SettingPanelMargin = (Thickness)Application.Current.FindResource("SettingPanelMargin");
|
||||
private static readonly Thickness SettingPanelItemLeftMargin = (Thickness)Application.Current.FindResource("SettingPanelItemLeftMargin");
|
||||
private static readonly Thickness SettingPanelItemTopBottomMargin = (Thickness)Application.Current.FindResource("SettingPanelItemTopBottomMargin");
|
||||
private static readonly Thickness SettingPanelItemLeftTopBottomMargin = (Thickness)Application.Current.FindResource("SettingPanelItemLeftTopBottomMargin");
|
||||
private static readonly double SettingPanelTextBoxMinWidth = (double)Application.Current.FindResource("SettingPanelTextBoxMinWidth");
|
||||
private static readonly double SettingPanelPathTextBoxWidth = (double)Application.Current.FindResource("SettingPanelPathTextBoxWidth");
|
||||
private static readonly double SettingPanelAreaTextBoxMinHeight = (double)Application.Current.FindResource("SettingPanelAreaTextBoxMinHeight");
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
_storage = new JsonStorage<ConcurrentDictionary<string, object>>(SettingPath);
|
||||
Settings = await _storage.LoadAsync();
|
||||
|
||||
if (Configuration == null)
|
||||
if (Settings == 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)
|
||||
{
|
||||
if (attributes.Name == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Skip if the setting does not have attributes or name
|
||||
if (attributes?.Name == null) 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void UpdateSettings(IReadOnlyDictionary<string, object> settings)
|
||||
{
|
||||
if (settings == null || settings.Count == 0)
|
||||
return;
|
||||
if (settings == null || settings.Count == 0) return;
|
||||
|
||||
foreach (var (key, value) in settings)
|
||||
{
|
||||
|
|
@ -78,19 +95,23 @@ namespace Flow.Launcher.Core.Plugin
|
|||
switch (control)
|
||||
{
|
||||
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;
|
||||
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;
|
||||
case ComboBox comboBox:
|
||||
comboBox.Dispatcher.Invoke(() => comboBox.SelectedItem = value);
|
||||
break;
|
||||
case CheckBox checkBox:
|
||||
checkBox.Dispatcher.Invoke(() =>
|
||||
checkBox.IsChecked = value is bool isChecked
|
||||
? isChecked
|
||||
: bool.Parse(value as string ?? string.Empty));
|
||||
var isChecked = value is bool boolValue
|
||||
? boolValue
|
||||
// If can parse the default value to bool, use it, otherwise use false
|
||||
: value is string stringValue && bool.TryParse(stringValue, out var boolValueFromString)
|
||||
&& boolValueFromString;
|
||||
checkBox.Dispatcher.Invoke(() =>checkBox.IsChecked = isChecked);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -108,7 +129,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
{
|
||||
_storage.Save();
|
||||
}
|
||||
|
||||
|
||||
public bool NeedCreateSettingPanel()
|
||||
{
|
||||
// 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()
|
||||
{
|
||||
// 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();
|
||||
var mainPanel = new Grid { Margin = settingPanelMargin, VerticalAlignment = VerticalAlignment.Center };
|
||||
|
||||
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)
|
||||
// Create main grid with two columns (Column 1: Auto, Column 2: *)
|
||||
var mainPanel = new Grid { Margin = SettingPanelMargin, VerticalAlignment = VerticalAlignment.Center };
|
||||
mainPanel.ColumnDefinitions.Add(new ColumnDefinition()
|
||||
{
|
||||
Separator sep = new Separator();
|
||||
sep.VerticalAlignment = VerticalAlignment.Top;
|
||||
sep.Margin = settingSepMargin;
|
||||
sep.SetResourceReference(Separator.BackgroundProperty, "Color03B"); /* for theme change */
|
||||
var panel = new StackPanel
|
||||
Width = new GridLength(0, GridUnitType.Auto)
|
||||
});
|
||||
mainPanel.ColumnDefinitions.Add(new ColumnDefinition()
|
||||
{
|
||||
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,
|
||||
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);
|
||||
Height = new GridLength(0, GridUnitType.Auto)
|
||||
});
|
||||
|
||||
// State controls for column 0 and 1
|
||||
StackPanel? panel = null;
|
||||
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)
|
||||
{
|
||||
case "textBlock":
|
||||
{
|
||||
contentControl = new TextBlock
|
||||
{
|
||||
Text = attribute.Description.Replace("\\r\\n", "\r\n"),
|
||||
Margin = settingTextBlockMargin,
|
||||
Padding = new Thickness(0, 0, 0, 0),
|
||||
HorizontalAlignment = System.Windows.HorizontalAlignment.Left,
|
||||
TextAlignment = TextAlignment.Left,
|
||||
TextWrapping = TextWrapping.Wrap
|
||||
};
|
||||
contentControl = new TextBlock
|
||||
{
|
||||
Text = attributes.Description?.Replace("\\r\\n", "\r\n") ?? string.Empty,
|
||||
HorizontalAlignment = HorizontalAlignment.Left,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
Margin = SettingPanelItemTopBottomMargin,
|
||||
TextAlignment = TextAlignment.Left,
|
||||
TextWrapping = TextWrapping.Wrap
|
||||
};
|
||||
|
||||
Grid.SetColumn(contentControl, 0);
|
||||
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;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "input":
|
||||
{
|
||||
var textBox = new TextBox()
|
||||
{
|
||||
Text = Settings[attribute.Name] as string ?? string.Empty,
|
||||
Margin = settingControlMargin,
|
||||
HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch,
|
||||
ToolTip = attribute.Description
|
||||
};
|
||||
var textBox = new TextBox()
|
||||
{
|
||||
MinWidth = SettingPanelTextBoxMinWidth,
|
||||
HorizontalAlignment = HorizontalAlignment.Left,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
Margin = SettingPanelItemLeftTopBottomMargin,
|
||||
Text = Settings[attributes.Name] as string ?? string.Empty,
|
||||
ToolTip = attributes.Description
|
||||
};
|
||||
|
||||
textBox.TextChanged += (_, _) =>
|
||||
{
|
||||
Settings[attribute.Name] = textBox.Text;
|
||||
};
|
||||
textBox.TextChanged += (_, _) =>
|
||||
{
|
||||
Settings[attributes.Name] = textBox.Text;
|
||||
};
|
||||
|
||||
contentControl = textBox;
|
||||
Grid.SetColumn(contentControl, 1);
|
||||
Grid.SetRow(contentControl, rowCount);
|
||||
if (rowCount != 0)
|
||||
mainPanel.Children.Add(sep);
|
||||
contentControl = textBox;
|
||||
|
||||
Grid.SetRow(sep, rowCount);
|
||||
Grid.SetColumn(sep, 0);
|
||||
Grid.SetColumnSpan(sep, 2);
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "inputWithFileBtn":
|
||||
case "inputWithFolderBtn":
|
||||
{
|
||||
var textBox = new TextBox()
|
||||
{
|
||||
Margin = new Thickness(10, 0, 0, 0),
|
||||
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
|
||||
var textBox = new TextBox()
|
||||
{
|
||||
"inputWithFolderBtn" => new FolderBrowserDialog(),
|
||||
_ => new OpenFileDialog(),
|
||||
Width = SettingPanelPathTextBoxWidth,
|
||||
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,
|
||||
OpenFileDialog fileDialog => fileDialog.FileName,
|
||||
Settings[attributes.Name] = textBox.Text;
|
||||
};
|
||||
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);
|
||||
dockPanel.Children.Add(Btn);
|
||||
dockPanel.Children.Add(textBox);
|
||||
contentControl = dockPanel;
|
||||
Grid.SetColumn(contentControl, 1);
|
||||
Grid.SetRow(contentControl, rowCount);
|
||||
if (rowCount != 0)
|
||||
mainPanel.Children.Add(sep);
|
||||
Btn.Click += (_, _) =>
|
||||
{
|
||||
using System.Windows.Forms.CommonDialog dialog = type switch
|
||||
{
|
||||
"inputWithFolderBtn" => new System.Windows.Forms.FolderBrowserDialog(),
|
||||
_ => new System.Windows.Forms.OpenFileDialog(),
|
||||
};
|
||||
|
||||
Grid.SetRow(sep, rowCount);
|
||||
Grid.SetColumn(sep, 0);
|
||||
Grid.SetColumnSpan(sep, 2);
|
||||
if (dialog.ShowDialog() != System.Windows.Forms.DialogResult.OK)
|
||||
{
|
||||
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":
|
||||
{
|
||||
var textBox = new TextBox()
|
||||
{
|
||||
Height = 120,
|
||||
Margin = settingControlMargin,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
TextWrapping = TextWrapping.WrapWithOverflow,
|
||||
AcceptsReturn = true,
|
||||
HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch,
|
||||
Text = Settings[attribute.Name] as string ?? string.Empty,
|
||||
ToolTip = attribute.Description
|
||||
};
|
||||
var textBox = new TextBox()
|
||||
{
|
||||
MinHeight = SettingPanelAreaTextBoxMinHeight,
|
||||
HorizontalAlignment = HorizontalAlignment.Stretch,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
Margin = SettingPanelItemLeftTopBottomMargin,
|
||||
TextWrapping = TextWrapping.WrapWithOverflow,
|
||||
AcceptsReturn = true,
|
||||
Text = Settings[attributes.Name] as string ?? string.Empty,
|
||||
ToolTip = attributes.Description
|
||||
};
|
||||
|
||||
textBox.TextChanged += (sender, _) =>
|
||||
{
|
||||
Settings[attribute.Name] = ((TextBox)sender).Text;
|
||||
};
|
||||
textBox.TextChanged += (sender, _) =>
|
||||
{
|
||||
Settings[attributes.Name] = ((TextBox)sender).Text;
|
||||
};
|
||||
|
||||
contentControl = textBox;
|
||||
Grid.SetColumn(contentControl, 1);
|
||||
Grid.SetRow(contentControl, rowCount);
|
||||
if (rowCount != 0)
|
||||
mainPanel.Children.Add(sep);
|
||||
contentControl = textBox;
|
||||
|
||||
Grid.SetRow(sep, rowCount);
|
||||
Grid.SetColumn(sep, 0);
|
||||
Grid.SetColumnSpan(sep, 2);
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "passwordBox":
|
||||
{
|
||||
var passwordBox = new PasswordBox()
|
||||
{
|
||||
Margin = settingControlMargin,
|
||||
Password = Settings[attribute.Name] as string ?? string.Empty,
|
||||
PasswordChar = attribute.passwordChar == default ? '*' : attribute.passwordChar,
|
||||
HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch,
|
||||
ToolTip = attribute.Description
|
||||
};
|
||||
var passwordBox = new PasswordBox()
|
||||
{
|
||||
MinWidth = SettingPanelTextBoxMinWidth,
|
||||
HorizontalAlignment = HorizontalAlignment.Left,
|
||||
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, _) =>
|
||||
{
|
||||
Settings[attribute.Name] = ((PasswordBox)sender).Password;
|
||||
};
|
||||
passwordBox.PasswordChanged += (sender, _) =>
|
||||
{
|
||||
Settings[attributes.Name] = ((PasswordBox)sender).Password;
|
||||
};
|
||||
|
||||
contentControl = passwordBox;
|
||||
Grid.SetColumn(contentControl, 1);
|
||||
Grid.SetRow(contentControl, rowCount);
|
||||
if (rowCount != 0)
|
||||
mainPanel.Children.Add(sep);
|
||||
contentControl = passwordBox;
|
||||
|
||||
Grid.SetRow(sep, rowCount);
|
||||
Grid.SetColumn(sep, 0);
|
||||
Grid.SetColumnSpan(sep, 2);
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "dropdown":
|
||||
{
|
||||
var comboBox = new System.Windows.Controls.ComboBox()
|
||||
{
|
||||
ItemsSource = attribute.Options,
|
||||
SelectedItem = Settings[attribute.Name],
|
||||
Margin = settingControlMargin,
|
||||
HorizontalAlignment = System.Windows.HorizontalAlignment.Right,
|
||||
ToolTip = attribute.Description
|
||||
};
|
||||
var comboBox = new ComboBox()
|
||||
{
|
||||
ItemsSource = attributes.Options,
|
||||
SelectedItem = Settings[attributes.Name],
|
||||
HorizontalAlignment = HorizontalAlignment.Left,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
Margin = SettingPanelItemLeftTopBottomMargin,
|
||||
ToolTip = attributes.Description
|
||||
};
|
||||
|
||||
comboBox.SelectionChanged += (sender, _) =>
|
||||
{
|
||||
Settings[attribute.Name] = (string)((System.Windows.Controls.ComboBox)sender).SelectedItem;
|
||||
};
|
||||
comboBox.SelectionChanged += (sender, _) =>
|
||||
{
|
||||
Settings[attributes.Name] = (string)((ComboBox)sender).SelectedItem;
|
||||
};
|
||||
|
||||
contentControl = comboBox;
|
||||
Grid.SetColumn(contentControl, 1);
|
||||
Grid.SetRow(contentControl, rowCount);
|
||||
if (rowCount != 0)
|
||||
mainPanel.Children.Add(sep);
|
||||
contentControl = comboBox;
|
||||
|
||||
Grid.SetRow(sep, rowCount);
|
||||
Grid.SetColumn(sep, 0);
|
||||
Grid.SetColumnSpan(sep, 2);
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "checkbox":
|
||||
var checkBox = new CheckBox
|
||||
{
|
||||
IsChecked =
|
||||
Settings[attribute.Name] is bool isChecked
|
||||
// If can parse the default value to bool, use it, otherwise use false
|
||||
var defaultValue = bool.TryParse(attributes.DefaultValue, out var value) && value;
|
||||
var checkBox = new CheckBox
|
||||
{
|
||||
IsChecked =
|
||||
Settings[attributes.Name] is bool isChecked
|
||||
? isChecked
|
||||
: bool.Parse(attribute.DefaultValue),
|
||||
Margin = settingCheckboxMargin,
|
||||
HorizontalAlignment = System.Windows.HorizontalAlignment.Right,
|
||||
ToolTip = attribute.Description
|
||||
};
|
||||
: defaultValue,
|
||||
HorizontalAlignment = HorizontalAlignment.Left,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
Margin = SettingPanelItemTopBottomMargin,
|
||||
Content = attributes.Label,
|
||||
ToolTip = attributes.Description
|
||||
};
|
||||
|
||||
checkBox.Click += (sender, _) =>
|
||||
{
|
||||
Settings[attribute.Name] = ((CheckBox)sender).IsChecked;
|
||||
};
|
||||
checkBox.Click += (sender, _) =>
|
||||
{
|
||||
Settings[attributes.Name] = ((CheckBox)sender).IsChecked ?? defaultValue;
|
||||
};
|
||||
|
||||
contentControl = checkBox;
|
||||
Grid.SetColumn(contentControl, 1);
|
||||
Grid.SetRow(contentControl, rowCount);
|
||||
if (rowCount != 0)
|
||||
mainPanel.Children.Add(sep);
|
||||
contentControl = checkBox;
|
||||
|
||||
Grid.SetRow(sep, rowCount);
|
||||
Grid.SetColumn(sep, 0);
|
||||
Grid.SetColumnSpan(sep, 2);
|
||||
|
||||
break;
|
||||
break;
|
||||
}
|
||||
case "hyperlink":
|
||||
var hyperlink = new Hyperlink { ToolTip = attribute.Description, NavigateUri = attribute.url };
|
||||
|
||||
var linkbtn = new System.Windows.Controls.Button
|
||||
{
|
||||
HorizontalAlignment = System.Windows.HorizontalAlignment.Right,
|
||||
Margin = settingControlMargin
|
||||
};
|
||||
var hyperlink = new Hyperlink
|
||||
{
|
||||
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;
|
||||
Grid.SetColumn(contentControl, 1);
|
||||
Grid.SetRow(contentControl, rowCount);
|
||||
if (rowCount != 0)
|
||||
mainPanel.Children.Add(sep);
|
||||
var textBlock = new TextBlock()
|
||||
{
|
||||
HorizontalAlignment = HorizontalAlignment.Left,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
Margin = SettingPanelItemLeftTopBottomMargin,
|
||||
TextAlignment = TextAlignment.Left,
|
||||
TextWrapping = TextWrapping.Wrap
|
||||
};
|
||||
textBlock.Inlines.Add(hyperlink);
|
||||
|
||||
Grid.SetRow(sep, rowCount);
|
||||
Grid.SetColumn(sep, 0);
|
||||
Grid.SetColumnSpan(sep, 2);
|
||||
contentControl = textBlock;
|
||||
|
||||
break;
|
||||
break;
|
||||
}
|
||||
case "separator":
|
||||
{
|
||||
var sep = new Separator();
|
||||
|
||||
sep.SetResourceReference(Separator.StyleProperty, "SettingPanelSeparatorStyle");
|
||||
|
||||
contentControl = sep;
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
if (type != "textBlock")
|
||||
SettingControls[attribute.Name] = contentControl;
|
||||
// If type is textBlock or separator, we just add the content control to the main grid
|
||||
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++;
|
||||
}
|
||||
|
||||
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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -231,7 +231,6 @@ namespace Flow.Launcher.Core.Plugin
|
|||
if (!NonGlobalPlugins.ContainsKey(query.ActionKeyword))
|
||||
return GlobalPlugins;
|
||||
|
||||
|
||||
var plugin = NonGlobalPlugins[query.ActionKeyword];
|
||||
return new List<PluginPair>
|
||||
{
|
||||
|
|
@ -367,7 +366,16 @@ namespace Flow.Launcher.Core.Plugin
|
|||
NonGlobalPlugins[newActionKeyword] = plugin;
|
||||
}
|
||||
|
||||
// Update action keywords and action keyword in plugin metadata
|
||||
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>
|
||||
|
|
@ -388,16 +396,15 @@ namespace Flow.Launcher.Core.Plugin
|
|||
if (oldActionkeyword != Query.GlobalPluginWildcardSign)
|
||||
NonGlobalPlugins.Remove(oldActionkeyword);
|
||||
|
||||
|
||||
// Update action keywords and action keyword in plugin metadata
|
||||
plugin.Metadata.ActionKeywords.Remove(oldActionkeyword);
|
||||
}
|
||||
|
||||
public static void ReplaceActionKeyword(string id, string oldActionKeyword, string newActionKeyword)
|
||||
{
|
||||
if (oldActionKeyword != newActionKeyword)
|
||||
if (plugin.Metadata.ActionKeywords.Count > 0)
|
||||
{
|
||||
AddActionKeyword(id, newActionKeyword);
|
||||
RemoveActionKeyword(id, oldActionKeyword);
|
||||
plugin.Metadata.ActionKeyword = plugin.Metadata.ActionKeywords[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
plugin.Metadata.ActionKeyword = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,21 +3,29 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Markup;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Effects;
|
||||
using System.Windows.Shell;
|
||||
using System.Windows.Threading;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Microsoft.Win32;
|
||||
using TextBox = System.Windows.Controls.TextBox;
|
||||
|
||||
namespace Flow.Launcher.Core.Resource
|
||||
{
|
||||
public class Theme
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
public bool BlurEnabled { get; set; }
|
||||
|
||||
private const string ThemeMetadataNamePrefix = "Name:";
|
||||
private const string ThemeMetadataIsDarkPrefix = "IsDark:";
|
||||
private const string ThemeMetadataHasBlurPrefix = "HasBlur:";
|
||||
|
|
@ -31,14 +39,12 @@ namespace Flow.Launcher.Core.Resource
|
|||
private string _oldTheme;
|
||||
private const string Folder = Constant.Themes;
|
||||
private const string Extension = ".xaml";
|
||||
private string DirectoryPath => Path.Combine(Constant.ProgramDirectory, Folder);
|
||||
private string UserDirectoryPath => Path.Combine(DataLocation.DataDirectory(), Folder);
|
||||
private static string DirectoryPath => Path.Combine(Constant.ProgramDirectory, Folder);
|
||||
private static string UserDirectoryPath => Path.Combine(DataLocation.DataDirectory(), Folder);
|
||||
|
||||
public string CurrentTheme => _settings.Theme;
|
||||
#endregion
|
||||
|
||||
public bool BlurEnabled { get; set; }
|
||||
|
||||
private double mainWindowWidth;
|
||||
#region Constructor
|
||||
|
||||
public Theme(IPublicAPI publicAPI, Settings settings)
|
||||
{
|
||||
|
|
@ -66,6 +72,15 @@ namespace Flow.Launcher.Core.Resource
|
|||
_oldTheme = Path.GetFileNameWithoutExtension(_oldResource.Source.AbsolutePath);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Theme Resources
|
||||
|
||||
public string GetCurrentTheme()
|
||||
{
|
||||
return _settings.Theme;
|
||||
}
|
||||
|
||||
private void MakeSureThemeDirectoriesExist()
|
||||
{
|
||||
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)
|
||||
{
|
||||
var dicts = Application.Current.Resources.MergedDictionaries;
|
||||
|
|
@ -154,9 +116,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
return dict;
|
||||
}
|
||||
|
||||
private ResourceDictionary CurrentThemeResourceDictionary() => GetThemeResourceDictionary(_settings.Theme);
|
||||
|
||||
public ResourceDictionary GetResourceDictionary(string theme)
|
||||
private ResourceDictionary GetResourceDictionary(string theme)
|
||||
{
|
||||
var dict = GetThemeResourceDictionary(theme);
|
||||
|
||||
|
|
@ -213,7 +173,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
|
||||
Setter[] setters = { fontFamily, fontStyle, fontWeight, fontStretch };
|
||||
Array.ForEach(
|
||||
new[] { resultSubItemStyle,resultSubItemSelectedStyle}, o
|
||||
new[] { resultSubItemStyle, resultSubItemSelectedStyle }, o
|
||||
=> Array.ForEach(setters, p => o.Setters.Add(p)));
|
||||
}
|
||||
|
||||
|
|
@ -221,28 +181,12 @@ namespace Flow.Launcher.Core.Resource
|
|||
var windowStyle = dict["WindowStyle"] as Style;
|
||||
var width = _settings.WindowSize;
|
||||
windowStyle.Setters.Add(new Setter(Window.WidthProperty, width));
|
||||
mainWindowWidth = (double)width;
|
||||
return dict;
|
||||
}
|
||||
|
||||
private ResourceDictionary GetCurrentResourceDictionary( )
|
||||
private ResourceDictionary GetCurrentResourceDictionary()
|
||||
{
|
||||
return GetResourceDictionary(_settings.Theme);
|
||||
}
|
||||
|
||||
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();
|
||||
return GetResourceDictionary(GetCurrentTheme());
|
||||
}
|
||||
|
||||
private ThemeData GetThemeDataFromPath(string path)
|
||||
|
|
@ -264,15 +208,15 @@ namespace Flow.Launcher.Core.Resource
|
|||
{
|
||||
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))
|
||||
{
|
||||
isDark = bool.Parse(line.Remove(0, ThemeMetadataIsDarkPrefix.Length).Trim());
|
||||
isDark = bool.Parse(line[ThemeMetadataIsDarkPrefix.Length..].Trim());
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
#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()
|
||||
{
|
||||
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 (marginSetter == null)
|
||||
if (windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == Border.MarginProperty) is not Setter marginSetter)
|
||||
{
|
||||
var margin = new Thickness(ShadowExtraMargin, 12, ShadowExtraMargin, ShadowExtraMargin);
|
||||
marginSetter = new Setter()
|
||||
|
|
@ -347,14 +365,12 @@ namespace Flow.Launcher.Core.Resource
|
|||
var dict = GetCurrentResourceDictionary();
|
||||
var windowBorderStyle = dict["WindowBorderStyle"] as Style;
|
||||
|
||||
var effectSetter = windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == Border.EffectProperty) as Setter;
|
||||
var marginSetter = windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == Border.MarginProperty) as Setter;
|
||||
|
||||
if (effectSetter != null)
|
||||
if (windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == Border.EffectProperty) is Setter 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 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);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
|||
123
Flow.Launcher.Infrastructure/MonitorInfo.cs
Normal file
123
Flow.Launcher.Infrastructure/MonitorInfo.cs
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.Graphics.Gdi;
|
||||
using Windows.Win32.UI.WindowsAndMessaging;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure;
|
||||
|
||||
/// <summary>
|
||||
/// Contains full information about a display monitor.
|
||||
/// Codes are edited from: <see href="https://github.com/Jack251970/DesktopWidgets3">.
|
||||
/// </summary>
|
||||
internal class MonitorInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the display monitors (including invisible pseudo-monitors associated with the mirroring drivers).
|
||||
/// </summary>
|
||||
/// <returns>A list of display monitors</returns>
|
||||
public static unsafe IList<MonitorInfo> GetDisplayMonitors()
|
||||
{
|
||||
var monitorCount = PInvoke.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CMONITORS);
|
||||
var list = new List<MonitorInfo>(monitorCount);
|
||||
var callback = new MONITORENUMPROC((HMONITOR monitor, HDC deviceContext, RECT* rect, LPARAM data) =>
|
||||
{
|
||||
list.Add(new MonitorInfo(monitor, rect));
|
||||
return true;
|
||||
});
|
||||
var dwData = new LPARAM();
|
||||
var hdc = new HDC();
|
||||
bool ok = PInvoke.EnumDisplayMonitors(hdc, (RECT?)null, callback, dwData);
|
||||
if (!ok)
|
||||
{
|
||||
Marshal.ThrowExceptionForHR(Marshal.GetLastWin32Error());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the display monitor that is nearest to a given window.
|
||||
/// </summary>
|
||||
/// <param name="hwnd">Window handle</param>
|
||||
/// <returns>The display monitor that is nearest to a given window, or null if no monitor is found.</returns>
|
||||
public static unsafe MonitorInfo GetNearestDisplayMonitor(HWND hwnd)
|
||||
{
|
||||
var nearestMonitor = PInvoke.MonitorFromWindow(hwnd, MONITOR_FROM_FLAGS.MONITOR_DEFAULTTONEAREST);
|
||||
MonitorInfo nearestMonitorInfo = null;
|
||||
var callback = new MONITORENUMPROC((HMONITOR monitor, HDC deviceContext, RECT* rect, LPARAM data) =>
|
||||
{
|
||||
if (monitor == nearestMonitor)
|
||||
{
|
||||
nearestMonitorInfo = new MonitorInfo(monitor, rect);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
var dwData = new LPARAM();
|
||||
var hdc = new HDC();
|
||||
bool ok = PInvoke.EnumDisplayMonitors(hdc, (RECT?)null, callback, dwData);
|
||||
if (!ok)
|
||||
{
|
||||
Marshal.ThrowExceptionForHR(Marshal.GetLastWin32Error());
|
||||
}
|
||||
return nearestMonitorInfo;
|
||||
}
|
||||
|
||||
private readonly HMONITOR _monitor;
|
||||
|
||||
internal unsafe MonitorInfo(HMONITOR monitor, RECT* rect)
|
||||
{
|
||||
RectMonitor =
|
||||
new Rect(new Point(rect->left, rect->top),
|
||||
new Point(rect->right, rect->bottom));
|
||||
_monitor = monitor;
|
||||
var info = new MONITORINFOEXW() { monitorInfo = new MONITORINFO() { cbSize = (uint)sizeof(MONITORINFOEXW) } };
|
||||
GetMonitorInfo(monitor, ref info);
|
||||
RectWork =
|
||||
new Rect(new Point(info.monitorInfo.rcWork.left, info.monitorInfo.rcWork.top),
|
||||
new Point(info.monitorInfo.rcWork.right, info.monitorInfo.rcWork.bottom));
|
||||
Name = new string(info.szDevice.AsSpan()).Replace("\0", "").Trim();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the display.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the display monitor rectangle, expressed in virtual-screen coordinates.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <note>If the monitor is not the primary display monitor, some of the rectangle's coordinates may be negative values.</note>
|
||||
/// </remarks>
|
||||
public Rect RectMonitor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the work area rectangle of the display monitor, expressed in virtual-screen coordinates.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <note>If the monitor is not the primary display monitor, some of the rectangle's coordinates may be negative values.</note>
|
||||
/// </remarks>
|
||||
public Rect RectWork { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets if the monitor is the the primary display monitor.
|
||||
/// </summary>
|
||||
public bool IsPrimary => _monitor == PInvoke.MonitorFromWindow(new(IntPtr.Zero), MONITOR_FROM_FLAGS.MONITOR_DEFAULTTOPRIMARY);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString() => $"{Name} {RectMonitor.Width}x{RectMonitor.Height}";
|
||||
|
||||
private static unsafe bool GetMonitorInfo(HMONITOR hMonitor, ref MONITORINFOEXW lpmi)
|
||||
{
|
||||
fixed (MONITORINFOEXW* lpmiLocal = &lpmi)
|
||||
{
|
||||
var lpmiBase = (MONITORINFO*)lpmiLocal;
|
||||
var __result = PInvoke.GetMonitorInfo(hMonitor, lpmiBase);
|
||||
return __result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -16,4 +16,34 @@ WM_KEYUP
|
|||
WM_SYSKEYDOWN
|
||||
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
|
||||
25
Flow.Launcher.Infrastructure/PInvokeExtensions.cs
Normal file
25
Flow.Launcher.Infrastructure/PInvokeExtensions.cs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
using System.Runtime.InteropServices;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.UI.WindowsAndMessaging;
|
||||
|
||||
namespace Windows.Win32;
|
||||
|
||||
// Edited from: https://github.com/files-community/Files
|
||||
internal static partial class PInvoke
|
||||
{
|
||||
[DllImport("User32", EntryPoint = "SetWindowLongW", ExactSpelling = true)]
|
||||
static extern int _SetWindowLong(HWND hWnd, int nIndex, int dwNewLong);
|
||||
|
||||
[DllImport("User32", EntryPoint = "SetWindowLongPtrW", ExactSpelling = true)]
|
||||
static extern nint _SetWindowLongPtr(HWND hWnd, int nIndex, nint dwNewLong);
|
||||
|
||||
// NOTE:
|
||||
// CsWin32 doesn't generate SetWindowLong on other than x86 and vice versa.
|
||||
// For more info, visit https://github.com/microsoft/CsWin32/issues/882
|
||||
public static unsafe nint SetWindowLongPtr(HWND hWnd, WINDOW_LONG_PTR_INDEX nIndex, nint dwNewLong)
|
||||
{
|
||||
return sizeof(nint) is 4
|
||||
? _SetWindowLong(hWnd, (int)nIndex, (int)dwNewLong)
|
||||
: _SetWindowLongPtr(hWnd, (int)nIndex, dwNewLong);
|
||||
}
|
||||
}
|
||||
|
|
@ -76,6 +76,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
}
|
||||
}
|
||||
public bool UseDropShadowEffect { get; set; } = true;
|
||||
public BackdropTypes BackdropType{ get; set; } = BackdropTypes.None;
|
||||
|
||||
/* Appearance Settings. It should be separated from the setting later.*/
|
||||
public double WindowHeightSize { get; set; } = 42;
|
||||
|
|
@ -457,4 +458,12 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
Fast,
|
||||
Custom
|
||||
}
|
||||
|
||||
public enum BackdropTypes
|
||||
{
|
||||
None,
|
||||
Acrylic,
|
||||
Mica,
|
||||
MicaAlt
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,14 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Interop;
|
||||
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
|
||||
{
|
||||
|
|
@ -9,93 +16,306 @@ namespace Flow.Launcher.Infrastructure
|
|||
{
|
||||
#region Blur Handling
|
||||
|
||||
/*
|
||||
Found on https://github.com/riverar/sample-win10-aeroglass
|
||||
*/
|
||||
|
||||
private enum AccentState
|
||||
public static bool IsBackdropSupported()
|
||||
{
|
||||
ACCENT_DISABLED = 0,
|
||||
ACCENT_ENABLE_GRADIENT = 1,
|
||||
ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,
|
||||
ACCENT_ENABLE_BLURBEHIND = 3,
|
||||
ACCENT_INVALID_STATE = 4
|
||||
// Mica and Acrylic only supported Windows 11 22000+
|
||||
return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) &&
|
||||
Environment.OSVersion.Version.Build >= 22000;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct AccentPolicy
|
||||
public static unsafe bool DWMSetCloakForWindow(Window window, bool cloak)
|
||||
{
|
||||
public AccentState AccentState;
|
||||
public int AccentFlags;
|
||||
public int GradientColor;
|
||||
public int AnimationId;
|
||||
var cloaked = cloak ? 1 : 0;
|
||||
|
||||
return PInvoke.DwmSetWindowAttribute(
|
||||
GetWindowHandle(window),
|
||||
DWMWINDOWATTRIBUTE.DWMWA_CLOAK,
|
||||
&cloaked,
|
||||
(uint)Marshal.SizeOf<int>()).Succeeded;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct WindowCompositionAttributeData
|
||||
public static unsafe bool DWMSetBackdropForWindow(Window window, BackdropTypes backdrop)
|
||||
{
|
||||
public WindowCompositionAttribute Attribute;
|
||||
public IntPtr Data;
|
||||
public int SizeOfData;
|
||||
var backdropType = backdrop switch
|
||||
{
|
||||
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")]
|
||||
private static extern int SetWindowCompositionAttribute(IntPtr hwnd, ref WindowCompositionAttributeData data);
|
||||
return PInvoke.DwmSetWindowAttribute(
|
||||
GetWindowHandle(window),
|
||||
DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE,
|
||||
&darkMode,
|
||||
(uint)Marshal.SizeOf<int>()).Succeeded;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the blur theme is enabled
|
||||
///
|
||||
/// </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 b;
|
||||
return PInvoke.DwmSetWindowAttribute(
|
||||
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;
|
||||
// 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>
|
||||
/// Sets the blur for a window via SetWindowCompositionAttribute
|
||||
/// Transforms pixels to Device Independent Pixels used by WPF
|
||||
/// </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);
|
||||
}
|
||||
|
||||
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
|
||||
Matrix matrix;
|
||||
var source = PresentationSource.FromVisual(visual);
|
||||
if (source is not null)
|
||||
{
|
||||
Attribute = WindowCompositionAttribute.WCA_ACCENT_POLICY,
|
||||
SizeOfData = accentStructSize,
|
||||
Data = accentPtr
|
||||
};
|
||||
matrix = source.CompositionTarget.TransformFromDevice;
|
||||
}
|
||||
else
|
||||
{
|
||||
using var src = new HwndSource(new HwndSourceParameters());
|
||||
matrix = src.CompositionTarget.TransformFromDevice;
|
||||
}
|
||||
|
||||
SetWindowCompositionAttribute(windowHelper.Handle, ref data);
|
||||
|
||||
Marshal.FreeHGlobal(accentPtr);
|
||||
return new Point((int)(matrix.M11 * unitX), (int)(matrix.M22 * unitY));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region WndProc
|
||||
|
||||
public const int WM_ENTERSIZEMOVE = (int)PInvoke.WM_ENTERSIZEMOVE;
|
||||
public const int WM_EXITSIZEMOVE = (int)PInvoke.WM_EXITSIZEMOVE;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Window Handle
|
||||
|
||||
internal static HWND GetWindowHandle(Window window, bool ensure = false)
|
||||
{
|
||||
var windowHelper = new WindowInteropHelper(window);
|
||||
if (ensure)
|
||||
{
|
||||
windowHelper.EnsureHandle();
|
||||
}
|
||||
return new(windowHelper.Handle);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -152,7 +152,6 @@ namespace Flow.Launcher.Plugin
|
|||
/// <param name="callback"></param>
|
||||
public void RemoveGlobalKeyboardCallback(Func<int, int, SpecialKeyState, bool> callback);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Fuzzy Search the string with the given query. This is the core search mechanism Flow uses
|
||||
/// </summary>
|
||||
|
|
@ -191,14 +190,15 @@ namespace Flow.Launcher.Plugin
|
|||
Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath, Action<double> reportProgress = null, CancellationToken token = default);
|
||||
|
||||
/// <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>
|
||||
/// <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>
|
||||
void AddActionKeyword(string pluginId, string newActionKeyword);
|
||||
|
||||
/// <summary>
|
||||
/// Remove ActionKeyword for specific plugin
|
||||
/// Remove ActionKeyword and update action keyword metadata for specific plugin
|
||||
/// </summary>
|
||||
/// <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>
|
||||
|
|
|
|||
|
|
@ -33,7 +33,9 @@ namespace Flow.Launcher.Plugin
|
|||
public string ActionKeyword { get; set; }
|
||||
|
||||
public List<string> ActionKeywords { get; set; }
|
||||
|
||||
|
||||
public bool HideActionKeywordPanel { get; set; }
|
||||
|
||||
public int SearchDelay { get; set; }
|
||||
|
||||
public string IcoPath { get; set;}
|
||||
|
|
|
|||
|
|
@ -39,10 +39,9 @@ namespace Flow.Launcher.Plugin
|
|||
public const string TermSeparator = " ";
|
||||
|
||||
/// <summary>
|
||||
/// User can set multiple action keywords seperated by ';'
|
||||
/// User can set multiple action keywords seperated by whitespace
|
||||
/// </summary>
|
||||
public const string ActionKeywordSeparator = ";";
|
||||
|
||||
public const string ActionKeywordSeparator = TermSeparator;
|
||||
|
||||
/// <summary>
|
||||
/// Wildcard action keyword. Plugins using this value will be queried on every search.
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ using Flow.Launcher.Core.Resource;
|
|||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.ViewModel;
|
||||
using Flow.Launcher.Core;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Flow.Launcher
|
||||
{
|
||||
|
|
@ -32,14 +34,37 @@ namespace Flow.Launcher
|
|||
|
||||
private void btnDone_OnClick(object sender, RoutedEventArgs _)
|
||||
{
|
||||
var oldActionKeyword = plugin.Metadata.ActionKeywords[0];
|
||||
var newActionKeyword = tbAction.Text.Trim();
|
||||
newActionKeyword = newActionKeyword.Length > 0 ? newActionKeyword : "*";
|
||||
|
||||
if (!PluginViewModel.IsActionKeywordRegistered(newActionKeyword))
|
||||
var oldActionKeywords = plugin.Metadata.ActionKeywords;
|
||||
|
||||
var newActionKeywords = tbAction.Text.Split(Query.ActionKeywordSeparator).ToList();
|
||||
newActionKeywords.RemoveAll(string.IsNullOrEmpty);
|
||||
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);
|
||||
Close();
|
||||
if (oldActionKeywords.Count != newActionKeywords.Count)
|
||||
{
|
||||
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
|
||||
{
|
||||
|
|
@ -47,5 +72,21 @@ namespace Flow.Launcher
|
|||
App.API.ShowMsgBox(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private void ReplaceActionKeyword(string id, IReadOnlyList<string> removedActionKeywords, IReadOnlyList<string> addedActionKeywords)
|
||||
{
|
||||
foreach (var actionKeyword in removedActionKeywords)
|
||||
{
|
||||
App.API.RemoveActionKeyword(id, actionKeyword);
|
||||
}
|
||||
foreach (var actionKeyword in addedActionKeywords)
|
||||
{
|
||||
App.API.AddActionKeyword(id, actionKeyword);
|
||||
}
|
||||
|
||||
// Update action keywords text and close window
|
||||
pluginViewModel.OnActionKeywordsChanged();
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:ui="http://schemas.modernwpf.com/2019"
|
||||
ShutdownMode="OnMainWindowClose"
|
||||
Startup="OnStartupAsync">
|
||||
Startup="OnStartup">
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
|
|
|
|||
|
|
@ -101,15 +101,15 @@ namespace Flow.Launcher
|
|||
{
|
||||
if (SingleInstance<App>.InitializeAsFirstInstance(Unique))
|
||||
{
|
||||
using (var application = new App())
|
||||
{
|
||||
application.InitializeComponent();
|
||||
application.Run();
|
||||
}
|
||||
using var application = new App();
|
||||
application.InitializeComponent();
|
||||
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 () =>
|
||||
{
|
||||
|
|
@ -128,7 +128,7 @@ namespace Flow.Launcher
|
|||
AbstractPluginEnvironment.PreStartPluginExecutablePathUpdate(_settings);
|
||||
|
||||
// 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);
|
||||
|
||||
|
|
@ -137,8 +137,7 @@ namespace Flow.Launcher
|
|||
await PluginManager.InitializePluginsAsync();
|
||||
await imageLoadertask;
|
||||
|
||||
var mainVM = Ioc.Default.GetRequiredService<MainViewModel>();
|
||||
var window = new MainWindow(_settings, mainVM);
|
||||
var window = new MainWindow();
|
||||
|
||||
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
|
||||
// TODO: Clean ThemeManager.Instance in future
|
||||
ThemeManager.Instance.ChangeTheme(_settings.Theme);
|
||||
Ioc.Default.GetRequiredService<Theme>().ChangeTheme();
|
||||
|
||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
|
||||
|
|
@ -164,6 +163,8 @@ namespace Flow.Launcher
|
|||
});
|
||||
}
|
||||
|
||||
#pragma warning restore VSTHRD100 // Avoid async void methods
|
||||
|
||||
private void AutoStartup()
|
||||
{
|
||||
// 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
|
||||
// this also gives the user a visual indication in the Settings widget
|
||||
_settings.StartFlowLauncherOnSystemStartup = false;
|
||||
Notification.Show(InternationalizationManager.Instance.GetTranslation("setAutoStartFailed"),
|
||||
e.Message);
|
||||
API.ShowMsg(API.GetTranslation("setAutoStartFailed"), e.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,10 +94,6 @@
|
|||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
|
||||
<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 -->
|
||||
<!-- https://github.com/Flow-Launcher/Flow.Launcher/issues/1772#issuecomment-1502440801 -->
|
||||
<PackageReference Include="ModernWpfUI" Version="0.9.4" />
|
||||
|
|
|
|||
|
|
@ -1,66 +0,0 @@
|
|||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Interop;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.Graphics.Dwm;
|
||||
using Windows.Win32.UI.Controls;
|
||||
|
||||
namespace Flow.Launcher.Helper;
|
||||
|
||||
public class DwmDropShadow
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Drops a standard shadow to a WPF Window, even if the window isborderless. Only works with DWM (Vista and Seven).
|
||||
/// This method is much more efficient than setting AllowsTransparency to true and using the DropShadow effect,
|
||||
/// as AllowsTransparency involves a huge permormance issue (hardware acceleration is turned off for all the window).
|
||||
/// </summary>
|
||||
/// <param name="window">Window to which the shadow will be applied</param>
|
||||
public static void DropShadowToWindow(Window window)
|
||||
{
|
||||
if (!DropShadow(window))
|
||||
{
|
||||
window.SourceInitialized += window_SourceInitialized;
|
||||
}
|
||||
}
|
||||
|
||||
private static void window_SourceInitialized(object sender, EventArgs e) //fixed typo
|
||||
{
|
||||
Window window = (Window)sender;
|
||||
|
||||
DropShadow(window);
|
||||
|
||||
window.SourceInitialized -= window_SourceInitialized;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The actual method that makes API calls to drop the shadow to the window
|
||||
/// </summary>
|
||||
/// <param name="window">Window to which the shadow will be applied</param>
|
||||
/// <returns>True if the method succeeded, false if not</returns>
|
||||
private static unsafe bool DropShadow(Window window)
|
||||
{
|
||||
try
|
||||
{
|
||||
WindowInteropHelper helper = new WindowInteropHelper(window);
|
||||
int val = 2;
|
||||
var ret1 = PInvoke.DwmSetWindowAttribute(new (helper.Handle), DWMWINDOWATTRIBUTE.DWMWA_NCRENDERING_POLICY, &val, 4);
|
||||
|
||||
if (ret1 == HRESULT.S_OK)
|
||||
{
|
||||
var m = new MARGINS { cyBottomHeight = 0, cxLeftWidth = 0, cxRightWidth = 0, cyTopHeight = 0 };
|
||||
var ret2 = PInvoke.DwmExtendFrameIntoClientArea(new(helper.Handle), &m);
|
||||
return ret2 == HRESULT.S_OK;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Probably dwmapi.dll not found (incompatible OS)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -2,19 +2,16 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Microsoft.Win32;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.UI.WindowsAndMessaging;
|
||||
|
||||
namespace Flow.Launcher.Helper;
|
||||
|
||||
public static class WallpaperPathRetrieval
|
||||
{
|
||||
private static readonly int MAX_PATH = 260;
|
||||
private static readonly int MAX_CACHE_SIZE = 3;
|
||||
|
||||
private static readonly Dictionary<(string, DateTime), ImageBrush> wallpaperCache = new();
|
||||
|
|
@ -29,7 +26,7 @@ public static class WallpaperPathRetrieval
|
|||
|
||||
try
|
||||
{
|
||||
var wallpaperPath = GetWallpaperPath();
|
||||
var wallpaperPath = Win32Helper.GetWallpaperPath();
|
||||
if (wallpaperPath is not null && File.Exists(wallpaperPath))
|
||||
{
|
||||
// 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()
|
||||
{
|
||||
RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Control Panel\Colors", true);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -214,13 +214,14 @@ namespace Flow.Launcher
|
|||
|
||||
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)
|
||||
{
|
||||
SetKeysToDisplay(new HotkeyModel(Hotkey));
|
||||
CurrentHotkey = new HotkeyModel(Hotkey);
|
||||
SetKeysToDisplay(new HotkeyModel(hotkey));
|
||||
CurrentHotkey = new HotkeyModel(hotkey);
|
||||
}
|
||||
|
||||
private static bool CheckHotkeyAvailability(HotkeyModel hotkey, bool validateKeyGesture) =>
|
||||
|
|
|
|||
|
|
@ -108,6 +108,11 @@
|
|||
<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="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 -->
|
||||
<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="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="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="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 -->
|
||||
<system:String x:Key="customeQueryHotkeyTitle">Custom Query Hotkey</system:String>
|
||||
|
|
|
|||
|
|
@ -20,17 +20,17 @@
|
|||
Closing="OnClosing"
|
||||
Deactivated="OnDeactivated"
|
||||
Icon="Images/app.png"
|
||||
SourceInitialized="OnSourceInitialized"
|
||||
Initialized="OnInitialized"
|
||||
Left="{Binding Settings.WindowLeft, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
Loaded="OnLoaded"
|
||||
LocationChanged="OnLocationChanged"
|
||||
Opacity="{Binding MainWindowOpacity, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
PreviewKeyDown="OnKeyDown"
|
||||
PreviewKeyUp="OnKeyUp"
|
||||
PreviewMouseMove="OnPreviewMouseMove"
|
||||
ResizeMode="CanResize"
|
||||
ShowInTaskbar="False"
|
||||
SizeToContent="Height"
|
||||
SourceInitialized="OnSourceInitialized"
|
||||
Topmost="True"
|
||||
Visibility="{Binding MainWindowVisibility, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
WindowStartupLocation="Manual"
|
||||
|
|
@ -240,14 +240,14 @@
|
|||
FontSize="{Binding QueryBoxFontSize, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
InputMethod.PreferredImeConversionMode="{Binding StartWithEnglishMode, Converter={StaticResource BoolToIMEConversionModeConverter}}"
|
||||
InputMethod.PreferredImeState="{Binding StartWithEnglishMode, Converter={StaticResource BoolToIMEStateConverter}}"
|
||||
PreviewDragOver="OnPreviewDragOver"
|
||||
PreviewDragOver="QueryTextBox_OnPreviewDragOver"
|
||||
PreviewKeyUp="QueryTextBox_KeyUp"
|
||||
Style="{DynamicResource QueryBoxStyle}"
|
||||
Text="{Binding QueryText, Mode=OneWay}"
|
||||
Visibility="Visible"
|
||||
WindowChrome.IsHitTestVisibleInChrome="True">
|
||||
<TextBox.CommandBindings>
|
||||
<CommandBinding Command="ApplicationCommands.Copy" Executed="OnCopy" />
|
||||
<CommandBinding Command="ApplicationCommands.Copy" Executed="QueryTextBox_OnCopy" />
|
||||
</TextBox.CommandBindings>
|
||||
<TextBox.ContextMenu>
|
||||
<ContextMenu MinWidth="160">
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,10 +1,9 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Forms;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media.Animation;
|
||||
using Flow.Launcher.Helper;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.Image;
|
||||
|
||||
|
|
@ -12,14 +11,14 @@ namespace Flow.Launcher
|
|||
{
|
||||
public partial class Msg : Window
|
||||
{
|
||||
Storyboard fadeOutStoryboard = new Storyboard();
|
||||
private readonly Storyboard fadeOutStoryboard = new();
|
||||
private bool closing;
|
||||
|
||||
public Msg()
|
||||
{
|
||||
InitializeComponent();
|
||||
var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position);
|
||||
var dipWorkingArea = WindowsInteropHelper.TransformPixelsToDIP(this,
|
||||
var dipWorkingArea = Win32Helper.TransformPixelsToDIP(this,
|
||||
screen.WorkingArea.Width,
|
||||
screen.WorkingArea.Height);
|
||||
Left = dipWorkingArea.X - Width;
|
||||
|
|
@ -82,13 +81,13 @@ namespace Flow.Launcher
|
|||
Show();
|
||||
|
||||
await Dispatcher.InvokeAsync(async () =>
|
||||
{
|
||||
if (!closing)
|
||||
{
|
||||
closing = true;
|
||||
await Dispatcher.InvokeAsync(fadeOutStoryboard.Begin);
|
||||
}
|
||||
});
|
||||
{
|
||||
if (!closing)
|
||||
{
|
||||
closing = true;
|
||||
await Dispatcher.InvokeAsync(fadeOutStoryboard.Begin);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +0,0 @@
|
|||
DwmSetWindowAttribute
|
||||
DwmExtendFrameIntoClientArea
|
||||
SystemParametersInfo
|
||||
SetForegroundWindow
|
||||
|
||||
GetWindowLong
|
||||
SetWindowLong
|
||||
GetForegroundWindow
|
||||
GetDesktopWindow
|
||||
GetShellWindow
|
||||
GetWindowRect
|
||||
GetClassName
|
||||
FindWindowEx
|
||||
WINDOW_STYLE
|
||||
|
||||
WM_ENTERSIZEMOVE
|
||||
WM_EXITSIZEMOVE
|
||||
|
||||
SetLastError
|
||||
WINDOW_EX_STYLE
|
||||
|
|
@ -1,46 +1,48 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Squirrel;
|
||||
using Flow.Launcher.Core;
|
||||
using Flow.Launcher.Core.Plugin;
|
||||
using Flow.Launcher.Core.Resource;
|
||||
using Flow.Launcher.Helper;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.Http;
|
||||
using Flow.Launcher.Infrastructure.Hotkey;
|
||||
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.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.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
|
||||
{
|
||||
public class PublicAPIInstance : IPublicAPI
|
||||
{
|
||||
private readonly Settings _settings;
|
||||
private readonly Internationalization _translater;
|
||||
private readonly MainViewModel _mainVM;
|
||||
|
||||
#region Constructor
|
||||
|
||||
public PublicAPIInstance(Settings settings, MainViewModel mainVM)
|
||||
public PublicAPIInstance(Settings settings, Internationalization translater, MainViewModel mainVM)
|
||||
{
|
||||
_settings = settings;
|
||||
_translater = translater;
|
||||
_mainVM = mainVM;
|
||||
GlobalHotkey.hookedKeyboardCallback = KListener_hookedKeyboardCallback;
|
||||
WebRequest.RegisterPrefix("data", new DataWebRequestFactory());
|
||||
|
|
@ -153,17 +155,17 @@ namespace Flow.Launcher
|
|||
|
||||
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 MatchResult FuzzySearch(string query, string 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) =>
|
||||
Http.GetStreamAsync(url);
|
||||
Http.GetStreamAsync(url, token);
|
||||
|
||||
public Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath, Action<double> reportProgress = null,
|
||||
CancellationToken token = default) => Http.DownloadAsync(url, filePath, reportProgress, token);
|
||||
|
|
@ -329,7 +331,6 @@ namespace Flow.Launcher
|
|||
return _mainVM.GameModeStatus;
|
||||
}
|
||||
|
||||
|
||||
private readonly List<Func<int, int, SpecialKeyState, bool>> _globalKeyboardHandlers = new();
|
||||
|
||||
public void RegisterGlobalKeyboardCallback(Func<int, int, SpecialKeyState, bool> callback) => _globalKeyboardHandlers.Add(callback);
|
||||
|
|
|
|||
|
|
@ -11,30 +11,33 @@
|
|||
mc:Ignorable="d">
|
||||
<Border
|
||||
Width="Auto"
|
||||
Height="52"
|
||||
Height="Auto"
|
||||
Margin="0"
|
||||
Padding="0"
|
||||
BorderThickness="0 1 0 0"
|
||||
CornerRadius="0"
|
||||
Style="{DynamicResource SettingGroupBox}"
|
||||
Visibility="{Binding ActionKeywordsVisibility}">
|
||||
<DockPanel Margin="22 0 18 0" VerticalAlignment="Center">
|
||||
<DockPanel Margin="{StaticResource SettingPanelMargin}">
|
||||
<TextBlock
|
||||
Margin="48 0 10 0"
|
||||
Margin="{StaticResource SettingPanelItemRightMargin}"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
DockPanel.Dock="Left"
|
||||
Style="{StaticResource Glyph}">
|
||||

|
||||
</TextBlock>
|
||||
<TextBlock
|
||||
Margin="{StaticResource SettingPanelItemRightMargin}"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
DockPanel.Dock="Left"
|
||||
Style="{DynamicResource SettingTitleLabel}"
|
||||
Text="{DynamicResource actionKeywords}" />
|
||||
<!-- Here Margin="0 -4.5 0 -4.5" is to remove redundant top bottom margin from Margin="{StaticResource SettingPanelMargin}" -->
|
||||
<Button
|
||||
Width="100"
|
||||
Height="34"
|
||||
Margin="5 0 0 0"
|
||||
Margin="0 -4.5 0 -4.5"
|
||||
HorizontalAlignment="Right"
|
||||
Command="{Binding SetActionKeywordsCommand}"
|
||||
Content="{Binding ActionKeywordsText}"
|
||||
|
|
|
|||
|
|
@ -5585,4 +5585,31 @@
|
|||
</Setter.Value>
|
||||
</Setter>
|
||||
</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>
|
||||
|
|
|
|||
|
|
@ -7,17 +7,17 @@
|
|||
xmlns:sys="clr-namespace:System;assembly=mscorlib">
|
||||
|
||||
<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="SubTitleForeground" Color="#7b7b7b" />
|
||||
<SolidColorBrush x:Key="SubTitleSelectedForeground" Color="#7b7b7b" />
|
||||
<SolidColorBrush x:Key="SearchIconForeground" Color="#4d4d4d" />
|
||||
<SolidColorBrush x:Key="SeparatorForeground" Color="#14ffffff" />
|
||||
<SolidColorBrush x:Key="SubTitleForeground" Color="#33FFFFFF" />
|
||||
<SolidColorBrush x:Key="SubTitleSelectedForeground" Color="#33FFFFFF" />
|
||||
<SolidColorBrush x:Key="SearchIconForeground" Color="#33FFFFFF" />
|
||||
<SolidColorBrush x:Key="SeparatorForeground" Color="#24FFFFFF" />
|
||||
<SolidColorBrush x:Key="ThumbColor" Color="#9a9a9a" />
|
||||
<SolidColorBrush x:Key="InlineHighlight" Color="#0078d7" />
|
||||
<SolidColorBrush x:Key="HotkeyForeground" Color="#7b7b7b" />
|
||||
<SolidColorBrush x:Key="HotkeySelectedForeground" Color="#7b7b7b" />
|
||||
<SolidColorBrush x:Key="ClockDateForeground" Color="#4d4d4d" />
|
||||
<SolidColorBrush x:Key="HotkeyForeground" Color="#33FFFFFF" />
|
||||
<SolidColorBrush x:Key="HotkeySelectedForeground" Color="#33FFFFFF" />
|
||||
<SolidColorBrush x:Key="ClockDateForeground" Color="#26FFFFFF" />
|
||||
<Color x:Key="ItemSelectedBackgroundColorBrush">#198F8F8F</Color>
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -7,18 +7,18 @@
|
|||
xmlns:sys="clr-namespace:System;assembly=mscorlib">
|
||||
|
||||
<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="SubTitleForeground" Color="#818181" />
|
||||
<SolidColorBrush x:Key="SubTitleSelectedForeground" Color="#72767d" />
|
||||
<SolidColorBrush x:Key="SearchIconForeground" Color="#d3d3d3" />
|
||||
<SolidColorBrush x:Key="SeparatorForeground" Color="#14000000" />
|
||||
<SolidColorBrush x:Key="SearchIconForeground" Color="#20000000" />
|
||||
<SolidColorBrush x:Key="SeparatorForeground" Color="#20000000" />
|
||||
<SolidColorBrush x:Key="ThumbColor" Color="#868686" />
|
||||
<SolidColorBrush x:Key="InlineHighlight" Color="#0078d7" />
|
||||
<SolidColorBrush x:Key="HotkeyForeground" Color="#acacac" />
|
||||
<SolidColorBrush x:Key="HotkeySelectedForeground" Color="#acacac" />
|
||||
<SolidColorBrush x:Key="ClockDateForeground" Color="#d3d3d3" />
|
||||
<Color x:Key="ItemSelectedBackgroundColorBrush">#198F8F8F</Color>
|
||||
<SolidColorBrush x:Key="HotkeyForeground" Color="#51000000" />
|
||||
<SolidColorBrush x:Key="HotkeySelectedForeground" Color="#51000000" />
|
||||
<SolidColorBrush x:Key="ClockDateForeground" Color="#44000000" />
|
||||
<Color x:Key="ItemSelectedBackgroundColorBrush">#0C000000</Color>
|
||||
|
||||
<SolidColorBrush x:Key="Color00B" Color="#fbfbfb" />
|
||||
<SolidColorBrush x:Key="Color01B" Color="#f3f3f3" />
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System.Globalization;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows.Media;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Flow.Launcher.Core.Resource;
|
||||
using Flow.Launcher.Helper;
|
||||
|
|
@ -13,7 +14,6 @@ using Flow.Launcher.Infrastructure.UserSettings;
|
|||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.ViewModel;
|
||||
using ModernWpf;
|
||||
using ThemeManager = Flow.Launcher.Core.Resource.ThemeManager;
|
||||
using ThemeManagerForColorSchemeSwitch = ModernWpf.ThemeManager;
|
||||
|
||||
namespace Flow.Launcher.SettingPages.ViewModels;
|
||||
|
|
@ -22,45 +22,68 @@ public partial class SettingsPaneThemeViewModel : BaseModel
|
|||
{
|
||||
private const string DefaultFont = "Segoe UI";
|
||||
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 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;
|
||||
public Theme.ThemeData SelectedTheme
|
||||
{
|
||||
get => _selectedTheme ??= Themes.Find(v => v.FileNameWithoutExtension == Settings.Theme);
|
||||
get => _selectedTheme ??= Themes.Find(v => v.FileNameWithoutExtension == _theme.GetCurrentTheme());
|
||||
set
|
||||
{
|
||||
_selectedTheme = value;
|
||||
ThemeManager.Instance.ChangeTheme(value.FileNameWithoutExtension);
|
||||
_theme.ChangeTheme(value.FileNameWithoutExtension);
|
||||
|
||||
if (ThemeManager.Instance.BlurEnabled && Settings.UseDropShadowEffect)
|
||||
DropShadowEffect = false;
|
||||
// Update UI state
|
||||
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
|
||||
{
|
||||
get => Settings.UseDropShadowEffect;
|
||||
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;
|
||||
}
|
||||
|
||||
// User can change shadow with non-blur theme.
|
||||
if (value)
|
||||
{
|
||||
ThemeManager.Instance.AddDropShadowEffectToCurrentTheme();
|
||||
_theme.AddDropShadowEffectToCurrentTheme();
|
||||
}
|
||||
else
|
||||
{
|
||||
ThemeManager.Instance.RemoveDropShadowEffectFromCurrentTheme();
|
||||
_theme.RemoveDropShadowEffectFromCurrentTheme();
|
||||
}
|
||||
|
||||
Settings.UseDropShadowEffect = value;
|
||||
OnPropertyChanged(nameof(DropShadowEffect));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -94,12 +117,25 @@ public partial class SettingsPaneThemeViewModel : BaseModel
|
|||
set => Settings.ResultSubItemFontSize = value;
|
||||
}
|
||||
|
||||
private List<Theme.ThemeData> _themes;
|
||||
public List<Theme.ThemeData> Themes => _themes ??= ThemeManager.Instance.LoadAvailableThemes();
|
||||
|
||||
public class ColorSchemeData : DropdownDataGeneric<ColorSchemes> { }
|
||||
|
||||
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()
|
||||
{
|
||||
|
|
@ -174,6 +210,31 @@ public partial class SettingsPaneThemeViewModel : BaseModel
|
|||
public class AnimationSpeedData : DropdownDataGeneric<AnimationSpeeds> { }
|
||||
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
|
||||
{
|
||||
get => Settings.UseSound;
|
||||
|
|
@ -219,37 +280,37 @@ public partial class SettingsPaneThemeViewModel : BaseModel
|
|||
{
|
||||
var results = new List<Result>
|
||||
{
|
||||
new Result
|
||||
new()
|
||||
{
|
||||
Title = InternationalizationManager.Instance.GetTranslation("SampleTitleExplorer"),
|
||||
SubTitle = InternationalizationManager.Instance.GetTranslation("SampleSubTitleExplorer"),
|
||||
Title = App.API.GetTranslation("SampleTitleExplorer"),
|
||||
SubTitle = App.API.GetTranslation("SampleSubTitleExplorer"),
|
||||
IcoPath = Path.Combine(
|
||||
Constant.ProgramDirectory,
|
||||
@"Plugins\Flow.Launcher.Plugin.Explorer\Images\explorer.png"
|
||||
)
|
||||
},
|
||||
new Result
|
||||
new()
|
||||
{
|
||||
Title = InternationalizationManager.Instance.GetTranslation("SampleTitleWebSearch"),
|
||||
SubTitle = InternationalizationManager.Instance.GetTranslation("SampleSubTitleWebSearch"),
|
||||
Title = App.API.GetTranslation("SampleTitleWebSearch"),
|
||||
SubTitle = App.API.GetTranslation("SampleSubTitleWebSearch"),
|
||||
IcoPath = Path.Combine(
|
||||
Constant.ProgramDirectory,
|
||||
@"Plugins\Flow.Launcher.Plugin.WebSearch\Images\web_search.png"
|
||||
)
|
||||
},
|
||||
new Result
|
||||
new()
|
||||
{
|
||||
Title = InternationalizationManager.Instance.GetTranslation("SampleTitleProgram"),
|
||||
SubTitle = InternationalizationManager.Instance.GetTranslation("SampleSubTitleProgram"),
|
||||
Title = App.API.GetTranslation("SampleTitleProgram"),
|
||||
SubTitle = App.API.GetTranslation("SampleSubTitleProgram"),
|
||||
IcoPath = Path.Combine(
|
||||
Constant.ProgramDirectory,
|
||||
@"Plugins\Flow.Launcher.Plugin.Program\Images\program.png"
|
||||
)
|
||||
},
|
||||
new Result
|
||||
new()
|
||||
{
|
||||
Title = InternationalizationManager.Instance.GetTranslation("SampleTitleProcessKiller"),
|
||||
SubTitle = InternationalizationManager.Instance.GetTranslation("SampleSubTitleProcessKiller"),
|
||||
Title = App.API.GetTranslation("SampleTitleProcessKiller"),
|
||||
SubTitle = App.API.GetTranslation("SampleSubTitleProcessKiller"),
|
||||
IcoPath = Path.Combine(
|
||||
Constant.ProgramDirectory,
|
||||
@"Plugins\Flow.Launcher.Plugin.ProcessKiller\Images\app.png"
|
||||
|
|
@ -281,7 +342,7 @@ public partial class SettingsPaneThemeViewModel : BaseModel
|
|||
set
|
||||
{
|
||||
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.QueryBoxFontWeight = value.Weight.ToString();
|
||||
Settings.QueryBoxFontStyle = value.Style.ToString();
|
||||
ThemeManager.Instance.ChangeTheme(Settings.Theme);
|
||||
_theme.ChangeTheme();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -325,7 +386,7 @@ public partial class SettingsPaneThemeViewModel : BaseModel
|
|||
set
|
||||
{
|
||||
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.ResultFontWeight = value.Weight.ToString();
|
||||
Settings.ResultFontStyle = value.Style.ToString();
|
||||
ThemeManager.Instance.ChangeTheme(Settings.Theme);
|
||||
_theme.ChangeTheme();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -355,9 +416,9 @@ public partial class SettingsPaneThemeViewModel : BaseModel
|
|||
{
|
||||
get
|
||||
{
|
||||
if (Fonts.SystemFontFamilies.Count(o =>
|
||||
if (Fonts.SystemFontFamilies.Any(o =>
|
||||
o.FamilyNames.Values != null &&
|
||||
o.FamilyNames.Values.Contains(Settings.ResultSubFont)) > 0)
|
||||
o.FamilyNames.Values.Contains(Settings.ResultSubFont)))
|
||||
{
|
||||
var font = new FontFamily(Settings.ResultSubFont);
|
||||
return font;
|
||||
|
|
@ -371,7 +432,7 @@ public partial class SettingsPaneThemeViewModel : BaseModel
|
|||
set
|
||||
{
|
||||
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.ResultSubFontWeight = value.Weight.ToString();
|
||||
Settings.ResultSubFontStyle = value.Style.ToString();
|
||||
ThemeManager.Instance.ChangeTheme(Settings.Theme);
|
||||
_theme.ChangeTheme();
|
||||
}
|
||||
}
|
||||
|
||||
public string ThemeImage => Constant.QueryTextBoxIconImagePath;
|
||||
|
||||
public SettingsPaneThemeViewModel(Settings settings)
|
||||
{
|
||||
Settings = settings;
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void OpenThemesFolder()
|
||||
{
|
||||
App.API.OpenDirectory(Path.Combine(DataLocation.DataDirectory(), Constant.Themes));
|
||||
}
|
||||
|
||||
public void UpdateColorScheme()
|
||||
{
|
||||
ThemeManagerForColorSchemeSwitch.Current.ApplicationTheme = Settings.ColorScheme switch
|
||||
{
|
||||
Constant.Light => ApplicationTheme.Light,
|
||||
Constant.Dark => ApplicationTheme.Dark,
|
||||
Constant.System => null,
|
||||
_ => ThemeManagerForColorSchemeSwitch.Current.ApplicationTheme
|
||||
};
|
||||
}
|
||||
|
||||
public SettingsPaneThemeViewModel(Settings settings)
|
||||
{
|
||||
Settings = settings;
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
public void Reset()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -303,7 +303,7 @@
|
|||
Width="400"
|
||||
Margin="40 30 0 30"
|
||||
SnapsToDevicePixels="True"
|
||||
Style="{DynamicResource WindowBorderStyle}">
|
||||
Style="{DynamicResource PreviewWindowBorderStyle}">
|
||||
<Border BorderThickness="0" Style="{DynamicResource WindowRadius}">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
|
|
@ -370,17 +370,6 @@
|
|||
</DockPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
<!-- Drop shadow effect -->
|
||||
<cc:Card
|
||||
Title="{DynamicResource queryWindowShadowEffect}"
|
||||
Margin="0 8 0 0"
|
||||
Icon=""
|
||||
Sub="{DynamicResource shadowEffectCPUUsage}">
|
||||
<ui:ToggleSwitch
|
||||
IsOn="{Binding DropShadowEffect}"
|
||||
OffContent="{DynamicResource disable}"
|
||||
OnContent="{DynamicResource enable}" />
|
||||
</cc:Card>
|
||||
|
||||
<!-- Theme -->
|
||||
<cc:ExCard
|
||||
|
|
@ -469,12 +458,46 @@
|
|||
</ListBox.Template>
|
||||
</ListBox>
|
||||
</cc:ExCard>
|
||||
|
||||
<cc:CardGroup Margin="0 10 0 0">
|
||||
<!-- Backdrop effect -->
|
||||
<cc:Card
|
||||
Title="{DynamicResource BackdropType}"
|
||||
Margin="0 0 0 0"
|
||||
Icon="">
|
||||
<ComboBox
|
||||
MinWidth="160"
|
||||
VerticalAlignment="Center"
|
||||
DisplayMemberPath="Display"
|
||||
FontSize="14"
|
||||
IsEnabled="{Binding IsBackdropEnabled}"
|
||||
ItemsSource="{Binding BackdropTypesList}"
|
||||
SelectedValue="{Binding BackdropType, Mode=TwoWay}"
|
||||
SelectedValuePath="Value" />
|
||||
<!-- ✅ 추가 -->
|
||||
</cc:Card>
|
||||
|
||||
|
||||
<!-- Drop shadow effect -->
|
||||
<cc:Card
|
||||
Title="{DynamicResource queryWindowShadowEffect}"
|
||||
Margin="0 0 0 0"
|
||||
Icon="">
|
||||
<ui:ToggleSwitch
|
||||
IsEnabled="{Binding IsDropShadowEnabled}"
|
||||
IsOn="{Binding DropShadowEffect}"
|
||||
OffContent="{DynamicResource disable}"
|
||||
OnContent="{DynamicResource enable}" />
|
||||
</cc:Card>
|
||||
</cc:CardGroup>
|
||||
<cc:HyperLink
|
||||
Margin="10"
|
||||
HorizontalAlignment="Right"
|
||||
Text="{DynamicResource browserMoreThemes}"
|
||||
Uri="{Binding LinkThemeGallery}" />
|
||||
|
||||
|
||||
|
||||
<!-- Fixed Height -->
|
||||
<cc:CardGroup Margin="0 20 0 0">
|
||||
<cc:Card
|
||||
|
|
@ -667,9 +690,8 @@
|
|||
DisplayMemberPath="Display"
|
||||
FontSize="14"
|
||||
ItemsSource="{Binding ColorSchemes}"
|
||||
SelectedValue="{Binding Settings.ColorScheme}"
|
||||
SelectedValuePath="Value"
|
||||
SelectionChanged="Selector_OnSelectionChanged" />
|
||||
SelectedValue="{Binding ColorScheme, Mode=TwoWay}"
|
||||
SelectedValuePath="Value" />
|
||||
</cc:Card>
|
||||
|
||||
<!-- Theme folder -->
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
using System.Windows.Controls;
|
||||
using System.Windows.Navigation;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Flow.Launcher.SettingPages.ViewModels;
|
||||
|
|
@ -23,9 +22,4 @@ public partial class SettingsPaneTheme : Page
|
|||
|
||||
base.OnNavigatedTo(e);
|
||||
}
|
||||
|
||||
private void Selector_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
_viewModel.UpdateColorScheme();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ using System.Windows.Forms;
|
|||
using System.Windows.Input;
|
||||
using System.Windows.Interop;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Flow.Launcher.Helper;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.SettingPages.Views;
|
||||
|
|
@ -92,13 +92,13 @@ public partial class SettingWindow
|
|||
{
|
||||
if (WindowState == WindowState.Maximized)
|
||||
{
|
||||
MaximizeButton.Visibility = Visibility.Collapsed;
|
||||
MaximizeButton.Visibility = Visibility.Hidden;
|
||||
RestoreButton.Visibility = Visibility.Visible;
|
||||
}
|
||||
else
|
||||
{
|
||||
MaximizeButton.Visibility = Visibility.Visible;
|
||||
RestoreButton.Visibility = Visibility.Collapsed;
|
||||
RestoreButton.Visibility = Visibility.Hidden;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -143,8 +143,8 @@ public partial class SettingWindow
|
|||
private double WindowLeft()
|
||||
{
|
||||
var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position);
|
||||
var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0);
|
||||
var dip2 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.Width, 0);
|
||||
var dip1 = Win32Helper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0);
|
||||
var dip2 = Win32Helper.TransformPixelsToDIP(this, screen.WorkingArea.Width, 0);
|
||||
var left = (dip2.X - ActualWidth) / 2 + dip1.X;
|
||||
return left;
|
||||
}
|
||||
|
|
@ -152,8 +152,8 @@ public partial class SettingWindow
|
|||
private double WindowTop()
|
||||
{
|
||||
var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position);
|
||||
var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Y);
|
||||
var dip2 = WindowsInteropHelper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Height);
|
||||
var dip1 = Win32Helper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Y);
|
||||
var dip2 = Win32Helper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Height);
|
||||
var top = (dip2.Y - ActualHeight) / 2 + dip1.Y - 20;
|
||||
return top;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@
|
|||
</Style>
|
||||
|
||||
<Style x:Key="BasePendingLineStyle" TargetType="{x:Type Line}">
|
||||
<Setter Property="Stroke" Value="Blue" />
|
||||
<Setter Property="Stroke" Value="{StaticResource SystemAccentColorLight1Brush}" />
|
||||
</Style>
|
||||
|
||||
<Style x:Key="BaseClockPanelPosition" TargetType="{x:Type Canvas}" />
|
||||
|
|
@ -165,29 +165,6 @@
|
|||
BasedOn="{StaticResource BaseClockPanel}"
|
||||
TargetType="{x:Type StackPanel}">
|
||||
<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>
|
||||
|
||||
<!-- Item Style -->
|
||||
|
|
@ -557,6 +534,14 @@
|
|||
<Setter Property="TextWrapping" Value="Wrap" />
|
||||
</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
|
||||
x:Key="PreviewItemTitleStyle"
|
||||
|
|
|
|||
|
|
@ -12,6 +12,9 @@
|
|||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<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}">
|
||||
<Setter Property="CornerRadius" Value="0" />
|
||||
|
|
@ -139,8 +142,8 @@
|
|||
BasedOn="{StaticResource BaseSearchIconStyle}"
|
||||
TargetType="{x:Type Path}">
|
||||
<Setter Property="Fill" Value="#ffffff" />
|
||||
<Setter Property="Width" Value="32" />
|
||||
<Setter Property="Height" Value="32" />
|
||||
<Setter Property="Width" Value="30" />
|
||||
<Setter Property="Height" Value="30" />
|
||||
<Setter Property="Opacity" Value="0.2" />
|
||||
</Style>
|
||||
<Style x:Key="ItemHotkeyStyle" TargetType="{x:Type TextBlock}">
|
||||
|
|
|
|||
|
|
@ -10,8 +10,11 @@
|
|||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/Themes/Base.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<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}">
|
||||
<Setter Property="CornerRadius" Value="0" />
|
||||
</Style>
|
||||
|
|
@ -135,8 +138,8 @@
|
|||
BasedOn="{StaticResource BaseSearchIconStyle}"
|
||||
TargetType="{x:Type Path}">
|
||||
<Setter Property="Fill" Value="#ffffff" />
|
||||
<Setter Property="Width" Value="32" />
|
||||
<Setter Property="Height" Value="32" />
|
||||
<Setter Property="Width" Value="30" />
|
||||
<Setter Property="Height" Value="30" />
|
||||
<Setter Property="Opacity" Value="0.5" />
|
||||
</Style>
|
||||
<Style x:Key="ItemHotkeyStyle" TargetType="{x:Type TextBlock}">
|
||||
|
|
|
|||
|
|
@ -10,14 +10,24 @@
|
|||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/Themes/Base.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<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
|
||||
x:Key="ItemGlyph"
|
||||
BasedOn="{StaticResource BaseGlyphStyle}"
|
||||
TargetType="{x:Type TextBlock}">
|
||||
<Setter Property="Foreground" Value="#FF000000" />
|
||||
</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}">
|
||||
<Setter Property="CornerRadius" Value="0" />
|
||||
</Style>
|
||||
|
|
@ -33,7 +43,9 @@
|
|||
<Style
|
||||
x:Key="QuerySuggestionBoxStyle"
|
||||
BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}"
|
||||
TargetType="{x:Type TextBox}" />
|
||||
TargetType="{x:Type TextBox}">
|
||||
<Setter Property="Foreground" Value="#39000000" />
|
||||
</Style>
|
||||
|
||||
<Style
|
||||
x:Key="WindowBorderStyle"
|
||||
|
|
@ -76,7 +88,7 @@
|
|||
x:Key="ItemSubTitleStyle"
|
||||
BasedOn="{StaticResource BaseItemSubTitleStyle}"
|
||||
TargetType="{x:Type TextBlock}">
|
||||
<Setter Property="Foreground" Value="#818181" />
|
||||
<Setter Property="Foreground" Value="#85000000" />
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="ItemTitleSelectedStyle"
|
||||
|
|
@ -88,18 +100,18 @@
|
|||
x:Key="ItemSubTitleSelectedStyle"
|
||||
BasedOn="{StaticResource BaseItemSubTitleSelectedStyle}"
|
||||
TargetType="{x:Type TextBlock}">
|
||||
<Setter Property="Foreground" Value="#72767d" />
|
||||
<Setter Property="Foreground" Value="#85000000" />
|
||||
</Style>
|
||||
<SolidColorBrush
|
||||
x:Key="ItemSelectedBackgroundColor"
|
||||
Opacity="0.5"
|
||||
Color="#ccd0d4" />
|
||||
Opacity="0.6"
|
||||
Color="#33000000" />
|
||||
|
||||
<Style
|
||||
x:Key="SeparatorStyle"
|
||||
BasedOn="{StaticResource BaseSeparatorStyle}"
|
||||
TargetType="{x:Type Rectangle}">
|
||||
<Setter Property="Fill" Value="#c6c6c6" />
|
||||
<Setter Property="Fill" Value="#22000000" />
|
||||
<Setter Property="Height" Value="1" />
|
||||
<Setter Property="Margin" Value="12 0 12 8" />
|
||||
</Style>
|
||||
|
|
@ -112,7 +124,7 @@
|
|||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type Thumb}">
|
||||
<Border
|
||||
Background="#bebebe"
|
||||
Background="#3C000000"
|
||||
BorderBrush="Transparent"
|
||||
BorderThickness="0"
|
||||
CornerRadius="2"
|
||||
|
|
@ -132,8 +144,8 @@
|
|||
BasedOn="{StaticResource BaseSearchIconStyle}"
|
||||
TargetType="{x:Type Path}">
|
||||
<Setter Property="Fill" Value="#000000" />
|
||||
<Setter Property="Width" Value="32" />
|
||||
<Setter Property="Height" Value="32" />
|
||||
<Setter Property="Width" Value="30" />
|
||||
<Setter Property="Height" Value="30" />
|
||||
<Setter Property="Opacity" Value="0.2" />
|
||||
</Style>
|
||||
<Style x:Key="ItemHotkeyStyle" TargetType="{x:Type TextBlock}">
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<!--
|
||||
Name: Circle
|
||||
IsDark: True
|
||||
HasBlur: False
|
||||
HasBlur: True
|
||||
-->
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
|
|
@ -12,6 +12,10 @@
|
|||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/Themes/Base.xaml" />
|
||||
</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
|
||||
x:Key="ItemGlyph"
|
||||
BasedOn="{StaticResource BaseGlyphStyle}"
|
||||
|
|
@ -23,7 +27,7 @@
|
|||
BasedOn="{StaticResource BaseQueryBoxStyle}"
|
||||
TargetType="{x:Type TextBox}">
|
||||
<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="Height" Value="38" />
|
||||
</Style>
|
||||
|
|
@ -75,7 +79,7 @@
|
|||
x:Key="SeparatorStyle"
|
||||
BasedOn="{StaticResource BaseSeparatorStyle}"
|
||||
TargetType="{x:Type Rectangle}">
|
||||
<Setter Property="Fill" Value="{m:DynamicColor SystemControlBackgroundChromeMediumBrush}" />
|
||||
<Setter Property="Fill" Value="{DynamicResource SeparatorForeground}" />
|
||||
<Setter Property="Height" Value="1" />
|
||||
<Setter Property="Margin" Value="0 0 0 8" />
|
||||
</Style>
|
||||
|
|
|
|||
|
|
@ -29,7 +29,9 @@
|
|||
<Style
|
||||
x:Key="WindowBorderStyle"
|
||||
BasedOn="{StaticResource BaseWindowBorderStyle}"
|
||||
TargetType="{x:Type Border}" />
|
||||
TargetType="{x:Type Border}">
|
||||
<Setter Property="Background" Value="#2F2F2F" />
|
||||
</Style>
|
||||
|
||||
<Style
|
||||
x:Key="WindowStyle"
|
||||
|
|
@ -89,7 +91,7 @@
|
|||
TargetType="{x:Type Rectangle}">
|
||||
<Setter Property="Fill" Value="#3b3b3b" />
|
||||
<Setter Property="Height" Value="1" />
|
||||
<Setter Property="Margin" Value="8,0,8,8" />
|
||||
<Setter Property="Margin" Value="8 0 8 8" />
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="PreviewBorderStyle"
|
||||
|
|
|
|||
|
|
@ -2,46 +2,45 @@
|
|||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/Themes/Base.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<Thickness x:Key="ResultMargin">0 0 0 0</Thickness>
|
||||
<Thickness x:Key="ResultMargin">0 0 0 4</Thickness>
|
||||
<Style
|
||||
x:Key="ItemGlyph"
|
||||
BasedOn="{StaticResource BaseGlyphStyle}"
|
||||
TargetType="{x:Type TextBlock}">
|
||||
<Setter Property="Foreground" Value="#0e172c" />
|
||||
<Setter Property="Foreground" Value="#ffffff" />
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="QueryBoxStyle"
|
||||
BasedOn="{StaticResource BaseQueryBoxStyle}"
|
||||
TargetType="{x:Type TextBox}">
|
||||
<Setter Property="Foreground" Value="#0E172C" />
|
||||
<Setter Property="CaretBrush" Value="#0E172C" />
|
||||
<Setter Property="SelectionBrush" Value="#a786df" />
|
||||
<Setter Property="Foreground" Value="#cc1081" />
|
||||
<Setter Property="CaretBrush" Value="#cc1081" />
|
||||
<Setter Property="SelectionBrush" Value="#e564b1" />
|
||||
</Style>
|
||||
|
||||
<Style
|
||||
x:Key="QuerySuggestionBoxStyle"
|
||||
BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}"
|
||||
TargetType="{x:Type TextBox}">
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="Foreground" Value="#E9819F" />
|
||||
<Setter Property="Background" Value="#1f1d1f" />
|
||||
<Setter Property="Foreground" Value="#71114b" />
|
||||
</Style>
|
||||
|
||||
<Style
|
||||
x:Key="WindowBorderStyle"
|
||||
BasedOn="{StaticResource BaseWindowBorderStyle}"
|
||||
TargetType="{x:Type Border}">
|
||||
<Setter Property="CornerRadius" Value="8" />
|
||||
<Setter Property="Background" Value="#fec7d7" />
|
||||
<Setter Property="BorderThickness" Value="3" />
|
||||
<Setter Property="BorderBrush" Value="#0e172c" />
|
||||
<Setter Property="Background" Value="#1f1d1f" />
|
||||
<Setter Property="BorderThickness" Value="2" />
|
||||
<Setter Property="BorderBrush" Value="#000000" />
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="SeparatorStyle"
|
||||
BasedOn="{StaticResource BaseSeparatorStyle}"
|
||||
TargetType="{x:Type Rectangle}">
|
||||
<Setter Property="Fill" Value="#0e172c" />
|
||||
<Setter Property="Height" Value="3" />
|
||||
<Setter Property="Margin" Value="0 0 0 0" />
|
||||
<Setter Property="Fill" Value="#000000" />
|
||||
<Setter Property="Height" Value="1" />
|
||||
<Setter Property="Margin" Value="12 0 12 4" />
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="WindowStyle"
|
||||
|
|
@ -51,33 +50,34 @@
|
|||
x:Key="PendingLineStyle"
|
||||
BasedOn="{StaticResource BasePendingLineStyle}"
|
||||
TargetType="{x:Type Line}">
|
||||
<Setter Property="Stroke" Value="#a786df" />
|
||||
<Setter Property="Stroke" Value="#cc1081" />
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="ItemTitleStyle"
|
||||
BasedOn="{StaticResource BaseItemTitleStyle}"
|
||||
TargetType="{x:Type TextBlock}">
|
||||
<Setter Property="Foreground" Value="#0e172c" />
|
||||
<Setter Property="Foreground" Value="#f5f5f5" />
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="ItemSubTitleStyle"
|
||||
BasedOn="{StaticResource BaseItemSubTitleStyle}"
|
||||
TargetType="{x:Type TextBlock}">
|
||||
<Setter Property="Foreground" Value="#0e172c" />
|
||||
<Setter Property="Foreground" Value="#c2c2c2" />
|
||||
<Setter Property="Opacity" Value="0.5" />
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="ItemTitleSelectedStyle"
|
||||
BasedOn="{StaticResource BaseItemTitleSelectedStyle}"
|
||||
TargetType="{x:Type TextBlock}">
|
||||
<Setter Property="Foreground" Value="#fffffe" />
|
||||
<Setter Property="Foreground" Value="#f5f5f5" />
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="ItemSubTitleSelectedStyle"
|
||||
BasedOn="{StaticResource BaseItemSubTitleSelectedStyle}"
|
||||
TargetType="{x:Type TextBlock}">
|
||||
<Setter Property="Foreground" Value="#fffffe" />
|
||||
<Setter Property="Foreground" Value="#ed92c9" />
|
||||
</Style>
|
||||
<SolidColorBrush x:Key="ItemSelectedBackgroundColor">#0e172c</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="ItemSelectedBackgroundColor">#cc1081</SolidColorBrush>
|
||||
<Style
|
||||
x:Key="ThumbStyle"
|
||||
BasedOn="{StaticResource BaseThumbStyle}"
|
||||
|
|
@ -86,7 +86,7 @@
|
|||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type Thumb}">
|
||||
<Border
|
||||
Background="#E9819F"
|
||||
Background="#e564b1"
|
||||
BorderBrush="Transparent"
|
||||
BorderThickness="0"
|
||||
CornerRadius="2"
|
||||
|
|
@ -109,56 +109,53 @@
|
|||
<Setter Property="HorizontalAlignment" Value="Right" />
|
||||
</Style>
|
||||
<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="Height" Value="28" />
|
||||
</Style>
|
||||
<Style x:Key="ItemHotkeyStyle" TargetType="{x:Type TextBlock}">
|
||||
<Setter Property="FontSize" Value="11" />
|
||||
<Setter Property="Foreground" Value="#E9819F" />
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="Foreground" Value="#ed92c9" />
|
||||
</Style>
|
||||
<Style x:Key="ItemHotkeySelectedStyle" TargetType="{x:Type TextBlock}">
|
||||
<Setter Property="FontSize" Value="11" />
|
||||
<Setter Property="Foreground" Value="#E9819F" />
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="Foreground" Value="#ed92c9" />
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="ClockBox"
|
||||
BasedOn="{StaticResource BaseClockBox}"
|
||||
TargetType="{x:Type TextBlock}">
|
||||
<Setter Property="Foreground" Value="#E9819F" />
|
||||
<Setter Property="Foreground" Value="#71114b" />
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="DateBox"
|
||||
BasedOn="{StaticResource BaseDateBox}"
|
||||
TargetType="{x:Type TextBlock}">
|
||||
<Setter Property="FontWeight" Value="Bold" />
|
||||
<Setter Property="Foreground" Value="#E9819F" />
|
||||
<Setter Property="Foreground" Value="#71114b" />
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="PreviewBorderStyle"
|
||||
BasedOn="{StaticResource BasePreviewBorderStyle}"
|
||||
TargetType="{x:Type Border}">
|
||||
<Setter Property="Margin" Value="0 0 10 0" />
|
||||
<Setter Property="Width" Value="5" />
|
||||
<Setter Property="BorderThickness" Value="3 0 0 0" />
|
||||
<Setter Property="BorderBrush" Value="#0e172c" />
|
||||
<Setter Property="BorderBrush" Value="#000000" />
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="PreviewItemTitleStyle"
|
||||
BasedOn="{StaticResource BasePreviewItemTitleStyle}"
|
||||
TargetType="{x:Type TextBlock}">
|
||||
<Setter Property="Foreground" Value="#0E172C" />
|
||||
<Setter Property="Foreground" Value="#f5f5f5" />
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="PreviewItemSubTitleStyle"
|
||||
BasedOn="{StaticResource BasePreviewItemSubTitleStyle}"
|
||||
TargetType="{x:Type TextBlock}">
|
||||
<Setter Property="Foreground" Value="#0E172C" />
|
||||
<Setter Property="Foreground" Value="#c2c2c2" />
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="PreviewGlyph"
|
||||
BasedOn="{StaticResource BasePreviewGlyph}"
|
||||
TargetType="{x:Type TextBlock}">
|
||||
<Setter Property="Foreground" Value="#0E172C" />
|
||||
<Setter Property="Foreground" Value="#ffffff" />
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
|
|
@ -32,7 +32,7 @@
|
|||
x:Key="QueryBoxStyle"
|
||||
BasedOn="{StaticResource BaseQueryBoxStyle}"
|
||||
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="Foreground" Value="#ffffff" />
|
||||
<Setter Property="FontSize" Value="18" />
|
||||
|
|
@ -43,7 +43,7 @@
|
|||
x:Key="QuerySuggestionBoxStyle"
|
||||
BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}"
|
||||
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="Height" Value="38" />
|
||||
<Setter Property="FontSize" Value="18" />
|
||||
|
|
@ -56,15 +56,7 @@
|
|||
TargetType="{x:Type Border}">
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
<Setter Property="BorderBrush" Value="#e2e2e2" />
|
||||
<Setter Property="Background">
|
||||
<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="Background" Value="#2e2e2e" />
|
||||
<Setter Property="CornerRadius" Value="8" />
|
||||
<Setter Property="UseLayoutRounding" Value="True" />
|
||||
</Style>
|
||||
|
|
@ -96,7 +88,7 @@
|
|||
TargetType="{x:Type Rectangle}">
|
||||
<Setter Property="Fill" Value="#3b3b3b" />
|
||||
<Setter Property="Height" Value="1" />
|
||||
<Setter Property="Margin" Value="0,0,0,8" />
|
||||
<Setter Property="Margin" Value="0 0 0 8" />
|
||||
</Style>
|
||||
<Style x:Key="HighlightStyle" />
|
||||
<Style
|
||||
|
|
@ -147,7 +139,7 @@
|
|||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="Width" 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" />
|
||||
</Style>
|
||||
|
||||
|
|
@ -174,7 +166,7 @@
|
|||
x:Key="ClockPanel"
|
||||
BasedOn="{StaticResource ClockPanel}"
|
||||
TargetType="{x:Type StackPanel}">
|
||||
<Setter Property="Margin" Value="0,0,54,2" />
|
||||
<Setter Property="Margin" Value="0 0 54 2" />
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="ClockBox"
|
||||
|
|
|
|||
|
|
@ -11,6 +11,10 @@
|
|||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/Themes/Base.xaml" />
|
||||
</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>
|
||||
<Style
|
||||
x:Key="ItemGlyph"
|
||||
|
|
@ -175,13 +179,13 @@
|
|||
x:Key="ClockBox"
|
||||
BasedOn="{StaticResource BaseClockBox}"
|
||||
TargetType="{x:Type TextBlock}">
|
||||
<Setter Property="Foreground" Value="{DynamicResource ClockDateForeground}" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource SystemControlBackgroundBaseLowBrush}" />
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="DateBox"
|
||||
BasedOn="{StaticResource BaseDateBox}"
|
||||
TargetType="{x:Type TextBlock}">
|
||||
<Setter Property="Foreground" Value="{DynamicResource ClockDateForeground}" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource SystemControlBackgroundBaseLowBrush}" />
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="PreviewBorderStyle"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<!--
|
||||
Name: Windows 11
|
||||
IsDark: True
|
||||
HasBlur: False
|
||||
HasBlur: True
|
||||
-->
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
|
|
@ -12,6 +12,10 @@
|
|||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/Themes/Base.xaml" />
|
||||
</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
|
||||
x:Key="BulletStyle"
|
||||
BasedOn="{StaticResource BaseBulletStyle}"
|
||||
|
|
|
|||
|
|
@ -1,31 +1,29 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Windows.Input;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Channels;
|
||||
using System.Threading.Tasks;
|
||||
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.Resource;
|
||||
using Flow.Launcher.Helper;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.Hotkey;
|
||||
using Flow.Launcher.Infrastructure.Image;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Infrastructure.Storage;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
using Flow.Launcher.Storage;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
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
|
||||
{
|
||||
|
|
@ -48,8 +46,6 @@ namespace Flow.Launcher.ViewModel
|
|||
private CancellationTokenSource _updateSource;
|
||||
private CancellationToken _updateToken;
|
||||
|
||||
private readonly Internationalization _translator = InternationalizationManager.Instance;
|
||||
|
||||
private ChannelWriter<ResultsForUpdate> _resultsUpdateChannelWriter;
|
||||
private Task _resultsViewUpdateTask;
|
||||
|
||||
|
|
@ -141,7 +137,6 @@ namespace Flow.Launcher.ViewModel
|
|||
_userSelectedRecord = _userSelectedRecordStorage.Load();
|
||||
_topMostRecord = _topMostRecordStorage.Load();
|
||||
|
||||
|
||||
ContextMenu = new ResultsViewModel(Settings)
|
||||
{
|
||||
LeftClickResultCommand = OpenResultCommand,
|
||||
|
|
@ -182,9 +177,9 @@ namespace Flow.Launcher.ViewModel
|
|||
var resultUpdateChannel = Channel.CreateUnbounded<ResultsForUpdate>();
|
||||
_resultsUpdateChannelWriter = resultUpdateChannel.Writer;
|
||||
_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 channelReader = resultUpdateChannel.Reader;
|
||||
|
|
@ -213,7 +208,7 @@ namespace Flow.Launcher.ViewModel
|
|||
#else
|
||||
Log.Error($"Error happen in task dealing with viewupdate for results. {t.Exception}");
|
||||
_resultsViewUpdateTask =
|
||||
Task.Run(updateAction).ContinueWith(continueAction, TaskContinuationOptions.OnlyOnFaulted);
|
||||
Task.Run(UpdateActionAsync).ContinueWith(continueAction, CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default);
|
||||
#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]
|
||||
private async Task ReloadPluginDataAsync()
|
||||
{
|
||||
Hide();
|
||||
|
||||
await PluginManager.ReloadDataAsync().ConfigureAwait(false);
|
||||
Notification.Show(InternationalizationManager.Instance.GetTranslation("success"),
|
||||
InternationalizationManager.Instance.GetTranslation("completedSuccessfully"));
|
||||
Notification.Show(App.API.GetTranslation("success"),
|
||||
App.API.GetTranslation("completedSuccessfully"));
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
|
|
@ -274,14 +282,14 @@ namespace Flow.Launcher.ViewModel
|
|||
{
|
||||
if (SelectedIsFromQueryResults())
|
||||
{
|
||||
QueryResults(null, isReQuery: true);
|
||||
_ = QueryResultsAsync(null, isReQuery: true);
|
||||
}
|
||||
}
|
||||
|
||||
public void ReQuery(bool reselect)
|
||||
{
|
||||
BackToQueryResults();
|
||||
QueryResults(null, isReQuery: true, reSelect: reselect);
|
||||
_ = QueryResultsAsync(null, isReQuery: true, reSelect: reselect);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
|
|
@ -289,7 +297,7 @@ namespace Flow.Launcher.ViewModel
|
|||
{
|
||||
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)
|
||||
{
|
||||
lastHistoryIndex++;
|
||||
|
|
@ -302,7 +310,7 @@ namespace Flow.Launcher.ViewModel
|
|||
{
|
||||
if (_history.Items.Count > 0)
|
||||
{
|
||||
ChangeQueryText(_history.Items[_history.Items.Count - lastHistoryIndex].Query.ToString());
|
||||
ChangeQueryText(_history.Items[^lastHistoryIndex].Query.ToString());
|
||||
if (lastHistoryIndex > 1)
|
||||
{
|
||||
lastHistoryIndex--;
|
||||
|
|
@ -353,12 +361,12 @@ namespace Flow.Launcher.ViewModel
|
|||
}
|
||||
else if (!string.IsNullOrEmpty(SelectedResults.SelectedItem?.QuerySuggestionText))
|
||||
{
|
||||
var defaultSuggestion = SelectedResults.SelectedItem.QuerySuggestionText;
|
||||
// check if result.actionkeywordassigned is empty
|
||||
if (!string.IsNullOrEmpty(result.ActionKeywordAssigned))
|
||||
{
|
||||
autoCompleteText = $"{result.ActionKeywordAssigned} {defaultSuggestion}";
|
||||
}
|
||||
//var defaultSuggestion = SelectedResults.SelectedItem.QuerySuggestionText;
|
||||
//// check if result.actionkeywordassigned is empty
|
||||
//if (!string.IsNullOrEmpty(result.ActionKeywordAssigned))
|
||||
//{
|
||||
// autoCompleteText = $"{result.ActionKeywordAssigned} {defaultSuggestion}";
|
||||
//}
|
||||
|
||||
autoCompleteText = SelectedResults.SelectedItem.QuerySuggestionText;
|
||||
}
|
||||
|
|
@ -389,11 +397,11 @@ namespace Flow.Launcher.ViewModel
|
|||
}
|
||||
|
||||
var hideWindow = await result.ExecuteAsync(new ActionContext
|
||||
{
|
||||
// not null means pressing modifier key + number, should ignore the modifier key
|
||||
SpecialKeyState = index is not null ? SpecialKeyState.Default : GlobalHotkey.CheckModifiers()
|
||||
})
|
||||
.ConfigureAwait(false);
|
||||
{
|
||||
// not null means pressing modifier key + number, should ignore the modifier key
|
||||
SpecialKeyState = index is not null ? SpecialKeyState.Default : GlobalHotkey.CheckModifiers()
|
||||
})
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (SelectedIsFromQueryResults())
|
||||
{
|
||||
|
|
@ -457,7 +465,6 @@ namespace Flow.Launcher.ViewModel
|
|||
SelectedResults.SelectLastResult();
|
||||
}
|
||||
|
||||
|
||||
[RelayCommand]
|
||||
private void SelectPrevPage()
|
||||
{
|
||||
|
|
@ -484,7 +491,6 @@ namespace Flow.Launcher.ViewModel
|
|||
{
|
||||
SelectedResults.SelectPrevResult();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
|
|
@ -539,19 +545,6 @@ namespace Flow.Launcher.ViewModel
|
|||
public string ClockText { 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 ContextMenu { get; private set; }
|
||||
|
|
@ -561,7 +554,6 @@ namespace Flow.Launcher.ViewModel
|
|||
public bool GameModeStatus { get; set; } = false;
|
||||
|
||||
private string _queryText;
|
||||
|
||||
public string QueryText
|
||||
{
|
||||
get => _queryText;
|
||||
|
|
@ -683,7 +675,6 @@ namespace Flow.Launcher.ViewModel
|
|||
Results.Visibility = Visibility.Collapsed;
|
||||
_queryTextBeforeLeaveResults = QueryText;
|
||||
|
||||
|
||||
// Because of Fody's optimization
|
||||
// setter won't be called when property value is not changed.
|
||||
// so we need manually call Query()
|
||||
|
|
@ -761,7 +752,7 @@ namespace Flow.Launcher.ViewModel
|
|||
|
||||
public string OpenResultCommandModifiers => Settings.OpenResultModifiers;
|
||||
|
||||
public string VerifyOrSetDefaultHotkey(string hotkey, string defaultHotkey)
|
||||
private static string VerifyOrSetDefaultHotkey(string hotkey, string defaultHotkey)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
@ -790,9 +781,6 @@ namespace Flow.Launcher.ViewModel
|
|||
public string CycleHistoryUpHotkey => VerifyOrSetDefaultHotkey(Settings.CycleHistoryUpHotkey, "Alt+Up");
|
||||
public string CycleHistoryDownHotkey => VerifyOrSetDefaultHotkey(Settings.CycleHistoryDownHotkey, "Alt+Down");
|
||||
|
||||
|
||||
public string Image => Constant.QueryTextBoxIconImagePath;
|
||||
|
||||
public bool StartWithEnglishMode => Settings.AlwaysStartEn;
|
||||
|
||||
#endregion
|
||||
|
|
@ -812,8 +800,8 @@ namespace Flow.Launcher.ViewModel
|
|||
throw new NotImplementedException("ResultAreaColumn should match ResultAreaColumnPreviewShown/ResultAreaColumnPreviewHidden value");
|
||||
#else
|
||||
Log.Error("MainViewModel", "ResultAreaColumnPreviewHidden/ResultAreaColumnPreviewShown int value not implemented", "InternalPreviewVisible");
|
||||
#endif
|
||||
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)
|
||||
{
|
||||
_ = PluginManager.OpenExternalPreviewAsync(path, sendFailToast).ConfigureAwait(false);
|
||||
|
|
@ -901,7 +877,7 @@ namespace Flow.Launcher.ViewModel
|
|||
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);
|
||||
}
|
||||
|
|
@ -983,7 +959,7 @@ namespace Flow.Launcher.ViewModel
|
|||
{
|
||||
if (SelectedIsFromQueryResults())
|
||||
{
|
||||
QueryResults(searchDelay, isReQuery);
|
||||
_ = QueryResultsAsync(searchDelay, isReQuery);
|
||||
}
|
||||
else if (ContextMenuSelected())
|
||||
{
|
||||
|
|
@ -1054,8 +1030,8 @@ namespace Flow.Launcher.ViewModel
|
|||
var results = new List<Result>();
|
||||
foreach (var h in _history.Items)
|
||||
{
|
||||
var title = _translator.GetTranslation("executeQuery");
|
||||
var time = _translator.GetTranslation("lastExecuteTime");
|
||||
var title = App.API.GetTranslation("executeQuery");
|
||||
var time = App.API.GetTranslation("lastExecuteTime");
|
||||
var result = new Result
|
||||
{
|
||||
Title = string.Format(title, h.Query),
|
||||
|
|
@ -1088,8 +1064,8 @@ namespace Flow.Launcher.ViewModel
|
|||
}
|
||||
|
||||
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");
|
||||
|
||||
|
|
@ -1111,8 +1087,7 @@ namespace Flow.Launcher.ViewModel
|
|||
|
||||
var currentUpdateSource = new CancellationTokenSource();
|
||||
_updateSource = currentUpdateSource;
|
||||
var currentCancellationToken = _updateSource.Token;
|
||||
_updateToken = currentCancellationToken;
|
||||
_updateToken = _updateSource.Token;
|
||||
|
||||
ProgressBarVisibility = Visibility.Hidden;
|
||||
_isQueryRunning = true;
|
||||
|
|
@ -1120,7 +1095,7 @@ namespace Flow.Launcher.ViewModel
|
|||
// Switch to ThreadPool thread
|
||||
await TaskScheduler.Default;
|
||||
|
||||
if (currentCancellationToken.IsCancellationRequested)
|
||||
if (_updateSource.Token.IsCancellationRequested)
|
||||
return;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
if (query.ActionKeyword == Plugin.Query.GlobalPluginWildcardSign)
|
||||
{
|
||||
// Wait 45 millisecond for query change in global query
|
||||
// if query changes, return so that it won't be calculated
|
||||
await Task.Delay(45, currentCancellationToken);
|
||||
if (currentCancellationToken.IsCancellationRequested)
|
||||
await Task.Delay(45, _updateSource.Token);
|
||||
if (_updateSource.Token.IsCancellationRequested)
|
||||
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
|
||||
if (!currentCancellationToken.IsCancellationRequested && _isQueryRunning)
|
||||
if (!_updateSource.Token.IsCancellationRequested && _isQueryRunning)
|
||||
{
|
||||
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
|
||||
|
||||
|
|
@ -1207,7 +1181,7 @@ namespace Flow.Launcher.ViewModel
|
|||
}
|
||||
System.Diagnostics.Debug.Write("\n");
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
// 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
|
||||
}
|
||||
|
||||
if (currentCancellationToken.IsCancellationRequested)
|
||||
if (_updateSource.Token.IsCancellationRequested)
|
||||
return;
|
||||
|
||||
// this should happen once after all queries are done so progress bar should continue
|
||||
// until the end of all querying
|
||||
_isQueryRunning = false;
|
||||
if (!currentCancellationToken.IsCancellationRequested)
|
||||
if (!_updateSource.Token.IsCancellationRequested)
|
||||
{
|
||||
// update to hidden if this is still the current query
|
||||
ProgressBarVisibility = Visibility.Hidden;
|
||||
}
|
||||
|
||||
// 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
|
||||
// Task.Yield will force it to run in ThreadPool
|
||||
await Task.Yield();
|
||||
|
||||
IReadOnlyList<Result> results =
|
||||
await PluginManager.QueryForPluginAsync(plugin, query, currentCancellationToken);
|
||||
await PluginManager.QueryForPluginAsync(plugin, query, _updateSource.Token);
|
||||
|
||||
currentCancellationToken.ThrowIfCancellationRequested();
|
||||
_updateSource.Token.ThrowIfCancellationRequested();
|
||||
|
||||
IReadOnlyList<Result> resultsCopy;
|
||||
if (results == null)
|
||||
|
|
@ -1254,7 +1228,7 @@ namespace Flow.Launcher.ViewModel
|
|||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
|
@ -1330,13 +1304,13 @@ namespace Flow.Launcher.ViewModel
|
|||
{
|
||||
menu = new Result
|
||||
{
|
||||
Title = InternationalizationManager.Instance.GetTranslation("cancelTopMostInThisQuery"),
|
||||
Title = App.API.GetTranslation("cancelTopMostInThisQuery"),
|
||||
IcoPath = "Images\\down.png",
|
||||
PluginDirectory = Constant.ProgramDirectory,
|
||||
Action = _ =>
|
||||
{
|
||||
_topMostRecord.Remove(result);
|
||||
App.API.ShowMsg(InternationalizationManager.Instance.GetTranslation("success"));
|
||||
App.API.ShowMsg(App.API.GetTranslation("success"));
|
||||
App.API.ReQuery();
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1346,14 +1320,14 @@ namespace Flow.Launcher.ViewModel
|
|||
{
|
||||
menu = new Result
|
||||
{
|
||||
Title = InternationalizationManager.Instance.GetTranslation("setAsTopMostInThisQuery"),
|
||||
Title = App.API.GetTranslation("setAsTopMostInThisQuery"),
|
||||
IcoPath = "Images\\up.png",
|
||||
Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\xeac2"),
|
||||
PluginDirectory = Constant.ProgramDirectory,
|
||||
Action = _ =>
|
||||
{
|
||||
_topMostRecord.AddOrUpdate(result);
|
||||
App.API.ShowMsg(InternationalizationManager.Instance.GetTranslation("success"));
|
||||
App.API.ShowMsg(App.API.GetTranslation("success"));
|
||||
App.API.ReQuery();
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1363,10 +1337,10 @@ namespace Flow.Launcher.ViewModel
|
|||
return menu;
|
||||
}
|
||||
|
||||
private Result ContextMenuPluginInfo(string id)
|
||||
private static Result ContextMenuPluginInfo(string id)
|
||||
{
|
||||
var metadata = PluginManager.GetPluginForId(id).Metadata;
|
||||
var translator = InternationalizationManager.Instance;
|
||||
var translator = App.API;
|
||||
|
||||
var author = translator.GetTranslation("author");
|
||||
var website = translator.GetTranslation("website");
|
||||
|
|
@ -1429,70 +1403,108 @@ namespace Flow.Launcher.ViewModel
|
|||
{
|
||||
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;
|
||||
|
||||
MainWindowOpacity = 1;
|
||||
|
||||
MainWindowVisibilityStatus = true;
|
||||
VisibilityChanged?.Invoke(this, new VisibilityChangedEventArgs { IsVisible = true });
|
||||
});
|
||||
}
|
||||
|
||||
#pragma warning disable VSTHRD100 // Avoid async void methods
|
||||
|
||||
public async void Hide()
|
||||
{
|
||||
lastHistoryIndex = 1;
|
||||
// Trick for no delay
|
||||
MainWindowOpacity = 0;
|
||||
|
||||
if (ExternalPreviewVisible)
|
||||
{
|
||||
CloseExternalPreview();
|
||||
}
|
||||
|
||||
if (!SelectedIsFromQueryResults())
|
||||
{
|
||||
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)
|
||||
{
|
||||
case LastQueryMode.Empty:
|
||||
ChangeQueryText(string.Empty);
|
||||
await Task.Delay(100); //Time for change to opacity
|
||||
break;
|
||||
case LastQueryMode.Preserved:
|
||||
if (Settings.UseAnimation)
|
||||
await Task.Delay(100);
|
||||
LastQuerySelected = true;
|
||||
break;
|
||||
case LastQueryMode.Selected:
|
||||
if (Settings.UseAnimation)
|
||||
await Task.Delay(100);
|
||||
LastQuerySelected = false;
|
||||
LastQuerySelected = (Settings.LastQueryMode == LastQueryMode.Preserved);
|
||||
break;
|
||||
case LastQueryMode.ActionKeywordPreserved or LastQueryMode.ActionKeywordSelected:
|
||||
|
||||
case LastQueryMode.ActionKeywordPreserved:
|
||||
case LastQueryMode.ActionKeywordSelected:
|
||||
var newQuery = _lastQuery.ActionKeyword;
|
||||
if (!string.IsNullOrEmpty(newQuery))
|
||||
newQuery += " ";
|
||||
ChangeQueryText(newQuery);
|
||||
if (Settings.UseAnimation)
|
||||
await Task.Delay(100);
|
||||
|
||||
if (Settings.LastQueryMode == LastQueryMode.ActionKeywordSelected)
|
||||
LastQuerySelected = false;
|
||||
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;
|
||||
MainWindowVisibility = Visibility.Collapsed;
|
||||
VisibilityChanged?.Invoke(this, new VisibilityChangedEventArgs { IsVisible = false });
|
||||
}
|
||||
|
||||
#pragma warning restore VSTHRD100 // Avoid async void methods
|
||||
|
||||
/// <summary>
|
||||
/// Checks if Flow Launcher should ignore any hotkeys
|
||||
/// </summary>
|
||||
public bool ShouldIgnoreHotkeys()
|
||||
{
|
||||
return Settings.IgnoreHotkeysOnFullscreen && WindowsInteropHelper.IsWindowFullscreen() || GameModeStatus;
|
||||
return Settings.IgnoreHotkeysOnFullscreen && Win32Helper.IsForegroundWindowFullscreen() || GameModeStatus;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@ namespace Flow.Launcher.ViewModel
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private async void LoadIconAsync()
|
||||
{
|
||||
Image = await ImageLoader.LoadAsync(PluginPair.Metadata.IcoPath);
|
||||
|
|
@ -119,7 +118,7 @@ namespace Flow.Launcher.ViewModel
|
|||
: null;
|
||||
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 QueryTime => PluginPair.Metadata.AvgQueryTime + "ms";
|
||||
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 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));
|
||||
}
|
||||
|
||||
|
|
@ -169,8 +167,6 @@ namespace Flow.Launcher.ViewModel
|
|||
App.API.ShowMainWindow();
|
||||
}
|
||||
|
||||
public static bool IsActionKeywordRegistered(string newActionKeyword) => PluginManager.ActionKeywordRegistered(newActionKeyword);
|
||||
|
||||
[RelayCommand]
|
||||
private void SetActionKeywords()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -8,77 +8,87 @@
|
|||
d:DesignWidth="500"
|
||||
DataContext="{Binding RelativeSource={RelativeSource Self}}"
|
||||
mc:Ignorable="d">
|
||||
<Grid Margin="60,0,10,0">
|
||||
<Grid Margin="{StaticResource SettingPanelMargin}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel Margin="0,10,0,10" Orientation="Vertical">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Margin="10" Text="{DynamicResource flowlauncher_plugin_browserbookmark_loadBrowserFrom}" />
|
||||
<CheckBox
|
||||
Margin="0,0,15,0"
|
||||
Content="Chrome"
|
||||
IsChecked="{Binding LoadChromeBookmark}" />
|
||||
<CheckBox
|
||||
Margin="0,0,15,0"
|
||||
Content="Edge"
|
||||
IsChecked="{Binding LoadEdgeBookmark}" />
|
||||
<CheckBox
|
||||
Margin="0,0,15,0"
|
||||
Content="Firefox"
|
||||
IsChecked="{Binding LoadFirefoxBookmark}" />
|
||||
<StackPanel
|
||||
Grid.Row="0"
|
||||
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="{StaticResource SettingPanelItemRightMargin}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{DynamicResource flowlauncher_plugin_browserbookmark_loadBrowserFrom}" />
|
||||
<CheckBox
|
||||
Margin="{StaticResource SettingPanelItemRightMargin}"
|
||||
Content="Chrome"
|
||||
IsChecked="{Binding LoadChromeBookmark}" />
|
||||
<CheckBox
|
||||
Margin="{StaticResource SettingPanelItemRightMargin}"
|
||||
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
|
||||
Margin="0,0,15,0"
|
||||
Click="Others_Click"
|
||||
Content="{DynamicResource flowlauncher_plugin_browserbookmark_others}" />
|
||||
</StackPanel>
|
||||
<StackPanel Name="CustomBrowsersList" Visibility="Collapsed">
|
||||
<ListView
|
||||
Name="CustomBrowsers"
|
||||
Height="auto"
|
||||
Margin="10"
|
||||
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 HorizontalAlignment="Right" Orientation="Horizontal">
|
||||
<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>
|
||||
Width="100"
|
||||
Click="NewCustomBrowser"
|
||||
Content="{DynamicResource flowlauncher_plugin_browserbookmark_addBrowserBookmark}" />
|
||||
<Button
|
||||
Width="100"
|
||||
Margin="{StaticResource SettingPanelItemLeftMargin}"
|
||||
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
|
||||
Width="100"
|
||||
Margin="{StaticResource SettingPanelItemLeftMargin}"
|
||||
Click="DeleteCustomBrowser"
|
||||
Content="{DynamicResource flowlauncher_plugin_browserbookmark_removeBrowserBookmark}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Flow.Launcher.Core\Flow.Launcher.Core.csproj" />
|
||||
<ProjectReference Include="..\..\Flow.Launcher.Plugin\Flow.Launcher.Plugin.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -155,16 +155,16 @@ namespace Flow.Launcher.Plugin.Calculator
|
|||
return value.ToString(numberFormatInfo);
|
||||
}
|
||||
|
||||
private string GetDecimalSeparator()
|
||||
private static string GetDecimalSeparator()
|
||||
{
|
||||
string systemDecimalSeperator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
|
||||
switch (_settings.DecimalSeparator)
|
||||
string systemDecimalSeparator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
|
||||
return _settings.DecimalSeparator switch
|
||||
{
|
||||
case DecimalSeparator.UseSystemLocale: return systemDecimalSeperator;
|
||||
case DecimalSeparator.Dot: return dot;
|
||||
case DecimalSeparator.Comma: return comma;
|
||||
default: return systemDecimalSeperator;
|
||||
}
|
||||
DecimalSeparator.UseSystemLocale => systemDecimalSeparator,
|
||||
DecimalSeparator.Dot => dot,
|
||||
DecimalSeparator.Comma => comma,
|
||||
_ => systemDecimalSeparator,
|
||||
};
|
||||
}
|
||||
|
||||
private bool IsBracketComplete(string query)
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
<core:LocalizationConverter x:Key="LocalizationConverter" />
|
||||
</UserControl.Resources>
|
||||
|
||||
<Grid Margin="70,14,0,14">
|
||||
<Grid Margin="{StaticResource SettingPanelMargin}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
|
|
@ -30,7 +30,7 @@
|
|||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Margin="0,0,10,0"
|
||||
Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
Text="{DynamicResource flowlauncher_plugin_calculator_output_decimal_seperator}" />
|
||||
|
|
@ -39,8 +39,9 @@
|
|||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
MaxWidth="300"
|
||||
Margin="10,5,0,5"
|
||||
Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
ItemsSource="{Binding Source={ui:EnumBindingSource {x:Type calculator:DecimalSeparator}}}"
|
||||
SelectedItem="{Binding Settings.DecimalSeparator}">
|
||||
<ComboBox.ItemTemplate>
|
||||
|
|
@ -53,7 +54,7 @@
|
|||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Margin="0,0,10,0"
|
||||
Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
Text="{DynamicResource flowlauncher_plugin_calculator_max_decimal_places}" />
|
||||
|
|
@ -61,8 +62,9 @@
|
|||
x:Name="MaxDecimalPlaces"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Margin="10,5,0,5"
|
||||
Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
ItemsSource="{Binding MaxDecimalPlacesRange}"
|
||||
SelectedItem="{Binding Settings.MaxDecimalPlaces}" />
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -7,6 +7,7 @@
|
|||
"*",
|
||||
"*"
|
||||
],
|
||||
"HideActionKeywordPanel": true,
|
||||
"Name": "Explorer",
|
||||
"Description": "Find and manage files and folders via Windows Search or Everything",
|
||||
"Author": "Jeremy Wu",
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<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" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
d:DesignHeight="450"
|
||||
d:DesignWidth="800"
|
||||
mc:Ignorable="d">
|
||||
<Grid Margin="70 15 18 15">
|
||||
<Grid Margin="{StaticResource SettingPanelMargin}">
|
||||
<Grid.ColumnDefinitions />
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto" />
|
||||
|
|
@ -15,13 +15,12 @@
|
|||
</Grid.RowDefinitions>
|
||||
<CheckBox
|
||||
Grid.Row="0"
|
||||
Padding="8 0 0 0"
|
||||
Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
|
||||
Content="{DynamicResource plugin_pluginsmanager_plugin_settings_unknown_source}"
|
||||
IsChecked="{Binding WarnFromUnknownSource}" />
|
||||
<CheckBox
|
||||
Grid.Row="1"
|
||||
Margin="0 10 0 0"
|
||||
Padding="8 0 0 0"
|
||||
Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
|
||||
Content="{DynamicResource plugin_pluginsmanager_plugin_settings_auto_restart}"
|
||||
IsChecked="{Binding AutoRestartAfterChanging}" />
|
||||
</Grid>
|
||||
|
|
|
|||
|
|
@ -10,124 +10,109 @@
|
|||
<UserControl.Resources>
|
||||
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
|
||||
</UserControl.Resources>
|
||||
<Grid Margin="0">
|
||||
<Grid Margin="{StaticResource SettingPanelMargin}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<DockPanel
|
||||
Margin="70 10 0 8"
|
||||
HorizontalAlignment="Stretch"
|
||||
LastChildFill="True">
|
||||
<DockPanel HorizontalAlignment="Stretch" LastChildFill="True">
|
||||
<TextBlock
|
||||
MinWidth="120"
|
||||
Margin="0 5 10 0"
|
||||
Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{DynamicResource flowlauncher_plugin_program_index_source}" />
|
||||
<WrapPanel
|
||||
Width="Auto"
|
||||
Margin="0 0 14 0"
|
||||
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
|
||||
HorizontalAlignment="Right"
|
||||
DockPanel.Dock="Right">
|
||||
<CheckBox
|
||||
Name="UWPEnabled"
|
||||
Margin="12 0 12 0"
|
||||
Margin="{StaticResource SettingPanelItemRightMargin}"
|
||||
Content="{DynamicResource flowlauncher_plugin_program_index_uwp}"
|
||||
IsChecked="{Binding EnableUWP}"
|
||||
ToolTip="{DynamicResource flowlauncher_plugin_program_index_uwp_tooltip}"
|
||||
Visibility="{Binding ShowUWPCheckbox, Converter={StaticResource BooleanToVisibilityConverter}}" />
|
||||
<CheckBox
|
||||
Name="StartMenuEnabled"
|
||||
Margin="12 0 12 0"
|
||||
Margin="{StaticResource SettingPanelItemRightMargin}"
|
||||
Content="{DynamicResource flowlauncher_plugin_program_index_start}"
|
||||
IsChecked="{Binding EnableStartMenuSource}"
|
||||
ToolTip="{DynamicResource flowlauncher_plugin_program_index_start_tooltip}" />
|
||||
<CheckBox
|
||||
Name="RegistryEnabled"
|
||||
Margin="12 0 12 0"
|
||||
Margin="{StaticResource SettingPanelItemRightMargin}"
|
||||
Content="{DynamicResource flowlauncher_plugin_program_index_registry}"
|
||||
IsChecked="{Binding EnableRegistrySource}"
|
||||
ToolTip="{DynamicResource flowlauncher_plugin_program_index_registry_tooltip}" />
|
||||
<CheckBox
|
||||
Name="PATHEnabled"
|
||||
Margin="12 0 12 0"
|
||||
Content="{DynamicResource flowlauncher_plugin_program_index_PATH}"
|
||||
IsChecked="{Binding EnablePATHSource}"
|
||||
ToolTip="{DynamicResource flowlauncher_plugin_program_index_PATH_tooltip}" />
|
||||
</WrapPanel>
|
||||
</DockPanel>
|
||||
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical">
|
||||
<Separator
|
||||
Height="1"
|
||||
BorderBrush="{DynamicResource Color03B}"
|
||||
BorderThickness="1" />
|
||||
<DockPanel
|
||||
Margin="70 10 0 8"
|
||||
HorizontalAlignment="Stretch"
|
||||
LastChildFill="True">
|
||||
<Separator Style="{StaticResource SettingPanelSeparatorStyle}" />
|
||||
<DockPanel HorizontalAlignment="Stretch" LastChildFill="True">
|
||||
<TextBlock
|
||||
MinWidth="120"
|
||||
Margin="0 5 10 0"
|
||||
Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{DynamicResource flowlauncher_plugin_program_index_option}" />
|
||||
<WrapPanel
|
||||
Width="Auto"
|
||||
Margin="0 0 14 0"
|
||||
HorizontalAlignment="Right"
|
||||
DockPanel.Dock="Right">
|
||||
<CheckBox
|
||||
Margin="12 0 12 0"
|
||||
Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
|
||||
Content="{DynamicResource flowlauncher_plugin_program_enable_hidelnkpath}"
|
||||
IsChecked="{Binding HideAppsPath}"
|
||||
ToolTip="{DynamicResource flowlauncher_plugin_program_enable_hidelnkpath_tooltip}" />
|
||||
<CheckBox
|
||||
Margin="12 0 12 0"
|
||||
Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
|
||||
Content="{DynamicResource flowlauncher_plugin_program_enable_hideuninstallers}"
|
||||
IsChecked="{Binding HideUninstallers}"
|
||||
ToolTip="{DynamicResource flowlauncher_plugin_program_enable_hideuninstallers_tooltip}" />
|
||||
<CheckBox
|
||||
Margin="12 0 12 0"
|
||||
Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
|
||||
Content="{DynamicResource flowlauncher_plugin_program_enable_description}"
|
||||
IsChecked="{Binding EnableDescription}"
|
||||
ToolTip="{DynamicResource flowlauncher_plugin_program_enable_description_tooltip}" />
|
||||
<CheckBox
|
||||
Margin="12 0 12 0"
|
||||
Content="{DynamicResource flowlauncher_plugin_program_enable_hideduplicatedwindowsapp}"
|
||||
IsChecked="{Binding HideDuplicatedWindowsApp}"
|
||||
ToolTip="{DynamicResource flowlauncher_plugin_program_enable_hideduplicatedwindowsapp_tooltip}" />
|
||||
</WrapPanel>
|
||||
</DockPanel>
|
||||
<Separator
|
||||
Height="1"
|
||||
BorderBrush="{DynamicResource Color03B}"
|
||||
BorderThickness="1" />
|
||||
<Separator Style="{StaticResource SettingPanelSeparatorStyle}" />
|
||||
<StackPanel
|
||||
Margin="60 0 0 2"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
x:Name="btnLoadAllProgramSource"
|
||||
MinWidth="120"
|
||||
Margin="10 10 5 10"
|
||||
Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
|
||||
HorizontalAlignment="Right"
|
||||
Click="btnLoadAllProgramSource_OnClick"
|
||||
Content="{DynamicResource flowlauncher_plugin_program_all_programs}" />
|
||||
<Button
|
||||
x:Name="btnProgramSuffixes"
|
||||
MinWidth="120"
|
||||
Margin="5 10 5 10"
|
||||
Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
|
||||
HorizontalAlignment="Right"
|
||||
Click="BtnProgramSuffixes_OnClick"
|
||||
Content="{DynamicResource flowlauncher_plugin_program_suffixes}" />
|
||||
<Button
|
||||
x:Name="btnReindex"
|
||||
MinWidth="120"
|
||||
Margin="5 10 5 10"
|
||||
Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
|
||||
HorizontalAlignment="Right"
|
||||
Click="btnReindex_Click"
|
||||
Content="{DynamicResource flowlauncher_plugin_program_reindex}" />
|
||||
|
|
@ -140,12 +125,13 @@
|
|||
x:Name="progressBarIndexing"
|
||||
Width="80"
|
||||
Height="20"
|
||||
Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
|
||||
IsIndeterminate="True"
|
||||
Maximum="100"
|
||||
Minimum="0" />
|
||||
<TextBlock
|
||||
Height="20"
|
||||
Margin="10 0 0 0"
|
||||
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
|
||||
HorizontalAlignment="Center"
|
||||
Text="{DynamicResource flowlauncher_plugin_program_indexing}" />
|
||||
</StackPanel>
|
||||
|
|
@ -154,7 +140,7 @@
|
|||
<ListView
|
||||
x:Name="programSourceView"
|
||||
Grid.Row="2"
|
||||
Margin="70 0 20 0"
|
||||
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
|
||||
AllowDrop="True"
|
||||
BorderBrush="DarkGray"
|
||||
BorderThickness="1"
|
||||
|
|
@ -203,27 +189,24 @@
|
|||
</GridView>
|
||||
</ListView.View>
|
||||
</ListView>
|
||||
<DockPanel
|
||||
Grid.Row="3"
|
||||
Grid.RowSpan="1"
|
||||
Margin="0 0 20 0">
|
||||
<DockPanel Grid.Row="3">
|
||||
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
|
||||
<Button
|
||||
x:Name="btnProgramSourceStatus"
|
||||
MinWidth="100"
|
||||
Margin="10"
|
||||
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
|
||||
Click="btnProgramSourceStatus_OnClick"
|
||||
Content="{DynamicResource flowlauncher_plugin_program_disable}" />
|
||||
<Button
|
||||
x:Name="btnEditProgramSource"
|
||||
MinWidth="100"
|
||||
Margin="10"
|
||||
Margin="{StaticResource SettingPanelItemLeftTopBottomMargin}"
|
||||
Click="btnEditProgramSource_OnClick"
|
||||
Content="{DynamicResource flowlauncher_plugin_program_edit}" />
|
||||
<Button
|
||||
x:Name="btnAddProgramSource"
|
||||
MinWidth="100"
|
||||
Margin="10 10 0 10"
|
||||
Margin="{StaticResource SettingPanelItemLeftTopBottomMargin}"
|
||||
Click="btnAddProgramSource_OnClick"
|
||||
Content="{DynamicResource flowlauncher_plugin_program_add}" />
|
||||
</StackPanel>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
d:DesignWidth="300"
|
||||
Loaded="CMDSetting_OnLoaded"
|
||||
mc:Ignorable="d">
|
||||
<Grid Margin="60,10,10,20" VerticalAlignment="Top">
|
||||
<Grid Margin="{StaticResource SettingPanelMargin}" VerticalAlignment="Top">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
|
|
@ -21,37 +21,37 @@
|
|||
<CheckBox
|
||||
x:Name="ReplaceWinR"
|
||||
Grid.Row="0"
|
||||
Margin="10,10,5,5"
|
||||
Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
|
||||
HorizontalAlignment="Left"
|
||||
Content="{DynamicResource flowlauncher_plugin_cmd_relace_winr}" />
|
||||
<CheckBox
|
||||
x:Name="CloseShellAfterPress"
|
||||
Grid.Row="1"
|
||||
Margin="10,5,5,5"
|
||||
Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
|
||||
HorizontalAlignment="Left"
|
||||
Content="{DynamicResource flowlauncher_plugin_cmd_close_cmd_after_press}" />
|
||||
<CheckBox
|
||||
x:Name="LeaveShellOpen"
|
||||
Grid.Row="2"
|
||||
Margin="10,5,5,5"
|
||||
Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
|
||||
HorizontalAlignment="Left"
|
||||
Content="{DynamicResource flowlauncher_plugin_cmd_leave_cmd_open}" />
|
||||
<CheckBox
|
||||
x:Name="AlwaysRunAsAdministrator"
|
||||
Grid.Row="3"
|
||||
Margin="10,5,5,5"
|
||||
Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
|
||||
HorizontalAlignment="Left"
|
||||
Content="{DynamicResource flowlauncher_plugin_cmd_always_run_as_administrator}" />
|
||||
<CheckBox
|
||||
x:Name="UseWindowsTerminal"
|
||||
Grid.Row="4"
|
||||
Margin="10,5,5,5"
|
||||
Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
|
||||
HorizontalAlignment="Left"
|
||||
Content="{DynamicResource flowlauncher_plugin_cmd_use_windows_terminal}" />
|
||||
<ComboBox
|
||||
x:Name="ShellComboBox"
|
||||
Grid.Row="5"
|
||||
Margin="10,5,5,5"
|
||||
Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
|
||||
HorizontalAlignment="Left">
|
||||
<ComboBoxItem>CMD</ComboBoxItem>
|
||||
<ComboBoxItem>PowerShell</ComboBoxItem>
|
||||
|
|
@ -61,11 +61,11 @@
|
|||
<StackPanel Grid.Row="6" Orientation="Horizontal">
|
||||
<CheckBox
|
||||
x:Name="ShowOnlyMostUsedCMDs"
|
||||
Margin="10,5,5,5"
|
||||
Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
|
||||
Content="{DynamicResource flowlauncher_plugin_cmd_history}" />
|
||||
<ComboBox
|
||||
x:Name="ShowOnlyMostUsedCMDsNumber"
|
||||
Margin="10,5,5,5"
|
||||
Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
|
||||
HorizontalAlignment="Left" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@
|
|||
d:DesignHeight="300"
|
||||
d:DesignWidth="300"
|
||||
mc:Ignorable="d">
|
||||
<Grid Margin="70 18 18 18">
|
||||
|
||||
<Grid Margin="{StaticResource SettingPanelMargin}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
|
|
@ -18,7 +19,7 @@
|
|||
<ListView
|
||||
x:Name="lbxCommands"
|
||||
Grid.Row="0"
|
||||
Margin="0"
|
||||
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
|
||||
BorderBrush="DarkGray"
|
||||
BorderThickness="1"
|
||||
ItemsSource="{Binding Settings.Commands}"
|
||||
|
|
@ -57,11 +58,12 @@
|
|||
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
|
||||
HorizontalAlignment="Right"
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
Width="100"
|
||||
Margin="10"
|
||||
Margin="{StaticResource SettingPanelItemLeftMargin}"
|
||||
Click="OnEditCommandKeywordClick"
|
||||
Content="{DynamicResource flowlauncher_plugin_sys_edit}" />
|
||||
</StackPanel>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
using System.Linq;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Flow.Launcher.Core.Resource;
|
||||
using FLSettings = Flow.Launcher.Infrastructure.UserSettings.Settings;
|
||||
|
||||
namespace Flow.Launcher.Plugin.Sys
|
||||
{
|
||||
|
|
@ -10,7 +9,6 @@ namespace Flow.Launcher.Plugin.Sys
|
|||
{
|
||||
public const string Keyword = "fltheme";
|
||||
|
||||
private readonly FLSettings _settings;
|
||||
private readonly Theme _theme;
|
||||
private readonly PluginInitContext _context;
|
||||
|
||||
|
|
@ -19,19 +17,15 @@ namespace Flow.Launcher.Plugin.Sys
|
|||
// Theme select codes simplified from SettingsPaneThemeViewModel.cs
|
||||
|
||||
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
|
||||
{
|
||||
_selectedTheme = value;
|
||||
_theme.ChangeTheme(value.FileNameWithoutExtension);
|
||||
|
||||
if (_theme.BlurEnabled && _settings.UseDropShadowEffect)
|
||||
{
|
||||
_theme.RemoveDropShadowEffectFromCurrentTheme();
|
||||
_settings.UseDropShadowEffect = false;
|
||||
}
|
||||
_ = _theme.RefreshFrameAsync();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -43,7 +37,6 @@ namespace Flow.Launcher.Plugin.Sys
|
|||
{
|
||||
_context = context;
|
||||
_theme = Ioc.Default.GetRequiredService<Theme>();
|
||||
_settings = Ioc.Default.GetRequiredService<FLSettings>();
|
||||
}
|
||||
|
||||
public List<Result> Query(Query query)
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Flow.Launcher.Core\Flow.Launcher.Core.csproj" />
|
||||
<ProjectReference Include="..\..\Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj" />
|
||||
<ProjectReference Include="..\..\Flow.Launcher.Plugin\Flow.Launcher.Plugin.csproj" />
|
||||
</ItemGroup>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
<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_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_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>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
using Microsoft.Win32;
|
||||
using Flow.Launcher.Core.Plugin;
|
||||
|
||||
namespace Flow.Launcher.Plugin.WebSearch
|
||||
{
|
||||
|
|
@ -16,7 +15,6 @@ namespace Flow.Launcher.Plugin.WebSearch
|
|||
private SearchSourceViewModel _viewModel;
|
||||
private string selectedNewIconImageFullPath;
|
||||
|
||||
|
||||
public SearchSourceSettingWindow(IList<SearchSource> sources, PluginInitContext context, SearchSource old)
|
||||
{
|
||||
_oldSearchSource = old;
|
||||
|
|
@ -80,10 +78,10 @@ namespace Flow.Launcher.Plugin.WebSearch
|
|||
private void AddSearchSource()
|
||||
{
|
||||
var keyword = _searchSource.ActionKeyword;
|
||||
if (!PluginManager.ActionKeywordRegistered(keyword))
|
||||
if (!_context.API.ActionKeywordAssigned(keyword))
|
||||
{
|
||||
var id = _context.CurrentPluginMetadata.ID;
|
||||
PluginManager.AddActionKeyword(id, keyword);
|
||||
_context.API.AddActionKeyword(id, keyword);
|
||||
|
||||
_searchSources.Add(_searchSource);
|
||||
|
||||
|
|
@ -100,10 +98,11 @@ namespace Flow.Launcher.Plugin.WebSearch
|
|||
{
|
||||
var newKeyword = _searchSource.ActionKeyword;
|
||||
var oldKeyword = _oldSearchSource.ActionKeyword;
|
||||
if (!PluginManager.ActionKeywordRegistered(newKeyword) || oldKeyword == newKeyword)
|
||||
if (!_context.API.ActionKeywordAssigned(newKeyword) || oldKeyword == newKeyword)
|
||||
{
|
||||
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);
|
||||
_searchSources[index] = _searchSource;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
d:DesignHeight="300"
|
||||
d:DesignWidth="500"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<UserControl.Resources>
|
||||
<Style x:Key="BrowserPathBoxStyle" TargetType="TextBox">
|
||||
<Setter Property="Height" Value="28" />
|
||||
|
|
@ -35,16 +36,19 @@
|
|||
</DockPanel>
|
||||
</DataTemplate>
|
||||
</UserControl.Resources>
|
||||
<Grid Margin="0,4,0,0">
|
||||
|
||||
<Grid Margin="{StaticResource SettingPanelMargin}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="56" />
|
||||
<RowDefinition Height="50" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<ListView
|
||||
x:Name="SearchSourcesListView"
|
||||
Grid.Row="0"
|
||||
Margin="18,18,18,0"
|
||||
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
|
||||
BorderBrush="DarkGray"
|
||||
BorderThickness="1"
|
||||
GridViewColumnHeader.Click="SortByColumn"
|
||||
|
|
@ -60,7 +64,7 @@
|
|||
<Image
|
||||
Width="20"
|
||||
Height="20"
|
||||
Margin="6,0,0,0"
|
||||
Margin="6 0 0 0"
|
||||
Source="{Binding Path=IconPath}" />
|
||||
</DataTemplate>
|
||||
</GridViewColumn.CellTemplate>
|
||||
|
|
@ -69,11 +73,11 @@
|
|||
<GridViewColumn
|
||||
Width="130"
|
||||
DisplayMemberBinding="{Binding ActionKeyword}"
|
||||
Header="{DynamicResource flowlauncher_plugin_websearch_action_keyword}"/>
|
||||
Header="{DynamicResource flowlauncher_plugin_websearch_action_keyword}" />
|
||||
<GridViewColumn
|
||||
Width="350"
|
||||
Width="239"
|
||||
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.CellTemplate>
|
||||
<DataTemplate>
|
||||
|
|
@ -95,59 +99,57 @@
|
|||
</GridView>
|
||||
</ListView.View>
|
||||
</ListView>
|
||||
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
|
||||
HorizontalAlignment="Right"
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
Width="100"
|
||||
Margin="10"
|
||||
Margin="{StaticResource SettingPanelItemLeftMargin}"
|
||||
Click="OnDeleteSearchSearchClick"
|
||||
Content="{DynamicResource flowlauncher_plugin_websearch_delete}" />
|
||||
<Button
|
||||
Width="100"
|
||||
Margin="10"
|
||||
Margin="{StaticResource SettingPanelItemLeftMargin}"
|
||||
Click="OnEditSearchSourceClick"
|
||||
Content="{DynamicResource flowlauncher_plugin_websearch_edit}" />
|
||||
<Button
|
||||
Width="100"
|
||||
Margin="10,10,18,10"
|
||||
Margin="{StaticResource SettingPanelItemLeftMargin}"
|
||||
Click="OnAddSearchSearchClick"
|
||||
Content="{DynamicResource flowlauncher_plugin_websearch_add}" />
|
||||
</StackPanel>
|
||||
<Border
|
||||
Grid.Row="2"
|
||||
Margin="0,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
BorderBrush="{DynamicResource Color03B}"
|
||||
BorderThickness="0,1,0,0">
|
||||
<DockPanel HorizontalAlignment="Right">
|
||||
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal">
|
||||
<Label
|
||||
Margin="14,0,10,0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Content="{DynamicResource flowlauncher_plugin_websearch_enable_suggestion_provider}" />
|
||||
<ComboBox
|
||||
Height="30"
|
||||
Margin="0,0,20,0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="11"
|
||||
IsEnabled="{Binding ElementName=EnableSuggestion, Path=IsChecked}"
|
||||
ItemsSource="{Binding Settings.Suggestions}"
|
||||
SelectedItem="{Binding Settings.SelectedSuggestion}" />
|
||||
<Label
|
||||
Margin="0,0,10,0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Content="{DynamicResource flowlauncher_plugin_websearch_enable_suggestion}" />
|
||||
<CheckBox
|
||||
Name="EnableSuggestion"
|
||||
Margin="0,0,8,0"
|
||||
IsChecked="{Binding Settings.EnableSuggestion}" />
|
||||
</StackPanel>
|
||||
<!-- Not sure why binding IsEnabled directly to Settings.EnableWebSearchSuggestion is not working -->
|
||||
</DockPanel>
|
||||
</Border>
|
||||
|
||||
<Separator Grid.Row="2" Style="{StaticResource SettingPanelSeparatorStyle}" />
|
||||
|
||||
<DockPanel
|
||||
Grid.Row="3"
|
||||
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
|
||||
HorizontalAlignment="Right">
|
||||
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal">
|
||||
<Label
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Content="{DynamicResource flowlauncher_plugin_websearch_enable_suggestion_provider}" />
|
||||
<ComboBox
|
||||
Height="30"
|
||||
Margin="{StaticResource SettingPanelItemLeftMargin}"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="11"
|
||||
IsEnabled="{Binding ElementName=EnableSuggestion, Path=IsChecked}"
|
||||
ItemsSource="{Binding Settings.Suggestions}"
|
||||
SelectedItem="{Binding Settings.SelectedSuggestion}" />
|
||||
<CheckBox
|
||||
Name="EnableSuggestion"
|
||||
Margin="{StaticResource SettingPanelItemLeftMargin}"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Content="{DynamicResource flowlauncher_plugin_websearch_enable_suggestion}"
|
||||
IsChecked="{Binding Settings.EnableSuggestion}" />
|
||||
</StackPanel>
|
||||
<!-- Not sure why binding IsEnabled directly to Settings.EnableWebSearchSuggestion is not working -->
|
||||
</DockPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using Flow.Launcher.Core.Plugin;
|
||||
using System.ComponentModel;
|
||||
using System.Windows.Data;
|
||||
|
||||
|
|
@ -40,7 +39,7 @@ namespace Flow.Launcher.Plugin.WebSearch
|
|||
if (result == MessageBoxResult.Yes)
|
||||
{
|
||||
var id = _context.CurrentPluginMetadata.ID;
|
||||
PluginManager.RemoveActionKeyword(id, selected.ActionKeyword);
|
||||
_context.API.RemoveActionKeyword(id, selected.ActionKeyword);
|
||||
_settings.SearchSources.Remove(selected);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
"yahoo",
|
||||
"bd"
|
||||
],
|
||||
"HideActionKeywordPanel": true,
|
||||
"Name": "Web Searches",
|
||||
"Description": "Provide the web search ability",
|
||||
"Author": "qianlifeng",
|
||||
|
|
|
|||
Loading…
Reference in a new issue