mirror of
https://github.com/labwc/labwc.git
synced 2026-02-05 04:06:33 -05:00
view: rework saving/restoring geometry across layout changes
After several iterations, this is basically a complete re-work. The old implementation was difficult to follow and sometimes failed to restore fullscreen/maximized/tiled geometry correctly, since it was based entirely on natural (floating) geometry. The new implementation: - always saves the actual (pending) geometry at first layout change - explicitly tracks whether a view has moved between outputs - consolidates invalidating the saved geometry into one place, rather than having lots of invalidate() calls sprinkled everywhere
This commit is contained in:
parent
c1c156ef39
commit
20929c0484
3 changed files with 55 additions and 131 deletions
|
|
@ -215,11 +215,14 @@ struct view {
|
|||
struct wlr_box natural_geometry;
|
||||
/*
|
||||
* Whenever an output layout change triggers a view relocation, the
|
||||
* last pending position (or natural geometry) will be saved so the
|
||||
* view may be restored to its original location on a subsequent layout
|
||||
* change.
|
||||
* last pending position will be saved so the view may be restored
|
||||
* to its original location on a subsequent layout change.
|
||||
*/
|
||||
struct wlr_box last_layout_geometry;
|
||||
/* Set temporarily when moving view due to layout change */
|
||||
bool adjusting_for_layout_change;
|
||||
/* True if original output was disconnected or disabled */
|
||||
bool lost_output_due_to_layout_change;
|
||||
|
||||
/* used by xdg-shell views */
|
||||
uint32_t pending_configure_serial;
|
||||
|
|
@ -533,7 +536,6 @@ bool view_titlebar_visible(struct view *view);
|
|||
void view_set_ssd_mode(struct view *view, enum lab_ssd_mode mode);
|
||||
void view_set_decorations(struct view *view, enum lab_ssd_mode mode, bool force_ssd);
|
||||
void view_toggle_fullscreen(struct view *view);
|
||||
void view_invalidate_last_layout_geometry(struct view *view);
|
||||
void view_adjust_for_layout_change(struct view *view);
|
||||
void view_move_to_edge(struct view *view, enum lab_edge direction, bool snap_to_windows);
|
||||
void view_grow_to_edge(struct view *view, enum lab_edge direction);
|
||||
|
|
|
|||
|
|
@ -92,9 +92,6 @@ interactive_begin(struct view *view, enum input_mode mode, enum lab_edge edges)
|
|||
|
||||
/* Store natural geometry at start of move */
|
||||
view_store_natural_geometry(view);
|
||||
if (view_is_floating(view)) {
|
||||
view_invalidate_last_layout_geometry(view);
|
||||
}
|
||||
|
||||
/* Prevent region snapping when just moving via A-Left mousebind */
|
||||
seat->region_prevent_snap = keyboard_get_all_modifiers(seat);
|
||||
|
|
@ -111,12 +108,6 @@ interactive_begin(struct view *view, enum input_mode mode, enum lab_edge edges)
|
|||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Resizing overrides any attempt to restore window
|
||||
* geometries altered by layout changes.
|
||||
*/
|
||||
view_invalidate_last_layout_geometry(view);
|
||||
|
||||
/*
|
||||
* If tiled or maximized in only one direction, reset
|
||||
* tiled state and un-maximize the relevant axes, but
|
||||
|
|
|
|||
167
src/view.c
167
src/view.c
|
|
@ -588,6 +588,21 @@ view_move_resize(struct view *view, struct wlr_box geo)
|
|||
if (view->impl->configure) {
|
||||
view->impl->configure(view, geo);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the move/resize was user-initiated (rather than due to
|
||||
* output layout change), then invalidate the saved geometry.
|
||||
*
|
||||
* TODO: consider also updating view->output here for floating
|
||||
* views (based on view->pending) rather than waiting until
|
||||
* view_moved(). This might eliminate some race conditions with
|
||||
* view_adjust_for_layout_change(), which uses view->pending.
|
||||
* Not sure if it might have other side-effects though.
|
||||
*/
|
||||
if (!view->adjusting_for_layout_change) {
|
||||
view->last_layout_geometry = (struct wlr_box){0};
|
||||
view->lost_output_due_to_layout_change = false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -1405,9 +1420,6 @@ view_maximize(struct view *view, enum view_axis axis)
|
|||
* a maximized view.
|
||||
*/
|
||||
interactive_cancel(view);
|
||||
if (store_natural_geometry && view_is_floating(view)) {
|
||||
view_invalidate_last_layout_geometry(view);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1695,7 +1707,6 @@ view_set_fullscreen(struct view *view, bool fullscreen)
|
|||
*/
|
||||
interactive_cancel(view);
|
||||
view_store_natural_geometry(view);
|
||||
view_invalidate_last_layout_geometry(view);
|
||||
}
|
||||
|
||||
set_fullscreen(view, fullscreen);
|
||||
|
|
@ -1713,139 +1724,62 @@ view_set_fullscreen(struct view *view, bool fullscreen)
|
|||
cursor_update_focus(view->server);
|
||||
}
|
||||
|
||||
static bool
|
||||
last_layout_geometry_is_valid(struct view *view)
|
||||
{
|
||||
return view->last_layout_geometry.width > 0
|
||||
&& view->last_layout_geometry.height > 0;
|
||||
}
|
||||
|
||||
static void
|
||||
update_last_layout_geometry(struct view *view)
|
||||
{
|
||||
/*
|
||||
* Only update an invalid last-layout geometry to prevent a series of
|
||||
* successive layout changes from continually replacing the "preferred"
|
||||
* location with whatever location the view currently holds. The
|
||||
* "preferred" location should be whatever state was set by user
|
||||
* interaction, not automatic responses to layout changes.
|
||||
*/
|
||||
if (last_layout_geometry_is_valid(view)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (view_is_floating(view)) {
|
||||
view->last_layout_geometry = view->pending;
|
||||
} else if (!wlr_box_empty(&view->natural_geometry)) {
|
||||
view->last_layout_geometry = view->natural_geometry;
|
||||
} else {
|
||||
/* e.g. initially-maximized window */
|
||||
view->last_layout_geometry =
|
||||
view_get_fallback_natural_geometry(view);
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
apply_last_layout_geometry(struct view *view, bool force_update)
|
||||
{
|
||||
/* Only apply a valid last-layout geometry */
|
||||
if (!last_layout_geometry_is_valid(view)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unless forced, the last-layout geometry is only applied
|
||||
* when the relevant view geometry is distinct.
|
||||
*/
|
||||
if (!force_update) {
|
||||
struct wlr_box *relevant = view_is_floating(view) ?
|
||||
&view->pending : &view->natural_geometry;
|
||||
|
||||
if (wlr_box_equal(relevant, &view->last_layout_geometry)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
view->natural_geometry = view->last_layout_geometry;
|
||||
adjust_floating_geometry(view, &view->natural_geometry,
|
||||
/* midpoint_visibility */ true);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
view_invalidate_last_layout_geometry(struct view *view)
|
||||
{
|
||||
assert(view);
|
||||
view->last_layout_geometry.width = 0;
|
||||
view->last_layout_geometry.height = 0;
|
||||
}
|
||||
|
||||
void
|
||||
view_adjust_for_layout_change(struct view *view)
|
||||
{
|
||||
assert(view);
|
||||
|
||||
bool is_floating = view_is_floating(view);
|
||||
bool use_natural = false;
|
||||
view->adjusting_for_layout_change = true;
|
||||
|
||||
if (!output_is_usable(view->output)) {
|
||||
/* A view losing an output should have a last-layout geometry */
|
||||
update_last_layout_geometry(view);
|
||||
}
|
||||
|
||||
/* Capture a pointer to the last-layout geometry (only if valid) */
|
||||
struct wlr_box *last_geometry = NULL;
|
||||
if (last_layout_geometry_is_valid(view)) {
|
||||
last_geometry = &view->last_layout_geometry;
|
||||
view->lost_output_due_to_layout_change = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Save the view's geometry if this is the first layout change
|
||||
* since a user-initiated move/resize. Do not save it again for
|
||||
* subsequent layout changes, since the point is to be able to
|
||||
* restore to the original location after multiple changes
|
||||
* (e.g. output disconnected and then reconnected).
|
||||
*
|
||||
* Note that it's important to do this even if an output change
|
||||
* is not (yet) required, to properly handle cases of multiple
|
||||
* outputs being disconnected/reconnected in any order. In that
|
||||
* case, there can be multiple layout change events, and a view
|
||||
* can be moved first and only later lose its own output.
|
||||
*/
|
||||
if (wlr_box_empty(&view->last_layout_geometry)) {
|
||||
view->last_layout_geometry = view->pending;
|
||||
}
|
||||
/*
|
||||
* Check if an output change is required:
|
||||
* - Floating views are always mapped to the nearest output
|
||||
* - Any view without a usable output needs to be repositioned
|
||||
* - Any view with a valid last-layout geometry might be better
|
||||
* positioned on another output
|
||||
* - Any fullscreen/tiled/maximized view which lost its output
|
||||
* due to a layout change should be moved back if the output
|
||||
* is reconnected.
|
||||
*
|
||||
* If so, try to find the "nearest" output (in terms of layout
|
||||
* coordinates) to the saved geometry. Other strategies might be
|
||||
* possible here -- for example, trying to match the same physical
|
||||
* output the view was on previously -- but this is simplest.
|
||||
*/
|
||||
if (is_floating || last_geometry || !output_is_usable(view->output)) {
|
||||
/* Move the view to an appropriate output, if needed */
|
||||
bool output_changed = view_discover_output(view, last_geometry);
|
||||
|
||||
/*
|
||||
* Try to apply the last-layout to the natural geometry
|
||||
* (adjusting to ensure that it fits on the screen). This is
|
||||
* forced if the output has changed, but will be done
|
||||
* opportunistically even on the same output if the last-layout
|
||||
* geometry is different from the view's governing geometry.
|
||||
*/
|
||||
if (apply_last_layout_geometry(view, output_changed)) {
|
||||
use_natural = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Whether or not the view has moved, the layout has changed.
|
||||
* Ensure that the view now has a valid last-layout geometry.
|
||||
*/
|
||||
update_last_layout_geometry(view);
|
||||
if (is_floating || view->lost_output_due_to_layout_change) {
|
||||
view_discover_output(view, &view->last_layout_geometry);
|
||||
}
|
||||
|
||||
if (!is_floating) {
|
||||
view_apply_special_geometry(view);
|
||||
} else if (use_natural) {
|
||||
/*
|
||||
* Move the window to its natural location, because
|
||||
* we are trying to restore a prior layout.
|
||||
*/
|
||||
view_apply_natural_geometry(view);
|
||||
} else {
|
||||
/* Otherwise, just ensure the view is on screen. */
|
||||
struct wlr_box geometry = view->pending;
|
||||
if (adjust_floating_geometry(view, &geometry,
|
||||
/* midpoint_visibility */ true)) {
|
||||
view_move_resize(view, geometry);
|
||||
}
|
||||
/* Restore saved geometry, ensuring view is on-screen */
|
||||
struct wlr_box geometry = view->last_layout_geometry;
|
||||
adjust_floating_geometry(view, &geometry,
|
||||
/* midpoint_visibility */ true);
|
||||
view_move_resize(view, geometry);
|
||||
}
|
||||
|
||||
view_update_outputs(view);
|
||||
view->adjusting_for_layout_change = false;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -2134,7 +2068,6 @@ view_snap_to_edge(struct view *view, enum lab_edge edge,
|
|||
} else if (store_natural_geometry) {
|
||||
/* store current geometry as new natural_geometry */
|
||||
view_store_natural_geometry(view);
|
||||
view_invalidate_last_layout_geometry(view);
|
||||
}
|
||||
view_set_untiled(view);
|
||||
view_set_output(view, output);
|
||||
|
|
@ -2168,7 +2101,6 @@ view_snap_to_region(struct view *view, struct region *region)
|
|||
} else if (store_natural_geometry) {
|
||||
/* store current geometry as new natural_geometry */
|
||||
view_store_natural_geometry(view);
|
||||
view_invalidate_last_layout_geometry(view);
|
||||
}
|
||||
view_set_untiled(view);
|
||||
view->tiled_region = region;
|
||||
|
|
@ -2181,7 +2113,6 @@ view_move_to_output(struct view *view, struct output *output)
|
|||
{
|
||||
assert(view);
|
||||
|
||||
view_invalidate_last_layout_geometry(view);
|
||||
view_set_output(view, output);
|
||||
if (view_is_floating(view)) {
|
||||
struct wlr_box output_area = output_usable_area_in_layout_coords(output);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue