Sticky Keys backend
authorJulian Eisel <eiseljulian@gmail.com>
Fri, 3 Apr 2015 14:21:22 +0000 (16:21 +0200)
committerJulian Eisel <eiseljulian@gmail.com>
Fri, 3 Apr 2015 14:21:22 +0000 (16:21 +0200)
Design task: T42339
Differential Revision: D840
Initial implementation proposal: T41867

Short description:
With this we can distinguish between holding and tabbing a key. Useful
is this if we want to assign to operators to a single shortcut. If two
operators are assigned to one shortcut, we call this a sticky key.

More info is accessible through the design task and the diff.

A few people that were involved with this:
* Sean Olson for stressing me with this burden ;) - It is his enthusiasm
that pushed me forward to get this done
* Campbell and Antony for the code and design review
* Ton for the design review
* All the other people that gave feedback on the patch and helped to
make this possible

A big "Thank You" for you all!

28 files changed:
release/scripts/startup/bl_ui/space_userpref.py
source/blender/blenkernel/BKE_blender.h
source/blender/editors/armature/armature_ops.c
source/blender/editors/curve/curve_ops.c
source/blender/editors/gpencil/gpencil_paint.c
source/blender/editors/interface/interface_eyedropper.c
source/blender/editors/interface/interface_handlers.c
source/blender/editors/interface/interface_panel.c
source/blender/editors/interface/interface_regions.c
source/blender/editors/interface/resources.c
source/blender/editors/mesh/mesh_ops.c
source/blender/editors/object/object_ops.c
source/blender/editors/space_console/console_ops.c
source/blender/editors/space_file/space_file.c
source/blender/editors/space_graph/graph_ops.c
source/blender/editors/space_node/node_relationships.c
source/blender/editors/space_outliner/outliner_ops.c
source/blender/editors/space_text/text_ops.c
source/blender/editors/space_view3d/view3d_ops.c
source/blender/makesdna/DNA_userdef_types.h
source/blender/makesdna/DNA_windowmanager_types.h
source/blender/makesrna/intern/rna_userdef.c
source/blender/makesrna/intern/rna_wm.c
source/blender/windowmanager/WM_types.h
source/blender/windowmanager/intern/wm_event_system.c
source/blender/windowmanager/intern/wm_keymap.c
source/blender/windowmanager/intern/wm_operators.c
source/blender/windowmanager/intern/wm_window.c

index 3b17e40c29454dded683609fdb25800a119210c7..04020d0a1bb190dd58005d8af7b217c913e07a21 100644 (file)
@@ -1055,6 +1055,15 @@ class USERPREF_PT_input(Panel):
         subrow.menu("USERPREF_MT_interaction_presets", text=bpy.types.USERPREF_MT_interaction_presets.bl_label)
         subrow.operator("wm.interaction_preset_add", text="", icon='ZOOMIN')
         subrow.operator("wm.interaction_preset_add", text="", icon='ZOOMOUT').remove_active = True
+
+        sub.separator()
+
+        sub = col.column()
+        sub.label(text="Double Click:")
+        sub.prop(inputs, "double_click_time", text="Speed")
+        sub.label(text="Sticky Keys:")
+        sub.prop(inputs, "click_timeout")
+
         sub.separator()
 
         sub.label(text="Mouse:")
@@ -1068,10 +1077,6 @@ class USERPREF_PT_input(Panel):
         sub.label(text="Select With:")
         sub.row().prop(inputs, "select_mouse", expand=True)
 
-        sub = col.column()
-        sub.label(text="Double Click:")
-        sub.prop(inputs, "mouse_double_click_time", text="Speed")
-
         sub.separator()
 
         sub.prop(inputs, "use_emulate_numpad")
index e4d3fe4868b85d8449099761652f0ded0ec55631..a790715ac98cb17f39d3698c8a09ac9eb5351771 100644 (file)
@@ -42,7 +42,7 @@ extern "C" {
  * and keep comment above the defines.
  * Use STRINGIFY() rather than defining with quotes */
 #define BLENDER_VERSION         274
-#define BLENDER_SUBVERSION      2
+#define BLENDER_SUBVERSION      3
 /* Several breakages with 270, e.g. constraint deg vs rad */
 #define BLENDER_MINVERSION      270
 #define BLENDER_MINSUBVERSION   5
index 61c9dfb24fab496c568fe0359c509ca389de97f6..3f00068c3d26b44fba9fa4c6924f49c5d62aab3d 100644 (file)
@@ -271,7 +271,7 @@ void ED_keymap_armature(wmKeyConfig *keyconf)
        WM_keymap_add_item(keymap, "ARMATURE_OT_duplicate_move", DKEY, KM_PRESS, KM_SHIFT, 0);
        WM_keymap_add_item(keymap, "ARMATURE_OT_extrude_move", EKEY, KM_PRESS, 0, 0);
        WM_keymap_add_item(keymap, "ARMATURE_OT_extrude_forked", EKEY, KM_PRESS, KM_SHIFT, 0);
-       WM_keymap_add_item(keymap, "ARMATURE_OT_click_extrude", ACTIONMOUSE, KM_CLICK, KM_CTRL, 0);
+       WM_keymap_add_item(keymap, "ARMATURE_OT_click_extrude", ACTIONMOUSE, KM_PRESS, KM_CTRL, 0);
        WM_keymap_add_item(keymap, "ARMATURE_OT_fill", FKEY, KM_PRESS, 0, 0);
        WM_keymap_add_item(keymap, "ARMATURE_OT_merge", MKEY, KM_PRESS, KM_ALT, 0);
        WM_keymap_add_item(keymap, "ARMATURE_OT_split", YKEY, KM_PRESS, 0, 0);
index 4bcb16d31ef8dd851f61f78def4e8eca4cb6be4a..561c349edece8c95cbffaff5b10d11b36212b83c 100644 (file)
@@ -230,7 +230,7 @@ void ED_keymap_curve(wmKeyConfig *keyconf)
        
        WM_keymap_add_item(keymap, "CURVE_OT_handle_type_set", VKEY, KM_PRESS, 0, 0);
 
-       WM_keymap_add_item(keymap, "CURVE_OT_vertex_add", ACTIONMOUSE, KM_CLICK, KM_CTRL, 0);
+       WM_keymap_add_item(keymap, "CURVE_OT_vertex_add", ACTIONMOUSE, KM_PRESS, KM_CTRL, 0);
 
        kmi = WM_keymap_add_item(keymap, "CURVE_OT_select_all", AKEY, KM_PRESS, 0, 0);
        RNA_enum_set(kmi->ptr, "action", SEL_TOGGLE);
index c8774cb73a1e6024944a68a7badf6c7a092056d2..a299e23830d26e1ad0d8c3fa9ee28ff29a5ee82b 100644 (file)
@@ -1930,7 +1930,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
                                /* stroke could be smoothed, send notifier to refresh screen */
                                WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
                        }
