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>2017-11-01 20:30:07 +0300
committerCampbell Barton <ideasman42@gmail.com>2017-11-01 21:01:10 +0300
commit3ec4d0b51bf3a76725e39a57b0f30bec3edc6882 (patch)
tree8d99f8552e0cc465a9469f5d569433cbfa3dab88 /source/blender/editors/interface
parent07dbff7dc1d5cfca9fbfb74204a2298e16d471b4 (diff)
UI: Add UILayout.operator_menu_hold
This is an operator button that opens a menu when the button is held.
Diffstat (limited to 'source/blender/editors/interface')
-rw-r--r--source/blender/editors/interface/interface.c10
-rw-r--r--source/blender/editors/interface/interface_handlers.c67
-rw-r--r--source/blender/editors/interface/interface_intern.h4
-rw-r--r--source/blender/editors/interface/interface_layout.c46
-rw-r--r--source/blender/editors/interface/interface_regions.c21
-rw-r--r--source/blender/editors/interface/interface_widgets.c54
6 files changed, 183 insertions, 19 deletions
diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c
index dc4c1b71066..bc2397d8e47 100644
--- a/source/blender/editors/interface/interface.c
+++ b/source/blender/editors/interface/interface.c
@@ -2672,6 +2672,10 @@ static void ui_but_free(const bContext *C, uiBut *but)
MEM_freeN(but->tip_argN);
}
+ if (but->hold_argN) {
+ MEM_freeN(but->hold_argN);
+ }
+
if (but->active) {
/* XXX solve later, buttons should be free-able without context ideally,
* however they may have open tooltips or popup windows, which need to
@@ -4520,6 +4524,12 @@ void UI_but_focus_on_enter_event(wmWindow *win, uiBut *but)
wm_event_add(win, &event);
}
+void UI_but_func_hold_set(uiBut *but, uiButHandleHoldFunc func, void *argN)
+{
+ but->hold_func = func;
+ but->hold_argN = argN;
+}
+
void UI_but_string_info_get(bContext *C, uiBut *but, ...)
{
va_list args;
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index 0dec751526a..57f9b2ae356 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -304,6 +304,9 @@ typedef struct uiHandleButtonData {
bool used_mouse;
wmTimer *autoopentimer;
+ /* auto open (hold) */
+ wmTimer *hold_action_timer;
+
/* text selection/editing */
/* size of 'str' (including terminator) */
int maxlen;
@@ -7808,6 +7811,15 @@ static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState s
data->flashtimer = NULL;
}
+ /* add hold timer if it's used */
+ if (state == BUTTON_STATE_WAIT_RELEASE && (but->hold_func != NULL)) {
+ data->hold_action_timer = WM_event_add_timer(data->wm, data->window, TIMER, BUTTON_AUTO_OPEN_THRESH);
+ }
+ else if (data->hold_action_timer) {
+ WM_event_remove_timer(data->wm, data->window, data->hold_action_timer);
+ data->hold_action_timer = NULL;
+ }
+
/* add a blocking ui handler at the window handler for blocking, modal states
* but not for popups, because we already have a window level handler*/
if (!(but->block->handle && but->block->handle->popup)) {
@@ -8422,6 +8434,25 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but)
button_activate_state(C, but, BUTTON_STATE_EXIT);
break;
+ case TIMER:
+ {
+ if (event->customdata == data->hold_action_timer) {
+ if (true) {
+ data->cancel = true;
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ }
+ else {
+ /* Do this so we can still mouse-up, closing the menu and running the button.
+ * This is nice to support but there are times when the button gets left pressed.
+ * Keep disavled for now. */
+ WM_event_remove_timer(data->wm, data->window, data->hold_action_timer);
+ data->hold_action_timer = NULL;
+ }
+ retval = WM_UI_HANDLER_CONTINUE;
+ but->hold_func(C, data->region, but);
+ }
+ break;
+ }
case MOUSEMOVE:
if (ELEM(but->type, UI_BTYPE_LINK, UI_BTYPE_INLINK)) {
but->flag |= UI_SELECT;
@@ -9348,21 +9379,29 @@ static int ui_handle_menu_event(
if (inside == 0) {
uiSafetyRct *saferct = block->saferct.first;
- if (ELEM(event->type, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE) &&
- ELEM(event->val, KM_PRESS, KM_DBL_CLICK))
- {
- if ((is_parent_menu == false) && (U.uiflag & USER_MENUOPENAUTO) == 0) {
- /* for root menus, allow clicking to close */
- if (block->flag & (UI_BLOCK_OUT_1))
- menu->menuretval = UI_RETURN_OK;
- else
- menu->menuretval = UI_RETURN_OUT;
+ if (ELEM(event->type, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE)) {
+ if (ELEM(event->val, KM_PRESS, KM_DBL_CLICK)) {
+ if ((is_parent_menu == false) && (U.uiflag & USER_MENUOPENAUTO) == 0) {
+ /* for root menus, allow clicking to close */
+ if (block->flag & (UI_BLOCK_OUT_1))
+ menu->menuretval = UI_RETURN_OK;
+ else
+ menu->menuretval = UI_RETURN_OUT;
+ }
+ else if (saferct && !BLI_rctf_isect_pt(&saferct->parent, event->x, event->y)) {
+ if (block->flag & (UI_BLOCK_OUT_1))
+ menu->menuretval = UI_RETURN_OK;
+ else
+ menu->menuretval = UI_RETURN_OUT;
+ }
}
- else if (saferct && !BLI_rctf_isect_pt(&saferct->parent, event->x, event->y)) {
- if (block->flag & (UI_BLOCK_OUT_1))
- menu->menuretval = UI_RETURN_OK;
- else
- menu->menuretval = UI_RETURN_OUT;
+ else if (ELEM(event->val, KM_RELEASE, KM_CLICK)) {
+ /* For buttons that use a hold function, exit when mouse-up outside the menu. */
+ if (block->flag & UI_BLOCK_POPUP_HOLD) {
+ /* Note, we could check the cursor is over the parent button. */
+ menu->menuretval = UI_RETURN_CANCEL;
+ retval = WM_UI_HANDLER_CONTINUE;
+ }
}
}
}
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
index ab760c40451..da11c2abab2 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -264,6 +264,10 @@ struct uiBut {
void *rename_arg1;
void *rename_orig;
+ /* Run an action when holding the button down. */
+ uiButHandleHoldFunc hold_func;
+ void *hold_argN;
+
uiLink *link;
short linkto[2]; /* region relative coords */
diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c
index 616fc055c03..33dc74cbe23 100644
--- a/source/blender/editors/interface/interface_layout.c
+++ b/source/blender/editors/interface/interface_layout.c
@@ -795,7 +795,7 @@ static void ui_item_disabled(uiLayout *layout, const char *name)
* \param r_opptr: Optional, initialize with operator properties when not NULL.
* Will always be written to even in the case of errors.
*/
-void uiItemFullO_ptr(
+static uiBut *uiItemFullO_ptr_ex(
uiLayout *layout, wmOperatorType *ot,
const char *name, int icon, IDProperty *properties, int context, int flag,
PointerRNA *r_opptr)
@@ -862,6 +862,50 @@ void uiItemFullO_ptr(
*r_opptr = *opptr;
}
}
+
+ return but;
+}
+
+static void ui_item_hold_menu(struct bContext *C, ARegion *butregion, uiBut *but)
+{
+ uiPopupMenu *pup = UI_popup_menu_begin(C, "", ICON_NONE);
+ uiLayout *layout = UI_popup_menu_layout(pup);
+ uiBlock *block = layout->root->block;
+ UI_popup_menu_but_set(pup, butregion, but);
+
+ block->flag |= UI_BLOCK_POPUP_HOLD;
+
+ const char *menu_id = but->hold_argN;
+ MenuType *mt = WM_menutype_find(menu_id, true);
+ if (mt) {
+ Menu menu = {NULL};
+ menu.layout = layout;
+ menu.type = mt;
+ mt->draw(C, &menu);
+ }
+ else {
+ uiItemL(layout, "Menu Missing:", ICON_NONE);
+ uiItemL(layout, menu_id, ICON_NONE);
+ }
+ UI_popup_menu_end(C, pup);
+}
+
+void uiItemFullO_ptr(
+ uiLayout *layout, wmOperatorType *ot,
+ const char *name, int icon, IDProperty *properties, int context, int flag,
+ PointerRNA *r_opptr)
+{
+ uiItemFullO_ptr_ex(layout, ot, name, icon, properties, context, flag, r_opptr);
+}
+
+void uiItemFullOMenuHold_ptr(
+ uiLayout *layout, wmOperatorType *ot,
+ const char *name, int icon, IDProperty *properties, int context, int flag,
+ const char *menu_id,
+ PointerRNA *r_opptr)
+{
+ uiBut *but = uiItemFullO_ptr_ex(layout, ot, name, icon, properties, context, flag, r_opptr);
+ UI_but_func_hold_set(but, ui_item_hold_menu, BLI_strdup(menu_id));
}
void uiItemFullO(
diff --git a/source/blender/editors/interface/interface_regions.c b/source/blender/editors/interface/interface_regions.c
index 71124cf8eb7..06d5e5a314e 100644
--- a/source/blender/editors/interface/interface_regions.c
+++ b/source/blender/editors/interface/interface_regions.c
@@ -2627,6 +2627,7 @@ struct uiPopupMenu {
uiBlock *block;
uiLayout *layout;
uiBut *but;
+ ARegion *butregion;
int mx, my;
bool popup, slideout;
@@ -2873,17 +2874,33 @@ uiPopupMenu *UI_popup_menu_begin(bContext *C, const char *title, int icon)
return UI_popup_menu_begin_ex(C, title, __func__, icon);
}
+/**
+ * Setting the button makes the popup open from the button instead of the cursor.
+ */
+void UI_popup_menu_but_set(uiPopupMenu *pup, struct ARegion *butregion, uiBut *but)
+{
+ pup->but = but;
+ pup->butregion = butregion;
+}
+
/* set the whole structure to work */
void UI_popup_menu_end(bContext *C, uiPopupMenu *pup)
{
wmWindow *window = CTX_wm_window(C);
uiPopupBlockHandle *menu;
+ uiBut *but = NULL;
+ ARegion *butregion = NULL;
pup->popup = true;
pup->mx = window->eventstate->x;
pup->my = window->eventstate->y;
-
- menu = ui_popup_block_create(C, NULL, NULL, NULL, ui_block_func_POPUP, pup);
+
+ if (pup->but) {
+ but = pup->but;
+ butregion = pup->butregion;
+ }
+
+ menu = ui_popup_block_create(C, butregion, but, NULL, ui_block_func_POPUP, pup);
menu->popup = true;
UI_popup_handlers_add(C, &window->modalhandlers, menu, 0);
diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c
index 51bf09125ba..44a9d0d91b6 100644
--- a/source/blender/editors/interface/interface_widgets.c
+++ b/source/blender/editors/interface/interface_widgets.c
@@ -66,6 +66,15 @@
/* icons are 80% of height of button (16 pixels inside 20 height) */
#define ICON_SIZE_FROM_BUTRECT(rect) (0.8f * BLI_rcti_size_y(rect))
+#define UI_BUT_FLAGS_PUBLIC \
+ (UI_SELECT | UI_SCROLLED | UI_ACTIVE | UI_HAS_ICON | UI_TEXTINPUT | UI_HIDDEN)
+
+/* Bits 0..5 are from UI_SELECT .. etc */
+enum {
+ /* Show that holding the button opens a menu. */
+ UI_STATE_HOLD_ACTION = (1 << 6),
+};
+
/* ************** widget base functions ************** */
/**
* - in: roundbox codes for corner types and radius
@@ -184,6 +193,15 @@ static const unsigned int check_tria_face[4][3] = {
{3, 2, 4}, {3, 4, 5}, {1, 0, 3}, {0, 2, 3}
};
+#define OY -0.2
+#define SC 0.35
+static const unsigned int hold_action_tri_face[2][3] = {{2, 0, 1}, {3, 5, 4}};
+static const float hold_action_tri_vert[6][2] = {
+ {-0.5 + SC, 1.0 + OY}, {0.5, 1.0 + OY}, {0.5, 0.0 + OY + SC},
+};
+#undef OY
+#undef SC
+
/* ************************************************* */
void ui_draw_anti_tria(float x1, float y1, float x2, float y2, float x3, float y3)
@@ -529,6 +547,14 @@ static void widget_num_tria(uiWidgetTrias *tria, const rcti *rect, float triasiz
num_tria_face, ARRAY_SIZE(num_tria_face));
}
+static void widget_hold_action_tria(uiWidgetTrias *tria, const rcti *rect, float triasize, char where)
+{
+ widget_draw_tria_ex(
+ tria, rect, triasize, where,
+ hold_action_tri_vert, ARRAY_SIZE(hold_action_tri_vert),
+ hold_action_tri_face, ARRAY_SIZE(hold_action_tri_face));
+}
+
static void widget_scroll_circle(uiWidgetTrias *tria, const rcti *rect, float triasize, char where)
{
widget_draw_tria_ex(
@@ -3347,6 +3373,7 @@ static void widget_but(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int
widgetbase_draw(&wtb, wcol);
}
+#if 0
static void widget_roundbut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign)
{
uiWidgetBase wtb;
@@ -3359,6 +3386,25 @@ static void widget_roundbut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state),
widgetbase_draw(&wtb, wcol);
}
+#endif
+
+static void widget_roundbut_exec(uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
+{
+ uiWidgetBase wtb;
+ const float rad = 0.25f * U.widget_unit;
+
+ widget_init(&wtb);
+
+ if (state & UI_STATE_HOLD_ACTION) {
+ /* Show that keeping pressed performs another action (typically a menu). */
+ widget_hold_action_tria(&wtb.tria1, rect, 0.75f, 'r');
+ }
+
+ /* half rounded */
+ round_box_edges(&wtb, roundboxalign, rect, rad);
+
+ widgetbase_draw(&wtb, wcol);
+}
static void widget_draw_extra_mask(const bContext *C, uiBut *but, uiWidgetType *wt, rcti *rect)
{
@@ -3439,7 +3485,7 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type)
case UI_WTYPE_EXEC:
wt.wcol_theme = &btheme->tui.wcol_tool;
- wt.draw = widget_roundbut;
+ wt.draw = widget_roundbut_exec;
break;
case UI_WTYPE_TOOLTIP:
@@ -3849,7 +3895,7 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct
roundboxalign = widget_roundbox_set(but, rect);
- state = but->flag;
+ state = but->flag & UI_BUT_FLAGS_PUBLIC;
if ((but->editstr) ||
(UNLIKELY(but->flag & UI_BUT_DRAG_MULTI) && ui_but_drag_multi_edit_get(but)))
@@ -3857,6 +3903,10 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct
state |= UI_TEXTINPUT;
}
+ if (but->hold_func) {
+ state |= UI_STATE_HOLD_ACTION;
+ }
+
if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE))
if (but->dt != UI_EMBOSS_PULLDOWN)
disabled = true;