Skip to content
Merged
41 changes: 28 additions & 13 deletions Flow.Launcher.Core/Resource/Theme.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class Theme
{
#region Properties & Fields

public bool BlurEnabled { get; set; }
public bool BlurEnabled { get; private set; }

private const string ThemeMetadataNamePrefix = "Name:";
private const string ThemeMetadataIsDarkPrefix = "IsDark:";
Expand All @@ -42,6 +42,8 @@ public class Theme
private static string DirectoryPath => Path.Combine(Constant.ProgramDirectory, Folder);
private static string UserDirectoryPath => Path.Combine(DataLocation.DataDirectory(), Folder);

private Thickness _themeResizeBorderThickness;

#endregion

#region Constructor
Expand Down Expand Up @@ -463,7 +465,7 @@ public void AddDropShadowEffectToCurrentTheme()

var effectSetter = new Setter
{
Property = Border.EffectProperty,
Property = UIElement.EffectProperty,
Value = new DropShadowEffect
{
Opacity = 0.3,
Expand All @@ -473,12 +475,12 @@ public void AddDropShadowEffectToCurrentTheme()
}
};

if (windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == Border.MarginProperty) is not Setter marginSetter)
if (windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == FrameworkElement.MarginProperty) is not Setter marginSetter)
{
var margin = new Thickness(ShadowExtraMargin, 12, ShadowExtraMargin, ShadowExtraMargin);
marginSetter = new Setter()
{
Property = Border.MarginProperty,
Property = FrameworkElement.MarginProperty,
Value = margin,
};
windowBorderStyle.Setters.Add(marginSetter);
Expand Down Expand Up @@ -508,12 +510,12 @@ public void RemoveDropShadowEffectFromCurrentTheme()
var dict = GetCurrentResourceDictionary();
var windowBorderStyle = dict["WindowBorderStyle"] as Style;

if (windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == Border.EffectProperty) is Setter effectSetter)
if (windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == UIElement.EffectProperty) is Setter effectSetter)
{
windowBorderStyle.Setters.Remove(effectSetter);
}

if (windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == Border.MarginProperty) is Setter marginSetter)
if (windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == FrameworkElement.MarginProperty) is Setter marginSetter)
{
var currentMargin = (Thickness)marginSetter.Value;
var newMargin = new Thickness(
Expand All @@ -529,28 +531,41 @@ public void RemoveDropShadowEffectFromCurrentTheme()
UpdateResourceDictionary(dict);
}

public void SetResizeBoarderThickness(WindowChrome windowChrome, bool resizeWindow)
{
if (resizeWindow)
{
windowChrome.ResizeBorderThickness = _themeResizeBorderThickness;
}
else
{
windowChrome.ResizeBorderThickness = new Thickness(0);
}
}

// because adding drop shadow effect will change the margin of the window,
// we need to update the window chrome thickness to correct set the resize border
private static void SetResizeBoarderThickness(Thickness? effectMargin)
private void SetResizeBoarderThickness(Thickness? effectMargin)
{
var window = Application.Current.MainWindow;
if (WindowChrome.GetWindowChrome(window) is WindowChrome windowChrome)
{
Thickness thickness;
// Save the theme resize border thickness so that we can restore it if we change ResizeWindow setting
if (effectMargin == null)
{
thickness = SystemParameters.WindowResizeBorderThickness;
_themeResizeBorderThickness = SystemParameters.WindowResizeBorderThickness;
}
else
{
thickness = new Thickness(
_themeResizeBorderThickness = new Thickness(
effectMargin.Value.Left + SystemParameters.WindowResizeBorderThickness.Left,
effectMargin.Value.Top + SystemParameters.WindowResizeBorderThickness.Top,
effectMargin.Value.Right + SystemParameters.WindowResizeBorderThickness.Right,
effectMargin.Value.Bottom + SystemParameters.WindowResizeBorderThickness.Bottom);
}

windowChrome.ResizeBorderThickness = thickness;
// Apply the resize border thickness to the window chrome
SetResizeBoarderThickness(windowChrome, _settings.ResizeWindow);
}
}

Expand Down Expand Up @@ -582,7 +597,7 @@ await Application.Current.Dispatcher.InvokeAsync(() =>
{
AutoDropShadow(useDropShadowEffect);
}
}, DispatcherPriority.Normal);
}, DispatcherPriority.Render);
}

/// <summary>
Expand All @@ -596,7 +611,7 @@ await Application.Current.Dispatcher.InvokeAsync(() =>
var (backdropType, _) = GetActualValue();

SetBlurForWindow(GetCurrentTheme(), backdropType);
}, DispatcherPriority.Normal);
}, DispatcherPriority.Render);
}

/// <summary>
Expand Down
12 changes: 0 additions & 12 deletions Flow.Launcher.Core/Resource/ThemeManager.cs

This file was deleted.

