Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/interface/view2d_ops.c')
-rw-r--r--source/blender/editors/interface/view2d_ops.c185
1 files changed, 185 insertions, 0 deletions
diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c
index 5b1e5f746ef..ff35b25e488 100644
--- a/source/blender/editors/interface/view2d_ops.c
+++ b/source/blender/editors/interface/view2d_ops.c
@@ -100,6 +100,10 @@ typedef struct v2dViewPanData {
/** for MMB in scrollers (old feature in past, but now not that useful) */
short in_scroller;
+
+ /* View2D Edge Panning */
+ double edge_pan_last_time;
+ double edge_pan_start_time_x, edge_pan_start_time_y;
} v2dViewPanData;
/* initialize panning customdata */
@@ -357,6 +361,186 @@ static void VIEW2D_OT_pan(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name View Edge Pan Operator (modal)
+ *
+ * Scroll the region if the mouse is dragged to an edge. "Invisible" operator that always
+ * passes through.
+ * \{ */
+
+/** Distance from the edge of the region within which to start panning. */
+#define EDGE_PAN_REGION_PAD (U.widget_unit)
+/** Speed factor in pixels per second per pixel of distance from edge pan zone beginning. */
+#define EDGE_PAN_SPEED_PER_PIXEL (25.0f * (float)U.dpi_fac)
+/** Delay before drag panning in seconds. */
+#define EDGE_PAN_DELAY 1.0f
+
+/* set up modal operator and relevant settings */
+static int view_edge_pan_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ /* Set up customdata. */
+ if (!view_pan_init(C, op)) {
+ return OPERATOR_PASS_THROUGH;
+ }
+
+ v2dViewPanData *vpd = op->customdata;
+
+ vpd->edge_pan_start_time_x = 0.0;
+ vpd->edge_pan_start_time_y = 0.0;
+ vpd->edge_pan_last_time = PIL_check_seconds_timer();
+
+ WM_event_add_modal_handler(C, op);
+
+ return (OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH);
+}
+
+/**
+ * Reset the edge pan timers if the mouse isn't in the scroll zone and
+ * start the timers when the mouse enters a scroll zone.
+ */
+static void edge_pan_manage_delay_timers(v2dViewPanData *vpd,
+ int pan_dir_x,
+ int pan_dir_y,
+ const double current_time)
+{
+ if (pan_dir_x == 0) {
+ vpd->edge_pan_start_time_x = 0.0;
+ }
+ else if (vpd->edge_pan_start_time_x == 0.0) {
+ vpd->edge_pan_start_time_x = current_time;
+ }
+ if (pan_dir_y == 0) {
+ vpd->edge_pan_start_time_y = 0.0;
+ }
+ else if (vpd->edge_pan_start_time_y == 0.0) {
+ vpd->edge_pan_start_time_y = current_time;
+ }
+}
+
+/**
+ * Used to calculate a "fade in" factor for edge panning to make the interaction feel smooth
+ * and more purposeful.
+ *
+ * \note Assumes a domain_min of 0.0f.
+ */
+static float smootherstep(const float domain_max, float x)
+{
+ x = clamp_f(x / domain_max, 0.0, 1.0);
+ return x * x * x * (x * (x * 6.0 - 15.0) + 10.0);
+}
+
+static float edge_pan_speed(v2dViewPanData *vpd,
+ int event_loc,
+ bool x_dir,
+ const double current_time)
+{
+ ARegion *region = vpd->region;
+
+ /* Find the distance from the start of the drag zone. */
+ int min = x_dir ? region->winrct.xmin : region->winrct.ymin + EDGE_PAN_REGION_PAD;
+ int max = x_dir ? region->winrct.xmax : region->winrct.ymax - EDGE_PAN_REGION_PAD;
+ int distance = 0.0;
+ if (event_loc > max) {
+ distance = event_loc - max;
+ }
+ else if (event_loc < min) {
+ distance = min - event_loc;
+ }
+ else {
+ BLI_assert(!"Calculating speed outside of pan zones");
+ return 0.0f;
+ }
+
+ /* Apply a fade in to the speed based on a start time delay. */
+ double start_time = x_dir ? vpd->edge_pan_start_time_x : vpd->edge_pan_start_time_y;
+ float delay_factor = smootherstep(EDGE_PAN_DELAY, (float)(current_time - start_time));
+
+ return distance * EDGE_PAN_SPEED_PER_PIXEL * delay_factor;
+}
+
+static int view_edge_pan_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ v2dViewPanData *vpd = op->customdata;
+ ARegion *region = vpd->region;
+
+ if (event->val == KM_RELEASE || event->type == EVT_ESCKEY) {
+ view_pan_exit(op);
+ return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH);
+ }
+ /* Only mousemove events matter here, ignore others. */
+ if (event->type != MOUSEMOVE) {
+ return OPERATOR_PASS_THROUGH;
+ }
+
+ /* This operator is supposed to run together with some drag action.
+ * On successful handling, always pass events on to other handlers. */
+ const int success_retval = OPERATOR_PASS_THROUGH;
+
+ /* Find whether the mouse is beyond X and Y edges. */
+ int pan_dir_x = 0;
+ int pan_dir_y = 0;
+ if (event->x > region->winrct.xmax - EDGE_PAN_REGION_PAD) {
+ pan_dir_x = 1;
+ }
+ else if (event->x < region->winrct.xmin + EDGE_PAN_REGION_PAD) {
+ pan_dir_x = -1;
+ }
+ if (event->y > region->winrct.ymax - EDGE_PAN_REGION_PAD) {
+ pan_dir_y = 1;
+ }
+ else if (event->y < region->winrct.ymin + EDGE_PAN_REGION_PAD) {
+ pan_dir_y = -1;
+ }
+
+ const double current_time = PIL_check_seconds_timer();
+ edge_pan_manage_delay_timers(vpd, pan_dir_x, pan_dir_y, current_time);
+
+ /* Calculate the delta since the last time the operator was called. */
+ float dtime = (float)(current_time - vpd->edge_pan_last_time);
+ float dx = 0.0f, dy = 0.0f;
+ if (pan_dir_x != 0) {
+ float speed = edge_pan_speed(vpd, event->x, true, current_time);
+ dx = dtime * speed * (float)pan_dir_x;
+ }
+ if (pan_dir_y != 0) {
+ float speed = edge_pan_speed(vpd, event->y, false, current_time);
+ dy = dtime * speed * (float)pan_dir_y;
+ }
+ vpd->edge_pan_last_time = current_time;
+
+ /* Pan, clamping inside the regions's total bounds. */
+ view_pan_apply_ex(C, vpd, dx, dy);
+
+ return success_retval;
+}
+
+static void view_edge_pan_cancel(bContext *UNUSED(C), wmOperator *op)
+{
+ view_pan_exit(op);
+}
+
+static void VIEW2D_OT_edge_pan(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "View Edge Pan";
+ ot->description = "Pan the view when the mouse is held at an edge";
+ ot->idname = "VIEW2D_OT_edge_pan";
+
+ /* api callbacks */
+ ot->invoke = view_edge_pan_invoke;
+ ot->modal = view_edge_pan_modal;
+ ot->cancel = view_edge_pan_cancel;
+
+ /* operator is modal */
+ ot->flag = OPTYPE_INTERNAL;
+}
+
+#undef EDGE_PAN_REGION_PAD
+#undef EDGE_PAN_SPEED_PER_PIXEL
+#undef EDGE_PAN_DELAY
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name View Pan Operator (single step)
* \{ */
@@ -2246,6 +2430,7 @@ static void VIEW2D_OT_reset(wmOperatorType *ot)
void ED_operatortypes_view2d(void)
{
WM_operatortype_append(VIEW2D_OT_pan);
+ WM_operatortype_append(VIEW2D_OT_edge_pan);
WM_operatortype_append(VIEW2D_OT_scroll_left);
WM_operatortype_append(VIEW2D_OT_scroll_right);