mirror of
				https://github.com/labwc/labwc.git
				synced 2025-11-03 09:01:51 -05:00 
			
		
		
		
	view: try to honor original geometry with layout changes
Whenever the output layout changes, each view's original geometry will be captured as last_layout_geometry (if it has not already been captured by a previous layout change), which will remain valid unless the user modifies the view's geometry (i.e., by tiling, maximizing, moving, resizing or full-screening). On subsequent output layout changes, views with valid last_layout_geometry will be back to their original position if possible, or else to the closest possible output.
This commit is contained in:
		
							parent
							
								
									1b5af86402
								
							
						
					
					
						commit
						c229f6edc0
					
				
					 3 changed files with 156 additions and 19 deletions
				
			
		| 
						 | 
					@ -180,6 +180,13 @@ struct view {
 | 
				
			||||||
	 * maximized/fullscreen/tiled.
 | 
						 * maximized/fullscreen/tiled.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	struct wlr_box natural_geometry;
 | 
						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.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						struct wlr_box last_layout_geometry;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* used by xdg-shell views */
 | 
						/* used by xdg-shell views */
 | 
				
			||||||
	uint32_t pending_configure_serial;
 | 
						uint32_t pending_configure_serial;
 | 
				
			||||||
| 
						 | 
					@ -420,6 +427,7 @@ bool view_is_floating(struct view *view);
 | 
				
			||||||
void view_move_to_workspace(struct view *view, struct workspace *workspace);
 | 
					void view_move_to_workspace(struct view *view, struct workspace *workspace);
 | 
				
			||||||
void view_set_decorations(struct view *view, bool decorations);
 | 
					void view_set_decorations(struct view *view, bool decorations);
 | 
				
			||||||
void view_toggle_fullscreen(struct view *view);
 | 
					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_adjust_for_layout_change(struct view *view);
 | 
				
			||||||
void view_move_to_edge(struct view *view, enum view_edge direction, bool snap_to_windows);
 | 
					void view_move_to_edge(struct view *view, enum view_edge direction, bool snap_to_windows);
 | 
				
			||||||
void view_grow_to_edge(struct view *view, enum view_edge direction);
 | 
					void view_grow_to_edge(struct view *view, enum view_edge direction);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -70,6 +70,7 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges)
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			/* Store natural geometry at start of move */
 | 
								/* Store natural geometry at start of move */
 | 
				
			||||||
			view_store_natural_geometry(view);
 | 
								view_store_natural_geometry(view);
 | 
				
			||||||
 | 
								view_invalidate_last_layout_geometry(view);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* Prevent region snapping when just moving via A-Left mousebind */
 | 
							/* Prevent region snapping when just moving via A-Left mousebind */
 | 
				
			||||||
