I've been doing some work on making various maximisation hotkeys behave more intuitively, looking for example at cases like pressing Maximize Horizontally then Maximize Vertically then Unmaximize. Currently that would leave the window maximised horizontally because its (horizontally-maximized) geometry is saved when Maximize Vertically is pressed. With the patch that won't happen; the window will go back to its original size.

The patch has a few too many special cases for my liking. I started off with a clean flowchart of what I wanted to happen if the window was in state X and hotkey Y was pressed but soon got confused with all the different permutations and things ended up devolving into a cycle of me pressing various chains of hotkeys, saying "no no that should result in the window being in state Z" and tweaking. Rinse and repeat.

While the patch could potentially benefit from a cleanup it does at least make things work in what I find to be a more sensible way.
From e68a6df4ac8383a0286baa87d17f1177e1347f49 Mon Sep 17 00:00:00 2001
From: Iain Patterson <[email protected]>
Date: Fri, 4 May 2012 17:30:01 +0100
Subject: [PATCH] More intuitive maximization handling.

Avoid some pitfalls with window maximization and make it behave more
intuitively.  We now treat a window's vertical and horizontal
maximization as separate properties and only remember its original
geometry in a particular direction when it actually changes.  We also
deliberately do not remember a window's geometry when it changes from
one maximized state to another.  As a result windows can be more
reliably restored to their original size.

For example the "Maximize active window" hotkey followed by the
"Maximize active window vertically" hotkey will now result in the window
being maximized horizontally only, whereas previously the second hotkey
would have no effect because the window was already maximized
vertically.  In addition selecting the Unmaximize window menu in the
same example will now result in the window being restored to its
original size.  Previously the unmaximize attempt would have no effect
because the vertical maximization would have remembered the window's
"original" geometry when it was fully maximized.

Maximus is handled separately.  The "Maximus" hotkey will now toggle
Maximus mode regardless of the window's current maximization state.  For
example if two unmaximized windows are on screen and one is Maximusized
it will fill the space left by the second window, as before.  But if the
first window is maximized and the "Maximus" hotkey is pressed the window
will now fill the same space as if it were Maximusized from its original
size.  Previously the window would not change size from its fully
maximized state because the Maximus algorithm would consider
fully-maximized to be a valid Maximus size.
---
 src/actions.c |  178 ++++++++++++++++++++++++++++++++++++++++++++++++++-------
 src/actions.h |    1 +
 src/event.c   |   30 ++--------
 src/window.h  |    1 +
 4 files changed, 164 insertions(+), 46 deletions(-)

diff --git a/src/actions.c b/src/actions.c
index e730570..4cf1f3d 100644
--- a/src/actions.c
+++ b/src/actions.c
@@ -58,7 +58,7 @@ extern int calcIntersectionLength(int p1, int l1, int p2, int 
l2);
 
 static void find_Maximus_geometry(WWindow *wwin, WArea usableArea, int *new_x, 
int *new_y,
                                  unsigned int *new_width, unsigned int 
*new_height);
-static void save_old_geometry(WWindow *wwin);
+static void save_old_geometry(WWindow *wwin, int directions);
 
 /******* Local Variables *******/
 static struct {
@@ -284,20 +284,43 @@ void wUnshadeWindow(WWindow *wwin)
 }
 
 /* Set the old coordinates using the current values */
-static void save_old_geometry(WWindow *wwin)
+static void save_old_geometry(WWindow *wwin, int directions)
 {
-       wwin->old_geometry.width = wwin->client.width;
-       wwin->old_geometry.height = wwin->client.height;
-       wwin->old_geometry.x = wwin->frame_x;
-       wwin->old_geometry.y = wwin->frame_y;
+       if (directions & MAX_HORIZONTAL || ! wwin->old_geometry.width) {
+               wwin->old_geometry.width = wwin->client.width;
+               wwin->old_geometry.x = wwin->frame_x;
+       }
+       if (directions & MAX_VERTICAL || ! wwin->old_geometry.height) {
+               wwin->old_geometry.height = wwin->client.height;
+               wwin->old_geometry.y = wwin->frame_y;
+       }
+}
+
+static void remember_geometry(WWindow *wwin, int *x, int *y, int *w, int *h)
+{
+       WMRect old_geom_rect;
+       int old_head;
+       Bool same_head;
+
+       old_geom_rect = wmkrect(wwin->old_geometry.x, wwin->old_geometry.y, 
wwin->old_geometry.width, wwin->old_geometry.height);
+       old_head = wGetHeadForRect(wwin->screen_ptr, old_geom_rect);
+       same_head = (wGetHeadForWindow(wwin) == old_head);
+       *x = (wwin->old_geometry.x && same_head) ? wwin->old_geometry.x : 
wwin->frame_x;
+       *y = (wwin->old_geometry.y && same_head) ? wwin->old_geometry.y : 
wwin->frame_y;
+       *w = wwin->old_geometry.width ? wwin->old_geometry.width : 
wwin->client.width;
+       *h = wwin->old_geometry.height ? wwin->old_geometry.height : 
wwin->client.height;
 }
 
