/* Metacity size/position constraints */ /* * Copyright (C) 2002 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include /* The way this code works was suggested by Owen Taylor. * * For any move_resize, we determine which variables are "free * variables" and apply constraints in terms of those. During the move * resize, we only want to modify those variables; otherwise the * constraint process can have peculiar side effects when the size and * position constraints interact. For example, resizing a window from * the top might go wrong when position constraints apply to the top * edge, and result in the bottom edge moving downward while the top * stays fixed. * * After selecting the variables we plan to vary, we define * each constraint on the window in terms of those variables. * * Trivial example, say we are resizing vertically from the top of the * window. In that case we are applying the user's mouse motion delta * to an original size and position, note that dy is positive to * resize downward: * * new_height = orig_height - dy; * new_y = orig_y + dy; * * A constraint that the position can't go above the top panel would * look like this: * * new_y >= screen_top_bound * * Substitute: * * orig_y + dy >= screen_top_bound * * Find the "boundary point" by changing to an equality: * * orig_y + dy = screen_top_bound * * Solve: * * dy = screen_top_bound - orig_y * * This dy is now the _maximum_ dy and you constrain dy with that * value, applying it to both the move and the resize: * * new_height = orig_height - dy; * new_y = orig_y + dy; * * This way the constraint is applied simultaneously to size/position, * so you aren't running the risk of constraining one but still * changing the other. i.e. we've converted an operation that may * modify both the Y position and the height of the window into an * operation that modifies a single variable, dy. That variable is * then constrained, rather than the constraining the Y pos and height * separately. * */ /* To adjust for window gravity, such as a client moving itself to the * southeast corner, we want to compute the gravity reference point * - (screen_width,screen_height) in the SE corner case - using the * size the client had in its configure request. But then we want * to compute the actual position we intend to land on using * the real constrained dimensions of the window. * * So for a window being placed in the SE corner and simultaneously * resized, we get the gravity reference point, then compute where the * window should go to maintain that ref. point at its current size * instead of at the requested size, and conceptually move the window * to the requested ref. point but at its current size, without * applying any constraints. Then we constrain it with the top and * left edges as the edges that vary, with a dx/dy that are the delta * from the current size to the requested size. * * This method applies to any ConfigureRequest that does a simultaneous * move/resize. * * We use the same method to e.g. maximize a window; if the window is * maximized, we want to MOVE_VERTICAL/MOVE_HORIZONTAL to the top * center of the screen, then RESIZE_BOTTOM and * RESIZE_HORIZONTAL_CENTER. i.e. essentially NorthGravity. * */ /* Clamps the delta to nearest permitted. * delta is the "free variable" in any given operation. */ typedef void (* MetaConstraintFunc) (MetaWindow *window, MetaFrameGeometry *fgeom, const MetaRectangle *orig, int *x_delta, int *y_delta); /* This enum marks what x_delta and y_delta mean, i.e. it * identifies the current free variables. */ enum { RESIZE_TOP, RESIZE_BOTTOM, RESIZE_LEFT, RESIZE_RIGHT, RESIZE_VERTICAL_CENTER, RESIZE_HORIZONTAL_CENTER, MOVE_VERTICAL, MOVE_HORIZONTAL, LAST_MOVE_CONTEXT } MoveContext; /* Maximization constraint: * * new_x = workarea_x + frame_left * new_y = workarea_y + frame_top * new_w = workarea_w - frame_left - frame_right * new_h = workarea_h - frame_top - frame_bottom * * No need to do anything hard because it just locks specific * size/pos. * * The min/max size constraints override maximization. */ /* Full screen constraint: * * new_x = 0; * new_y = 0; * new_w = xinerama_width; * new_h = xinerama_height; * * The min/max size constraints override fullscreen. */ /* Titlebar is onscreen constraint: * * Constants: * titlebar_width_onscreen = amount of titlebar width that has to be onscreen * nw_x, nw_y = left/top edges that titlebar can't go outside * se_x, se_y = right/bottom edges * * NW limit has priority over SE, since titlebar is on NW * * RESIZE_LEFT: * new_width = orig_width + dx * new_x = orig_x - dx * * new_x >= nw_x - (left_width + new_width + right_width - titlebar_width_onscreen) * * orig_x - dx >= nw_x - (left_width + orig_width + dx + right_width - titlebar_width_onscreen) * 0 >= nw_x - left_width - orig_width - right_width + titlebar_width_onscreen - orig_x * * i.e. dx drops out so there is no constraint at all when moving left edge. * * RESIZE_RIGHT and RESIZE_BOTTOM are the same, cannot break this constraint * by moving in those directions. * * RESIZE_TOP: * * new_height = orig_height - dy * new_y = orig_y + dy * * Can't move titlebar off the top at all regardless of height: * new_y >= nw_y + top_height * * orig_y + dy = nw_y + top_height * dy = nw_y + top_height - orig_y * * Max dy is thus (nw_y + top_height - orig_y) * * RESIZE_VERTICAL_CENTER: * * RESIZE_HORIZONTAL_CENTER is like vertical * * * MOVE_VERTICAL: * new_height = orig_height * new_y = orig_y + dy * * new_y >= nw_y + top_height * * Min negative dy (nw_y + top_height - orig_y) just as with RESIZE_TOP * Max positive dy has to be computed from se_y and given less priority than the * min negative: * * new_y < se_y * orig_y + dy = se_y * so max dy is (se_y - orig_y) * * * MOVE_HORIZONTAL: * works out same as vertical in the other direction * * */ void meta_window_constrain (MetaWindow *window, MetaFrameGeometry *orig_fgeom, int resize_gravity, const MetaRectangle *orig, MetaRectangle *new) { MetaFrameGeometry fgeom; /* Create a fake frame geometry if none really exists */ if (orig_fgeom) fgeom = *orig_fgeom; else { fgeom.top_height = 0; fgeom.bottom_height = 0; fgeom.left_width = 0; fgeom.right_width = 0; } }