-                       else {
+                       else if (event->val == KM_RELEASE) {
                                /* printf("\t\tGP - end of stroke + op\n"); */
                                p->status = GP_STATUS_DONE;
                                estate = OPERATOR_FINISHED;
index f5847a9d7013299830cf25fdcedc555fae6d5dda..96f9b87611f288d82982192004bc776750170072 100644 (file)
@@ -277,6 +277,10 @@ static int eyedropper_modal(bContext *C, wmOperator *op, const wmEvent *event)
                        eyedropper_cancel(C, op);
                        return OPERATOR_CANCELLED;
                case LEFTMOUSE:
+                       /* two release events are sent on KM_CLICK, so ignore second one */
+                       if (event->click_type == KM_CLICK) {
+                               break;
+                       }
                        if (event->val == KM_RELEASE) {
                                if (eye->accum_tot == 0) {
                                        eyedropper_color_sample(C, eye, event->x, event->y);
index bc35fde633c0d73946a5b723438200125c47dc0e..163502b423029fd058036316ec237647f3d89346 100644 (file)
@@ -1180,7 +1180,7 @@ static int ui_handler_region_drag_toggle(bContext *C, const wmEvent *event, void
        switch (event->type) {
                case LEFTMOUSE:
                {
-                       if (event->val != KM_PRESS) {
+                       if (event->val == KM_RELEASE) {
                                done = true;
                        }
                        break;
@@ -2721,8 +2721,7 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle
                        if (data->searchbox)
                                inbox = ui_searchbox_inside(data->searchbox, event->x, event->y);
 
-                       /* for double click: we do a press again for when you first click on button (selects all text, no cursor pos) */
-                       if (event->val == KM_PRESS || event->val == KM_DBL_CLICK) {
+                       if (event->val == KM_PRESS) {
                                float mx, my;
 
                                mx = event->x;
@@ -2747,7 +2746,7 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle
                        }
                        
                        /* only select a word in button if there was no selection before */
-                       if (event->val == KM_DBL_CLICK && had_selection == false) {
+                       if ((event->click_type == KM_DBL_CLICK) && (had_selection == false)) {
                                ui_textedit_move(but, data, STRCUR_DIR_PREV, false, STRCUR_JUMP_DELIM);
                                ui_textedit_move(but, data, STRCUR_DIR_NEXT, true, STRCUR_JUMP_DELIM);
                                retval = WM_UI_HANDLER_BREAK;
@@ -3171,7 +3170,7 @@ static int ui_do_but_BUT(bContext *C, uiBut *but, uiHandleButtonData *data, cons
                }
        }
        else if (data->state == BUTTON_STATE_WAIT_RELEASE) {
-               if (event->type == LEFTMOUSE && event->val != KM_PRESS) {
+               if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
                        if (!(but->flag & UI_SELECT))
                                data->cancel = true;
                        button_activate_state(C, but, BUTTON_STATE_EXIT);
@@ -3739,7 +3738,7 @@ static int ui_do_but_NUM(bContext *C, uiBlock *block, uiBut *but, uiHandleButton
                                button_activate_state(C, but, BUTTON_STATE_EXIT);
                        }
                }
-               else if (event->type == LEFTMOUSE && event->val != KM_PRESS) {
+               else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
                        if (data->dragchange) {
 #ifdef USE_DRAG_MULTINUM
                                /* if we started multibutton but didnt drag, then edit */
@@ -4036,7 +4035,7 @@ static int ui_do_but_SLI(bContext *C, uiBlock *block, uiBut *but, uiHandleButton
                                button_activate_state(C, but, BUTTON_STATE_EXIT);
                        }
                }
-               else if (event->type == LEFTMOUSE && event->val != KM_PRESS) {
+               else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
                        if (data->dragchange) {
 #ifdef USE_DRAG_MULTINUM
                                /* if we started multibutton but didnt drag, then edit */
@@ -4181,7 +4180,7 @@ static int ui_do_but_SCROLL(bContext *C, uiBlock *block, uiBut *but, uiHandleBut
                                button_activate_state(C, but, BUTTON_STATE_EXIT);
                        }
                }
-               else if (event->type == LEFTMOUSE && event->val != KM_PRESS) {
+               else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
                        button_activate_state(C, but, BUTTON_STATE_EXIT);
                }
                else if (event->type == MOUSEMOVE) {
@@ -4229,7 +4228,7 @@ static int ui_do_but_GRIP(bContext *C, uiBlock *block, uiBut *but, uiHandleButto
                                button_activate_state(C, but, BUTTON_STATE_EXIT);
                        }
                }
-               else if (event->type == LEFTMOUSE && event->val != KM_PRESS) {
+               else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
                        button_activate_state(C, but, BUTTON_STATE_EXIT);
                }
                else if (event->type == MOUSEMOVE) {
@@ -4253,7 +4252,7 @@ static int ui_do_but_LISTROW(bContext *C, uiBut *but, uiHandleButtonData *data,
                 * editing field for editing list item names
                 */
                if ((ELEM(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val == KM_PRESS && event->ctrl) ||
-                   (event->type == LEFTMOUSE && event->val == KM_DBL_CLICK))
+                   (event->type == LEFTMOUSE && event->click_type == KM_DBL_CLICK))
                {
                        uiBut *labelbut = ui_but_list_row_text_activate(C, but, data, event, BUTTON_ACTIVATE_TEXT_EDITING);
                        if (labelbut) {
@@ -4605,7 +4604,7 @@ static int ui_do_but_UNITVEC(bContext *C, uiBlock *block, uiBut *but, uiHandleBu
                                        ui_numedit_apply(C, block, but, data);
                        }
                }
-               else if (event->type == LEFTMOUSE && event->val != KM_PRESS) {
+               else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
                        button_activate_state(C, but, BUTTON_STATE_EXIT);
                }
 
@@ -4926,7 +4925,7 @@ static int ui_do_but_HSVCUBE(bContext *C, uiBlock *block, uiBut *but, uiHandleBu
                                        ui_numedit_apply(C, block, but, data);
                        }
                }
-               else if (event->type == LEFTMOUSE && event->val != KM_PRESS) {
+               else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
                        button_activate_state(C, but, BUTTON_STATE_EXIT);
                }
                
@@ -5199,7 +5198,7 @@ static int ui_do_but_HSVCIRCLE(bContext *C, uiBlock *block, uiBut *but, uiHandle
                                }
                        }
                }
-               else if (event->type == LEFTMOUSE && event->val != KM_PRESS) {
+               else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
                        button_activate_state(C, but, BUTTON_STATE_EXIT);
                }
                return WM_UI_HANDLER_BREAK;
@@ -5284,7 +5283,7 @@ static int ui_do_but_COLORBAND(bContext *C, uiBlock *block, uiBut *but, uiHandle
                                        ui_numedit_apply(C, block, but, data);
                        }
                }
-               else if (event->type == LEFTMOUSE && event->val != KM_PRESS) {
+               else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
                        button_activate_state(C, but, BUTTON_STATE_EXIT);
                }
                
@@ -5523,7 +5522,7 @@ static int ui_do_but_CURVE(bContext *C, uiBlock *block, uiBut *but, uiHandleButt
                                        ui_numedit_apply(C, block, but, data);
                        }
                }
-               else if (event->type == LEFTMOUSE && event->val != KM_PRESS) {
+               else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
                        if (data->dragsel != -1) {
                                CurveMapping *cumap = (CurveMapping *)but->poin;
                                CurveMap *cuma = cumap->cm + cumap->cur;
@@ -5619,7 +5618,7 @@ static int ui_do_but_HISTOGRAM(bContext *C, uiBlock *block, uiBut *but, uiHandle
                                        ui_numedit_apply(C, block, but, data);
                        }
                }
-               else if (event->type == LEFTMOUSE && event->val != KM_PRESS) {
+               else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
                        button_activate_state(C, but, BUTTON_STATE_EXIT);
                }
                return WM_UI_HANDLER_BREAK;
@@ -5692,7 +5691,7 @@ static int ui_do_but_WAVEFORM(bContext *C, uiBlock *block, uiBut *but, uiHandleB
                                        ui_numedit_apply(C, block, but, data);
                        }
                }
-               else if (event->type == LEFTMOUSE && event->val != KM_PRESS) {
+               else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
                        button_activate_state(C, but, BUTTON_STATE_EXIT);
                }
                return WM_UI_HANDLER_BREAK;
@@ -5717,7 +5716,7 @@ static int ui_do_but_LINK(bContext *C, uiBut *but, uiHandleButtonData *data, con
        }
        else if (data->state == BUTTON_STATE_WAIT_RELEASE) {
                
-               if (event->type == LEFTMOUSE && event->val != KM_PRESS) {
+               if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
                        if (!(but->flag & UI_SELECT))
                                data->cancel = true;
                        button_activate_state(C, but, BUTTON_STATE_EXIT);
@@ -5800,7 +5799,7 @@ static int ui_do_but_TRACKPREVIEW(bContext *C, uiBlock *block, uiBut *but, uiHan
                                        ui_numedit_apply(C, block, but, data);
                        }
                }
-               else if (event->type == LEFTMOUSE && event->val != KM_PRESS) {
+               else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
                        button_activate_state(C, but, BUTTON_STATE_EXIT);
                }
                return WM_UI_HANDLER_BREAK;
@@ -8316,7 +8315,7 @@ static int ui_handle_menu_event(
 
 #ifdef USE_DRAG_POPUP
        if (menu->is_grab) {
-               if (event->type == LEFTMOUSE) {
+               if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
                        menu->is_grab = false;
                        retval = WM_UI_HANDLER_BREAK;
                }
@@ -8601,7 +8600,7 @@ static int ui_handle_menu_event(
                                uiSafetyRct *saferct = block->saferct.first;
 
                                if (ELEM(event->type, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE) &&
-                                   ELEM(event->val, KM_PRESS, KM_DBL_CLICK))
+                                   (event->val == KM_PRESS || event->click_type == KM_DBL_CLICK))
                                {
                                        if ((is_parent_menu == false) && (U.uiflag & USER_MENUOPENAUTO) == 0) {
                                                /* for root menus, allow clicking to close */
@@ -8978,6 +8977,11 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle
 
                switch (event->type) {
                        case MOUSEMOVE:
+                               /* verify we have a real mousemove */
+                               if (event->x == event->prevx && event->y == event->prevy) {
+                                       break;
+                               }
+
                                if (!is_click_style) {
                                        float len_sq = len_squared_v2v2(event_xy, block->pie_data.pie_center_init);
 
@@ -9004,7 +9008,9 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle
 
                        case LEFTMOUSE:
                                if (is_click_style) {
-                                       if (block->pie_data.flags & UI_PIE_INVALID_DIR) {
+                                       /* the click_type test sends a second event on KM_CLICK that
+                                        * may destroy nested pies even before they are drawn */
+                                       if ((block->pie_data.flags & UI_PIE_INVALID_DIR) && (event->click_type != KM_CLICK)) {
                                                menu->menuretval = UI_RETURN_CANCEL;
                                        }
                                        else {
@@ -9045,9 +9051,7 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle
                        case YKEY:
                        case ZKEY:
                        {
-                               if ((event->val  == KM_PRESS || event->val == KM_DBL_CLICK) &&
-                                   !IS_EVENT_MOD(event, shift, ctrl, oskey))
-                               {
+                               if ((event->val  == KM_PRESS) && !IS_EVENT_MOD(event, shift, ctrl, oskey)) {
                                        for (but = block->buttons.first; but; but = but->next) {
                                                if (but->menu_key == event->type) {
                                                        ui_but_pie_button_activate(C, but, menu);
@@ -9154,8 +9158,9 @@ static int ui_handle_menus_recursive(
 
                        if (block->flag & UI_BLOCK_RADIAL)
                                retval = ui_pie_handler(C, event, menu);
-                       else if (event->type == LEFTMOUSE || event->val != KM_DBL_CLICK)
+                       else if ((event->type == LEFTMOUSE) || (event->click_type != KM_DBL_CLICK)) {
                                retval = ui_handle_menu_event(C, event, menu, level, is_parent_inside, is_parent_menu, is_floating);
+                       }
                }
        }
 
@@ -9191,10 +9196,16 @@ static int ui_region_handler(bContext *C, const wmEvent *event, void *UNUSED(use
                retval = ui_handle_list_event(C, event, ar);
 
        if (retval == WM_UI_HANDLER_CONTINUE) {
-               if (but)
+               if (but) {
                        retval = ui_handle_button_event(C, event, but);
-               else
+               }
+               else {
                        retval = ui_handle_button_over(C, event, ar);
+
+                       /* let's make sure we are really not hovering a button by adding a mousemove!
+                        * XXX some WM_event_add_mousemove calls may become unnecessary with this and can be removed */
+                       WM_event_add_mousemove(C);
+               }
        }
 
        /* re-enable tooltips */
index d165e2719c5156e4b9d0558e12dab32e2129ecf9..7d6d5c3a6d7ac30c9ac89abeec4ba873b7ff49de 100644 (file)
@@ -1865,7 +1865,7 @@ static int ui_handler_panel(bContext *C, const wmEvent *event, void *userdata)
        uiHandlePanelData *data = panel->activedata;
 
        /* verify if we can stop */
-       if (event->type == LEFTMOUSE && event->val != KM_PRESS) {
+       if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
                ScrArea *sa = CTX_wm_area(C);
                ARegion *ar = CTX_wm_region(C);
                int align = panel_aligned(sa, ar);
index 7cb9dca78bf8116372c95d79961b5fe6078a31bd..0fbd0c4051019c686d41d116c7c78be5342420aa 100644 (file)
@@ -2749,8 +2749,8 @@ uiPieMenu *UI_pie_menu_begin(struct bContext *C, const char *title, int icon, co
        pie->block_radial->puphash = ui_popup_menu_hash(title);
        pie->block_radial->flag |= UI_BLOCK_RADIAL;
 
-       /* if pie is spawned by a left click, it is always assumed to be click style */
-       if (event->type == LEFTMOUSE) {
+       /* if pie is spawned by a left click or on release, it is always assumed to be click style */
+       if ((event->type == LEFTMOUSE) || (event->val == KM_RELEASE)) {
                pie->block_radial->pie_data.flags |= UI_PIE_CLICK_STYLE;
                pie->block_radial->pie_data.event = EVENT_NONE;
                win->lock_pie_event = EVENT_NONE;
index 15e742c930a87a43c966fcea7209d0a9e8423d68..b7545199f56438a113179a649efa79ae705002df 100644 (file)
@@ -2602,7 +2602,12 @@ void init_userdef_do_versions(void)
                        cp[3] = 255;
                }
        }
-               
+
+       if (U.versionfile < 274 || (U.versionfile == 274 && U.subversionfile < 3)) {
+               if (U.click_timeout == 0)
+                       U.click_timeout = 250;
+       }
+
        if (U.pixelsize == 0.0f)
                U.pixelsize = 1.0f;
        
index 2855af063c096dabf300c002f7dc2c8f30aa50da..126d1cb0ad996970b0155e2e8529343e1e173b2d 100644 (file)
@@ -409,7 +409,7 @@ void ED_keymap_mesh(wmKeyConfig *keyconf)
        /* use KM_CLICK because same key is used for tweaks */
        kmi = WM_keymap_add_item(keymap, "MESH_OT_dupli_extrude_cursor", ACTIONMOUSE, KM_CLICK, KM_CTRL, 0);
        RNA_boolean_set(kmi->ptr, "rotate_source", true);
-       kmi = WM_keymap_add_item(keymap, "MESH_OT_dupli_extrude_cursor", ACTIONMOUSE, KM_CLICK, KM_SHIFT | KM_CTRL, 0);
+       kmi = WM_keymap_add_item(keymap, "MESH_OT_dupli_extrude_cursor", ACTIONMOUSE, KM_PRESS, KM_SHIFT | KM_CTRL, 0);
        RNA_boolean_set(kmi->ptr, "rotate_source", false);
 
        WM_keymap_add_menu(keymap, "VIEW3D_MT_edit_mesh_delete", XKEY, KM_PRESS, 0, 0);
index 422f0c12e519ea45f7433d3f6e8e63859a42d84b..cf717cc48caf111d434da7ea37f450dce433b939 100644 (file)
@@ -295,7 +295,7 @@ void ED_keymap_object(wmKeyConfig *keyconf)
        keymap = WM_keymap_find(keyconf, "Object Non-modal", 0, 0);
        
        /* Note: this keymap works disregarding mode */
-       kmi = WM_keymap_add_item(keymap, "OBJECT_OT_mode_set", TABKEY, KM_PRESS, 0, 0);
+       kmi = WM_keymap_add_item(keymap, "OBJECT_OT_mode_set", TABKEY, KM_CLICK, 0, 0);
        RNA_enum_set(kmi->ptr, "mode", OB_MODE_EDIT);
        RNA_boolean_set(kmi->ptr, "toggle", true);
 
index 92731c2f1357a4c200d10b540fe0ce117987391b..b3f5e81f15485222146f7c6dc3fb8313fb8f848e 100644 (file)
@@ -1140,8 +1140,10 @@ static int console_modal_select(bContext *C, wmOperator *op, const wmEvent *even
                case LEFTMOUSE:
                case MIDDLEMOUSE:
                case RIGHTMOUSE:
-                       console_cursor_set_exit(C, op);
-                       return OPERATOR_FINISHED;
+                       if (event->val == KM_RELEASE) {
+                               console_cursor_set_exit(C, op);
+                               return OPERATOR_FINISHED;
+                       }
                case MOUSEMOVE:
                        console_modal_select_apply(C, op, event);
                        break;
index 782b318b8a26635ea51206475bd9eb25ac9a115e..557a495cb8f4c1308b330d2f966d15ee7eae2e45 100644 (file)
@@ -444,27 +444,27 @@ static void file_keymap(struct wmKeyConfig *keyconf)
        RNA_boolean_set(kmi->ptr, "need_active", true);
 
        /* left mouse selects and opens */
-       WM_keymap_add_item(keymap, "FILE_OT_select", LEFTMOUSE, KM_CLICK, 0, 0);
-       kmi = WM_keymap_add_item(keymap, "FILE_OT_select", LEFTMOUSE, KM_CLICK, KM_SHIFT, 0);
+       WM_keymap_add_item(keymap, "FILE_OT_select", LEFTMOUSE, KM_PRESS, 0, 0);
+       kmi = WM_keymap_add_item(keymap, "FILE_OT_select", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0);
        RNA_boolean_set(kmi->ptr, "extend", true);
-       kmi = WM_keymap_add_item(keymap, "FILE_OT_select", LEFTMOUSE, KM_CLICK, KM_CTRL | KM_SHIFT, 0);
+       kmi = WM_keymap_add_item(keymap, "FILE_OT_select", LEFTMOUSE, KM_PRESS, KM_CTRL | KM_SHIFT, 0);
        RNA_boolean_set(kmi->ptr, "extend", true);
        RNA_boolean_set(kmi->ptr, "fill", true);
 
        /* right mouse selects without opening */
-       kmi = WM_keymap_add_item(keymap, "FILE_OT_select", RIGHTMOUSE, KM_CLICK, 0, 0);
+       kmi = WM_keymap_add_item(keymap, "FILE_OT_select", RIGHTMOUSE, KM_PRESS, 0, 0);
        RNA_boolean_set(kmi->ptr, "open", false);
-       kmi = WM_keymap_add_item(keymap, "FILE_OT_select", RIGHTMOUSE, KM_CLICK, KM_SHIFT, 0);
+       kmi = WM_keymap_add_item(keymap, "FILE_OT_select", RIGHTMOUSE, KM_PRESS, KM_SHIFT, 0);
        RNA_boolean_set(kmi->ptr, "extend", true);
        RNA_boolean_set(kmi->ptr, "open", false);
-       kmi = WM_keymap_add_item(keymap, "FILE_OT_select", RIGHTMOUSE, KM_CLICK, KM_ALT, 0);
+       kmi = WM_keymap_add_item(keymap, "FILE_OT_select", RIGHTMOUSE, KM_PRESS, KM_ALT, 0);
        RNA_boolean_set(kmi->ptr, "extend", true);
        RNA_boolean_set(kmi->ptr, "fill", true);
        RNA_boolean_set(kmi->ptr, "open", false);
 
        /* front and back mouse folder navigation */
-       WM_keymap_add_item(keymap, "FILE_OT_previous", BUTTON4MOUSE, KM_CLICK, 0, 0);
-       WM_keymap_add_item(keymap, "FILE_OT_next", BUTTON5MOUSE, KM_CLICK, 0, 0);
+       WM_keymap_add_item(keymap, "FILE_OT_previous", BUTTON4MOUSE, KM_PRESS, 0, 0);
+       WM_keymap_add_item(keymap, "FILE_OT_next", BUTTON5MOUSE, KM_PRESS, 0, 0);
 
        WM_keymap_add_item(keymap, "FILE_OT_select_all_toggle", AKEY, KM_PRESS, 0, 0);
        WM_keymap_add_item(keymap, "FILE_OT_refresh", PADPERIOD, KM_PRESS, 0, 0);
index 33e8b522335e15c4ad00648914453e070dd51e71..e266a5089c81decfa191c33f034c8d8b877e4f38 100644 (file)
@@ -574,7 +574,7 @@ static void graphedit_keymap_keyframes(wmKeyConfig *keyconf, wmKeyMap *keymap)
        
        /* insertkey */
        WM_keymap_add_item(keymap, "GRAPH_OT_keyframe_insert", IKEY, KM_PRESS, 0, 0);
-       WM_keymap_add_item(keymap, "GRAPH_OT_click_insert", ACTIONMOUSE, KM_CLICK, KM_CTRL, 0);
+       WM_keymap_add_item(keymap, "GRAPH_OT_click_insert", ACTIONMOUSE, KM_PRESS, KM_CTRL, 0);
        
        /* copy/paste */
        WM_keymap_add_item(keymap, "GRAPH_OT_copy", CKEY, KM_PRESS, KM_CTRL, 0);
index c8951a1172ed55ec00602f111c2d40f8fea41804..f72def8377fe5219e3b744c043c56e72a412c556 100644 (file)
@@ -633,11 +633,14 @@ static int node_link_modal(bContext *C, wmOperator *op, const wmEvent *event)
                case RIGHTMOUSE:
                case MIDDLEMOUSE:
                {
-                       node_link_exit(C, op, true);
-                       
-                       ED_area_headerprint(CTX_wm_area(C), NULL);
-                       ED_region_tag_redraw(ar);
-                       return OPERATOR_FINISHED;
+                       if (event->val != KM_NOTHING) {
+                               node_link_exit(C, op, true);
+                               
+                               ED_area_headerprint(CTX_wm_area(C), NULL);
+                               ED_region_tag_redraw(ar);
+                               return OPERATOR_FINISHED;
+                       }
+                       break;
                }
        }
        
index f5869575cc6d7495bdd9f5d38c556d902e1af13f..93116dbe22cd967985a85f8dc05c69e1611dd919 100644 (file)
@@ -91,19 +91,19 @@ void outliner_keymap(wmKeyConfig *keyconf)
        
        WM_keymap_add_item(keymap, "OUTLINER_OT_item_rename", LEFTMOUSE, KM_DBL_CLICK, 0, 0);
 
-       kmi = WM_keymap_add_item(keymap, "OUTLINER_OT_item_activate", LEFTMOUSE, KM_CLICK, 0, 0);
+       kmi = WM_keymap_add_item(keymap, "OUTLINER_OT_item_activate", LEFTMOUSE, KM_PRESS, 0, 0);
        RNA_boolean_set(kmi->ptr, "recursive", false);
        RNA_boolean_set(kmi->ptr, "extend", false);
 
-       kmi = WM_keymap_add_item(keymap, "OUTLINER_OT_item_activate", LEFTMOUSE, KM_CLICK, KM_SHIFT, 0);
+       kmi = WM_keymap_add_item(keymap, "OUTLINER_OT_item_activate", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0);
        RNA_boolean_set(kmi->ptr, "recursive", false);
        RNA_boolean_set(kmi->ptr, "extend", true);
 
-       kmi = WM_keymap_add_item(keymap, "OUTLINER_OT_item_activate", LEFTMOUSE, KM_CLICK, KM_CTRL, 0);
+       kmi = WM_keymap_add_item(keymap, "OUTLINER_OT_item_activate", LEFTMOUSE, KM_PRESS, KM_CTRL, 0);
        RNA_boolean_set(kmi->ptr, "recursive", true);
        RNA_boolean_set(kmi->ptr, "extend", false);
 
-       kmi = WM_keymap_add_item(keymap, "OUTLINER_OT_item_activate", LEFTMOUSE, KM_CLICK, KM_CTRL | KM_SHIFT, 0);
+       kmi = WM_keymap_add_item(keymap, "OUTLINER_OT_item_activate", LEFTMOUSE, KM_PRESS, KM_CTRL | KM_SHIFT, 0);
        RNA_boolean_set(kmi->ptr, "recursive", true);
        RNA_boolean_set(kmi->ptr, "extend", true);
 
index ad8050a50e8e017c63f599e395e3a2cc7dd2a114..6fb5706bfdf6fe4f8af3237a305c3b305f82a5fa 100644 (file)
@@ -2163,6 +2163,11 @@ static int text_scroll_modal(bContext *C, wmOperator *op, const wmEvent *event)
                case LEFTMOUSE:
                case RIGHTMOUSE:
                case MIDDLEMOUSE:
+                       /* don't exit on dummy events */
+                       if (event->val == KM_NOTHING) {
+                               return OPERATOR_RUNNING_MODAL;
+                       }
+
                        if (ELEM(tsc->zone, SCROLLHANDLE_MIN_OUTSIDE, SCROLLHANDLE_MAX_OUTSIDE)) {
                                txt_screen_skip(st, ar, st->viewlines * (tsc->zone == SCROLLHANDLE_MIN_OUTSIDE ? 1 : -1));
 
@@ -2667,8 +2672,10 @@ static int text_set_selection_modal(bContext *C, wmOperator *op, const wmEvent *
                case LEFTMOUSE:
                case MIDDLEMOUSE:
                case RIGHTMOUSE:
-                       text_cursor_set_exit(C, op);
-                       return OPERATOR_FINISHED;
+                       if (event->val != KM_NOTHING) {
+                               text_cursor_set_exit(C, op);
+                               return OPERATOR_FINISHED;
+                       }
                case TIMER:
                case MOUSEMOVE:
                        text_cursor_set_apply(C, op, event);
index 8c668b2b8e0c5ccc1b357a983abd136b876e2896..c5425f238cc6783ad2282b382be9d46162bc5901 100644 (file)
@@ -386,7 +386,7 @@ void view3d_keymap(wmKeyConfig *keyconf)
        
        /* drawtype */
 
-       kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle_enum", ZKEY, KM_PRESS, 0, 0);
+       kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle_enum", ZKEY, KM_CLICK, 0, 0);
        RNA_string_set(kmi->ptr, "data_path", "space_data.viewport_shade");
        RNA_string_set(kmi->ptr, "value_1", "SOLID");
        RNA_string_set(kmi->ptr, "value_2", "WIREFRAME");
index afa1f03a8cad6a2dfc645ddb528adc2255a74da1..348e895e0a9c85803d32e8d2baac0422ded46d78 100644 (file)
@@ -447,7 +447,9 @@ typedef struct UserDef {
        
        short versions;
        short dbl_click_time;
-       
+       short click_timeout;
+       short pad3;
+
        short gameflags;
        short wheellinescroll;
        int uiflag, uiflag2;
@@ -465,7 +467,8 @@ typedef struct UserDef {
        char pad2[2];
        short transopts;
        short menuthreshold1, menuthreshold2;
-       
+       int pad4;
+
        struct ListBase themes;
        struct ListBase uifonts;
        struct ListBase uistyles;
index 919cdb7ea2fee65fbed6c4874e9fd5f93463d5cb..ebbafb19aa5d001161d61379cbde5a7871566845 100644 (file)
@@ -249,7 +249,8 @@ typedef struct wmKeyMapItem {
 
        /* event */
        short type;                     /* event code itself */
-       short val;                      /* KM_ANY, KM_PRESS, KM_NOTHING etc */
+       short val;                      /* NOTE: other than event->val this can be the value itself
+                                        * (KM_ANY, KM_PRESS, etc) AND the clicktype (KM_DBL_CLICK, KM_HOLD, etc) */
        short shift, ctrl, alt, oskey;  /* oskey is apple or windowskey, value denotes order of pressed */
        short keymodifier;              /* rawkey modifier */
 
index 15a4ef5eaf60dd36d4683ffeaf46b77b63d6688c..fc6d28e67c2cea981b045fd5d0bb2e17dc63db96 100644 (file)
@@ -4324,12 +4324,16 @@ static void rna_def_userdef_input(BlenderRNA *brna)
        RNA_def_property_boolean_sdna(prop, NULL, "ndof_flag", NDOF_FLY_HELICOPTER);
        RNA_def_property_ui_text(prop, "Helicopter Mode", "Device up/down directly controls your Z position");
 
-
-       prop = RNA_def_property(srna, "mouse_double_click_time", PROP_INT, PROP_NONE);
+       prop = RNA_def_property(srna, "double_click_time", PROP_INT, PROP_NONE);
        RNA_def_property_int_sdna(prop, NULL, "dbl_click_time");
        RNA_def_property_range(prop, 1, 1000);
        RNA_def_property_ui_text(prop, "Double Click Timeout", "Time/delay (in ms) for a double click");
 
+       prop = RNA_def_property(srna, "click_timeout", PROP_INT, PROP_NONE);
+       RNA_def_property_int_sdna(prop, NULL, "click_timeout");
+       RNA_def_property_range(prop, 0, 10000);
+       RNA_def_property_ui_text(prop, "Click Timeout", "Time (in ms) to determine if a key is clicked or held");
+
        prop = RNA_def_property(srna, "use_mouse_emulate_3_button", PROP_BOOLEAN, PROP_NONE);
        RNA_def_property_boolean_sdna(prop, NULL, "flag", USER_TWOBUTTONMOUSE);
        RNA_def_property_ui_text(prop, "Emulate 3 Button Mouse",
index 6119bc968e6226f36e87870ac691cf313d580e7f..64ecf61f7743c17099792b06be913fc703d7e636 100644 (file)
@@ -49,6 +49,7 @@ static EnumPropertyItem event_keymouse_value_items[] = {
        {KM_PRESS, "PRESS", 0, "Press", ""},
        {KM_RELEASE, "RELEASE", 0, "Release", ""},
        {KM_CLICK, "CLICK", 0, "Click", ""},
+       {KM_HOLD, "HOLD", 0, "Hold", ""},
        {KM_DBL_CLICK, "DOUBLE_CLICK", 0, "Double Click", ""},
        {0, NULL, 0, NULL, NULL}
 };
@@ -380,6 +381,7 @@ EnumPropertyItem event_value_items[] = {
        {KM_PRESS, "PRESS", 0, "Press", ""},
        {KM_RELEASE, "RELEASE", 0, "Release", ""},
        {KM_CLICK, "CLICK", 0, "Click", ""},
+       {KM_HOLD, "HOLD", 0, "Hold", ""},
        {KM_DBL_CLICK, "DOUBLE_CLICK", 0, "Double Click", ""},
        {EVT_GESTURE_N, "NORTH", 0, "North", ""},
        {EVT_GESTURE_NE, "NORTH_EAST", 0, "North-East", ""},
index e716cd6b7792d022ac7ca8ce720cefa2277f42dc..366d32181760a941bed957b5eb5f7a84d0e3357b 100644 (file)
@@ -180,9 +180,11 @@ enum {
 #define KM_NOTHING     0
 #define KM_PRESS       1
 #define KM_RELEASE     2
-#define KM_CLICK       3
-#define KM_DBL_CLICK   4
 
+/* clicktype */
+#define KM_CLICK     3 /* clicked key (click_time <= U.click_timeout) */
+#define KM_DBL_CLICK 4 /* double click - keep at 4 to avoid breakage with older key configs */
+#define KM_HOLD      5 /* held key    (click_time >  U.click_timeout) */
 
 /* ************** UI Handler ***************** */
 
@@ -427,7 +429,9 @@ typedef struct wmEvent {
        
        short type;                     /* event code itself (short, is also in keymap) */
        short val;                      /* press, release, scrollvalue */
+       short click_type;       /* click, hold or double click */
        int x, y;                       /* mouse pointer position, screen coord */
+       double click_time;      /* the time since keypress started */
        int mval[2];            /* region mouse position, name convention pre 2.5 :) */
        char utf8_buf[6];       /* from, ghost if utf8 is enabled for the platform,
                                                 * BLI_str_utf8_size() must _always_ be valid, check
@@ -435,20 +439,19 @@ typedef struct wmEvent {
        char ascii;                     /* from ghost, fallback if utf8 isn't set */
        char pad;
 
-       /* previous state, used for double click and the 'click' */
+       bool is_key_pressed; /* is a (non-modifier) key is pressed? (keyboard, mouse, NDOF, ...) */
+
+       /* previous state, used for clicktype */
        short prevtype;
        short prevval;
        int prevx, prevy;
-       double prevclicktime;
+       double prevclick_time;
        int prevclickx, prevclicky;
        
        /* modifier states */
        short shift, ctrl, alt, oskey;  /* oskey is apple or windowskey, value denotes order of pressed */
        short keymodifier;                              /* rawkey modifier */
        
-       /* set in case a KM_PRESS went by unhandled */
-       short check_click;
-       
        /* keymap item, set by handler (weak?) */
        const char *keymap_idname;
 
index e80042970110f993fa90456472d16ceaa70471d8..ce9a729a7a9d5e61fbdc585a353db084427cda14 100644 (file)
@@ -404,7 +404,7 @@ static int wm_handler_ui_call(bContext *C, wmEventHandler *handler, wmEvent *eve
         * to make the DBL_CLICK conversion work, we just don't send this to UI, except mouse clicks */
        if (((handler->flag & WM_HANDLER_ACCEPT_DBL_CLICK) == 0) &&
            (event->type != LEFTMOUSE) &&
-           (event->val == KM_DBL_CLICK))
+           (event->click_type == KM_DBL_CLICK))
        {
                return WM_HANDLER_CONTINUE;
        }
@@ -1442,6 +1442,7 @@ int WM_userdef_event_map(int kmitype)
 }
 
 
+/* XXX rename to something more descriptive like wm_event_is_keymapitem_matching and use bool */
 static int wm_eventmatch(wmEvent *winevent, wmKeyMapItem *kmi)
 {
        int kmitype = WM_userdef_event_map(kmi->type);
@@ -1458,10 +1459,13 @@ static int wm_eventmatch(wmEvent *winevent, wmKeyMapItem *kmi)
 
        if (kmitype != KM_ANY)
                if (winevent->type != kmitype) return 0;
-       
+
+       /* KM_ANY excludes click_type events - filter them out */
+       if (kmi->val == KM_ANY && winevent->click_type) return 0;
+
        if (kmi->val != KM_ANY)
-               if (winevent->val != kmi->val) return 0;
-       
+               if (!ELEM(kmi->val, winevent->val, winevent->click_type)) return 0;
+
        /* modifiers also check bits, so it allows modifier order */
        if (kmi->shift != KM_ANY)
                if (winevent->shift != kmi->shift && !(winevent->shift & kmi->shift)) return 0;
@@ -1508,8 +1512,9 @@ static void wm_event_modalkeymap(const bContext *C, wmOperator *op, wmEvent *eve
                /* modal keymap checking returns handled events fine, but all hardcoded modal
                 * handling typically swallows all events (OPERATOR_RUNNING_MODAL).
                 * This bypass just disables support for double clicks in hardcoded modal handlers */
-               if (event->val == KM_DBL_CLICK) {
+               if (event->click_type == KM_DBL_CLICK) {
                        event->val = KM_PRESS;
+                       event->click_type = 0;
                        *dbl_click_disabled = true;
                }
        }
@@ -1541,9 +1546,9 @@ static void wm_event_modalmap_end(wmEvent *event, bool dbl_click_disabled)
                event->val = event->prevval;
                event->prevval = 0;
        }
-       else if (dbl_click_disabled)
-               event->val = KM_DBL_CLICK;
-
+       else if (dbl_click_disabled) {
+               event->click_type = KM_DBL_CLICK;
+       }
 }
 
 /* Warning: this function removes a modal handler, when finished */
@@ -2021,47 +2026,21 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
                /* test for CLICK events */
                if (wm_action_not_handled(action)) {
                        wmWindow *win = CTX_wm_window(C);
-                       
-                       /* eventstate stores if previous event was a KM_PRESS, in case that 
-                        * wasn't handled, the KM_RELEASE will become a KM_CLICK */
-                       
-                       if (win && event->val == KM_PRESS) {
-                               win->eventstate->check_click = true;
-                       }
-                       
-                       if (win && win->eventstate->prevtype == event->type) {
-                               
-                               if ((event->val == KM_RELEASE) &&
-                                   (win->eventstate->prevval == KM_PRESS) &&
-                                   (win->eventstate->check_click == true))
-                               {
-                                       event->val = KM_CLICK;
-                                       
-                                       if (G.debug & (G_DEBUG_HANDLERS)) {
-                                               printf("%s: handling CLICK\n", __func__);
-                                       }
-
-                                       action |= wm_handlers_do_intern(C, event, handlers);
 
-                                       event->val = KM_RELEASE;
-                               }
-                               else if (event->val == KM_DBL_CLICK) {
+                       /* XXX check if those double click hacks can be removed/improved since click_type was introduced */
+                       if (win && win->eventstate->prevtype == event->type) {
+                               if (event->click_type == KM_DBL_CLICK) {
                                        event->val = KM_PRESS;
+                                       event->click_type = 0;
                                        action |= wm_handlers_do_intern(C, event, handlers);
                                        
                                        /* revert value if not handled */
                                        if (wm_action_not_handled(action)) {
-                                               event->val = KM_DBL_CLICK;
+                                               event->click_type = KM_DBL_CLICK;
                                        }
                                }
                        }
                }
-               else {
-                       wmWindow *win = CTX_wm_window(C);
-
-                       if (win)
-                               win->eventstate->check_click = 0;
-               }
        }
        
        return action;
@@ -3005,22 +2984,50 @@ static wmWindow *wm_event_cursor_other_windows(wmWindowManager *wm, wmWindow *wi
        return NULL;
 }
 
-static bool wm_event_is_double_click(wmEvent *event, wmEvent *event_state)
+/**
+ * Clicktype test
+ *
+ * We have 3 different click_types: #KM_CLICK, #KM_HOLD# and #KM_DBL_CLICK.
+ *
+ * Time is used to determine, what to send. It works as follows:
+ * - #KM_RELEASE && time since first #KM_PRESS < U.click_timeout --> send #KM_CLICK
+ * - #KM_PRESS && time since first #KM_PRESS > U.click_timeout --> send #KM_HOLD
+ * - #KM_PRESS after a #KM_RELEASE && time since previous #KM_PRESS < U.dbl_click_time --> send #KM_DBL_CLICK
+ *
+ * \note: only #KM_DBL_CLICK is handled here, rest in #wm_window_event_clicktype_init (wm_window.c)
+ */
+static void wm_event_clicktype_init(wmWindow *win, wmEvent *event, wmEvent *event_state)
 {
-       if ((event->type == event_state->prevtype) &&
-           (event_state->prevval == KM_RELEASE) &&
-           (event->val == KM_PRESS))
+       short click_type = 0;
+
+       if ((event->val == KM_PRESS) &&
+           (event_state->prevval != KM_PRESS || event->prevtype != win->eventstate->prevtype))
+       {
+               event_state->prevclick_time = event->click_time;
+               event_state->prevclickx = event->x;
+               event_state->prevclicky = event->y;
+       }
+
+       /* double click */
+       if (event->type == event_state->prevtype &&
+           event_state->prevval == KM_RELEASE &&
+           event->val == KM_PRESS)
        {
                if ((ISMOUSE(event->type) == false) || ((ABS(event->x - event_state->prevclickx)) <= 2 &&
                                                        (ABS(event->y - event_state->prevclicky)) <= 2))
                {
-                       if ((PIL_check_seconds_timer() - event_state->prevclicktime) * 1000 < U.dbl_click_time) {
-                               return true;
+                       if ((PIL_check_seconds_timer() - event_state->prevclick_time) * 1000 < U.dbl_click_time) {
+                               click_type = KM_DBL_CLICK;
+                               if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS)) {
+                                       printf("%s Send double click event\n", __func__);
+                               }
                        }
                }
        }
 
-       return false;
+       if (click_type != event->click_type) {
+               event_state->click_type = event->click_type = click_type;
+       }
 }
 
 static void wm_event_add_mousemove(wmWindow *win, const wmEvent *event)
@@ -3152,6 +3159,9 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
                        evt->val = event.val;
                        evt->type = event.type;
 
+                       /* click_type */
+                       wm_event_clicktype_init(win, &event, evt);
+
                        if (win->active == 0) {
                                int cx, cy;
                                
@@ -3162,18 +3172,6 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
                                event.y = evt->y = cy;
                        }
                        
-                       /* double click test */
-                       if (wm_event_is_double_click(&event, evt)) {
-                               if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS))
-                                       printf("%s Send double click\n", __func__);
-                               event.val = KM_DBL_CLICK;
-                       }
-                       if (event.val == KM_PRESS) {
-                               evt->prevclicktime = PIL_check_seconds_timer();
-                               evt->prevclickx = event.x;
-                               evt->prevclicky = event.y;
-                       }
-                       
                        /* add to other window if event is there (not to both!) */
                        owin = wm_event_cursor_other_windows(wm, win, &event);
                        if (owin) {
@@ -3212,7 +3210,10 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
                        /* copy to event state */
                        evt->val = event.val;
                        evt->type = event.type;
-                       
+
+                       /* clicktype */
+                       wm_event_clicktype_init(win, &event, evt);
+
                        /* exclude arrow keys, esc, etc from text input */
                        if (type == GHOST_kEventKeyUp) {
                                event.ascii = '\0';
@@ -3278,14 +3279,6 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
                                                event.keymodifier = evt->keymodifier = 0;
                                        break;
                        }
-
-                       /* double click test */
-                       /* if previous event was same type, and previous was release, and now it presses... */
-                       if (wm_event_is_double_click(&event, evt)) {
-                               if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS))
-                                       printf("%s Send double click\n", __func__);
-                               evt->val = event.val = KM_DBL_CLICK;
-                       }
                        
                        /* this case happens on holding a key pressed, it should not generate
                         * press events events with the same key as modifier */
@@ -3306,13 +3299,6 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
                                G.is_break = true;
                        }
                        
-                       /* double click test - only for press */
-                       if (event.val == KM_PRESS) {
-                               evt->prevclicktime = PIL_check_seconds_timer();
-                               evt->prevclickx = event.x;
-                               evt->prevclicky = event.y;
-                       }
-                       
                        wm_event_add(win, &event);
                        
                        break;
index 82e46c1b333293e8fc1380592857882fce657327..0a89ca113c4f0e3ef2704d9d9cc655fa7e9a2943 100644 (file)
@@ -1192,11 +1192,15 @@ int WM_keymap_item_compare(wmKeyMapItem *k1, wmKeyMapItem *k2)
                return 0;
 
        if (k1->val != KM_ANY && k2->val != KM_ANY) {
+
+#if 0  /* thanks to clicktype those shouldn't be needed anymore */
                /* take click, press, release conflict into account */
                if (k1->val == KM_CLICK && ELEM(k2->val, KM_PRESS, KM_RELEASE, KM_CLICK) == 0)
                        return 0;
                if (k2->val == KM_CLICK && ELEM(k1->val, KM_PRESS, KM_RELEASE, KM_CLICK) == 0)
                        return 0;
+#endif
+
                if (k1->val != k2->val)
                        return 0;
        }
index c62e82b8e43611f560edaa3ee313b4792b0b51e2..c2cd96165f3507543a99b6cd45dbcc7fc2a1fb85 100644 (file)
@@ -5108,7 +5108,7 @@ void wm_window_keymap(wmKeyConfig *keyconf)
        WM_keymap_verify_item(keymap, "WM_OT_debug_menu", DKEY, KM_PRESS, KM_ALT | KM_CTRL, 0);
 
        /* menus that can be accessed anywhere in blender */
-       WM_keymap_verify_item(keymap, "WM_OT_search_menu", SPACEKEY, KM_PRESS, 0, 0);
+       WM_keymap_verify_item(keymap, "WM_OT_search_menu", SPACEKEY, KM_CLICK, 0, 0);
        WM_keymap_add_menu(keymap, "USERPREF_MT_ndof_settings", NDOF_BUTTON_MENU, KM_PRESS, 0, 0);
 
        /* Space switching */
index 4f7e5ab75b352cfd051d5e7fdef2fc9bcf2237bc..98766f350067dd6d28a252003b942bce1da3c0e8 100644 (file)
@@ -1097,6 +1097,76 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr
        return 1;
 }
 
+/**
+ * #KM_DBL_CLICK is set in wm_event_clicktype_init (wm_event_system.c)
+ * Normally, this should be there too, but for #KM_CLICK/#KM_HOLD, we need a
+ * time precision of a few milliseconds, which we can't get from there
+ */
+static void wm_window_event_clicktype_init(const bContext *C)
+{
+       wmWindowManager *wm = CTX_wm_manager(C);
+
+       if (wm->winactive) {
+               wmWindow *win = wm->winactive;
+               wmEvent *event = win->eventstate;
+               short click_type = event->click_type;
+
+               BLI_assert(event != NULL);
+
+               if ((event->type == EVENT_NONE) ||
+                   ((event->val == KM_NOTHING) && (event->is_key_pressed == false)))
+               {
+                       /* nothing needs to be done here */
+                       return;
+               }
+
+               /* we always want click_type of last clicked button (to enable
+                * use with modifier keys) - unnecessary for mouse though */
+               if (!ISMOUSE(event->type) &&
+                   event->val == KM_PRESS &&
+                   event->type != event->keymodifier)
+               {
+                       event->is_key_pressed = false;
+               }
+               else if (event->val == KM_PRESS && !event->is_key_pressed) {
+                       event->is_key_pressed = true;
+                       event->click_time = PIL_check_seconds_timer();
+               }
+               else if (event->val == KM_RELEASE && event->is_key_pressed) {
+                       event->is_key_pressed = false;
+               }
+               else if (event->is_key_pressed == false) {
+                       return;
+               }
+
+               /* the actual test */
+               if ((PIL_check_seconds_timer() - event->click_time) * 1000 <= U.click_timeout) {
+                       /* for any reason some X11 systems send two release events triggering two KM_CLICK
+                        * events - making the rules more strict by checking for prevval resolves this */
+                       if (event->val == KM_RELEASE && event->prevval != KM_RELEASE) {
+                               click_type = KM_CLICK;
+                               if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS)) {
+                                       printf("%s Send click event\n", __func__);
+                               }
+                       }
+               }
+               else if (event->is_key_pressed) {
+                       click_type = KM_HOLD;
+                       if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS)) {
+                               printf("%s Send hold event\n", __func__);
+                       }
+
+                       /* the event we send in this case is a "dummy" event - don't send value */
+                       event->val = KM_NOTHING;
+               }
+
+               /* send event with new click_type */
+               if (event->click_type != click_type) {
+                       event->click_type = click_type;
+                       wm_event_add(win, event);
+               }
+       }
+}
 
 /* This timer system only gives maximum 1 timer event per redraw cycle,
  * to prevent queues to get overloaded.
@@ -1156,7 +1226,11 @@ void wm_window_process_events(const bContext *C)
 
        if (hasevent)
                GHOST_DispatchEvents(g_system);
-       
+
+       /* not nice to have this here, but it's the only place
+        * that can call it with the needed time precision */
+       wm_window_event_clicktype_init(C);
+
        hasevent |= wm_window_timer(C);
 
        /* no event, we sleep 5 milliseconds */