mirror of
https://github.com/brl/mutter.git
synced 2024-12-23 11:32:04 +00:00
window: Add new tiling code
The new tiling code, instead of based around "tiling states", is instead based around constrained edges. This allows us to have windows that have three constrained edges, but keep one free-floating, e.g. a window tiled to the left has the left, top, and bottom edges constrained, but the right edge can be left resizable. This system also is easily extended to support corner tiling. We also, using the new "size state" system, also keep normal, tiled, and maximized sizes independently, allowing the maximize button to bounce between maximized and tiled states without reverting to normal in between. Dragging from the top will always restore the normal state, though. https://bugzilla.gnome.org/show_bug.cgi?id=751857
This commit is contained in:
parent
2c7ef2269f
commit
50e3e3b929
@ -97,7 +97,7 @@ typedef enum
|
||||
PRIORITY_ENTIRELY_VISIBLE_ON_WORKAREA = 1,
|
||||
PRIORITY_SIZE_HINTS_INCREMENTS = 1,
|
||||
PRIORITY_MAXIMIZATION = 2,
|
||||
PRIORITY_TILING = 2,
|
||||
PRIORITY_CONSTRAINED_EDGES = 2,
|
||||
PRIORITY_FULLSCREEN = 2,
|
||||
PRIORITY_SIZE_HINTS_LIMITS = 3,
|
||||
PRIORITY_TITLEBAR_VISIBLE = 4,
|
||||
@ -152,6 +152,10 @@ static gboolean constrain_maximization (MetaWindow *window,
|
||||
ConstraintInfo *info,
|
||||
ConstraintPriority priority,
|
||||
gboolean check_only);
|
||||
static gboolean constrain_constrained_edges (MetaWindow *window,
|
||||
ConstraintInfo *info,
|
||||
ConstraintPriority priority,
|
||||
gboolean check_only);
|
||||
static gboolean constrain_fullscreen (MetaWindow *window,
|
||||
ConstraintInfo *info,
|
||||
ConstraintPriority priority,
|
||||
@ -209,6 +213,7 @@ typedef struct {
|
||||
static const Constraint all_constraints[] = {
|
||||
{constrain_modal_dialog, "constrain_modal_dialog"},
|
||||
{constrain_maximization, "constrain_maximization"},
|
||||
{constrain_constrained_edges, "constrain_constrained_edges"},
|
||||
{constrain_fullscreen, "constrain_fullscreen"},
|
||||
{constrain_size_increments, "constrain_size_increments"},
|
||||
{constrain_size_limits, "constrain_size_limits"},
|
||||
@ -798,6 +803,45 @@ constrain_fullscreen (MetaWindow *window,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
constrain_constrained_edges (MetaWindow *window,
|
||||
ConstraintInfo *info,
|
||||
ConstraintPriority priority,
|
||||
gboolean check_only)
|
||||
{
|
||||
MetaRectangle monitor, new_rectangle;
|
||||
gboolean constraint_already_satisfied;
|
||||
|
||||
if (priority > PRIORITY_CONSTRAINED_EDGES)
|
||||
return TRUE;
|
||||
|
||||
/* Determine whether constraint applies; exit if it doesn't */
|
||||
if (!window->constrained_edges)
|
||||
return TRUE;
|
||||
|
||||
new_rectangle = info->current;
|
||||
monitor = info->work_area_monitor;
|
||||
|
||||
if (window->constrained_edges & META_DIRECTION_LEFT)
|
||||
new_rectangle.x = monitor.x;
|
||||
if (window->constrained_edges & META_DIRECTION_RIGHT)
|
||||
new_rectangle.x = monitor.x + monitor.width - new_rectangle.width;
|
||||
if (window->constrained_edges & META_DIRECTION_TOP)
|
||||
new_rectangle.y = monitor.y;
|
||||
if (window->constrained_edges & META_DIRECTION_BOTTOM)
|
||||
new_rectangle.y = monitor.y + monitor.height - new_rectangle.height;
|
||||
|
||||
constraint_already_satisfied =
|
||||
meta_rectangle_equal (&info->current, &new_rectangle);
|
||||
|
||||
if (check_only || constraint_already_satisfied)
|
||||
return constraint_already_satisfied;
|
||||
|
||||
/*** Enforce constraint ***/
|
||||
info->current = new_rectangle;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
constrain_size_increments (MetaWindow *window,
|
||||
ConstraintInfo *info,
|
||||
|
@ -2936,6 +2936,7 @@ handle_toggle_tiled_left (MetaDisplay *display,
|
||||
MetaKeyBinding *binding,
|
||||
gpointer dummy)
|
||||
{
|
||||
meta_window_tile (window, META_TILE_ZONE_W);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -2946,6 +2947,7 @@ handle_toggle_tiled_right (MetaDisplay *display,
|
||||
MetaKeyBinding *binding,
|
||||
gpointer dummy)
|
||||
{
|
||||
meta_window_tile (window, META_TILE_ZONE_E);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -57,7 +57,13 @@ struct _MetaScreen
|
||||
MetaRectangle rect; /* Size of screen; rect.x & rect.y are always 0 */
|
||||
MetaUI *ui;
|
||||
|
||||
guint tile_preview_timeout_id;
|
||||
struct {
|
||||
gboolean exists;
|
||||
guint timeout_id;
|
||||
MetaWindow *window;
|
||||
MetaRectangle area;
|
||||
int monitor;
|
||||
} tile_preview;
|
||||
|
||||
MetaWorkspace *active_workspace;
|
||||
|
||||
@ -136,9 +142,12 @@ void meta_screen_foreach_window (MetaScreen *scree
|
||||
|
||||
void meta_screen_update_cursor (MetaScreen *screen);
|
||||
|
||||
void meta_screen_update_tile_preview (MetaScreen *screen,
|
||||
gboolean delay);
|
||||
void meta_screen_hide_tile_preview (MetaScreen *screen);
|
||||
void meta_screen_update_tile_preview (MetaScreen *screen,
|
||||
MetaWindow *window,
|
||||
MetaRectangle area,
|
||||
int monitor,
|
||||
gboolean delay);
|
||||
void meta_screen_hide_tile_preview (MetaScreen *screen);
|
||||
|
||||
MetaWindow* meta_screen_get_mouse_window (MetaScreen *screen,
|
||||
MetaWindow *not_this_one);
|
||||
|
@ -723,8 +723,6 @@ meta_screen_new (MetaDisplay *display,
|
||||
screen->ui = meta_ui_new (screen->display->xdisplay,
|
||||
screen->xscreen);
|
||||
|
||||
screen->tile_preview_timeout_id = 0;
|
||||
|
||||
screen->stack = meta_stack_new (screen);
|
||||
screen->stack_tracker = meta_stack_tracker_new (screen);
|
||||
|
||||
@ -841,8 +839,8 @@ meta_screen_free (MetaScreen *screen,
|
||||
|
||||
g_free (screen->monitor_infos);
|
||||
|
||||
if (screen->tile_preview_timeout_id)
|
||||
g_source_remove (screen->tile_preview_timeout_id);
|
||||
if (screen->tile_preview.timeout_id)
|
||||
g_source_remove (screen->tile_preview.timeout_id);
|
||||
|
||||
g_free (screen->screen_name);
|
||||
|
||||
@ -1320,43 +1318,69 @@ meta_screen_set_cursor (MetaScreen *screen,
|
||||
static gboolean
|
||||
meta_screen_update_tile_preview_timeout (gpointer data)
|
||||
{
|
||||
MetaScreen *screen = data;
|
||||
|
||||
screen->tile_preview.timeout_id = 0;
|
||||
|
||||
if (screen->tile_preview.exists)
|
||||
{
|
||||
meta_compositor_show_tile_preview (screen->display->compositor,
|
||||
screen->tile_preview.window,
|
||||
&screen->tile_preview.area,
|
||||
screen->tile_preview.monitor);
|
||||
}
|
||||
else
|
||||
{
|
||||
meta_compositor_hide_tile_preview (screen->display->compositor);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#define TILE_PREVIEW_TIMEOUT_MS 200
|
||||
|
||||
void
|
||||
meta_screen_update_tile_preview (MetaScreen *screen,
|
||||
gboolean delay)
|
||||
meta_screen_update_tile_preview (MetaScreen *screen,
|
||||
MetaWindow *window,
|
||||
MetaRectangle area,
|
||||
int monitor,
|
||||
gboolean delay)
|
||||
{
|
||||
screen->tile_preview.exists = TRUE;
|
||||
screen->tile_preview.window = window;
|
||||
screen->tile_preview.area = area;
|
||||
screen->tile_preview.monitor = monitor;
|
||||
|
||||
if (delay)
|
||||
{
|
||||
if (screen->tile_preview_timeout_id > 0)
|
||||
if (screen->tile_preview.timeout_id > 0)
|
||||
return;
|
||||
|
||||
screen->tile_preview_timeout_id =
|
||||
screen->tile_preview.timeout_id =
|
||||
g_timeout_add (TILE_PREVIEW_TIMEOUT_MS,
|
||||
meta_screen_update_tile_preview_timeout,
|
||||
screen);
|
||||
g_source_set_name_by_id (screen->tile_preview_timeout_id,
|
||||
g_source_set_name_by_id (screen->tile_preview.timeout_id,
|
||||
"[mutter] meta_screen_update_tile_preview_timeout");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (screen->tile_preview_timeout_id > 0)
|
||||
g_source_remove (screen->tile_preview_timeout_id);
|
||||
if (screen->tile_preview.timeout_id > 0)
|
||||
g_source_remove (screen->tile_preview.timeout_id);
|
||||
|
||||
meta_screen_update_tile_preview_timeout ((gpointer)screen);
|
||||
meta_screen_update_tile_preview_timeout ((gpointer) screen);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
meta_screen_hide_tile_preview (MetaScreen *screen)
|
||||
{
|
||||
if (screen->tile_preview_timeout_id > 0)
|
||||
g_source_remove (screen->tile_preview_timeout_id);
|
||||
screen->tile_preview.exists = FALSE;
|
||||
|
||||
meta_compositor_hide_tile_preview (screen->display->compositor);
|
||||
if (screen->tile_preview.timeout_id > 0)
|
||||
g_source_remove (screen->tile_preview.timeout_id);
|
||||
|
||||
meta_screen_update_tile_preview_timeout ((gpointer) screen);
|
||||
}
|
||||
|
||||
MetaWindow*
|
||||
|
@ -450,8 +450,10 @@ struct _MetaWindow
|
||||
/* This is where we store data while in another state. The information
|
||||
* above in MetaWindow is always the current state of the window. */
|
||||
struct {
|
||||
MetaWindowSizeState normal, maximized;
|
||||
MetaWindowSizeState normal, tiled, maximized;
|
||||
} size_states;
|
||||
|
||||
MetaDirection constrained_edges;
|
||||
};
|
||||
|
||||
struct _MetaWindowClass
|
||||
@ -617,7 +619,6 @@ void meta_window_update_for_monitors_changed (MetaWindow *window);
|
||||
void meta_window_on_all_workspaces_changed (MetaWindow *window);
|
||||
|
||||
gboolean meta_window_should_attach_to_parent (MetaWindow *window);
|
||||
gboolean meta_window_can_tile_side_by_side (MetaWindow *window);
|
||||
|
||||
gboolean meta_window_updates_are_frozen (MetaWindow *window);
|
||||
|
||||
|
@ -2609,6 +2609,8 @@ get_current_size_state (MetaWindow *window)
|
||||
{
|
||||
if (META_WINDOW_MAXIMIZED (window))
|
||||
return &window->size_states.maximized;
|
||||
else if (window->constrained_edges != 0)
|
||||
return &window->size_states.tiled;
|
||||
else
|
||||
return &window->size_states.normal;
|
||||
}
|
||||
@ -2880,35 +2882,48 @@ meta_window_requested_dont_bypass_compositor (MetaWindow *window)
|
||||
return window->bypass_compositor == _NET_WM_BYPASS_COMPOSITOR_HINT_OFF;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
meta_window_can_tile_maximized (MetaWindow *window)
|
||||
static void
|
||||
get_tile_zone_area (MetaWindow *window,
|
||||
MetaTileZone tile_zone,
|
||||
MetaRectangle *rect_p)
|
||||
{
|
||||
return window->has_maximize_func;
|
||||
}
|
||||
MetaRectangle work_area;
|
||||
meta_window_get_work_area_for_monitor (window, window->monitor->number, &work_area);
|
||||
|
||||
gboolean
|
||||
meta_window_can_tile_side_by_side (MetaWindow *window)
|
||||
{
|
||||
int monitor;
|
||||
MetaRectangle tile_area;
|
||||
MetaRectangle client_rect;
|
||||
MetaRectangle rect = {
|
||||
.x = work_area.x,
|
||||
.y = work_area.y,
|
||||
};
|
||||
|
||||
if (!meta_window_can_tile_maximized (window))
|
||||
return FALSE;
|
||||
switch (tile_zone & META_DIRECTION_HORIZONTAL)
|
||||
{
|
||||
case META_DIRECTION_HORIZONTAL:
|
||||
rect.width = work_area.width;
|
||||
break;
|
||||
case META_DIRECTION_LEFT:
|
||||
rect.width = work_area.width / 2;
|
||||
break;
|
||||
case META_DIRECTION_RIGHT:
|
||||
rect.x += work_area.width / 2;
|
||||
rect.width = work_area.width / 2;
|
||||
break;
|
||||
}
|
||||
|
||||
monitor = meta_screen_get_current_monitor (window->screen);
|
||||
meta_window_get_work_area_for_monitor (window, monitor, &tile_area);
|
||||
switch (tile_zone & META_DIRECTION_VERTICAL)
|
||||
{
|
||||
case META_DIRECTION_VERTICAL:
|
||||
rect.height = work_area.height;
|
||||
break;
|
||||
case META_DIRECTION_TOP:
|
||||
rect.height = work_area.height / 2;
|
||||
break;
|
||||
case META_DIRECTION_BOTTOM:
|
||||
rect.y += work_area.height / 2;
|
||||
rect.height = work_area.height / 2;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Do not allow tiling in portrait orientation */
|
||||
if (tile_area.height > tile_area.width)
|
||||
return FALSE;
|
||||
|
||||
tile_area.width /= 2;
|
||||
|
||||
meta_window_frame_rect_to_client_rect (window, &tile_area, &client_rect);
|
||||
|
||||
return client_rect.width >= window->size_hints.min_width &&
|
||||
client_rect.height >= window->size_hints.min_height;
|
||||
*rect_p = rect;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -3022,7 +3037,10 @@ meta_window_unmaximize (MetaWindow *window,
|
||||
g_assert (unmaximize_horizontally || unmaximize_vertically);
|
||||
|
||||
MetaWindowSizeState *size_state;
|
||||
size_state = &window->size_states.normal;
|
||||
if (window->constrained_edges != 0)
|
||||
size_state = &window->size_states.tiled;
|
||||
else
|
||||
size_state = &window->size_states.normal;
|
||||
|
||||
/* Special-case unmaximizing both directions to restoring the
|
||||
* last state. This makes the unmaximize buttons go back to the
|
||||
@ -3085,6 +3103,12 @@ meta_window_unmaximize (MetaWindow *window,
|
||||
|
||||
new_size_state.rect = target_rect;
|
||||
|
||||
/* Clear any constrained edges when unmaximizing */
|
||||
if (unmaximize_horizontally)
|
||||
window->constrained_edges &= ~META_DIRECTION_HORIZONTAL;
|
||||
if (unmaximize_vertically)
|
||||
window->constrained_edges &= ~META_DIRECTION_VERTICAL;
|
||||
|
||||
meta_window_unmaximize_internal (window, &new_size_state);
|
||||
}
|
||||
}
|
||||
@ -5485,6 +5509,119 @@ update_move_timeout (gpointer data)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
META_RECT_EDGE_W = 1 << 0, /* west */
|
||||
META_RECT_EDGE_E = 1 << 1, /* east */
|
||||
META_RECT_EDGE_H = 1 << 2, /* horizontal (middle) */
|
||||
META_RECT_EDGE_N = 1 << 3, /* north */
|
||||
META_RECT_EDGE_S = 1 << 4, /* south */
|
||||
META_RECT_EDGE_V = 1 << 5, /* vertical (middle) */
|
||||
} MetaRectEdges;
|
||||
|
||||
typedef struct {
|
||||
int x1, y1, x2, y2;
|
||||
} Box;
|
||||
|
||||
static MetaRectEdges
|
||||
get_rect_edges (Box outer, Box inner, int x, int y)
|
||||
{
|
||||
MetaDirection edges = 0;
|
||||
|
||||
if (x >= outer.x1 && x < inner.x1)
|
||||
edges |= META_RECT_EDGE_W;
|
||||
else if (x >= inner.x2 && x < outer.x2)
|
||||
edges |= META_RECT_EDGE_E;
|
||||
else if (x >= inner.x1 && x < inner.x2)
|
||||
edges |= META_RECT_EDGE_H;
|
||||
|
||||
if (y >= outer.y1 && y < inner.y1)
|
||||
edges |= META_RECT_EDGE_N;
|
||||
else if (y >= inner.y2 && y < outer.y2)
|
||||
edges |= META_RECT_EDGE_S;
|
||||
else if (y >= inner.y1 && y < inner.y2)
|
||||
edges |= META_RECT_EDGE_V;
|
||||
|
||||
return edges;
|
||||
}
|
||||
|
||||
static inline Box
|
||||
box_from_rect (MetaRectangle rect)
|
||||
{
|
||||
Box box = { .x1 = rect.x, .y1 = rect.y, .x2 = rect.x + rect.width, .y2 = rect.y + rect.height };
|
||||
return box;
|
||||
}
|
||||
|
||||
static inline int
|
||||
get_drag_shake_threshold (void)
|
||||
{
|
||||
/* Originally for detaching maximized windows, but we use this
|
||||
* for the zones at the sides of the monitor where trigger tiling
|
||||
* because it's about the right size
|
||||
*/
|
||||
#define DRAG_THRESHOLD_TO_SHAKE_THRESHOLD_FACTOR 6
|
||||
return meta_prefs_get_drag_threshold () * DRAG_THRESHOLD_TO_SHAKE_THRESHOLD_FACTOR;
|
||||
}
|
||||
|
||||
static MetaTileZone
|
||||
get_tile_zone_for_pointer (MetaWindow *window,
|
||||
int x, int y)
|
||||
{
|
||||
if (!window->has_maximize_func)
|
||||
return 0;
|
||||
|
||||
const MetaMonitorInfo *monitor;
|
||||
MetaRectangle work_area;
|
||||
monitor = meta_screen_get_current_monitor_info_for_pos (window->screen, x, y);
|
||||
meta_window_get_work_area_for_monitor (window, monitor->number, &work_area);
|
||||
|
||||
MetaRectangle client_rect;
|
||||
meta_window_frame_rect_to_client_rect (window, &work_area, &client_rect);
|
||||
|
||||
gboolean can_tile_horz = ((client_rect.width / 2) >= window->size_hints.min_width);
|
||||
gboolean can_tile_vert = ((client_rect.height / 2) >= window->size_hints.min_height);
|
||||
gboolean can_max_horz = (client_rect.width >= window->size_hints.min_width);
|
||||
gboolean can_max_vert = (client_rect.height >= window->size_hints.min_height);
|
||||
|
||||
int shake_threshold = get_drag_shake_threshold ();
|
||||
Box outer = box_from_rect (monitor->rect);
|
||||
/* fudge the work area with the shake threshold */
|
||||
Box inner = box_from_rect (work_area);
|
||||
inner.x1 = MAX (inner.x1, shake_threshold);
|
||||
inner.x2 = MIN (outer.x2 - shake_threshold, inner.x2);
|
||||
inner.y1 = MAX (inner.y1, shake_threshold);
|
||||
inner.y2 = MIN (outer.y2 - shake_threshold, inner.y2);
|
||||
|
||||
MetaRectEdges edges = get_rect_edges (outer, inner, x, y);
|
||||
|
||||
/* Simple cases: outside of a monitor, or in the inner rectangle
|
||||
* entirely aren't tile zones. */
|
||||
if (edges == 0 ||
|
||||
edges == (META_RECT_EDGE_H | META_RECT_EDGE_V))
|
||||
return 0;
|
||||
|
||||
/* Special case: the top border is maximization */
|
||||
if (edges == (META_RECT_EDGE_N | META_RECT_EDGE_H))
|
||||
return META_TILE_ZONE_MAXIMIZED;
|
||||
|
||||
MetaTileZone tile_zone = 0;
|
||||
|
||||
if ((edges & META_RECT_EDGE_W) && can_tile_horz)
|
||||
tile_zone |= META_DIRECTION_LEFT;
|
||||
else if ((edges & META_RECT_EDGE_E) && can_tile_horz)
|
||||
tile_zone |= META_DIRECTION_RIGHT;
|
||||
else if ((edges & META_RECT_EDGE_H) && can_max_horz)
|
||||
tile_zone |= META_DIRECTION_HORIZONTAL;
|
||||
|
||||
if ((edges & META_RECT_EDGE_N) && can_tile_vert)
|
||||
tile_zone |= META_DIRECTION_TOP;
|
||||
else if ((edges & META_RECT_EDGE_S) && can_tile_vert)
|
||||
tile_zone |= META_DIRECTION_BOTTOM;
|
||||
else if ((edges & META_RECT_EDGE_V) && can_max_vert)
|
||||
tile_zone |= META_DIRECTION_VERTICAL;
|
||||
|
||||
return tile_zone;
|
||||
}
|
||||
|
||||
static void
|
||||
update_move (MetaWindow *window,
|
||||
gboolean snap,
|
||||
@ -5521,20 +5658,36 @@ update_move (MetaWindow *window,
|
||||
if (dx == 0 && dy == 0)
|
||||
return;
|
||||
|
||||
/* Originally for detaching maximized windows, but we use this
|
||||
* for the zones at the sides of the monitor where trigger tiling
|
||||
* because it's about the right size
|
||||
*/
|
||||
#define DRAG_THRESHOLD_TO_SHAKE_THRESHOLD_FACTOR 6
|
||||
shake_threshold = meta_prefs_get_drag_threshold () *
|
||||
DRAG_THRESHOLD_TO_SHAKE_THRESHOLD_FACTOR;
|
||||
shake_threshold = get_drag_shake_threshold ();
|
||||
|
||||
/* shake loose (unmaximize) maximized or tiled window if dragged beyond
|
||||
* the threshold in the Y direction. Tiled windows can also be pulled
|
||||
* loose via X motion.
|
||||
*/
|
||||
int shake_x_threshold = G_MAXINT;
|
||||
|
||||
if ((META_WINDOW_MAXIMIZED (window) && ABS (dy) >= shake_threshold))
|
||||
if ((window->constrained_edges & META_DIRECTION_HORIZONTAL) &&
|
||||
(window->constrained_edges & META_DIRECTION_HORIZONTAL) != META_DIRECTION_HORIZONTAL)
|
||||
shake_x_threshold = shake_threshold;
|
||||
|
||||
if (meta_prefs_get_edge_tiling ())
|
||||
{
|
||||
MetaTileZone tile_zone = get_tile_zone_for_pointer (window, x, y);
|
||||
if (tile_zone != 0)
|
||||
{
|
||||
MetaRectangle tile_area;
|
||||
get_tile_zone_area (window, tile_zone, &tile_area);
|
||||
const MetaMonitorInfo *monitor = meta_screen_get_current_monitor_info_for_pos (window->screen, x, y);
|
||||
meta_screen_update_tile_preview (window->screen, window, tile_area, monitor->number, TRUE);
|
||||
}
|
||||
else
|
||||
{
|
||||
meta_screen_hide_tile_preview (window->screen);
|
||||
}
|
||||
}
|
||||
|
||||
if ((META_WINDOW_MAXIMIZED (window) || window->constrained_edges) &&
|
||||
(ABS (dy) >= shake_threshold || ABS (dx) >= shake_x_threshold))
|
||||
{
|
||||
double prop;
|
||||
|
||||
@ -5564,6 +5717,8 @@ update_move (MetaWindow *window,
|
||||
window->size_states.normal.rect.x = display->grab_initial_window_pos.x;
|
||||
window->size_states.normal.rect.y = display->grab_initial_window_pos.y;
|
||||
|
||||
window->constrained_edges = 0;
|
||||
|
||||
meta_window_unmaximize_internal (window, &window->size_states.normal);
|
||||
return;
|
||||
}
|
||||
@ -5818,6 +5973,10 @@ end_grab_op (MetaWindow *window,
|
||||
update_move (window,
|
||||
modifiers & CLUTTER_SHIFT_MASK,
|
||||
x, y);
|
||||
|
||||
MetaTileZone tile_zone = get_tile_zone_for_pointer (window, x, y);
|
||||
if (tile_zone != 0)
|
||||
meta_window_tile (window, tile_zone);
|
||||
}
|
||||
else if (meta_grab_op_is_resizing (window->display->grab_op))
|
||||
{
|
||||
@ -5827,6 +5986,9 @@ end_grab_op (MetaWindow *window,
|
||||
TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
meta_screen_hide_tile_preview (window->screen);
|
||||
|
||||
meta_display_end_grab_op (window->display, clutter_event_get_time (event));
|
||||
}
|
||||
|
||||
@ -7643,3 +7805,36 @@ meta_window_emit_size_changed (MetaWindow *window)
|
||||
{
|
||||
g_signal_emit (window, window_signals[SIZE_CHANGED], 0);
|
||||
}
|
||||
|
||||
void
|
||||
meta_window_tile (MetaWindow *window,
|
||||
MetaTileZone tile_zone)
|
||||
{
|
||||
/* Special case: Tile maximizing is the same as maximizing. */
|
||||
if (tile_zone == META_TILE_ZONE_MAXIMIZED)
|
||||
{
|
||||
meta_window_maximize (window, META_MAXIMIZE_BOTH);
|
||||
return;
|
||||
}
|
||||
|
||||
MetaWindowSizeState *size_state = get_current_size_state (window);
|
||||
save_size_state (window, size_state, &window->rect);
|
||||
|
||||
window->constrained_edges = tile_zone;
|
||||
|
||||
if ((window->constrained_edges & META_DIRECTION_VERTICAL) == META_DIRECTION_VERTICAL)
|
||||
window->maximized_vertically = TRUE;
|
||||
if ((window->constrained_edges & META_DIRECTION_HORIZONTAL) == META_DIRECTION_HORIZONTAL)
|
||||
window->maximized_horizontally = TRUE;
|
||||
|
||||
meta_window_recalc_features (window);
|
||||
set_net_wm_state (window);
|
||||
|
||||
MetaRectangle target_rect;
|
||||
get_tile_zone_area (window, tile_zone, &target_rect);
|
||||
|
||||
meta_window_move_resize_internal (window,
|
||||
META_MOVE_RESIZE_MOVE_ACTION | META_MOVE_RESIZE_RESIZE_ACTION | META_MOVE_RESIZE_STATE_CHANGED,
|
||||
NorthWestGravity,
|
||||
target_rect);
|
||||
}
|
||||
|
@ -317,6 +317,26 @@ typedef enum
|
||||
META_DIRECTION_VERTICAL = META_DIRECTION_UP | META_DIRECTION_DOWN,
|
||||
} MetaDirection;
|
||||
|
||||
/* Tile zones are specified in terms of their constrained edges.
|
||||
* So, "top left corner" is top and left, which should be obvious,
|
||||
* but "left side of the screen" is top, bottom, and left
|
||||
*/
|
||||
typedef enum {
|
||||
META_TILE_ZONE_MAXIMIZED_HORZ = META_DIRECTION_HORIZONTAL,
|
||||
META_TILE_ZONE_MAXIMIZED_VERT = META_DIRECTION_VERTICAL,
|
||||
META_TILE_ZONE_MAXIMIZED = META_TILE_ZONE_MAXIMIZED_HORZ | META_TILE_ZONE_MAXIMIZED_VERT,
|
||||
|
||||
META_TILE_ZONE_W = META_TILE_ZONE_MAXIMIZED_VERT | META_DIRECTION_LEFT,
|
||||
META_TILE_ZONE_E = META_TILE_ZONE_MAXIMIZED_VERT | META_DIRECTION_RIGHT,
|
||||
META_TILE_ZONE_N = META_TILE_ZONE_MAXIMIZED_HORZ | META_DIRECTION_TOP,
|
||||
META_TILE_ZONE_S = META_TILE_ZONE_MAXIMIZED_HORZ | META_DIRECTION_BOTTOM,
|
||||
|
||||
META_TILE_ZONE_NW = META_DIRECTION_TOP | META_DIRECTION_LEFT,
|
||||
META_TILE_ZONE_NE = META_DIRECTION_TOP | META_DIRECTION_RIGHT,
|
||||
META_TILE_ZONE_SW = META_DIRECTION_BOTTOM | META_DIRECTION_LEFT,
|
||||
META_TILE_ZONE_SE = META_DIRECTION_BOTTOM | META_DIRECTION_RIGHT,
|
||||
} MetaTileZone;
|
||||
|
||||
/**
|
||||
* MetaMotionDirection:
|
||||
* @META_MOTION_UP: Upwards motion
|
||||
|
@ -258,4 +258,7 @@ gboolean meta_window_is_client_decorated (MetaWindow *window);
|
||||
gboolean meta_window_titlebar_is_onscreen (MetaWindow *window);
|
||||
void meta_window_shove_titlebar_onscreen (MetaWindow *window);
|
||||
|
||||
void meta_window_tile (MetaWindow *window,
|
||||
MetaTileZone tile_zone);
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user