UI: Add 'x' icon to text buttons to clear content
authorJulian Eisel <eiseljulian@gmail.com>
Mon, 28 Nov 2016 17:59:31 +0000 (18:59 +0100)
committerJulian Eisel <eiseljulian@gmail.com>
Mon, 28 Nov 2016 18:03:31 +0000 (19:03 +0100)
This is useful e.g. for search buttons to quickly clear the filter string. We might want to make this optional for python scripts.

source/blender/editors/include/UI_interface.h
source/blender/editors/interface/interface.c
source/blender/editors/interface/interface_eyedropper.c
source/blender/editors/interface/interface_handlers.c
source/blender/editors/interface/interface_intern.h
source/blender/editors/interface/interface_layout.c
source/blender/editors/interface/interface_regions.c
source/blender/editors/interface/interface_utils.c
source/blender/editors/interface/interface_widgets.c

index fd5351394c3ec2a2ebe6aef5a7f45c3d7c39046f..9fbce7dd20336d5dbc870208f35f59fc8b9725d8 100644 (file)
@@ -181,7 +181,7 @@ enum {
        UI_BUT_HAS_SEP_CHAR    = (1 << 27),  /* but->str contains UI_SEP_CHAR, used for key shortcuts */
        UI_BUT_UPDATE_DELAY    = (1 << 28),  /* don't run updates while dragging (needed in rare cases). */
        UI_BUT_TEXTEDIT_UPDATE = (1 << 29),  /* when widget is in textedit mode, update value on each char stroke */
-       UI_BUT_SEARCH_UNLINK   = (1 << 30),  /* show unlink for search button */
+       UI_BUT_VALUE_CLEAR     = (1 << 30),  /* show 'x' icon to clear/unlink value of text or search button */
 };
 
 #define UI_PANEL_WIDTH          340
index 6bba35e821fd7a6930f27affd2dda4c4d03fd51a..a913421d12c07d35d5b61b5e2e4d600c1343ef0e 100644 (file)
@@ -1987,22 +1987,29 @@ uiBut *ui_but_drag_multi_edit_get(uiBut *but)
 /** \name Check to show extra icons
  *
  * Extra icons are shown on the right hand side of buttons.
+ * This could (should!) definitely become more generic, but for now this is good enough.
  * \{ */
 
+static bool ui_but_icon_extra_is_visible_text_clear(const uiBut *but)
+{
+       BLI_assert(but->type == UI_BTYPE_TEXT);
+       return ((but->flag & UI_BUT_VALUE_CLEAR) && but->drawstr && but->drawstr[0]);
+}
+
 static bool ui_but_icon_extra_is_visible_search_unlink(const uiBut *but)
 {
        BLI_assert(but->type == UI_BTYPE_SEARCH_MENU);
        return ((but->editstr == NULL) &&
                (but->drawstr[0] != '\0') &&
-               (but->flag & UI_BUT_SEARCH_UNLINK));
+               (but->flag & UI_BUT_VALUE_CLEAR));
 }
 