+#define IS_MAX_HORIZONTALLY(directions) ((directions & MAX_HORIZONTAL) | 
(directions & MAX_LEFTHALF) | (directions & MAX_RIGHTHALF))
 void wMaximizeWindow(WWindow *wwin, int directions)
 {
        int new_x, new_y;
        unsigned int new_width, new_height, half_scr_width;
+       int maximus_x, maximus_y;
+       unsigned int maximus_width, maximus_height;
        WArea usableArea, totalArea;
        Bool has_border = 1;
+       int save_directions = 0;
        int adj_size;
 
        if (!IS_RESIZABLE(wwin))
@@ -309,8 +332,19 @@ void wMaximizeWindow(WWindow *wwin, int directions)
        /* the size to adjust the geometry */
        adj_size = FRAME_BORDER_WIDTH * 2 * has_border;
 
-       /* save old coordinates before we change the current values */
-       save_old_geometry(wwin);
+       /* save old coordinates before we change the current values
+        * but never if the window has been Maximusized */
+       if (!(wwin->flags.old_maximized & MAX_MAXIMUS)) {
+               if ((directions & MAX_VERTICAL) &&
+                   !(wwin->flags.maximized & MAX_VERTICAL))
+                       save_directions |= MAX_VERTICAL;
+               if (IS_MAX_HORIZONTALLY(directions) &&
+                   !IS_MAX_HORIZONTALLY(wwin->flags.maximized))
+                       save_directions |= MAX_HORIZONTAL;
+       }
+       if ((directions & MAX_MAXIMUS) && !wwin->flags.maximized)
+               save_directions |= MAX_VERTICAL | MAX_HORIZONTAL;
+       save_old_geometry(wwin, save_directions);
 
        totalArea.x1 = 0;
        totalArea.y1 = 0;
@@ -330,6 +364,10 @@ void wMaximizeWindow(WWindow *wwin, int directions)
                usableArea = wGetUsableAreaForHead(scr, head, &totalArea, True);
        }
 
+       /* remember Maximus geometry if we'll need it later */
+       if ((wwin->flags.old_maximized & MAX_MAXIMUS) || (directions & 
MAX_MAXIMUS))
+               find_Maximus_geometry(wwin, usableArea, &maximus_x, &maximus_y, 
&maximus_width, &maximus_height);
+
        /* Only save directions, not kbd or xinerama hints */
        directions &= (MAX_HORIZONTAL | MAX_VERTICAL | MAX_LEFTHALF | 
MAX_RIGHTHALF | MAX_MAXIMUS);
 
@@ -349,10 +387,21 @@ void wMaximizeWindow(WWindow *wwin, int directions)
        } else if (directions & MAX_LEFTHALF) {
                new_width = half_scr_width - adj_size;
                new_x = usableArea.x1;
+               wwin->flags.old_maximized |= MAX_LEFTHALF;
+               wwin->flags.old_maximized &= ~MAX_RIGHTHALF;
        } else if (directions & MAX_RIGHTHALF) {
                new_width = half_scr_width - adj_size;
                new_x = usableArea.x1 + half_scr_width;
+               wwin->flags.old_maximized |= MAX_RIGHTHALF;
+               wwin->flags.old_maximized &= ~MAX_LEFTHALF;
+       } else if (wwin->flags.old_maximized & MAX_MAXIMUS) {
+               new_x = maximus_x;
+               new_width = maximus_width - adj_size;
+       } else if (IS_MAX_HORIZONTALLY(wwin->flags.maximized)) {
+               new_x = (wwin->old_geometry.x) ? wwin->old_geometry.x : 
wwin->frame_x;
+               new_width = (wwin->old_geometry.width) ? 
wwin->old_geometry.width : wwin->frame->core->width;
        } else {
+               wwin->flags.old_maximized &= ~(MAX_LEFTHALF | MAX_RIGHTHALF);
                new_x = wwin->frame_x;
                new_width = wwin->frame->core->width;
        }
