view: Anchor right/bottom edge only when resizing via top/left edge

Currently, we anchor the right/bottom edge of the view whenever the top/
left edge is moving (current.x/y != pending.x/y). Doing so doesn't make
much sense when the right/bottom edge is also moving. In that case it's
probably best to move the view (or at least its top/left corner)
directly to its final position.

The most noticeable effect of this change is with views that don't
accept their requested size exactly when tiled or maximized (examples:
havoc, xfce4-terminal). Previously, their right-bottom corner would be
aligned with the screen edge, leaving gaps on the left and top. Now the
top-left corner will be aligned and the gaps will be on the right and
bottom. This is still not ideal, but IMHO less surprising to the user.
This commit is contained in:
John Lindgren 2023-02-25 12:05:22 -05:00 committed by Johan Malm
parent 9f00087a82
commit 0b34b9f69f
4 changed files with 77 additions and 35 deletions

View file

@ -7,8 +7,16 @@
* Please note: only xdg-shell-toplevel-view and xwayland-view view_impl
* functions should call these functions.
*/
struct view;
void view_impl_move_to_front(struct view *view);
void view_impl_map(struct view *view);
/*
* Updates view geometry at commit based on current position/size,
* pending move/resize, and committed surface size. The computed
* position may not match pending.x/y exactly in some cases.
*/
void view_impl_apply_geometry(struct view *view, int w, int h);
#endif /* __LABWC_VIEW_IMPL_COMMON_H */

View file

@ -22,3 +22,58 @@ view_impl_map(struct view *view)
view_update_title(view);
view_update_app_id(view);
}
static bool
resizing_edge(struct view *view, uint32_t edge)
{
struct server *server = view->server;
return server->input_mode == LAB_INPUT_STATE_RESIZE
&& server->grabbed_view == view
&& (server->resize_edges & edge);
}
void
view_impl_apply_geometry(struct view *view, int w, int h)
{
struct wlr_box *current = &view->current;
struct wlr_box *pending = &view->pending;
struct wlr_box old = *current;
/*
* Anchor right edge if resizing via left edge.
*
* Note that answering the question "are we resizing?" is a bit
* tricky. The most obvious method is to look at the server
* flags; but that method will not account for any late commits
* that occur after the mouse button is released, as the client
* catches up with pending configure requests. So as a fallback,
* we resort to a geometry-based heuristic -- also not 100%
* reliable on its own. The combination of the two methods
* should catch 99% of resize cases that we care about.
*/
bool resizing_left_edge = resizing_edge(view, WLR_EDGE_LEFT);
if (resizing_left_edge || (current->x != pending->x
&& current->x + current->width ==
pending->x + pending->width)) {
current->x = pending->x + pending->width - w;
} else {
current->x = pending->x;
}
/* Anchor bottom edge if resizing via top edge */
bool resizing_top_edge = resizing_edge(view, WLR_EDGE_TOP);
if (resizing_top_edge || (current->y != pending->y
&& current->y + current->height ==
pending->y + pending->height)) {
current->y = pending->y + pending->height - h;
} else {
current->y = pending->y;
}
current->width = w;
current->height = h;
if (!wlr_box_equal(current, &old)) {
view_moved(view);
}
}

View file

@ -74,31 +74,18 @@ handle_commit(struct wl_listener *listener, void *data)
wlr_xdg_surface_get_geometry(xdg_surface, &size);
struct wlr_box *current = &view->current;
struct wlr_box *pending = &view->pending;
bool update_required = false;
if (current->width != size.width || current->height != size.height) {
update_required = true;
current->width = size.width;
current->height = size.height;
}
bool update_required = current->width != size.width
|| current->height != size.height;
uint32_t serial = view->pending_configure_serial;
if (serial > 0 && serial >= xdg_surface->current.configure_serial) {
if (current->x != pending->x) {
update_required = true;
current->x = pending->x + pending->width - size.width;
}
if (current->y != pending->y) {
update_required = true;
current->y = pending->y + pending->height - size.height;
}
if (serial == xdg_surface->current.configure_serial) {
view->pending_configure_serial = 0;
}
}
if (update_required) {
view_moved(view);
view_impl_apply_geometry(view, size.width, size.height);
}
}

View file

@ -116,24 +116,16 @@ handle_commit(struct wl_listener *listener, void *data)
/* Must receive commit signal before accessing surface->current* */
struct wlr_surface_state *state = &view->surface->current;
struct wlr_box *current = &view->current;
struct wlr_box *pending = &view->pending;
if (current->width == state->width && current->height == state->height) {
return;
/*
* If there is a pending move/resize, wait until the surface
* size changes to update geometry. The hope is to update both
* the position and the size of the view at the same time,
* reducing visual glitches.
*/
if (current->width != state->width || current->height != state->height) {
view_impl_apply_geometry(view, state->width, state->height);
}
current->width = state->width;
current->height = state->height;
if (current->x != pending->x) {
/* Adjust x for queued up configure events */
current->x = pending->x + pending->width - current->width;
}
if (current->y != pending->y) {
/* Adjust y for queued up configure events */
current->y = pending->y + pending->height - current->height;
}
view_moved(view);
}
static void
@ -143,7 +135,7 @@ handle_request_move(struct wl_listener *listener, void *data)
* This event is raised when a client would like to begin an interactive
* move, typically because the user clicked on their client-side
* decorations. Note that a more sophisticated compositor should check
* the provied serial against a list of button press serials sent to
* the provided serial against a list of button press serials sent to
* this client, to prevent the client from requesting this whenever they
* want.
*/
@ -158,7 +150,7 @@ handle_request_resize(struct wl_listener *listener, void *data)
* This event is raised when a client would like to begin an interactive
* resize, typically because the user clicked on their client-side
* decorations. Note that a more sophisticated compositor should check
* the provied serial against a list of button press serials sent to
* the provided serial against a list of button press serials sent to
* this client, to prevent the client from requesting this whenever they
* want.
*/