Skip to content

Fix application null exception & Remove unused MainWindowOpacity property #3392

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 3 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
1 change: 0 additions & 1 deletion Flow.Launcher/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
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"
Expand Down
53 changes: 30 additions & 23 deletions Flow.Launcher/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,8 @@ private async void OnLoaded(object sender, RoutedEventArgs _)
App.API.SaveAppAllSettings();

// Show Welcome Window
var WelcomeWindow = new WelcomeWindow();
WelcomeWindow.Show();
var welcomeWindow = new WelcomeWindow();
welcomeWindow.Show();
}

// Hide window if need
Expand Down Expand Up @@ -241,7 +241,7 @@ private async void OnLoaded(object sender, RoutedEventArgs _)
};

// QueryTextBox.Text change detection (modified to only work when character count is 1 or higher)
QueryTextBox.TextChanged += (sender, e) => UpdateClockPanelVisibility();
QueryTextBox.TextChanged += (s, e) => UpdateClockPanelVisibility();

// Detecting ContextMenu.Visibility changes
DependencyPropertyDescriptor
Expand Down Expand Up @@ -351,15 +351,13 @@ private void OnKeyDown(object sender, KeyEventArgs e)
_viewModel.LoadContextMenuCommand.Execute(null);
e.Handled = true;
}

break;
case Key.Left:
if (!_viewModel.QueryResultsSelected() && QueryTextBox.CaretIndex == 0)
{
_viewModel.EscCommand.Execute(null);
e.Handled = true;
}

break;
case Key.Back:
if (specialKeyState.CtrlPressed)
Expand All @@ -378,7 +376,6 @@ private void OnKeyDown(object sender, KeyEventArgs e)
}
}
}

break;
default:
break;
Expand Down Expand Up @@ -864,16 +861,18 @@ private void WindowAnimation()
private void UpdateClockPanelVisibility()
{
if (QueryTextBox == null || ContextMenu == null || History == null || ClockPanel == null)
{
return;
}

// ✅ Initialize animation length & duration
var animationLength = _settings.AnimationSpeed switch
{
AnimationSpeeds.Slow => 560,
AnimationSpeeds.Medium => 360,
AnimationSpeeds.Fast => 160,
_ => _settings.CustomAnimationLength
};

var animationDuration = TimeSpan.FromMilliseconds(animationLength * 2 / 3);

// ✅ Conditions for showing ClockPanel (No query input & ContextMenu, History are closed)
Expand All @@ -890,15 +889,21 @@ private void UpdateClockPanelVisibility()
}

// ✅ 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)
else if (QueryTextBox.Text.Length > 0)
{
_viewModel.ClockPanelVisibility = Visibility.Hidden;
_viewModel.ClockPanelOpacity = 0.0;
return;
}

// ✅ Prevent multiple animations
if (_isClockPanelAnimating)
{
return;
}

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

Expand All @@ -920,32 +925,32 @@ private void UpdateClockPanelVisibility()
}

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

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

var fadeIn = new DoubleAnimation
{
_viewModel.ClockPanelVisibility = Visibility.Visible; // ✅ Set Visibility to Visible first
From = 0.0,
To = 1.0,
Duration = animationDuration,
FillBehavior = FillBehavior.HoldEnd
};

var fadeIn = new DoubleAnimation
{
From = 0.0,
To = 1.0,
Duration = animationDuration,
FillBehavior = FillBehavior.HoldEnd
};

fadeIn.Completed += (s, e) => _isClockPanelAnimating = false;
ClockPanel.BeginAnimation(OpacityProperty, fadeIn);
}, DispatcherPriority.Render);
fadeIn.Completed += (s, e) => _isClockPanelAnimating = false;

ClockPanel.BeginAnimation(OpacityProperty, fadeIn);
}
}

private static double GetOpacityFromStyle(Style style, double defaultOpacity = 1.0)
{
if (style == null)
{
return defaultOpacity;
}

foreach (Setter setter in style.Setters.Cast<Setter>())
{
Expand All @@ -961,7 +966,9 @@ private static double GetOpacityFromStyle(Style style, double defaultOpacity = 1
private static Thickness GetThicknessFromStyle(Style style, Thickness defaultThickness)
{
if (style == null)
{
return defaultThickness;
}

foreach (Setter setter in style.Setters.Cast<Setter>())
{
Expand Down
15 changes: 9 additions & 6 deletions Flow.Launcher/ViewModel/MainViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -753,8 +753,7 @@ private ResultsViewModel SelectedResults

public Visibility ProgressBarVisibility { get; set; }
public Visibility MainWindowVisibility { get; set; }
public double MainWindowOpacity { get; set; } = 1;


// This is to be used for determining the visibility status of the mainwindow instead of MainWindowVisibility
// because it is more accurate and reliable representation than using Visibility as a condition check
public bool MainWindowVisibilityStatus { get; set; } = true;
Expand Down Expand Up @@ -1454,9 +1453,11 @@ public bool ShouldIgnoreHotkeys()

public void Show()
{
// Invoke on UI thread
Application.Current.Dispatcher.Invoke(() =>
{
if (Application.Current.MainWindow is MainWindow mainWindow)
// When application is exitting, the Application.Current will be null
if (Application.Current?.MainWindow is MainWindow mainWindow)
{
// 📌 Remove DWM Cloak (Make the window visible normally)
Win32Helper.DWMSetCloakForWindow(mainWindow, false);
Expand All @@ -1481,10 +1482,10 @@ public void Show()

// Update WPF properties
MainWindowVisibility = Visibility.Visible;
MainWindowOpacity = 1;
MainWindowVisibilityStatus = true;
VisibilityChanged?.Invoke(this, new VisibilityChangedEventArgs { IsVisible = true });

// Switch keyboard layout
if (StartWithEnglishMode)
{
Win32Helper.SwitchToEnglishKeyboardLayout(true);
Expand Down Expand Up @@ -1527,9 +1528,11 @@ public async void Hide()
break;
}

// Invoke on UI thread
Application.Current.Dispatcher.Invoke(() =>
{
if (Application.Current.MainWindow is MainWindow mainWindow)
// When application is exitting, the Application.Current will be null
if (Application.Current?.MainWindow is MainWindow mainWindow)
{
// Set clock and search icon opacity
var opacity = Settings.UseAnimation ? 0.0 : 1.0;
Expand All @@ -1549,6 +1552,7 @@ public async void Hide()
}
}, DispatcherPriority.Render);

// Switch keyboard layout
if (StartWithEnglishMode)
{
Win32Helper.RestorePreviousKeyboardLayout();
Expand All @@ -1558,7 +1562,6 @@ public async void Hide()
await Task.Delay(50);

// Update WPF properties
//MainWindowOpacity = 0;
MainWindowVisibilityStatus = false;
MainWindowVisibility = Visibility.Collapsed;
VisibilityChanged?.Invoke(this, new VisibilityChangedEventArgs { IsVisible = false });
Expand Down
Loading