Compare commits

...

18 commits

Author SHA1 Message Date
VictoriousRaptor
f8366e59f2
Merge 8b6c694434 into 24f6c90f72 2026-03-10 14:06:39 +08:00
Jack Ye
24f6c90f72
Merge pull request #4333 from Flow-Launcher/dependabot/nuget/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Svg.Skia-3.5.0
Some checks are pending
Build / build (push) Waiting to run
Bump Svg.Skia from 3.4.1 to 3.5.0
2026-03-10 11:52:19 +08:00
dependabot[bot]
3e493263c3
Bump Svg.Skia from 3.4.1 to 3.5.0
---
updated-dependencies:
- dependency-name: Svg.Skia
  dependency-version: 3.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-09 23:05:08 +00:00
Jack Ye
916b4ff091
Merge pull request #4312 from Flow-Launcher/Theme_Enhancement
Some checks are pending
Build / build (push) Waiting to run
Improve code quality
2026-03-09 15:15:41 +08:00
Jack Ye
dad8f3e695
Merge branch 'dev' into Theme_Enhancement 2026-03-09 12:05:48 +08:00
Jeremy Wu
7a46fac41a
Merge pull request #4329 from Flow-Launcher/merge_back_2_1_1
Merge v2.1.1 back to dev
2026-03-09 15:00:27 +11:00
Jeremy
79f3bfab96 merge back v2.1.1 from master to dev 2026-03-09 14:50:29 +11:00
Jeremy Wu
0d9cc7ef83
Release 2.1.1 (#4327)
Some checks failed
Publish Default Plugins / publish (push) Has been cancelled
Build / build (push) Has been cancelled
2026-03-09 11:23:36 +08:00
Jack Ye
0ddcc5c1c6
Merge pull request #4319 from Flow-Launcher/FreezeResources1
Freeze SolidColorBrush to improve performance
2026-03-09 11:11:59 +08:00
Jack251970
7766d7053d Rename GetFreezeSolidColorBrush to GetFrozenSolidColorBrush
Renamed the method GetFreezeSolidColorBrush to GetFrozenSolidColorBrush in ThemeHelper.cs for improved naming consistency. Updated all references in Theme.cs accordingly. No changes to functionality.
2026-03-08 23:49:09 +08:00
Jack251970
36e15d3809 Refactor: move CopyStyle to ThemeHelper class
Moved the CopyStyle method from Theme to a new static ThemeHelper class for better code organization and reusability. Updated all references in Theme to use ThemeHelper.CopyStyle. Added ThemeHelper.cs and necessary using directives.
2026-03-08 23:48:10 +08:00
Jack251970
ed14c45fd2 Refactor brush creation with ThemeHelper utility
Introduce ThemeHelper.GetFreezeSolidColorBrush to centralize and reuse SolidColorBrush creation and freezing logic. Refactor Theme.cs to use this helper, improving code clarity and maintainability. Also, use Brushes.Transparent directly where appropriate.
2026-03-08 23:46:08 +08:00
Jack251970
9abf136ea2 Improve SolidColorBrush usage and property checks in Theme
Refactor background brush creation to assign, freeze, and reuse
SolidColorBrush instances for better WPF performance. Replace
string-based property checks with type-safe Control.BackgroundProperty
comparisons for improved robustness.
2026-03-04 12:48:48 +08:00
Jack251970
6566871b15 Refactor theme blur and dark mode handling logic
Refactored Theme.cs to improve theme blur and dark mode handling:
- Replaced static IsBlurTheme() with IsThemeBlurEnabled(dict) for per-theme blur detection.
- Updated all blur checks to use the new method.
- Improved code style, readability, and comments.
- Moved system dark mode registry check to only run when needed.
- Ensured blur and dark mode are applied based on theme and user/system preferences.
2026-03-02 18:03:24 +08:00
Jack Ye
8b6c694434
Merge branch 'dev' into optimize 2026-02-26 21:01:23 +08:00
Jack Ye
5e87156258
Merge branch 'dev' into optimize 2026-02-10 11:54:22 +08:00
VictoriousRaptor
e9fb8c5330 Fix order of hotkey numbers 2026-01-18 12:09:04 +08:00
VictoriousRaptor
0364fafdf0 Recycle items for result list box 2026-01-03 12:16:30 +08:00
8 changed files with 112 additions and 73 deletions

View file

@ -124,8 +124,8 @@ namespace Flow.Launcher.Core.Resource
try
{
// Load a ResourceDictionary for the specified theme.
var themeName = _settings.Theme;
var dict = GetThemeResourceDictionary(themeName);
var theme = _settings.Theme;
var dict = GetThemeResourceDictionary(theme);
// Apply font settings to the theme resource.
ApplyFontSettings(dict);
@ -292,10 +292,10 @@ namespace Flow.Launcher.Core.Resource
dict["ItemHotkeyStyle"] is Style resultHotkeyItemStyle &&
dict["ItemHotkeySelectedStyle"] is Style resultHotkeyItemSelectedStyle)
{
Setter fontFamily = new Setter(TextBlock.FontFamilyProperty, new FontFamily(_settings.ResultFont));
Setter fontStyle = new Setter(TextBlock.FontStyleProperty, FontHelper.GetFontStyleFromInvariantStringOrNormal(_settings.ResultFontStyle));
Setter fontWeight = new Setter(TextBlock.FontWeightProperty, FontHelper.GetFontWeightFromInvariantStringOrNormal(_settings.ResultFontWeight));
Setter fontStretch = new Setter(TextBlock.FontStretchProperty, FontHelper.GetFontStretchFromInvariantStringOrNormal(_settings.ResultFontStretch));
var fontFamily = new Setter(TextBlock.FontFamilyProperty, new FontFamily(_settings.ResultFont));
var fontStyle = new Setter(TextBlock.FontStyleProperty, FontHelper.GetFontStyleFromInvariantStringOrNormal(_settings.ResultFontStyle));
var fontWeight = new Setter(TextBlock.FontWeightProperty, FontHelper.GetFontWeightFromInvariantStringOrNormal(_settings.ResultFontWeight));
var fontStretch = new Setter(TextBlock.FontStretchProperty, FontHelper.GetFontStretchFromInvariantStringOrNormal(_settings.ResultFontStretch));
Setter[] setters = { fontFamily, fontStyle, fontWeight, fontStretch };
Array.ForEach(
@ -307,10 +307,10 @@ namespace Flow.Launcher.Core.Resource
dict["ItemSubTitleStyle"] is Style resultSubItemStyle &&
dict["ItemSubTitleSelectedStyle"] is Style resultSubItemSelectedStyle)
{
Setter fontFamily = new Setter(TextBlock.FontFamilyProperty, new FontFamily(_settings.ResultSubFont));
Setter fontStyle = new Setter(TextBlock.FontStyleProperty, FontHelper.GetFontStyleFromInvariantStringOrNormal(_settings.ResultSubFontStyle));
Setter fontWeight = new Setter(TextBlock.FontWeightProperty, FontHelper.GetFontWeightFromInvariantStringOrNormal(_settings.ResultSubFontWeight));
Setter fontStretch = new Setter(TextBlock.FontStretchProperty, FontHelper.GetFontStretchFromInvariantStringOrNormal(_settings.ResultSubFontStretch));
var fontFamily = new Setter(TextBlock.FontFamilyProperty, new FontFamily(_settings.ResultSubFont));
var fontStyle = new Setter(TextBlock.FontStyleProperty, FontHelper.GetFontStyleFromInvariantStringOrNormal(_settings.ResultSubFontStyle));
var fontWeight = new Setter(TextBlock.FontWeightProperty, FontHelper.GetFontWeightFromInvariantStringOrNormal(_settings.ResultSubFontWeight));
var fontStretch = new Setter(TextBlock.FontStretchProperty, FontHelper.GetFontStretchFromInvariantStringOrNormal(_settings.ResultSubFontStretch));
Setter[] setters = { fontFamily, fontStyle, fontWeight, fontStretch };
Array.ForEach(
@ -395,7 +395,7 @@ namespace Flow.Launcher.Core.Resource
public List<ThemeData> GetAvailableThemes()
{
List<ThemeData> themes = new List<ThemeData>();
var themes = new List<ThemeData>();
foreach (var themeDirectory in _themeDirectories)
{
var filePaths = Directory
@ -410,8 +410,7 @@ namespace Flow.Launcher.Core.Resource
public bool ChangeTheme(string theme = null)
{
if (string.IsNullOrEmpty(theme))
theme = _settings.Theme;
if (string.IsNullOrEmpty(theme)) theme = _settings.Theme;
string path = GetThemePath(theme);
try
@ -426,13 +425,14 @@ 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
// Always allow re-loading default theme, in case of failure of switching to a new theme from default theme
if (_oldTheme != theme || theme == Constant.DefaultTheme)
{
_oldTheme = Path.GetFileNameWithoutExtension(_oldResource.Source.AbsolutePath);
}
BlurEnabled = IsBlurTheme();
// Check if blur is enabled
BlurEnabled = Win32Helper.IsBackdropSupported() && IsThemeBlurEnabled(resourceDict);
// Apply blur and drop shadow effect so that we do not need to call it again
_ = RefreshFrameAsync();
@ -667,21 +667,21 @@ namespace Flow.Launcher.Core.Resource
if (mainWindow == null) return;
// Check if the theme supports blur
bool hasBlur = dict.Contains("ThemeBlurEnabled") && dict["ThemeBlurEnabled"] is bool b && b;
var hasBlur = IsThemeBlurEnabled(dict);
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)
if (backdropType is BackdropTypes.Mica or BackdropTypes.MicaAlt)
{
windowBorderStyle.Setters.Remove(windowBorderStyle.Setters.OfType<Setter>().FirstOrDefault(x => x.Property.Name == "Background"));
windowBorderStyle.Setters.Add(new Setter(Border.BackgroundProperty, new SolidColorBrush(Color.FromArgb(1, 0, 0, 0))));
windowBorderStyle.Setters.Remove(windowBorderStyle.Setters.OfType<Setter>().FirstOrDefault(x => x.Property == Control.BackgroundProperty));
windowBorderStyle.Setters.Add(new Setter(Border.BackgroundProperty, ThemeHelper.GetFrozenSolidColorBrush(Color.FromArgb(1, 0, 0, 0))));
}
else if (backdropType == BackdropTypes.Acrylic)
{
windowBorderStyle.Setters.Remove(windowBorderStyle.Setters.OfType<Setter>().FirstOrDefault(x => x.Property.Name == "Background"));
windowBorderStyle.Setters.Add(new Setter(Border.BackgroundProperty, new SolidColorBrush(Colors.Transparent)));
windowBorderStyle.Setters.Remove(windowBorderStyle.Setters.OfType<Setter>().FirstOrDefault(x => x.Property == Control.BackgroundProperty));
windowBorderStyle.Setters.Add(new Setter(Border.BackgroundProperty, Brushes.Transparent));
}
// For themes with blur enabled, the window border is rendered by the system, so it's treated as a simple rectangle regardless of thickness.
//(This is to avoid issues when the window is forcibly changed to a rectangular shape during snap scenarios.)
var cornerRadiusSetter = windowBorderStyle.Setters.OfType<Setter>().FirstOrDefault(x => x.Property == Border.CornerRadiusProperty);
@ -689,7 +689,7 @@ namespace Flow.Launcher.Core.Resource
cornerRadiusSetter.Value = new CornerRadius(0);
else
windowBorderStyle.Setters.Add(new Setter(Border.CornerRadiusProperty, new CornerRadius(0)));
// Apply the blur effect
Win32Helper.DWMSetBackdropForWindow(mainWindow, backdropType);
ColorizeWindow(theme, backdropType);
@ -798,12 +798,12 @@ namespace Flow.Launcher.Core.Resource
Application.Current.Resources["WindowBorderStyle"] is Style originalStyle)
{
// Copy the original style, including the base style if it exists
CopyStyle(originalStyle, previewStyle);
ThemeHelper.CopyStyle(originalStyle, previewStyle);
}
// Apply background color (remove transparency in color)
Color backgroundColor = Color.FromRgb(bgColor.Value.R, bgColor.Value.G, bgColor.Value.B);
previewStyle.Setters.Add(new Setter(Border.BackgroundProperty, new SolidColorBrush(backgroundColor)));
var backgroundColor = Color.FromRgb(bgColor.Value.R, bgColor.Value.G, bgColor.Value.B);
previewStyle.Setters.Add(new Setter(Border.BackgroundProperty, ThemeHelper.GetFrozenSolidColorBrush(backgroundColor)));
// The blur theme keeps the corner round fixed (applying DWM code to modify it causes rendering issues).
// The non-blur theme retains the previously set WindowBorderStyle.
@ -817,21 +817,6 @@ namespace Flow.Launcher.Core.Resource
Application.Current.Resources["PreviewWindowBorderStyle"] = previewStyle;
}
private void CopyStyle(Style originalStyle, Style targetStyle)
{
// If the style is based on another style, copy the base style first
if (originalStyle.BasedOn != null)
{
CopyStyle(originalStyle.BasedOn, targetStyle);
}
// Copy the setters from the original style
foreach (var setter in originalStyle.Setters.OfType<Setter>())
{
targetStyle.Setters.Add(new Setter(setter.Property, setter.Value));
}
}
private void ColorizeWindow(string theme, BackdropTypes backdropType)
{
var dict = GetThemeResourceDictionary(theme);
@ -841,20 +826,16 @@ namespace Flow.Launcher.Core.Resource
if (mainWindow == null) return;
// Check if the theme supports blur
bool hasBlur = dict.Contains("ThemeBlurEnabled") && dict["ThemeBlurEnabled"] is bool b && b;
var hasBlur = IsThemeBlurEnabled(dict);
// SystemBG value check (Auto, Light, Dark)
string systemBG = dict.Contains("SystemBG") ? dict["SystemBG"] as string : "Auto"; // 기본값 Auto
var systemBG = dict.Contains("SystemBG") ? dict["SystemBG"] as string : "Auto"; // 기본값 Auto
// Check the user's ColorScheme setting
string colorScheme = _settings.ColorScheme;
// Check system dark mode setting (read AppsUseLightTheme value)
int themeValue = (int)Registry.GetValue(@"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize", "AppsUseLightTheme", 1);
bool isSystemDark = themeValue == 0;
var colorScheme = _settings.ColorScheme;
// Final decision on whether to use dark mode
bool useDarkMode = false;
var useDarkMode = false;
// If systemBG is not "Auto", prioritize it over ColorScheme and set the mode based on systemBG value
if (systemBG == "Dark")
@ -869,11 +850,20 @@ namespace Flow.Launcher.Core.Resource
{
// If systemBG is "Auto", decide based on ColorScheme
if (colorScheme == "Dark")
{
useDarkMode = true;
}
else if (colorScheme == "Light")
{
useDarkMode = false;
}
else
{
// Check system dark mode setting (read AppsUseLightTheme value)
var themeValue = (int)Registry.GetValue(@"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize", "AppsUseLightTheme", 1);
var isSystemDark = themeValue == 0;
useDarkMode = isSystemDark; // Auto (based on system setting)
}
}
// Apply DWM Dark Mode
@ -915,25 +905,20 @@ namespace Flow.Launcher.Core.Resource
else
{
// Only set the background to transparent if the theme supports blur
if (backdropType == BackdropTypes.Mica || backdropType == BackdropTypes.MicaAlt)
if (backdropType is BackdropTypes.Mica or BackdropTypes.MicaAlt)
{
mainWindow.Background = new SolidColorBrush(Color.FromArgb(1, 0, 0, 0));
mainWindow.Background = ThemeHelper.GetFrozenSolidColorBrush(Color.FromArgb(1, 0, 0, 0));
}
else
{
mainWindow.Background = new SolidColorBrush(selectedBG);
mainWindow.Background = ThemeHelper.GetFrozenSolidColorBrush(selectedBG);
}
}
}
private static bool IsBlurTheme()
private static bool IsThemeBlurEnabled(ResourceDictionary dict)
{
if (!Win32Helper.IsBackdropSupported()) // Windows 11 미만이면 무조건 false
return false;
var resource = Application.Current.TryFindResource("ThemeBlurEnabled");
return resource is bool b && b;
return dict.Contains("ThemeBlurEnabled") && dict["ThemeBlurEnabled"] is bool enabled && enabled;
}
#endregion

View file

@ -0,0 +1,30 @@
using System.Linq;
using System.Windows;
using System.Windows.Media;
namespace Flow.Launcher.Core.Resource;
public static class ThemeHelper
{
public static void CopyStyle(Style originalStyle, Style targetStyle)
{
// If the style is based on another style, copy the base style first
if (originalStyle.BasedOn != null)
{
CopyStyle(originalStyle.BasedOn, targetStyle);
}
// Copy the setters from the original style
foreach (var setter in originalStyle.Setters.OfType<Setter>())
{
targetStyle.Setters.Add(new Setter(setter.Property, setter.Value));
}
}
public static SolidColorBrush GetFrozenSolidColorBrush(Color color)
{
var brush = new SolidColorBrush(color);
brush.Freeze();
return brush;
}
}

View file

@ -17,7 +17,17 @@ public class OpenResultHotkeyVisibilityConverter : IValueConverter
if (value is ListBoxItem listBoxItem
&& ItemsControl.ItemsControlFromItemContainer(listBoxItem) is ListBox listBox)
number = listBox.ItemContainerGenerator.IndexFromContainer(listBoxItem) + 1;
{
var dataItem = listBoxItem.DataContext;
if (dataItem != null)
{
var index = listBox.Items.IndexOf(dataItem);
if (index >= 0)
{
number = index + 1;
}
}
}
return number <= MaxVisibleHotkeys ? Visibility.Visible : Visibility.Collapsed;
}

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Globalization;
using System.Windows.Controls;
using System.Windows.Data;
@ -15,9 +15,20 @@ public class OrdinalConverter : IValueConverter
return 0;
}
var res = listBox.ItemContainerGenerator.IndexFromContainer(listBoxItem) + 1;
return res == 10 ? 0 : res; // 10th item => HOTKEY+0
var dataItem = listBoxItem.DataContext;
if (dataItem == null)
{
return 0;
}
var index = listBox.Items.IndexOf(dataItem);
if (index < 0)
{
return 0;
}
var res = index + 1;
return res == 10 ? 0 : res; // 10th item => HOTKEY+0
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new InvalidOperationException();

View file

@ -27,7 +27,7 @@
Style="{DynamicResource BaseListboxStyle}"
VirtualizingPanel.ScrollUnit="Item"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Standard"
VirtualizingStackPanel.VirtualizationMode="Recycling"
Visibility="{Binding Visibility}"
mc:Ignorable="d">
<!-- IsSynchronizedWithCurrentItem: http://stackoverflow.com/a/7833798/2833083 -->
@ -82,7 +82,7 @@
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}+{1}">
<Binding Mode="OneWay" Path="Settings.OpenResultModifiers" />
<Binding Converter="{StaticResource ResourceKey=OrdinalConverter}" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=ListBoxItem}" />
<Binding Converter="{StaticResource ResourceKey=OrdinalConverter}" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=ListBoxItem}"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>