@@ -364,6 +413,17 @@ void wMaximizeWindow(WWindow *wwin, int directions)
                        new_y -= wwin->frame->top_width;
                        new_height += wwin->frame->bottom_width - 1;
                }
+       } else if (wwin->flags.old_maximized & MAX_MAXIMUS) {
+               new_y = maximus_y;
+               new_height = maximus_height - adj_size;
+               /* HACK: this will be subtracted again below */
+               new_height += wwin->frame->top_width + 
wwin->frame->bottom_width;
+       } else if (wwin->flags.maximized & MAX_VERTICAL) {
+               new_y = (wwin->old_geometry.y) ? wwin->old_geometry.y : 
wwin->frame_y;
+               new_height = (wwin->old_geometry.height) ? 
wwin->old_geometry.height : wwin->frame->core->height;
+               /* HACK: this will be subtracted again below */
+               new_height += wwin->frame->top_width + 
wwin->frame->bottom_width;
+               wwin->flags.old_maximized &= ~(MAX_LEFTHALF | MAX_RIGHTHALF);
        } else {
                new_y = wwin->frame_y;
                new_height = wwin->frame->core->height;
@@ -374,13 +434,15 @@ void wMaximizeWindow(WWindow *wwin, int directions)
        }
 
        if (directions & MAX_MAXIMUS) {
-               find_Maximus_geometry(wwin, usableArea, &new_x, &new_y, 
&new_width, &new_height);
-               new_width -= adj_size;
-               new_height -= adj_size;
+               new_x = maximus_x;
+               new_y = maximus_y;
+               new_width = maximus_width - adj_size;
+               new_height = maximus_height - adj_size;
                if (WFLAGP(wwin, full_maximize) && new_y == 0) {
                        new_y -= wwin->frame->top_width;
                        new_height += wwin->frame->top_width - 1;
                }
+               wwin->flags.old_maximized |= MAX_MAXIMUS;
        }
 
        wWindowConstrainSize(wwin, &new_width, &new_height);
@@ -395,6 +457,79 @@ void wMaximizeWindow(WWindow *wwin, int directions)
 
        /* set maximization state */
        wwin->flags.maximized = directions;
