mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-11-13 12:23:38 +01:00
feat(wm): track a window the user maximizes or restores
A window maximized with its button or title bar fills the work area, but the window manager only had a window as maximized when it was done by the command, so the layout was not put back when it was restored and the rest of the workspace was laid out around a window that took all of it. The location change of a window of the window manager is looked at for a change of whether it is maximized, and the window manager is told of one as Maximize and Unmaximize, which do what the command does. What the command itself does is already known by the time these are read.
This commit is contained in:
1 parent
365d705c75
commit
3fa3d429fa
3 files changed
+110
No files matched your search
@@ -884,6 +884,40 @@ impl WindowManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
WindowManagerEvent::Maximize(_, window) => {
|
||||
// The user maximized a tiled window themselves, with its button or by
|
||||
// double-clicking its title bar: it is the window that is maximized as if it had
|
||||
// been by the command. It may have been restored again by the time this is read,
|
||||
// and a maximize that the command made is already known.
|
||||
let workspace = self.focused_workspace_mut()?;
|
||||
|
||||
if WindowsApi::is_zoomed(window.hwnd)
|
||||
&& workspace.maximized_window.is_none()
|
||||
&& workspace.monocle_container.is_none()
|
||||
&& workspace.container_idx_for_window(window.hwnd).is_some()
|
||||
{
|
||||
tracing::info!("window maximized by the user");
|
||||
workspace.focus_container_by_window(window.hwnd)?;
|
||||
workspace.layer = WorkspaceLayer::Tiling;
|
||||
workspace.new_maximized_window()?;
|
||||
self.update_focused_workspace(false, false)?;
|
||||
}
|
||||
}
|
||||
WindowManagerEvent::Unmaximize(_, window) => {
|
||||
// The user restored the window that is maximized, which puts it back in its
|
||||
// tile as the command does. A restore that the command made is already known.
|
||||
let workspace = self.focused_workspace_mut()?;
|
||||
|
||||
if !WindowsApi::is_zoomed(window.hwnd)
|
||||
&& workspace
|
||||
.maximized_window
|
||||
.is_some_and(|maximized| maximized.hwnd == window.hwnd)
|
||||
{
|
||||
tracing::info!("window restored by the user");
|
||||
workspace.reintegrate_maximized_window()?;
|
||||
self.update_focused_workspace(false, false)?;
|
||||
}
|
||||
}
|
||||
WindowManagerEvent::MouseCapture(..)
|
||||
| WindowManagerEvent::Cloak(..)
|
||||
| WindowManagerEvent::TitleUpdate(..) => {}
|
||||
|
||||
@@ -25,6 +25,8 @@ pub enum WindowManagerEvent {
|
||||
MoveResizeStart(WinEvent, Window),
|
||||
MoveResizeEnd(WinEvent, Window),
|
||||
MouseCapture(WinEvent, Window),
|
||||
Maximize(WinEvent, Window),
|
||||
Unmaximize(WinEvent, Window),
|
||||
Manage(Window),
|
||||
Unmanage(Window),
|
||||
Raise(Window),
|
||||
@@ -73,6 +75,12 @@ impl Display for WindowManagerEvent {
|
||||
Self::MouseCapture(winevent, window) => {
|
||||
write!(f, "MouseCapture (WinEvent: {winevent}, Window: {window})",)
|
||||
}
|
||||
Self::Maximize(winevent, window) => {
|
||||
write!(f, "Maximize (WinEvent: {winevent}, Window: {window})")
|
||||
}
|
||||
Self::Unmaximize(winevent, window) => {
|
||||
write!(f, "Unmaximize (WinEvent: {winevent}, Window: {window})")
|
||||
}
|
||||
Self::Raise(window) => {
|
||||
write!(f, "Raise (Window: {window})")
|
||||
}
|
||||
@@ -96,6 +104,8 @@ impl WindowManagerEvent {
|
||||
| Self::MoveResizeStart(_, window)
|
||||
| Self::MoveResizeEnd(_, window)
|
||||
| Self::MouseCapture(_, window)
|
||||
| Self::Maximize(_, window)
|
||||
| Self::Unmaximize(_, window)
|
||||
| Self::Raise(window)
|
||||
| Self::Manage(window)
|
||||
| Self::Unmanage(window)
|
||||
@@ -119,6 +129,8 @@ impl WindowManagerEvent {
|
||||
WindowManagerEvent::MoveResizeStart(_, _) => "MoveResizeStart",
|
||||
WindowManagerEvent::MoveResizeEnd(_, _) => "MoveResizeEnd",
|
||||
WindowManagerEvent::MouseCapture(_, _) => "MouseCapture",
|
||||
WindowManagerEvent::Maximize(_, _) => "Maximize",
|
||||
WindowManagerEvent::Unmaximize(_, _) => "Unmaximize",
|
||||
WindowManagerEvent::Manage(_) => "Manage",
|
||||
WindowManagerEvent::Unmanage(_) => "Unmanage",
|
||||
WindowManagerEvent::Raise(_) => "Raise",
|
||||
@@ -138,6 +150,8 @@ impl WindowManagerEvent {
|
||||
| WindowManagerEvent::MoveResizeStart(event, _)
|
||||
| WindowManagerEvent::MoveResizeEnd(event, _)
|
||||
| WindowManagerEvent::MouseCapture(event, _)
|
||||
| WindowManagerEvent::Maximize(event, _)
|
||||
| WindowManagerEvent::Unmaximize(event, _)
|
||||
| WindowManagerEvent::TitleUpdate(event, _) => Some(event.to_string()),
|
||||
WindowManagerEvent::Manage(_)
|
||||
| WindowManagerEvent::Unmanage(_)
|
||||
|
||||
@@ -98,6 +98,49 @@ fn has_filtered_style(hwnd: HWND) -> bool {
|
||||
|| ex_style & WS_EX_NOACTIVATE.0 != 0
|
||||
}
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
/// The windows of the window manager that were maximized when last looked at
|
||||
static ref ZOOMED: parking_lot::Mutex<std::collections::HashSet<isize>> =
|
||||
parking_lot::Mutex::new(std::collections::HashSet::new());
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum ZoomChange {
|
||||
Maximized,
|
||||
Restored,
|
||||
}
|
||||
|
||||
fn zoom_change(was_zoomed: bool, is_zoomed: bool) -> Option<ZoomChange> {
|
||||
match (was_zoomed, is_zoomed) {
|
||||
(false, true) => Some(ZoomChange::Maximized),
|
||||
(true, false) => Some(ZoomChange::Restored),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// A window of the window manager that has been maximized or restored since the last time it was
|
||||
/// looked at, which the user does with the button or the title bar of the window. This is read
|
||||
/// for the location change of every window that moves, so it is a lookup of the handle and one
|
||||
/// call, before anything is asked of the window.
|
||||
fn zoom_event(winevent: WinEvent, window: Window) -> Option<WindowManagerEvent> {
|
||||
if !crate::reaper::HWNDS_CACHE.lock().contains_key(&window.hwnd) {
|
||||
ZOOMED.lock().remove(&window.hwnd);
|
||||
return None;
|
||||
}
|
||||
|
||||
let is_zoomed = WindowsApi::is_zoomed(window.hwnd);
|
||||
let was_zoomed = if is_zoomed {
|
||||
!ZOOMED.lock().insert(window.hwnd)
|
||||
} else {
|
||||
ZOOMED.lock().remove(&window.hwnd)
|
||||
};
|
||||
|
||||
match zoom_change(was_zoomed, is_zoomed)? {
|
||||
ZoomChange::Maximized => Some(WindowManagerEvent::Maximize(winevent, window)),
|
||||
ZoomChange::Restored => Some(WindowManagerEvent::Unmaximize(winevent, window)),
|
||||
}
|
||||
}
|
||||
|
||||
pub extern "system" fn win_event_hook(
|
||||
_h_win_event_hook: HWINEVENTHOOK,
|
||||
event: u32,
|
||||
@@ -165,6 +208,12 @@ pub extern "system" fn win_event_hook(
|
||||
// is moved, and what a window is takes a call into it for each of the
|
||||
// exe, the title and the class: the hook is held up for as long as that takes
|
||||
if matches!(winevent, WinEvent::ObjectLocationChange) {
|
||||
if let Some(event) = zoom_event(winevent, window) {
|
||||
winevent_listener::event_tx()
|
||||
.send(event)
|
||||
.expect("could not send message on winevent_listener::event_tx");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -185,3 +234,16 @@ pub extern "system" fn win_event_hook(
|
||||
.send(event_type)
|
||||
.expect("could not send message on winevent_listener::event_tx");
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn a_window_is_maximized_or_restored_only_when_it_changes() {
|
||||
assert_eq!(zoom_change(false, true), Some(ZoomChange::Maximized));
|
||||
assert_eq!(zoom_change(true, false), Some(ZoomChange::Restored));
|
||||
assert_eq!(zoom_change(true, true), None);
|
||||
assert_eq!(zoom_change(false, false), None);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user