| 
						 | 
					@ -86,6 +87,13 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges)
 | 
				
			||||||
			 */
 | 
								 */
 | 
				
			||||||
			return;
 | 
								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
 | 
							 * If tiled or maximized in only one direction, reset
 | 
				
			||||||
		 * tiled/maximized state but keep the same geometry as
 | 
							 * tiled/maximized state but keep the same geometry as
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										157
									
								
								src/view.c
									
										
									
									
									
								
							
							
						
						
									
										157
									
								
								src/view.c
									
										
									
									
									
								
							| 
						 | 
					@ -252,14 +252,27 @@ view_get_edge_snap_box(struct view *view, struct output *output,
 | 
				
			||||||
	return dst;
 | 
						return dst;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static bool
 | 
				
			||||||
view_discover_output(struct view *view)
 | 
					view_discover_output(struct view *view, struct wlr_box *geometry)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	assert(view);
 | 
						assert(view);
 | 
				
			||||||
	assert(!view->fullscreen);
 | 
						assert(!view->fullscreen);
 | 
				
			||||||
	view->output = output_nearest_to(view->server,
 | 
					
 | 
				
			||||||
		view->current.x + view->current.width / 2,
 | 
						if (!geometry) {
 | 
				
			||||||
		view->current.y + view->current.height / 2);
 | 
							geometry = &view->current;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct output *output =
 | 
				
			||||||
 | 
							output_nearest_to(view->server,
 | 
				
			||||||
 | 
								geometry->x + geometry->width / 2,
 | 
				
			||||||
 | 
								geometry->y + geometry->height / 2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (output && output != view->output) {
 | 
				
			||||||
 | 
							view->output = output;
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return false;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
| 
						 | 
					@ -343,7 +356,7 @@ view_moved(struct view *view)
 | 
				
			||||||
	 * output when they enter that state.
 | 
						 * output when they enter that state.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (view_is_floating(view)) {
 | 
						if (view_is_floating(view)) {
 | 
				
			||||||
		view_discover_output(view);
 | 
							view_discover_output(view, NULL);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ssd_update_geometry(view->ssd);
 | 
						ssd_update_geometry(view->ssd);
 | 
				
			||||||
	cursor_update_focus(view->server);
 | 
						cursor_update_focus(view->server);
 | 
				
			||||||
| 
						 | 
					@ -614,11 +627,11 @@ view_adjust_floating_geometry(struct view *view, struct wlr_box *geometry)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool adjusted = false;
 | 
						bool adjusted = false;
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * First check whether the view is onscreen. For now, "onscreen"
 | 
						 * First check whether the view is the target screen, meaning that at
 | 
				
			||||||
	 * is defined as even one pixel of the client area being visible.
 | 
						 * least one client pixel is on the screen.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (wlr_output_layout_intersects(view->server->output_layout,
 | 
						if (wlr_output_layout_intersects(view->server->output_layout,
 | 
				
			||||||
			NULL, geometry)) {
 | 
								view->output->wlr_output, geometry)) {
 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
		 * If onscreen, then make sure the titlebar is also
 | 
							 * If onscreen, then make sure the titlebar is also
 | 
				
			||||||
		 * visible (and not overlapping any panels/docks)
 | 
							 * visible (and not overlapping any panels/docks)
 | 
				
			||||||
| 
						 | 
					@ -988,6 +1001,7 @@ view_maximize(struct view *view, enum view_axis axis,
 | 
				
			||||||
		interactive_cancel(view);
 | 
							interactive_cancel(view);
 | 
				
			||||||
		if (store_natural_geometry && view_is_floating(view)) {
 | 
							if (store_natural_geometry && view_is_floating(view)) {
 | 
				
			||||||
			view_store_natural_geometry(view);
 | 
								view_store_natural_geometry(view);
 | 
				
			||||||
 | 
								view_invalidate_last_layout_geometry(view);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	set_maximized(view, axis);
 | 
						set_maximized(view, axis);
 | 
				
			||||||
| 
						 | 
					@ -1200,6 +1214,7 @@ view_set_fullscreen(struct view *view, bool fullscreen)
 | 
				
			||||||
		 */
 | 
							 */
 | 
				
			||||||
		interactive_cancel(view);
 | 
							interactive_cancel(view);
 | 
				
			||||||
		view_store_natural_geometry(view);
 | 
							view_store_natural_geometry(view);
 | 
				
			||||||
 | 
							view_invalidate_last_layout_geometry(view);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	set_fullscreen(view, fullscreen);
 | 
						set_fullscreen(view, fullscreen);
 | 
				
			||||||
| 
						 | 
					@ -1211,37 +1226,141 @@ view_set_fullscreen(struct view *view, bool fullscreen)
 | 
				
			||||||
	set_adaptive_sync_fullscreen(view);
 | 
						set_adaptive_sync_fullscreen(view);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 {
 | 
				
			||||||
 | 
							view->last_layout_geometry = view->natural_geometry;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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;
 | 
				
			||||||
 | 
						view_adjust_floating_geometry(view, &view->natural_geometry);
 | 
				
			||||||
 | 
						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
 | 
					void
 | 
				
			||||||
view_adjust_for_layout_change(struct view *view)
 | 
					view_adjust_for_layout_change(struct view *view)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	assert(view);
 | 
						assert(view);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Exit fullscreen if output is lost */
 | 
					 | 
				
			||||||
	bool was_fullscreen = view->fullscreen;
 | 
						bool was_fullscreen = view->fullscreen;
 | 
				
			||||||
	if (was_fullscreen && !output_is_usable(view->output)) {
 | 
						bool is_floating = view_is_floating(view);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!output_is_usable(view->output)) {
 | 
				
			||||||
 | 
							/* A view losing an output should have a last-layout geometry */
 | 
				
			||||||
 | 
							update_last_layout_geometry(view);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Exit fullscreen and re-assess floating status */
 | 
				
			||||||
 | 
							if (was_fullscreen) {
 | 
				
			||||||
			set_fullscreen(view, false);
 | 
								set_fullscreen(view, false);
 | 
				
			||||||
 | 
								is_floating = view_is_floating(view);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Restore any full-screen window to natural geometry */
 | 
				
			||||||
 | 
						bool use_natural = was_fullscreen;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* 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;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Floating views are always assigned to the nearest output.
 | 
						 * Check if an output change is required:
 | 
				
			||||||
	 * Maximized/tiled views remain on the same output if possible.
 | 
						 * - 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
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	bool is_floating = view_is_floating(view);
 | 
						if (is_floating || last_geometry || !output_is_usable(view->output)) {
 | 
				
			||||||
	if (is_floating || !output_is_usable(view->output)) {
 | 
							/* Move the view to an appropriate output, if needed */
 | 
				
			||||||
		view_discover_output(view);
 | 
							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) {
 | 
						if (!is_floating) {
 | 
				
			||||||
		view_apply_special_geometry(view);
 | 
							view_apply_special_geometry(view);
 | 
				
			||||||
	} else if (was_fullscreen) {
 | 
						} else if (use_natural) {
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * Move the window to its natural location, either because it
 | 
				
			||||||
 | 
							 * was fullscreen or we are trying to restore a prior layout.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
		view_apply_natural_geometry(view);
 | 
							view_apply_natural_geometry(view);
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		/* reposition view if it's offscreen */
 | 
							/* Otherwise, just ensure the view is on screen. */
 | 
				
			||||||
		struct wlr_box geometry = view->pending;
 | 
							struct wlr_box geometry = view->pending;
 | 
				
			||||||
		if (view_adjust_floating_geometry(view, &geometry)) {
 | 
							if (view_adjust_floating_geometry(view, &geometry)) {
 | 
				
			||||||
			view_move_resize(view, geometry);
 | 
								view_move_resize(view, geometry);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (view->toplevel.handle) {
 | 
						if (view->toplevel.handle) {
 | 
				
			||||||
		foreign_toplevel_update_outputs(view);
 | 
							foreign_toplevel_update_outputs(view);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -1542,6 +1661,7 @@ view_snap_to_edge(struct view *view, enum view_edge edge,
 | 
				
			||||||
	} else if (store_natural_geometry) {
 | 
						} else if (store_natural_geometry) {
 | 
				
			||||||
		/* store current geometry as new natural_geometry */
 | 
							/* store current geometry as new natural_geometry */
 | 
				
			||||||
		view_store_natural_geometry(view);
 | 
							view_store_natural_geometry(view);
 | 
				
			||||||
 | 
							view_invalidate_last_layout_geometry(view);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	view_set_untiled(view);
 | 
						view_set_untiled(view);
 | 
				
			||||||
	view_set_output(view, output);
 | 
						view_set_output(view, output);
 | 
				
			||||||
| 
						 | 
					@ -1571,6 +1691,7 @@ view_snap_to_region(struct view *view, struct region *region,
 | 
				
			||||||
	} else if (store_natural_geometry) {
 | 
						} else if (store_natural_geometry) {
 | 
				
			||||||
		/* store current geometry as new natural_geometry */
 | 
							/* store current geometry as new natural_geometry */
 | 
				
			||||||
		view_store_natural_geometry(view);
 | 
							view_store_natural_geometry(view);
 | 
				
			||||||
 | 
							view_invalidate_last_layout_geometry(view);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	view_set_untiled(view);
 | 
						view_set_untiled(view);
 | 
				
			||||||
	view->tiled_region = region;
 | 
						view->tiled_region = region;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue