Merge branch 'dev' into settings_panel

This commit is contained in:
Jack Ye 2025-03-01 09:58:38 +08:00 committed by GitHub
commit 4f46a8cd14
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 506 additions and 146 deletions

View file

@ -29,7 +29,9 @@ namespace Flow.Launcher.Core.Plugin
public static readonly HashSet<PluginPair> GlobalPlugins = new();
public static readonly Dictionary<string, PluginPair> NonGlobalPlugins = new();
public static IPublicAPI API { get; private set; } = Ioc.Default.GetRequiredService<IPublicAPI>();
// We should not initialize API in static constructor because it will create another API instance
private static IPublicAPI api = null;
private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService<IPublicAPI>();
private static PluginsSettings Settings;
private static List<PluginMetadata> _metadatas;

View file

@ -256,6 +256,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
public bool EnableUpdateLog { get; set; }
public bool StartFlowLauncherOnSystemStartup { get; set; } = false;
public bool UseLogonTaskForStartup { get; set; } = false;
public bool HideOnStartup { get; set; } = true;
bool _hideNotifyIcon { get; set; }
public bool HideNotifyIcon

View file

@ -323,17 +323,16 @@ namespace Flow.Launcher.Plugin
public MessageBoxResult ShowMsgBox(string messageBoxText, string caption = "", MessageBoxButton button = MessageBoxButton.OK, MessageBoxImage icon = MessageBoxImage.None, MessageBoxResult defaultResult = MessageBoxResult.OK);
/// <summary>
/// Displays a standardised Flow message box.
/// If there is issue when showing the message box, it will return null.
/// Displays a standardised Flow progress box.
/// </summary>
/// <param name="caption">The caption of the message box.</param>
/// <param name="caption">The caption of the progress box.</param>
/// <param name="reportProgressAsync">
/// Time-consuming task function, whose input is the action to report progress.
/// The input of the action is the progress value which is a double value between 0 and 100.
/// If there are any exceptions, this action will be null.
/// </param>
/// <param name="forceClosed">When user closes the progress box manually by button or esc key, this action will be called.</param>
/// <returns>A progress box interface.</returns>
public Task ShowProgressBoxAsync(string caption, Func<Action<double>, Task> reportProgressAsync, Action forceClosed = null);
/// <param name="cancelProgress">When user cancel the progress, this action will be called.</param>
/// <returns></returns>
public Task ShowProgressBoxAsync(string caption, Func<Action<double>, Task> reportProgressAsync, Action cancelProgress = null);
}
}

View file