-static bool ui_but_icon_extra_is_visible_eyedropper(uiBut *but)
+static bool ui_but_icon_extra_is_visible_search_eyedropper(uiBut *but)
 {
        StructRNA *type;
        short idcode;
 
-       BLI_assert(but->type == UI_BTYPE_SEARCH_MENU && (but->flag & UI_BUT_SEARCH_UNLINK));
+       BLI_assert(but->type == UI_BTYPE_SEARCH_MENU && (but->flag & UI_BUT_VALUE_CLEAR));
 
        if (but->rnaprop == NULL) {
                return false;
@@ -2011,21 +2018,31 @@ static bool ui_but_icon_extra_is_visible_eyedropper(uiBut *but)
        type = RNA_property_pointer_type(&but->rnapoin, but->rnaprop);
        idcode = RNA_type_to_ID_code(type);
 
-
        return ((but->editstr == NULL) &&
                (idcode == ID_OB || OB_DATA_SUPPORT_ID(idcode)));
 }
 
 uiButExtraIconType ui_but_icon_extra_get(uiBut *but)
 {
-       if ((but->flag & UI_BUT_SEARCH_UNLINK) == 0) {
-               /* pass */
-       }
-       else if (ui_but_icon_extra_is_visible_search_unlink(but)) {
-               return UI_BUT_ICONEXTRA_UNLINK;
-       }
-       else if (ui_but_icon_extra_is_visible_eyedropper(but)) {
-               return UI_BUT_ICONEXTRA_EYEDROPPER;
+       switch (but->type) {
+               case UI_BTYPE_TEXT:
+                       if (ui_but_icon_extra_is_visible_text_clear(but)) {
+                               return UI_BUT_ICONEXTRA_CLEAR;
+                       }
+                       break;
+               case UI_BTYPE_SEARCH_MENU:
+                       if ((but->flag & UI_BUT_VALUE_CLEAR) == 0) {
+                               /* pass */
+                       }
+                       else if (ui_but_icon_extra_is_visible_search_unlink(but)) {
+                               return UI_BUT_ICONEXTRA_CLEAR;
+                       }
+                       else if (ui_but_icon_extra_is_visible_search_eyedropper(but)) {
+                               return UI_BUT_ICONEXTRA_EYEDROPPER;
+                       }
+                       break;
+               default:
+                       break;
        }
 
        return UI_BUT_ICONEXTRA_NONE;
index d7f06b7db13e1f07c8af7e9c5f8fd93be1ee39ed..5f7a018e4c2e01f477bff09c6068588b80e024de 100644 (file)
@@ -748,7 +748,7 @@ static int datadropper_poll(bContext *C)
        if ((CTX_wm_window(C) != NULL) &&
            (but = UI_context_active_but_prop_get(C, &ptr, &prop, &index_dummy)) &&
            (but->type == UI_BTYPE_SEARCH_MENU) &&
-           (but->flag & UI_BUT_SEARCH_UNLINK))
+           (but->flag & UI_BUT_VALUE_CLEAR))
        {
                if (prop && RNA_property_type(prop) == PROP_POINTER) {
                        StructRNA *type = RNA_property_pointer_type(&ptr, prop);
index fc511d61e2b9c5d483ff4669bc4ba77928f2517d..33f1d5e93cbdd751082ac01a0dc8dea29faa6f8a 100644 (file)
@@ -2563,6 +2563,18 @@ void ui_but_text_password_hide(char password_str[UI_MAX_PASSWORD_STR], uiBut *bu
        }
 }
 
+static void ui_but_text_clear(bContext *C, uiBut *but, uiHandleButtonData *data)
+{
+       /* most likely NULL, but let's check, and give it temp zero string */
+       if (!data->str) {
+               data->str = MEM_callocN(1, "temp str");
+       }
+       data->str[0] = 0;
+
+       ui_apply_but_TEX(C, but, data);
+       button_activate_state(C, but, BUTTON_STATE_EXIT);
+}
+
 
 /* ************* in-button text selection/editing ************* */
 
@@ -3842,6 +3854,21 @@ static int ui_do_but_KEYEVT(
        return WM_UI_HANDLER_CONTINUE;
 }
 
+static bool ui_but_is_mouse_over_icon_extra(const ARegion *region, uiBut *but, const int mouse_xy[2])
+{
+       int x = mouse_xy[0], y = mouse_xy[1];
+       rcti icon_rect;
+
+       BLI_assert(ui_but_icon_extra_get(but) != UI_BUT_ICONEXTRA_NONE);
+
+       ui_window_to_block(region, but->block, &x, &y);
+
+       BLI_rcti_rctf_copy(&icon_rect, &but->rect);
+       icon_rect.xmin = icon_rect.xmax - (BLI_rcti_size_y(&icon_rect));
+
+       return BLI_rcti_isect_pt(&icon_rect, x, y);
+}
+
 static int ui_do_but_TEX(
         bContext *C, uiBlock *block, uiBut *but,
         uiHandleButtonData *data, const wmEvent *event)
@@ -3855,7 +3882,14 @@ static int ui_do_but_TEX(
                                /* pass */
                        }
                        else {
-                               button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
+                               const bool has_icon_extra = ui_but_icon_extra_get(but) == UI_BUT_ICONEXTRA_CLEAR;
+
+                               if (has_icon_extra && ui_but_is_mouse_over_icon_extra(data->region, but, &event->x)) {
+                                       ui_but_text_clear(C, but, data);
+                               }
+                               else {
+                                       button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
+                               }
                                return WM_UI_HANDLER_BREAK;
                        }
                }
@@ -3876,47 +3910,29 @@ static int ui_do_but_SEARCH_UNLINK(
         bContext *C, uiBlock *block, uiBut *but,
         uiHandleButtonData *data, const wmEvent *event)
 {
-       uiButExtraIconType extra_icon_type;
+       const uiButExtraIconType extra_icon_type = ui_but_icon_extra_get(but);
+       const bool has_icon_extra = (extra_icon_type != UI_BUT_ICONEXTRA_NONE);
 
        /* unlink icon is on right */
        if ((ELEM(event->type, LEFTMOUSE, EVT_BUT_OPEN, PADENTER, RETKEY)) &&
-           ((extra_icon_type = ui_but_icon_extra_get(but)) != UI_BUT_ICONEXTRA_NONE))
+           (has_icon_extra == true) &&
+           (ui_but_is_mouse_over_icon_extra(data->region, but, &event->x) == true))
        {
-               ARegion *ar = data->region;
-               rcti rect;
-               int x = event->x, y = event->y;
-               
-               ui_window_to_block(ar, but->block, &x, &y);
-               
-               BLI_rcti_rctf_copy(&rect, &but->rect);
-               
-               rect.xmin = rect.xmax - (BLI_rcti_size_y(&rect));
-               /* handle click on unlink/eyedropper icon */
-               if (BLI_rcti_isect_pt(&rect, x, y)) {
-                       /* doing this on KM_PRESS calls eyedropper after clicking unlink icon */
-                       if (event->val == KM_RELEASE) {
-                               /* unlink */
-                               if (extra_icon_type == UI_BUT_ICONEXTRA_UNLINK) {
-                                       /* most likely NULL, but let's check, and give it temp zero string */
-                                       if (data->str == NULL) {
-                                               data->str = MEM_callocN(1, "temp str");
-                                       }
-                                       data->str[0] = 0;
-
-                                       ui_apply_but_TEX(C, but, data);
-                                       button_activate_state(C, but, BUTTON_STATE_EXIT);
-                               }
-                               /* eyedropper */
-                               else if (extra_icon_type == UI_BUT_ICONEXTRA_EYEDROPPER) {
-                                       WM_operator_name_call(C, "UI_OT_eyedropper_id", WM_OP_INVOKE_DEFAULT, NULL);
-                               }
-                               else {
-                                       BLI_assert(0);
-                               }
+               /* doing this on KM_PRESS calls eyedropper after clicking unlink icon */
+               if (event->val == KM_RELEASE) {
+                       /* unlink */
+                       if (extra_icon_type == UI_BUT_ICONEXTRA_CLEAR) {
+                               ui_but_text_clear(C, but, data);
+                       }
+                       /* eyedropper */
+                       else if (extra_icon_type == UI_BUT_ICONEXTRA_EYEDROPPER) {
+                               WM_operator_name_call(C, "UI_OT_eyedropper_id", WM_OP_INVOKE_DEFAULT, NULL);
+                       }
+                       else {
+                               BLI_assert(0);
                        }
-
-                       return WM_UI_HANDLER_BREAK;
                }
+               return WM_UI_HANDLER_BREAK;
        }
        return ui_do_but_TEX(C, block, but, data, event);
 }
@@ -7091,7 +7107,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
                case UI_BTYPE_TEXT:
                case UI_BTYPE_SEARCH_MENU:
                        if ((but->type == UI_BTYPE_SEARCH_MENU) &&
-                           (but->flag & UI_BUT_SEARCH_UNLINK))
+                           (but->flag & UI_BUT_VALUE_CLEAR))
                        {
                                retval = ui_do_but_SEARCH_UNLINK(C, block, but, data, event);
                                if (retval & WM_UI_HANDLER_BREAK) {
index fcf827bdbe6c73e423d7bd6ee43c5ed186fc56a4..d8f9fdcbaae7f730bbb9fe092ea4b363fc914547 100644 (file)
@@ -127,7 +127,7 @@ enum {
  * (e.g. 'x' icon in search menu) - used with ui_but_icon_extra_get */
 typedef enum uiButExtraIconType {
        UI_BUT_ICONEXTRA_NONE = 1,
-       UI_BUT_ICONEXTRA_UNLINK,
+       UI_BUT_ICONEXTRA_CLEAR,
        UI_BUT_ICONEXTRA_EYEDROPPER,
 } uiButExtraIconType;
 
index 875522e01c6b25d2e6c2f03fa0985f891eb28c05..b128bf47b5f697fc4f7369d7f516f7c70a272fa4 100644 (file)
@@ -1659,7 +1659,7 @@ void ui_but_add_search(uiBut *but, PointerRNA *ptr, PropertyRNA *prop, PointerRN
                but->rnasearchprop = searchprop;
                but->drawflag |= UI_BUT_ICON_LEFT | UI_BUT_TEXT_LEFT;
                if (RNA_property_is_unlink(prop)) {
-                       but->flag |= UI_BUT_SEARCH_UNLINK;
+                       but->flag |= UI_BUT_VALUE_CLEAR;
                }
 
                if (RNA_property_type(prop) == PROP_ENUM) {
index cdf34642a8d47f73981b5c999f437b5abf48c4e0..466978272bcf4c700fbc6b5f420851475b57448e 100644 (file)
@@ -851,7 +851,7 @@ static void ui_searchbox_select(bContext *C, ARegion *ar, uiBut *but, int step)
                }
                else {
                        /* only let users step into an 'unset' state for unlink buttons */
-                       data->active = (but->flag & UI_BUT_SEARCH_UNLINK) ? -1 : 0;
+                       data->active = (but->flag & UI_BUT_VALUE_CLEAR) ? -1 : 0;
                }
        }
        
@@ -922,8 +922,8 @@ bool ui_searchbox_apply(uiBut *but, ARegion *ar)
 
                return true;
        }
-       else if (but->flag & UI_BUT_SEARCH_UNLINK) {
-               /* It is valid for _UNLINK flavor to have no active element (it's a valid way to unlink). */
+       else if (but->flag & UI_BUT_VALUE_CLEAR) {
+               /* It is valid for _VALUE_CLEAR flavor to have no active element (it's a valid way to unlink). */
                but->editstr[0] = '\0';
 
                return true;
index 1d51c0588b6b32cf87509edf0540f66ebdc8942f..f4df3d4ec2eb45a2a068e31eea810e35d880db5e 100644 (file)
@@ -119,6 +119,7 @@ uiBut *uiDefAutoButR(uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, int ind
                        else
                                but = uiDefButR_prop(block, UI_BTYPE_TEXT, 0, name, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL);
 
+                       UI_but_flag_enable(but, UI_BUT_VALUE_CLEAR); /* might want to make this optional? */
                        if (RNA_property_flag(prop) & PROP_TEXTEDIT_UPDATE) {
                                UI_but_flag_enable(but, UI_BUT_TEXTEDIT_UPDATE);
                        }
index c285d753b96663fd8a46cdd1d93048f58a83ce73..d43a94c55143cbf6bcb33c3984c4c252782d3db2 100644 (file)
@@ -1508,10 +1508,10 @@ static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *b
 /* draws text and icons for buttons */
 static void widget_draw_text_icon(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *but, rcti *rect)
 {
+       const uiButExtraIconType extra_icon_type = ui_but_icon_extra_get(but);
        const bool show_menu_icon = ui_but_draw_menu_icon(but);
        float alpha = (float)wcol->text[3] / 255.0f;
        char password_str[UI_MAX_DRAW_STR];
-       uiButExtraIconType extra_icon_type;
 
        ui_but_text_password_hide(password_str, but, false);
 
@@ -1577,15 +1577,13 @@ static void widget_draw_text_icon(uiFontStyle *fstyle, uiWidgetColors *wcol, uiB
                rect->xmax -= (UI_TEXT_MARGIN_X * U.widget_unit) / but->block->aspect;
        }
 
-       /* unlink icon for this button type */
-       if ((but->type == UI_BTYPE_SEARCH_MENU) &&
-           ((extra_icon_type = ui_but_icon_extra_get(but)) != UI_BUT_ICONEXTRA_NONE))
-       {
+       /* extra icons, e.g. 'x' icon to clear text or icon for eyedropper */
+       if (extra_icon_type != UI_BUT_ICONEXTRA_NONE) {
                rcti temp = *rect;
 
                temp.xmin = temp.xmax - (BLI_rcti_size_y(rect) * 1.08f);
 
-               if (extra_icon_type == UI_BUT_ICONEXTRA_UNLINK) {
+               if (extra_icon_type == UI_BUT_ICONEXTRA_CLEAR) {
                        widget_draw_icon(but, ICON_X, alpha, &temp, false);
                }
                else if (extra_icon_type == UI_BUT_ICONEXTRA_EYEDROPPER) {