From 2cd16837419d8f82612881c7c718658a0005e003 Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Sun, 16 Mar 2025 22:24:06 +0800
Subject: [PATCH 01/12] Improve code quality
---
Flow.Launcher.Infrastructure/MonitorInfo.cs | 123 ++++++++
.../NativeMethods.txt | 29 +-
Flow.Launcher.Infrastructure/Win32Helper.cs | 262 ++++++++++++++++--
Flow.Launcher/Flow.Launcher.csproj | 4 -
Flow.Launcher/Helper/DWMDropShadow.cs | 66 -----
.../Helper/WallpaperPathRetrieval.cs | 18 +-
Flow.Launcher/Helper/WindowsInteropHelper.cs | 211 --------------
Flow.Launcher/MainWindow.xaml.cs | 44 ++-
Flow.Launcher/Msg.xaml.cs | 21 +-
Flow.Launcher/NativeMethods.txt | 20 --
Flow.Launcher/SettingWindow.xaml.cs | 10 +-
Flow.Launcher/ViewModel/MainViewModel.cs | 4 +-
12 files changed, 434 insertions(+), 378 deletions(-)
create mode 100644 Flow.Launcher.Infrastructure/MonitorInfo.cs
delete mode 100644 Flow.Launcher/Helper/DWMDropShadow.cs
delete mode 100644 Flow.Launcher/Helper/WindowsInteropHelper.cs
delete mode 100644 Flow.Launcher/NativeMethods.txt
diff --git a/Flow.Launcher.Infrastructure/MonitorInfo.cs b/Flow.Launcher.Infrastructure/MonitorInfo.cs
new file mode 100644
index 000000000..3221708c1
--- /dev/null
+++ b/Flow.Launcher.Infrastructure/MonitorInfo.cs
@@ -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;
+
+///
+/// Contains full information about a display monitor.
+/// Codes are edited from: .
+///
+internal class MonitorInfo
+{
+ ///
+ /// Gets the display monitors (including invisible pseudo-monitors associated with the mirroring drivers).
+ ///
+ /// A list of display monitors
+ public static unsafe IList GetDisplayMonitors()
+ {
+ var monitorCount = PInvoke.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CMONITORS);
+ var list = new List(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;
+ }
+
+ ///
+ /// Gets the display monitor that is nearest to a given window.
+ ///
+ /// Window handle
+ /// The display monitor that is nearest to a given window, or null if no monitor is found.
+ 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();
+ }
+
+ ///
+ /// Gets the name of the display.
+ ///
+ public string Name { get; }
+
+ ///
+ /// Gets the display monitor rectangle, expressed in virtual-screen coordinates.
+ ///
+ ///
+ /// If the monitor is not the primary display monitor, some of the rectangle's coordinates may be negative values.
+ ///
+ public Rect RectMonitor { get; }
+
+ ///
+ /// Gets the work area rectangle of the display monitor, expressed in virtual-screen coordinates.
+ ///
+ ///
+ /// If the monitor is not the primary display monitor, some of the rectangle's coordinates may be negative values.
+ ///
+ public Rect RectWork { get; }
+
+ ///
+ /// Gets if the monitor is the the primary display monitor.
+ ///
+ public bool IsPrimary => _monitor == PInvoke.MonitorFromWindow(new(IntPtr.Zero), MONITOR_FROM_FLAGS.MONITOR_DEFAULTTOPRIMARY);
+
+ ///
+ 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;
+ }
+ }
+}
diff --git a/Flow.Launcher.Infrastructure/NativeMethods.txt b/Flow.Launcher.Infrastructure/NativeMethods.txt
index e7256e8c4..5d72239f3 100644
--- a/Flow.Launcher.Infrastructure/NativeMethods.txt
+++ b/Flow.Launcher.Infrastructure/NativeMethods.txt
@@ -20,4 +20,31 @@ EnumWindows
DwmSetWindowAttribute
DWM_SYSTEMBACKDROP_TYPE
-DWM_WINDOW_CORNER_PREFERENCE
\ No newline at end of file
+DWM_WINDOW_CORNER_PREFERENCE
+
+MAX_PATH
+SystemParametersInfo
+
+SetForegroundWindow
+
+GetWindowLong
+SetWindowLong
+GetForegroundWindow
+GetDesktopWindow
+GetShellWindow
+GetWindowRect
+GetClassName
+FindWindowEx
+WINDOW_STYLE
+
+SetLastError
+WINDOW_EX_STYLE
+
+GetSystemMetrics
+EnumDisplayMonitors
+MonitorFromWindow
+GetMonitorInfo
+MONITORINFOEXW
+
+WM_ENTERSIZEMOVE
+WM_EXITSIZEMOVE
\ No newline at end of file
diff --git a/Flow.Launcher.Infrastructure/Win32Helper.cs b/Flow.Launcher.Infrastructure/Win32Helper.cs
index 08452b721..9cc4be0c5 100644
--- a/Flow.Launcher.Infrastructure/Win32Helper.cs
+++ b/Flow.Launcher.Infrastructure/Win32Helper.cs
@@ -1,9 +1,13 @@
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
@@ -14,20 +18,17 @@ namespace Flow.Launcher.Infrastructure
public static bool IsBackdropSupported()
{
- // Windows 11 (22000) 이상에서만 Mica 및 Acrylic 효과 지원
+ // Mica and Acrylic only supported Windows 11 22000+
return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) &&
Environment.OSVersion.Version.Build >= 22000;
}
public static unsafe bool DWMSetCloakForWindow(Window window, bool cloak)
{
- var windowHelper = new WindowInteropHelper(window);
- windowHelper.EnsureHandle();
-
var cloaked = cloak ? 1 : 0;
return PInvoke.DwmSetWindowAttribute(
- new(windowHelper.Handle),
+ GetWindowHandle(window),
DWMWINDOWATTRIBUTE.DWMWA_CLOAK,
&cloaked,
(uint)Marshal.SizeOf()).Succeeded;
@@ -35,9 +36,6 @@ namespace Flow.Launcher.Infrastructure
public static unsafe bool DWMSetBackdropForWindow(Window window, BackdropTypes backdrop)
{
- var windowHelper = new WindowInteropHelper(window);
- windowHelper.EnsureHandle();
-
var backdropType = backdrop switch
{
BackdropTypes.Acrylic => DWM_SYSTEMBACKDROP_TYPE.DWMSBT_TRANSIENTWINDOW,
@@ -47,7 +45,7 @@ namespace Flow.Launcher.Infrastructure
};
return PInvoke.DwmSetWindowAttribute(
- new(windowHelper.Handle),
+ GetWindowHandle(window),
DWMWINDOWATTRIBUTE.DWMWA_SYSTEMBACKDROP_TYPE,
&backdropType,
(uint)Marshal.SizeOf()).Succeeded;
@@ -55,13 +53,10 @@ namespace Flow.Launcher.Infrastructure
public static unsafe bool DWMSetDarkModeForWindow(Window window, bool useDarkMode)
{
- var windowHelper = new WindowInteropHelper(window);
- windowHelper.EnsureHandle();
-
var darkMode = useDarkMode ? 1 : 0;
return PInvoke.DwmSetWindowAttribute(
- new(windowHelper.Handle),
+ GetWindowHandle(window),
DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE,
&darkMode,
(uint)Marshal.SizeOf()).Succeeded;
@@ -75,9 +70,6 @@ namespace Flow.Launcher.Infrastructure
///
public static unsafe bool DWMSetCornerPreferenceForWindow(Window window, string cornerType)
{
- var windowHelper = new WindowInteropHelper(window);
- windowHelper.EnsureHandle();
-
var preference = cornerType switch
{
"DoNotRound" => DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_DONOTROUND,
@@ -88,12 +80,246 @@ namespace Flow.Launcher.Infrastructure
};
return PInvoke.DwmSetWindowAttribute(
- new(windowHelper.Handle),
+ GetWindowHandle(window),
DWMWINDOWATTRIBUTE.DWMWA_WINDOW_CORNER_PREFERENCE,
&preference,
(uint)Marshal.SizeOf()).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
+
+ ///
+ /// Hide windows in the Alt+Tab window list
+ ///
+ /// To hide a window
+ public static void HideFromAltTab(Window window)
+ {
+ var hwnd = GetWindowHandle(window);
+
+ var exStyle = GetCurrentWindowStyle(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;
+
+ SetWindowLong(hwnd, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE, (int)newExStyle);
+ }
+
+ ///
+ /// Restore window display in the Alt+Tab window list.
+ ///
+ /// To restore the displayed window
+ public static void ShowInAltTab(Window window)
+ {
+ var hwnd = GetWindowHandle(window);
+
+ var exStyle = GetCurrentWindowStyle(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;
+
+ SetWindowLong(GetWindowHandle(window), WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE, (int)newExStyle);
+ }
+
+ ///
+ /// Disable windows toolbar's control box
+ /// This will also disable system menu with Alt+Space hotkey
+ ///
+ public static void DisableControlBox(Window window)
+ {
+ var hwnd = GetWindowHandle(window);
+
+ var style = GetCurrentWindowStyle(hwnd, WINDOW_LONG_PTR_INDEX.GWL_STYLE);
+
+ style &= ~(int)WINDOW_STYLE.WS_SYSMENU;
+
+ SetWindowLong(hwnd, WINDOW_LONG_PTR_INDEX.GWL_STYLE, style);
+ }
+
+ private static int GetCurrentWindowStyle(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 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;
+ }
+
+ #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;
+ }
+
+ // 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 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
+
+ ///
+ /// Transforms pixels to Device Independent Pixels used by WPF
+ ///
+ /// current window, required to get presentation source
+ /// horizontal position in pixels
+ /// vertical position in pixels
+ /// point containing device independent pixels
+ public static Point TransformPixelsToDIP(Visual visual, double unitX, double unitY)
+ {
+ Matrix matrix;
+ var source = PresentationSource.FromVisual(visual);
+ if (source is not null)
+ {
+ matrix = source.CompositionTarget.TransformFromDevice;
+ }
+ else
+ {
+ using var src = new HwndSource(new HwndSourceParameters());
+ matrix = src.CompositionTarget.TransformFromDevice;
+ }
+
+ return new Point((int)(matrix.M11 * unitX), (int)(matrix.M22 * unitY));
+ }
+
+ #endregion
+
+ #region WndProc
+
+ public static bool WM_ENTERSIZEMOVE(int msg)
+ {
+ return msg == (int)PInvoke.WM_ENTERSIZEMOVE;
+ }
+
+ public static bool WM_EXITSIZEMOVE(int msg)
+ {
+ return msg == (int)PInvoke.WM_EXITSIZEMOVE;
+ }
+
+ #endregion
+
+ #region Window Handle
+
+ internal static HWND GetWindowHandle(Window window)
+ {
+ var windowHelper = new WindowInteropHelper(window);
+ windowHelper.EnsureHandle();
+ return new(windowHelper.Handle);
+ }
+
+ #endregion
}
}
diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj
index 1e305d3d9..91cab9fa0 100644
--- a/Flow.Launcher/Flow.Launcher.csproj
+++ b/Flow.Launcher/Flow.Launcher.csproj
@@ -94,10 +94,6 @@
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
diff --git a/Flow.Launcher/Helper/DWMDropShadow.cs b/Flow.Launcher/Helper/DWMDropShadow.cs
deleted file mode 100644
index 58817d70e..000000000
--- a/Flow.Launcher/Helper/DWMDropShadow.cs
+++ /dev/null
@@ -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
-{
-
- ///
- /// 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).
- ///
- /// Window to which the shadow will be applied
- 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;
- }
-
- ///
- /// The actual method that makes API calls to drop the shadow to the window
- ///
- /// Window to which the shadow will be applied
- /// True if the method succeeded, false if not
- 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;
- }
- }
-
-}
diff --git a/Flow.Launcher/Helper/WallpaperPathRetrieval.cs b/Flow.Launcher/Helper/WallpaperPathRetrieval.cs
index a3bd83a97..151ce97dd 100644
--- a/Flow.Launcher/Helper/WallpaperPathRetrieval.cs
+++ b/Flow.Launcher/Helper/WallpaperPathRetrieval.cs
@@ -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);
diff --git a/Flow.Launcher/Helper/WindowsInteropHelper.cs b/Flow.Launcher/Helper/WindowsInteropHelper.cs
deleted file mode 100644
index 3e57948a5..000000000
--- a/Flow.Launcher/Helper/WindowsInteropHelper.cs
+++ /dev/null
@@ -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 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;
- }
-
- ///
- /// disable windows toolbar's control box
- /// this will also disable system menu with Alt+Space hotkey
- ///
- 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());
- }
- }
-
- ///
- /// Transforms pixels to Device Independent Pixels used by WPF
- ///
- /// current window, required to get presentation source
- /// horizontal position in pixels
- /// vertical position in pixels
- /// point containing device independent pixels
- 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;
- }
-
- ///
- /// Hide windows in the Alt+Tab window list
- ///
- /// To hide a window
- 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);
- }
-
- ///
- /// Restore window display in the Alt+Tab window list.
- ///
- /// To restore the displayed window
- 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);
- }
-
- ///
- /// To obtain the current overridden style of a window.
- ///
- /// To obtain the style dialog window
- /// current extension style value
- 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
-}
diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs
index 5aba73aa3..1e8ba1369 100644
--- a/Flow.Launcher/MainWindow.xaml.cs
+++ b/Flow.Launcher/MainWindow.xaml.cs
@@ -8,7 +8,6 @@ using System.Windows.Controls;
using System.Windows.Forms;
using Flow.Launcher.Core.Plugin;
using Flow.Launcher.Core.Resource;
-using Flow.Launcher.Helper;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.ViewModel;
using Screen = System.Windows.Forms.Screen;
@@ -26,10 +25,10 @@ using System.Media;
using DataObject = System.Windows.DataObject;
using System.Windows.Media;
using System.Windows.Interop;
-using Windows.Win32;
using Window = System.Windows.Window;
using System.Linq;
using System.Windows.Shapes;
+using CommunityToolkit.Mvvm.DependencyInjection;
namespace Flow.Launcher
{
@@ -78,14 +77,13 @@ namespace Flow.Launcher
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
- if (msg == PInvoke.WM_ENTERSIZEMOVE)
+ if (Win32Helper.WM_ENTERSIZEMOVE(msg))
{
_initialWidth = (int)Width;
_initialHeight = (int)Height;
handled = true;
}
-
- if (msg == PInvoke.WM_EXITSIZEMOVE)
+ else if (Win32Helper.WM_EXITSIZEMOVE(msg))
{
if (_initialHeight != (int)Height)
{
@@ -168,7 +166,8 @@ namespace Flow.Launcher
private void OnSourceInitialized(object sender, EventArgs e)
{
- WindowsInteropHelper.HideFromAltTab(this);
+ Win32Helper.HideFromAltTab(this);
+ Win32Helper.DisableControlBox(this);
}
private void OnInitialized(object sender, EventArgs e)
@@ -184,13 +183,12 @@ namespace Flow.Launcher
// Show notify icon when flowlauncher is hidden
InitializeNotifyIcon();
InitializeColorScheme();
- WindowsInteropHelper.DisableControlBox(this);
InitProgressbarAnimation();
// Initialize call twice to work around multi-display alignment issue- https://github.com/Flow-Launcher/Flow.Launcher/issues/2910
InitializePosition();
InitializePosition();
// Refresh frame
- await ThemeManager.Instance.RefreshFrameAsync();
+ await Ioc.Default.GetRequiredService().RefreshFrameAsync();
PreviewReset();
// Since the default main window visibility is visible, so we need set focus during startup
QueryTextBox.Focus();
@@ -314,9 +312,9 @@ namespace Flow.Launcher
Top = 10;
break;
case SearchWindowAligns.Custom:
- Left = WindowsInteropHelper.TransformPixelsToDIP(this,
+ Left = Win32Helper.TransformPixelsToDIP(this,
screen.WorkingArea.X + _settings.CustomWindowLeft, 0).X;
- Top = WindowsInteropHelper.TransformPixelsToDIP(this, 0,
+ Top = Win32Helper.TransformPixelsToDIP(this, 0,
screen.WorkingArea.Y + _settings.CustomWindowTop).Y;
break;
}
@@ -377,7 +375,7 @@ namespace Flow.Launcher
open.Click += (o, e) => _viewModel.ToggleFlowLauncher();
gamemode.Click += (o, e) => _viewModel.ToggleGameMode();
- positionreset.Click += (o, e) => PositionReset();
+ positionreset.Click += (o, e) => _ = PositionResetAsync();
settings.Click += (o, e) => App.API.OpenSettingDialog();
exit.Click += (o, e) => Close();
@@ -403,7 +401,7 @@ namespace Flow.Launcher
// Get context menu handle and bring it to the foreground
if (PresentationSource.FromVisual(contextMenu) is HwndSource hwndSource)
{
- PInvoke.SetForegroundWindow(new(hwndSource.Handle));
+ Win32Helper.SetForegroundWindow(hwndSource.Handle);
}
contextMenu.Focus();
@@ -422,13 +420,13 @@ namespace Flow.Launcher
}
}
- private void OpenWelcomeWindow()
+ private static void OpenWelcomeWindow()
{
var WelcomeWindow = new WelcomeWindow();
WelcomeWindow.Show();
}
- private async void PositionReset()
+ private async Task PositionResetAsync()
{
_viewModel.Show();
await Task.Delay(300); // If don't give a time, Positioning will be weird.
@@ -846,7 +844,7 @@ namespace Flow.Launcher
screen = Screen.PrimaryScreen;
break;
case SearchWindowScreens.Focus:
- var foregroundWindowHandle = PInvoke.GetForegroundWindow().Value;
+ var foregroundWindowHandle = Win32Helper.GetForegroundWindow();
screen = Screen.FromHandle(foregroundWindowHandle);
break;
case SearchWindowScreens.Custom:
@@ -865,38 +863,38 @@ namespace Flow.Launcher
public double HorizonCenter(Screen screen)
{
- 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;
}
public double VerticalCenter(Screen screen)
{
- 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 - QueryTextBox.ActualHeight) / 4 + dip1.Y;
return top;
}
public double HorizonRight(Screen screen)
{
- 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 = (dip1.X + dip2.X - ActualWidth) - 10;
return left;
}
public double HorizonLeft(Screen screen)
{
- var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0);
+ var dip1 = Win32Helper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0);
var left = dip1.X + 10;
return left;
}
///
/// Register up and down key
- /// todo: any way to put this in xaml ?
+ /// todo: Put this in xaml?
///
private void OnKeyDown(object sender, KeyEventArgs e)
{
diff --git a/Flow.Launcher/Msg.xaml.cs b/Flow.Launcher/Msg.xaml.cs
index 0bb02bbc5..94184ff63 100644
--- a/Flow.Launcher/Msg.xaml.cs
+++ b/Flow.Launcher/Msg.xaml.cs
@@ -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);
+ }
+ });
}
}
}
diff --git a/Flow.Launcher/NativeMethods.txt b/Flow.Launcher/NativeMethods.txt
deleted file mode 100644
index 88eeeca6e..000000000
--- a/Flow.Launcher/NativeMethods.txt
+++ /dev/null
@@ -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
\ No newline at end of file
diff --git a/Flow.Launcher/SettingWindow.xaml.cs b/Flow.Launcher/SettingWindow.xaml.cs
index 30b51f992..d7f8489af 100644
--- a/Flow.Launcher/SettingWindow.xaml.cs
+++ b/Flow.Launcher/SettingWindow.xaml.cs
@@ -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;
@@ -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;
}
diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs
index 476479405..7d719a442 100644
--- a/Flow.Launcher/ViewModel/MainViewModel.cs
+++ b/Flow.Launcher/ViewModel/MainViewModel.cs
@@ -6,7 +6,6 @@ using System.Threading.Tasks;
using System.Windows;
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.Storage;
@@ -27,7 +26,6 @@ using Flow.Launcher.Infrastructure.Image;
using System.Windows.Media;
using CommunityToolkit.Mvvm.DependencyInjection;
using System.Windows.Threading;
-using System.Windows.Interop;
namespace Flow.Launcher.ViewModel
{
@@ -1484,7 +1482,7 @@ namespace Flow.Launcher.ViewModel
///
public bool ShouldIgnoreHotkeys()
{
- return Settings.IgnoreHotkeysOnFullscreen && WindowsInteropHelper.IsWindowFullscreen() || GameModeStatus;
+ return Settings.IgnoreHotkeysOnFullscreen && Win32Helper.IsForegroundWindowFullscreen() || GameModeStatus;
}
#endregion
From a98a0335b3c49f0875f7e68972329418afb26edb Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Sun, 16 Mar 2025 22:37:22 +0800
Subject: [PATCH 02/12] Improve code quality
---
Flow.Launcher.Infrastructure/Win32Helper.cs | 7 ++++--
Flow.Launcher/MainWindow.xaml.cs | 27 +++++++++------------
2 files changed, 16 insertions(+), 18 deletions(-)
diff --git a/Flow.Launcher.Infrastructure/Win32Helper.cs b/Flow.Launcher.Infrastructure/Win32Helper.cs
index 9cc4be0c5..6e429cddd 100644
--- a/Flow.Launcher.Infrastructure/Win32Helper.cs
+++ b/Flow.Launcher.Infrastructure/Win32Helper.cs
@@ -313,10 +313,13 @@ namespace Flow.Launcher.Infrastructure
#region Window Handle
- internal static HWND GetWindowHandle(Window window)
+ internal static HWND GetWindowHandle(Window window, bool ensure = false)
{
var windowHelper = new WindowInteropHelper(window);
- windowHelper.EnsureHandle();
+ if (ensure)
+ {
+ windowHelper.EnsureHandle();
+ }
return new(windowHelper.Handle);
}
diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs
index 1e8ba1369..f81f03f42 100644
--- a/Flow.Launcher/MainWindow.xaml.cs
+++ b/Flow.Launcher/MainWindow.xaml.cs
@@ -5,7 +5,6 @@ using System.Windows;
using System.Windows.Input;
using System.Windows.Media.Animation;
using System.Windows.Controls;
-using System.Windows.Forms;
using Flow.Launcher.Core.Plugin;
using Flow.Launcher.Core.Resource;
using Flow.Launcher.Infrastructure.UserSettings;
@@ -63,13 +62,6 @@ namespace Flow.Launcher
InitSoundEffects();
DataObject.AddPastingHandler(QueryTextBox, OnPaste);
-
- Loaded += (_, _) =>
- {
- var handle = new WindowInteropHelper(this).Handle;
- var win = HwndSource.FromHwnd(handle);
- win.AddHook(WndProc);
- };
}
private int _initialWidth;
@@ -143,13 +135,13 @@ namespace Flow.Launcher
private void OnPaste(object sender, DataObjectPastingEventArgs e)
{
- var isText = e.SourceDataObject.GetDataPresent(System.Windows.DataFormats.UnicodeText, true);
+ var isText = e.SourceDataObject.GetDataPresent(DataFormats.UnicodeText, true);
if (isText)
{
- var text = e.SourceDataObject.GetData(System.Windows.DataFormats.UnicodeText) as string;
+ var text = e.SourceDataObject.GetData(DataFormats.UnicodeText) as string;
text = text.Replace(Environment.NewLine, " ");
DataObject data = new DataObject();
- data.SetData(System.Windows.DataFormats.UnicodeText, text);
+ data.SetData(DataFormats.UnicodeText, text);
e.DataObject = data;
}
}
@@ -166,6 +158,9 @@ namespace Flow.Launcher
private void OnSourceInitialized(object sender, EventArgs e)
{
+ var handle = Win32Helper.GetWindowHandle(this, true);
+ var win = HwndSource.FromHwnd(handle);
+ win.AddHook(WndProc);
Win32Helper.HideFromAltTab(this);
Win32Helper.DisableControlBox(this);
}
@@ -392,10 +387,10 @@ namespace Flow.Launcher
{
switch (e.Button)
{
- case MouseButtons.Left:
+ case System.Windows.Forms.MouseButtons.Left:
_viewModel.ToggleFlowLauncher();
break;
- case MouseButtons.Right:
+ case System.Windows.Forms.MouseButtons.Right:
contextMenu.IsOpen = true;
// Get context menu handle and bring it to the foreground
@@ -713,7 +708,7 @@ namespace Flow.Launcher
{
_isClockPanelAnimating = true;
- System.Windows.Application.Current.Dispatcher.Invoke(() =>
+ Application.Current.Dispatcher.Invoke(() =>
{
ClockPanel.Visibility = Visibility.Visible; // ✅ Visibility를 먼저 Visible로 설정
@@ -969,7 +964,7 @@ namespace Flow.Launcher
}
}
- private void MainPreviewMouseMove(object sender, System.Windows.Input.MouseEventArgs e)
+ private void MainPreviewMouseMove(object sender, MouseEventArgs e)
{
if (isArrowKeyPressed)
{
@@ -1005,7 +1000,7 @@ namespace Flow.Launcher
{
if (_viewModel.QueryText != QueryTextBox.Text)
{
- BindingExpression be = QueryTextBox.GetBindingExpression(System.Windows.Controls.TextBox.TextProperty);
+ BindingExpression be = QueryTextBox.GetBindingExpression(TextBox.TextProperty);
be.UpdateSource();
}
}
From 7d62dedece537152dbb76035aeba99f42001ef2d Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Sun, 16 Mar 2025 23:10:12 +0800
Subject: [PATCH 03/12] Improve code quality
---
Flow.Launcher/MainWindow.xaml | 6 +-
Flow.Launcher/MainWindow.xaml.cs | 1062 +++++++++++++++---------------
2 files changed, 535 insertions(+), 533 deletions(-)
diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml
index 93d79d767..5b63303ac 100644
--- a/Flow.Launcher/MainWindow.xaml
+++ b/Flow.Launcher/MainWindow.xaml
@@ -20,13 +20,13 @@
Closing="OnClosing"
Deactivated="OnDeactivated"
Icon="Images/app.png"
- 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"
@@ -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=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Visibility="Visible"
WindowChrome.IsHitTestVisibleInChrome="True">
-
+
diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs
index f81f03f42..d0375e8f6 100644
--- a/Flow.Launcher/MainWindow.xaml.cs
+++ b/Flow.Launcher/MainWindow.xaml.cs
@@ -45,13 +45,19 @@ namespace Flow.Launcher
private MediaPlayer animationSoundWMP;
private SoundPlayer animationSoundWPF;
+ private int _initialWidth;
+ private int _initialHeight;
+
// Window Animations
private Storyboard clocksb;
private Storyboard iconsb;
private Storyboard windowsb;
+ private bool _isClockPanelAnimating = false; // 애니메이션 실행 중인지 여부
#endregion
+ #region Constructor
+
public MainWindow(Settings settings, MainViewModel mainVM)
{
DataContext = mainVM;
@@ -61,100 +67,14 @@ namespace Flow.Launcher
InitializeComponent();
InitSoundEffects();
- DataObject.AddPastingHandler(QueryTextBox, OnPaste);
+ DataObject.AddPastingHandler(QueryTextBox, QueryTextBox_OnPaste);
}
- private int _initialWidth;
- private int _initialHeight;
+ #endregion
- private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
- {
- if (Win32Helper.WM_ENTERSIZEMOVE(msg))
- {
- _initialWidth = (int)Width;
- _initialHeight = (int)Height;
- handled = true;
- }
- else if (Win32Helper.WM_EXITSIZEMOVE(msg))
- {
- if (_initialHeight != (int)Height)
- {
- OnResizeEnd();
- }
+ #region Window Event
- if (_initialWidth != (int)Width)
- {
- FlowMainWindow.SizeToContent = SizeToContent.Height;
- }
-
- handled = true;
- }
-
- return IntPtr.Zero;
- }
-
- private void OnResizeEnd()
- {
- int shadowMargin = 0;
- if (_settings.UseDropShadowEffect)
- {
- shadowMargin = 32;
- }
-
- if (!_settings.KeepMaxResults)
- {
- var itemCount = (Height - (_settings.WindowHeightSize + 14) - shadowMargin) / _settings.ItemHeightSize;
-
- if (itemCount < 2)
- {
- _settings.MaxResultsToShow = 2;
- }
- else
- {
- _settings.MaxResultsToShow = Convert.ToInt32(Math.Truncate(itemCount));
- }
- }
-
- FlowMainWindow.SizeToContent = SizeToContent.Height;
- _viewModel.MainWindowWidth = Width;
- }
-
- private void OnCopy(object sender, ExecutedRoutedEventArgs e)
- {
- var result = _viewModel.Results.SelectedItem?.Result;
- if (QueryTextBox.SelectionLength == 0 && result != null)
- {
- string copyText = result.CopyText;
- App.API.CopyToClipboard(copyText, directCopy: true);
- }
- else if (!string.IsNullOrEmpty(QueryTextBox.Text))
- {
- App.API.CopyToClipboard(QueryTextBox.SelectedText, showDefaultNotification: false);
- }
- }
-
- private void OnPaste(object sender, DataObjectPastingEventArgs e)
- {
- var isText = e.SourceDataObject.GetDataPresent(DataFormats.UnicodeText, true);
- if (isText)
- {
- var text = e.SourceDataObject.GetData(DataFormats.UnicodeText) as string;
- text = text.Replace(Environment.NewLine, " ");
- DataObject data = new DataObject();
- data.SetData(DataFormats.UnicodeText, text);
- e.DataObject = data;
- }
- }
-
- private async void OnClosing(object sender, CancelEventArgs e)
- {
- _notifyIcon.Visible = false;
- App.API.SaveAppAllSettings();
- e.Cancel = true;
- await PluginManager.DisposePluginsAsync();
- Notification.Uninstall();
- Environment.Exit(0);
- }
+#pragma warning disable VSTHRD100
private void OnSourceInitialized(object sender, EventArgs e)
{
@@ -165,26 +85,52 @@ namespace Flow.Launcher
Win32Helper.DisableControlBox(this);
}
- private void OnInitialized(object sender, EventArgs e)
- {
- }
-
private async void OnLoaded(object sender, RoutedEventArgs _)
{
- // MouseEventHandler
- PreviewMouseMove += MainPreviewMouseMove;
- CheckFirstLaunch();
- HideStartup();
+ // Check first launch
+ if (_settings.FirstLaunch)
+ {
+ _settings.FirstLaunch = false;
+ App.API.SaveAppAllSettings();
+ var WelcomeWindow = new WelcomeWindow();
+ WelcomeWindow.Show();
+ }
+
+ // Hide window if need
+ if (_settings.HideOnStartup)
+ {
+ _viewModel.Hide();
+ }
+ else
+ {
+ _viewModel.Show();
+ }
+
// Show notify icon when flowlauncher is hidden
InitializeNotifyIcon();
- InitializeColorScheme();
+
+ // Initialize color scheme
+ if (_settings.ColorScheme == Constant.Light)
+ {
+ ModernWpf.ThemeManager.Current.ApplicationTheme = ModernWpf.ApplicationTheme.Light;
+ }
+ else if (_settings.ColorScheme == Constant.Dark)
+ {
+ ModernWpf.ThemeManager.Current.ApplicationTheme = ModernWpf.ApplicationTheme.Dark;
+ }
+
+ // Initialize position
InitProgressbarAnimation();
- // Initialize call twice to work around multi-display alignment issue- https://github.com/Flow-Launcher/Flow.Launcher/issues/2910
- InitializePosition();
- InitializePosition();
+
+ // Force update position
+ UpdatePosition(true);
+
// Refresh frame
await Ioc.Default.GetRequiredService().RefreshFrameAsync();
- PreviewReset();
+
+ // Reset preview
+ _viewModel.ResetPreview();
+
// Since the default main window visibility is visible, so we need set focus during startup
QueryTextBox.Focus();
@@ -193,37 +139,39 @@ namespace Flow.Launcher
switch (e.PropertyName)
{
case nameof(MainViewModel.MainWindowVisibilityStatus):
- {
- Dispatcher.Invoke(() =>
{
- if (_viewModel.MainWindowVisibilityStatus)
+ Dispatcher.Invoke(() =>
{
- if (_settings.UseSound)
+ if (_viewModel.MainWindowVisibilityStatus)
{
- SoundPlay();
- }
+ if (_settings.UseSound)
+ {
+ SoundPlay();
+ }
- UpdatePosition();
- PreviewReset();
- Activate();
- QueryTextBox.Focus();
- _settings.ActivateTimes++;
- if (!_viewModel.LastQuerySelected)
- {
- QueryTextBox.SelectAll();
- _viewModel.LastQuerySelected = true;
- }
+ UpdatePosition(false);
+ _viewModel.ResetPreview();
+ Activate();
+ QueryTextBox.Focus();
+ _settings.ActivateTimes++;
+ if (!_viewModel.LastQuerySelected)
+ {
+ QueryTextBox.SelectAll();
+ _viewModel.LastQuerySelected = true;
+ }
- if (_settings.UseAnimation)
- WindowAnimator();
- }
- });
- break;
- }
+ if (_settings.UseAnimation)
+ WindowAnimation();
+ }
+ });
+ break;
+ }
case nameof(MainViewModel.QueryTextCursorMovedToEnd):
if (_viewModel.QueryTextCursorMovedToEnd)
{
- MoveQueryTextToEnd();
+ // QueryTextBox seems to be update with a DispatcherPriority as low as ContextIdle.
+ // To ensure QueryTextBox is up to date with QueryText from the View, we need to Dispatch with such a priority
+ Dispatcher.Invoke(() => QueryTextBox.CaretIndex = QueryTextBox.Text.Length);
_viewModel.QueryTextCursorMovedToEnd = false;
}
@@ -271,6 +219,356 @@ namespace Flow.Launcher
.AddValueChanged(History, (s, e) => UpdateClockPanelVisibility());
}
+ private async void OnClosing(object sender, CancelEventArgs e)
+ {
+ _notifyIcon.Visible = false;
+ App.API.SaveAppAllSettings();
+ e.Cancel = true;
+ await PluginManager.DisposePluginsAsync();
+ Notification.Uninstall();
+ Environment.Exit(0);
+ }
+
+ private void OnLocationChanged(object sender, EventArgs e)
+ {
+ if (_animating)
+ return;
+ if (_settings.SearchWindowScreen == SearchWindowScreens.RememberLastLaunchLocation)
+ {
+ _settings.WindowLeft = Left;
+ _settings.WindowTop = Top;
+ }
+ }
+
+ private async void OnDeactivated(object sender, EventArgs e)
+ {
+ _settings.WindowLeft = Left;
+ _settings.WindowTop = Top;
+ //This condition stops extra hide call when animator is on,
+ // which causes the toggling to occasional hide instead of show.
+ if (_viewModel.MainWindowVisibilityStatus)
+ {
+ // Need time to initialize the main query window animation.
+ // This also stops the mainwindow from flickering occasionally after Settings window is opened
+ // and always after Settings window is closed.
+ if (_settings.UseAnimation)
+ await Task.Delay(100);
+
+ if (_settings.HideWhenDeactivated && !_viewModel.ExternalPreviewVisible)
+ {
+ _viewModel.Hide();
+ }
+ }
+ }
+
+ private void OnKeyDown(object sender, KeyEventArgs e)
+ {
+ var specialKeyState = GlobalHotkey.CheckModifiers();
+ switch (e.Key)
+ {
+ case Key.Down:
+ isArrowKeyPressed = true;
+ _viewModel.SelectNextItemCommand.Execute(null);
+ e.Handled = true;
+ break;
+ case Key.Up:
+ isArrowKeyPressed = true;
+ _viewModel.SelectPrevItemCommand.Execute(null);
+ e.Handled = true;
+ break;
+ case Key.PageDown:
+ _viewModel.SelectNextPageCommand.Execute(null);
+ e.Handled = true;
+ break;
+ case Key.PageUp:
+ _viewModel.SelectPrevPageCommand.Execute(null);
+ e.Handled = true;
+ break;
+ case Key.Right:
+ if (_viewModel.SelectedIsFromQueryResults()
+ && QueryTextBox.CaretIndex == QueryTextBox.Text.Length
+ && !string.IsNullOrEmpty(QueryTextBox.Text))
+ {
+ _viewModel.LoadContextMenuCommand.Execute(null);
+ e.Handled = true;
+ }
+
+ break;
+ case Key.Left:
+ if (!_viewModel.SelectedIsFromQueryResults() && QueryTextBox.CaretIndex == 0)
+ {
+ _viewModel.EscCommand.Execute(null);
+ e.Handled = true;
+ }
+
+ break;
+ case Key.Back:
+ if (specialKeyState.CtrlPressed)
+ {
+ if (_viewModel.SelectedIsFromQueryResults()
+ && QueryTextBox.Text.Length > 0
+ && QueryTextBox.CaretIndex == QueryTextBox.Text.Length)
+ {
+ var queryWithoutActionKeyword =
+ QueryBuilder.Build(QueryTextBox.Text.Trim(), PluginManager.NonGlobalPlugins)?.Search;
+
+ if (FilesFolders.IsLocationPathString(queryWithoutActionKeyword))
+ {
+ _viewModel.BackspaceCommand.Execute(null);
+ e.Handled = true;
+ }
+ }
+ }
+
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void OnKeyUp(object sender, KeyEventArgs e)
+ {
+ if (e.Key == Key.Up || e.Key == Key.Down)
+ {
+ isArrowKeyPressed = false;
+ }
+ }
+
+ private void OnPreviewMouseMove(object sender, MouseEventArgs e)
+ {
+ if (isArrowKeyPressed)
+ {
+ e.Handled = true; // Ignore Mouse Hover when press Arrowkeys
+ }
+ }
+
+#pragma warning restore VSTHRD100
+
+ #endregion
+
+ #region Window Boarder Event
+
+ private void OnMouseDown(object sender, MouseButtonEventArgs e)
+ {
+ if (e.ChangedButton == MouseButton.Left) DragMove();
+ }
+
+ #endregion
+
+ #region Window Context Menu Event
+
+#pragma warning disable VSTHRD100
+
+ private async void OnContextMenusForSettingsClick(object sender, RoutedEventArgs e)
+ {
+ _viewModel.Hide();
+
+ if (_settings.UseAnimation)
+ await Task.Delay(100);
+
+ App.API.OpenSettingDialog();
+ }
+
+#pragma warning restore VSTHRD100
+
+ #endregion
+
+ #region Window WndProc
+
+ private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
+ {
+ if (Win32Helper.WM_ENTERSIZEMOVE(msg))
+ {
+ _initialWidth = (int)Width;
+ _initialHeight = (int)Height;
+ handled = true;
+ }
+ else if (Win32Helper.WM_EXITSIZEMOVE(msg))
+ {
+ if (_initialHeight != (int)Height)
+ {
+ var shadowMargin = 0;
+ if (_settings.UseDropShadowEffect)
+ {
+ shadowMargin = 32;
+ }
+
+ if (!_settings.KeepMaxResults)
+ {
+ var itemCount = (Height - (_settings.WindowHeightSize + 14) - shadowMargin) / _settings.ItemHeightSize;
+
+ if (itemCount < 2)
+ {
+ _settings.MaxResultsToShow = 2;
+ }
+ else
+ {
+ _settings.MaxResultsToShow = Convert.ToInt32(Math.Truncate(itemCount));
+ }
+ }
+
+ SizeToContent = SizeToContent.Height;
+ _viewModel.MainWindowWidth = Width;
+ }
+
+ if (_initialWidth != (int)Width)
+ {
+ SizeToContent = SizeToContent.Height;
+ }
+
+ handled = true;
+ }
+
+ return IntPtr.Zero;
+ }
+
+ #endregion
+
+ #region Window Sound Effects
+
+ private void InitSoundEffects()
+ {
+ if (_settings.WMPInstalled)
+ {
+ animationSoundWMP = new MediaPlayer();
+ animationSoundWMP.Open(new Uri(AppContext.BaseDirectory + "Resources\\open.wav"));
+ }
+ else
+ {
+ animationSoundWPF = new SoundPlayer(AppContext.BaseDirectory + "Resources\\open.wav");
+ }
+ }
+
+ private void SoundPlay()
+ {
+ if (_settings.WMPInstalled)
+ {
+ animationSoundWMP.Position = TimeSpan.Zero;
+ animationSoundWMP.Volume = _settings.SoundVolume / 100.0;
+ animationSoundWMP.Play();
+ }
+ else
+ {
+ animationSoundWPF.Play();
+ }
+ }
+
+ #endregion
+
+ #region Window Notify Icon
+
+ private void InitializeNotifyIcon()
+ {
+ _notifyIcon = new NotifyIcon
+ {
+ Text = Constant.FlowLauncherFullName,
+ Icon = Constant.Version == "1.0.0" ? Properties.Resources.dev : Properties.Resources.app,
+ Visible = !_settings.HideNotifyIcon
+ };
+ var openIcon = new FontIcon { Glyph = "\ue71e" };
+ var open = new MenuItem
+ {
+ Header = App.API.GetTranslation("iconTrayOpen") + " (" + _settings.Hotkey + ")",
+ Icon = openIcon
+ };
+ var gamemodeIcon = new FontIcon { Glyph = "\ue7fc" };
+ var gamemode = new MenuItem
+ {
+ Header = App.API.GetTranslation("GameMode"),
+ Icon = gamemodeIcon
+ };
+ var positionresetIcon = new FontIcon { Glyph = "\ue73f" };
+ var positionreset = new MenuItem
+ {
+ Header = App.API.GetTranslation("PositionReset"),
+ Icon = positionresetIcon
+ };
+ var settingsIcon = new FontIcon { Glyph = "\ue713" };
+ var settings = new MenuItem
+ {
+ Header = App.API.GetTranslation("iconTraySettings"),
+ Icon = settingsIcon
+ };
+ var exitIcon = new FontIcon { Glyph = "\ue7e8" };
+ var exit = new MenuItem
+ {
+ Header = App.API.GetTranslation("iconTrayExit"),
+ Icon = exitIcon
+ };
+
+ open.Click += (o, e) => _viewModel.ToggleFlowLauncher();
+ gamemode.Click += (o, e) => _viewModel.ToggleGameMode();
+ positionreset.Click += (o, e) => _ = PositionResetAsync();
+ settings.Click += (o, e) => App.API.OpenSettingDialog();
+ exit.Click += (o, e) => Close();
+
+ gamemode.ToolTip = App.API.GetTranslation("GameModeToolTip");
+ positionreset.ToolTip = App.API.GetTranslation("PositionResetToolTip");
+
+ contextMenu.Items.Add(open);
+ contextMenu.Items.Add(gamemode);
+ contextMenu.Items.Add(positionreset);
+ contextMenu.Items.Add(settings);
+ contextMenu.Items.Add(exit);
+
+ _notifyIcon.MouseClick += (o, e) =>
+ {
+ switch (e.Button)
+ {
+ case System.Windows.Forms.MouseButtons.Left:
+ _viewModel.ToggleFlowLauncher();
+ break;
+ case System.Windows.Forms.MouseButtons.Right:
+
+ contextMenu.IsOpen = true;
+ // Get context menu handle and bring it to the foreground
+ if (PresentationSource.FromVisual(contextMenu) is HwndSource hwndSource)
+ {
+ Win32Helper.SetForegroundWindow(hwndSource.Handle);
+ }
+
+ contextMenu.Focus();
+ break;
+ }
+ };
+ }
+
+ private void UpdateNotifyIconText()
+ {
+ var menu = contextMenu;
+ ((MenuItem)menu.Items[0]).Header = App.API.GetTranslation("iconTrayOpen") +
+ " (" + _settings.Hotkey + ")";
+ ((MenuItem)menu.Items[1]).Header = App.API.GetTranslation("GameMode");
+ ((MenuItem)menu.Items[2]).Header = App.API.GetTranslation("PositionReset");
+ ((MenuItem)menu.Items[3]).Header = App.API.GetTranslation("iconTraySettings");
+ ((MenuItem)menu.Items[4]).Header = App.API.GetTranslation("iconTrayExit");
+ }
+
+ #endregion
+
+ #region Window Position
+
+ private void UpdatePosition(bool force)
+ {
+ if (_animating && !force)
+ {
+ return;
+ }
+
+ // Initialize call twice to work around multi-display alignment issue- https://github.com/Flow-Launcher/Flow.Launcher/issues/2910
+ InitializePosition();
+ InitializePosition();
+ }
+
+ private async Task PositionResetAsync()
+ {
+ _viewModel.Show();
+ await Task.Delay(300); // If don't give a time, Positioning will be weird.
+ var screen = SelectedScreen();
+ Left = HorizonCenter(screen);
+ Top = VerticalCenter(screen);
+ }
+
private void InitializePosition()
{
// Initialize call twice to work around multi-display alignment issue- https://github.com/Flow-Launcher/Flow.Launcher/issues/2910
@@ -317,119 +615,70 @@ namespace Flow.Launcher
}
}
- private void UpdateNotifyIconText()
+ private Screen SelectedScreen()
{
- var menu = contextMenu;
- ((MenuItem)menu.Items[0]).Header = InternationalizationManager.Instance.GetTranslation("iconTrayOpen") +
- " (" + _settings.Hotkey + ")";
- ((MenuItem)menu.Items[1]).Header = InternationalizationManager.Instance.GetTranslation("GameMode");
- ((MenuItem)menu.Items[2]).Header = InternationalizationManager.Instance.GetTranslation("PositionReset");
- ((MenuItem)menu.Items[3]).Header = InternationalizationManager.Instance.GetTranslation("iconTraySettings");
- ((MenuItem)menu.Items[4]).Header = InternationalizationManager.Instance.GetTranslation("iconTrayExit");
- }
-
- private void InitializeNotifyIcon()
- {
- _notifyIcon = new NotifyIcon
+ Screen screen;
+ switch (_settings.SearchWindowScreen)
{
- Text = Constant.FlowLauncherFullName,
- Icon = Constant.Version == "1.0.0" ? Properties.Resources.dev : Properties.Resources.app,
- Visible = !_settings.HideNotifyIcon
- };
- var openIcon = new FontIcon { Glyph = "\ue71e" };
- var open = new MenuItem
- {
- Header = InternationalizationManager.Instance.GetTranslation("iconTrayOpen") + " (" +
- _settings.Hotkey + ")",
- Icon = openIcon
- };
- var gamemodeIcon = new FontIcon { Glyph = "\ue7fc" };
- var gamemode = new MenuItem
- {
- Header = InternationalizationManager.Instance.GetTranslation("GameMode"),
- Icon = gamemodeIcon
- };
- var positionresetIcon = new FontIcon { Glyph = "\ue73f" };
- var positionreset = new MenuItem
- {
- Header = InternationalizationManager.Instance.GetTranslation("PositionReset"),
- Icon = positionresetIcon
- };
- var settingsIcon = new FontIcon { Glyph = "\ue713" };
- var settings = new MenuItem
- {
- Header = InternationalizationManager.Instance.GetTranslation("iconTraySettings"),
- Icon = settingsIcon
- };
- var exitIcon = new FontIcon { Glyph = "\ue7e8" };
- var exit = new MenuItem
- {
- Header = InternationalizationManager.Instance.GetTranslation("iconTrayExit"),
- Icon = exitIcon
- };
-
- open.Click += (o, e) => _viewModel.ToggleFlowLauncher();
- gamemode.Click += (o, e) => _viewModel.ToggleGameMode();
- positionreset.Click += (o, e) => _ = PositionResetAsync();
- settings.Click += (o, e) => App.API.OpenSettingDialog();
- exit.Click += (o, e) => Close();
-
- gamemode.ToolTip = InternationalizationManager.Instance.GetTranslation("GameModeToolTip");
- positionreset.ToolTip = InternationalizationManager.Instance.GetTranslation("PositionResetToolTip");
-
- contextMenu.Items.Add(open);
- contextMenu.Items.Add(gamemode);
- contextMenu.Items.Add(positionreset);
- contextMenu.Items.Add(settings);
- contextMenu.Items.Add(exit);
-
- _notifyIcon.MouseClick += (o, e) =>
- {
- switch (e.Button)
- {
- case System.Windows.Forms.MouseButtons.Left:
- _viewModel.ToggleFlowLauncher();
- break;
- case System.Windows.Forms.MouseButtons.Right:
-
- contextMenu.IsOpen = true;
- // Get context menu handle and bring it to the foreground
- if (PresentationSource.FromVisual(contextMenu) is HwndSource hwndSource)
- {
- Win32Helper.SetForegroundWindow(hwndSource.Handle);
- }
-
- contextMenu.Focus();
- break;
- }
- };
- }
-
- private void CheckFirstLaunch()
- {
- if (_settings.FirstLaunch)
- {
- _settings.FirstLaunch = false;
- App.API.SaveAppAllSettings();
- OpenWelcomeWindow();
+ case SearchWindowScreens.Cursor:
+ screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position);
+ break;
+ case SearchWindowScreens.Primary:
+ screen = Screen.PrimaryScreen;
+ break;
+ case SearchWindowScreens.Focus:
+ var foregroundWindowHandle = Win32Helper.GetForegroundWindow();
+ screen = Screen.FromHandle(foregroundWindowHandle);
+ break;
+ case SearchWindowScreens.Custom:
+ if (_settings.CustomScreenNumber <= Screen.AllScreens.Length)
+ screen = Screen.AllScreens[_settings.CustomScreenNumber - 1];
+ else
+ screen = Screen.AllScreens[0];
+ break;
+ default:
+ screen = Screen.AllScreens[0];
+ break;
}
+
+ return screen ?? Screen.AllScreens[0];
}
- private static void OpenWelcomeWindow()
+ private double HorizonCenter(Screen screen)
{
- var WelcomeWindow = new WelcomeWindow();
- WelcomeWindow.Show();
+ 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;
}
- private async Task PositionResetAsync()
+ private double VerticalCenter(Screen screen)
{
- _viewModel.Show();
- await Task.Delay(300); // If don't give a time, Positioning will be weird.
- var screen = SelectedScreen();
- Left = HorizonCenter(screen);
- Top = VerticalCenter(screen);
+ var dip1 = Win32Helper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Y);
+ var dip2 = Win32Helper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Height);
+ var top = (dip2.Y - QueryTextBox.ActualHeight) / 4 + dip1.Y;
+ return top;
}
+ private double HorizonRight(Screen screen)
+ {
+ var dip1 = Win32Helper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0);
+ var dip2 = Win32Helper.TransformPixelsToDIP(this, screen.WorkingArea.Width, 0);
+ var left = (dip1.X + dip2.X - ActualWidth) - 10;
+ return left;
+ }
+
+ private double HorizonLeft(Screen screen)
+ {
+ var dip1 = Win32Helper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0);
+ var left = dip1.X + 10;
+ return left;
+ }
+
+ #endregion
+
+ #region Window Animation
+
private void InitProgressbarAnimation()
{
var progressBarStoryBoard = new Storyboard();
@@ -477,27 +726,14 @@ namespace Flow.Launcher
_viewModel.ProgressBarVisibility = Visibility.Hidden;
}
- public void ResetAnimation()
- {
- // 애니메이션 중지
- clocksb?.Stop(ClockPanel);
- iconsb?.Stop(SearchIcon);
- windowsb?.Stop(FlowMainWindow);
-
- // UI 요소 상태 초기화
- //ClockPanel.Margin = new Thickness(0, 0, ClockPanel.Margin.Right, 0);
- ClockPanel.Opacity = 0;
- SearchIcon.Opacity = 0;
- }
-
- public void WindowAnimator()
+ private void WindowAnimation()
{
if (_animating)
return;
isArrowKeyPressed = true;
_animating = true;
- UpdatePosition();
+ UpdatePosition(false);
windowsb = new Storyboard();
clocksb = new Storyboard();
@@ -604,43 +840,9 @@ namespace Flow.Launcher
}
iconsb.Begin(SearchIcon);
- windowsb.Begin(FlowMainWindow);
+ windowsb.Begin(this);
}
- private static double GetOpacityFromStyle(Style style, double defaultOpacity = 1.0)
- {
- if (style == null)
- return defaultOpacity;
-
- foreach (Setter setter in style.Setters.Cast())
- {
- if (setter.Property == OpacityProperty)
- {
- return setter.Value is double opacity ? opacity : defaultOpacity;
- }
- }
-
- return defaultOpacity;
- }
-
- private static Thickness GetThicknessFromStyle(Style style, Thickness defaultThickness)
- {
- if (style == null)
- return defaultThickness;
-
- foreach (Setter setter in style.Setters.Cast())
- {
- if (setter.Property == MarginProperty)
- {
- return setter.Value is Thickness thickness ? thickness : defaultThickness;
- }
- }
-
- return defaultThickness;
- }
-
- private bool _isClockPanelAnimating = false; // 애니메이션 실행 중인지 여부
-
private void UpdateClockPanelVisibility()
{
if (QueryTextBox == null || ContextMenu == null || History == null || ClockPanel == null)
@@ -726,273 +928,66 @@ namespace Flow.Launcher
}
}
- private void InitSoundEffects()
+ private static double GetOpacityFromStyle(Style style, double defaultOpacity = 1.0)
{
- if (_settings.WMPInstalled)
+ if (style == null)
+ return defaultOpacity;
+
+ foreach (Setter setter in style.Setters.Cast())
{
- animationSoundWMP = new MediaPlayer();
- animationSoundWMP.Open(new Uri(AppDomain.CurrentDomain.BaseDirectory + "Resources\\open.wav"));
- }
- else
- {
- animationSoundWPF = new SoundPlayer(AppDomain.CurrentDomain.BaseDirectory + "Resources\\open.wav");
- }
- }
-
- private void SoundPlay()
- {
- if (_settings.WMPInstalled)
- {
- animationSoundWMP.Position = TimeSpan.Zero;
- animationSoundWMP.Volume = _settings.SoundVolume / 100.0;
- animationSoundWMP.Play();
- }
- else
- {
- animationSoundWPF.Play();
- }
- }
-
- private void OnMouseDown(object sender, MouseButtonEventArgs e)
- {
- if (e.ChangedButton == MouseButton.Left) DragMove();
- }
-
- private void OnPreviewDragOver(object sender, DragEventArgs e)
- {
- e.Handled = true;
- }
-
- private async void OnContextMenusForSettingsClick(object sender, RoutedEventArgs e)
- {
- _viewModel.Hide();
-
- if (_settings.UseAnimation)
- await Task.Delay(100);
-
- App.API.OpenSettingDialog();
- }
-
- private async void OnDeactivated(object sender, EventArgs e)
- {
- _settings.WindowLeft = Left;
- _settings.WindowTop = Top;
- //This condition stops extra hide call when animator is on,
- // which causes the toggling to occasional hide instead of show.
- if (_viewModel.MainWindowVisibilityStatus)
- {
- // Need time to initialize the main query window animation.
- // This also stops the mainwindow from flickering occasionally after Settings window is opened
- // and always after Settings window is closed.
- if (_settings.UseAnimation)
- await Task.Delay(100);
-
- if (_settings.HideWhenDeactivated && !_viewModel.ExternalPreviewVisible)
+ if (setter.Property == OpacityProperty)
{
- _viewModel.Hide();
+ return setter.Value is double opacity ? opacity : defaultOpacity;
}
}
+
+ return defaultOpacity;
}
- private void UpdatePosition()
+ private static Thickness GetThicknessFromStyle(Style style, Thickness defaultThickness)
{
- if (_animating)
- return;
+ if (style == null)
+ return defaultThickness;
- // Initialize call twice to work around multi-display alignment issue- https://github.com/Flow-Launcher/Flow.Launcher/issues/2910
- InitializePosition();
- InitializePosition();
- }
-
- private void OnLocationChanged(object sender, EventArgs e)
- {
- if (_animating)
- return;
- if (_settings.SearchWindowScreen == SearchWindowScreens.RememberLastLaunchLocation)
+ foreach (Setter setter in style.Setters.Cast())
{
- _settings.WindowLeft = Left;
- _settings.WindowTop = Top;
+ if (setter.Property == MarginProperty)
+ {
+ return setter.Value is Thickness thickness ? thickness : defaultThickness;
+ }
+ }
+
+ return defaultThickness;
+ }
+
+ #endregion
+
+ #region QueryTextBox Event
+
+ private void QueryTextBox_OnCopy(object sender, ExecutedRoutedEventArgs e)
+ {
+ var result = _viewModel.Results.SelectedItem?.Result;
+ if (QueryTextBox.SelectionLength == 0 && result != null)
+ {
+ string copyText = result.CopyText;
+ App.API.CopyToClipboard(copyText, directCopy: true);
+ }
+ else if (!string.IsNullOrEmpty(QueryTextBox.Text))
+ {
+ App.API.CopyToClipboard(QueryTextBox.SelectedText, showDefaultNotification: false);
}
}
- public void HideStartup()
+ private void QueryTextBox_OnPaste(object sender, DataObjectPastingEventArgs e)
{
- if (_settings.HideOnStartup)
+ var isText = e.SourceDataObject.GetDataPresent(DataFormats.UnicodeText, true);
+ if (isText)
{
- _viewModel.Hide();
- }
- else
- {
- _viewModel.Show();
- }
- }
-
- public Screen SelectedScreen()
- {
- Screen screen;
- switch (_settings.SearchWindowScreen)
- {
- case SearchWindowScreens.Cursor:
- screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position);
- break;
- case SearchWindowScreens.Primary:
- screen = Screen.PrimaryScreen;
- break;
- case SearchWindowScreens.Focus:
- var foregroundWindowHandle = Win32Helper.GetForegroundWindow();
- screen = Screen.FromHandle(foregroundWindowHandle);
- break;
- case SearchWindowScreens.Custom:
- if (_settings.CustomScreenNumber <= Screen.AllScreens.Length)
- screen = Screen.AllScreens[_settings.CustomScreenNumber - 1];
- else
- screen = Screen.AllScreens[0];
- break;
- default:
- screen = Screen.AllScreens[0];
- break;
- }
-
- return screen ?? Screen.AllScreens[0];
- }
-
- public double HorizonCenter(Screen screen)
- {
- 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;
- }
-
- public double VerticalCenter(Screen screen)
- {
- var dip1 = Win32Helper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Y);
- var dip2 = Win32Helper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Height);
- var top = (dip2.Y - QueryTextBox.ActualHeight) / 4 + dip1.Y;
- return top;
- }
-
- public double HorizonRight(Screen screen)
- {
- var dip1 = Win32Helper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0);
- var dip2 = Win32Helper.TransformPixelsToDIP(this, screen.WorkingArea.Width, 0);
- var left = (dip1.X + dip2.X - ActualWidth) - 10;
- return left;
- }
-
- public double HorizonLeft(Screen screen)
- {
- var dip1 = Win32Helper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0);
- var left = dip1.X + 10;
- return left;
- }
-
- ///
- /// Register up and down key
- /// todo: Put this in xaml?
- ///
- private void OnKeyDown(object sender, KeyEventArgs e)
- {
- var specialKeyState = GlobalHotkey.CheckModifiers();
- switch (e.Key)
- {
- case Key.Down:
- isArrowKeyPressed = true;
- _viewModel.SelectNextItemCommand.Execute(null);
- e.Handled = true;
- break;
- case Key.Up:
- isArrowKeyPressed = true;
- _viewModel.SelectPrevItemCommand.Execute(null);
- e.Handled = true;
- break;
- case Key.PageDown:
- _viewModel.SelectNextPageCommand.Execute(null);
- e.Handled = true;
- break;
- case Key.PageUp:
- _viewModel.SelectPrevPageCommand.Execute(null);
- e.Handled = true;
- break;
- case Key.Right:
- if (_viewModel.SelectedIsFromQueryResults()
- && QueryTextBox.CaretIndex == QueryTextBox.Text.Length
- && !string.IsNullOrEmpty(QueryTextBox.Text))
- {
- _viewModel.LoadContextMenuCommand.Execute(null);
- e.Handled = true;
- }
-
- break;
- case Key.Left:
- if (!_viewModel.SelectedIsFromQueryResults() && QueryTextBox.CaretIndex == 0)
- {
- _viewModel.EscCommand.Execute(null);
- e.Handled = true;
- }
-
- break;
- case Key.Back:
- if (specialKeyState.CtrlPressed)
- {
- if (_viewModel.SelectedIsFromQueryResults()
- && QueryTextBox.Text.Length > 0
- && QueryTextBox.CaretIndex == QueryTextBox.Text.Length)
- {
- var queryWithoutActionKeyword =
- QueryBuilder.Build(QueryTextBox.Text.Trim(), PluginManager.NonGlobalPlugins)?.Search;
-
- if (FilesFolders.IsLocationPathString(queryWithoutActionKeyword))
- {
- _viewModel.BackspaceCommand.Execute(null);
- e.Handled = true;
- }
- }
- }
-
- break;
- default:
- break;
- }
- }
-
- private void OnKeyUp(object sender, KeyEventArgs e)
- {
- if (e.Key == Key.Up || e.Key == Key.Down)
- {
- isArrowKeyPressed = false;
- }
- }
-
- private void MainPreviewMouseMove(object sender, MouseEventArgs e)
- {
- if (isArrowKeyPressed)
- {
- e.Handled = true; // Ignore Mouse Hover when press Arrowkeys
- }
- }
-
- public void PreviewReset()
- {
- _viewModel.ResetPreview();
- }
-
- private void MoveQueryTextToEnd()
- {
- // QueryTextBox seems to be update with a DispatcherPriority as low as ContextIdle.
- // To ensure QueryTextBox is up to date with QueryText from the View, we need to Dispatch with such a priority
- Dispatcher.Invoke(() => QueryTextBox.CaretIndex = QueryTextBox.Text.Length);
- }
-
- public void InitializeColorScheme()
- {
- if (_settings.ColorScheme == Constant.Light)
- {
- ModernWpf.ThemeManager.Current.ApplicationTheme = ModernWpf.ApplicationTheme.Light;
- }
- else if (_settings.ColorScheme == Constant.Dark)
- {
- ModernWpf.ThemeManager.Current.ApplicationTheme = ModernWpf.ApplicationTheme.Dark;
+ var text = e.SourceDataObject.GetData(DataFormats.UnicodeText) as string;
+ text = text.Replace(Environment.NewLine, " ");
+ DataObject data = new DataObject();
+ data.SetData(DataFormats.UnicodeText, text);
+ e.DataObject = data;
}
}
@@ -1004,5 +999,12 @@ namespace Flow.Launcher
be.UpdateSource();
}
}
+
+ private void QueryTextBox_OnPreviewDragOver(object sender, DragEventArgs e)
+ {
+ e.Handled = true;
+ }
+
+ #endregion
}
}
From 49f1d7979843845d22ad90ad45bcce568b699f30 Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Sun, 16 Mar 2025 23:18:50 +0800
Subject: [PATCH 04/12] Fix SetWindowLong issue
---
.../NativeMethods.txt | 1 -
.../PInvokeExtensions.cs | 25 ++++++++++++++++++
Flow.Launcher.Infrastructure/Win32Helper.cs | 26 +++++++++----------
Flow.Launcher/MainWindow.xaml.cs | 4 +--
4 files changed, 40 insertions(+), 16 deletions(-)
create mode 100644 Flow.Launcher.Infrastructure/PInvokeExtensions.cs
diff --git a/Flow.Launcher.Infrastructure/NativeMethods.txt b/Flow.Launcher.Infrastructure/NativeMethods.txt
index 5d72239f3..f080f24de 100644
--- a/Flow.Launcher.Infrastructure/NativeMethods.txt
+++ b/Flow.Launcher.Infrastructure/NativeMethods.txt
@@ -28,7 +28,6 @@ SystemParametersInfo
SetForegroundWindow
GetWindowLong
-SetWindowLong
GetForegroundWindow
GetDesktopWindow
GetShellWindow
diff --git a/Flow.Launcher.Infrastructure/PInvokeExtensions.cs b/Flow.Launcher.Infrastructure/PInvokeExtensions.cs
new file mode 100644
index 000000000..1a72ab7a6
--- /dev/null
+++ b/Flow.Launcher.Infrastructure/PInvokeExtensions.cs
@@ -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);
+ }
+}
diff --git a/Flow.Launcher.Infrastructure/Win32Helper.cs b/Flow.Launcher.Infrastructure/Win32Helper.cs
index 6e429cddd..cdb384ab7 100644
--- a/Flow.Launcher.Infrastructure/Win32Helper.cs
+++ b/Flow.Launcher.Infrastructure/Win32Helper.cs
@@ -132,12 +132,12 @@ namespace Flow.Launcher.Infrastructure
{
var hwnd = GetWindowHandle(window);
- var exStyle = GetCurrentWindowStyle(hwnd, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE);
+ 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;
- SetWindowLong(hwnd, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE, (int)newExStyle);
+ SetWindowStyle(hwnd, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE, (int)newExStyle);
}
///
@@ -148,12 +148,12 @@ namespace Flow.Launcher.Infrastructure
{
var hwnd = GetWindowHandle(window);
- var exStyle = GetCurrentWindowStyle(hwnd, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE);
+ 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;
- SetWindowLong(GetWindowHandle(window), WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE, (int)newExStyle);
+ SetWindowStyle(GetWindowHandle(window), WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE, (int)newExStyle);
}
///
@@ -164,14 +164,14 @@ namespace Flow.Launcher.Infrastructure
{
var hwnd = GetWindowHandle(window);
- var style = GetCurrentWindowStyle(hwnd, WINDOW_LONG_PTR_INDEX.GWL_STYLE);
+ var style = GetWindowStyle(hwnd, WINDOW_LONG_PTR_INDEX.GWL_STYLE);
style &= ~(int)WINDOW_STYLE.WS_SYSMENU;
- SetWindowLong(hwnd, WINDOW_LONG_PTR_INDEX.GWL_STYLE, style);
+ SetWindowStyle(hwnd, WINDOW_LONG_PTR_INDEX.GWL_STYLE, style);
}
- private static int GetCurrentWindowStyle(HWND hWnd, WINDOW_LONG_PTR_INDEX nIndex)
+ private static int GetWindowStyle(HWND hWnd, WINDOW_LONG_PTR_INDEX nIndex)
{
var style = PInvoke.GetWindowLong(hWnd, nIndex);
if (style == 0 && Marshal.GetLastPInvokeError() != 0)
@@ -181,11 +181,11 @@ namespace Flow.Launcher.Infrastructure
return style;
}
- private static int SetWindowLong(HWND hWnd, WINDOW_LONG_PTR_INDEX nIndex, int dwNewLong)
+ 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.SetWindowLong(hWnd, nIndex, dwNewLong);
+ var result = PInvoke.SetWindowLongPtr(hWnd, nIndex, dwNewLong);
if (result == 0 && Marshal.GetLastPInvokeError() != 0)
{
throw new Win32Exception(Marshal.GetLastPInvokeError());
@@ -299,14 +299,14 @@ namespace Flow.Launcher.Infrastructure
#region WndProc
- public static bool WM_ENTERSIZEMOVE(int msg)
+ public static bool WM_ENTERSIZEMOVE(uint msg)
{
- return msg == (int)PInvoke.WM_ENTERSIZEMOVE;
+ return msg == PInvoke.WM_ENTERSIZEMOVE;
}
- public static bool WM_EXITSIZEMOVE(int msg)
+ public static bool WM_EXITSIZEMOVE(uint msg)
{
- return msg == (int)PInvoke.WM_EXITSIZEMOVE;
+ return msg == PInvoke.WM_EXITSIZEMOVE;
}
#endregion
diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs
index d0375e8f6..9038762f2 100644
--- a/Flow.Launcher/MainWindow.xaml.cs
+++ b/Flow.Launcher/MainWindow.xaml.cs
@@ -377,13 +377,13 @@ namespace Flow.Launcher
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
- if (Win32Helper.WM_ENTERSIZEMOVE(msg))
+ if (Win32Helper.WM_ENTERSIZEMOVE((uint)msg))
{
_initialWidth = (int)Width;
_initialHeight = (int)Height;
handled = true;
}
- else if (Win32Helper.WM_EXITSIZEMOVE(msg))
+ else if (Win32Helper.WM_EXITSIZEMOVE((uint)msg))
{
if (_initialHeight != (int)Height)
{
From bf5591c9a84c6cf58f0b5be162729626c8681e23 Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Sun, 16 Mar 2025 23:19:35 +0800
Subject: [PATCH 05/12] Fix position set issue
---
Flow.Launcher/MainWindow.xaml.cs | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs
index 9038762f2..aa77d64f3 100644
--- a/Flow.Launcher/MainWindow.xaml.cs
+++ b/Flow.Launcher/MainWindow.xaml.cs
@@ -65,7 +65,8 @@ namespace Flow.Launcher
_settings = settings;
InitializeComponent();
-
+ UpdatePosition(true);
+
InitSoundEffects();
DataObject.AddPastingHandler(QueryTextBox, QueryTextBox_OnPaste);
}
@@ -97,6 +98,7 @@ namespace Flow.Launcher
}
// Hide window if need
+ UpdatePosition(true);
if (_settings.HideOnStartup)
{
_viewModel.Hide();
From 0bcc187b5defe11d79054bcb64816d5054f6b4b7 Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Sun, 16 Mar 2025 23:55:15 +0800
Subject: [PATCH 06/12] Keep user settings when changing theme
---
Flow.Launcher.Core/Resource/Theme.cs | 137 +++++++++++-------
.../UserSettings/Settings.cs | 1 -
Flow.Launcher/App.xaml.cs | 3 +-
Flow.Launcher/MainWindow.xaml.cs | 13 +-
.../ViewModels/SettingsPaneThemeViewModel.cs | 16 +-
.../Flow.Launcher.Plugin.Sys/ThemeSelector.cs | 15 --
6 files changed, 94 insertions(+), 91 deletions(-)
diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs
index 4169f3534..17fb698b4 100644
--- a/Flow.Launcher.Core/Resource/Theme.cs
+++ b/Flow.Launcher.Core/Resource/Theme.cs
@@ -416,21 +416,24 @@ namespace Flow.Launcher.Core.Resource
///
public async Task RefreshFrameAsync()
{
- await Application.Current.Dispatcher.InvokeAsync(async () =>
+ 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();
+ AutoDropShadow(useDropShadowEffect);
}
- await SetBlurForWindowAsync();
+ SetBlurForWindow(backdropType);
if (!BlurEnabled)
{
- AutoDropShadow();
+ AutoDropShadow(useDropShadowEffect);
}
}, DispatcherPriority.Normal);
}
@@ -440,61 +443,87 @@ namespace Flow.Launcher.Core.Resource
///
public async Task SetBlurForWindowAsync()
{
- await Application.Current.Dispatcher.InvokeAsync(async () =>
+ await Application.Current.Dispatcher.InvokeAsync(() =>
{
- var dict = GetThemeResourceDictionary(_settings.Theme);
- if (dict == null)
- return;
+ // Get the actual backdrop type and drop shadow effect settings
+ var (backdropType, _) = GetActualValue();
- 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 (!hasBlur)
- {
- _settings.BackdropType = BackdropTypes.None;
- }
-
- if (BlurEnabled && hasBlur && Win32Helper.IsBackdropSupported())
- {
- // If the BackdropType is Mica or MicaAlt, set the windowborderstyle's background to transparent
- if (_settings.BackdropType == BackdropTypes.Mica || _settings.BackdropType == BackdropTypes.MicaAlt)
- {
- windowBorderStyle.Setters.Remove(windowBorderStyle.Setters.OfType().FirstOrDefault(x => x.Property.Name == "Background"));
- windowBorderStyle.Setters.Add(new Setter(Border.BackgroundProperty, new SolidColorBrush(Color.FromArgb(1, 0, 0, 0))));
- }
- else if (_settings.BackdropType == BackdropTypes.Acrylic)
- {
- windowBorderStyle.Setters.Remove(windowBorderStyle.Setters.OfType().FirstOrDefault(x => x.Property.Name == "Background"));
- windowBorderStyle.Setters.Add(new Setter(Border.BackgroundProperty, new SolidColorBrush(Colors.Transparent)));
- }
-
- // Apply the blur effect
- Win32Helper.DWMSetBackdropForWindow(mainWindow, _settings.BackdropType);
- ColorizeWindow();
- }
- else
- {
- // Apply default style when Blur is disabled
- Win32Helper.DWMSetBackdropForWindow(mainWindow, BackdropTypes.None);
- ColorizeWindow();
- }
-
- UpdateResourceDictionary(dict);
+ SetBlurForWindow(backdropType);
}, DispatcherPriority.Normal);
}
- private void AutoDropShadow()
+ ///
+ /// Gets the actual backdrop type and drop shadow effect settings based on the current theme status.
+ ///
+ 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(BackdropTypes backdropType)
+ {
+ var dict = GetThemeResourceDictionary(_settings.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().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().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(backdropType);
+ }
+ else
+ {
+ // Apply default style when Blur is disabled
+ Win32Helper.DWMSetBackdropForWindow(mainWindow, BackdropTypes.None);
+ ColorizeWindow(backdropType);
+ }
+
+ UpdateResourceDictionary(dict);
+ }
+
+ private void AutoDropShadow(bool useDropShadowEffect)
{
SetWindowCornerPreference("Default");
RemoveDropShadowEffectFromCurrentTheme();
- if (_settings.UseDropShadowEffect)
+ if (useDropShadowEffect)
{
if (BlurEnabled && Win32Helper.IsBackdropSupported())
{
@@ -605,7 +634,7 @@ namespace Flow.Launcher.Core.Resource
Application.Current.Resources["PreviewWindowBorderStyle"] = previewStyle;
}
- private void ColorizeWindow()
+ private void ColorizeWindow(BackdropTypes backdropType)
{
var dict = GetThemeResourceDictionary(_settings.Theme);
if (dict == null) return;
@@ -688,7 +717,7 @@ namespace Flow.Launcher.Core.Resource
else
{
// Only set the background to transparent if the theme supports blur
- if (_settings.BackdropType == BackdropTypes.Mica || _settings.BackdropType == BackdropTypes.MicaAlt)
+ if (backdropType == BackdropTypes.Mica || backdropType == BackdropTypes.MicaAlt)
{
mainWindow.Background = new SolidColorBrush(Color.FromArgb(1, 0, 0, 0));
}
diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs
index 8277e8e0b..63debfb47 100644
--- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs
+++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs
@@ -76,7 +76,6 @@ 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.*/
diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs
index 550b1bdae..7d094a0af 100644
--- a/Flow.Launcher/App.xaml.cs
+++ b/Flow.Launcher/App.xaml.cs
@@ -137,8 +137,7 @@ namespace Flow.Launcher
await PluginManager.InitializePluginsAsync();
await imageLoadertask;
- var mainVM = Ioc.Default.GetRequiredService();
- var window = new MainWindow(_settings, mainVM);
+ var window = new MainWindow();
Log.Info($"|App.OnStartup|Dependencies Info:{ErrorReporting.DependenciesInfo()}");
diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs
index aa77d64f3..3bc9be83f 100644
--- a/Flow.Launcher/MainWindow.xaml.cs
+++ b/Flow.Launcher/MainWindow.xaml.cs
@@ -36,6 +36,7 @@ namespace Flow.Launcher
#region Private Fields
private readonly Settings _settings;
+ private readonly Theme _theme;
private NotifyIcon _notifyIcon;
private readonly ContextMenu contextMenu = new();
private readonly MainViewModel _viewModel;
@@ -58,11 +59,12 @@ namespace Flow.Launcher
#region Constructor
- public MainWindow(Settings settings, MainViewModel mainVM)
+ public MainWindow()
{
- DataContext = mainVM;
- _viewModel = mainVM;
- _settings = settings;
+ _settings = Ioc.Default.GetRequiredService();
+ _theme = Ioc.Default.GetRequiredService();
+ _viewModel = Ioc.Default.GetRequiredService();
+ DataContext = _viewModel;
InitializeComponent();
UpdatePosition(true);
@@ -390,7 +392,8 @@ namespace Flow.Launcher
if (_initialHeight != (int)Height)
{
var shadowMargin = 0;
- if (_settings.UseDropShadowEffect)
+ var (_, useDropShadowEffect) = _theme.GetActualValue();
+ if (useDropShadowEffect)
{
shadowMargin = 32;
}
diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs
index 1036d321c..f8c0fa642 100644
--- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs
+++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs
@@ -35,18 +35,6 @@ public partial class SettingsPaneThemeViewModel : BaseModel
_selectedTheme = value;
ThemeManager.Instance.ChangeTheme(value.FileNameWithoutExtension);
- // when changed non-blur theme, change to backdrop to none
- if (!ThemeManager.Instance.BlurEnabled)
- {
- Settings.BackdropType = BackdropTypes.None;
- }
-
- // dropshadow on and control disabled.(user can't change dropshadow with blur theme)
- if (ThemeManager.Instance.BlurEnabled)
- {
- Settings.UseDropShadowEffect = true;
- }
-
// Update UI state
OnPropertyChanged(nameof(BackdropType));
OnPropertyChanged(nameof(IsBackdropEnabled));
@@ -235,8 +223,8 @@ public partial class SettingsPaneThemeViewModel : BaseModel
public BackdropTypes BackdropType
{
get => Enum.IsDefined(typeof(BackdropTypes), Settings.BackdropType)
- ? Settings.BackdropType
- : BackdropTypes.None;
+ ? Settings.BackdropType
+ : BackdropTypes.None;
set
{
if (!Enum.IsDefined(typeof(BackdropTypes), value))
diff --git a/Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs b/Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs
index 913c313de..b467aefee 100644
--- a/Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs
+++ b/Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs
@@ -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;
@@ -27,18 +25,6 @@ namespace Flow.Launcher.Plugin.Sys
_selectedTheme = value;
_theme.ChangeTheme(value.FileNameWithoutExtension);
- // when changed non-blur theme, change to backdrop to none
- if (!_theme.BlurEnabled)
- {
- _settings.BackdropType = 0; // Change to 0 instead of BackdropTypes.None
- }
-
- // dropshadow on and control disabled.(user can't change dropshadow with blur theme)
- if (_theme.BlurEnabled)
- {
- _settings.UseDropShadowEffect = true;
- }
-
_ = _theme.RefreshFrameAsync();
}
}
@@ -51,7 +37,6 @@ namespace Flow.Launcher.Plugin.Sys
{
_context = context;
_theme = Ioc.Default.GetRequiredService();
- _settings = Ioc.Default.GetRequiredService();
}
public List Query(Query query)
From 2b6e1bf1c73d321909937e3cd31dd974654ffcae Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Mon, 17 Mar 2025 09:35:42 +0800
Subject: [PATCH 07/12] Improve code quality
---
Flow.Launcher/App.xaml | 2 +-
Flow.Launcher/App.xaml.cs | 21 +++++++++++----------
Flow.Launcher/MainWindow.xaml.cs | 8 ++++----
3 files changed, 16 insertions(+), 15 deletions(-)
diff --git a/Flow.Launcher/App.xaml b/Flow.Launcher/App.xaml
index 17c0ae0d5..565bbe3c7 100644
--- a/Flow.Launcher/App.xaml
+++ b/Flow.Launcher/App.xaml
@@ -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">
diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs
index 7d094a0af..23c77618f 100644
--- a/Flow.Launcher/App.xaml.cs
+++ b/Flow.Launcher/App.xaml.cs
@@ -101,15 +101,15 @@ namespace Flow.Launcher
{
if (SingleInstance.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().ChangeLanguage(_settings.Language);
PluginManager.LoadPlugins(_settings.PluginSettings);
@@ -148,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().ChangeTheme();
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
@@ -163,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
@@ -185,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);
}
}
}
diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs
index 3bc9be83f..3fd13afdf 100644
--- a/Flow.Launcher/MainWindow.xaml.cs
+++ b/Flow.Launcher/MainWindow.xaml.cs
@@ -77,7 +77,7 @@ namespace Flow.Launcher
#region Window Event
-#pragma warning disable VSTHRD100
+#pragma warning disable VSTHRD100 // Avoid async void methods
private void OnSourceInitialized(object sender, EventArgs e)
{
@@ -346,7 +346,7 @@ namespace Flow.Launcher
}
}
-#pragma warning restore VSTHRD100
+#pragma warning restore VSTHRD100 // Avoid async void methods
#endregion
@@ -361,7 +361,7 @@ namespace Flow.Launcher
#region Window Context Menu Event
-#pragma warning disable VSTHRD100
+#pragma warning disable VSTHRD100 // Avoid async void methods
private async void OnContextMenusForSettingsClick(object sender, RoutedEventArgs e)
{
@@ -373,7 +373,7 @@ namespace Flow.Launcher
App.API.OpenSettingDialog();
}
-#pragma warning restore VSTHRD100
+#pragma warning restore VSTHRD100 // Avoid async void methods
#endregion
From da30e2e5958fecc9ae64edb1e7241f22117525d2 Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Mon, 17 Mar 2025 09:37:34 +0800
Subject: [PATCH 08/12] Improve code quality
---
Flow.Launcher.Core/Resource/Theme.cs | 52 +++++++------
.../ViewModels/SettingsPaneThemeViewModel.cs | 78 +++++++++----------
.../Flow.Launcher.Plugin.Sys/ThemeSelector.cs | 2 +-
3 files changed, 66 insertions(+), 66 deletions(-)
diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs
index 17fb698b4..2027f2f32 100644
--- a/Flow.Launcher.Core/Resource/Theme.cs
+++ b/Flow.Launcher.Core/Resource/Theme.cs
@@ -24,8 +24,6 @@ namespace Flow.Launcher.Core.Resource
{
#region Properties & Fields
- public string CurrentTheme => _settings.Theme;
-
public bool BlurEnabled { get; set; }
private const string ThemeMetadataNamePrefix = "Name:";
@@ -78,6 +76,11 @@ namespace Flow.Launcher.Core.Resource
#region Theme Resources
+ public string GetCurrentTheme()
+ {
+ return _settings.Theme;
+ }
+
private void MakeSureThemeDirectoriesExist()
{
foreach (var dir in _themeDirectories.Where(dir => !Directory.Exists(dir)))
@@ -183,7 +186,7 @@ namespace Flow.Launcher.Core.Resource
private ResourceDictionary GetCurrentResourceDictionary()
{
- return GetResourceDictionary(_settings.Theme);
+ return GetResourceDictionary(GetCurrentTheme());
}
private ThemeData GetThemeDataFromPath(string path)
@@ -253,9 +256,10 @@ namespace Flow.Launcher.Core.Resource
return themes.OrderBy(o => o.Name).ToList();
}
- public bool ChangeTheme(string theme)
+ public bool ChangeTheme(string theme = null)
{
- const string defaultTheme = Constant.DefaultTheme;
+ if (string.IsNullOrEmpty(theme))
+ theme = GetCurrentTheme();
string path = GetThemePath(theme);
try
@@ -270,7 +274,7 @@ namespace Flow.Launcher.Core.Resource
_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)
+ if (_oldTheme != theme || theme == Constant.DefaultTheme)
{
_oldTheme = Path.GetFileNameWithoutExtension(_oldResource.Source.AbsolutePath);
}
@@ -284,20 +288,20 @@ namespace Flow.Launcher.Core.Resource
catch (DirectoryNotFoundException)
{
Log.Error($"|Theme.ChangeTheme|Theme <{theme}> path can't be found");
- if (theme != defaultTheme)
+ if (theme != Constant.DefaultTheme)
{
- _api.ShowMsgBox(string.Format(InternationalizationManager.Instance.GetTranslation("theme_load_failure_path_not_exists"), theme));
- ChangeTheme(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 != defaultTheme)
+ if (theme != Constant.DefaultTheme)
{
- _api.ShowMsgBox(string.Format(InternationalizationManager.Instance.GetTranslation("theme_load_failure_parse_error"), theme));
- ChangeTheme(defaultTheme);
+ _api.ShowMsgBox(string.Format(_api.GetTranslation("theme_load_failure_parse_error"), theme));
+ ChangeTheme(Constant.DefaultTheme);
}
return false;
}
@@ -429,7 +433,7 @@ namespace Flow.Launcher.Core.Resource
{
AutoDropShadow(useDropShadowEffect);
}
- SetBlurForWindow(backdropType);
+ SetBlurForWindow(GetCurrentTheme(), backdropType);
if (!BlurEnabled)
{
@@ -448,7 +452,7 @@ namespace Flow.Launcher.Core.Resource
// Get the actual backdrop type and drop shadow effect settings
var (backdropType, _) = GetActualValue();
- SetBlurForWindow(backdropType);
+ SetBlurForWindow(GetCurrentTheme(), backdropType);
}, DispatcherPriority.Normal);
}
@@ -475,9 +479,9 @@ namespace Flow.Launcher.Core.Resource
return (backdropType, useDropShadowEffect);
}
- private void SetBlurForWindow(BackdropTypes backdropType)
+ private void SetBlurForWindow(string theme, BackdropTypes backdropType)
{
- var dict = GetThemeResourceDictionary(_settings.Theme);
+ var dict = GetThemeResourceDictionary(theme);
if (dict == null)
return;
@@ -507,13 +511,13 @@ namespace Flow.Launcher.Core.Resource
// Apply the blur effect
Win32Helper.DWMSetBackdropForWindow(mainWindow, backdropType);
- ColorizeWindow(backdropType);
+ ColorizeWindow(theme, backdropType);
}
else
{
// Apply default style when Blur is disabled
Win32Helper.DWMSetBackdropForWindow(mainWindow, BackdropTypes.None);
- ColorizeWindow(backdropType);
+ ColorizeWindow(theme, backdropType);
}
UpdateResourceDictionary(dict);
@@ -559,9 +563,9 @@ namespace Flow.Launcher.Core.Resource
// Get Background Color from WindowBorderStyle when there not color for BG.
// for theme has not "LightBG" or "DarkBG" case.
- private Color GetWindowBorderStyleBackground()
+ private Color GetWindowBorderStyleBackground(string theme)
{
- var Resources = GetThemeResourceDictionary(_settings.Theme);
+ var Resources = GetThemeResourceDictionary(theme);
var windowBorderStyle = (Style)Resources["WindowBorderStyle"];
var backgroundSetter = windowBorderStyle.Setters
@@ -634,9 +638,9 @@ namespace Flow.Launcher.Core.Resource
Application.Current.Resources["PreviewWindowBorderStyle"] = previewStyle;
}
- private void ColorizeWindow(BackdropTypes backdropType)
+ private void ColorizeWindow(string theme, BackdropTypes backdropType)
{
- var dict = GetThemeResourceDictionary(_settings.Theme);
+ var dict = GetThemeResourceDictionary(theme);
if (dict == null) return;
var mainWindow = Application.Current.MainWindow;
@@ -687,11 +691,11 @@ namespace Flow.Launcher.Core.Resource
// Retrieve LightBG value (fallback to WindowBorderStyle background color if not found)
try
{
- LightBG = dict.Contains("LightBG") ? (Color)dict["LightBG"] : GetWindowBorderStyleBackground();
+ LightBG = dict.Contains("LightBG") ? (Color)dict["LightBG"] : GetWindowBorderStyleBackground(theme);
}
catch (Exception)
{
- LightBG = GetWindowBorderStyleBackground();
+ LightBG = GetWindowBorderStyleBackground(theme);
}
// Retrieve DarkBG value (fallback to LightBG if not found)
diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs
index f8c0fa642..61d365b64 100644
--- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs
+++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs
@@ -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,18 +22,22 @@ public partial class SettingsPaneThemeViewModel : BaseModel
{
private const string DefaultFont = "Segoe UI";
public Settings Settings { get; }
+ private readonly Theme _theme = Ioc.Default.GetRequiredService();
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 _themes;
+ public List 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);
// Update UI state
OnPropertyChanged(nameof(BackdropType));
@@ -41,7 +45,7 @@ public partial class SettingsPaneThemeViewModel : BaseModel
OnPropertyChanged(nameof(IsDropShadowEnabled));
OnPropertyChanged(nameof(DropShadowEffect));
- _ = ThemeManager.Instance.RefreshFrameAsync();
+ _ = _theme.RefreshFrameAsync();
}
}
@@ -54,14 +58,14 @@ public partial class SettingsPaneThemeViewModel : BaseModel
}
}
- public bool IsDropShadowEnabled => !ThemeManager.Instance.BlurEnabled;
+ public bool IsDropShadowEnabled => !_theme.BlurEnabled;
public bool DropShadowEffect
{
get => Settings.UseDropShadowEffect;
set
{
- if (ThemeManager.Instance.BlurEnabled)
+ if (_theme.BlurEnabled)
{
// Always DropShadowEffect = true with blur theme
Settings.UseDropShadowEffect = true;
@@ -71,11 +75,11 @@ public partial class SettingsPaneThemeViewModel : BaseModel
// User can change shadow with non-blur theme.
if (value)
{
- ThemeManager.Instance.AddDropShadowEffectToCurrentTheme();
+ _theme.AddDropShadowEffectToCurrentTheme();
}
else
{
- ThemeManager.Instance.RemoveDropShadowEffectFromCurrentTheme();
+ _theme.RemoveDropShadowEffectFromCurrentTheme();
}
Settings.UseDropShadowEffect = value;
@@ -113,9 +117,6 @@ public partial class SettingsPaneThemeViewModel : BaseModel
set => Settings.ResultSubItemFontSize = value;
}
- private List _themes;
- public List Themes => _themes ??= ThemeManager.Instance.LoadAvailableThemes();
-
public class ColorSchemeData : DropdownDataGeneric { }
public List ColorSchemes { get; } = DropdownDataGeneric.GetValues("ColorScheme");
@@ -132,7 +133,7 @@ public partial class SettingsPaneThemeViewModel : BaseModel
_ => ThemeManagerForColorSchemeSwitch.Current.ApplicationTheme
};
Settings.ColorScheme = value;
- _ = ThemeManager.Instance.RefreshFrameAsync();
+ _ = _theme.RefreshFrameAsync();
}
}
@@ -209,13 +210,7 @@ public partial class SettingsPaneThemeViewModel : BaseModel
public class AnimationSpeedData : DropdownDataGeneric { }
public List AnimationSpeeds { get; } = DropdownDataGeneric.GetValues("AnimationSpeed");
- public class BackdropTypeData : DropdownDataGeneric
- {
- public void ApplyBackdrop()
- {
- _ = ThemeManager.Instance.SetBlurForWindowAsync();
- }
- }
+ public class BackdropTypeData : DropdownDataGeneric { }
public List BackdropTypesList { get; } =
DropdownDataGeneric.GetValues("BackdropTypes");
@@ -233,8 +228,9 @@ public partial class SettingsPaneThemeViewModel : BaseModel
}
Settings.BackdropType = value;
- var backdropData = BackdropTypesList.FirstOrDefault(b => b.Value == value);
- backdropData?.ApplyBackdrop();
+
+ _ = _theme.SetBlurForWindowAsync();
+
OnPropertyChanged(nameof(IsDropShadowEnabled));
}
}
@@ -284,37 +280,37 @@ public partial class SettingsPaneThemeViewModel : BaseModel
{
var results = new List
{
- 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"
@@ -346,7 +342,7 @@ public partial class SettingsPaneThemeViewModel : BaseModel
set
{
Settings.QueryBoxFont = value.ToString();
- ThemeManager.Instance.ChangeTheme(Settings.Theme);
+ _theme.ChangeTheme();
}
}
@@ -368,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();
}
}
@@ -390,7 +386,7 @@ public partial class SettingsPaneThemeViewModel : BaseModel
set
{
Settings.ResultFont = value.ToString();
- ThemeManager.Instance.ChangeTheme(Settings.Theme);
+ _theme.ChangeTheme();
}
}
@@ -412,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();
}
}
@@ -420,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;
@@ -436,7 +432,7 @@ public partial class SettingsPaneThemeViewModel : BaseModel
set
{
Settings.ResultSubFont = value.ToString();
- ThemeManager.Instance.ChangeTheme(Settings.Theme);
+ _theme.ChangeTheme();
}
}
@@ -457,7 +453,7 @@ 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();
}
}
diff --git a/Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs b/Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs
index b467aefee..4467b94fb 100644
--- a/Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs
+++ b/Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs
@@ -19,7 +19,7 @@ namespace Flow.Launcher.Plugin.Sys
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;
From a2f70f412a79e50f1b1b959de093618ec8e55d14 Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Mon, 17 Mar 2025 09:41:22 +0800
Subject: [PATCH 09/12] Organize usings
---
Flow.Launcher/MainWindow.xaml.cs | 38 ++++++++++++++------------------
1 file changed, 17 insertions(+), 21 deletions(-)
diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs
index 3fd13afdf..253144457 100644
--- a/Flow.Launcher/MainWindow.xaml.cs
+++ b/Flow.Launcher/MainWindow.xaml.cs
@@ -1,33 +1,29 @@
using System;
using System.ComponentModel;
+using System.Linq;
+using System.Media;
using System.Threading.Tasks;
using System.Windows;
-using System.Windows.Input;
-using System.Windows.Media.Animation;
using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Input;
+using System.Windows.Interop;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+using System.Windows.Threading;
+using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Core.Plugin;
using Flow.Launcher.Core.Resource;
-using Flow.Launcher.Infrastructure.UserSettings;
-using Flow.Launcher.ViewModel;
-using Screen = System.Windows.Forms.Screen;
-using DragEventArgs = System.Windows.DragEventArgs;
-using KeyEventArgs = System.Windows.Input.KeyEventArgs;
-using NotifyIcon = System.Windows.Forms.NotifyIcon;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Hotkey;
+using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin.SharedCommands;
-using System.Windows.Threading;
-using System.Windows.Data;
+using Flow.Launcher.ViewModel;
using ModernWpf.Controls;
-using Key = System.Windows.Input.Key;
-using System.Media;
-using DataObject = System.Windows.DataObject;
-using System.Windows.Media;
-using System.Windows.Interop;
-using Window = System.Windows.Window;
-using System.Linq;
-using System.Windows.Shapes;
-using CommunityToolkit.Mvvm.DependencyInjection;
+using MouseButtons = System.Windows.Forms.MouseButtons;
+using NotifyIcon = System.Windows.Forms.NotifyIcon;
+using Screen = System.Windows.Forms.Screen;
namespace Flow.Launcher
{
@@ -520,10 +516,10 @@ namespace Flow.Launcher
{
switch (e.Button)
{
- case System.Windows.Forms.MouseButtons.Left:
+ case MouseButtons.Left:
_viewModel.ToggleFlowLauncher();
break;
- case System.Windows.Forms.MouseButtons.Right:
+ case MouseButtons.Right:
contextMenu.IsOpen = true;
// Get context menu handle and bring it to the foreground
From 1e6bbdd1b7e3f2a47e628ce9b10e64b36fc1ae13 Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Mon, 17 Mar 2025 09:44:02 +0800
Subject: [PATCH 10/12] Organize usings
---
Flow.Launcher/ViewModel/MainViewModel.cs | 23 +++++++++++------------
1 file changed, 11 insertions(+), 12 deletions(-)
diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs
index 7d719a442..f7c683bc6 100644
--- a/Flow.Launcher/ViewModel/MainViewModel.cs
+++ b/Flow.Launcher/ViewModel/MainViewModel.cs
@@ -1,31 +1,30 @@
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.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;
-using System.Windows.Threading;
namespace Flow.Launcher.ViewModel
{
From 698217f25d14a6ced6faaad23cb874d30c4b85f4 Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Mon, 17 Mar 2025 10:07:40 +0800
Subject: [PATCH 11/12] Improve code quality
---
Flow.Launcher/PublicAPIInstance.cs | 31 ++++++------
Flow.Launcher/ViewModel/MainViewModel.cs | 62 ++++++++++++------------
2 files changed, 46 insertions(+), 47 deletions(-)
diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs
index ac22170ae..d9f935833 100644
--- a/Flow.Launcher/PublicAPIInstance.cs
+++ b/Flow.Launcher/PublicAPIInstance.cs
@@ -1,33 +1,32 @@
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;
namespace Flow.Launcher
{
@@ -153,7 +152,7 @@ namespace Flow.Launcher
public void StopLoadingBar() => _mainVM.ProgressBarVisibility = Visibility.Collapsed;
- public string GetTranslation(string key) => InternationalizationManager.Instance.GetTranslation(key);
+ public string GetTranslation(string key) => App.API.GetTranslation(key);
public List GetAllPlugins() => PluginManager.AllPlugins.ToList();
diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs
index f7c683bc6..59ca356a7 100644
--- a/Flow.Launcher/ViewModel/MainViewModel.cs
+++ b/Flow.Launcher/ViewModel/MainViewModel.cs
@@ -14,7 +14,6 @@ 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.Infrastructure;
using Flow.Launcher.Infrastructure.Hotkey;
using Flow.Launcher.Infrastructure.Image;
@@ -47,8 +46,6 @@ namespace Flow.Launcher.ViewModel
private CancellationTokenSource _updateSource;
private CancellationToken _updateToken;
- private readonly Internationalization _translator = InternationalizationManager.Instance;
-
private ChannelWriter _resultsUpdateChannelWriter;
private Task _resultsViewUpdateTask;
@@ -180,9 +177,9 @@ namespace Flow.Launcher.ViewModel
var resultUpdateChannel = Channel.CreateUnbounded();
_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();
var channelReader = resultUpdateChannel.Reader;
@@ -249,8 +246,8 @@ namespace Flow.Launcher.ViewModel
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]
@@ -272,14 +269,14 @@ namespace Flow.Launcher.ViewModel
{
if (SelectedIsFromQueryResults())
{
- QueryResults(isReQuery: true);
+ _ = QueryResultsAsync(isReQuery: true);
}
}
public void ReQuery(bool reselect)
{
BackToQueryResults();
- QueryResults(isReQuery: true, reSelect: reselect);
+ _ = QueryResultsAsync(isReQuery: true, reSelect: reselect);
}
[RelayCommand]
@@ -387,11 +384,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())
{
@@ -455,7 +452,6 @@ namespace Flow.Launcher.ViewModel
SelectedResults.SelectLastResult();
}
-
[RelayCommand]
private void SelectPrevPage()
{
@@ -482,7 +478,6 @@ namespace Flow.Launcher.ViewModel
{
SelectedResults.SelectPrevResult();
}
-
}
[RelayCommand]
@@ -559,7 +554,6 @@ namespace Flow.Launcher.ViewModel
public bool GameModeStatus { get; set; } = false;
private string _queryText;
-
public string QueryText
{
get => _queryText;
@@ -808,8 +802,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
}
}
@@ -897,7 +891,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);
}
@@ -979,7 +973,7 @@ namespace Flow.Launcher.ViewModel
{
if (SelectedIsFromQueryResults())
{
- QueryResults(isReQuery);
+ _ = QueryResultsAsync(isReQuery);
}
else if (ContextMenuSelected())
{
@@ -1042,8 +1036,8 @@ namespace Flow.Launcher.ViewModel
var results = new List();
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),
@@ -1077,7 +1071,7 @@ namespace Flow.Launcher.ViewModel
private readonly IReadOnlyList _emptyResult = new List();
- private async void QueryResults(bool isReQuery = false, bool reSelect = true)
+ private async Task QueryResultsAsync(bool isReQuery = false, bool reSelect = true)
{
_updateSource?.Cancel();
@@ -1155,7 +1149,7 @@ namespace Flow.Launcher.ViewModel
var tasks = plugins.Select(plugin => plugin.Metadata.Disabled switch
{
- false => QueryTask(plugin, reSelect),
+ false => QueryTaskAsync(plugin, reSelect),
true => Task.CompletedTask
}).ToArray();
@@ -1183,7 +1177,7 @@ namespace Flow.Launcher.ViewModel
}
// 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
@@ -1282,13 +1276,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;
}
@@ -1298,14 +1292,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;
}
@@ -1315,10 +1309,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");
@@ -1400,12 +1394,16 @@ namespace Flow.Launcher.ViewModel
});
}
+#pragma warning disable VSTHRD100 // Avoid async void methods
+
public async void Hide()
{
lastHistoryIndex = 1;
if (ExternalPreviewVisible)
+ {
CloseExternalPreview();
+ }
if (!SelectedIsFromQueryResults())
{
@@ -1476,6 +1474,8 @@ namespace Flow.Launcher.ViewModel
VisibilityChanged?.Invoke(this, new VisibilityChangedEventArgs { IsVisible = false });
}
+#pragma warning restore VSTHRD100 // Avoid async void methods
+
///
/// Checks if Flow Launcher should ignore any hotkeys
///
From 4d080a90c651568851a62613d91f51181f9ed1f1 Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Mon, 17 Mar 2025 10:25:02 +0800
Subject: [PATCH 12/12] Fix build issue
---
Flow.Launcher/PublicAPIInstance.cs | 11 +++++++----
Flow.Launcher/ViewModel/MainViewModel.cs | 2 +-
2 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs
index d9f935833..f367ac921 100644
--- a/Flow.Launcher/PublicAPIInstance.cs
+++ b/Flow.Launcher/PublicAPIInstance.cs
@@ -27,19 +27,22 @@ 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());
@@ -152,17 +155,17 @@ namespace Flow.Launcher
public void StopLoadingBar() => _mainVM.ProgressBarVisibility = Visibility.Collapsed;
- public string GetTranslation(string key) => App.API.GetTranslation(key);
+ public string GetTranslation(string key) => _translater.GetTranslation(key);
public List GetAllPlugins() => PluginManager.AllPlugins.ToList();
public MatchResult FuzzySearch(string query, string stringToCompare) =>
StringMatcher.FuzzySearch(query, stringToCompare);
- public Task HttpGetStringAsync(string url, CancellationToken token = default) => Http.GetAsync(url);
+ public Task HttpGetStringAsync(string url, CancellationToken token = default) => Http.GetAsync(url, token);
public Task HttpGetStreamAsync(string url, CancellationToken token = default) =>
- Http.GetStreamAsync(url);
+ Http.GetStreamAsync(url, token);
public Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath, Action reportProgress = null,
CancellationToken token = default) => Http.DownloadAsync(url, filePath, reportProgress, token);
diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs
index 59ca356a7..6cd4f7f96 100644
--- a/Flow.Launcher/ViewModel/MainViewModel.cs
+++ b/Flow.Launcher/ViewModel/MainViewModel.cs
@@ -208,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
}
}