@ -35,31 +35,64 @@ namespace Flow.Launcher
public App()
{
// Initialize settings
var storage = new FlowLauncherJsonStorage<Settings>();
_settings = storage.Load();
_settings.SetStorage(storage);
_settings.WMPInstalled = WindowsMediaPlayerHelper.IsWindowsMediaPlayerInstalled();
try
{
var storage = new FlowLauncherJsonStorage<Settings>();
_settings = storage.Load();
_settings.SetStorage(storage);
_settings.WMPInstalled = WindowsMediaPlayerHelper.IsWindowsMediaPlayerInstalled();
}
catch (Exception e)
{
ShowErrorMsgBoxAndFailFast("Cannot load setting storage, please check local data directory", e);
return;
}
// Configure the dependency injection container
var host = Host.CreateDefaultBuilder()
.UseContentRoot(AppContext.BaseDirectory)
.ConfigureServices(services => services
.AddSingleton(_ => _settings)
.AddSingleton(sp => new Updater(sp.GetRequiredService<IPublicAPI>(), Launcher.Properties.Settings.Default.GithubRepo))
.AddSingleton<Portable>()
.AddSingleton<SettingWindowViewModel>()
.AddSingleton<IAlphabet, PinyinAlphabet>()
.AddSingleton<StringMatcher>()
.AddSingleton<Internationalization>()
.AddSingleton<IPublicAPI, PublicAPIInstance>()
.AddSingleton<MainViewModel>()
.AddSingleton<Theme>()
).Build();
Ioc.Default.ConfigureServices(host.Services);
try
{
var host = Host.CreateDefaultBuilder()
.UseContentRoot(AppContext.BaseDirectory)
.ConfigureServices(services => services
.AddSingleton(_ => _settings)
.AddSingleton(sp => new Updater(sp.GetRequiredService<IPublicAPI>(), Launcher.Properties.Settings.Default.GithubRepo))
.AddSingleton<Portable>()
.AddSingleton<SettingWindowViewModel>()
.AddSingleton<IAlphabet, PinyinAlphabet>()
.AddSingleton<StringMatcher>()
.AddSingleton<Internationalization>()
.AddSingleton<IPublicAPI, PublicAPIInstance>()
.AddSingleton<MainViewModel>()
.AddSingleton<Theme>()
).Build();
Ioc.Default.ConfigureServices(host.Services);
}
catch (Exception e)
{
ShowErrorMsgBoxAndFailFast("Cannot configure dependency injection container, please open new issue in Flow.Launcher", e);
return;
}
// Initialize the public API and Settings first
API = Ioc.Default.GetRequiredService<IPublicAPI>();
_settings.Initialize();
try
{
API = Ioc.Default.GetRequiredService<IPublicAPI>();
_settings.Initialize();
}
catch (Exception e)
{
ShowErrorMsgBoxAndFailFast("Cannot initialize api and settings, please open new issue in Flow.Launcher", e);
return;
}
}
private static void ShowErrorMsgBoxAndFailFast(string message, Exception e)
{
// Firstly show users the message
MessageBox.Show(e.ToString(), message, MessageBoxButton.OK, MessageBoxImage.Error);
// Flow cannot construct its App instance, so ensure Flow crashes w/ the exception info.
Environment.FailFast(message, e);
}
[STAThread]
@ -136,7 +169,14 @@ namespace Flow.Launcher
{
try
{
Helper.AutoStartup.Enable();
if (_settings.UseLogonTaskForStartup)
{
Helper.AutoStartup.EnableViaLogonTask();
}
else
{
Helper.AutoStartup.EnableViaRegistry();
}
}
catch (Exception e)
{

View file

@ -32,14 +32,11 @@
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button
Grid.Column="4"
Grid.Column="1"
Click="BtnCancel_OnClick"
Style="{StaticResource TitleBarCloseButtonStyle}">
<Path

View file

@ -30,14 +30,11 @@
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button
Grid.Column="4"
Grid.Column="1"
Click="BtnCancel_OnClick"
Style="{StaticResource TitleBarCloseButtonStyle}">
<Path

View file

@ -90,8 +90,9 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="InputSimulator" Version="1.0.4" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
<!-- Do not upgrade Microsoft.Extensions.DependencyInjection and Microsoft.Extensions.Hosting since we are .Net7.0 -->
<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>
@ -103,6 +104,7 @@
<PackageReference Include="NHotkey.Wpf" Version="3.0.0" />
<PackageReference Include="PropertyChanged.Fody" Version="3.4.0" />
<PackageReference Include="SemanticVersioning" Version="3.0.0" />
<PackageReference Include="Jack251970.TaskScheduler" Version="2.12.1" />
<PackageReference Include="VirtualizingWrapPanel" Version="2.1.1" />
</ItemGroup>

View file

@ -1,18 +1,31 @@
using System;
using System.IO;
using System.Linq;
using System.Security.Principal;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Logger;
using Microsoft.Win32;
using Microsoft.Win32.TaskScheduler;
namespace Flow.Launcher.Helper;
public class AutoStartup
{
private const string StartupPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Run";
private const string LogonTaskName = $"{Constant.FlowLauncher} Startup";
private const string LogonTaskDesc = $"{Constant.FlowLauncher} Auto Startup";
public static bool IsEnabled
{
get
{
// Check if logon task is enabled
if (CheckLogonTask())
{
return true;
}
// Check if registry is enabled
try
{
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
@ -28,12 +41,74 @@ public class AutoStartup
}
}
public static void Disable()
private static bool CheckLogonTask()
{
using var taskService = new TaskService();
var task = taskService.RootFolder.AllTasks.FirstOrDefault(t => t.Name == LogonTaskName);
if (task != null)
{
try
{
// Check if the action is the same as the current executable path
var action = task.Definition.Actions.FirstOrDefault()!.ToString().Trim();
if (!Constant.ExecutablePath.Equals(action, StringComparison.OrdinalIgnoreCase) && !File.Exists(action))
{
UnscheduleLogonTask();
ScheduleLogonTask();
}
return true;
}
catch (Exception e)
{
Log.Error("AutoStartup", $"Failed to check logon task: {e}");
}
}
return false;
}
public static void DisableViaLogonTaskAndRegistry()
{
Disable(true);
Disable(false);
}
public static void EnableViaLogonTask()
{
Enable(true);
}
public static void EnableViaRegistry()
{
Enable(false);
}
public static void ChangeToViaLogonTask()
{
Disable(false);
Enable(true);
}
public static void ChangeToViaRegistry()
{
Disable(true);
Enable(false);
}
private static void Disable(bool logonTask)
{
try
{
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
key?.DeleteValue(Constant.FlowLauncher, false);
if (logonTask)
{
UnscheduleLogonTask();
}
else
{
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
key?.DeleteValue(Constant.FlowLauncher, false);
}
}
catch (Exception e)
{
@ -42,12 +117,19 @@ public class AutoStartup
}
}
internal static void Enable()
private static void Enable(bool logonTask)
{
try
{
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
key?.SetValue(Constant.FlowLauncher, $"\"{Constant.ExecutablePath}\"");
if (logonTask)
{
ScheduleLogonTask();
}
else
{
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
key?.SetValue(Constant.FlowLauncher, $"\"{Constant.ExecutablePath}\"");
}
}
catch (Exception e)
{
@ -55,4 +137,54 @@ public class AutoStartup
throw;
}
}
private static bool ScheduleLogonTask()
{
using var td = TaskService.Instance.NewTask();
td.RegistrationInfo.Description = LogonTaskDesc;
td.Triggers.Add(new LogonTrigger { UserId = WindowsIdentity.GetCurrent().Name, Delay = TimeSpan.FromSeconds(2) });
td.Actions.Add(Constant.ExecutablePath);
if (IsCurrentUserIsAdmin())
{
td.Principal.RunLevel = TaskRunLevel.Highest;
}
td.Settings.StopIfGoingOnBatteries = false;
td.Settings.DisallowStartIfOnBatteries = false;
td.Settings.ExecutionTimeLimit = TimeSpan.Zero;
try
{
TaskService.Instance.RootFolder.RegisterTaskDefinition(LogonTaskName, td);
return true;
}
catch (Exception e)
{
Log.Error("AutoStartup", $"Failed to schedule logon task: {e}");
return false;
}
}
private static bool UnscheduleLogonTask()
{
using var taskService = new TaskService();
try
{
taskService.RootFolder.DeleteTask(LogonTaskName);
return true;
}
catch (Exception e)
{
Log.Error("AutoStartup", $"Failed to unschedule logon task: {e}");
return false;
}
}
private static bool IsCurrentUserIsAdmin()
{
var identity = WindowsIdentity.GetCurrent();
var principal = new WindowsPrincipal(identity);
return principal.IsInRole(WindowsBuiltInRole.Administrator);
}
}

View file

@ -1,9 +1,11 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Documents;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Microsoft.Win32;
using Windows.Win32;
using Windows.Win32.UI.WindowsAndMessaging;
@ -13,8 +15,70 @@ namespace Flow.Launcher.Helper;
public static class WallpaperPathRetrieval
{
private static readonly int MAX_PATH = 260;
private static readonly int MAX_CACHE_SIZE = 3;
public static unsafe string GetWallpaperPath()
private static readonly Dictionary<(string, DateTime), ImageBrush> wallpaperCache = new();
public static Brush GetWallpaperBrush()
{
// Invoke the method on the UI thread
if (!Application.Current.Dispatcher.CheckAccess())
{
return Application.Current.Dispatcher.Invoke(GetWallpaperBrush);
}
try
{
var wallpaperPath = GetWallpaperPath();
if (wallpaperPath is not null && File.Exists(wallpaperPath))
{
// Since the wallpaper file name can be the same (TranscodedWallpaper),
// we need to add the last modified date to differentiate them
var dateModified = File.GetLastWriteTime(wallpaperPath);
wallpaperCache.TryGetValue((wallpaperPath, dateModified), out var cachedWallpaper);
if (cachedWallpaper != null)
{
return cachedWallpaper;
}
// We should not dispose the memory stream since the bitmap is still in use
var memStream = new MemoryStream(File.ReadAllBytes(wallpaperPath));
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.StreamSource = memStream;
bitmap.DecodePixelWidth = 800;
bitmap.DecodePixelHeight = 600;
bitmap.EndInit();
bitmap.Freeze(); // Make the bitmap thread-safe
var wallpaperBrush = new ImageBrush(bitmap) { Stretch = Stretch.UniformToFill };
wallpaperBrush.Freeze(); // Make the brush thread-safe
// Manage cache size
if (wallpaperCache.Count >= MAX_CACHE_SIZE)
{
// Remove the oldest wallpaper from the cache
var oldestCache = wallpaperCache.Keys.OrderBy(k => k.Item2).FirstOrDefault();
if (oldestCache != default)
{
wallpaperCache.Remove(oldestCache);
}
}
wallpaperCache.Add((wallpaperPath, dateModified), wallpaperBrush);
return wallpaperBrush;
}
var wallpaperColor = GetWallpaperColor();
return new SolidColorBrush(wallpaperColor);
}
catch (Exception ex)
{
App.API.LogException(nameof(WallpaperPathRetrieval), "Error retrieving wallpaper", ex);
return new SolidColorBrush(Colors.Transparent);
}
}
private static unsafe string GetWallpaperPath()
{
var wallpaperPtr = stackalloc char[MAX_PATH];
PInvoke.SystemParametersInfo(SYSTEM_PARAMETERS_INFO_ACTION.SPI_GETDESKWALLPAPER, (uint)MAX_PATH,
@ -25,7 +89,7 @@ public static class WallpaperPathRetrieval
return wallpaper.ToString();
}
public static Color GetWallpaperColor()
private static Color GetWallpaperColor()
{
RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Control Panel\Colors", true);
var result = key?.GetValue("Background", null);

View file

@ -47,6 +47,8 @@
<system:String x:Key="portableMode">Portable Mode</system:String>
<system:String x:Key="portableModeToolTIp">Store all settings and user data in one folder (Useful when used with removable drives or cloud services).</system:String>
<system:String x:Key="startFlowLauncherOnSystemStartup">Start Flow Launcher on system startup</system:String>
<system:String x:Key="useLogonTaskForStartup">Use logon task instead of startup entry for faster startup experience</system:String>
<system:String x:Key="useLogonTaskForStartupTooltip">After uninstallation, you need to manually remove this task (Flow.Launcher Startup) via Task Scheduler</system:String>
<system:String x:Key="setAutoStartFailed">Error setting launch on startup</system:String>
<system:String x:Key="hideFlowLauncherWhenLoseFocus">Hide Flow Launcher when focus is lost</system:String>
<system:String x:Key="dontPromptUpdateMsg">Do not show new version notifications</system:String>
@ -366,6 +368,7 @@
<system:String x:Key="commonOK">OK</system:String>
<system:String x:Key="commonYes">Yes</system:String>
<system:String x:Key="commonNo">No</system:String>
<system:String x:Key="commonBackground">Background</system:String>
<!-- Crash Reporter -->
<system:String x:Key="reportWindow_version">Version</system:String>
@ -382,6 +385,9 @@
<system:String x:Key="reportWindow_report_succeed">Report sent successfully</system:String>
<system:String x:Key="reportWindow_report_failed">Failed to send report</system:String>
<system:String x:Key="reportWindow_flowlauncher_got_an_error">Flow Launcher got an error</system:String>
<system:String x:Key="reportWindow_please_open_issue">Please open new issue in</system:String>
<system:String x:Key="reportWindow_upload_log">1. Upload log file: {0}</system:String>
<system:String x:Key="reportWindow_copy_below">2. Copy below exception message</system:String>
<!-- General Notice -->
<system:String x:Key="pleaseWait">Please wait...</system:String>

View file

@ -438,7 +438,7 @@ namespace Flow.Launcher
if (_settings.FirstLaunch)
{
_settings.FirstLaunch = false;
PluginManager.API.SaveAppAllSettings();
App.API.SaveAppAllSettings();
OpenWelcomeWindow();
}
}

View file

@ -29,14 +29,11 @@
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button
Grid.Column="4"
Grid.Column="1"
Click="BtnCancel_OnClick"
Style="{StaticResource TitleBarCloseButtonStyle}">
<Path

View file

@ -12,6 +12,7 @@
Foreground="{DynamicResource PopupTextColor}"
ResizeMode="NoResize"
SizeToContent="Height"
Topmost="True"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d">
<WindowChrome.WindowChrome>
@ -35,9 +36,32 @@
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button
Grid.Column="1"
Click="Button_Minimize"
RenderOptions.EdgeMode="Aliased"
Style="{DynamicResource TitleBarButtonStyle}">
<Path
Width="46"
Height="32"
Data="M 18,15 H 28"
Stroke="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}"
StrokeThickness="1">
<Path.Style>
<Style TargetType="Path">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsActive, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" Value="False">
<Setter Property="Opacity" Value="0.5" />
</DataTrigger>
</Style.Triggers>
</Style>
</Path.Style>
</Path>
</Button>
<Button
Grid.Column="2"
Click="Button_Cancel"
Style="{StaticResource TitleBarCloseButtonStyle}">
<Path
@ -94,11 +118,17 @@
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<Button
x:Name="btnBackground"
MinWidth="120"
Margin="5 0 5 0"
Click="Button_Background"
Content="{DynamicResource commonBackground}" />
<Button
x:Name="btnCancel"
MinWidth="120"
Margin="5 0 5 0"
Click="Button_Click"
Click="Button_Cancel"
Content="{DynamicResource commonCancel}" />
</WrapPanel>
</Border>

View file

@ -8,15 +8,15 @@ namespace Flow.Launcher
{
public partial class ProgressBoxEx : Window
{
private readonly Action _forceClosed;
private readonly Action _cancelProgress;
private ProgressBoxEx(Action forceClosed)
private ProgressBoxEx(Action cancelProgress)
{
_forceClosed = forceClosed;
_cancelProgress = cancelProgress;
InitializeComponent();
}
public static async Task ShowAsync(string caption, Func<Action<double>, Task> reportProgressAsync, Action forceClosed = null)
public static async Task ShowAsync(string caption, Func<Action<double>, Task> reportProgressAsync, Action cancelProgress = null)
{
ProgressBoxEx prgBox = null;
try
@ -25,7 +25,7 @@ namespace Flow.Launcher
{
await Application.Current.Dispatcher.InvokeAsync(() =>
{
prgBox = new ProgressBoxEx(forceClosed)
prgBox = new ProgressBoxEx(cancelProgress)
{
Title = caption
};
@ -35,7 +35,7 @@ namespace Flow.Launcher
}
else
{
prgBox = new ProgressBoxEx(forceClosed)
prgBox = new ProgressBoxEx(cancelProgress)
{
Title = caption
};
@ -95,20 +95,25 @@ namespace Flow.Launcher
ForceClose();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
ForceClose();
}
private void Button_Cancel(object sender, RoutedEventArgs e)
{
ForceClose();
}
private void Button_Minimize(object sender, RoutedEventArgs e)
{
WindowState = WindowState.Minimized;
}
private void Button_Background(object sender, RoutedEventArgs e)
{
Hide();
}
private void ForceClose()
{
Close();
_forceClosed?.Invoke();
_cancelProgress?.Invoke();
}
}
}

View file

@ -342,7 +342,7 @@ namespace Flow.Launcher
public MessageBoxResult ShowMsgBox(string messageBoxText, string caption = "", MessageBoxButton button = MessageBoxButton.OK, MessageBoxImage icon = MessageBoxImage.None, MessageBoxResult defaultResult = MessageBoxResult.OK) =>
MessageBoxEx.Show(messageBoxText, caption, button, icon, defaultResult);
public Task ShowProgressBoxAsync(string caption, Func<Action<double>, Task> reportProgressAsync, Action forceClosed = null) => ProgressBoxEx.ShowAsync(caption, reportProgressAsync, forceClosed);
public Task ShowProgressBoxAsync(string caption, Func<Action<double>, Task> reportProgressAsync, Action cancelProgress = null) => ProgressBoxEx.ShowAsync(caption, reportProgressAsync, cancelProgress);
#endregion

View file

@ -1,23 +1,82 @@
<Window x:Class="Flow.Launcher.ReportWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
WindowStartupLocation="CenterScreen"
Icon="Images/app_error.png"
Topmost="True"
ResizeMode="NoResize"
Width="600"
Height="455"
Title="{DynamicResource reportWindow_flowlauncher_got_an_error}"
d:DesignHeight="300" d:DesignWidth="600" x:ClassModifier="internal">
<RichTextBox x:Name="ErrorTextbox"
IsDocumentEnabled="True"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto"
FontSize="14"
Margin="10"
BorderThickness="0"/>
<Window
x:Class="Flow.Launcher.ReportWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="{DynamicResource reportWindow_flowlauncher_got_an_error}"
Width="600"
Height="455"
d:DesignHeight="300"
d:DesignWidth="600"
x:ClassModifier="internal"
Background="{DynamicResource PopuBGColor}"
Foreground="{DynamicResource PopupTextColor}"
Icon="/Images/app_error.png"
ResizeMode="NoResize"
Topmost="True"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d">
<WindowChrome.WindowChrome>
<WindowChrome CaptionHeight="32" ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
</WindowChrome.WindowChrome>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Image
Grid.Column="0"
Width="16"
Height="16"
Margin="10 4 4 4"
RenderOptions.BitmapScalingMode="HighQuality"
Source="/Images/app_error.png" />
<TextBlock
Grid.Column="1"
Margin="4 0 0 0"
VerticalAlignment="Center"
FontSize="12"
Foreground="{DynamicResource Color05B}"
Text="{DynamicResource reportWindow_flowlauncher_got_an_error}" />
<Button
Grid.Column="2"
Click="BtnCancel_OnClick"
Style="{StaticResource TitleBarCloseButtonStyle}">
<Path
Width="46"
Height="32"
Data="M 18,11 27,20 M 18,20 27,11"
Stroke="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}"
StrokeThickness="1">
<Path.Style>
<Style TargetType="Path">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsActive, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" Value="False">
<Setter Property="Opacity" Value="0.5" />
</DataTrigger>
</Style.Triggers>
</Style>
</Path.Style>
</Path>
</Button>
</Grid>
<RichTextBox
x:Name="ErrorTextbox"
Grid.Row="1"
Margin="10"
BorderThickness="0"
FontSize="14"
HorizontalScrollBarVisibility="Auto"
IsDocumentEnabled="True"
VerticalScrollBarVisibility="Auto" />
</Grid>
</Window>

View file

@ -43,20 +43,21 @@ namespace Flow.Launcher
var log = directory.GetFiles().OrderByDescending(f => f.LastWriteTime).First();
var websiteUrl = exception switch
{
FlowPluginException pluginException =>GetIssuesUrl(pluginException.Metadata.Website),
_ => Constant.IssuesUrl
};
{
FlowPluginException pluginException =>GetIssuesUrl(pluginException.Metadata.Website),
_ => Constant.IssuesUrl
};
var paragraph = Hyperlink("Please open new issue in: ", websiteUrl);
paragraph.Inlines.Add($"1. upload log file: {log.FullName}\n");
paragraph.Inlines.Add($"2. copy below exception message");
var paragraph = Hyperlink(App.API.GetTranslation("reportWindow_please_open_issue"), websiteUrl);
paragraph.Inlines.Add(string.Format(App.API.GetTranslation("reportWindow_upload_log"), log.FullName));
paragraph.Inlines.Add("\n");
paragraph.Inlines.Add(App.API.GetTranslation("reportWindow_copy_below"));
ErrorTextbox.Document.Blocks.Add(paragraph);
StringBuilder content = new StringBuilder();
content.AppendLine(ErrorReporting.RuntimeInfo());
content.AppendLine(ErrorReporting.DependenciesInfo());
content.AppendLine();
content.AppendLine($"Date: {DateTime.Now.ToString(CultureInfo.InvariantCulture)}");
content.AppendLine("Exception:");
content.AppendLine(exception.ToString());
@ -65,10 +66,12 @@ namespace Flow.Launcher
ErrorTextbox.Document.Blocks.Add(paragraph);
}
private Paragraph Hyperlink(string textBeforeUrl, string url)
private static Paragraph Hyperlink(string textBeforeUrl, string url)
{
var paragraph = new Paragraph();
paragraph.Margin = new Thickness(0);
var paragraph = new Paragraph
{
Margin = new Thickness(0)
};
var link = new Hyperlink
{
@ -79,10 +82,16 @@ namespace Flow.Launcher
link.Click += (s, e) => SearchWeb.OpenInBrowserTab(url);
paragraph.Inlines.Add(textBeforeUrl);
paragraph.Inlines.Add(" ");
paragraph.Inlines.Add(link);
paragraph.Inlines.Add("\n");
return paragraph;
}
private void BtnCancel_OnClick(object sender, RoutedEventArgs e)
{
Close();
}
}
}

View file

@ -2,11 +2,10 @@
using Flow.Launcher.Infrastructure.Hotkey;
using Flow.Launcher.Infrastructure.UserSettings;
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Navigation;
using CommunityToolkit.Mvvm.Input;
using Flow.Launcher.ViewModel;
using System.Windows.Media;
namespace Flow.Launcher.Resources.Pages
{
@ -29,5 +28,10 @@ namespace Flow.Launcher.Resources.Pages
{
HotKeyMapper.SetHotkey(hotkey, HotKeyMapper.OnToggleHotkey);
}
public Brush PreviewBackground
{
get => WallpaperPathRetrieval.GetWallpaperBrush();
}
}
}

View file

@ -28,14 +28,11 @@
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button
Grid.Column="4"
Grid.Column="1"
Click="btnCancel_Click"
Style="{StaticResource TitleBarCloseButtonStyle}">
<Path

View file

@ -28,14 +28,11 @@
<StackPanel>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button
Grid.Column="4"
Grid.Column="1"
Click="btnCancel_Click"
Style="{StaticResource TitleBarCloseButtonStyle}">
<Path

View file

@ -6,7 +6,6 @@ using System.Threading.Tasks;
using System.Windows;
using CommunityToolkit.Mvvm.Input;
using Flow.Launcher.Core;
using Flow.Launcher.Core.Plugin;
using Flow.Launcher.Core.Resource;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.UserSettings;
@ -77,7 +76,7 @@ public partial class SettingsPaneAboutViewModel : BaseModel
[RelayCommand]
private void OpenSettingsFolder()
{
PluginManager.API.OpenDirectory(Path.Combine(DataLocation.DataDirectory(), Constant.Settings));
App.API.OpenDirectory(Path.Combine(DataLocation.DataDirectory(), Constant.Settings));
}
[RelayCommand]
@ -85,7 +84,7 @@ public partial class SettingsPaneAboutViewModel : BaseModel
{
string settingsFolderPath = Path.Combine(DataLocation.DataDirectory(), Constant.Settings);
string parentFolderPath = Path.GetDirectoryName(settingsFolderPath);
PluginManager.API.OpenDirectory(parentFolderPath);
App.API.OpenDirectory(parentFolderPath);
}

View file

@ -42,9 +42,20 @@ public partial class SettingsPaneGeneralViewModel : BaseModel
try
{
if (value)
AutoStartup.Enable();
{
if (UseLogonTaskForStartup)
{
AutoStartup.EnableViaLogonTask();
}
else
{
AutoStartup.EnableViaRegistry();
}
}
else
AutoStartup.Disable();
{
AutoStartup.DisableViaLogonTaskAndRegistry();
}
}
catch (Exception e)
{
@ -54,6 +65,34 @@ public partial class SettingsPaneGeneralViewModel : BaseModel
}
}
public bool UseLogonTaskForStartup
{
get => Settings.UseLogonTaskForStartup;
set
{
Settings.UseLogonTaskForStartup = value;
if (StartFlowLauncherOnSystemStartup)
{
try
{
if (UseLogonTaskForStartup)
{
AutoStartup.ChangeToViaLogonTask();
}
else
{
AutoStartup.ChangeToViaRegistry();
}
}
catch (Exception e)
{
Notification.Show(InternationalizationManager.Instance.GetTranslation("setAutoStartFailed"),
e.Message);
}
}
}
}
public List<SearchWindowScreenData> SearchWindowScreens { get; } =
DropdownDataGeneric<SearchWindowScreens>.GetValues<SearchWindowScreenData>("SearchWindowScreen");

View file

@ -5,7 +5,6 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using CommunityToolkit.Mvvm.Input;
using Flow.Launcher.Core.Resource;
using Flow.Launcher.Helper;
@ -14,7 +13,6 @@ using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using Flow.Launcher.ViewModel;
using ModernWpf;
using Flow.Launcher.Core;
using ThemeManager = Flow.Launcher.Core.Resource.ThemeManager;
using ThemeManagerForColorSchemeSwitch = ModernWpf.ThemeManager;
@ -212,24 +210,7 @@ public partial class SettingsPaneThemeViewModel : BaseModel
public Brush PreviewBackground
{
get
{
var wallpaper = WallpaperPathRetrieval.GetWallpaperPath();
if (wallpaper is not null && File.Exists(wallpaper))
{
var memStream = new MemoryStream(File.ReadAllBytes(wallpaper));
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.StreamSource = memStream;
bitmap.DecodePixelWidth = 800;
bitmap.DecodePixelHeight = 600;
bitmap.EndInit();
return new ImageBrush(bitmap) { Stretch = Stretch.UniformToFill };
}
var wallpaperColor = WallpaperPathRetrieval.GetWallpaperColor();
return new SolidColorBrush(wallpaperColor);
}
get => WallpaperPathRetrieval.GetWallpaperBrush();
}
public ResultsViewModel PreviewResults

View file

@ -36,6 +36,13 @@
OnContent="{DynamicResource enable}" />
</cc:Card>
<cc:Card Title="{DynamicResource useLogonTaskForStartup}" Sub="{DynamicResource useLogonTaskForStartupTooltip}">
<ui:ToggleSwitch
IsOn="{Binding UseLogonTaskForStartup}"
OffContent="{DynamicResource disable}"
OnContent="{DynamicResource enable}" />
</cc:Card>
<cc:Card
Title="{DynamicResource hideOnStartup}"
Icon="&#xed1a;"

View file

@ -3,7 +3,6 @@ using System.ComponentModel;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Navigation;
using Flow.Launcher.Core.Plugin;
using Flow.Launcher.SettingPages.ViewModels;
using Flow.Launcher.ViewModel;
@ -49,7 +48,7 @@ public partial class SettingsPanePluginStore
private void Hyperlink_OnRequestNavigate(object sender, RequestNavigateEventArgs e)
{
PluginManager.API.OpenUrl(e.Uri.AbsoluteUri);
App.API.OpenUrl(e.Uri.AbsoluteUri);
e.Handled = true;
}

View file

@ -442,7 +442,7 @@ namespace Flow.Launcher.ViewModel
[RelayCommand]
private void SelectHelp()
{
PluginManager.API.OpenUrl("https://www.flowlauncher.com/docs/#/usage-tips");
App.API.OpenUrl("https://www.flowlauncher.com/docs/#/usage-tips");
}
[RelayCommand]

View file

@ -134,20 +134,20 @@ namespace Flow.Launcher.ViewModel
{
var directory = PluginPair.Metadata.PluginDirectory;
if (!string.IsNullOrEmpty(directory))
PluginManager.API.OpenDirectory(directory);
App.API.OpenDirectory(directory);
}
[RelayCommand]
private void OpenSourceCodeLink()
{
PluginManager.API.OpenUrl(PluginPair.Metadata.Website);
App.API.OpenUrl(PluginPair.Metadata.Website);
}
[RelayCommand]
private void OpenDeletePluginWindow()
{
PluginManager.API.ChangeQuery($"{PluginManagerActionKeyword} uninstall {PluginPair.Metadata.Name}".Trim(), true);
PluginManager.API.ShowMainWindow();
App.API.ChangeQuery($"{PluginManagerActionKeyword} uninstall {PluginPair.Metadata.Name}".Trim(), true);
App.API.ShowMainWindow();
}
public static bool IsActionKeywordRegistered(string newActionKeyword) => PluginManager.ActionKeywordRegistered(newActionKeyword);

View file

@ -36,8 +36,6 @@
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Image
Grid.Column="0"
@ -53,9 +51,8 @@
FontSize="12"
Foreground="{DynamicResource Color05B}"
Text="{DynamicResource Welcome_Page1_Title}" />
<Button
Grid.Column="4"
Grid.Column="2"
Click="BtnCancel_OnClick"
Style="{StaticResource TitleBarCloseButtonStyle}">
<Path