32 changes: 32 additions & 0 deletions Flow.Launcher.Infrastructure/UserSettings/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,38 @@ public string Theme
public double? SettingWindowLeft { get; set; } = null;
public System.Windows.WindowState SettingWindowState { get; set; } = WindowState.Normal;

bool _resizeWindow { get; set; } = true;
public bool ResizeWindow
{
get => _resizeWindow;
set
{
_resizeWindow = value;
OnPropertyChanged();
}
}

bool _showPlaceholder { get; set; } = false;
public bool ShowPlaceholder
{
get => _showPlaceholder;
set
{
_showPlaceholder = value;
OnPropertyChanged();
}
}
string _placeholderText { get; set; } = string.Empty;
public string PlaceholderText
{
get => _placeholderText;
set
{
_placeholderText = value;
OnPropertyChanged();
}
}

public int CustomExplorerIndex { get; set; } = 0;

[JsonIgnore]
Expand Down
1 change: 0 additions & 1 deletion Flow.Launcher/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,6 @@ await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () =>
HotKeyMapper.Initialize();

// main windows needs initialized before theme change because of blur settings
// TODO: Clean ThemeManager.Instance in future
Ioc.Default.GetRequiredService<Theme>().ChangeTheme();

Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
Expand Down
18 changes: 12 additions & 6 deletions Flow.Launcher/Languages/en.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
<system:String x:Key="GameMode">Game Mode</system:String>
<system:String x:Key="GameModeToolTip">Suspend the use of Hotkeys.</system:String>
<system:String x:Key="PositionReset">Position Reset</system:String>
<system:String x:Key="PositionResetToolTip">Reset search window position</system:String>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Jack251970 Man~!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh no

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see #3448

<system:String x:Key="queryTextBoxSuggestion">Type here to search</system:String>

<!-- Setting General -->
<system:String x:Key="flowlauncher_settings">Settings</system:String>
Expand Down Expand Up @@ -104,11 +104,6 @@
<system:String x:Key="AlwaysPreview">Always Preview</system:String>
<system:String x:Key="AlwaysPreviewToolTip">Always open preview panel when Flow activates. Press {0} to toggle preview.</system:String>
<system:String x:Key="shadowEffectNotAllowed">Shadow effect is not allowed while current theme has blur effect enabled</system:String>
<system:String x:Key="BackdropType">Backdrop Type</system:String>
<system:String x:Key="BackdropTypesNone">None</system:String>
<system:String x:Key="BackdropTypesAcrylic">Acrylic</system:String>
<system:String x:Key="BackdropTypesMica">Mica</system:String>
<system:String x:Key="BackdropTypesMicaAlt">Mica Alt</system:String>

<!-- Setting Plugin -->
<system:String x:Key="searchplugin">Search Plugin</system:String>
Expand Down Expand Up @@ -200,8 +195,19 @@
<system:String x:Key="AnimationSpeedCustom">Custom</system:String>
<system:String x:Key="Clock">Clock</system:String>
<system:String x:Key="Date">Date</system:String>
<system:String x:Key="BackdropType">Backdrop Type</system:String>
<system:String x:Key="BackdropTypesNone">None</system:String>
<system:String x:Key="BackdropTypesAcrylic">Acrylic</system:String>
<system:String x:Key="BackdropTypesMica">Mica</system:String>
<system:String x:Key="BackdropTypesMicaAlt">Mica Alt</system:String>
<system:String x:Key="TypeIsDarkToolTip">This theme supports two(light/dark) modes.</system:String>
<system:String x:Key="TypeHasBlurToolTip">This theme supports Blur Transparent Background.</system:String>
<system:String x:Key="ShowPlaceholder">Show placeholder</system:String>
<system:String x:Key="ShowPlaceholderTip">Display placeholder when query is empty</system:String>
<system:String x:Key="PlaceholderText">Placeholder text</system:String>
<system:String x:Key="PlaceholderTextTip">Change placeholder text. Input empty will use: Type here to search</system:String>
<system:String x:Key="ResizeWindow">Allow window size change</system:String>
<system:String x:Key="ResizeWindowTip">Allow dragging the search window edges to change its size</system:String>

<!-- Setting Hotkey -->
<system:String x:Key="hotkey">Hotkey</system:String>
Expand Down
8 changes: 8 additions & 0 deletions Flow.Launcher/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,14 @@
<Grid x:Name="QueryBoxArea">
<Border MinHeight="30" Style="{DynamicResource QueryBoxBgStyle}">
<Grid>
<TextBox
x:Name="QueryTextPlaceholderBox"
Height="{Binding MainWindowHeight, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
FontSize="{Binding QueryBoxFontSize, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="False"
Style="{DynamicResource QuerySuggestionBoxStyle}"
Text="{Binding PlaceholderText, Mode=OneWay}"
Visibility="Collapsed" />
<TextBox
x:Name="QueryTextSuggestionBox"
Height="{Binding MainWindowHeight, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Expand Down
70 changes: 69 additions & 1 deletion Flow.Launcher/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Shell;
using System.Windows.Threading;
using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Core.Plugin;
Expand Down Expand Up @@ -115,6 +116,10 @@ private async void OnLoaded(object sender, RoutedEventArgs _)
welcomeWindow.Show();
}

