From a521537542531f3eeb52a6a21e714e852a8a3a5b Mon Sep 17 00:00:00 2001 From: Nicolas Phister Date: Mon, 16 Oct 2023 17:47:42 +0200 Subject: [PATCH] feat: maximized windows stay maximized (#421) --- .../RedrawContainersHandler.cs | 7 +- .../WindowLocationChangedHandler.cs | 111 +++++++++++++++++- .../WindowMinimizeEndedHandler.cs | 3 +- GlazeWM.Domain/Windows/MaximizedWindow.cs | 6 +- GlazeWM.Domain/Windows/WindowService.cs | 1 + 5 files changed, 123 insertions(+), 5 deletions(-) diff --git a/GlazeWM.Domain/Containers/CommandHandlers/RedrawContainersHandler.cs b/GlazeWM.Domain/Containers/CommandHandlers/RedrawContainersHandler.cs index fe476c65b..176b02e9b 100644 --- a/GlazeWM.Domain/Containers/CommandHandlers/RedrawContainersHandler.cs +++ b/GlazeWM.Domain/Containers/CommandHandlers/RedrawContainersHandler.cs @@ -39,8 +39,8 @@ public CommandResponse Handle(RedrawContainersCommand command) var windowsToRestore = windowsToRedraw .Where( (window) => - window is not MinimizedWindow && - window.HasWindowStyle(WindowStyles.Maximize | WindowStyles.Minimize) + (window is not MinimizedWindow && window.HasWindowStyle(WindowStyles.Minimize)) || + (window is not MaximizedWindow && window.HasWindowStyle(WindowStyles.Maximize)) ) .ToList(); @@ -85,6 +85,9 @@ private void SetWindowPosition(Window window) else defaultFlags |= SetWindowPosFlags.HideWindow; + if (window is MaximizedWindow) + defaultFlags |= SetWindowPosFlags.NoSize; + // Transition display state depending on whether window will be shown/hidden. window.DisplayState = window.DisplayState switch { diff --git a/GlazeWM.Domain/Windows/EventHandlers/WindowLocationChangedHandler.cs b/GlazeWM.Domain/Windows/EventHandlers/WindowLocationChangedHandler.cs index f0d52ee83..03a0f13d2 100644 --- a/GlazeWM.Domain/Windows/EventHandlers/WindowLocationChangedHandler.cs +++ b/GlazeWM.Domain/Windows/EventHandlers/WindowLocationChangedHandler.cs @@ -1,18 +1,27 @@ +using System; +using System.Linq; +using GlazeWM.Domain.Containers; using GlazeWM.Domain.Containers.Commands; using GlazeWM.Domain.Monitors.Commands; +using GlazeWM.Domain.Workspaces; using GlazeWM.Infrastructure.Bussing; using GlazeWM.Infrastructure.Common.Events; +using static GlazeWM.Infrastructure.WindowsApi.WindowsApiService; namespace GlazeWM.Domain.Windows.EventHandlers { internal sealed class WindowLocationChangedHandler : IEventHandler { private readonly Bus _bus; + private readonly ContainerService _containerService; private readonly WindowService _windowService; - public WindowLocationChangedHandler(Bus bus, WindowService windowService) + public WindowLocationChangedHandler(Bus bus, + WindowService windowService, + ContainerService containerService) { _bus = bus; + _containerService = containerService; _windowService = windowService; } @@ -20,11 +29,111 @@ public void Handle(WindowLocationChangedEvent @event) { var windowHandle = @event.WindowHandle; + HandleMaximizedWindow(windowHandle); + if (!_windowService.AppBarHandles.Contains(windowHandle)) return; _bus.Invoke(new RefreshMonitorStateCommand()); _bus.Invoke(new RedrawContainersCommand()); } + + private void HandleMaximizedWindow(IntPtr windowHandle) + { + var window = _windowService.GetWindowByHandle(windowHandle); + if (window is null) + return; + + var windowPlacement = WindowService.GetPlacementOfHandle(windowHandle); + var isMaximized = windowPlacement.ShowCommand == ShowWindowFlags.Maximize; + + // Window is being maximized. + if (isMaximized && window is not MaximizedWindow) + { + var previousState = WindowService.GetWindowType(window); + var maximizedWindow = new MaximizedWindow( + window.Handle, + window.FloatingPlacement, + window.BorderDelta, + previousState + ) + { + Id = window.Id + }; + + if (!window.HasSiblings() && window.Parent is not Workspace) + _bus.Invoke(new FlattenSplitContainerCommand(window.Parent as SplitContainer)); + + _bus.Invoke(new ReplaceContainerCommand(maximizedWindow, window.Parent, window.Index)); + + _containerService.ContainersToRedraw.Concat(window.Siblings); + _bus.Invoke(new RedrawContainersCommand()); + } + + // Maximized window is being restored. + if (!isMaximized && window is MaximizedWindow) + { + var restoredWindow = CreateWindowFromPreviousState(window as MaximizedWindow); + _bus.Invoke(new ReplaceContainerCommand(restoredWindow, window.Parent, window.Index)); + + // Non-tiling window expect to be direct children of workspace. + if (restoredWindow is not TilingWindow) + { + var workspace = WorkspaceService.GetWorkspaceFromChildContainer(restoredWindow); + _bus.Invoke(new MoveContainerWithinTreeCommand(restoredWindow, workspace, false)); + } + else + { + // TODO: Temporary hack to resize restored tiling window. + _bus.Invoke( + new MoveContainerWithinTreeCommand( + restoredWindow, + restoredWindow.Parent, + restoredWindow.Index, + true + ) + ); + } + + _containerService.ContainersToRedraw.Add(restoredWindow); + _bus.Invoke(new RedrawContainersCommand()); + } + } + + // TODO: Share logic with `WindowMinimizedHandler`. + private static Window CreateWindowFromPreviousState(MaximizedWindow window) + { + Window restoredWindow = window.PreviousState switch + { + WindowType.Floating => new FloatingWindow( + window.Handle, + window.FloatingPlacement, + window.BorderDelta + ), + WindowType.Fullscreen => new FullscreenWindow( + window.Handle, + window.FloatingPlacement, + window.BorderDelta + ), + // Set `SizePercentage` to 0 to correctly resize the container when moved within tree. + WindowType.Tiling => new TilingWindow( + window.Handle, + window.FloatingPlacement, + window.BorderDelta, + 0 + ), + WindowType.Minimized => new MinimizedWindow( + window.Handle, + window.FloatingPlacement, + window.BorderDelta, + WindowType.Tiling + ), + WindowType.Maximized => throw new ArgumentException(null, nameof(window)), + _ => throw new ArgumentException(null, nameof(window)), + }; + + restoredWindow.Id = window.Id; + return restoredWindow; + } } } diff --git a/GlazeWM.Domain/Windows/EventHandlers/WindowMinimizeEndedHandler.cs b/GlazeWM.Domain/Windows/EventHandlers/WindowMinimizeEndedHandler.cs index a3a54f6b1..9f4443fd5 100644 --- a/GlazeWM.Domain/Windows/EventHandlers/WindowMinimizeEndedHandler.cs +++ b/GlazeWM.Domain/Windows/EventHandlers/WindowMinimizeEndedHandler.cs @@ -78,7 +78,8 @@ private static Window CreateWindowFromPreviousState(MinimizedWindow window) WindowType.Maximized => new MaximizedWindow( window.Handle, window.FloatingPlacement, - window.BorderDelta + window.BorderDelta, + WindowType.Tiling ), WindowType.Fullscreen => new FullscreenWindow( window.Handle, diff --git a/GlazeWM.Domain/Windows/MaximizedWindow.cs b/GlazeWM.Domain/Windows/MaximizedWindow.cs index 556be64b0..4e5532ebb 100644 --- a/GlazeWM.Domain/Windows/MaximizedWindow.cs +++ b/GlazeWM.Domain/Windows/MaximizedWindow.cs @@ -5,12 +5,16 @@ namespace GlazeWM.Domain.Windows { public sealed class MaximizedWindow : Window { + public WindowType PreviousState; + public MaximizedWindow( IntPtr handle, Rect floatingPlacement, - RectDelta borderDelta + RectDelta borderDelta, + WindowType previousState ) : base(handle, floatingPlacement, borderDelta) { + PreviousState = previousState; } } } diff --git a/GlazeWM.Domain/Windows/WindowService.cs b/GlazeWM.Domain/Windows/WindowService.cs index 9bc92567c..de5a311e7 100644 --- a/GlazeWM.Domain/Windows/WindowService.cs +++ b/GlazeWM.Domain/Windows/WindowService.cs @@ -241,6 +241,7 @@ public static WindowType GetWindowType(Window window) { TilingWindow => WindowType.Tiling, FloatingWindow => WindowType.Floating, + MinimizedWindow => WindowType.Minimized, MaximizedWindow => WindowType.Maximized, FullscreenWindow => WindowType.Fullscreen, _ => throw new ArgumentException(null, nameof(window)),