View file

@ -12,7 +12,7 @@ namespace Flow.Launcher
{
protected Lock _lock = new();
private Point _lastpos;
private ListBoxItem curItem = null;
private ResultViewModel _currentResult = null;
public ResultListBox()
{
InitializeComponent();
@ -60,9 +60,12 @@ namespace Flow.Launcher
{
lock (_lock)
{
curItem = (ListBoxItem)sender;
var p = e.GetPosition((IInputElement)sender);
_lastpos = p;
if (sender is FrameworkElement { DataContext: ResultViewModel result })
{
_currentResult = result;
var p = e.GetPosition((IInputElement)sender);
_lastpos = p;
}
}
}
@ -82,9 +85,9 @@ namespace Flow.Launcher
{
lock (_lock)
{
if (curItem != null)
if (_currentResult != null && sender is ListBox listBox)
{
curItem.IsSelected = true;
listBox.SelectedItem = _currentResult;
}
}
}

View file

@ -110,7 +110,7 @@
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="Flow.Launcher.Localization" Version="0.0.6" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="10.0.3" />
<PackageReference Include="Svg.Skia" Version="3.4.1" />
<PackageReference Include="Svg.Skia" Version="3.5.0" />
<PackageReference Include="SkiaSharp" Version="3.119.1" />
</ItemGroup>

View file

@ -1,4 +1,4 @@
version: '2.1.0.{build}'
version: '2.1.1.{build}'
# Do not build on tags because we create a release on merge to master. Otherwise will upload artifacts twice changing the hash, as well as triggering duplicate GitHub release action & NuGet deployments.
skip_tags: true