2025-09-23 08:21:09 +00:00
using System ;
2015-01-08 14:49:42 +00:00
using System.ComponentModel ;
2025-03-17 01:41:22 +00:00
using System.Linq ;
using System.Media ;
2025-09-30 11:41:27 +00:00
using System.Threading ;
2021-06-15 09:27:21 +00:00
using System.Threading.Tasks ;
2014-01-29 10:33:24 +00:00
using System.Windows ;
2025-03-17 01:41:22 +00:00
using System.Windows.Controls ;
using System.Windows.Data ;
2014-01-29 10:33:24 +00:00
using System.Windows.Input ;
2025-03-17 01:41:22 +00:00
using System.Windows.Interop ;
using System.Windows.Media ;
2014-01-29 10:33:24 +00:00
using System.Windows.Media.Animation ;
2025-03-17 01:41:22 +00:00
using System.Windows.Shapes ;
2025-03-29 06:15:24 +00:00
using System.Windows.Shell ;
2025-03-17 01:41:22 +00:00
using System.Windows.Threading ;
using CommunityToolkit.Mvvm.DependencyInjection ;
2020-04-21 09:12:17 +00:00
using Flow.Launcher.Core.Plugin ;
using Flow.Launcher.Core.Resource ;
2021-11-28 21:07:08 +00:00
using Flow.Launcher.Infrastructure ;
2021-12-03 17:23:00 +00:00
using Flow.Launcher.Infrastructure.Hotkey ;
2025-07-20 11:11:09 +00:00
using Flow.Launcher.Infrastructure.DialogJump ;
2025-03-17 01:41:22 +00:00
using Flow.Launcher.Infrastructure.UserSettings ;
2025-07-05 05:35:17 +00:00
using Flow.Launcher.Plugin ;
2022-01-19 20:43:00 +00:00
using Flow.Launcher.Plugin.SharedCommands ;
2025-07-23 05:51:35 +00:00
using Flow.Launcher.Plugin.SharedModels ;
2025-03-17 01:41:22 +00:00
using Flow.Launcher.ViewModel ;
2025-10-05 10:44:40 +00:00
using iNKORE.UI.WPF.Modern ;
using iNKORE.UI.WPF.Modern.Controls ;
2024-02-04 21:02:53 +00:00
using DataObject = System . Windows . DataObject ;
2025-03-20 23:57:27 +00:00
using Key = System . Windows . Input . Key ;
2025-03-17 01:41:22 +00:00
using MouseButtons = System . Windows . Forms . MouseButtons ;
using NotifyIcon = System . Windows . Forms . NotifyIcon ;
2014-01-29 10:33:24 +00:00
2020-04-21 09:12:17 +00:00
namespace Flow.Launcher
2014-07-23 20:46:19 +00:00
{
2025-03-21 12:55:57 +00:00
public partial class MainWindow : IDisposable
2014-07-23 20:46:19 +00:00
{
2025-04-03 15:01:16 +00:00
#region Public Property
// Window Event: Close Event
public bool CanClose { get ; set ; } = false ;
#endregion
2016-02-26 23:43:57 +00:00
#region Private Fields
2014-07-23 20:46:19 +00:00
2025-07-11 14:24:05 +00:00
// Class Name
private static readonly string ClassName = nameof ( MainWindow ) ;
2025-03-17 12:09:02 +00:00
// Dependency Injection
2025-03-16 07:05:15 +00:00
private readonly Settings _settings ;
2025-03-16 15:55:15 +00:00
private readonly Theme _theme ;
2025-03-17 12:09:02 +00:00
// Window Notify Icon
2016-05-09 22:35:20 +00:00
private NotifyIcon _notifyIcon ;
2025-03-17 12:09:02 +00:00
// Window Context Menu
2025-03-22 05:26:12 +00:00
private readonly ContextMenu _contextMenu = new ( ) ;
2025-03-16 07:05:15 +00:00
private readonly MainViewModel _viewModel ;
2025-03-17 12:09:02 +00:00
2025-03-22 02:24:34 +00:00
// Window Event: Key Event
2025-03-22 05:26:12 +00:00
private bool _isArrowKeyPressed = false ;
2021-10-06 15:38:00 +00:00
2025-03-17 12:09:02 +00:00
// Window Sound Effects
2025-09-30 11:41:27 +00:00
private MediaPlayer _animationSoundWMP ;
private SoundPlayer _animationSoundWPF ;
private readonly Lock _soundLock = new ( ) ;
2021-10-06 15:38:00 +00:00
2025-03-17 12:09:02 +00:00
// Window WndProc
2025-03-21 12:55:57 +00:00
private HwndSource _hwndSource ;
2025-03-16 15:10:12 +00:00
private int _initialWidth ;
private int _initialHeight ;
2025-03-17 12:09:02 +00:00
// Window Animation
private const double DefaultRightMargin = 66 ; //* this value from base.xaml
2025-03-21 05:34:52 +00:00
private bool _isClockPanelAnimating = false ;
2025-03-21 12:55:57 +00:00
// IDisposable
2025-03-22 05:26:12 +00:00
private bool _disposed = false ;
2025-03-06 05:14:25 +00:00
2014-07-23 20:46:19 +00:00
#endregion
2016-02-26 23:43:57 +00:00
2025-03-16 15:10:12 +00:00
#region Constructor
2025-03-16 15:55:15 +00:00
public MainWindow ( )
2016-03-28 02:09:57 +00:00
{
2025-03-16 15:55:15 +00:00
_settings = Ioc . Default . GetRequiredService < Settings > ( ) ;
_theme = Ioc . Default . GetRequiredService < Theme > ( ) ;
_viewModel = Ioc . Default . GetRequiredService < MainViewModel > ( ) ;
DataContext = _viewModel ;
2022-12-08 03:13:07 +00:00
2025-05-13 13:54:03 +00:00
Topmost = _settings . ShowAtTopmost ;
2017-02-23 02:00:39 +00:00
InitializeComponent ( ) ;
2025-03-27 05:22:10 +00:00
UpdatePosition ( ) ;
2024-01-17 09:21:14 +00:00
2024-05-19 12:13:37 +00:00
InitSoundEffects ( ) ;
2025-09-30 13:11:03 +00:00
RegisterSoundEffectsEvent ( ) ;
2025-03-16 15:10:12 +00:00
DataObject . AddPastingHandler ( QueryTextBox , QueryTextBox_OnPaste ) ;
2025-07-05 05:35:17 +00:00
_viewModel . ActualApplicationThemeChanged + = ViewModel_ActualApplicationThemeChanged ;
2016-03-28 02:09:57 +00:00
}
2021-06-15 09:27:21 +00:00
2025-03-16 15:10:12 +00:00
#endregion
2024-09-21 18:19:34 +00:00
2025-03-16 15:10:12 +00:00
#region Window Event
2024-09-21 18:19:34 +00:00
2025-03-17 01:35:42 +00:00
#pragma warning disable VSTHRD100 // Avoid async void methods
2024-09-21 18:19:34 +00:00
2025-07-05 05:35:17 +00:00
private void ViewModel_ActualApplicationThemeChanged ( object sender , ActualApplicationThemeChangedEventArgs args )
2025-05-14 04:09:18 +00:00
{
2025-06-02 03:16:22 +00:00
_ = _theme . RefreshFrameAsync ( ) ;
2025-05-14 04:09:18 +00:00
}
2025-03-16 15:10:12 +00:00
private void OnSourceInitialized ( object sender , EventArgs e )
{
var handle = Win32Helper . GetWindowHandle ( this , true ) ;
2025-03-21 12:55:57 +00:00
_hwndSource = HwndSource . FromHwnd ( handle ) ;
_hwndSource . AddHook ( WndProc ) ;
2025-03-16 15:10:12 +00:00
Win32Helper . HideFromAltTab ( this ) ;
Win32Helper . DisableControlBox ( this ) ;
2024-05-14 07:54:15 +00:00
}
2024-05-25 15:55:41 +00:00
2025-07-20 11:11:09 +00:00
private void OnLoaded ( object sender , RoutedEventArgs e )
2024-05-14 07:54:15 +00:00
{
2025-03-16 15:10:12 +00:00
// Check first launch
if ( _settings . FirstLaunch )
2024-05-14 07:54:15 +00:00
{
2025-03-28 04:33:07 +00:00
// Set First Launch to false
2025-03-16 15:10:12 +00:00
_settings . FirstLaunch = false ;
2025-03-28 04:33:07 +00:00
2025-06-07 03:24:35 +00:00
// Update release notes version
_settings . ReleaseNotesVersion = Constant . Version ;
2025-03-28 04:33:07 +00:00
// Set Backdrop Type to Acrylic for Windows 11 when First Launch. Default is None
if ( Win32Helper . IsBackdropSupported ( ) ) _settings . BackdropType = BackdropTypes . Acrylic ;
// Save settings
2025-03-16 15:10:12 +00:00
App . API . SaveAppAllSettings ( ) ;
2025-03-28 04:33:07 +00:00
// Show Welcome Window
2025-03-28 13:46:23 +00:00
var welcomeWindow = new WelcomeWindow ( ) ;
welcomeWindow . Show ( ) ;
2024-05-14 07:54:15 +00:00
}
2025-06-11 06:54:38 +00:00
if ( Constant . Version ! = "1.0.0" & & _settings . ReleaseNotesVersion ! = Constant . Version ) // Skip release notes notification for developer builds (version 1.0.0)
2025-06-07 03:24:35 +00:00
{
// Update release notes version
_settings . ReleaseNotesVersion = Constant . Version ;
2025-06-11 06:54:38 +00:00
// Show release note popup with button
2025-06-09 06:42:48 +00:00
App . API . ShowMsgWithButton (
2025-09-23 08:21:09 +00:00
Localize . appUpdateTitle ( Constant . Version ) ,
Localize . appUpdateButtonContent ( ) ,
2025-06-09 06:42:48 +00:00
( ) = >
{
Application . Current . Dispatcher . Invoke ( ( ) = >
{
var releaseNotesWindow = new ReleaseNotesWindow ( ) ;
releaseNotesWindow . Show ( ) ;
} ) ;
} ) ;
2025-06-07 03:24:35 +00:00
}
2025-06-06 15:08:41 +00:00
2025-03-29 05:10:06 +00:00
// Initialize place holder
SetupPlaceholderText ( ) ;
2025-03-29 07:08:49 +00:00
_viewModel . PlaceholderText = _settings . PlaceholderText ;
2025-03-29 05:10:06 +00:00
2025-03-16 15:10:12 +00:00
// Hide window if need
2025-03-27 05:22:10 +00:00
UpdatePosition ( ) ;
2025-03-16 15:10:12 +00:00
if ( _settings . HideOnStartup )
2024-05-27 07:09:22 +00:00
{
2025-03-16 15:10:12 +00:00
_viewModel . Hide ( ) ;
2025-07-20 11:11:09 +00:00
_viewModel . InitializeVisibilityStatus ( false ) ;
2024-05-14 07:54:15 +00:00
}
2025-03-16 15:10:12 +00:00
else
2021-12-02 08:55:47 +00:00
{
2025-03-16 15:10:12 +00:00
_viewModel . Show ( ) ;
2025-07-20 11:11:09 +00:00
_viewModel . InitializeVisibilityStatus ( true ) ;
2025-04-25 08:32:23 +00:00
// When HideOnStartup is off and UseAnimation is on,
// there was a bug where the clock would not appear at all on the initial launch
2025-04-25 08:34:32 +00:00
// So we need to forcibly trigger animation here to ensure the clock is visible
2025-04-22 02:21:11 +00:00
if ( _settings . UseAnimation )
{
WindowAnimation ( ) ;
}
2021-12-02 09:39:54 +00:00
}
2024-09-21 18:19:34 +00:00
2025-04-24 14:01:38 +00:00
// Initialize context menu & notify icon
InitializeContextMenu ( ) ;
2025-03-16 15:10:12 +00:00
InitializeNotifyIcon ( ) ;
2024-05-25 15:55:41 +00:00
2025-03-16 15:10:12 +00:00
// Initialize color scheme
if ( _settings . ColorScheme = = Constant . Light )
2022-01-26 00:45:08 +00:00
{
2025-10-05 10:44:40 +00:00
ThemeManager . Current . ApplicationTheme = ApplicationTheme . Light ;
2022-01-26 00:45:08 +00:00
}
2025-03-16 15:10:12 +00:00
else if ( _settings . ColorScheme = = Constant . Dark )
2023-12-26 14:46:46 +00:00
{
2025-10-05 10:44:40 +00:00
ThemeManager . Current . ApplicationTheme = ApplicationTheme . Dark ;
2023-12-26 14:46:46 +00:00
}
2024-05-27 07:09:22 +00:00
2025-03-16 15:10:12 +00:00
// Force update position
2025-03-27 05:22:10 +00:00
UpdatePosition ( ) ;
2025-02-14 05:22:27 +00:00
2025-03-29 06:15:24 +00:00
// Initialize resize mode after refreshing frame
SetupResizeMode ( ) ;
2024-09-21 18:19:34 +00:00
2025-03-16 15:10:12 +00:00
// Reset preview
_viewModel . ResetPreview ( ) ;
2025-03-30 04:20:14 +00:00
2025-03-16 09:11:04 +00:00
// Since the default main window visibility is visible, so we need set focus during startup
2017-02-23 02:00:39 +00:00
QueryTextBox . Focus ( ) ;
2025-03-29 06:15:24 +00:00
2025-03-23 08:21:03 +00:00
// 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 ;
2025-04-05 04:52:26 +00:00
2025-07-20 11:11:09 +00:00
// Register Dialog Jump events
InitializeDialogJump ( ) ;
2025-03-21 05:34:52 +00:00
// View model property changed event
2016-06-16 00:49:15 +00:00
_viewModel . PropertyChanged + = ( o , e ) = >
{
2021-06-15 09:27:21 +00:00
switch ( e . PropertyName )
2016-06-16 00:49:15 +00:00
{
2021-11-27 02:38:47 +00:00
case nameof ( MainViewModel . MainWindowVisibilityStatus ) :
2016-06-16 00:49:15 +00:00
{
2025-03-15 13:58:55 +00:00
Dispatcher . Invoke ( ( ) = >
2021-06-15 09:27:21 +00:00
{
2025-03-15 13:58:55 +00:00
if ( _viewModel . MainWindowVisibilityStatus )
2021-12-19 18:22:22 +00:00
{
2025-03-28 04:00:23 +00:00
// Play sound effect before activing the window
2025-07-20 11:11:09 +00:00
if ( _settings . UseSound & & ! _viewModel . IsDialogJumpWindowUnderDialog ( ) )
2025-03-15 13:58:55 +00:00
{
SoundPlay ( ) ;
}
2025-03-16 15:10:12 +00:00
2025-03-28 04:00:23 +00:00
// Update position & Activate
2025-03-27 05:22:10 +00:00
UpdatePosition ( ) ;
2025-03-15 13:58:55 +00:00
Activate ( ) ;
2025-03-28 04:00:23 +00:00
// Reset preview
_viewModel . ResetPreview ( ) ;
// Select last query if need
2025-03-15 13:58:55 +00:00
if ( ! _viewModel . LastQuerySelected )
{
QueryTextBox . SelectAll ( ) ;
_viewModel . LastQuerySelected = true ;
}
2025-03-28 04:00:23 +00:00
// Focus query box
QueryTextBox . Focus ( ) ;
// Play window animation
2025-07-20 11:11:09 +00:00
if ( _settings . UseAnimation & & ! _viewModel . IsDialogJumpWindowUnderDialog ( ) )
2025-03-28 04:00:23 +00:00
{
2025-03-16 15:10:12 +00:00
WindowAnimation ( ) ;
2025-03-28 04:00:23 +00:00
}
2025-03-28 04:33:07 +00:00
// Update activate times
2025-03-28 04:00:23 +00:00
_settings . ActivateTimes + + ;
2021-12-19 18:22:22 +00:00
}
2025-03-17 23:43:34 +00:00
} ) ;
break ;
}
2021-07-18 08:05:28 +00:00
case nameof ( MainViewModel . QueryTextCursorMovedToEnd ) :
if ( _viewModel . QueryTextCursorMovedToEnd )
{
2025-03-16 15:10:12 +00:00
// 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 ) ;
2021-07-18 08:05:28 +00:00
_viewModel . QueryTextCursorMovedToEnd = false ;
}
break ;
2022-12-23 06:14:53 +00:00
case nameof ( MainViewModel . GameModeStatus ) :
2024-09-21 18:19:34 +00:00
_notifyIcon . Icon = _viewModel . GameModeStatus
? Properties . Resources . gamemode
: Properties . Resources . app ;
2022-12-23 06:14:53 +00:00
break ;
2021-01-19 14:42:32 +00:00
}
2016-06-16 00:49:15 +00:00
} ;
2022-12-23 06:14:53 +00:00
2025-03-21 05:34:52 +00:00
// Settings property changed event
2018-12-19 03:46:27 +00:00
_settings . PropertyChanged + = ( o , e ) = >
{
2020-11-29 12:50:28 +00:00
switch ( e . PropertyName )
2018-12-19 03:46:27 +00:00
{
2020-11-29 12:50:28 +00:00
case nameof ( Settings . HideNotifyIcon ) :
_notifyIcon . Visible = ! _settings . HideNotifyIcon ;
break ;
case nameof ( Settings . Language ) :
UpdateNotifyIconText ( ) ;
2025-07-09 03:12:46 +00:00
if ( _settings . ShowHomePage & & _viewModel . QueryResultsSelected ( ) & & string . IsNullOrEmpty ( _viewModel . QueryText ) )
{
_viewModel . QueryResults ( ) ;
}
2020-11-29 12:50:28 +00:00
break ;
2021-12-19 13:55:17 +00:00
case nameof ( Settings . Hotkey ) :
UpdateNotifyIconText ( ) ;
break ;
2024-06-30 09:03:25 +00:00
case nameof ( Settings . WindowLeft ) :
Left = _settings . WindowLeft ;
break ;
case nameof ( Settings . WindowTop ) :
Top = _settings . WindowTop ;
2022-10-19 10:25:00 +00:00
break ;
2025-03-29 05:10:06 +00:00
case nameof ( Settings . ShowPlaceholder ) :
SetupPlaceholderText ( ) ;
break ;
2025-03-29 07:08:49 +00:00
case nameof ( Settings . PlaceholderText ) :
_viewModel . PlaceholderText = _settings . PlaceholderText ;
break ;
2025-03-29 07:27:25 +00:00
case nameof ( Settings . KeepMaxResults ) :
2025-03-29 05:25:00 +00:00
SetupResizeMode ( ) ;
2025-03-30 04:07:04 +00:00
break ;
2025-04-24 14:01:38 +00:00
case nameof ( Settings . SettingWindowFont ) :
InitializeContextMenu ( ) ;
break ;
2025-05-03 14:54:46 +00:00
case nameof ( Settings . ShowHomePage ) :
2025-05-17 10:41:25 +00:00
case nameof ( Settings . ShowHistoryResultsForHomePage ) :
2025-10-14 12:28:39 +00:00
case nameof ( Settings . HistoryStyle ) :
2025-05-04 01:07:14 +00:00
if ( _viewModel . QueryResultsSelected ( ) & & string . IsNullOrEmpty ( _viewModel . QueryText ) )
2025-05-03 14:54:46 +00:00
{
_viewModel . QueryResults ( ) ;
}
break ;
2025-05-13 13:54:03 +00:00
case nameof ( Settings . ShowAtTopmost ) :
Topmost = _settings . ShowAtTopmost ;
break ;
2018-12-19 03:46:27 +00:00
}
} ;
2025-03-19 10:03:02 +00:00
2025-03-22 05:40:33 +00:00
// QueryTextBox.Text change detection (modified to only work when character count is 1 or higher)
2025-03-28 13:42:40 +00:00
QueryTextBox . TextChanged + = ( s , e ) = > UpdateClockPanelVisibility ( ) ;
2025-03-06 11:49:14 +00:00
2025-05-31 09:15:39 +00:00
// Detecting ResultContextMenu.Visibility changes
2025-03-06 11:49:14 +00:00
DependencyPropertyDescriptor
2025-05-31 09:15:39 +00:00
. FromProperty ( VisibilityProperty , typeof ( ResultListBox ) )
. AddValueChanged ( ResultContextMenu , ( s , e ) = > UpdateClockPanelVisibility ( ) ) ;
2025-03-06 11:49:14 +00:00
2025-03-22 05:40:33 +00:00
// Detect History.Visibility changes
2025-03-06 11:49:14 +00:00
DependencyPropertyDescriptor
2025-05-31 09:15:39 +00:00
. FromProperty ( VisibilityProperty , typeof ( ResultListBox ) )
2025-03-06 11:49:14 +00:00
. AddValueChanged ( History , ( s , e ) = > UpdateClockPanelVisibility ( ) ) ;
2025-05-03 09:02:39 +00:00
// Initialize query state
2025-05-03 14:54:46 +00:00
if ( _settings . ShowHomePage & & string . IsNullOrEmpty ( _viewModel . QueryText ) )
{
_viewModel . QueryResults ( ) ;
}
2017-02-23 02:00:39 +00:00
}
2026-03-02 10:20:09 +00:00
private void ProgressBar_Loaded ( object sender , RoutedEventArgs e )
{
InitProgressbarAnimation ( ) ;
}
2025-03-16 15:10:12 +00:00
private async void OnClosing ( object sender , CancelEventArgs e )
2017-02-23 02:00:39 +00:00
{
2025-04-03 15:01:16 +00:00
if ( ! CanClose )
2025-03-22 05:26:12 +00:00
{
2025-04-11 14:05:04 +00:00
CanClose = true ;
2025-03-22 05:26:12 +00:00
_notifyIcon . Visible = false ;
App . API . SaveAppAllSettings ( ) ;
e . Cancel = true ;
await PluginManager . DisposePluginsAsync ( ) ;
Notification . Uninstall ( ) ;
2025-04-11 14:05:04 +00:00
// After plugins are all disposed, we shutdown application to close app
// We use this instead of Close() to avoid InvalidOperationException when calling Close() in OnClosing event
2025-03-28 06:31:46 +00:00
Application . Current . Shutdown ( ) ;
2025-03-22 05:26:12 +00:00
}
2025-03-21 12:55:57 +00:00
}
private void OnClosed ( object sender , EventArgs e )
{
try
{
_hwndSource . RemoveHook ( WndProc ) ;
}
catch ( Exception )
{
// Ignored
}
_hwndSource = null ;
2024-06-05 22:37:13 +00:00
}
2024-09-21 18:19:34 +00:00
2025-03-16 15:10:12 +00:00
private void OnLocationChanged ( object sender , EventArgs e )
2020-11-29 12:50:28 +00:00
{
2025-07-20 11:11:09 +00:00
if ( _viewModel . IsDialogJumpWindowUnderDialog ( ) )
{
return ;
}
2025-04-25 01:03:04 +00:00
if ( IsLoaded )
2024-06-05 21:02:26 +00:00
{
2025-03-16 15:10:12 +00:00
_settings . WindowLeft = Left ;
_settings . WindowTop = Top ;
}
2020-11-29 12:50:28 +00:00
}
2025-03-16 15:10:12 +00:00
private async void OnDeactivated ( object sender , EventArgs e )
2016-05-09 22:35:20 +00:00
{
2025-07-20 11:11:09 +00:00
if ( _viewModel . IsDialogJumpWindowUnderDialog ( ) )
{
return ;
}
2025-03-16 15:10:12 +00:00
_settings . WindowLeft = Left ;
_settings . WindowTop = Top ;
2025-03-27 05:22:10 +00:00
2025-03-28 08:13:02 +00:00
_viewModel . ClockPanelOpacity = 0.0 ;
_viewModel . SearchIconOpacity = 0.0 ;
2025-03-27 05:22:10 +00:00
// This condition stops extra hide call when animator is on,
2025-03-16 15:10:12 +00:00
// which causes the toggling to occasional hide instead of show.
if ( _viewModel . MainWindowVisibilityStatus )
2021-10-27 02:49:00 +00:00
{
2025-03-16 15:10:12 +00:00
// 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 )
2025-03-28 04:00:23 +00:00
{
2025-03-16 15:10:12 +00:00
await Task . Delay ( 100 ) ;
2025-03-28 04:00:23 +00:00
}
2022-11-21 17:49:10 +00:00
2025-03-16 15:10:12 +00:00
if ( _settings . HideWhenDeactivated & & ! _viewModel . ExternalPreviewVisible )
2024-06-04 22:03:26 +00:00
{
2025-03-16 15:10:12 +00:00
_viewModel . Hide ( ) ;
2024-09-21 18:19:34 +00:00
}
2025-03-16 15:10:12 +00:00
}
}
2017-02-20 00:33:32 +00:00
2025-03-16 15:10:12 +00:00
private void OnKeyDown ( object sender , KeyEventArgs e )
{
var specialKeyState = GlobalHotkey . CheckModifiers ( ) ;
switch ( e . Key )
{
case Key . Down :
2025-03-22 05:26:12 +00:00
_isArrowKeyPressed = true ;
2025-03-16 15:10:12 +00:00
_viewModel . SelectNextItemCommand . Execute ( null ) ;
e . Handled = true ;
break ;
case Key . Up :
2025-03-22 05:26:12 +00:00
_isArrowKeyPressed = true ;
2025-03-16 15:10:12 +00:00
_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 :
2025-03-26 02:07:35 +00:00
if ( _viewModel . QueryResultsSelected ( )
2025-03-16 15:10:12 +00:00
& & QueryTextBox . CaretIndex = = QueryTextBox . Text . Length
& & ! string . IsNullOrEmpty ( QueryTextBox . Text ) )
{
_viewModel . LoadContextMenuCommand . Execute ( null ) ;
e . Handled = true ;
}
break ;
case Key . Left :
2025-03-26 02:07:35 +00:00
if ( ! _viewModel . QueryResultsSelected ( ) & & QueryTextBox . CaretIndex = = 0 )
2025-03-16 15:10:12 +00:00
{
_viewModel . EscCommand . Execute ( null ) ;
e . Handled = true ;
}
break ;
case Key . Back :
if ( specialKeyState . CtrlPressed )
{
2025-03-26 02:07:35 +00:00
if ( _viewModel . QueryResultsSelected ( )
2025-03-16 15:10:12 +00:00
& & QueryTextBox . Text . Length > 0
& & QueryTextBox . CaretIndex = = QueryTextBox . Text . Length )
{
var queryWithoutActionKeyword =
2025-11-26 10:15:12 +00:00
QueryBuilder . Build ( QueryTextBox . Text , QueryTextBox . Text . Trim ( ) , PluginManager . GetNonGlobalPlugins ( ) ) ? . Search ;
2025-03-16 15:10:12 +00:00
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 )
{
2025-03-22 05:26:12 +00:00
_isArrowKeyPressed = false ;
2025-03-16 15:10:12 +00:00
}
}
private void OnPreviewMouseMove ( object sender , MouseEventArgs e )
{
2025-03-22 05:26:12 +00:00
if ( _isArrowKeyPressed )
2025-03-16 15:10:12 +00:00
{
e . Handled = true ; // Ignore Mouse Hover when press Arrowkeys
}
}
2025-03-17 01:35:42 +00:00
#pragma warning restore VSTHRD100 // Avoid async void methods
2025-03-16 15:10:12 +00:00
#endregion
#region Window Boarder Event
private void OnMouseDown ( object sender , MouseButtonEventArgs e )
{
2025-06-05 08:12:55 +00:00
// When the window is maximized via Snap,
// dragging attempts will first switch the window from Maximized to Normal state,
// and adjust the drag position accordingly.
2025-06-02 09:33:45 +00:00
if ( e . ChangedButton = = MouseButton . Left )
{
try
{
if ( WindowState = = WindowState . Maximized )
{
2025-06-02 10:16:01 +00:00
// Calculate ratio based on maximized window dimensions
2025-06-05 08:12:55 +00:00
double maxWidth = ActualWidth ;
double maxHeight = ActualHeight ;
2025-06-02 09:33:45 +00:00
var mousePos = e . GetPosition ( this ) ;
double xRatio = mousePos . X / maxWidth ;
double yRatio = mousePos . Y / maxHeight ;
2025-06-02 10:16:01 +00:00
// Current monitor information
2025-07-23 05:28:50 +00:00
var screen = MonitorInfo . GetNearestDisplayMonitor ( new WindowInteropHelper ( this ) . Handle ) ;
2025-06-02 09:33:45 +00:00
var workingArea = screen . WorkingArea ;
var screenLeftTop = Win32Helper . TransformPixelsToDIP ( this , workingArea . X , workingArea . Y ) ;
2025-06-02 10:16:01 +00:00
// Switch to Normal state
2025-06-02 09:33:45 +00:00
WindowState = WindowState . Normal ;
2025-06-05 08:12:55 +00:00
Application . Current ? . Dispatcher . Invoke ( new Action ( ( ) = >
2025-06-02 09:33:45 +00:00
{
double normalWidth = Width ;
double normalHeight = Height ;
2025-06-02 10:16:01 +00:00
// Apply ratio based on the difference between maximized and normal window sizes
2025-06-02 09:33:45 +00:00
Left = screenLeftTop . X + ( maxWidth - normalWidth ) * xRatio ;
Top = screenLeftTop . Y + ( maxHeight - normalHeight ) * yRatio ;
if ( Mouse . LeftButton = = MouseButtonState . Pressed )
{
DragMove ( ) ;
}
} ) , DispatcherPriority . ApplicationIdle ) ;
}
else
{
DragMove ( ) ;
}
}
catch ( InvalidOperationException )
{
2025-06-02 10:16:01 +00:00
// Ignored - can occur if drag operation is already in progress
2025-06-02 09:33:45 +00:00
}
}
2025-03-16 15:10:12 +00:00
}
#endregion
#region Window Context Menu Event
2025-03-17 01:35:42 +00:00
#pragma warning disable VSTHRD100 // Avoid async void methods
2025-03-16 15:10:12 +00:00
private async void OnContextMenusForSettingsClick ( object sender , RoutedEventArgs e )
{
_viewModel . Hide ( ) ;
if ( _settings . UseAnimation )
await Task . Delay ( 100 ) ;
App . API . OpenSettingDialog ( ) ;
}
2025-03-17 01:35:42 +00:00
#pragma warning restore VSTHRD100 // Avoid async void methods
2025-03-16 15:10:12 +00:00
#endregion
#region Window WndProc
2024-09-21 18:19:34 +00:00
2025-06-02 08:44:41 +00:00
private IntPtr WndProc ( IntPtr hwnd , int msg , IntPtr wParam , IntPtr lParam , ref bool handled )
2025-06-05 08:12:55 +00:00
{
switch ( msg )
2025-03-16 15:10:12 +00:00
{
2025-06-05 08:12:55 +00:00
case Win32Helper . WM_ENTERSIZEMOVE :
2025-07-20 11:11:09 +00:00
// Do do handle size move event for dialog jump window
if ( _viewModel . IsDialogJumpWindowUnderDialog ( ) )
{
return IntPtr . Zero ;
}
2025-06-05 08:12:55 +00:00
_initialWidth = ( int ) Width ;
_initialHeight = ( int ) Height ;
handled = true ;
break ;
case Win32Helper . WM_EXITSIZEMOVE :
2025-07-20 11:11:09 +00:00
// Do do handle size move event for Dialog Jump window
if ( _viewModel . IsDialogJumpWindowUnderDialog ( ) )
{
return IntPtr . Zero ;
}
2025-06-05 08:12:55 +00:00
//Prevent updating the number of results when the window height is below the height of a single result item.
//This situation occurs not only when the user manually resizes the window, but also when the window is released from a side snap, as the OS automatically adjusts the window height.
//(Without this check, releasing from a snap can cause the window height to hit the minimum, resulting in only 2 results being shown.)
if ( _initialHeight ! = ( int ) Height & & Height > ( _settings . WindowHeightSize + _settings . ItemHeightSize ) )
2025-03-16 15:10:12 +00:00
{
2025-06-05 08:12:55 +00:00
if ( ! _settings . KeepMaxResults )
2025-03-29 07:38:44 +00:00
{
2025-06-05 08:12:55 +00:00
// Get shadow margin
var shadowMargin = 0 ;
var ( _ , useDropShadowEffect ) = _theme . GetActualValue ( ) ;
if ( useDropShadowEffect )
{
shadowMargin = 32 ;
}
2025-03-16 15:10:12 +00:00
2025-06-05 08:12:55 +00:00
// Calculate max results to show
var itemCount = ( Height - ( _settings . WindowHeightSize + 14 ) - shadowMargin ) / _settings . ItemHeightSize ;
if ( itemCount < 2 )
{
_settings . MaxResultsToShow = 2 ;
}
else
{
_settings . MaxResultsToShow = Convert . ToInt32 ( Math . Truncate ( itemCount ) ) ;
}
2025-03-16 15:10:12 +00:00
}
2025-06-05 08:12:55 +00:00
SizeToContent = SizeToContent . Height ;
}
else
2025-03-29 07:38:44 +00:00
{
2025-06-05 08:12:55 +00:00
// Update height when exiting maximized snap state.
SizeToContent = SizeToContent . Height ;
2025-03-29 07:38:44 +00:00
}
2025-06-05 08:12:55 +00:00
if ( _initialWidth ! = ( int ) Width )
{
if ( ! _settings . KeepMaxResults )
{
// Update width
_viewModel . MainWindowWidth = Width ;
}
2025-03-16 15:10:12 +00:00
2025-06-05 08:12:55 +00:00
SizeToContent = SizeToContent . Height ;
}
handled = true ;
break ;
case Win32Helper . WM_NCLBUTTONDBLCLK : // Block the double click in frame
2025-06-02 05:55:46 +00:00
SizeToContent = SizeToContent . Height ;
handled = true ;
2025-06-05 08:12:55 +00:00
break ;
case Win32Helper . WM_SYSCOMMAND : // Block Maximize/Minimize by Win+Up and Win+Down Arrow
var command = wParam . ToInt32 ( ) & 0xFFF0 ;
if ( command = = Win32Helper . SC_MAXIMIZE | | command = = Win32Helper . SC_MINIMIZE )
{
SizeToContent = SizeToContent . Height ;
handled = true ;
}
break ;
2025-06-02 05:55:46 +00:00
}
2025-06-05 08:12:55 +00:00
2025-03-16 15:10:12 +00:00
return IntPtr . Zero ;
2024-06-05 22:37:13 +00:00
}
2024-06-19 18:38:34 +00:00
2025-03-16 15:10:12 +00:00
#endregion
#region Window Sound Effects
private void InitSoundEffects ( )
2020-11-29 12:50:28 +00:00
{
2025-09-30 11:41:27 +00:00
lock ( _soundLock )
2025-03-16 15:10:12 +00:00
{
2025-09-30 11:41:27 +00:00
if ( _settings . WMPInstalled )
{
_animationSoundWMP ? . Close ( ) ;
_animationSoundWMP = new MediaPlayer ( ) ;
_animationSoundWMP . Open ( new Uri ( AppContext . BaseDirectory + "Resources\\open.wav" ) ) ;
}
else
{
_animationSoundWPF ? . Dispose ( ) ;
_animationSoundWPF = new SoundPlayer ( AppContext . BaseDirectory + "Resources\\open.wav" ) ;
_animationSoundWPF . Load ( ) ;
}
2025-03-16 15:10:12 +00:00
}
2020-11-29 12:50:28 +00:00
}
2025-03-16 15:10:12 +00:00
private void SoundPlay ( )
2020-11-29 12:50:28 +00:00
{
2025-09-30 11:41:27 +00:00
lock ( _soundLock )
2025-03-16 15:10:12 +00:00
{
2025-09-30 11:41:27 +00:00
if ( _settings . WMPInstalled )
{
_animationSoundWMP . Position = TimeSpan . Zero ;
_animationSoundWMP . Volume = _settings . SoundVolume / 100.0 ;
_animationSoundWMP . Play ( ) ;
}
else
{
_animationSoundWPF . Play ( ) ;
}
2024-06-04 22:03:26 +00:00
}
2020-11-29 12:50:28 +00:00
}
2025-09-30 13:11:03 +00:00
private void RegisterSoundEffectsEvent ( )
2020-11-29 12:50:28 +00:00
{
2025-09-30 13:11:03 +00:00
// Fix for sound not playing after sleep / hibernate for both modern standby and legacy standby
// https://stackoverflow.com/questions/64805186/mediaplayer-doesnt-play-after-computer-sleeps
try
2025-03-16 15:10:12 +00:00
{
2025-09-30 13:11:03 +00:00
Win32Helper . RegisterSleepModeListener ( ( ) = >
{
if ( Application . Current = = null )
{
return ;
}
// We must run InitSoundEffects on UI thread because MediaPlayer is a DispatcherObject
if ( ! Application . Current . Dispatcher . CheckAccess ( ) )
{
Application . Current . Dispatcher . Invoke ( InitSoundEffects ) ;
return ;
}
InitSoundEffects ( ) ;
} ) ;
2025-03-16 15:10:12 +00:00
}
2025-09-30 13:11:03 +00:00
catch ( Exception e )
{
App . API . LogException ( ClassName , "Failed to register sound effect event" , e ) ;
}
}
private static void UnregisterSoundEffectsEvent ( )
{
try
{
Win32Helper . UnregisterSleepModeListener ( ) ;
}
catch ( Exception e )
2025-03-16 15:10:12 +00:00
{
2025-09-30 13:11:03 +00:00
App . API . LogException ( ClassName , "Failed to unregister sound effect event" , e ) ;
2025-03-16 15:10:12 +00:00
}
2020-11-29 12:50:28 +00:00
}
2025-03-16 15:10:12 +00:00
#endregion
#region Window Notify Icon
2016-05-09 22:35:20 +00:00
private void InitializeNotifyIcon ( )
{
2017-02-20 00:33:32 +00:00
_notifyIcon = new NotifyIcon
{
2025-03-16 07:05:15 +00:00
Text = Constant . FlowLauncherFullName ,
2024-05-30 16:48:29 +00:00
Icon = Constant . Version = = "1.0.0" ? Properties . Resources . dev : Properties . Resources . app ,
2018-12-19 03:46:27 +00:00
Visible = ! _settings . HideNotifyIcon
2017-02-20 00:33:32 +00:00
} ;
2025-04-24 14:01:38 +00:00
_notifyIcon . MouseClick + = ( o , e ) = >
{
switch ( e . Button )
{
case MouseButtons . Left :
_viewModel . ToggleFlowLauncher ( ) ;
break ;
case 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 ;
2025-09-23 10:07:15 +00:00
( ( MenuItem ) menu . Items [ 0 ] ) . Header = Localize . iconTrayOpen ( ) +
2025-04-24 14:01:38 +00:00
" (" + _settings . Hotkey + ")" ;
2025-09-23 08:21:09 +00:00
( ( MenuItem ) menu . Items [ 1 ] ) . Header = Localize . GameMode ( ) ;
( ( MenuItem ) menu . Items [ 2 ] ) . Header = Localize . PositionReset ( ) ;
( ( MenuItem ) menu . Items [ 3 ] ) . Header = Localize . iconTraySettings ( ) ;
( ( MenuItem ) menu . Items [ 4 ] ) . Header = Localize . iconTrayExit ( ) ;
2025-04-24 14:01:38 +00:00
}
private void InitializeContextMenu ( )
{
var menu = _contextMenu ;
menu . Items . Clear ( ) ;
2024-09-21 18:19:34 +00:00
var openIcon = new FontIcon { Glyph = "\ue71e" } ;
2021-10-27 02:49:00 +00:00
var open = new MenuItem
{
2025-09-23 10:07:15 +00:00
Header = Localize . iconTrayOpen ( ) + " (" + _settings . Hotkey + ")" ,
2024-04-23 19:48:31 +00:00
Icon = openIcon
2022-12-08 03:13:07 +00:00
} ;
2024-09-21 18:19:34 +00:00
var gamemodeIcon = new FontIcon { Glyph = "\ue7fc" } ;
2021-11-19 06:43:47 +00:00
var gamemode = new MenuItem
{
2025-09-23 08:21:09 +00:00
Header = Localize . GameMode ( ) ,
2025-03-14 11:32:48 +00:00
Icon = gamemodeIcon
2021-11-19 06:43:47 +00:00
} ;
2024-09-21 18:19:34 +00:00
var positionresetIcon = new FontIcon { Glyph = "\ue73f" } ;
2022-09-07 02:31:00 +00:00
var positionreset = new MenuItem
{
2025-09-23 08:21:09 +00:00
Header = Localize . PositionReset ( ) ,
2024-04-23 19:48:31 +00:00
Icon = positionresetIcon
2022-12-08 03:13:07 +00:00
} ;
2024-09-21 18:19:34 +00:00
var settingsIcon = new FontIcon { Glyph = "\ue713" } ;
2021-10-27 02:49:00 +00:00
var settings = new MenuItem
{
2025-09-23 08:21:09 +00:00
Header = Localize . iconTraySettings ( ) ,
2024-04-23 19:48:31 +00:00
Icon = settingsIcon
2022-12-08 03:13:07 +00:00
} ;
2024-09-21 18:19:34 +00:00
var exitIcon = new FontIcon { Glyph = "\ue7e8" } ;
2021-10-27 02:49:00 +00:00
var exit = new MenuItem
{
2025-09-23 08:21:09 +00:00
Header = Localize . iconTrayExit ( ) ,
2025-03-14 11:32:48 +00:00
Icon = exitIcon
2021-10-27 02:49:00 +00:00
} ;
2017-02-20 00:33:32 +00:00
2021-11-11 21:20:43 +00:00
open . Click + = ( o , e ) = > _viewModel . ToggleFlowLauncher ( ) ;
2022-12-23 06:14:53 +00:00
gamemode . Click + = ( o , e ) = > _viewModel . ToggleGameMode ( ) ;
2025-03-16 14:24:06 +00:00
positionreset . Click + = ( o , e ) = > _ = PositionResetAsync ( ) ;
2021-10-20 03:33:23 +00:00
settings . Click + = ( o , e ) = > App . API . OpenSettingDialog ( ) ;
2016-05-09 22:35:20 +00:00
exit . Click + = ( o , e ) = > Close ( ) ;
2022-11-21 17:49:10 +00:00
2025-09-23 08:21:09 +00:00
gamemode . ToolTip = Localize . GameModeToolTip ( ) ;
positionreset . ToolTip = Localize . PositionResetToolTip ( ) ;
2022-11-21 17:49:10 +00:00
2025-03-22 05:26:12 +00:00
_contextMenu . Items . Add ( open ) ;
_contextMenu . Items . Add ( gamemode ) ;
_contextMenu . Items . Add ( positionreset ) ;
_contextMenu . Items . Add ( settings ) ;
_contextMenu . Items . Add ( exit ) ;
2021-12-02 09:39:44 +00:00
}
2022-12-23 03:46:12 +00:00
2025-03-16 15:10:12 +00:00
#endregion
#region Window Position
2025-07-20 11:11:09 +00:00
public void UpdatePosition ( )
2021-11-30 06:57:44 +00:00
{
2025-10-15 04:24:36 +00:00
// Initialize call twice to workaround multi-display alignment issue- https://github.com/Flow-Launcher/Flow.Launcher/issues/2910
2025-07-20 11:11:09 +00:00
if ( _viewModel . IsDialogJumpWindowUnderDialog ( ) )
{
InitializeDialogJumpPosition ( ) ;
InitializeDialogJumpPosition ( ) ;
}
else
{
InitializePosition ( ) ;
InitializePosition ( ) ;
}
2021-11-30 06:57:44 +00:00
}
2022-12-23 03:46:12 +00:00
2025-03-16 14:24:06 +00:00
private async Task PositionResetAsync ( )
2022-09-07 02:31:00 +00:00
{
2022-12-08 03:13:07 +00:00
_viewModel . Show ( ) ;
await Task . Delay ( 300 ) ; // If don't give a time, Positioning will be weird.
2023-03-16 03:13:42 +00:00
var screen = SelectedScreen ( ) ;
Left = HorizonCenter ( screen ) ;
Top = VerticalCenter ( screen ) ;
2022-09-07 02:31:00 +00:00
}
2022-12-23 03:46:12 +00:00
2025-03-16 15:10:12 +00:00
private void InitializePosition ( )
{
2025-10-15 04:24:36 +00:00
// Initialize call twice to workaround multi-display alignment issue- https://github.com/Flow-Launcher/Flow.Launcher/issues/2910
2025-03-16 15:10:12 +00:00
InitializePositionInner ( ) ;
InitializePositionInner ( ) ;
return ;
void InitializePositionInner ( )
{
if ( _settings . SearchWindowScreen = = SearchWindowScreens . RememberLastLaunchLocation )
{
2025-05-12 14:03:00 +00:00
var previousScreenWidth = _settings . PreviousScreenWidth ;
var previousScreenHeight = _settings . PreviousScreenHeight ;
GetDpi ( out var previousDpiX , out var previousDpiY ) ;
_settings . PreviousScreenWidth = SystemParameters . VirtualScreenWidth ;
_settings . PreviousScreenHeight = SystemParameters . VirtualScreenHeight ;
GetDpi ( out var currentDpiX , out var currentDpiY ) ;
if ( previousScreenWidth ! = 0 & & previousScreenHeight ! = 0 & &
previousDpiX ! = 0 & & previousDpiY ! = 0 & &
( previousScreenWidth ! = SystemParameters . VirtualScreenWidth | |
previousScreenHeight ! = SystemParameters . VirtualScreenHeight | |
previousDpiX ! = currentDpiX | | previousDpiY ! = currentDpiY ) )
{
AdjustPositionForResolutionChange ( ) ;
return ;
}
2025-03-16 15:10:12 +00:00
Left = _settings . WindowLeft ;
2025-05-12 14:03:00 +00:00
Top = _settings . WindowTop ;
2025-03-16 15:10:12 +00:00
}
else
{
var screen = SelectedScreen ( ) ;
switch ( _settings . SearchWindowAlign )
{
case SearchWindowAligns . Center :
Left = HorizonCenter ( screen ) ;
Top = VerticalCenter ( screen ) ;
break ;
case SearchWindowAligns . CenterTop :
Left = HorizonCenter ( screen ) ;
2025-05-12 14:03:00 +00:00
Top = VerticalTop ( screen ) ;
2025-03-16 15:10:12 +00:00
break ;
case SearchWindowAligns . LeftTop :
Left = HorizonLeft ( screen ) ;
2025-05-12 14:03:00 +00:00
Top = VerticalTop ( screen ) ;
2025-03-16 15:10:12 +00:00
break ;
case SearchWindowAligns . RightTop :
Left = HorizonRight ( screen ) ;
2025-05-12 14:03:00 +00:00
Top = VerticalTop ( screen ) ;
2025-03-16 15:10:12 +00:00
break ;
case SearchWindowAligns . Custom :
2025-05-12 14:03:00 +00:00
var customLeft = Win32Helper . TransformPixelsToDIP ( this ,
screen . WorkingArea . X + _settings . CustomWindowLeft , 0 ) ;
var customTop = Win32Helper . TransformPixelsToDIP ( this , 0 ,
screen . WorkingArea . Y + _settings . CustomWindowTop ) ;
Left = customLeft . X ;
Top = customTop . Y ;
2025-03-16 15:10:12 +00:00
break ;
}
}
}
}
2025-05-12 14:03:00 +00:00
private void AdjustPositionForResolutionChange ( )
{
var screenWidth = SystemParameters . VirtualScreenWidth ;
var screenHeight = SystemParameters . VirtualScreenHeight ;
GetDpi ( out var currentDpiX , out var currentDpiY ) ;
var previousLeft = _settings . WindowLeft ;
var previousTop = _settings . WindowTop ;
GetDpi ( out var previousDpiX , out var previousDpiY ) ;
var widthRatio = screenWidth / _settings . PreviousScreenWidth ;
var heightRatio = screenHeight / _settings . PreviousScreenHeight ;
var dpiXRatio = currentDpiX / previousDpiX ;
var dpiYRatio = currentDpiY / previousDpiY ;
var newLeft = previousLeft * widthRatio * dpiXRatio ;
var newTop = previousTop * heightRatio * dpiYRatio ;
var screenLeft = SystemParameters . VirtualScreenLeft ;
var screenTop = SystemParameters . VirtualScreenTop ;
var maxX = screenLeft + screenWidth - ActualWidth ;
var maxY = screenTop + screenHeight - ActualHeight ;
Left = Math . Max ( screenLeft , Math . Min ( newLeft , maxX ) ) ;
Top = Math . Max ( screenTop , Math . Min ( newTop , maxY ) ) ;
}
private void GetDpi ( out double dpiX , out double dpiY )
{
var source = PresentationSource . FromVisual ( this ) ;
if ( source ! = null & & source . CompositionTarget ! = null )
{
var matrix = source . CompositionTarget . TransformToDevice ;
dpiX = 96 * matrix . M11 ;
dpiY = 96 * matrix . M22 ;
}
else
{
dpiX = 96 ;
dpiY = 96 ;
}
}
2025-07-23 05:28:50 +00:00
private MonitorInfo SelectedScreen ( )
2025-03-16 15:10:12 +00:00
{
2025-07-23 05:28:50 +00:00
MonitorInfo screen ;
2025-03-16 15:10:12 +00:00
switch ( _settings . SearchWindowScreen )
{
case SearchWindowScreens . Cursor :
2025-07-23 05:28:50 +00:00
screen = MonitorInfo . GetCursorDisplayMonitor ( ) ;
2025-03-16 15:10:12 +00:00
break ;
case SearchWindowScreens . Focus :
2025-07-23 05:28:50 +00:00
screen = MonitorInfo . GetNearestDisplayMonitor ( Win32Helper . GetForegroundWindow ( ) ) ;
break ;
case SearchWindowScreens . Primary :
screen = MonitorInfo . GetPrimaryDisplayMonitor ( ) ;
2025-03-16 15:10:12 +00:00
break ;
case SearchWindowScreens . Custom :
2025-07-23 05:28:50 +00:00
var allScreens = MonitorInfo . GetDisplayMonitors ( ) ;
if ( _settings . CustomScreenNumber < = allScreens . Count )
screen = allScreens [ _settings . CustomScreenNumber - 1 ] ;
2025-03-16 15:10:12 +00:00
else
2025-07-23 05:28:50 +00:00
screen = allScreens [ 0 ] ;
2025-03-16 15:10:12 +00:00
break ;
default :
2025-07-23 05:28:50 +00:00
screen = MonitorInfo . GetDisplayMonitors ( ) [ 0 ] ;
2025-03-16 15:10:12 +00:00
break ;
}
2025-07-23 05:28:50 +00:00
return screen ? ? MonitorInfo . GetDisplayMonitors ( ) [ 0 ] ;
2025-03-16 15:10:12 +00:00
}
2025-07-23 05:28:50 +00:00
private double HorizonCenter ( MonitorInfo screen )
2025-03-16 15:10:12 +00:00
{
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 ;
}
2025-07-23 05:28:50 +00:00
private double VerticalCenter ( MonitorInfo screen )
2025-03-16 15:10:12 +00:00
{
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 ;
}
2025-07-23 05:28:50 +00:00
private double HorizonRight ( MonitorInfo screen )
2025-03-16 15:10:12 +00:00
{
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 ;
}
2025-07-23 05:28:50 +00:00
private double HorizonLeft ( MonitorInfo screen )
2025-03-16 15:10:12 +00:00
{
var dip1 = Win32Helper . TransformPixelsToDIP ( this , screen . WorkingArea . X , 0 ) ;
var left = dip1 . X + 10 ;
return left ;
}
2025-07-23 05:28:50 +00:00
private double VerticalTop ( MonitorInfo screen )
2025-05-12 14:03:00 +00:00
{
var dip1 = Win32Helper . TransformPixelsToDIP ( this , 0 , screen . WorkingArea . Y ) ;
var top = dip1 . Y + 10 ;
return top ;
}
2025-03-16 15:10:12 +00:00
#endregion
#region Window Animation
2014-07-23 20:46:19 +00:00
private void InitProgressbarAnimation ( )
{
2022-10-05 07:20:41 +00:00
var progressBarStoryBoard = new Storyboard ( ) ;
2024-09-21 18:19:34 +00:00
var da = new DoubleAnimation ( ProgressBar . X2 , ActualWidth + 100 ,
new Duration ( new TimeSpan ( 0 , 0 , 0 , 0 , 1600 ) ) ) ;
var da1 = new DoubleAnimation ( ProgressBar . X1 , ActualWidth + 0 ,
new Duration ( new TimeSpan ( 0 , 0 , 0 , 0 , 1600 ) ) ) ;
2022-12-17 09:37:24 +00:00
Storyboard . SetTargetProperty ( da , new PropertyPath ( "(Line.X2)" ) ) ;
Storyboard . SetTargetProperty ( da1 , new PropertyPath ( "(Line.X1)" ) ) ;
2022-10-05 07:20:41 +00:00
progressBarStoryBoard . Children . Add ( da ) ;
progressBarStoryBoard . Children . Add ( da1 ) ;
progressBarStoryBoard . RepeatBehavior = RepeatBehavior . Forever ;
da . Freeze ( ) ;
da1 . Freeze ( ) ;
2022-11-24 20:01:32 +00:00
const string progressBarAnimationName = "ProgressBarAnimation" ;
var beginStoryboard = new BeginStoryboard
{
Name = progressBarAnimationName , Storyboard = progressBarStoryBoard
} ;
2025-05-11 12:56:21 +00:00
2022-11-24 20:01:32 +00:00
var stopStoryboard = new StopStoryboard ( )
{
BeginStoryboardName = progressBarAnimationName
} ;
2022-10-05 07:20:41 +00:00
2022-11-24 20:01:32 +00:00
var trigger = new Trigger
{
Property = VisibilityProperty , Value = Visibility . Visible
} ;
2022-10-05 07:20:41 +00:00
trigger . EnterActions . Add ( beginStoryboard ) ;
2022-11-24 20:01:32 +00:00
trigger . ExitActions . Add ( stopStoryboard ) ;
2025-03-14 11:40:03 +00:00
var progressStyle = new Style ( typeof ( Line ) )
2022-11-24 20:01:32 +00:00
{
BasedOn = FindResource ( "PendingLineStyle" ) as Style
} ;
progressStyle . RegisterName ( progressBarAnimationName , beginStoryboard ) ;
2022-10-05 07:20:41 +00:00
progressStyle . Triggers . Add ( trigger ) ;
ProgressBar . Style = progressStyle ;
2025-05-11 12:56:21 +00:00
2016-05-25 00:00:10 +00:00
_viewModel . ProgressBarVisibility = Visibility . Hidden ;
2014-07-23 20:46:19 +00:00
}
2022-12-23 03:46:12 +00:00
2025-03-16 15:10:12 +00:00
private void WindowAnimation ( )
2021-11-11 21:20:43 +00:00
{
2025-03-22 05:26:12 +00:00
_isArrowKeyPressed = true ;
2022-11-21 17:49:10 +00:00
2025-03-17 12:09:02 +00:00
var clocksb = new Storyboard ( ) ;
var iconsb = new Storyboard ( ) ;
2025-03-27 05:22:10 +00:00
var easing = new CircleEase { EasingMode = EasingMode . EaseInOut } ;
2022-11-21 17:49:10 +00:00
2023-06-23 17:42:36 +00:00
var animationLength = _settings . AnimationSpeed switch
{
AnimationSpeeds . Slow = > 560 ,
AnimationSpeeds . Medium = > 360 ,
AnimationSpeeds . Fast = > 160 ,
_ = > _settings . CustomAnimationLength
} ;
2022-11-21 17:49:10 +00:00
var IconMotion = new DoubleAnimation
{
2022-12-08 03:13:07 +00:00
From = 12 ,
To = 0 ,
EasingFunction = easing ,
2023-06-23 17:42:36 +00:00
Duration = TimeSpan . FromMilliseconds ( animationLength ) ,
2025-03-17 10:59:59 +00:00
FillBehavior = FillBehavior . HoldEnd
2022-11-21 17:49:10 +00:00
} ;
var ClockOpacity = new DoubleAnimation
{
From = 0 ,
To = 1 ,
EasingFunction = easing ,
2023-06-23 17:42:36 +00:00
Duration = TimeSpan . FromMilliseconds ( animationLength ) ,
2025-03-06 05:14:25 +00:00
FillBehavior = FillBehavior . HoldEnd
2022-11-21 17:49:10 +00:00
} ;
2025-03-06 05:14:25 +00:00
2025-03-27 05:22:10 +00:00
var TargetIconOpacity = GetOpacityFromStyle ( SearchIcon . Style , 1.0 ) ;
2025-03-06 12:19:09 +00:00
2022-11-21 17:49:10 +00:00
var IconOpacity = new DoubleAnimation
{
From = 0 ,
To = TargetIconOpacity ,
EasingFunction = easing ,
2023-06-23 17:42:36 +00:00
Duration = TimeSpan . FromMilliseconds ( animationLength ) ,
2025-03-06 05:14:25 +00:00
FillBehavior = FillBehavior . HoldEnd
2022-11-21 17:49:10 +00:00
} ;
2025-05-11 12:56:21 +00:00
2025-03-27 05:22:10 +00:00
var rightMargin = GetThicknessFromStyle ( ClockPanel . Style , new Thickness ( 0 , 0 , DefaultRightMargin , 0 ) ) . Right ;
2022-11-21 17:49:10 +00:00
var thicknessAnimation = new ThicknessAnimation
{
2025-03-15 05:21:14 +00:00
From = new Thickness ( 0 , 12 , rightMargin , 0 ) ,
To = new Thickness ( 0 , 0 , rightMargin , 0 ) ,
2022-11-21 17:49:10 +00:00
EasingFunction = easing ,
2023-06-23 17:42:36 +00:00
Duration = TimeSpan . FromMilliseconds ( animationLength ) ,
2025-03-06 05:14:25 +00:00
FillBehavior = FillBehavior . HoldEnd
2022-11-21 17:49:10 +00:00
} ;
2025-03-06 05:14:25 +00:00
Storyboard . SetTarget ( ClockOpacity , ClockPanel ) ;
2025-04-25 08:28:29 +00:00
Storyboard . SetTargetProperty ( ClockOpacity , new PropertyPath ( OpacityProperty ) ) ;
2025-03-06 05:14:25 +00:00
2025-04-25 08:28:29 +00:00
Storyboard . SetTarget ( thicknessAnimation , ClockPanel ) ;
2022-11-21 17:49:10 +00:00
Storyboard . SetTargetProperty ( thicknessAnimation , new PropertyPath ( MarginProperty ) ) ;
2025-03-06 05:14:25 +00:00
Storyboard . SetTarget ( IconMotion , SearchIcon ) ;
2022-11-21 17:49:10 +00:00
Storyboard . SetTargetProperty ( IconMotion , new PropertyPath ( TopProperty ) ) ;
2025-03-06 05:14:25 +00:00
Storyboard . SetTarget ( IconOpacity , SearchIcon ) ;
2022-11-21 17:49:10 +00:00
Storyboard . SetTargetProperty ( IconOpacity , new PropertyPath ( OpacityProperty ) ) ;
clocksb . Children . Add ( thicknessAnimation ) ;
clocksb . Children . Add ( ClockOpacity ) ;
iconsb . Children . Add ( IconMotion ) ;
iconsb . Children . Add ( IconOpacity ) ;
2025-05-11 12:56:21 +00:00
2024-06-30 09:03:25 +00:00
_settings . WindowLeft = Left ;
2025-03-22 05:26:12 +00:00
_isArrowKeyPressed = false ;
2025-05-11 12:56:21 +00:00
2025-03-26 16:14:13 +00:00
clocksb . Begin ( ClockPanel ) ;
2021-11-27 02:38:47 +00:00
iconsb . Begin ( SearchIcon ) ;
2021-11-11 21:20:43 +00:00
}
2025-03-06 11:49:14 +00:00
private void UpdateClockPanelVisibility ( )
2024-05-15 14:30:27 +00:00
{
2025-05-31 09:15:39 +00:00
if ( QueryTextBox = = null | | ResultContextMenu = = null | | History = = null | | ClockPanel = = null )
2025-03-28 13:42:40 +00:00
{
2025-03-06 11:49:14 +00:00
return ;
2025-03-28 13:42:40 +00:00
}
2025-03-06 11:49:14 +00:00
2025-03-28 13:42:40 +00:00
// ✅ Initialize animation length & duration
2025-03-06 11:49:14 +00:00
var animationLength = _settings . AnimationSpeed switch
2024-05-19 12:13:37 +00:00
{
2025-03-06 11:49:14 +00:00
AnimationSpeeds . Slow = > 560 ,
AnimationSpeeds . Medium = > 360 ,
AnimationSpeeds . Fast = > 160 ,
_ = > _settings . CustomAnimationLength
} ;
2025-03-16 02:13:24 +00:00
var animationDuration = TimeSpan . FromMilliseconds ( animationLength * 2 / 3 ) ;
2025-03-06 11:49:14 +00:00
2025-05-31 09:15:39 +00:00
// ✅ Conditions for showing ClockPanel (No query input / ResultContextMenu & History are closed)
2025-03-28 08:13:02 +00:00
var shouldShowClock = QueryTextBox . Text . Length = = 0 & &
2025-05-31 09:15:39 +00:00
ResultContextMenu . Visibility ! = Visibility . Visible & &
2025-03-17 12:41:06 +00:00
History . Visibility ! = Visibility . Visible ;
2025-03-16 02:13:24 +00:00
2025-05-31 09:15:39 +00:00
// ✅ 1. When ResultContextMenu opens, immediately set Visibility.Hidden (force hide without animation)
if ( ResultContextMenu . Visibility = = Visibility . Visible )
2024-05-15 14:30:27 +00:00
{
2025-03-28 08:13:02 +00:00
_viewModel . ClockPanelVisibility = Visibility . Hidden ;
_viewModel . ClockPanelOpacity = 0.0 ; // Set to 0 in case Opacity animation affects it
2025-03-16 02:13:24 +00:00
return ;
2024-05-15 14:30:27 +00:00
}
2025-03-06 11:49:14 +00:00
2025-05-31 09:15:39 +00:00
// ✅ 2. When ResultContextMenu is closed, keep it Hidden if there's text in the query (remember previous state)
2025-03-28 13:42:40 +00:00
else if ( QueryTextBox . Text . Length > 0 )
2025-03-16 02:13:24 +00:00
{
2025-03-28 08:13:02 +00:00
_viewModel . ClockPanelVisibility = Visibility . Hidden ;
_viewModel . ClockPanelOpacity = 0.0 ;
2025-03-16 02:13:24 +00:00
return ;
}
2025-03-28 13:42:40 +00:00
// ✅ Prevent multiple animations
if ( _isClockPanelAnimating )
2024-05-19 12:13:37 +00:00
{
2025-03-16 02:13:24 +00:00
return ;
2024-05-19 12:13:37 +00:00
}
2016-03-11 04:23:46 +00:00
2025-03-18 15:09:02 +00:00
// ✅ 3. When hiding ClockPanel (apply fade-out animation)
2025-03-28 13:42:40 +00:00
if ( ( ! shouldShowClock ) & & _viewModel . ClockPanelVisibility = = Visibility . Visible )
2025-03-16 02:13:24 +00:00
{
2025-03-06 11:49:14 +00:00
_isClockPanelAnimating = true ;
2022-12-08 03:13:07 +00:00
2025-03-06 11:49:14 +00:00
var fadeOut = new DoubleAnimation
{
From = 1.0 ,
To = 0.0 ,
Duration = animationDuration ,
2025-03-16 02:13:24 +00:00
FillBehavior = FillBehavior . HoldEnd
2025-03-06 11:49:14 +00:00
} ;
2022-12-08 03:13:07 +00:00
2025-03-06 11:49:14 +00:00
fadeOut . Completed + = ( s , e ) = >
{
2025-03-28 08:13:02 +00:00
_viewModel . ClockPanelVisibility = Visibility . Hidden ; // ✅ Completely hide after animation
2025-03-06 11:49:14 +00:00
_isClockPanelAnimating = false ;
} ;
2016-05-25 00:00:10 +00:00
2025-03-16 07:05:15 +00:00
ClockPanel . BeginAnimation ( OpacityProperty , fadeOut ) ;
2025-03-06 11:49:14 +00:00
}
2025-03-28 05:50:36 +00:00
2025-03-18 15:09:02 +00:00
// ✅ 4. When showing ClockPanel (apply fade-in animation)
2025-03-28 13:42:40 +00:00
else if ( shouldShowClock & & _viewModel . ClockPanelVisibility ! = Visibility . Visible )
2016-05-25 00:00:10 +00:00
{
2025-03-06 11:49:14 +00:00
_isClockPanelAnimating = true ;
2022-12-08 03:13:07 +00:00
2025-03-28 13:42:40 +00:00
_viewModel . ClockPanelVisibility = Visibility . Visible ; // ✅ Set Visibility to Visible first
var fadeIn = new DoubleAnimation
2021-11-27 02:38:47 +00:00
{
2025-03-28 13:42:40 +00:00
From = 0.0 ,
To = 1.0 ,
Duration = animationDuration ,
FillBehavior = FillBehavior . HoldEnd
} ;
2016-05-25 00:00:10 +00:00
2025-03-28 13:42:40 +00:00
fadeIn . Completed + = ( s , e ) = > _isClockPanelAnimating = false ;
ClockPanel . BeginAnimation ( OpacityProperty , fadeIn ) ;
2017-02-23 02:00:39 +00:00
}
2016-05-25 00:00:10 +00:00
}
2025-03-16 15:10:12 +00:00
private static double GetOpacityFromStyle ( Style style , double defaultOpacity = 1.0 )
2023-03-13 03:29:52 +00:00
{
2025-03-16 15:10:12 +00:00
if ( style = = null )
2025-03-28 13:42:40 +00:00
{
2025-03-16 15:10:12 +00:00
return defaultOpacity ;
2025-03-28 13:42:40 +00:00
}
2024-05-19 12:13:37 +00:00
2025-03-16 15:10:12 +00:00
foreach ( Setter setter in style . Setters . Cast < Setter > ( ) )
2023-03-13 03:29:52 +00:00
{
2025-03-16 15:10:12 +00:00
if ( setter . Property = = OpacityProperty )
2021-11-27 02:38:47 +00:00
{
2025-03-16 15:10:12 +00:00
return setter . Value is double opacity ? opacity : defaultOpacity ;
2021-11-27 02:38:47 +00:00
}
2023-03-13 03:29:52 +00:00
}
2024-09-21 18:19:34 +00:00
2025-03-16 15:10:12 +00:00
return defaultOpacity ;
2023-03-13 03:29:52 +00:00
}
2025-03-16 15:10:12 +00:00
private static Thickness GetThicknessFromStyle ( Style style , Thickness defaultThickness )
2023-03-13 03:29:52 +00:00
{
2025-03-16 15:10:12 +00:00
if ( style = = null )
2025-03-28 13:42:40 +00:00
{
2025-03-16 15:10:12 +00:00
return defaultThickness ;
2025-03-28 13:42:40 +00:00
}
2022-09-07 02:08:11 +00:00
2025-03-16 15:10:12 +00:00
foreach ( Setter setter in style . Setters . Cast < Setter > ( ) )
2016-05-25 00:00:10 +00:00
{
2025-03-16 15:10:12 +00:00
if ( setter . Property = = MarginProperty )
2021-11-27 02:38:47 +00:00
{
2025-03-16 15:10:12 +00:00
return setter . Value is Thickness thickness ? thickness : defaultThickness ;
2021-11-27 02:38:47 +00:00
}
2016-05-25 00:00:10 +00:00
}
2024-09-21 18:19:34 +00:00
2025-03-16 15:10:12 +00:00
return defaultThickness ;
2016-05-25 00:00:10 +00:00
}
2022-12-08 03:13:07 +00:00
2025-03-16 15:10:12 +00:00
#endregion
2024-09-21 18:19:34 +00:00
2025-03-16 15:10:12 +00:00
#region QueryTextBox Event
2024-09-21 18:19:34 +00:00
2025-03-16 15:10:12 +00:00
private void QueryTextBox_OnCopy ( object sender , ExecutedRoutedEventArgs e )
2024-05-16 08:04:29 +00:00
{
2025-03-16 15:10:12 +00:00
var result = _viewModel . Results . SelectedItem ? . Result ;
if ( QueryTextBox . SelectionLength = = 0 & & result ! = null )
2024-05-16 08:04:29 +00:00
{
2025-03-16 15:10:12 +00:00
string copyText = result . CopyText ;
App . API . CopyToClipboard ( copyText , directCopy : true ) ;
2024-05-16 08:04:29 +00:00
}
2025-03-16 15:10:12 +00:00
else if ( ! string . IsNullOrEmpty ( QueryTextBox . Text ) )
2024-05-16 08:04:29 +00:00
{
2025-03-16 15:10:12 +00:00
App . API . CopyToClipboard ( QueryTextBox . SelectedText , showDefaultNotification : false ) ;
2024-05-16 08:04:29 +00:00
}
}
2024-09-21 18:19:34 +00:00
2025-03-16 15:10:12 +00:00
private void QueryTextBox_OnPaste ( object sender , DataObjectPastingEventArgs e )
2021-11-23 05:33:11 +00:00
{
2025-07-11 14:24:05 +00:00
try
{
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 ;
}
}
catch ( Exception ex )
2021-11-23 05:33:11 +00:00
{
2025-07-11 14:24:05 +00:00
App . API . LogException ( ClassName , "Failed to paste text" , ex ) ;
2021-11-23 05:33:11 +00:00
}
}
2022-09-14 08:50:42 +00:00
private void QueryTextBox_KeyUp ( object sender , KeyEventArgs e )
{
2022-12-08 03:13:07 +00:00
if ( _viewModel . QueryText ! = QueryTextBox . Text )
2022-09-21 11:13:50 +00:00
{
2025-03-16 14:37:22 +00:00
BindingExpression be = QueryTextBox . GetBindingExpression ( TextBox . TextProperty ) ;
2022-09-21 11:13:50 +00:00
be . UpdateSource ( ) ;
}
2022-09-14 08:50:42 +00:00
}
2025-03-30 04:20:14 +00:00
2025-03-16 15:10:12 +00:00
private void QueryTextBox_OnPreviewDragOver ( object sender , DragEventArgs e )
2022-09-02 06:12:32 +00:00
{
2025-03-16 15:10:12 +00:00
e . Handled = true ;
2021-11-23 05:33:11 +00:00
}
2025-05-11 12:56:21 +00:00
2025-03-16 15:10:12 +00:00
#endregion
2025-03-21 12:55:57 +00:00
2025-03-29 05:10:06 +00:00
#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 ;
}
}
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
2025-03-29 05:25:00 +00:00
#region Resize Mode
private void SetupResizeMode ( )
{
2025-03-29 07:27:25 +00:00
ResizeMode = _settings . KeepMaxResults ? ResizeMode . NoResize : ResizeMode . CanResize ;
2025-03-29 06:15:24 +00:00
if ( WindowChrome . GetWindowChrome ( this ) is WindowChrome windowChrome )
{
2025-03-29 08:41:11 +00:00
_theme . SetResizeBorderThickness ( windowChrome , _settings . KeepMaxResults ) ;
2025-03-29 06:15:24 +00:00
}
2025-03-29 05:25:00 +00:00
}
2025-03-20 23:42:04 +00:00
#endregion
2025-05-11 12:56:21 +00:00
2025-03-15 13:58:55 +00:00
#region Search Delay
2025-03-30 01:38:59 +00:00
private void QueryTextBox_TextChanged1 ( object sender , TextChangedEventArgs e )
2025-03-15 13:58:55 +00:00
{
var textBox = ( TextBox ) sender ;
2025-03-30 14:40:46 +00:00
_viewModel . QueryText = textBox . Text ;
_viewModel . Query ( _settings . SearchQueryResultsWithDelay ) ;
2025-03-15 13:58:55 +00:00
}
#endregion
2025-03-21 12:55:57 +00:00
2025-07-20 11:11:09 +00:00
#region Dialog Jump
private void InitializeDialogJump ( )
{
DialogJump . ShowDialogJumpWindowAsync = _viewModel . SetupDialogJumpAsync ;
DialogJump . UpdateDialogJumpWindow = InitializeDialogJumpPosition ;
DialogJump . ResetDialogJumpWindow = _viewModel . ResetDialogJump ;
DialogJump . HideDialogJumpWindow = _viewModel . HideDialogJump ;
}
private void InitializeDialogJumpPosition ( )
{
if ( _viewModel . DialogWindowHandle = = nint . Zero | | ! _viewModel . MainWindowVisibilityStatus ) return ;
if ( ! _viewModel . IsDialogJumpWindowUnderDialog ( ) ) return ;
// Get dialog window rect
var result = Win32Helper . GetWindowRect ( _viewModel . DialogWindowHandle , out var window ) ;
if ( ! result ) return ;
// Move window below the bottom of the dialog and keep it center
Top = VerticalBottom ( window ) ;
Left = HorizonCenter ( window ) ;
}
private double HorizonCenter ( Rect window )
{
var dip1 = Win32Helper . TransformPixelsToDIP ( this , window . X , 0 ) ;
var dip2 = Win32Helper . TransformPixelsToDIP ( this , window . Width , 0 ) ;
var left = ( dip2 . X - ActualWidth ) / 2 + dip1 . X ;
return left ;
}
private double VerticalBottom ( Rect window )
{
var dip1 = Win32Helper . TransformPixelsToDIP ( this , 0 , window . Bottom ) ;
return dip1 . Y ;
}
#endregion
2025-03-21 12:55:57 +00:00
#region IDisposable
protected virtual void Dispose ( bool disposing )
{
2025-03-22 05:26:12 +00:00
if ( ! _disposed )
2025-03-21 12:55:57 +00:00
{
if ( disposing )
{
_hwndSource ? . Dispose ( ) ;
2025-03-22 05:26:12 +00:00
_notifyIcon ? . Dispose ( ) ;
2025-09-30 11:41:27 +00:00
_animationSoundWMP ? . Close ( ) ;
_animationSoundWPF ? . Dispose ( ) ;
2025-07-05 05:35:17 +00:00
_viewModel . ActualApplicationThemeChanged - = ViewModel_ActualApplicationThemeChanged ;
2025-09-30 13:11:03 +00:00
UnregisterSoundEffectsEvent ( ) ;
2025-03-21 12:55:57 +00:00
}
2025-03-22 05:26:12 +00:00
_disposed = true ;
2025-03-21 12:55:57 +00:00
}
}
public void Dispose ( )
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose ( disposing : true ) ;
GC . SuppressFinalize ( this ) ;
}
#endregion
2014-07-23 20:46:19 +00:00
}
2022-09-14 08:50:42 +00:00
}