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:
authorCampbell Barton <ideasman42@gmail.com>2021-09-17 05:09:24 +0300
committerCampbell Barton <ideasman42@gmail.com>2021-09-17 05:57:28 +0300
commitda2ba402689d7ed752b0b37ad49dce1a5851cc77 (patch)
tree38d313bbbbc6d7f7acf9aaf88e86bce5b49fa4eb
parent633c29fb7befed7a8fa4f209dd5187f551c91e37 (diff)
UI: wait for input for operators that depend on cursor location
Support waiting for input so operators that depend on the cursor location are usable from menus / buttons. Use an operator type flag which the user interface code checks for, waiting for input when run from a menu item. This patch only supports this feature, there are no functional changes. The motivation for this change is discoverability since some actions were either hidden or broken when accessed from menus (where the behavior of the operator depended on the menu location). In general, waiting for input is *not* an efficient way to access tools, however there are over 50 operators with a "wait_for_input" property so this isn't introducing a new kind of interaction, rather exposing this in a way that does not need to be hard-coded into each operator, or having modal callbacks added for the sole purpose of waiting for input. Besides requiring boiler plate code using a "wait_for_input" property has the added down-side of preventing key shortcuts from showing. Only the menu items will enable the property, causing them not to match key-map items. Reviewed By: Severin Ref D12255
-rw-r--r--source/blender/editors/interface/interface_handlers.c19
-rw-r--r--source/blender/editors/interface/interface_template_search_menu.c3
-rw-r--r--source/blender/makesrna/intern/rna_wm.c7
-rw-r--r--source/blender/windowmanager/WM_api.h6
-rw-r--r--source/blender/windowmanager/WM_types.h11
-rw-r--r--source/blender/windowmanager/intern/wm_cursors.c26
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c166
-rw-r--r--source/blender/windowmanager/wm_cursors.h2
8 files changed, 234 insertions, 6 deletions
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index 977e9661dd9..77ae16d7cc7 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -508,6 +508,7 @@ typedef struct uiAfterFunc {
bContextStore *context;
char undostr[BKE_UNDO_STR_MAX];
+ char drawstr[UI_MAX_DRAW_STR];
} uiAfterFunc;
static void button_activate_init(bContext *C,
@@ -790,6 +791,10 @@ static void ui_handle_afterfunc_add_operator_ex(wmOperatorType *ot,
if (context_but && context_but->context) {
after->context = CTX_store_copy(context_but->context);
}
+
+ if (context_but) {
+ ui_but_drawstr_without_sep_char(context_but, after->drawstr, sizeof(after->drawstr));
+ }
}
void ui_handle_afterfunc_add_operator(wmOperatorType *ot, int opcontext)
@@ -900,6 +905,8 @@ static void ui_apply_but_func(bContext *C, uiBut *but)
after->context = CTX_store_copy(but->context);
}
+ ui_but_drawstr_without_sep_char(but, after->drawstr, sizeof(after->drawstr));
+
but->optype = NULL;
but->opcontext = 0;
but->opptr = NULL;
@@ -1021,7 +1028,8 @@ static void ui_apply_but_funcs_after(bContext *C)
}
if (after.optype) {
- WM_operator_name_call_ptr(C, after.optype, after.opcontext, (after.opptr) ? &opptr : NULL);
+ WM_operator_name_call_ptr_with_depends_on_cursor(
+ C, after.optype, after.opcontext, (after.opptr) ? &opptr : NULL, after.drawstr);
}
if (after.opptr) {
@@ -4190,10 +4198,11 @@ static void ui_but_extra_operator_icon_apply(bContext *C, uiBut *but, uiButExtra
ui_apply_but(C, but->block, but, but->active, true);
}
button_activate_state(C, but, BUTTON_STATE_EXIT);
- WM_operator_name_call_ptr(C,
- op_icon->optype_params->optype,
- op_icon->optype_params->opcontext,
- op_icon->optype_params->opptr);
+ WM_operator_name_call_ptr_with_depends_on_cursor(C,
+ op_icon->optype_params->optype,
+ op_icon->optype_params->opcontext,
+ op_icon->optype_params->opptr,
+ NULL);
/* Force recreation of extra operator icons (pseudo update). */
ui_but_extra_operator_icons_free(but);
diff --git a/source/blender/editors/interface/interface_template_search_menu.c b/source/blender/editors/interface/interface_template_search_menu.c
index 3105891142f..672f1b64943 100644
--- a/source/blender/editors/interface/interface_template_search_menu.c
+++ b/source/blender/editors/interface/interface_template_search_menu.c
@@ -955,7 +955,8 @@ static void menu_search_exec_fn(bContext *C, void *UNUSED(arg1), void *arg2)
switch (item->type) {
case MENU_SEARCH_TYPE_OP: {
CTX_store_set(C, item->op.context);
- WM_operator_name_call_ptr(C, item->op.type, item->op.opcontext, item->op.opptr);
+ WM_operator_name_call_ptr_with_depends_on_cursor(
+ C, item->op.type, item->op.opcontext, item->op.opptr, item->drawstr);
CTX_store_set(C, NULL);
break;
}
diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c
index 21a3c087197..31fdbf528bb 100644
--- a/source/blender/makesrna/intern/rna_wm.c
+++ b/source/blender/makesrna/intern/rna_wm.c
@@ -468,6 +468,13 @@ const EnumPropertyItem rna_enum_operator_type_flag_items[] = {
"is enabled"},
{OPTYPE_GRAB_CURSOR_X, "GRAB_CURSOR_X", 0, "Grab Pointer X", "Grab, only warping the X axis"},
{OPTYPE_GRAB_CURSOR_Y, "GRAB_CURSOR_Y", 0, "Grab Pointer Y", "Grab, only warping the Y axis"},
+ {OPTYPE_DEPENDS_ON_CURSOR,
+ "DEPENDS_ON_CURSOR",
+ 0,
+ "Depends on Cursor",
+ "The initial cursor location is used, "
+ "when running from a menus or buttons the user is prompted to place the cursor "
+ "before beginning the operation"},
{OPTYPE_PRESET, "PRESET", 0, "Preset", "Display a preset button with the operators settings"},
{OPTYPE_INTERNAL, "INTERNAL", 0, "Internal", "Removes the operator from search results"},
{0, NULL, 0, NULL, NULL},
diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h
index 7ecbcad886d..189a231616e 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -472,6 +472,12 @@ int WM_operator_call_py(struct bContext *C,
struct ReportList *reports,
const bool is_undo);
+void WM_operator_name_call_ptr_with_depends_on_cursor(struct bContext *C,
+ wmOperatorType *ot,
+ short opcontext,
+ PointerRNA *properties,
+ const char *drawstr);
+
/* Used for keymap and macro items. */
void WM_operator_properties_alloc(struct PointerRNA **ptr,
struct IDProperty **properties,
diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h
index c1730957432..01b4c0419a8 100644
--- a/source/blender/windowmanager/WM_types.h
+++ b/source/blender/windowmanager/WM_types.h
@@ -184,6 +184,17 @@ enum {
OPTYPE_LOCK_BYPASS = (1 << 9),
/** Special type of undo which doesn't store itself multiple times. */
OPTYPE_UNDO_GROUPED = (1 << 10),
+
+ /**
+ * Depends on the cursor location, when activated from a menu wait for mouse press.
+ *
+ * In practice these operators often end up being accessed:
+ * - Directly from key bindings.
+ * - As tools in the toolbar.
+ *
+ * Even so, accessing from the menu should behave usefully.
+ */
+ OPTYPE_DEPENDS_ON_CURSOR = (1 << 11),
};
/** For #WM_cursor_grab_enable wrap axis. */
diff --git a/source/blender/windowmanager/intern/wm_cursors.c b/source/blender/windowmanager/intern/wm_cursors.c
index 50d3a856cbe..9c020b16234 100644
--- a/source/blender/windowmanager/intern/wm_cursors.c
+++ b/source/blender/windowmanager/intern/wm_cursors.c
@@ -1146,5 +1146,31 @@ void wm_init_cursor_data(void)
BlenderCursor[WM_CURSOR_ZOOM_OUT] = &ZoomOutCursor;
END_CURSOR_BLOCK;
+ /********************** Area Pick Cursor ***********************/
+ BEGIN_CURSOR_BLOCK;
+
+ static char pick_area_bitmap[] = {
+ 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0xfe, 0x00, 0x10,
+ 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0xbf, 0x00, 0x81, 0x00, 0x81,
+ 0x00, 0x81, 0x00, 0x81, 0x00, 0x81, 0x00, 0x80, 0x00, 0xff,
+ };
+
+ static char pick_area_mask[] = {
+ 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0xff, 0x01, 0xff, 0x01, 0xff,
+ 0x01, 0x38, 0x00, 0xb8, 0x7f, 0xb8, 0xff, 0x80, 0xc1, 0x80, 0xc1,
+ 0x80, 0xc1, 0x80, 0xc1, 0x80, 0xc1, 0x80, 0xff, 0x00, 0xff,
+ };
+
+ static BCursor PickAreaCursor = {
+ pick_area_bitmap,
+ pick_area_mask,
+ 4,
+ 4,
+ false,
+ };
+
+ BlenderCursor[WM_CURSOR_PICK_AREA] = &PickAreaCursor;
+ END_CURSOR_BLOCK;
+
/********************** Put the cursors in the array ***********************/
}
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index 83a9a6c6383..238172276e7 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -1673,6 +1673,172 @@ int WM_operator_call_py(bContext *C,
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Operator Wait For Input
+ *
+ * Delay executing operators that depend on cursor location.
+ *
+ * See: #OPTYPE_DEPENDS_ON_CURSOR doc-string for more information.
+ * \{ */
+
+typedef struct uiOperatorWaitForInput {
+ ScrArea *area;
+ wmOperatorCallParams optype_params;
+ bContextStore *context;
+} uiOperatorWaitForInput;
+
+static void ui_handler_wait_for_input_remove(bContext *C, void *userdata)
+{
+ uiOperatorWaitForInput *opwait = userdata;
+ if (opwait->optype_params.opptr) {
+ if (opwait->optype_params.opptr->data) {
+ IDP_FreeProperty(opwait->optype_params.opptr->data);
+ }
+ MEM_freeN(opwait->optype_params.opptr);
+ }
+ if (opwait->context) {
+ CTX_store_free(opwait->context);
+ }
+
+ if (opwait->area != NULL) {
+ ED_area_status_text(opwait->area, NULL);
+ }
+ else {
+ ED_workspace_status_text(C, NULL);
+ }
+
+ MEM_freeN(opwait);
+}
+
+static int ui_handler_wait_for_input(bContext *C, const wmEvent *event, void *userdata)
+{
+ uiOperatorWaitForInput *opwait = userdata;
+ enum { CONTINUE = 0, EXECUTE, CANCEL } state = CONTINUE;
+ state = CONTINUE;
+
+ switch (event->type) {
+ case LEFTMOUSE: {
+ if (event->val == KM_PRESS) {
+ state = EXECUTE;
+ }
+ break;
+ }
+ /* Useful if the operator isn't convenient to access while the mouse button is held.
+ * If it takes numeric input for example. */
+ case EVT_SPACEKEY:
+ case EVT_RETKEY: {
+ if (event->val == KM_PRESS) {
+ state = EXECUTE;
+ }
+ break;
+ }
+ case RIGHTMOUSE: {
+ if (event->val == KM_PRESS) {
+ state = CANCEL;
+ }
+ break;
+ }
+ case EVT_ESCKEY: {
+ if (event->val == KM_PRESS) {
+ state = CANCEL;
+ }
+ break;
+ }
+ }
+
+ if (state != CONTINUE) {
+ wmWindow *win = CTX_wm_window(C);
+ WM_cursor_modal_restore(win);
+
+ if (state == EXECUTE) {
+ CTX_store_set(C, opwait->context);
+ WM_operator_name_call_ptr(C,
+ opwait->optype_params.optype,
+ opwait->optype_params.opcontext,
+ opwait->optype_params.opptr);
+ CTX_store_set(C, NULL);
+ }
+
+ WM_event_remove_ui_handler(&win->modalhandlers,
+ ui_handler_wait_for_input,
+ ui_handler_wait_for_input_remove,
+ opwait,
+ false);
+
+ ui_handler_wait_for_input_remove(C, opwait);
+
+ return WM_UI_HANDLER_BREAK;
+ }
+
+ return WM_UI_HANDLER_CONTINUE;
+}
+
+void WM_operator_name_call_ptr_with_depends_on_cursor(
+ bContext *C, wmOperatorType *ot, short opcontext, PointerRNA *properties, const char *drawstr)
+{
+ int flag = ot->flag;
+
+ LISTBASE_FOREACH (wmOperatorTypeMacro *, macro, &ot->macro) {
+ wmOperatorType *otm = WM_operatortype_find(macro->idname, 0);
+ if (otm != NULL) {
+ flag |= otm->flag;
+ }
+ }
+
+ if ((flag & OPTYPE_DEPENDS_ON_CURSOR) == 0) {
+ WM_operator_name_call_ptr(C, ot, opcontext, properties);
+ return;
+ }
+
+ wmWindow *win = CTX_wm_window(C);
+ ScrArea *area = CTX_wm_area(C);
+
+ {
+ char header_text[UI_MAX_DRAW_STR];
+ SNPRINTF(header_text,
+ "%s %s",
+ IFACE_("Input pending "),
+ (drawstr && drawstr[0]) ? drawstr : CTX_IFACE_(ot->translation_context, ot->name));
+ if (area != NULL) {
+ ED_area_status_text(area, header_text);
+ }
+ else {
+ ED_workspace_status_text(C, header_text);
+ }
+ }
+
+ WM_cursor_modal_set(win, WM_CURSOR_PICK_AREA);
+
+ uiOperatorWaitForInput *opwait = MEM_callocN(sizeof(*opwait), __func__);
+ opwait->optype_params.optype = ot;
+ opwait->optype_params.opcontext = opcontext;
+ opwait->optype_params.opptr = properties;
+
+ opwait->area = area;
+
+ if (properties) {
+ opwait->optype_params.opptr = MEM_mallocN(sizeof(*opwait->optype_params.opptr), __func__);
+ *opwait->optype_params.opptr = *properties;
+ if (properties->data != NULL) {
+ opwait->optype_params.opptr->data = IDP_CopyProperty(properties->data);
+ }
+ }
+
+ bContextStore *store = CTX_store_get(C);
+ if (store) {
+ opwait->context = CTX_store_copy(store);
+ }
+
+ WM_event_add_ui_handler(C,
+ &win->modalhandlers,
+ ui_handler_wait_for_input,
+ ui_handler_wait_for_input_remove,
+ opwait,
+ WM_HANDLER_BLOCKING);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Handler Types
*
* General API for different handler types.
diff --git a/source/blender/windowmanager/wm_cursors.h b/source/blender/windowmanager/wm_cursors.h
index 2842538ebf1..d1694454490 100644
--- a/source/blender/windowmanager/wm_cursors.h
+++ b/source/blender/windowmanager/wm_cursors.h
@@ -74,6 +74,8 @@ typedef enum WMCursorType {
WM_CURSOR_NONE,
WM_CURSOR_MUTE,
+ WM_CURSOR_PICK_AREA,
+
/* --- ALWAYS LAST ----- */
WM_CURSOR_NUM,
} WMCursorType;