UI: Add 'x' icon to text buttons to clear content

This is useful e.g. for search buttons to quickly clear the filter string. We might want to make this optional for python scripts.
This commit is contained in:
Julian Eisel 2016-11-28 18:59:31 +01:00
parent df687837f8
commit e0307113a5
9 changed files with 94 additions and 62 deletions

View 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

View 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;

View 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);

View 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) {

View 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;

View 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) {

View 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;

View 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);
}

View 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) {