Skip to content

Fix first-time window flicker & clock panel flicker issue #3391

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Flow.Launcher/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,9 @@
<StackPanel
x:Name="ClockPanel"
IsHitTestVisible="False"
Style="{DynamicResource ClockPanel}">
Opacity="{Binding ClockPanelOpacity}"
Style="{DynamicResource ClockPanel}"
Visibility="{Binding ClockPanelVisibility}">
<TextBlock
x:Name="ClockBox"
Style="{DynamicResource ClockBox}"
Expand Down Expand Up @@ -318,6 +320,7 @@
Name="SearchIcon"
Margin="0"
Data="{DynamicResource SearchIconImg}"
Opacity="{Binding SearchIconOpacity}"
Stretch="Fill"
Style="{DynamicResource SearchIconStyle}"
Visibility="{Binding SearchIconVisibility}" />
Expand Down
53 changes: 11 additions & 42 deletions Flow.Launcher/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -166,22 +166,6 @@ private async void OnLoaded(object sender, RoutedEventArgs _)
{
if (_viewModel.MainWindowVisibilityStatus)
{
// Set clock and search icon opacity
var opacity = _settings.UseAnimation ? 0.0 : 1.0;
ClockPanel.Opacity = opacity;
SearchIcon.Opacity = opacity;

// Set clock and search icon visibility
ClockPanel.Visibility = string.IsNullOrEmpty(_viewModel.QueryText) ? Visibility.Visible : Visibility.Collapsed;
if (_viewModel.PluginIconSource != null)
{
SearchIcon.Opacity = 0.0;
}
else
{
_viewModel.SearchIconVisibility = Visibility.Visible;
}

// Play sound effect before activing the window
if (_settings.UseSound)
{
Expand Down Expand Up @@ -214,21 +198,6 @@ private async void OnLoaded(object sender, RoutedEventArgs _)
// Update activate times
_settings.ActivateTimes++;
}
else
{
// Set clock and search icon opacity
var opacity = _settings.UseAnimation ? 0.0 : 1.0;
ClockPanel.Opacity = opacity;
SearchIcon.Opacity = opacity;

// Set clock and search icon visibility
ClockPanel.Visibility = Visibility.Hidden;
_viewModel.SearchIconVisibility = Visibility.Hidden;

// Force UI update
ClockPanel.UpdateLayout();
SearchIcon.UpdateLayout();
}
});
break;
}
Expand Down Expand Up @@ -329,8 +298,8 @@ private async void OnDeactivated(object sender, EventArgs e)
_settings.WindowLeft = Left;
_settings.WindowTop = Top;

ClockPanel.Opacity = 0.0;
SearchIcon.Opacity = 0.0;
_viewModel.ClockPanelOpacity = 0.0;
_viewModel.SearchIconOpacity = 0.0;

// This condition stops extra hide call when animator is on,
// which causes the toggling to occasional hide instead of show.
Expand Down Expand Up @@ -908,28 +877,28 @@ private void UpdateClockPanelVisibility()
var animationDuration = TimeSpan.FromMilliseconds(animationLength * 2 / 3);

// ✅ Conditions for showing ClockPanel (No query input & ContextMenu, History are closed)
bool shouldShowClock = QueryTextBox.Text.Length == 0 &&
var shouldShowClock = QueryTextBox.Text.Length == 0 &&
ContextMenu.Visibility != Visibility.Visible &&
History.Visibility != Visibility.Visible;

// ✅ 1. When ContextMenu opens, immediately set Visibility.Hidden (force hide without animation)
if (ContextMenu.Visibility == Visibility.Visible)
{
ClockPanel.Visibility = Visibility.Hidden;
ClockPanel.Opacity = 0.0; // Set to 0 in case Opacity animation affects it
_viewModel.ClockPanelVisibility = Visibility.Hidden;
_viewModel.ClockPanelOpacity = 0.0; // Set to 0 in case Opacity animation affects it
return;
}

// ✅ 2. When ContextMenu is closed, keep it Hidden if there's text in the query (remember previous state)
if (ContextMenu.Visibility != Visibility.Visible && QueryTextBox.Text.Length > 0)
{
ClockPanel.Visibility = Visibility.Hidden;
ClockPanel.Opacity = 0.0;
_viewModel.ClockPanelVisibility = Visibility.Hidden;
_viewModel.ClockPanelOpacity = 0.0;
return;
}

// ✅ 3. When hiding ClockPanel (apply fade-out animation)
if ((!shouldShowClock) && ClockPanel.Visibility == Visibility.Visible && !_isClockPanelAnimating)
if ((!shouldShowClock) && _viewModel.ClockPanelVisibility == Visibility.Visible && !_isClockPanelAnimating)
{
_isClockPanelAnimating = true;

Expand All @@ -943,21 +912,21 @@ private void UpdateClockPanelVisibility()

fadeOut.Completed += (s, e) =>
{
ClockPanel.Visibility = Visibility.Hidden; // ✅ Completely hide after animation
_viewModel.ClockPanelVisibility = Visibility.Hidden; // ✅ Completely hide after animation
_isClockPanelAnimating = false;
};

ClockPanel.BeginAnimation(OpacityProperty, fadeOut);
}

// ✅ 4. When showing ClockPanel (apply fade-in animation)
else if (shouldShowClock && ClockPanel.Visibility != Visibility.Visible && !_isClockPanelAnimating)
else if (shouldShowClock && _viewModel.ClockPanelVisibility != Visibility.Visible && !_isClockPanelAnimating)
{
_isClockPanelAnimating = true;

Application.Current.Dispatcher.Invoke(() =>
{
ClockPanel.Visibility = Visibility.Visible; // ✅ Set Visibility to Visible first
_viewModel.ClockPanelVisibility = Visibility.Visible; // ✅ Set Visibility to Visible first

var fadeIn = new DoubleAnimation
{
Expand Down
58 changes: 53 additions & 5 deletions Flow.Launcher/ViewModel/MainViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using CommunityToolkit.Mvvm.DependencyInjection;
using CommunityToolkit.Mvvm.Input;
using Flow.Launcher.Core.Plugin;
Expand Down Expand Up @@ -760,7 +761,10 @@ private ResultsViewModel SelectedResults

public event VisibilityChangedEventHandler VisibilityChanged;

public Visibility ClockPanelVisibility { get; set; }
public Visibility SearchIconVisibility { get; set; }
public double ClockPanelOpacity { get; set; } = 1;
public double SearchIconOpacity { get; set; } = 1;

public double MainWindowWidth
{
Expand Down Expand Up @@ -1450,8 +1454,30 @@ public bool ShouldIgnoreHotkeys()

public void Show()
{
// 📌 Remove DWM Cloak (Make the window visible normally)
Win32Helper.DWMSetCloakForWindow(Application.Current.MainWindow, false);
Application.Current.Dispatcher.Invoke(() =>
{
if (Application.Current.MainWindow is MainWindow mainWindow)
{
// 📌 Remove DWM Cloak (Make the window visible normally)
Win32Helper.DWMSetCloakForWindow(mainWindow, false);

// Set clock and search icon opacity
var opacity = Settings.UseAnimation ? 0.0 : 1.0;
ClockPanelOpacity = opacity;
SearchIconOpacity = opacity;

// Set clock and search icon visibility
ClockPanelVisibility = string.IsNullOrEmpty(QueryText) ? Visibility.Visible : Visibility.Collapsed;
if (PluginIconSource != null)
{
SearchIconOpacity = 0.0;
}
else
{
SearchIconVisibility = Visibility.Visible;
}
}
}, DispatcherPriority.Render);

// Update WPF properties
MainWindowVisibility = Visibility.Visible;
Expand All @@ -1467,9 +1493,6 @@ public void Show()

public async void Hide()
{
// 📌 Apply DWM Cloak (Completely hide the window)
Win32Helper.DWMSetCloakForWindow(Application.Current.MainWindow, true);

lastHistoryIndex = 1;

if (ExternalPreviewVisible)
Expand Down Expand Up @@ -1504,11 +1527,36 @@ public async void Hide()
break;
}

Application.Current.Dispatcher.Invoke(() =>
{
if (Application.Current.MainWindow is MainWindow mainWindow)
{
// Set clock and search icon opacity
var opacity = Settings.UseAnimation ? 0.0 : 1.0;
ClockPanelOpacity = opacity;
SearchIconOpacity = opacity;

// Set clock and search icon visibility
ClockPanelVisibility = Visibility.Hidden;
SearchIconVisibility = Visibility.Hidden;

// Force UI update
mainWindow.ClockPanel.UpdateLayout();
mainWindow.SearchIcon.UpdateLayout();

// 📌 Apply DWM Cloak (Completely hide the window)
Win32Helper.DWMSetCloakForWindow(mainWindow, true);
}
}, DispatcherPriority.Render);

if (StartWithEnglishMode)
{
Win32Helper.RestorePreviousKeyboardLayout();
}

// Delay for a while to make sure clock will not flicker
await Task.Delay(50);

// Update WPF properties
//MainWindowOpacity = 0;
MainWindowVisibilityStatus = false;
Expand Down
Loading