+       if ((wwin->flags.old_maximized & MAX_MAXIMUS) && !wwin->flags.maximized)
+               wwin->flags.maximized = MAX_MAXIMUS;
+}
+
+/* generic (un)maximizer */
+void handleMaximize(WWindow *wwin, int directions)
+{
+       int current = wwin->flags.maximized;
+       int requested = directions & (MAX_HORIZONTAL | MAX_VERTICAL | 
MAX_LEFTHALF | MAX_RIGHTHALF | MAX_MAXIMUS);
+       int effective = requested ^ current;
+       int flags = directions & ~requested;
+
+       if (!effective) {
+               /* allow wMaximizeWindow to restore the Maximusized size */
+               if ((wwin->flags.old_maximized & MAX_MAXIMUS) &&
+                   !(requested & MAX_MAXIMUS))
+                       wMaximizeWindow(wwin, flags);
+               else
+                       wUnmaximizeWindow(wwin);
+       }
+       else {
+               /* MAX_MAXIMUS takes precedence */
+               effective &= ~MAX_MAXIMUS;
+               if (requested & MAX_MAXIMUS) {
+                       /* window was previously Maximusized then maximized */
+                       if ((wwin->flags.old_maximized & MAX_MAXIMUS) && 
!current) {
+                               wUnmaximizeWindow(wwin);
+                               return;
+                       }
+                       else
+                               effective = MAX_MAXIMUS;
+               }
+               else if (requested == (MAX_HORIZONTAL | MAX_VERTICAL))
+                       effective = requested;
+               else {
+                       /* handle MAX_HORIZONTAL -> MAX_(LEFT|RIGHT)HALF */
+                       if (IS_MAX_HORIZONTALLY(current)) {
+                               if (IS_MAX_HORIZONTALLY(requested)) {
+                                       effective &= ~(MAX_HORIZONTAL | 
MAX_LEFTHALF | MAX_RIGHTHALF);
+                                       effective |= (requested & 
(MAX_HORIZONTAL | MAX_LEFTHALF | MAX_RIGHTHALF));
+                                       if (requested & MAX_HORIZONTAL) {
+                                               /* restore to half maximization 
*/
+                                               if (wwin->flags.old_maximized & 
MAX_LEFTHALF)
+                                                       effective |= 
MAX_LEFTHALF;
+                                               else if 
(wwin->flags.old_maximized & MAX_RIGHTHALF)
+                                                       effective |= 
MAX_RIGHTHALF;
+                                       }
+                                       /* MAX_VERTICAL is implicit with 
MAX_(LEFT|RIGHT)HALF */
+                                       else
+                                               effective |= MAX_VERTICAL;
+                               } else {
+                                       /* toggling MAX_VERTICAL */
+                                       if ((requested & MAX_VERTICAL) &&
+                                           (current & MAX_VERTICAL)) {
+                                               effective &= ~(MAX_LEFTHALF | 
MAX_RIGHTHALF | MAX_VERTICAL);
+                                       }
+                               }
+                       }
+                       /* handle MAX_VERTICAL -> MAX_(LEFT|RIGHT)HALF */
+                       if (current & MAX_VERTICAL) {
+                               if ((requested & MAX_LEFTHALF) ||
+                                   (requested & MAX_RIGHTHALF)) {
+                                       effective |= MAX_VERTICAL;
+                               }
+                       }
+                       /* toggling MAX_HORIZONTAL */
+                       if ((requested & MAX_HORIZONTAL) &&
+                           (current & MAX_HORIZONTAL))
+                               effective &= ~MAX_HORIZONTAL;
+               }
+               wMaximizeWindow(wwin, effective | flags);
+       }
+       return;
 }
 
 /* the window boundary coordinates */
@@ -434,7 +569,14 @@ static void find_Maximus_geometry(WWindow *wwin, WArea 
usableArea, int *new_x, i
        win_coords obs, orig, new;
 
        /* set the original coordinate positions of the window to be 
Maximumized */
-       set_window_coords(wwin, &orig);
+       if (wwin->flags.maximized) {
+               /* window is already maximized; consider original geometry */
+               remember_geometry(wwin, &orig.left, &orig.top, &orig.width, 
&orig.height);
+               orig.bottom = orig.top + orig.height;
+               orig.right = orig.left + orig.width;
+       }
+       else
+               set_window_coords(wwin, &orig);
 
        /* Try to fully maximize first, then readjust later */
        new.left    = usableArea.x1;
@@ -520,9 +662,6 @@ static void find_Maximus_geometry(WWindow *wwin, WArea 
usableArea, int *new_x, i
 void wUnmaximizeWindow(WWindow *wwin)
 {
        int x, y, w, h;
-        WMRect old_geom_rect;
-        int old_head;
-       Bool same_head;
 
        if (!wwin->flags.maximized)
                return;
@@ -532,15 +671,10 @@ void wUnmaximizeWindow(WWindow *wwin)
                wUnshadeWindow(wwin);
        }
        /* Use old coordinates if they are set, current values otherwise */
-        old_geom_rect = wmkrect(wwin->old_geometry.x, wwin->old_geometry.y, 
wwin->old_geometry.width, wwin->old_geometry.height);
-       old_head = wGetHeadForRect(wwin->screen_ptr, old_geom_rect);
-       same_head = (wGetHeadForWindow(wwin) == old_head);
-       x = (wwin->old_geometry.x && same_head) ? wwin->old_geometry.x : 
wwin->frame_x;
-       y = (wwin->old_geometry.y && same_head) ? wwin->old_geometry.y : 
wwin->frame_y;
-       w = wwin->old_geometry.width ? wwin->old_geometry.width : 
wwin->client.width;
-       h = wwin->old_geometry.height ? wwin->old_geometry.height : 
wwin->client.height;
+       remember_geometry(wwin, &x, &y, &w, &h);
 
        wwin->flags.maximized = 0;
+       wwin->flags.old_maximized = 0;
        wWindowConfigure(wwin, x, y, w, h);
        wWindowSynthConfigureNotify(wwin);
 
diff --git a/src/actions.h b/src/actions.h
index da196ae..719df2e 100644
--- a/src/actions.h
+++ b/src/actions.h
@@ -51,6 +51,7 @@ void wUnselectWindows(WScreen *scr);
 
 void wMaximizeWindow(WWindow *wwin, int directions);
 void wUnmaximizeWindow(WWindow *wwin);
+void handleMaximize(WWindow *wwin, int directions);
 
 void wHideAll(WScreen *src);
 void wHideOtherApplications(WWindow *wwin);
diff --git a/src/event.c b/src/event.c
index 2eb0248..8e950a2 100644
--- a/src/event.c
+++ b/src/event.c
@@ -1444,60 +1444,42 @@ static void handleKeyPress(XEvent * event)
                if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
                        CloseWindowMenu(scr);
 
-                       if (wwin->flags.maximized == (MAX_VERTICAL | 
MAX_HORIZONTAL))
-                               wUnmaximizeWindow(wwin);
-                       else
-                               wMaximizeWindow(wwin, MAX_VERTICAL | 
MAX_HORIZONTAL | MAX_KEYBOARD);
+                       handleMaximize(wwin, MAX_VERTICAL | MAX_HORIZONTAL | 
MAX_KEYBOARD);
                }
                break;
        case WKBD_VMAXIMIZE:
                if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
                        CloseWindowMenu(scr);
 
-                       if (wwin->flags.maximized == MAX_VERTICAL)
-                               wUnmaximizeWindow(wwin);
-                       else
-                               wMaximizeWindow(wwin, MAX_VERTICAL | 
MAX_KEYBOARD);
+                       handleMaximize(wwin, MAX_VERTICAL | MAX_KEYBOARD);
                }
                break;
        case WKBD_HMAXIMIZE:
                if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
                        CloseWindowMenu(scr);
 
