Manipulator: Support click only manipulator's
authorCampbell Barton <ideasman42@gmail.com>
Tue, 19 Dec 2017 01:31:40 +0000 (12:31 +1100)
committerCampbell Barton <ideasman42@gmail.com>
Tue, 19 Dec 2017 01:49:02 +0000 (12:49 +1100)
Makes the 3D view navigation widget easier to use: dragging anywhere
in the rotation region now rotates without having to avoid the XYZ axis
hotspots which only activate on a single click.

Logic for drag detection is complicated by manipulators reliance
on keeping the modal operator running.
Currently this is wrapped in an ifdef,
we may want to implement it differently later.

source/blender/editors/space_view3d/view3d_manipulator_navigate.c
source/blender/editors/space_view3d/view3d_manipulator_navigate_type.c
source/blender/windowmanager/manipulators/WM_manipulator_types.h
source/blender/windowmanager/manipulators/intern/wm_manipulator.c
source/blender/windowmanager/manipulators/intern/wm_manipulator_group.c
source/blender/windowmanager/manipulators/intern/wm_manipulator_map.c

index b7a62c3e60fac742aeae1a2afc541108495b78b0..bcc067b8fc19572296d196ef04915c5f1f141563 100644 (file)
@@ -163,6 +163,9 @@ static void WIDGETGROUP_navigate_setup(const bContext *UNUSED(C), wmManipulatorG
                        PointerRNA *ptr = WM_manipulator_operator_set(mpr, mapping[part_index], ot_viewnumpad, NULL);
                        RNA_enum_set(ptr, "type", RV3D_VIEW_FRONT + part_index);
                }
+
+               /* When dragging an axis, use this instead. */
+               mpr->drag_part = 0;
        }
 
        mgroup->customdata = navgroup;
index b520f239ef53283cc56d2331c030f27c0b16f489..aac13339b6b70c29382e022fd8d2f6ea6592345d 100644 (file)
@@ -96,7 +96,7 @@ static void axis_geom_draw(
        };
        qsort(&axis_order, ARRAY_SIZE(axis_order), sizeof(axis_order[0]), BLI_sortutil_cmp_float);
 
-       const float scale_axis = 0.33f;
+       const float scale_axis = 0.25f;
        static const float axis_highlight[4] = {1, 1, 1, 1};
        static const float axis_black[4] = {0, 0, 0, 1};
        static float axis_color[3][4];
index e8b81e1a78cf6f8bcd5f5d2a012a5246278c1385..f2c0a8e93d94302dae6842acac5327c56523a990 100644 (file)
@@ -182,6 +182,8 @@ struct wmManipulator {
        /* Optional ID for highlighting different parts of this manipulator.
         * -1 when unset, otherwise a valid index. (Used as index to 'op_data'). */
        int highlight_part;
+       /* For single click button manipulators, use a different part as a fallback, -1 when unused. */
+       int drag_part;
 
        /* Transformation of the manipulator in 2d or 3d space.
         * - Matrix axis are expected to be unit length (scale is applied after).
index 7ca1f8fd5f2fc6969d8456cd4ad99790e5b2d7d2..5190a4932b40628278768fa3bb07666422db2e84 100644 (file)
@@ -98,6 +98,8 @@ static wmManipulator *wm_manipulator_create(
        unit_m4(mpr->matrix_basis);
        unit_m4(mpr->matrix_offset);
 
+       mpr->drag_part = -1;
+
        return mpr;
 }
 
index edf10afad8c832f5451c486e6c06aa327ab5bc43..10a76beada9390c0a94094f0a58c510163fe901f 100644 (file)
@@ -39,6 +39,7 @@
 
 #include "BLI_listbase.h"
 #include "BLI_string.h"
+#include "BLI_math.h"
 
 #include "BKE_context.h"
 #include "BKE_main.h"
 #  include "BPY_extern.h"
 #endif
 
+/* Allow manipulator part's to be single click only,
+ * dragging falls back to activating their 'drag_part' action. */
+#define USE_DRAG_DETECT
+
 /* -------------------------------------------------------------------- */
 /** \name wmManipulatorGroup
  *
@@ -307,15 +312,71 @@ typedef struct ManipulatorTweakData {
 
        int init_event; /* initial event type */
        int flag;       /* tweak flags */
+
+#ifdef USE_DRAG_DETECT
+       /* True until the mouse is moved (only use when the operator has no modal).
+        * this allows some manipulators to be click-only. */
+       enum {
+               /* Don't detect dragging. */
+               DRAG_NOP = 0,
+               /* Detect dragging (wait until a drag or click is detected). */
+               DRAG_DETECT,
+               /* Drag has started, idle until there is no active modal operator.
+                * This is needed because finishing the modal operator also exits
+                * the modal manipulator state (un-grabbs the cursor).
+                * Ideally this workaround could be removed later. */
+               DRAG_IDLE,
+       } drag_state;
+#endif
+
 } ManipulatorTweakData;
 
-static void manipulator_tweak_finish(bContext *C, wmOperator *op, const bool cancel)
+static bool manipulator_tweak_start(
+        bContext *C, wmManipulatorMap *mmap, wmManipulator *mpr, const wmEvent *event)
+{
+       /* activate highlighted manipulator */
+       wm_manipulatormap_modal_set(mmap, C, mpr, event, true);
+
+       return (mpr->state & WM_MANIPULATOR_STATE_MODAL);
+}
+
+static bool manipulator_tweak_start_and_finish(
+        bContext *C, wmManipulatorMap *mmap, wmManipulator *mpr, const wmEvent *event, bool *r_is_modal)
+{
+       wmManipulatorOpElem *mpop = WM_manipulator_operator_get(mpr, mpr->highlight_part);
+       if (r_is_modal) {
+               *r_is_modal = false;
+       }
+       if (mpop && mpop->type) {
+               /* XXX temporary workaround for modal manipulator operator
+                * conflicting with modal operator attached to manipulator */
+               if (mpop->type->modal) {
+                       /* activate highlighted manipulator */
+                       wm_manipulatormap_modal_set(mmap, C, mpr, event, true);
+                       if (r_is_modal) {
+                               *r_is_modal = true;
+                       }
+               }
+               else {
+                       /* Allow for 'button' manipulators, single click to run an action. */
+                       WM_operator_name_call_ptr(C, mpop->type, WM_OP_INVOKE_DEFAULT, &mpop->ptr);
+               }
+               return true;
+       }
+       else {
+               return false;
+       }
+}
+
+static void manipulator_tweak_finish(bContext *C, wmOperator *op, const bool cancel, bool clear_modal)
 {
        ManipulatorTweakData *mtweak = op->customdata;
        if (mtweak->mpr_modal->type->exit) {
                mtweak->mpr_modal->type->exit(C, mtweak->mpr_modal, cancel);
        }
-       wm_manipulatormap_modal_set(mtweak->mmap, C, mtweak->mpr_modal, NULL, false);
+       if (clear_modal) {
+               wm_manipulatormap_modal_set(mtweak->mmap, C, mtweak->mpr_modal, NULL, false);
+       }
        MEM_freeN(mtweak);
 }
 
@@ -324,13 +385,57 @@ static int manipulator_tweak_modal(bContext *C, wmOperator *op, const wmEvent *e
        ManipulatorTweakData *mtweak = op->customdata;
        wmManipulator *mpr = mtweak->mpr_modal;
        int retval = OPERATOR_PASS_THROUGH;
+       bool clear_modal = true;
 
        if (mpr == NULL) {
                BLI_assert(0);
                return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH);
        }
 
