diff options
| author | Diederik van Lierop <mail@diedenrezi.nl> | 2009-01-25 13:35:08 +0000 |
|---|---|---|
| committer | dvlierop2 <dvlierop2@users.sourceforge.net> | 2009-01-25 13:35:08 +0000 |
| commit | 3b35e676c9107f8627587365f6e01ebf0093e9c5 (patch) | |
| tree | 8e5d306024820cf9b2adf2e5b4d4735959c8803e /src/display | |
| parent | removing void files (diff) | |
| download | inkscape-3b35e676c9107f8627587365f6e01ebf0093e9c5.tar.gz inkscape-3b35e676c9107f8627587365f6e01ebf0093e9c5.zip | |
- The snap-delay mechanism should now be more robust. From now on, it must be turned on and off explicitely within each context. This prevents delayed snapping events from being fired after the context or context's state has changed.
- Creating single dots now snaps
(bzr r7172)
Diffstat (limited to 'src/display')
| -rw-r--r-- | src/display/sp-canvas.cpp | 171 | ||||
| -rw-r--r-- | src/display/sp-canvas.h | 5 |
2 files changed, 101 insertions, 75 deletions
diff --git a/src/display/sp-canvas.cpp b/src/display/sp-canvas.cpp index e5ef2c80d..edd64f021 100644 --- a/src/display/sp-canvas.cpp +++ b/src/display/sp-canvas.cpp @@ -946,8 +946,8 @@ static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int n static void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val); static int do_update (SPCanvas *canvas); -static gboolean sp_canvas_snap_watchdog_callback(gpointer data); -static void sp_canvas_snap_watchdog_set(SPCanvas *canvas, GdkEventMotion *event); +static gboolean sp_canvas_snap_watchdog_callback(gpointer data); +static void sp_canvas_snap_watchdog_set(SPCanvas *canvas, GdkEventMotion *event); static void sp_canvas_snap_watchdog_kill(SPCanvas *canvas); /** @@ -1049,6 +1049,7 @@ sp_canvas_init (SPCanvas *canvas) canvas->watchdog_id = 0; canvas->watchdog_event = NULL; + canvas->context_snap_delay_active = false; } /** @@ -1234,7 +1235,7 @@ sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation) } widget->allocation = *allocation; - + if (GTK_WIDGET_REALIZED (widget)) { gdk_window_move_resize (widget->window, widget->allocation.x, widget->allocation.y, @@ -1531,12 +1532,12 @@ sp_canvas_button (GtkWidget *widget, GdkEventButton *event) case GDK_BUTTON_PRESS: case GDK_2BUTTON_PRESS: case GDK_3BUTTON_PRESS: - if (dt) { + if (dt) { // Snapping will be on hold if we're moving the mouse at high speeds. When starting - // drawing a new shape we really should snap though. + // drawing a new shape we really should snap though. dt->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false); } - + /* Pick the current item as if the button were not pressed, and * then process the event. */ @@ -1547,8 +1548,8 @@ sp_canvas_button (GtkWidget *widget, GdkEventButton *event) break; case GDK_BUTTON_RELEASE: - sp_canvas_snap_watchdog_callback(canvas); // If we have any pending snapping action, then invoke it now - + sp_canvas_snap_watchdog_callback(canvas); // If we have any pending snapping action, then invoke it now + /* Process the event as if the button were pressed, then repick * after the button has been released */ @@ -1558,7 +1559,7 @@ sp_canvas_button (GtkWidget *widget, GdkEventButton *event) canvas->state = event->state; pick_current_item (canvas, (GdkEvent *) event); event->state ^= mask; - + break; default: @@ -1594,7 +1595,7 @@ sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event) { static guint32 prev_time; static boost::optional<Geom::Point> prev_pos; - + int status; SPCanvas *canvas = SP_CANVAS (widget); @@ -1605,61 +1606,53 @@ sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event) if (canvas->pixmap_gc == NULL) // canvas being deleted return FALSE; - + SPDesktop *dt = SP_ACTIVE_DESKTOP; - + // Snap when speed drops below e.g. 0.02 px/msec, or when no motion events have occured for some period. // i.e. snap when we're at stand still. A speed threshold enforces snapping for tablets, which might never // be fully at stand still and might keep spitting out motion events. - if (dt) { - bool const c1 = event->type == GDK_MOTION_NOTIFY; - bool const c21 = event->state & GDK_BUTTON1_MASK; // Snapping only occurs when dragging with the left mouse button down - bool const c22 = event->state & GDK_BUTTON2_MASK; // We shouldn't hold back any events when other mouse buttons have been - bool const c23 = event->state & GDK_BUTTON3_MASK; // pressed, e.g. when scrolling with the middle mouse button; if we do then - // Inkscape will get stuck in an unresponsive state - bool const c3 = dt->namedview->snap_manager.snapprefs.getSnapEnabledGlobally(); - if (c1 && c21 && (!c22) && (!c23) && c3) { - Geom::Point event_pos(event->x, event->y); - guint32 event_t = gdk_event_get_time ( (GdkEvent *) event ); - - dt->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(true); // put snapping on hold - - if (prev_pos) { - Geom::Coord dist = Geom::L2(event_pos - *prev_pos); - guint32 delta_t = event_t - prev_time; - gdouble speed = delta_t > 0 ? dist/delta_t : 1000; - // std::cout << "speed = " << speed << " px/msec " << "| time passed = " << delta_t << " msec" << std::endl; - if (speed > 0.02) { // Jitter threshold, might be needed for tablets - // We're moving fast, so postpone any snapping until the next GDK_MOTION_NOTIFY event. We - // will keep on postponing the snapping as long as the speed is high. - // We must snap at some point in time though, so set a watchdog timer at some time from - // now, just in case there's no future motion event that drops under the speed limit (when - // stoppping abruptly) - sp_canvas_snap_watchdog_kill(canvas); - sp_canvas_snap_watchdog_set(canvas, event); // watchdog is reset, i.e. pushed forward in time - // If the watchdog expires before a new motion event is received, we will snap (as explained - // above). This means however that when the timer is too short, we will always snap and that the - // speed threshold is ineffective. In the extreme case the delay is set to zero, and snapping will - // be immediate, as it used to be in the old days ;-). - } else { // Speed is very low, so we're virtually at stand still - // But if we're really standing still, then we should snap now. We could use some low-pass filtering, - // otherwise snapping occurs for each jitter movement. For this filtering we'll leave the watchdog to expire, - // snap, and set a new watchdog again. - if (canvas->watchdog_id == 0) { // no watchdog has been set - // it might have already expired, so we'll set a new one; the snapping frequency will be limited by this - sp_canvas_snap_watchdog_set(canvas, event); - } // else: watchdog has been set before and we'll wait for it to expire - } - } else { - // This is the first GDK_MOTION_NOTIFY event, so postpone snapping and set the watchdog - sp_canvas_snap_watchdog_set(canvas, event); + if (canvas->context_snap_delay_active && dt && dt->namedview->snap_manager.snapprefs.getSnapEnabledGlobally()) { + Geom::Point event_pos(event->x, event->y); + guint32 event_t = gdk_event_get_time ( (GdkEvent *) event ); + + dt->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(true); // put snapping on hold + + if (prev_pos) { + Geom::Coord dist = Geom::L2(event_pos - *prev_pos); + guint32 delta_t = event_t - prev_time; + gdouble speed = delta_t > 0 ? dist/delta_t : 1000; + // std::cout << "speed = " << speed << " px/msec " << "| time passed = " << delta_t << " msec" << std::endl; + if (speed > 0.02) { // Jitter threshold, might be needed for tablets + // We're moving fast, so postpone any snapping until the next GDK_MOTION_NOTIFY event. We + // will keep on postponing the snapping as long as the speed is high. + // We must snap at some point in time though, so set a watchdog timer at some time from + // now, just in case there's no future motion event that drops under the speed limit (when + // stopping abruptly) + sp_canvas_snap_watchdog_kill(canvas); + sp_canvas_snap_watchdog_set(canvas, event); // watchdog is reset, i.e. pushed forward in time + // If the watchdog expires before a new motion event is received, we will snap (as explained + // above). This means however that when the timer is too short, we will always snap and that the + // speed threshold is ineffective. In the extreme case the delay is set to zero, and snapping will + // be immediate, as it used to be in the old days ;-). + } else { // Speed is very low, so we're virtually at stand still + // But if we're really standing still, then we should snap now. We could use some low-pass filtering, + // otherwise snapping occurs for each jitter movement. For this filtering we'll leave the watchdog to expire, + // snap, and set a new watchdog again. + if (canvas->watchdog_id == 0) { // no watchdog has been set + // it might have already expired, so we'll set a new one; the snapping frequency will be limited by this + sp_canvas_snap_watchdog_set(canvas, event); + } // else: watchdog has been set before and we'll wait for it to expire } - - prev_pos = event_pos; - prev_time = event_t; - } + } else { + // This is the first GDK_MOTION_NOTIFY event, so postpone snapping and set the watchdog + sp_canvas_snap_watchdog_set(canvas, event); + } + + prev_pos = event_pos; + prev_time = event_t; } - + canvas->state = event->state; pick_current_item (canvas, (GdkEvent *) event); @@ -1672,50 +1665,80 @@ sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event) return status; } -gboolean sp_canvas_snap_watchdog_callback(gpointer data) +gboolean sp_canvas_snap_watchdog_callback(gpointer data) { - // Snap NOW! For this the "postponed" flag will be reset and an the last motion event will be repeated + // Snap NOW! For this the "postponed" flag will be reset and an the last motion event will be repeated SPCanvas *canvas = reinterpret_cast<SPCanvas *>(data); if (!canvas->watchdog_event) { return FALSE; - } - + } + SPDesktop *dt = SP_ACTIVE_DESKTOP; if (dt) { dt->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false); } - + emit_event(canvas, canvas->watchdog_event); gdk_event_free(canvas->watchdog_event); canvas->watchdog_event = NULL; canvas->watchdog_id = 0; - + return FALSE; } -void sp_canvas_snap_watchdog_set(SPCanvas *canvas, GdkEventMotion *event) +void sp_canvas_snap_watchdog_set(SPCanvas *canvas, GdkEventMotion *event) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); double value = prefs->getDoubleLimited("/options/snapdelay/value", 0, 0, 1000); g_assert(canvas->watchdog_id == 0); canvas->watchdog_id = g_timeout_add(value, &sp_canvas_snap_watchdog_callback, canvas); g_assert(canvas->watchdog_event == NULL); - canvas->watchdog_event = gdk_event_copy( (GdkEvent *) event); + canvas->watchdog_event = gdk_event_copy( (GdkEvent *) event); } -void sp_canvas_snap_watchdog_kill(SPCanvas *canvas) +void sp_canvas_snap_watchdog_kill(SPCanvas *canvas) { - if (canvas->watchdog_id) { + if (canvas->watchdog_id) { g_source_remove(canvas->watchdog_id); // Kill the watchdog canvas->watchdog_id = 0; } - + if (canvas->watchdog_event) { gdk_event_free(canvas->watchdog_event); canvas->watchdog_event = NULL; } } - + +void sp_canvas_set_snap_delay_active(SPCanvas *canvas, bool snapping) +{ + // Only when canvas->context_snap_delay_active has been set, Inkscape will know that snapping is active + // and will delay any snapping events (but only when asked to through the preferences) + + // When snapping is being delayed, then that will also mean that at some point the last event + // might be re-triggered. This should only occur when Inkscape is still in the same tool or context, + // and even more specifically, the tool should even be in the same state. If for example snapping is being delayed while + // creating a rectangle, then the rect-context will be active and it will be in the "dragging" state + // (see the static boolean variable "dragging" in the sp_rect_context_root_handler). The procedure is + // as follows: call sp_canvas_set_snap_delay_active(*, TRUE) when entering the "dragging" state, which will delay + // snapping from that moment on, and call sp_canvas_set_snap_delay_active(*, FALSE) when leaving the "dragging" + // state. This last call will also make sure that any pending snap events will be canceled. + + if (!canvas) { + g_warning("sp_canvas_set_snap_delay_active() has been called without providing a canvas!"); + return; + } + + if (canvas->context_snap_delay_active == snapping) { + g_warning("Snapping was already allowed or disallowed! This is a bug, please report it."); + } + + canvas->context_snap_delay_active = snapping; + + if (snapping == false) { + sp_canvas_snap_watchdog_kill(canvas); // kill any pending snapping events + } +} + static void sp_canvas_paint_single_buffer (SPCanvas *canvas, int x0, int y0, int x1, int y1, int draw_x1, int draw_y1, int draw_x2, int draw_y2, int sw) { @@ -1731,7 +1754,7 @@ sp_canvas_paint_single_buffer (SPCanvas *canvas, int x0, int y0, int x1, int y1, // Mark the region clean sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0); - buf.buf_rowstride = sw * 4; + buf.buf_rowstride = sw * 4; buf.rect.x0 = x0; buf.rect.y0 = y0; buf.rect.x1 = x1; @@ -2020,7 +2043,7 @@ sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1) } else { // paths only, so 1M works faster // 1M is the cached buffer and we need 4 channels - setup.max_pixels = 262144; + setup.max_pixels = 262144; } // Start the clock @@ -2282,7 +2305,7 @@ sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels) int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do! - int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the + int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the int dy = iy - canvas->y0; // canvas w.r.t its previous position canvas->dx0 = cx; // here the 'd' stands for double, not delta! diff --git a/src/display/sp-canvas.h b/src/display/sp-canvas.h index 2c416b8a7..cb63374e1 100644 --- a/src/display/sp-canvas.h +++ b/src/display/sp-canvas.h @@ -193,9 +193,10 @@ struct SPCanvas { Geom::Rect getViewbox() const; NR::IRect getViewboxIntegers() const; - + guint watchdog_id; GdkEvent *watchdog_event; + bool context_snap_delay_active; }; GtkWidget *sp_canvas_new_aa(); @@ -217,6 +218,8 @@ void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double wor Geom::Point sp_canvas_window_to_world(SPCanvas const *canvas, Geom::Point const win); Geom::Point sp_canvas_world_to_window(SPCanvas const *canvas, Geom::Point const world); +void sp_canvas_set_snap_delay_active(SPCanvas *canvas, bool snapping); + #endif // SEEN_SP_CANVAS_H /* |
