diff --git a/GlazeWM.App.IpcServer/IpcMessageHandler.cs b/GlazeWM.App.IpcServer/IpcMessageHandler.cs index 2fdbbdc63..10bc009c9 100644 --- a/GlazeWM.App.IpcServer/IpcMessageHandler.cs +++ b/GlazeWM.App.IpcServer/IpcMessageHandler.cs @@ -60,6 +60,7 @@ public sealed class IpcMessageHandler { DomainEvent.BindingModeChanged, DomainEvent.FocusChanged, + DomainEvent.FocusedContainerMoved, DomainEvent.MonitorAdded, DomainEvent.MonitorRemoved, DomainEvent.TilingDirectionChanged, diff --git a/GlazeWM.App.WindowManager/WmStartup.cs b/GlazeWM.App.WindowManager/WmStartup.cs index 373fcf55e..94291d37f 100644 --- a/GlazeWM.App.WindowManager/WmStartup.cs +++ b/GlazeWM.App.WindowManager/WmStartup.cs @@ -74,10 +74,23 @@ public ExitCode Run() _systemTrayIcon = new SystemTrayIcon(systemTrayIconConfig); _systemTrayIcon.Show(); + var nativeFocusReassigned = _bus.Events + .OfType() + .Select((@event) => @event.FocusedContainer); + if (_userConfigService.FocusBorderConfig.Active.Enabled || _userConfigService.FocusBorderConfig.Inactive.Enabled) - _bus.Events.OfType().Subscribe((@event) => - _bus.InvokeAsync(new SetActiveWindowBorderCommand(@event.FocusedContainer as Window))); + { + var focusChanged = _bus.Events + .OfType() + .Select(@event => @event.FocusedContainer); + + focusChanged + .Merge(nativeFocusReassigned) + .Subscribe((container) => + _bus.InvokeAsync(new SetActiveWindowBorderCommand(container as Window)) + ); + } // Hook mouse event for focus follows cursor. if (_userConfigService.GeneralConfig.FocusFollowsCursor) @@ -87,6 +100,18 @@ public ExitCode Run() _bus.InvokeAsync(new FocusContainerUnderCursorCommand(@event.Point)); }); + // Setup cursor follows focus + if (_userConfigService.GeneralConfig.CursorFollowsFocus) + { + var focusedContainerMoved = _bus.Events + .OfType() + .Select(@event => @event.FocusedContainer); + + focusedContainerMoved.Merge(nativeFocusReassigned) + .Where(container => container is Window) + .Subscribe((window) => _bus.InvokeAsync(new CenterCursorOnContainerCommand(window))); + } + System.Windows.Forms.Application.Run(); return ExitCode.Success; } diff --git a/GlazeWM.Domain/Common/DomainEvent.cs b/GlazeWM.Domain/Common/DomainEvent.cs index 0bc986cee..a5291668d 100644 --- a/GlazeWM.Domain/Common/DomainEvent.cs +++ b/GlazeWM.Domain/Common/DomainEvent.cs @@ -4,8 +4,10 @@ public static class DomainEvent { public const string BindingModeChanged = "binding_mode_changed"; public const string FocusChanged = "focus_changed"; + public const string FocusedContainerMoved = "focused_container_moved"; public const string MonitorAdded = "monitor_added"; public const string MonitorRemoved = "monitor_removed"; + public const string NativeFocusReassigned = "native_focus_reassigned"; public const string TilingDirectionChanged = "tiling_direction_changed"; public const string UserConfigReloaded = "user_config_reloaded"; public const string WindowManaged = "window_managed"; diff --git a/GlazeWM.Domain/Containers/CommandHandlers/MoveContainerWithinTreeHandler.cs b/GlazeWM.Domain/Containers/CommandHandlers/MoveContainerWithinTreeHandler.cs index 661ceb73a..882b12733 100644 --- a/GlazeWM.Domain/Containers/CommandHandlers/MoveContainerWithinTreeHandler.cs +++ b/GlazeWM.Domain/Containers/CommandHandlers/MoveContainerWithinTreeHandler.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using GlazeWM.Domain.Containers.Commands; +using GlazeWM.Domain.Containers.Events; using GlazeWM.Infrastructure.Bussing; using GlazeWM.Infrastructure.Utils; @@ -9,10 +10,12 @@ namespace GlazeWM.Domain.Containers.CommandHandlers internal sealed class MoveContainerWithinTreeHandler : ICommandHandler { private readonly Bus _bus; + private readonly ContainerService _containerService; - public MoveContainerWithinTreeHandler(Bus bus) + public MoveContainerWithinTreeHandler(Bus bus, ContainerService containerService) { _bus = bus; + _containerService = containerService; } public CommandResponse Handle(MoveContainerWithinTreeCommand command) @@ -32,6 +35,8 @@ public CommandResponse Handle(MoveContainerWithinTreeCommand command) targetParent ); + var focusedContainer = _containerService.FocusedContainer; + // Handle case where target parent is the LCA (eg. in the case of swapping sibling containers // or moving a container to a direct ancestor). if (targetParent == lowestCommonAncestor) @@ -43,8 +48,8 @@ public CommandResponse Handle(MoveContainerWithinTreeCommand command) shouldAdjustSize ); - // Center cursor in focused window's new location - _bus.InvokeAsync(new CenterCursorOnContainerCommand(containerToMove)); + if (containerToMove == focusedContainer) + _bus.Emit(new FocusedContainerMovedEvent(containerToMove)); return CommandResponse.Ok; } @@ -90,8 +95,8 @@ public CommandResponse Handle(MoveContainerWithinTreeCommand command) targetParentAncestor ); - // Center cursor in focused window's new location - _bus.InvokeAsync(new CenterCursorOnContainerCommand(containerToMove)); + if (containerToMove == focusedContainer) + _bus.Emit(new FocusedContainerMovedEvent(containerToMove)); return CommandResponse.Ok; } diff --git a/GlazeWM.Domain/Containers/CommandHandlers/SetNativeFocusHandler.cs b/GlazeWM.Domain/Containers/CommandHandlers/SetNativeFocusHandler.cs index 1909bd25c..e8dbf9b8d 100644 --- a/GlazeWM.Domain/Containers/CommandHandlers/SetNativeFocusHandler.cs +++ b/GlazeWM.Domain/Containers/CommandHandlers/SetNativeFocusHandler.cs @@ -36,6 +36,8 @@ public CommandResponse Handle(SetNativeFocusCommand command) KeybdEvent(0, 0, 0, 0); SetForegroundWindow(handleToFocus); + _bus.Emit(new NativeFocusReassignedEvent(containerToFocus)); + // Setting focus to the desktop window does not emit `EVENT_SYSTEM_FOREGROUND` window event, // so `SetFocusedDescendantCommand` has to be manually called. // TODO: This is called twice unnecessarily when setting workspace focus on unmanage. @@ -45,9 +47,6 @@ public CommandResponse Handle(SetNativeFocusCommand command) _bus.Emit(new FocusChangedEvent(containerToFocus)); } - // Center cursor in focused window's new location - _bus.InvokeAsync(new CenterCursorOnContainerCommand(containerToFocus)); - return CommandResponse.Ok; } } diff --git a/GlazeWM.Domain/Containers/Events/FocusedContainerMovedEvent.cs b/GlazeWM.Domain/Containers/Events/FocusedContainerMovedEvent.cs new file mode 100644 index 000000000..a781a9049 --- /dev/null +++ b/GlazeWM.Domain/Containers/Events/FocusedContainerMovedEvent.cs @@ -0,0 +1,8 @@ +using GlazeWM.Domain.Common; +using GlazeWM.Infrastructure.Bussing; + +namespace GlazeWM.Domain.Containers.Events +{ + public record FocusedContainerMovedEvent(Container FocusedContainer) + : Event(DomainEvent.FocusedContainerMoved); +} diff --git a/GlazeWM.Domain/Containers/Events/NativeFocusReassignedEvent.cs b/GlazeWM.Domain/Containers/Events/NativeFocusReassignedEvent.cs new file mode 100644 index 000000000..7c1795d07 --- /dev/null +++ b/GlazeWM.Domain/Containers/Events/NativeFocusReassignedEvent.cs @@ -0,0 +1,8 @@ +using GlazeWM.Domain.Common; +using GlazeWM.Infrastructure.Bussing; + +namespace GlazeWM.Domain.Containers.Events +{ + public record NativeFocusReassignedEvent(Container FocusedContainer) + : Event(DomainEvent.NativeFocusReassigned); +}