-       if (event->type == mtweak->init_event && event->val == KM_RELEASE) {
+#ifdef USE_DRAG_DETECT
+       wmManipulatorMap *mmap = mtweak->mmap;
+       if (mtweak->drag_state == DRAG_DETECT) {
+               if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+                       if (len_manhattan_v2v2_int(&event->x, mmap->mmap_context.event_xy) > 2) {
+                               mtweak->drag_state = DRAG_IDLE;
+                               mpr->highlight_part = mpr->drag_part;
+                       }
+               }
+               else if (event->type == mtweak->init_event && event->val == KM_RELEASE) {
+                       mtweak->drag_state = DRAG_NOP;
+                       retval = OPERATOR_FINISHED;
+               }
+
+               if (mtweak->drag_state != DRAG_DETECT) {
+                       /* Follow logic in 'manipulator_tweak_invoke' */
+                       bool is_modal = false;
+                       if (manipulator_tweak_start_and_finish(C, mmap, mpr, event, &is_modal)) {
+                               if (is_modal) {
+                                       clear_modal = false;
+                               }
+                       }
+                       else {
+                               if (!manipulator_tweak_start(C, mmap, mpr, event)) {
+                                       retval = OPERATOR_FINISHED;
+                               }
+                       }
+               }
+       }
+       if (mtweak->drag_state == DRAG_IDLE) {
+               if (mmap->mmap_context.modal != NULL) {
+                       return OPERATOR_PASS_THROUGH;
+               }
+               else {
+                       manipulator_tweak_finish(C, op, false, false);
+                       return OPERATOR_FINISHED;
+               }
+       }
+#endif  /* USE_DRAG_DETECT */
+
+       if (retval == OPERATOR_FINISHED) {
+               /* pass */
+       }
+       else if (event->type == mtweak->init_event && event->val == KM_RELEASE) {
                retval = OPERATOR_FINISHED;
        }
        else if (event->type == EVT_MODAL_MAP) {
@@ -358,7 +463,7 @@ static int manipulator_tweak_modal(bContext *C, wmOperator *op, const wmEvent *e
        }
 
        if (retval != OPERATOR_PASS_THROUGH) {
-               manipulator_tweak_finish(C, op, retval != OPERATOR_FINISHED);
+               manipulator_tweak_finish(C, op, retval != OPERATOR_FINISHED, clear_modal);
                return retval;
        }
 
@@ -368,7 +473,7 @@ static int manipulator_tweak_modal(bContext *C, wmOperator *op, const wmEvent *e
                int modal_retval = modal_fn(C, mpr, event, mtweak->flag);
 
                if ((modal_retval & OPERATOR_RUNNING_MODAL) == 0) {
-                       manipulator_tweak_finish(C, op, (modal_retval & OPERATOR_CANCELLED) != 0);
+                       manipulator_tweak_finish(C, op, (modal_retval & OPERATOR_CANCELLED) != 0, true);
                        return OPERATOR_FINISHED;
                }
 
@@ -394,30 +499,35 @@ static int manipulator_tweak_invoke(bContext *C, wmOperator *op, const wmEvent *
                return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH);
        }
 
-       wmManipulatorOpElem *mpop = WM_manipulator_operator_get(mpr, mpr->highlight_part);
+       bool use_drag_fallback = false;
 
-       /* Allow for 'button' manipulators, single click to run an action. */
-       if (mpop && mpop->type) {
-               if (mpop->type->modal == NULL) {
-                       WM_operator_name_call_ptr(C, mpop->type, WM_OP_INVOKE_DEFAULT, &mpop->ptr);
+#ifdef USE_DRAG_DETECT
+       use_drag_fallback = !ELEM(mpr->drag_part, -1, mpr->highlight_part);
+#endif
+
+       if (use_drag_fallback == false) {
+               if (manipulator_tweak_start_and_finish(C, mmap, mpr, event, NULL)) {
                        return OPERATOR_FINISHED;
                }
        }
 
-       /* activate highlighted manipulator */
-       wm_manipulatormap_modal_set(mmap, C, mpr, event, true);
-
-       /* XXX temporary workaround for modal manipulator operator
-        * conflicting with modal operator attached to manipulator */
-       if (mpop && mpop->type) {
-               if (mpop->type->modal) {
-                       return OPERATOR_FINISHED;
+       bool use_drag_detect = false;
+#ifdef USE_DRAG_DETECT
+       if (use_drag_fallback) {
+               wmManipulatorOpElem *mpop = WM_manipulator_operator_get(mpr, mpr->highlight_part);
+               if (mpop && mpop->type) {
+                       if (mpop->type->modal == NULL) {
+                               use_drag_detect = true;
+                       }
                }
        }
+#endif
 
-       /* Couldn't start the manipulator. */
-       if ((mpr->state & WM_MANIPULATOR_STATE_MODAL) == 0) {
-               return OPERATOR_PASS_THROUGH;
+       if (use_drag_detect == false) {
+               if (!manipulator_tweak_start(C, mmap, mpr, event)) {
+                       /* failed to start */
+                       return OPERATOR_PASS_THROUGH;
+               }
        }
 
        ManipulatorTweakData *mtweak = MEM_mallocN(sizeof(ManipulatorTweakData), __func__);
@@ -427,6 +537,10 @@ static int manipulator_tweak_invoke(bContext *C, wmOperator *op, const wmEvent *
        mtweak->mmap = mmap;
        mtweak->flag = 0;
 
+#ifdef USE_DRAG_DETECT
+       mtweak->drag_state = use_drag_detect ? DRAG_DETECT : DRAG_NOP;
+#endif
+
        op->customdata = mtweak;
 
        WM_event_add_modal_handler(C, op);
index ba3b405e612f819e75a8d265c8be53a27880f1bf..7e4d4f166466fd8e839d44c19d6c7af2c39799d0 100644 (file)
@@ -953,6 +953,8 @@ void wm_manipulatormap_modal_set(
                        ED_region_tag_redraw(CTX_wm_region(C));
                        WM_event_add_mousemove(C);
                }
+
+               mmap->mmap_context.event_xy[0] = INT_MAX;
        }
 }