-                       if (wwin->flags.maximized == MAX_HORIZONTAL)
-                               wUnmaximizeWindow(wwin);
-                       else
-                               wMaximizeWindow(wwin, MAX_HORIZONTAL | 
MAX_KEYBOARD);
+                       handleMaximize(wwin, MAX_HORIZONTAL | MAX_KEYBOARD);
                }
                break;
        case WKBD_LHMAXIMIZE:
                if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
                        CloseWindowMenu(scr);
 
-                       if (wwin->flags.maximized == (MAX_VERTICAL | 
MAX_LEFTHALF))
-                               wUnmaximizeWindow(wwin);
-                       else
-                               wMaximizeWindow(wwin, MAX_VERTICAL | 
MAX_LEFTHALF | MAX_KEYBOARD);
+                       handleMaximize(wwin, MAX_VERTICAL | MAX_LEFTHALF | 
MAX_KEYBOARD);
                }
                break;
        case WKBD_RHMAXIMIZE:
                if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
                        CloseWindowMenu(scr);
 
-                       if (wwin->flags.maximized == (MAX_VERTICAL | 
MAX_RIGHTHALF))
-                               wUnmaximizeWindow(wwin);
-                       else
-                               wMaximizeWindow(wwin, MAX_VERTICAL | 
MAX_RIGHTHALF | MAX_KEYBOARD);
+                       handleMaximize(wwin, MAX_VERTICAL | MAX_RIGHTHALF | 
MAX_KEYBOARD);
                }
                break;
        case WKBD_MAXIMUS:
                if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
                        CloseWindowMenu(scr);
 
-                       if (wwin->flags.maximized == MAX_MAXIMUS)
-                               wUnmaximizeWindow(wwin);
-                       else
-                               wMaximizeWindow(wwin, MAX_MAXIMUS | 
MAX_KEYBOARD);
+                       handleMaximize(wwin, MAX_MAXIMUS | MAX_KEYBOARD);
                }
                break;
        case WKBD_RAISE:
diff --git a/src/window.h b/src/window.h
index edebe80..28adcc5 100644
--- a/src/window.h
+++ b/src/window.h
@@ -248,6 +248,7 @@ typedef struct WWindow {
         unsigned int hidden:1;
         unsigned int shaded:1;
        unsigned int maximized:5;
+       unsigned int old_maximized:5;
         unsigned int fullscreen:1;
         unsigned int omnipresent:1;
 
-- 
1.7.7.6

Reply via email to