]> git.mdlowis.com Git - proto/labwc.git/commitdiff
view: implement separate horizontal/vertical maximize
authorJohn Lindgren <john@jlindgren.net>
Thu, 26 Oct 2023 04:38:29 +0000 (00:38 -0400)
committerConsolatis <35009135+Consolatis@users.noreply.github.com>
Sat, 28 Oct 2023 20:46:49 +0000 (22:46 +0200)
This is a useful (if lesser-known) feature of at least a few popular X11
window managers, for example Openbox and XFWM4. Typically right-click on
the maximize button toggles horizontal maximize, while middle-click
toggles vertical maximize.

Support in labwc uses the same configuration syntax as Openbox, where the
Maximize/ToggleMaximize actions have an optional "direction" argument:
horizontal, vertical, or both (default). The default mouse bindings match
the XFWM4 defaults (not sure what Openbox has by default).

Most of the external protocols still assume "maximized" is a Boolean,
which is no longer true internally. For the sake of the outside world,
a view is only "maximized" if maximized in both directions.

Internally, I've taken the following approach:

- SSD code decorates the view as "maximized" (i.e. hiding borders) only
  if maximized in both directions.

- Layout code (interactive move/resize, tiling, etc.) generally treats
  the view as "maximized" (with the restrictions that entails) if
  maximized in either direction. For example, moving a vertically-
  maximized view first restores the natural geometry (this differs from
  Openbox, which instead allows the view to move only horizontally.)

v2: use enum view_axis for view->maximized
v3:
  - update docs
  - allow resizing if partly maximized
  - add TODOs & corrections noted by Consolatis

15 files changed:
docs/labwc-actions.5.scd
docs/rc.xml.all
include/view.h
src/action.c
src/config/rcxml.c
src/foreign.c
src/interactive.c
src/snap.c
src/ssd/ssd.c
src/ssd/ssd_border.c
src/ssd/ssd_extents.c
src/ssd/ssd_titlebar.c
src/view.c
src/xdg.c
src/xwayland.c

index d9defff1e351eea82cc4c216d27c6fad17c996bd..1a65bcdab0881a96d0fef0e049d0bfbecf44bb72 100644 (file)
@@ -112,11 +112,13 @@ Actions are used in menus and keyboard/mouse bindings.
 *<action name="ToggleFullscreen" />*
        Toggle fullscreen state of focused window.
 
-*<action name="ToggleMaximize" />*
-       Toggle maximize state of focused window.
+*<action name="ToggleMaximize" direction="value" />*
+       Toggle maximize state of focused window. Supported directions are
+       "both" (default), "horizontal", and "vertical".
 
-*<action name="Maximize" />*
-       Maximize focused window.
+*<action name="Maximize" direction="value" />*
+       Maximize focused window. Supported directions are "both" (default),
+       "horizontal", and "vertical".
 
 *<action name="ToggleAlwaysOnTop" />*
        Toggle always-on-top of focused window.
index 7a44ea39d039d52ada0233a64e6df161656c17fa..973a97011e0f447fb416b6dfdd840c8030496a4f 100644 (file)
       <mousebind button="Right" action="Click">
         <action name="Focus" />
         <action name="Raise" />
-        <action name="ShowMenu" menu="client-menu" />
       </mousebind>
     </context>
 
       <mousebind button="Left" action="DoubleClick">
         <action name="ToggleMaximize" />
       </mousebind>
+      <mousebind button="Right" action="Click">
+        <action name="ShowMenu" menu="client-menu" />
+      </mousebind>
     </context>
 
     <context name="Maximize">
       <mousebind button="Left" action="Click">
-        <action name="Focus" />
-        <action name="Raise" />
         <action name="ToggleMaximize" />
       </mousebind>
+      <mousebind button="Right" action="Click">
+        <action name="ToggleMaximize" direction="horizontal" />
+      </mousebind>
+      <mousebind button="Middle" action="Click">
+        <action name="ToggleMaximize" direction="vertical" />
+      </mousebind>
     </context>
 
     <context name="WindowMenu">
       <mousebind button="Left" action="Click">
         <action name="ShowMenu" menu="client-menu" />
       </mousebind>
+      <mousebind button="Right" action="Click">
+        <action name="ShowMenu" menu="client-menu" />
+      </mousebind>
     </context>
 
     <context name="Iconify">
index 471e52dd38ecf82990747ad07cad597bd8f4d008..b2d8ce317d6e081f1c8818ef03376790cccba967 100644 (file)
@@ -29,6 +29,18 @@ enum ssd_preference {
        LAB_SSD_PREF_SERVER,
 };
 
+/**
+ * Directions in which a view can be maximized. "None" is used
+ * internally to mean "not maximized" but is not valid in rc.xml.
+ * Therefore when parsing rc.xml, "None" means "Invalid".
+ */
+enum view_axis {
+       VIEW_AXIS_NONE = 0,
+       VIEW_AXIS_HORIZONTAL = (1 << 0),
+       VIEW_AXIS_VERTICAL = (1 << 1),
+       VIEW_AXIS_BOTH = (VIEW_AXIS_HORIZONTAL | VIEW_AXIS_VERTICAL),
+};
+
 enum view_edge {
        VIEW_EDGE_INVALID = 0,
 
@@ -127,7 +139,7 @@ struct view {
        bool ssd_titlebar_hidden;
        enum ssd_preference ssd_preference;
        bool minimized;
-       bool maximized;
+       enum view_axis maximized;
        bool fullscreen;
        uint32_t tiled;  /* private, enum view_edge in src/view.c */
        bool inhibits_keybinds;
@@ -353,10 +365,10 @@ void view_store_natural_geometry(struct view *view);
 void view_center(struct view *view, const struct wlr_box *ref);
 void view_restore_to(struct view *view, struct wlr_box geometry);
 void view_set_untiled(struct view *view);
-void view_maximize(struct view *view, bool maximize,
+void view_maximize(struct view *view, enum view_axis axis,
        bool store_natural_geometry);
 void view_set_fullscreen(struct view *view, bool fullscreen);
-void view_toggle_maximize(struct view *view);
+void view_toggle_maximize(struct view *view, enum view_axis axis);
 void view_toggle_decorations(struct view *view);
 
 bool view_is_always_on_top(struct view *view);
@@ -400,6 +412,7 @@ void view_evacuate_region(struct view *view);
 void view_on_output_destroy(struct view *view);
 void view_destroy(struct view *view);
 
+enum view_axis view_axis_parse(const char *direction);
 enum view_edge view_edge_parse(const char *direction);
 
 /* xdg.c */
index 8b67403fbe3e7685ca1d29b1142631234cdf0d2e..26d609bb06601b0883a6b392071b58edd1d0a632 100644 (file)
@@ -296,6 +296,19 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
                        goto cleanup;
                }
                break;
+       case ACTION_TYPE_TOGGLE_MAXIMIZE:
+       case ACTION_TYPE_MAXIMIZE:
+               if (!strcmp(argument, "direction")) {
+                       enum view_axis axis = view_axis_parse(content);
+                       if (axis == VIEW_AXIS_NONE) {
+                               wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
+                                       action_names[action->type], argument, content);
+                       } else {
+                               action_arg_add_int(action, argument, axis);
+                       }
+                       goto cleanup;
+               }
+               break;
        case ACTION_TYPE_RESIZE_RELATIVE:
                if (!strcmp(argument, "left") || !strcmp(argument, "right") ||
                                !strcmp(argument, "top") || !strcmp(argument, "bottom")) {
@@ -694,12 +707,17 @@ actions_run(struct view *activator, struct server *server,
                        break;
                case ACTION_TYPE_TOGGLE_MAXIMIZE:
                        if (view) {
-                               view_toggle_maximize(view);
+                               enum view_axis axis = action_get_int(action,
+                                       "direction", VIEW_AXIS_BOTH);
+                               view_toggle_maximize(view, axis);
                        }
                        break;
                case ACTION_TYPE_MAXIMIZE:
                        if (view) {
-                               view_maximize(view, true, /*store_natural_geometry*/ true);
+                               enum view_axis axis = action_get_int(action,
+                                       "direction", VIEW_AXIS_BOTH);
+                               view_maximize(view, axis,
+                                       /*store_natural_geometry*/ true);
                        }
                        break;
                case ACTION_TYPE_TOGGLE_FULLSCREEN:
index 0f8547ba05cd9d60c08d5f724c2c4994b099a9dc..b834ea5d09616ce88920ccd457da3500a621b6d1 100644 (file)
@@ -1064,11 +1064,14 @@ static struct mouse_combos {
        { "Title", "Left", "DoubleClick", "ToggleMaximize", NULL, NULL },
        { "TitleBar", "Right", "Click", "Focus", NULL, NULL},
        { "TitleBar", "Right", "Click", "Raise", NULL, NULL},
-       { "TitleBar", "Right", "Click", "ShowMenu", "menu", "client-menu"},
+       { "Title", "Right", "Click", "ShowMenu", "menu", "client-menu"},
        { "Close", "Left", "Click", "Close", NULL, NULL },
        { "Iconify", "Left", "Click", "Iconify", NULL, NULL},
        { "Maximize", "Left", "Click", "ToggleMaximize", NULL, NULL},
+       { "Maximize", "Right", "Click", "ToggleMaximize", "direction", "horizontal"},
+       { "Maximize", "Middle", "Click", "ToggleMaximize", "direction", "vertical"},
        { "WindowMenu", "Left", "Click", "ShowMenu", "menu", "client-menu"},
+       { "WindowMenu", "Right", "Click", "ShowMenu", "menu", "client-menu"},
        { "Root", "Left", "Press", "ShowMenu", "menu", "root-menu"},
        { "Root", "Right", "Press", "ShowMenu", "menu", "root-menu"},
        { "Root", "Middle", "Press", "ShowMenu", "menu", "root-menu"},
index e71292d79993e1d38c761565d711797261cc8221..092752b3c8abbb5e9f07910af8d817507657a296 100644 (file)
@@ -17,7 +17,8 @@ handle_request_maximize(struct wl_listener *listener, void *data)
 {
        struct view *view = wl_container_of(listener, view, toplevel.maximize);
        struct wlr_foreign_toplevel_handle_v1_maximized_event *event = data;
-       view_maximize(view, event->maximized, /*store_natural_geometry*/ true);
+       view_maximize(view, event->maximized ? VIEW_AXIS_BOTH : VIEW_AXIS_NONE,
+               /*store_natural_geometry*/ true);
 }
 
 static void
index 3caac79e2f79526012d06ce6eaf1446ae2094209..0476f5fb650060d2702d249db1657997f865a574 100644 (file)
@@ -45,7 +45,7 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges)
                         */
                        return;
                }
-               if (view->maximized || view_is_tiled(view)) {
+               if (!view_is_floating(view)) {
                        /*
                         * Un-maximize and restore natural width/height.
                         * Don't reset tiled state yet since we may want
@@ -71,18 +71,20 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges)
                cursor_set(seat, LAB_CURSOR_GRAB);
                break;
        case LAB_INPUT_STATE_RESIZE:
-               if (view->maximized || view->fullscreen) {
+               if (view->fullscreen || view->maximized == VIEW_AXIS_BOTH) {
                        /*
-                        * We don't allow resizing while maximized or
-                        * fullscreen.
+                        * We don't allow resizing while fullscreen or
+                        * maximized in both directions.
                         */
                        return;
                }
                /*
-                * Reset tiled state but keep the same geometry as the
-                * starting point for the resize.
+                * If tiled or maximized in only one direction, reset
+                * tiled/maximized state but keep the same geometry as
+                * the starting point for the resize.
                 */
                view_set_untiled(view);
+               view_restore_to(view, view->pending);
                cursor_set(seat, cursor_get_from_edge(edges));
                break;
        default:
@@ -130,7 +132,7 @@ snap_to_edge(struct view *view)
                        /*store_natural_geometry*/ false);
        } else if (cursor_y <= area->y + snap_range) {
                if (rc.snap_top_maximize) {
-                       view_maximize(view, true,
+                       view_maximize(view, VIEW_AXIS_BOTH,
                                /*store_natural_geometry*/ false);
                } else {
                        view_snap_to_edge(view, VIEW_EDGE_UP,
index b403898d3339ee0ccb0b536b306b7385c139d618..698cbf3b57dcc37310cc23920bbaab8976662e38 100644 (file)
@@ -102,7 +102,8 @@ _snap_next_edge(struct view *view, int start_pos, const struct snap_search def,
        struct view *v;
        int p = max;
        for_each_view(v, &server->views, LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) {
-               if (v == view || v->output != output || v->minimized || v->maximized) {
+               if (v == view || v->output != output || v->minimized
+                               || v->maximized == VIEW_AXIS_BOTH) {
                        continue;
                }
 
index ef821b86a029f1c84d9b58abe45d1ec4856491c9..4580ec2f7b5b158bd41dea267be7fa0a70ed1e60 100644 (file)
@@ -35,7 +35,7 @@ ssd_thickness(struct view *view)
 
        struct theme *theme = view->server->theme;
 
-       if (view->maximized) {
+       if (view->maximized == VIEW_AXIS_BOTH) {
                struct border thickness = { 0 };
                if (!view->ssd_titlebar_hidden) {
                        thickness.top += theme->title_height;
@@ -226,7 +226,8 @@ ssd_update_geometry(struct ssd *ssd)
                        ssd_extents_update(ssd);
                        ssd->state.geometry = current;
                }
-               if (ssd->state.squared_corners != ssd->view->maximized) {
+               bool maximized = (ssd->view->maximized == VIEW_AXIS_BOTH);
+               if (ssd->state.squared_corners != maximized) {
                        ssd_border_update(ssd);
                        ssd_titlebar_update(ssd);
                }
index 6512ab8219ba25ae6dc47b375da8e797a84becf1..40be4f39ba3a31e5eadbe489678590533c91d7f0 100644 (file)
@@ -53,7 +53,7 @@ ssd_border_create(struct ssd *ssd)
                        -(ssd->titlebar.height + theme->border_width), color);
        } FOR_EACH_END
 
-       if (view->maximized) {
+       if (view->maximized == VIEW_AXIS_BOTH) {
                wlr_scene_node_set_enabled(&ssd->border.tree->node, false);
        }
 }
@@ -65,13 +65,14 @@ ssd_border_update(struct ssd *ssd)
        assert(ssd->border.tree);
 
        struct view *view = ssd->view;
-       if (view->maximized && ssd->border.tree->node.enabled) {
+       if (view->maximized == VIEW_AXIS_BOTH
+                       && ssd->border.tree->node.enabled) {
                /* Disable borders on maximize */
                wlr_scene_node_set_enabled(&ssd->border.tree->node, false);
                ssd->margin = ssd_thickness(ssd->view);
        }
 
-       if (view->maximized) {
+       if (view->maximized == VIEW_AXIS_BOTH) {
                return;
        } else if (!ssd->border.tree->node.enabled) {
                /* And re-enabled them when unmaximized */
index 1b6f465dcf72f9f2a4f4b134529618e535688f61..05869e2129fb0cd3db4776b75aae18a203b1453a 100644 (file)
@@ -35,7 +35,7 @@ ssd_extents_create(struct ssd *ssd)
 
        ssd->extents.tree = wlr_scene_tree_create(ssd->tree);
        struct wlr_scene_tree *parent = ssd->extents.tree;
-       if (view->maximized || view->fullscreen) {
+       if (view->fullscreen || view->maximized == VIEW_AXIS_BOTH) {
                wlr_scene_node_set_enabled(&parent->node, false);
        }
        wl_list_init(&ssd->extents.parts);
@@ -89,7 +89,7 @@ void
 ssd_extents_update(struct ssd *ssd)
 {
        struct view *view = ssd->view;
-       if (view->maximized || view->fullscreen) {
+       if (view->fullscreen || view->maximized == VIEW_AXIS_BOTH) {
                wlr_scene_node_set_enabled(&ssd->extents.tree->node, false);
                return;
        }
index b0aaa2d0ac3918b6b46e8b3f7a5719bff6b3a78a..62ac232cb7dd98a27130ddf0eef7a6e6a1bdd463 100644 (file)
@@ -86,8 +86,8 @@ ssd_titlebar_create(struct ssd *ssd)
 
        ssd_update_title(ssd);
 
-       if (view->maximized) {
-               set_squared_corners(ssd, view->maximized);
+       if (view->maximized == VIEW_AXIS_BOTH) {
+               set_squared_corners(ssd, true);
        }
 }
 
@@ -133,8 +133,9 @@ ssd_titlebar_update(struct ssd *ssd)
        int width = view->current.width;
        struct theme *theme = view->server->theme;
 
-       if (view->maximized != ssd->state.squared_corners) {
-               set_squared_corners(ssd, view->maximized);
+       bool maximized = (view->maximized == VIEW_AXIS_BOTH);
+       if (ssd->state.squared_corners != maximized) {
+               set_squared_corners(ssd, maximized);
        }
 
        if (width == ssd->state.geometry.width) {
index 106a2aa7b157d8ee24ee1038df219fd1efec392a..060816da7eeeb42d626c4805e0ae0360d8fc75b6 100644 (file)
@@ -337,7 +337,7 @@ void
 view_resize_relative(struct view *view, int left, int right, int top, int bottom)
 {
        assert(view);
-       if (view->fullscreen || view->maximized) {
+       if (view->fullscreen || view->maximized != VIEW_AXIS_NONE) {
                return;
        }
        struct wlr_box newgeo = view->pending;
@@ -356,7 +356,7 @@ view_move_relative(struct view *view, int x, int y)
        if (view->fullscreen) {
                return;
        }
-       view_maximize(view, false, /*store_natural_geometry*/ false);
+       view_maximize(view, VIEW_AXIS_NONE, /*store_natural_geometry*/ false);
        if (view_is_tiled(view)) {
                view_set_untiled(view);
                view_restore_to(view, view->natural_geometry);
@@ -373,12 +373,8 @@ view_move_to_cursor(struct view *view)
        if (!output_is_usable(pending_output)) {
                return;
        }
-       if (view->fullscreen) {
-               view_set_fullscreen(view, false);
-       }
-       if (view->maximized) {
-               view_maximize(view, false, /*store_natural_geometry*/ false);
-       }
+       view_set_fullscreen(view, false);
+       view_maximize(view, VIEW_AXIS_NONE, /*store_natural_geometry*/ false);
        if (view_is_tiled(view)) {
                view_set_untiled(view);
                view_restore_to(view, view->natural_geometry);
@@ -739,7 +735,7 @@ static void
 view_apply_maximized_geometry(struct view *view)
 {
        assert(view);
-       assert(view->maximized);
+       assert(view->maximized != VIEW_AXIS_NONE);
        struct output *output = view->output;
        assert(output_is_usable(output));
 
@@ -753,6 +749,23 @@ view_apply_maximized_geometry(struct view *view)
                box.width /= output->wlr_output->scale;
        }
 
+       /*
+        * If one axis (horizontal or vertical) is unmaximized, it
+        * should use the natural geometry. But if that geometry is not
+        * on-screen on the output where the view is maximized, then
+        * center the unmaximized axis.
+        */
+       struct wlr_box natural = view->natural_geometry;
+       if (view->maximized != VIEW_AXIS_BOTH) {
+               struct wlr_box intersect;
+               wlr_box_intersection(&intersect, &box, &natural);
+               if (wlr_box_empty(&intersect)) {
+                       view_compute_centered_position(view, NULL,
+                               natural.width, natural.height,
+                               &natural.x, &natural.y);
+               }
+       }
+
        if (view->ssd_enabled) {
                struct border border = ssd_thickness(view);
                box.x += border.left;
@@ -760,6 +773,15 @@ view_apply_maximized_geometry(struct view *view)
                box.width -= border.right + border.left;
                box.height -= border.top + border.bottom;
        }
+
+       if (view->maximized == VIEW_AXIS_VERTICAL) {
+               box.x = natural.x;
+               box.width = natural.width;
+       } else if (view->maximized == VIEW_AXIS_HORIZONTAL) {
+               box.y = natural.y;
+               box.height = natural.height;
+       }
+
        view_move_resize(view, box);
 }
 
@@ -775,7 +797,7 @@ view_apply_special_geometry(struct view *view)
 
        if (view->fullscreen) {
                view_apply_fullscreen_geometry(view);
-       } else if (view->maximized) {
+       } else if (view->maximized != VIEW_AXIS_NONE) {
                view_apply_maximized_geometry(view);
        } else if (view->tiled) {
                view_apply_tiled_geometry(view);
@@ -788,14 +810,14 @@ view_apply_special_geometry(struct view *view)
 
 /* For internal use only. Does not update geometry. */
 static void
-set_maximized(struct view *view, bool maximized)
+set_maximized(struct view *view, enum view_axis maximized)
 {
        if (view->impl->maximize) {
-               view->impl->maximize(view, maximized);
+               view->impl->maximize(view, (maximized == VIEW_AXIS_BOTH));
        }
        if (view->toplevel.handle) {
                wlr_foreign_toplevel_handle_v1_set_maximized(
-                       view->toplevel.handle, maximized);
+                       view->toplevel.handle, (maximized == VIEW_AXIS_BOTH));
        }
        view->maximized = maximized;
 
@@ -818,8 +840,8 @@ view_restore_to(struct view *view, struct wlr_box geometry)
        if (view->fullscreen) {
                return;
        }
-       if (view->maximized) {
-               set_maximized(view, false);
+       if (view->maximized != VIEW_AXIS_NONE) {
+               set_maximized(view, VIEW_AXIS_NONE);
        }
        view_move_resize(view, geometry);
 }
@@ -836,8 +858,8 @@ bool
 view_is_floating(struct view *view)
 {
        assert(view);
-       return !(view->fullscreen || view->maximized || view->tiled
-               || view->tiled_region || view->tiled_region_evacuate);
+       return !(view->fullscreen || (view->maximized != VIEW_AXIS_NONE)
+               || view_is_tiled(view));
 }
 
 /* Reset tiled state of view without changing geometry */
@@ -851,27 +873,28 @@ view_set_untiled(struct view *view)
 }
 
 void
-view_maximize(struct view *view, bool maximize, bool store_natural_geometry)
+view_maximize(struct view *view, enum view_axis axis,
+               bool store_natural_geometry)
 {
        assert(view);
-       if (view->maximized == maximize) {
+       if (view->maximized == axis) {
                return;
        }
        if (view->fullscreen) {
                return;
        }
-       if (maximize) {
+       if (axis != VIEW_AXIS_NONE) {
                /*
                 * Maximize via keybind or client request cancels
                 * interactive move/resize since we can't move/resize
                 * a maximized view.
                 */
                interactive_cancel(view);
-               if (store_natural_geometry) {
+               if (store_natural_geometry && view_is_floating(view)) {
                        view_store_natural_geometry(view);
                }
        }
-       set_maximized(view, maximize);
+       set_maximized(view, axis);
        if (view_is_floating(view)) {
                view_apply_natural_geometry(view);
        } else {
@@ -880,11 +903,28 @@ view_maximize(struct view *view, bool maximize, bool store_natural_geometry)
 }
 
 void
-view_toggle_maximize(struct view *view)
+view_toggle_maximize(struct view *view, enum view_axis axis)
 {
        assert(view);
-       view_maximize(view, !view->maximized,
-               /*store_natural_geometry*/ true);
+       switch (axis) {
+       case VIEW_AXIS_HORIZONTAL:
+       case VIEW_AXIS_VERTICAL:
+               /* Toggle one axis (XOR) */
+               view_maximize(view, view->maximized ^ axis,
+                       /*store_natural_geometry*/ true);
+               break;
+       case VIEW_AXIS_BOTH:
+               /*
+                * Maximize in both directions if unmaximized or partially
+                * maximized, otherwise unmaximize.
+                */
+               view_maximize(view, (view->maximized == VIEW_AXIS_BOTH) ?
+                       VIEW_AXIS_NONE : VIEW_AXIS_BOTH,
+                       /*store_natural_geometry*/ true);
+               break;
+       default:
+               break;
+       }
 }
 
 void
@@ -1167,7 +1207,8 @@ void
 view_grow_to_edge(struct view *view, enum view_edge direction)
 {
        assert(view);
-       if (view->fullscreen || view->maximized) {
+       /* TODO: allow grow to edge if maximized along the other axis */
+       if (view->fullscreen || view->maximized != VIEW_AXIS_NONE) {
                return;
        }
        if (!output_is_usable(view->output)) {
@@ -1184,7 +1225,8 @@ void
 view_shrink_to_edge(struct view *view, enum view_edge direction)
 {
        assert(view);
-       if (view->fullscreen || view->maximized) {
+       /* TODO: allow shrink to edge if maximized along the other axis */
+       if (view->fullscreen || view->maximized != VIEW_AXIS_NONE) {
                return;
        }
        if (!output_is_usable(view->output)) {
@@ -1197,6 +1239,23 @@ view_shrink_to_edge(struct view *view, enum view_edge direction)
        view_move_resize(view, geo);
 }
 
+enum view_axis
+view_axis_parse(const char *direction)
+{
+       if (!direction) {
+               return VIEW_AXIS_NONE;
+       }
+       if (!strcasecmp(direction, "horizontal")) {
+               return VIEW_AXIS_HORIZONTAL;
+       } else if (!strcasecmp(direction, "vertical")) {
+               return VIEW_AXIS_VERTICAL;
+       } else if (!strcasecmp(direction, "both")) {
+               return VIEW_AXIS_BOTH;
+       } else {
+               return VIEW_AXIS_NONE;
+       }
+}
+
 enum view_edge
 view_edge_parse(const char *direction)
 {
@@ -1231,7 +1290,7 @@ view_snap_to_edge(struct view *view, enum view_edge edge, bool store_natural_geo
                return;
        }
 
-       if (view->tiled == edge && !view->maximized) {
+       if (view->tiled == edge && view->maximized == VIEW_AXIS_NONE) {
                /* We are already tiled for this edge and thus should switch outputs */
                struct wlr_output *new_output = NULL;
                struct wlr_output *current_output = output->wlr_output;
@@ -1285,9 +1344,10 @@ view_snap_to_edge(struct view *view, enum view_edge edge, bool store_natural_geo
                }
        }
 
-       if (view->maximized) {
+       if (view->maximized != VIEW_AXIS_NONE) {
                /* Unmaximize + keep using existing natural_geometry */
-               view_maximize(view, false, /*store_natural_geometry*/ false);
+               view_maximize(view, VIEW_AXIS_NONE,
+                       /*store_natural_geometry*/ false);
        } else if (store_natural_geometry) {
                /* store current geometry as new natural_geometry */
                view_store_natural_geometry(view);
@@ -1313,9 +1373,10 @@ view_snap_to_region(struct view *view, struct region *region,
                return;
        }
 
-       if (view->maximized) {
+       if (view->maximized != VIEW_AXIS_NONE) {
                /* Unmaximize + keep using existing natural_geometry */
-               view_maximize(view, false, /*store_natural_geometry*/ false);
+               view_maximize(view, VIEW_AXIS_NONE,
+                       /*store_natural_geometry*/ false);
        } else if (store_natural_geometry) {
                /* store current geometry as new natural_geometry */
                view_store_natural_geometry(view);
index f63647e17150ecb061016110776d35b85f939e16..dc9be3347ad170863ccecb7b5a27803af52cc669 100644 (file)
--- a/src/xdg.c
+++ b/src/xdg.c
@@ -226,7 +226,8 @@ handle_request_maximize(struct wl_listener *listener, void *data)
        if (!view->mapped && !view->output) {
                view_set_output(view, output_nearest_to_cursor(view->server));
        }
-       view_maximize(view, xdg_toplevel_from_view(view)->requested.maximized,
+       bool maximized = xdg_toplevel_from_view(view)->requested.maximized;
+       view_maximize(view, maximized ? VIEW_AXIS_BOTH : VIEW_AXIS_NONE,
                /*store_natural_geometry*/ true);
 }
 
@@ -503,12 +504,10 @@ xdg_toplevel_view_map(struct view *view)
                        position_xdg_toplevel_view(view);
                }
 
-               if (!view->fullscreen && requested->fullscreen) {
-                       set_fullscreen_from_request(view, requested);
-               } else if (!view->maximized && requested->maximized) {
-                       view_maximize(view, true,
-                               /*store_natural_geometry*/ true);
-               }
+               set_fullscreen_from_request(view, requested);
+               view_maximize(view, requested->maximized ?
+                       VIEW_AXIS_BOTH : VIEW_AXIS_NONE,
+                       /*store_natural_geometry*/ true);
 
                /*
                 * Set initial "current" position directly before
index 020c1ecea1a3f6c528b9df958571dadb26ad1baa..6e4fae7f58f809d8e16ef07186bd5adff8783c98 100644 (file)
@@ -351,7 +351,7 @@ handle_request_maximize(struct wl_listener *listener, void *data)
                view_set_decorations(view,
                        want_deco(xwayland_surface_from_view(view)));
        }
-       view_toggle_maximize(view);
+       view_toggle_maximize(view, VIEW_AXIS_BOTH);
 }
 
 static void
@@ -540,14 +540,17 @@ xwayland_view_map(struct view *view)
         *   1. set fullscreen state
         *   2. set decorations (depends on fullscreen state)
         *   3. set maximized (geometry depends on decorations)
-        *
-        * TODO: support separate horizontal/vertical maximize
         */
-       bool maximize = xwayland_surface->maximized_horz
-               && xwayland_surface->maximized_vert;
        view_set_fullscreen(view, xwayland_surface->fullscreen);
        view_set_decorations(view, want_deco(xwayland_surface));
-       view_maximize(view, maximize, /*store_natural_geometry*/ true);
+       enum view_axis axis = VIEW_AXIS_NONE;
+       if (xwayland_surface->maximized_horz) {
+               axis |= VIEW_AXIS_HORIZONTAL;
+       }
+       if (xwayland_surface->maximized_vert) {
+               axis |= VIEW_AXIS_VERTICAL;
+       }
+       view_maximize(view, axis, /*store_natural_geometry*/ true);
 
        if (view->surface != xwayland_surface->surface) {
                if (view->surface) {