diff --git a/QuickLook/KeystrokeDispatcher.cs b/QuickLook/KeystrokeDispatcher.cs index c6e38b83a..e9db56c90 100644 --- a/QuickLook/KeystrokeDispatcher.cs +++ b/QuickLook/KeystrokeDispatcher.cs @@ -46,7 +46,7 @@ protected KeystrokeDispatcher() _validKeys = [ Keys.Up, Keys.Down, Keys.Left, Keys.Right, - Keys.Enter, Keys.Space, Keys.Escape + Keys.Enter, Keys.Space, Keys.Escape, Keys.F11 ]; } @@ -135,6 +135,10 @@ private void InvokeRoutine(Keys key, bool isKeyDown) case Keys.Space: PipeServerManager.SendMessage(PipeMessages.Toggle); break; + + case Keys.F11: + PipeServerManager.SendMessage(PipeMessages.Fullscreen); + break; } } else diff --git a/QuickLook/PipeServerManager.cs b/QuickLook/PipeServerManager.cs index db1321641..92a3b3583 100644 --- a/QuickLook/PipeServerManager.cs +++ b/QuickLook/PipeServerManager.cs @@ -36,6 +36,7 @@ public static class PipeMessages public const string Forget = "QuickLook.App.PipeMessages.Forget"; public const string Close = "QuickLook.App.PipeMessages.Close"; public const string Quit = "QuickLook.App.PipeMessages.Quit"; + public const string Fullscreen = "QuickLook.App.PipeMessages.Fullscreen"; } public class PipeServerManager : IDisposable @@ -161,6 +162,12 @@ private bool MessageReceived(string msg) DispatcherPriority.ApplicationIdle); return false; + case PipeMessages.Fullscreen: + Application.Current.Dispatcher.BeginInvoke( + new Action(() => ViewWindowManager.GetInstance().ToggleFullscreen()), + DispatcherPriority.ApplicationIdle); + return false; + case PipeMessages.Quit: return true; diff --git a/QuickLook/ViewWindowManager.cs b/QuickLook/ViewWindowManager.cs index 3ab9ae3d1..56b6c5ed5 100644 --- a/QuickLook/ViewWindowManager.cs +++ b/QuickLook/ViewWindowManager.cs @@ -198,6 +198,14 @@ public void ReloadPreview() BeginShowNewWindow(_invokedPath, matchedPlugin); } + public void ToggleFullscreen() + { + if (!_viewerWindow.IsVisible) + return; + + _viewerWindow.ToggleFullscreen(); + } + private void BeginShowNewWindow(string path, IViewer matchedPlugin) { _viewerWindow.UnloadPlugin(); diff --git a/QuickLook/ViewerWindow.Actions.cs b/QuickLook/ViewerWindow.Actions.cs index 8d1f4cb92..90032f0e2 100644 --- a/QuickLook/ViewerWindow.Actions.cs +++ b/QuickLook/ViewerWindow.Actions.cs @@ -29,9 +29,11 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Input; +using System.Windows.Interop; using System.Windows.Media; using System.Windows.Threading; using Wpf.Ui.Controls; +using WinForms = System.Windows.Forms; using MenuItem = System.Windows.Controls.MenuItem; namespace QuickLook; @@ -62,6 +64,70 @@ internal void RunAndClose() Close(); } + internal void ToggleFullscreen() + { + if (_isFullscreen) + { + // Exit fullscreen + _isFullscreen = false; + + // Restore window properties + WindowStyle = _preFullscreenWindowStyle; + ResizeMode = _preFullscreenResizeMode; + + // Restore position and size + Left = _preFullscreenBounds.Left; + Top = _preFullscreenBounds.Top; + Width = _preFullscreenBounds.Width; + Height = _preFullscreenBounds.Height; + + // Restore window state last to avoid flicker + WindowState = _preFullscreenWindowState; + } + else + { + // Enter fullscreen + _isFullscreen = true; + + // Save current window properties before any changes + _preFullscreenWindowState = WindowState; + _preFullscreenWindowStyle = WindowStyle; + _preFullscreenResizeMode = ResizeMode; + + // Get current bounds (account for maximized state) + if (WindowState == WindowState.Maximized) + { + _preFullscreenBounds = RestoreBounds; + } + else + { + _preFullscreenBounds = new Rect(Left, Top, Width, Height); + } + + // Get the screen bounds where the window is currently located + var screen = WinForms.Screen.FromHandle(new WindowInteropHelper(this).Handle); + var screenBounds = screen.Bounds; + + // Get DPI scale factor for proper coordinate conversion + // scale.Horizontal and scale.Vertical contain the DPI scaling ratios (e.g., 1.5 for 150%) + var scale = DisplayDeviceHelper.GetScaleFactorFromWindow(this); + + // Set to normal state first to allow manual positioning + WindowState = WindowState.Normal; + + // Hide window chrome for true fullscreen + WindowStyle = WindowStyle.None; + ResizeMode = ResizeMode.NoResize; + + // Convert screen bounds from physical pixels to DIPs for WPF + var dipWidth = screenBounds.Width / scale.Horizontal; + var dipHeight = screenBounds.Height / scale.Vertical; + + // Use MoveWindow to set position and size with proper DPI handling + this.MoveWindow(screenBounds.Left, screenBounds.Top, dipWidth, dipHeight); + } + } + private void PositionWindow(Size size) { // If the window is now maximized, do not move it diff --git a/QuickLook/ViewerWindow.Properties.cs b/QuickLook/ViewerWindow.Properties.cs index c459f2cfd..17ce99700 100644 --- a/QuickLook/ViewerWindow.Properties.cs +++ b/QuickLook/ViewerWindow.Properties.cs @@ -37,6 +37,11 @@ public partial class ViewerWindow : INotifyPropertyChanged private bool _canOldPluginResize; private bool _pinned; + private bool _isFullscreen; + private WindowState _preFullscreenWindowState; + private WindowStyle _preFullscreenWindowStyle; + private ResizeMode _preFullscreenResizeMode; + private Rect _preFullscreenBounds; public bool Pinned {