// Initialize place holder
SetupPlaceholderText();
_viewModel.PlaceholderText = _settings.PlaceholderText;

// Hide window if need
UpdatePosition();
if (_settings.HideOnStartup)
Expand Down Expand Up @@ -146,13 +151,17 @@ private async void OnLoaded(object sender, RoutedEventArgs _)
UpdatePosition();

// Refresh frame
await Ioc.Default.GetRequiredService<Theme>().RefreshFrameAsync();
await _theme.RefreshFrameAsync();

// Initialize resize mode after refreshing frame
SetupResizeMode();

// Reset preview
_viewModel.ResetPreview();

// Since the default main window visibility is visible, so we need set focus during startup
QueryTextBox.Focus();

// Set the initial state of the QueryTextBoxCursorMovedToEnd property
// Without this part, when shown for the first time, switching the context menu does not move the cursor to the end.
_viewModel.QueryTextCursorMovedToEnd = false;
Expand Down Expand Up @@ -237,6 +246,15 @@ private async void OnLoaded(object sender, RoutedEventArgs _)
case nameof(Settings.WindowTop):
Top = _settings.WindowTop;
break;
case nameof(Settings.ShowPlaceholder):
SetupPlaceholderText();
break;
case nameof(Settings.PlaceholderText):
_viewModel.PlaceholderText = _settings.PlaceholderText;
break;
case nameof(Settings.ResizeWindow):
SetupResizeMode();
break;
}
};

Expand Down Expand Up @@ -1028,6 +1046,56 @@ private void QueryTextBox_OnPreviewDragOver(object sender, DragEventArgs e)

#endregion

#region Placeholder

private void SetupPlaceholderText()
{
if (_settings.ShowPlaceholder)
{
QueryTextBox.TextChanged += QueryTextBox_TextChanged;
QueryTextSuggestionBox.TextChanged += QueryTextSuggestionBox_TextChanged;
SetPlaceholderText();
}
else
{
QueryTextBox.TextChanged -= QueryTextBox_TextChanged;
QueryTextSuggestionBox.TextChanged -= QueryTextSuggestionBox_TextChanged;
QueryTextPlaceholderBox.Visibility = Visibility.Collapsed;
}
}
Comment on lines +1056 to +1072
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Consider event handler cleanup in Dispose method.

Event handlers are correctly added/removed based on settings, but consider also ensuring these handlers are detached in the Dispose method to prevent potential memory leaks.


🏁 Script executed:

#!/bin/bash
# Check if event handlers are properly cleaned up in Dispose method

echo "Checking MainWindow.xaml.cs for event handler cleanup in Dispose method..."
rg -A 5 -B 5 "Dispose\(bool disposing\)" Flow.Launcher/MainWindow.xaml.cs

Length of output: 708


Event Handler Cleanup in Dispose Method Needed

The setup in SetupPlaceholderText properly adds and removes the event handlers based on settings, but as verified, the Dispose(bool disposing) method does not currently include logic to detach these handlers. This omission could lead to memory leaks if the handlers remain attached even after disposal.

  • Action Item: Update the Dispose(bool disposing) method (in Flow.Launcher/MainWindow.xaml.cs) to remove the attached event handlers (e.g., detach QueryTextBox_TextChanged and QueryTextSuggestionBox_TextChanged) when disposing is true.


private void QueryTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
SetPlaceholderText();
}

private void QueryTextSuggestionBox_TextChanged(object sender, TextChangedEventArgs e)
{
SetPlaceholderText();
}

private void SetPlaceholderText()
{
var queryText = QueryTextBox.Text;
var suggestionText = QueryTextSuggestionBox.Text;
QueryTextPlaceholderBox.Visibility = string.IsNullOrEmpty(queryText) && string.IsNullOrEmpty(suggestionText) ? Visibility.Visible : Visibility.Collapsed;
}

#endregion

#region Resize Mode

private void SetupResizeMode()
{
ResizeMode = _settings.ResizeWindow ? ResizeMode.CanResize : ResizeMode.NoResize;
if (WindowChrome.GetWindowChrome(this) is WindowChrome windowChrome)
{
_theme.SetResizeBoarderThickness(windowChrome, _settings.ResizeWindow);
}
}

#endregion

#region IDisposable

protected virtual void Dispose(bool disposing)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,24 @@ public double SoundEffectVolume
set => Settings.SoundVolume = value;
}

public bool ResizeWindow
{
get => Settings.ResizeWindow;
set => Settings.ResizeWindow = value;
}

public bool ShowPlaceholder
{
get => Settings.ShowPlaceholder;
set => Settings.ShowPlaceholder = value;
}

public string PlaceholderText
{
get => Settings.PlaceholderText;
set => Settings.PlaceholderText = value;
}

public bool UseClock
{
get => Settings.UseClock;
Expand Down
Loading
Loading