From ad6558a2fa03a44e8e6ae845fe2ac3ab77eb7d50 Mon Sep 17 00:00:00 2001 From: Elijah Newren Date: Tue, 8 Nov 2005 12:45:21 +0000 Subject: Get edge auto-snapping to work again (and for the first time ever for 2005-11-08 Elijah Newren Get edge auto-snapping to work again (and for the first time ever for mouse resizing). NOTE THAT EDGE RESISTANCE IS STILL NOT COMPLETE AND I STILL HAVE A RIDICULOUSLY HUGE THRESHOLD FOR THAT REASON. ;-) * src/display.h: (struct MetaDisplay): new display->grab_last_used_state_for_resize field, so that xsync messages know whether to snap-resize or just normally resize. I don't know if this is the right way to do this or not... (meta_display_apply_edge_resistance): take an auto_snap parameter * src/display.c: (meta_display_begin_grab_op): initialize display->grab_last_used_state_for_resize (find_index_of_edge_near_position): declare the edge array to be const (find_nearest_position): new function, also binary-search-esque (apply_edge_snapping): simple front-end to find_nearest_position() so that it can be called for multiple edge arrays and the closest overall one used. (meta_display_apply_edge_resistance): take an auto_snap parameter and use apply_edge_snapping() instead of apply_edge_resistance() if it is set * src/window.c: (update_move): track the proposed window size so that we can compare the edge-resisted-modifications to both the original window and the size initially proposed, handle snapping too (update_resize_timeout, meta_window_handle_mouse_grab_op_event): make use of display->grab_last_used_state_for_resize so that the resize knows whether to snap or not. I hope this is the right way to do this. (update_resize): take a mask tracking which buttons are pressed (like update_move), save the mask to window->display->grab_last_used_state_for_resize, handle snapping (meta_window_handle_mouse_grab_op_event): pass the xbutton/xmotion/xcrossing modifier state to update_resize() --- ChangeLog | 57 ++++++++++++++ src/display.c | 237 +++++++++++++++++++++++++++++++++++++++++++++++++++------- src/display.h | 4 +- src/window.c | 79 +++++++++++--------- 4 files changed, 313 insertions(+), 64 deletions(-) diff --git a/ChangeLog b/ChangeLog index 64a5d20c..c9c527b4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,60 @@ +2005-11-08 Elijah Newren + + Get edge auto-snapping to work again (and for the first time ever + for mouse resizing). NOTE THAT EDGE RESISTANCE IS STILL NOT + COMPLETE AND I STILL HAVE A RIDICULOUSLY HUGE THRESHOLD FOR THAT + REASON. ;-) + + * src/display.h: + + (struct MetaDisplay): + new display->grab_last_used_state_for_resize field, so that xsync + messages know whether to snap-resize or just normally resize. I + don't know if this is the right way to do this or not... + + (meta_display_apply_edge_resistance): + take an auto_snap parameter + + * src/display.c: + + (meta_display_begin_grab_op): + initialize display->grab_last_used_state_for_resize + + (find_index_of_edge_near_position): + declare the edge array to be const + + (find_nearest_position): + new function, also binary-search-esque + + (apply_edge_snapping): + simple front-end to find_nearest_position() so that it can be + called for multiple edge arrays and the closest overall one used. + + (meta_display_apply_edge_resistance): + take an auto_snap parameter and use apply_edge_snapping() instead + of apply_edge_resistance() if it is set + + * src/window.c: + + (update_move): + track the proposed window size so that we can compare the + edge-resisted-modifications to both the original window and the + size initially proposed, handle snapping too + + (update_resize_timeout, meta_window_handle_mouse_grab_op_event): + make use of display->grab_last_used_state_for_resize so that the + resize knows whether to snap or not. I hope this is the right way + to do this. + + (update_resize): + take a mask tracking which buttons are pressed (like update_move), + save the mask to window->display->grab_last_used_state_for_resize, + handle snapping + + (meta_window_handle_mouse_grab_op_event): + pass the xbutton/xmotion/xcrossing modifier state to + update_resize() + 2005-11-08 Elijah Newren * src/window.c (update_resize): get basic edge resistance to work diff --git a/src/display.c b/src/display.c index 1f97327f..e9ca3416 100644 --- a/src/display.c +++ b/src/display.c @@ -3303,6 +3303,7 @@ meta_display_begin_grab_op (MetaDisplay *display, display->grab_old_window_stacking = NULL; #ifdef HAVE_XSYNC display->grab_sync_request_alarm = None; + display->grab_last_used_state_for_resize = 0; #endif display->grab_was_cancelled = FALSE; @@ -3771,12 +3772,11 @@ meta_display_ungrab_focus_window_button (MetaDisplay *display, /***** Begin gobs of edge_resistance functions *****/ static int -find_index_of_edge_near_position (GArray *edges, - int position, - gboolean want_interval_min, - gboolean horizontal) +find_index_of_edge_near_position (const GArray *edges, + int position, + gboolean want_interval_min, + gboolean horizontal) { - /* This is basically like a binary search, except that we're trying to * find a range instead of an exact value. So, if we have in our array * Value: 3 27 316 316 316 505 522 800 1213 @@ -3874,6 +3874,119 @@ find_index_of_edge_near_position (GArray *edges, } } +static int +find_nearest_position (const GArray *edges, + int position, + int old_position, + const MetaRectangle *new_rect, + gboolean horizontal) +{ + /* This is basically just a binary search except that we're looking + * for the value closest to position, rather than finding that + * actual value. Also, we ignore any edges that aren't relevant + * given the horizontal/vertical position of new_rect. + */ + int low, high, mid; + int compare; + MetaEdge *edge; + int best, best_dist, i; + + /* Initialize mid, edge, & compare in the off change that the array only + * has one element. + */ + mid = 0; + edge = g_array_index (edges, MetaEdge*, mid); + compare = horizontal ? edge->rect.x : edge->rect.y; + + /* Begin the search... */ + low = 0; + high = edges->len - 1; + while (low < high) + { + mid = low + (high - low)/2; + edge = g_array_index (edges, MetaEdge*, mid); + compare = horizontal ? edge->rect.x : edge->rect.y; + + if (compare == position) + break; + + if (compare > position) + high = mid - 1; + else + low = mid + 1; + } + + /* mid should now be _really_ close to the index we want, so we + * start searching nearby for something that overlaps and is closer + * than the original position. + */ + best = old_position; + best_dist = INT_MAX; + + /* Start the search at mid */ + edge = g_array_index (edges, MetaEdge*, mid); + compare = horizontal ? edge->rect.x : edge->rect.y; + gboolean edges_align = horizontal ? + meta_rectangle_vert_overlap (&edge->rect, new_rect) : + meta_rectangle_horiz_overlap (&edge->rect, new_rect); + if (edges_align) + { + int dist = ABS (compare - position); + if (dist < best_dist) + { + best = compare; + best_dist = dist; + } + } + + /* Now start searching higher than mid */ + for (i = mid + 1; i < (int)edges->len; i++) + { + edge = g_array_index (edges, MetaEdge*, i); + compare = horizontal ? edge->rect.x : edge->rect.y; + + gboolean edges_align = horizontal ? + meta_rectangle_vert_overlap (&edge->rect, new_rect) : + meta_rectangle_horiz_overlap (&edge->rect, new_rect); + + if (edges_align) + { + int dist = ABS (compare - position); + if (dist < best_dist) + { + best = compare; + best_dist = dist; + } + break; + } + } + + /* Now start searching lower than mid */ + for (i = mid-1; i >= 0; i--) + { + edge = g_array_index (edges, MetaEdge*, i); + compare = horizontal ? edge->rect.x : edge->rect.y; + + gboolean edges_align = horizontal ? + meta_rectangle_vert_overlap (&edge->rect, new_rect) : + meta_rectangle_horiz_overlap (&edge->rect, new_rect); + + if (edges_align) + { + int dist = ABS (compare - position); + if (dist < best_dist) + { + best = compare; + best_dist = dist; + } + break; + } + } + + /* Return the best one found */ + return best; +} + static int apply_edge_resistance (int old_pos, int new_pos, @@ -3921,6 +4034,36 @@ apply_edge_resistance (int old_pos, return new_pos; } +static int +apply_edge_snapping (int old_pos, + int new_pos, + const MetaRectangle *new_rect, + GArray *edges1, + GArray *edges2, + gboolean xdir) +{ + int pos1, pos2; + + if (old_pos == new_pos) + return new_pos; + + pos1 = find_nearest_position (edges1, + new_pos, + old_pos, + new_rect, + xdir); + pos2 = find_nearest_position (edges2, + new_pos, + old_pos, + new_rect, + xdir); + + if (ABS (pos1 - new_pos) < ABS (pos2 - new_pos)) + return pos1; + else + return pos2; +} + /* This function takes the position (including any frame) of the window and * a proposed new position (ignoring edge resistance/snapping), and then * applies edge resistance to EACH edge (separately) updating new_outer. @@ -3931,7 +4074,8 @@ apply_edge_resistance (int old_pos, */ gboolean meta_display_apply_edge_resistance (MetaDisplay *display, const MetaRectangle *old_outer, - MetaRectangle *new_outer) + MetaRectangle *new_outer, + gboolean auto_snap) { /* FIXME: I need to know and use * a) whether this is mouse or keyboard resize (if keyboard resize, a @@ -3947,27 +4091,66 @@ gboolean meta_display_apply_edge_resistance (MetaDisplay *display, g_assert (display->grab_edge_resistance_data != NULL); edge_data = display->grab_edge_resistance_data; - /* Now, do the edge resistance application */ - new_left = apply_edge_resistance (BOX_LEFT (*old_outer), - BOX_LEFT (*new_outer), - new_outer, - edge_data->left_edges, - TRUE); - new_right = apply_edge_resistance (BOX_RIGHT (*old_outer), - BOX_RIGHT (*new_outer), - new_outer, - edge_data->right_edges, - TRUE); - new_top = apply_edge_resistance (BOX_TOP (*old_outer), - BOX_TOP (*new_outer), - new_outer, - edge_data->top_edges, - FALSE); - new_bottom = apply_edge_resistance (BOX_BOTTOM (*old_outer), - BOX_BOTTOM (*new_outer), - new_outer, - edge_data->bottom_edges, - FALSE); + if (auto_snap) + { + /* Do the auto snapping instead of normal edge resistance; in all + * cases, we allow snapping to opposite kinds of edges (e.g. left + * sides of windows to both left and right edges. + */ + + new_left = apply_edge_snapping (BOX_LEFT (*old_outer), + BOX_LEFT (*new_outer), + new_outer, + edge_data->left_edges, + edge_data->right_edges, + TRUE); + + new_right = apply_edge_snapping (BOX_RIGHT (*old_outer), + BOX_RIGHT (*new_outer), + new_outer, + edge_data->left_edges, + edge_data->right_edges, + TRUE); + + new_top = apply_edge_snapping (BOX_TOP (*old_outer), + BOX_TOP (*new_outer), + new_outer, + edge_data->top_edges, + edge_data->bottom_edges, + FALSE); + + new_bottom = apply_edge_snapping (BOX_BOTTOM (*old_outer), + BOX_BOTTOM (*new_outer), + new_outer, + edge_data->top_edges, + edge_data->bottom_edges, + FALSE); + + } + else + { + /* Now, apply the normal edge resistance */ + new_left = apply_edge_resistance (BOX_LEFT (*old_outer), + BOX_LEFT (*new_outer), + new_outer, + edge_data->left_edges, + TRUE); + new_right = apply_edge_resistance (BOX_RIGHT (*old_outer), + BOX_RIGHT (*new_outer), + new_outer, + edge_data->right_edges, + TRUE); + new_top = apply_edge_resistance (BOX_TOP (*old_outer), + BOX_TOP (*new_outer), + new_outer, + edge_data->top_edges, + FALSE); + new_bottom = apply_edge_resistance (BOX_BOTTOM (*old_outer), + BOX_BOTTOM (*new_outer), + new_outer, + edge_data->bottom_edges, + FALSE); + } /* Determine whether anything changed, and save the changes */ modified_rect = meta_rect (new_left, diff --git a/src/display.h b/src/display.h index 6a438127..74c5efe8 100644 --- a/src/display.h +++ b/src/display.h @@ -340,6 +340,7 @@ struct _MetaDisplay int render_error_base; #endif #ifdef HAVE_XSYNC + unsigned int grab_last_used_state_for_resize; unsigned int have_xsync : 1; #define META_DISPLAY_HAS_XSYNC(display) ((display)->have_xsync) #else @@ -451,7 +452,8 @@ void meta_display_ungrab_focus_window_button (MetaDisplay *display, gboolean meta_display_apply_edge_resistance (MetaDisplay *display, const MetaRectangle *old_outer, - MetaRectangle *new_outer); + MetaRectangle *new_outer, + gboolean auto_snap); /* make a request to ensure the event serial has changed */ void meta_display_increment_event_serial (MetaDisplay *display); diff --git a/src/window.c b/src/window.c index 229dc1ed..32029905 100644 --- a/src/window.c +++ b/src/window.c @@ -6377,7 +6377,7 @@ update_move (MetaWindow *window, int dx, dy; int new_x, new_y; int old_x, old_y; - MetaRectangle old_outer, new_outer; + MetaRectangle old_outer, proposed_outer, new_outer; int shake_threshold; window->display->grab_latest_motion_x = x; @@ -6495,13 +6495,15 @@ update_move (MetaWindow *window, /* Do any edge resistance/snapping */ meta_window_get_outer_rect (window, &old_outer); - new_outer = old_outer; - new_outer.x += (new_x - old_x); - new_outer.y += (new_y - old_y); + proposed_outer = old_outer; + proposed_outer.x += (new_x - old_x); + proposed_outer.y += (new_y - old_y); + new_outer = proposed_outer; if (meta_display_apply_edge_resistance (window->display, &old_outer, - &new_outer)) + &new_outer, + mask & ShiftMask)) { /* meta_display_apply_edge_resistance independently applies * resistance to both the right and left edges of new_outer as both @@ -6509,39 +6511,35 @@ update_move (MetaWindow *window, * just have both edges move according to the stricter of the * resistances. Same thing goes for top & bottom edges. */ + MetaRectangle *reference; int left_change, right_change, smaller_x_change; int top_change, bottom_change, smaller_y_change; - left_change = BOX_LEFT (new_outer) - BOX_LEFT (old_outer); - right_change = BOX_RIGHT (new_outer) - BOX_RIGHT (old_outer); + if (mask & ShiftMask) + reference = &proposed_outer; + else + reference = &old_outer; + + left_change = BOX_LEFT (new_outer) - BOX_LEFT (*reference); + right_change = BOX_RIGHT (new_outer) - BOX_RIGHT (*reference); if (ABS (left_change) < ABS (right_change)) smaller_x_change = left_change; else smaller_x_change = right_change; - top_change = BOX_TOP (new_outer) - BOX_TOP (old_outer); - bottom_change = BOX_BOTTOM (new_outer) - BOX_BOTTOM (old_outer); + top_change = BOX_TOP (new_outer) - BOX_TOP (*reference); + bottom_change = BOX_BOTTOM (new_outer) - BOX_BOTTOM (*reference); if (ABS (top_change) < ABS (bottom_change)) smaller_y_change = top_change; else smaller_y_change = bottom_change; - new_x = old_x + smaller_x_change; - new_y = old_y + smaller_y_change; + new_x = old_x + smaller_x_change + + (BOX_LEFT (*reference) - BOX_LEFT (old_outer)); + new_y = old_y + smaller_y_change + + (BOX_TOP (*reference) - BOX_TOP (old_outer)); } -#if 0 - if (mask & ShiftMask) - { - /* snap to edges */ - if (new_x != old_x) - new_x = meta_window_find_nearest_vertical_edge (window, new_x); - - if (new_y != old_y) - new_y = meta_window_find_nearest_horizontal_edge (window, new_y); - } -#endif - if (window->display->grab_wireframe_active) meta_window_update_wireframe (window, new_x, new_y, window->display->grab_wireframe_rect.width, @@ -6550,10 +6548,11 @@ update_move (MetaWindow *window, meta_window_move (window, TRUE, new_x, new_y); } -static void update_resize (MetaWindow *window, - int x, - int y, - gboolean force); +static void update_resize (MetaWindow *window, + unsigned int mask, + int x, + int y, + gboolean force); static gboolean update_resize_timeout (gpointer data) @@ -6561,14 +6560,16 @@ update_resize_timeout (gpointer data) MetaWindow *window = data; update_resize (window, - window->display->grab_latest_motion_x, - window->display->grab_latest_motion_y, - TRUE); + window->display->grab_last_used_state_for_resize, + window->display->grab_latest_motion_x, + window->display->grab_latest_motion_y, + TRUE); return FALSE; } static void update_resize (MetaWindow *window, + unsigned int mask, int x, int y, gboolean force) { @@ -6768,9 +6769,11 @@ update_resize (MetaWindow *window, new_outer_width, new_outer_height); + window->display->grab_last_used_state_for_resize = mask; if (meta_display_apply_edge_resistance (window->display, &old_outer, - &new_outer)) + &new_outer, + mask & ShiftMask)) { new_w = old.width + (new_outer.width - old_outer.width); new_h = old.height + (new_outer.height - old_outer.height); @@ -6909,6 +6912,7 @@ meta_window_handle_mouse_grab_op_event (MetaWindow *window, case META_GRAB_OP_KEYBOARD_RESIZING_NW: /* no pointer round trip here, to keep in sync */ update_resize (window, + window->display->grab_last_used_state_for_resize, window->display->grab_latest_motion_x, window->display->grab_latest_motion_y, TRUE); @@ -6933,9 +6937,10 @@ meta_window_handle_mouse_grab_op_event (MetaWindow *window, { if (event->xbutton.root == window->screen->xroot) update_resize (window, - event->xbutton.x_root, - event->xbutton.y_root, - TRUE); + event->xbutton.state, + event->xbutton.x_root, + event->xbutton.y_root, + TRUE); } meta_display_end_grab_op (window->display, event->xbutton.time); @@ -6961,9 +6966,10 @@ meta_window_handle_mouse_grab_op_event (MetaWindow *window, if (check_use_this_motion_notify (window, event)) update_resize (window, + event->xmotion.state, event->xmotion.x_root, event->xmotion.y_root, - FALSE); + FALSE); } } break; @@ -6983,9 +6989,10 @@ meta_window_handle_mouse_grab_op_event (MetaWindow *window, { if (event->xcrossing.root == window->screen->xroot) update_resize (window, + event->xcrossing.state, event->xcrossing.x_root, event->xcrossing.y_root, - FALSE); + FALSE); } break; #endif -- cgit v1.2.1