diff options
author | Campbell Barton <ideasman42@gmail.com> | 2018-06-26 13:18:54 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2018-06-26 13:18:54 +0300 |
commit | cdd915e9e9646b9dcb83999712f48546fb830c1f (patch) | |
tree | 8b84563f7f4f59248c2c0151289bd53a9b919921 /source | |
parent | 4adf19e80b5f7bed2737d720ba99d8d6d60ca955 (diff) |
UI: status bar cursor keymap display
Show mouse button actions in status bar, based on context,
modifiers and active tool.
See: T54861
Diffstat (limited to 'source')
-rw-r--r-- | source/blender/blenloader/intern/readfile.c | 1 | ||||
-rw-r--r-- | source/blender/editors/include/UI_interface.h | 1 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_templates.c | 18 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_windowmanager_types.h | 3 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_ui_api.c | 3 | ||||
-rw-r--r-- | source/blender/windowmanager/WM_api.h | 4 | ||||
-rw-r--r-- | source/blender/windowmanager/intern/wm_event_system.c | 295 | ||||
-rw-r--r-- | source/blender/windowmanager/intern/wm_window.c | 4 |
8 files changed, 306 insertions, 23 deletions
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 9c5d59d2404..138cd868db0 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -6946,6 +6946,7 @@ static void direct_link_windowmanager(FileData *fd, wmWindowManager *wm) win->ghostwin = NULL; win->gwnctx = NULL; win->eventstate = NULL; + win->cursor_keymap_status = NULL; win->tweak = NULL; #ifdef WIN32 win->ime_data = NULL; diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 0666691607d..d4285f5a96e 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -1065,6 +1065,7 @@ void uiTemplateHeader3D_mode(uiLayout *layout, struct bContext *C); void uiTemplateHeader3D(uiLayout *layout, struct bContext *C); void uiTemplateEditModeSelection(uiLayout *layout, struct bContext *C); void uiTemplateReportsBanner(uiLayout *layout, struct bContext *C); +void uiTemplateCursorKeymap(uiLayout *layout, struct bContext *C); void uiTemplateKeymapItemProperties(uiLayout *layout, struct PointerRNA *ptr); void uiTemplateComponentMenu(uiLayout *layout, struct PointerRNA *ptr, const char *propname, const char *name); void uiTemplateNodeSocket(uiLayout *layout, struct bContext *C, float *color); diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 61cb0cda8c8..c2bea466015 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -4295,6 +4295,24 @@ void uiTemplateReportsBanner(uiLayout *layout, bContext *C) NULL, 0.0f, 0.0f, 0, 0, ""); } + +void uiTemplateCursorKeymap(uiLayout *layout, struct bContext *C) +{ + wmWindow *win = CTX_wm_window(C); + for (int i = 0; i < 3; i++) { + uiLayout *box = uiLayoutRow(layout, true); + for (int j = 0; j < 2; j++) { + const char *msg = WM_window_cursor_keymap_status_get(win, i, j); + if ((j == 0) || (msg != NULL)) { + uiItemL(box, msg, j == 0 ? (ICON_MOUSE_LMB + i) : ICON_MOUSE_DRAG); + } + } + if (i != 2) { + uiItemSpacer(layout); + } + } +} + /********************************* Keymap *************************************/ static void keymap_item_modified(bContext *UNUSED(C), void *kmi_p, void *UNUSED(unused)) diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h index 19d4ab10165..e41058f356b 100644 --- a/source/blender/makesdna/DNA_windowmanager_types.h +++ b/source/blender/makesdna/DNA_windowmanager_types.h @@ -232,6 +232,9 @@ typedef struct wmWindow { /* custom drawing callbacks */ ListBase drawcalls; + + /* Private runtime info to show text in the status bar. */ + void *cursor_keymap_status; } wmWindow; #ifdef ime_data diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c index 9377ef8a925..702bcf15462 100644 --- a/source/blender/makesrna/intern/rna_ui_api.c +++ b/source/blender/makesrna/intern/rna_ui_api.c @@ -1021,6 +1021,9 @@ void RNA_api_ui_layout(StructRNA *srna) func = RNA_def_function(srna, "template_reports_banner", "uiTemplateReportsBanner"); RNA_def_function_flag(func, FUNC_USE_CONTEXT); + func = RNA_def_function(srna, "template_cursor_keymap", "uiTemplateCursorKeymap"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT); + func = RNA_def_function(srna, "template_node_link", "uiTemplateNodeLink"); parm = RNA_def_pointer(func, "ntree", "NodeTree", "", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 6b1825edf3e..ace9acf0e8a 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -602,6 +602,9 @@ bool WM_event_is_tablet(const struct wmEvent *event); bool WM_event_is_ime_switch(const struct wmEvent *event); #endif +const char *WM_window_cursor_keymap_status_get(const struct wmWindow *win, int button_index, int type_index); +void WM_window_cursor_keymap_status_refresh(struct bContext *C, struct wmWindow *win); + /* wm_tooltip.c */ typedef struct ARegion *(*wmTooltipInitFn)(struct bContext *, struct ARegion *, bool *); @@ -618,4 +621,3 @@ void WM_tooltip_refresh(struct bContext *C, struct wmWindow *win); #endif #endif /* __WM_API_H__ */ - diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 6047f801037..55e8970c77e 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -499,6 +499,14 @@ void wm_event_do_notifiers(bContext *C) } wm_event_do_refresh_wm_and_depsgraph(C); + + /* Status bar */ + if (wm->winactive) { + win = wm->winactive; + CTX_wm_window_set(C, win); + WM_window_cursor_keymap_status_refresh(C, win); + CTX_wm_window_set(C, NULL); + } } static int wm_event_always_pass(const wmEvent *event) @@ -2773,6 +2781,40 @@ static bool wm_event_pie_filter(wmWindow *win, const wmEvent *event) } } +#ifdef USE_WORKSPACE_TOOL +static void wm_event_manipulator_temp_handler_apply( + bContext *C, ScrArea *sa, ARegion *ar, wmEventHandler *sneaky_handler) +{ + if (ar->regiontype == RGN_TYPE_WINDOW) { + bToolRef_Runtime *tref_rt = sa->runtime.tool ? sa->runtime.tool->runtime : NULL; + if (tref_rt && tref_rt->keymap[0]) { + wmKeyMap *km = WM_keymap_find_all( + C, tref_rt->keymap, sa->spacetype, RGN_TYPE_WINDOW); + if (km != NULL) { + sneaky_handler->keymap = km; + sneaky_handler->keymap_tool = sa->runtime.tool; + + /* Handle widgets first. */ + wmEventHandler *handler_last = ar->handlers.last; + while (handler_last && handler_last->manipulator_map == NULL) { + handler_last = handler_last->prev; + } + /* Head of list or after last manipulator. */ + BLI_insertlinkafter(&ar->handlers, handler_last, sneaky_handler); + } + } + } +} + +static void wm_event_manipulator_temp_handler_clear( + bContext *UNUSED(C), ScrArea *UNUSED(sa), ARegion *ar, wmEventHandler *sneaky_handler) +{ + if (sneaky_handler->keymap) { + BLI_remlink(&ar->handlers, sneaky_handler); + } +} +#endif /* USE_WORKSPACE_TOOL */ + /* called in main loop */ /* goes over entire hierarchy: events -> window -> screen -> area -> region */ void wm_event_do_handlers(bContext *C) @@ -2957,33 +2999,13 @@ void wm_event_do_handlers(bContext *C) * to fetch its current keymap. */ wmEventHandler sneaky_handler = {NULL}; - if (ar->regiontype == RGN_TYPE_WINDOW) { - bToolRef_Runtime *tref_rt = sa->runtime.tool ? sa->runtime.tool->runtime : NULL; - if (tref_rt && tref_rt->keymap[0]) { - wmKeyMap *km = WM_keymap_find_all( - C, tref_rt->keymap, sa->spacetype, RGN_TYPE_WINDOW); - if (km != NULL) { - sneaky_handler.keymap = km; - sneaky_handler.keymap_tool = sa->runtime.tool; - - /* Handle widgets first. */ - wmEventHandler *handler_last = ar->handlers.last; - while (handler_last && handler_last->manipulator_map == NULL) { - handler_last = handler_last->prev; - } - /* Head of list or after last manipulator. */ - BLI_insertlinkafter(&ar->handlers, handler_last, &sneaky_handler); - } - } - } + wm_event_manipulator_temp_handler_apply(C, sa, ar, &sneaky_handler); #endif /* USE_WORKSPACE_TOOL */ action |= wm_handlers_do(C, event, &ar->handlers); #ifdef USE_WORKSPACE_TOOL - if (sneaky_handler.keymap) { - BLI_remlink(&ar->handlers, &sneaky_handler); - } + wm_event_manipulator_temp_handler_clear(C, sa, ar, &sneaky_handler); #endif /* USE_WORKSPACE_TOOL */ /* fileread case (python), [#29489] */ @@ -4206,3 +4228,232 @@ bool WM_event_is_ime_switch(const struct wmEvent *event) #endif /** \} */ + + +static wmKeyMapItem *wm_kmi_from_event( + bContext *C, wmWindowManager *wm, + ListBase *handlers, const wmEvent *event) +{ + for (wmEventHandler *handler = handlers->first; handler; handler = handler->next) { + /* during this loop, ui handlers for nested menus can tag multiple handlers free */ + if (handler->flag & WM_HANDLER_DO_FREE) { + /* pass */ + } + else if (handler_boundbox_test(handler, event)) { /* optional boundbox */ + if (handler->keymap) { + wmKeyMap *keymap = WM_keymap_active(wm, handler->keymap); + if (WM_keymap_poll(C, keymap)) { + for (wmKeyMapItem *kmi = keymap->items.first; kmi; kmi = kmi->next) { + if (wm_eventmatch(event, kmi)) { + wmOperatorType *ot = WM_operatortype_find(kmi->idname, 0); + if (WM_operator_poll_context(C, ot, WM_OP_INVOKE_DEFAULT)) { + return kmi; + } + } + } + } + } + } + } + return NULL; +} + +/* -------------------------------------------------------------------- */ +/** \name Cursor Keymap Status + * + * Show cursor keys in the status bar. + * This is done by detecting changes to the state - full keymap lookups are expensive + * so only perform this on changing tools, space types, pressing different modifier keys... etc. + * \{ */ + +/** State storage to detect changes between calls to refresh the information. */ +struct CursorKeymapInfo_State { + struct { + short shift, ctrl, alt, oskey; + } modifiers; + short space_type; + short region_type; + /* Never use, just compare memory for changes. */ + bToolRef tref; +}; + +struct CursorKeymapInfo { + /* 0: mouse button index + * 1: event type (click/press, drag) + * 2: text. + */ + char text[3][2][128]; + wmEvent state_event; + struct CursorKeymapInfo_State state; +}; + +static void wm_event_cursor_store( + struct CursorKeymapInfo_State *state, + const wmEvent *event, + short space_type, short region_type, + const bToolRef *tref) +{ + state->modifiers.shift = event->shift; + state->modifiers.ctrl = event->ctrl; + state->modifiers.alt = event->alt; + state->modifiers.oskey = event->oskey; + state->space_type = space_type; + state->region_type = region_type; + state->tref = tref ? *tref : (bToolRef){0}; +} + +const char *WM_window_cursor_keymap_status_get(const wmWindow *win, int button_index, int type_index) +{ + if (win->cursor_keymap_status != NULL) { + struct CursorKeymapInfo *cd = win->cursor_keymap_status; + const char *msg = cd->text[button_index][type_index]; + if (*msg) { + return msg; + } + } + return NULL; +} + +void WM_window_cursor_keymap_status_refresh(bContext *C, struct wmWindow *win) +{ + bScreen *screen = WM_window_get_active_screen(win); + if (screen->state == SCREENFULL) { + return; + } + ScrArea *sa_statusbar = NULL; + for (ScrArea *sa = win->global_areas.areabase.first; sa; sa = sa->next) { + if (sa->spacetype == SPACE_STATUSBAR) { + sa_statusbar = sa; + break; + } + } + if (sa_statusbar == NULL) { + return; + } + + struct CursorKeymapInfo *cd; + if (UNLIKELY(win->cursor_keymap_status == NULL)) { + win->cursor_keymap_status = MEM_callocN(sizeof(struct CursorKeymapInfo), __func__); + } + cd = win->cursor_keymap_status; + + /* Detect unchanged state (early exit). */ + if (memcmp(&cd->state_event, win->eventstate, sizeof(wmEvent)) == 0) { + return; + } + + /* Now perform more comprehensive check, + * still keep this fast since it happens on mouse-move. */ + struct CursorKeymapInfo cd_prev = *((struct CursorKeymapInfo *)win->cursor_keymap_status); + cd->state_event = *win->eventstate; + + ScrArea *sa = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, win->eventstate->x, win->eventstate->y); + if (sa == NULL) { + return; + } + ARegion *ar = BKE_area_find_region_xy(sa, RGN_TYPE_ANY, win->eventstate->x, win->eventstate->y); + if (ar == NULL) { + return; + } + /* Keep as-is. */ + if (ELEM(ar->regiontype, RGN_TYPE_HEADER, RGN_TYPE_TEMPORARY, RGN_TYPE_HUD)) { + return; + } + /* Fallback to window. */ + if (ELEM(ar->regiontype, RGN_TYPE_TOOLS, RGN_TYPE_TOOL_PROPS)) { + ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW); + } + + /* Detect changes to the state. */ + { + bToolRef *tref = NULL; + if (ar->regiontype == RGN_TYPE_WINDOW) { + Scene *scene = WM_window_get_active_scene(win); + WorkSpace *workspace = WM_window_get_active_workspace(win); + const bToolKey tkey = { + .space_type = sa->spacetype, + .mode = WM_toolsystem_mode_from_spacetype(workspace, scene, sa, sa->spacetype), + }; + tref = WM_toolsystem_ref_find(workspace, &tkey); + } + wm_event_cursor_store(&cd->state, win->eventstate, sa->spacetype, ar->regiontype, tref); + if (memcmp(&cd->state, &cd_prev.state, sizeof(cd->state)) == 0) { + return; + } + } + + /* Changed context found, detect changes to keymap and refresh the status bar. */ + const struct { + int button_index; + int type_index; /* 0: press or click, 1: drag. */ + int event_type; + int event_value; + } event_data[] = { + {0, 0, LEFTMOUSE, KM_PRESS}, + {0, 0, LEFTMOUSE, KM_CLICK}, + {0, 1, EVT_TWEAK_L, KM_ANY}, + + {1, 0, MIDDLEMOUSE, KM_PRESS}, + {1, 0, MIDDLEMOUSE, KM_CLICK}, + {1, 1, EVT_TWEAK_M, KM_ANY}, + + {2, 0, RIGHTMOUSE, KM_PRESS}, + {2, 0, RIGHTMOUSE, KM_CLICK}, + {2, 1, EVT_TWEAK_R, KM_ANY}, + }; + + for (int button_index = 0; button_index < 3; button_index++) { + cd->text[button_index][0][0] = '\0'; + cd->text[button_index][1][0] = '\0'; + } + + CTX_wm_window_set(C, win); + CTX_wm_area_set(C, sa); + CTX_wm_region_set(C, ar); + +#ifdef USE_WORKSPACE_TOOL + wmEventHandler sneaky_handler = {NULL}; + wm_event_manipulator_temp_handler_apply(C, sa, ar, &sneaky_handler); +#endif + + ListBase *handlers[] = { + &ar->handlers, + &sa->handlers, + &win->handlers, + }; + + wmWindowManager *wm = CTX_wm_manager(C); + for (int data_index = 0; data_index < ARRAY_SIZE(event_data); data_index++) { + const int button_index = event_data[data_index].button_index; + const int type_index = event_data[data_index].type_index; + if (cd->text[button_index][type_index][0] != 0) { + continue; + } + wmEvent test_event = *win->eventstate; + test_event.type = event_data[data_index].event_type; + test_event.val = event_data[data_index].event_value; + wmKeyMapItem *kmi = NULL; + for (int handler_index = 0; handler_index < ARRAY_SIZE(handlers); handler_index++) { + kmi = wm_kmi_from_event(C, wm, handlers[handler_index], &test_event); + if (kmi) { + break; + } + } + if (kmi) { + wmOperatorType *ot = WM_operatortype_find(kmi->idname, 0); + STRNCPY(cd->text[button_index][type_index], ot ? ot->name : kmi->idname); + } + } + +#ifdef USE_WORKSPACE_TOOL + wm_event_manipulator_temp_handler_clear(C, sa, ar, &sneaky_handler); +#endif + + if (memcmp(&cd_prev.text, &cd->text, sizeof(cd_prev.text)) != 0) { + ED_area_tag_redraw(sa_statusbar); + } + + CTX_wm_window_set(C, NULL); +} + +/** \} */ diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index acc9a500247..b03b156b78c 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -229,6 +229,10 @@ void wm_window_free(bContext *C, wmWindowManager *wm, wmWindow *win) if (win->eventstate) MEM_freeN(win->eventstate); + if (win->cursor_keymap_status) { + MEM_freeN(win->cursor_keymap_status); + } + wm_event_free_all(win); wm_ghostwindow_destroy(wm, win); |