Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/interface/interface_region_search.c
| Show First 20 Lines • Show All 273 Lines • ▼ Show 20 Lines | bool ui_searchbox_inside(ARegion *region, int x, int y) | ||||
| return BLI_rcti_isect_pt(&data->bbox, x - region->winrct.xmin, y - region->winrct.ymin); | return BLI_rcti_isect_pt(&data->bbox, x - region->winrct.xmin, y - region->winrct.ymin); | ||||
| } | } | ||||
| /* string validated to be of correct length (but->hardmax) */ | /* string validated to be of correct length (but->hardmax) */ | ||||
| bool ui_searchbox_apply(uiBut *but, ARegion *region) | bool ui_searchbox_apply(uiBut *but, ARegion *region) | ||||
| { | { | ||||
| uiSearchboxData *data = region->regiondata; | uiSearchboxData *data = region->regiondata; | ||||
| uiButSearch *search_but = (uiButSearch *)but; | |||||
| but->func_arg2 = NULL; | BLI_assert(but->type == UI_BTYPE_SEARCH_MENU); | ||||
| search_but->item_active = NULL; | |||||
| if (data->active != -1) { | if (data->active != -1) { | ||||
| const char *name = data->items.names[data->active]; | const char *name = data->items.names[data->active]; | ||||
| const char *name_sep = data->use_sep ? strrchr(name, UI_SEP_CHAR) : NULL; | const char *name_sep = data->use_sep ? strrchr(name, UI_SEP_CHAR) : NULL; | ||||
| BLI_strncpy(but->editstr, name, name_sep ? (name_sep - name) + 1 : data->items.maxstrlen); | BLI_strncpy(but->editstr, name, name_sep ? (name_sep - name) + 1 : data->items.maxstrlen); | ||||
| but->func_arg2 = data->items.pointers[data->active]; | search_but->item_active = data->items.pointers[data->active]; | ||||
| return true; | return true; | ||||
| } | } | ||||
| if (but->flag & UI_BUT_VALUE_CLEAR) { | if (but->flag & UI_BUT_VALUE_CLEAR) { | ||||
| /* It is valid for _VALUE_CLEAR flavor to have no active element | /* It is valid for _VALUE_CLEAR flavor to have no active element | ||||
| * (it's a valid way to unlink). */ | * (it's a valid way to unlink). */ | ||||
| but->editstr[0] = '\0'; | but->editstr[0] = '\0'; | ||||
| return true; | return true; | ||||
| } | } | ||||
| return false; | return false; | ||||
| } | } | ||||
| static struct ARegion *wm_searchbox_tooltip_init(struct bContext *C, | static struct ARegion *wm_searchbox_tooltip_init(struct bContext *C, | ||||
| struct ARegion *region, | struct ARegion *region, | ||||
| int *UNUSED(r_pass), | int *UNUSED(r_pass), | ||||
| double *UNUSED(pass_delay), | double *UNUSED(pass_delay), | ||||
| bool *r_exit_on_event) | bool *r_exit_on_event) | ||||
| { | { | ||||
| *r_exit_on_event = true; | *r_exit_on_event = true; | ||||
| LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { | LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { | ||||
| LISTBASE_FOREACH (uiBut *, but, &block->buttons) { | LISTBASE_FOREACH (uiBut *, but, &block->buttons) { | ||||
| if (but->search && but->search->tooltip_fn) { | if (but->type != UI_BTYPE_SEARCH_MENU) { | ||||
| return but->search->tooltip_fn(C, region, but->search->arg, but->func_arg2); | continue; | ||||
| } | |||||
| uiButSearch *search_but = (uiButSearch *)but; | |||||
| if (search_but->item_tooltip_fn) { | |||||
| return search_but->item_tooltip_fn(C, region, search_but->arg, search_but->item_active); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| bool ui_searchbox_event( | bool ui_searchbox_event( | ||||
| bContext *C, ARegion *region, uiBut *but, ARegion *butregion, const wmEvent *event) | bContext *C, ARegion *region, uiBut *but, ARegion *butregion, const wmEvent *event) | ||||
| { | { | ||||
| uiSearchboxData *data = region->regiondata; | uiSearchboxData *data = region->regiondata; | ||||
| uiButSearch *search_but = (uiButSearch *)but; | |||||
| int type = event->type, val = event->val; | int type = event->type, val = event->val; | ||||
| bool handled = false; | bool handled = false; | ||||
| bool tooltip_timer_started = false; | bool tooltip_timer_started = false; | ||||
| BLI_assert(but->type == UI_BTYPE_SEARCH_MENU); | |||||
| if (type == MOUSEPAN) { | if (type == MOUSEPAN) { | ||||
| ui_pan_to_scroll(event, &type, &val); | ui_pan_to_scroll(event, &type, &val); | ||||
| } | } | ||||
| switch (type) { | switch (type) { | ||||
| case WHEELUPMOUSE: | case WHEELUPMOUSE: | ||||
| case EVT_UPARROWKEY: | case EVT_UPARROWKEY: | ||||
| ui_searchbox_select(C, region, but, -1); | ui_searchbox_select(C, region, but, -1); | ||||
| handled = true; | handled = true; | ||||
| break; | break; | ||||
| case WHEELDOWNMOUSE: | case WHEELDOWNMOUSE: | ||||
| case EVT_DOWNARROWKEY: | case EVT_DOWNARROWKEY: | ||||
| ui_searchbox_select(C, region, but, 1); | ui_searchbox_select(C, region, but, 1); | ||||
| handled = true; | handled = true; | ||||
| break; | break; | ||||
| case RIGHTMOUSE: | case RIGHTMOUSE: | ||||
| if (val) { | if (val) { | ||||
| if (but->search->context_menu_fn) { | if (search_but->item_context_menu_fn) { | ||||
| if (data->active != -1) { | if (data->active != -1) { | ||||
| /* Check the cursor is over the active element | /* Check the cursor is over the active element | ||||
| * (a little confusing if this isn't the case, although it does work). */ | * (a little confusing if this isn't the case, although it does work). */ | ||||
| rcti rect; | rcti rect; | ||||
| ui_searchbox_butrect(&rect, data, data->active); | ui_searchbox_butrect(&rect, data, data->active); | ||||
| if (BLI_rcti_isect_pt( | if (BLI_rcti_isect_pt( | ||||
| &rect, event->x - region->winrct.xmin, event->y - region->winrct.ymin)) { | &rect, event->x - region->winrct.xmin, event->y - region->winrct.ymin)) { | ||||
| void *active = data->items.pointers[data->active]; | void *active = data->items.pointers[data->active]; | ||||
| if (but->search->context_menu_fn(C, but->search->arg, active, event)) { | if (search_but->item_context_menu_fn(C, search_but->arg, active, event)) { | ||||
| handled = true; | handled = true; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| break; | break; | ||||
| case MOUSEMOVE: { | case MOUSEMOVE: { | ||||
| Show All 17 Lines | case MOUSEMOVE: { | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| if (U.flag & USER_TOOLTIPS) { | if (U.flag & USER_TOOLTIPS) { | ||||
| if (is_inside) { | if (is_inside) { | ||||
| if (data->active != -1) { | if (data->active != -1) { | ||||
| ScrArea *area = CTX_wm_area(C); | ScrArea *area = CTX_wm_area(C); | ||||
| but->func_arg2 = data->items.pointers[data->active]; | search_but->item_active = data->items.pointers[data->active]; | ||||
| WM_tooltip_timer_init(C, CTX_wm_window(C), area, butregion, wm_searchbox_tooltip_init); | WM_tooltip_timer_init(C, CTX_wm_window(C), area, butregion, wm_searchbox_tooltip_init); | ||||
| tooltip_timer_started = true; | tooltip_timer_started = true; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| if (handled && (tooltip_timer_started == false)) { | if (handled && (tooltip_timer_started == false)) { | ||||
| wmWindow *win = CTX_wm_window(C); | wmWindow *win = CTX_wm_window(C); | ||||
| WM_tooltip_clear(C, win); | WM_tooltip_clear(C, win); | ||||
| } | } | ||||
| return handled; | return handled; | ||||
| } | } | ||||
| /** Wrap #uiButSearchUpdateFn callback. */ | /** Wrap #uiButSearchUpdateFn callback. */ | ||||
| static void ui_searchbox_update_fn(bContext *C, uiBut *but, const char *str, uiSearchItems *items) | static void ui_searchbox_update_fn(bContext *C, | ||||
| uiButSearch *search_but, | |||||
| const char *str, | |||||
| uiSearchItems *items) | |||||
| { | { | ||||
| wmWindow *win = CTX_wm_window(C); | wmWindow *win = CTX_wm_window(C); | ||||
| WM_tooltip_clear(C, win); | WM_tooltip_clear(C, win); | ||||
| but->search->update_fn(C, but->search->arg, str, items); | search_but->items_update_fn(C, search_but->arg, str, items); | ||||
| } | } | ||||
| /* region is the search box itself */ | /* region is the search box itself */ | ||||
| void ui_searchbox_update(bContext *C, ARegion *region, uiBut *but, const bool reset) | void ui_searchbox_update(bContext *C, ARegion *region, uiBut *but, const bool reset) | ||||
| { | { | ||||
| uiButSearch *search_but = (uiButSearch *)but; | |||||
| uiSearchboxData *data = region->regiondata; | uiSearchboxData *data = region->regiondata; | ||||
| BLI_assert(but->type == UI_BTYPE_SEARCH_MENU); | |||||
| /* reset vars */ | /* reset vars */ | ||||
| data->items.totitem = 0; | data->items.totitem = 0; | ||||
| data->items.more = 0; | data->items.more = 0; | ||||
| if (reset == false) { | if (reset == false) { | ||||
| data->items.offset_i = data->items.offset; | data->items.offset_i = data->items.offset; | ||||
| } | } | ||||
| else { | else { | ||||
| data->items.offset_i = data->items.offset = 0; | data->items.offset_i = data->items.offset = 0; | ||||
| data->active = -1; | data->active = -1; | ||||
| /* handle active */ | /* handle active */ | ||||
| if (but->search->update_fn && but->func_arg2) { | if (search_but->items_update_fn && search_but->item_active) { | ||||
| data->items.active = but->func_arg2; | data->items.active = search_but->item_active; | ||||
| ui_searchbox_update_fn(C, but, but->editstr, &data->items); | ui_searchbox_update_fn(C, search_but, but->editstr, &data->items); | ||||
| data->items.active = NULL; | data->items.active = NULL; | ||||
| /* found active item, calculate real offset by centering it */ | /* found active item, calculate real offset by centering it */ | ||||
| if (data->items.totitem) { | if (data->items.totitem) { | ||||
| /* first case, begin of list */ | /* first case, begin of list */ | ||||
| if (data->items.offset_i < data->items.maxitem) { | if (data->items.offset_i < data->items.maxitem) { | ||||
| data->active = data->items.offset_i; | data->active = data->items.offset_i; | ||||
| data->items.offset_i = 0; | data->items.offset_i = 0; | ||||
| Show All 12 Lines | if (search_but->items_update_fn && search_but->item_active) { | ||||
| } | } | ||||
| } | } | ||||
| data->items.offset = data->items.offset_i; | data->items.offset = data->items.offset_i; | ||||
| data->items.totitem = 0; | data->items.totitem = 0; | ||||
| } | } | ||||
| } | } | ||||
| /* callback */ | /* callback */ | ||||
| if (but->search->update_fn) { | if (search_but->items_update_fn) { | ||||
| ui_searchbox_update_fn(C, but, but->editstr, &data->items); | ui_searchbox_update_fn(C, search_but, but->editstr, &data->items); | ||||
| } | } | ||||
| /* handle case where editstr is equal to one of items */ | /* handle case where editstr is equal to one of items */ | ||||
| if (reset && data->active == -1) { | if (reset && data->active == -1) { | ||||
| int a; | int a; | ||||
| for (a = 0; a < data->items.totitem; a++) { | for (a = 0; a < data->items.totitem; a++) { | ||||
| const char *name = data->items.names[a]; | const char *name = data->items.names[a]; | ||||
| Show All 11 Lines | void ui_searchbox_update(bContext *C, ARegion *region, uiBut *but, const bool reset) | ||||
| /* validate selected item */ | /* validate selected item */ | ||||
| ui_searchbox_select(C, region, but, 0); | ui_searchbox_select(C, region, but, 0); | ||||
| ED_region_tag_redraw(region); | ED_region_tag_redraw(region); | ||||
| } | } | ||||
| int ui_searchbox_autocomplete(bContext *C, ARegion *region, uiBut *but, char *str) | int ui_searchbox_autocomplete(bContext *C, ARegion *region, uiBut *but, char *str) | ||||
| { | { | ||||
| uiButSearch *search_but = (uiButSearch *)but; | |||||
| uiSearchboxData *data = region->regiondata; | uiSearchboxData *data = region->regiondata; | ||||
| int match = AUTOCOMPLETE_NO_MATCH; | int match = AUTOCOMPLETE_NO_MATCH; | ||||
| BLI_assert(but->type == UI_BTYPE_SEARCH_MENU); | |||||
| if (str[0]) { | if (str[0]) { | ||||
| data->items.autocpl = UI_autocomplete_begin(str, ui_but_string_get_max_length(but)); | data->items.autocpl = UI_autocomplete_begin(str, ui_but_string_get_max_length(but)); | ||||
| ui_searchbox_update_fn(C, but, but->editstr, &data->items); | ui_searchbox_update_fn(C, search_but, but->editstr, &data->items); | ||||
| match = UI_autocomplete_end(data->items.autocpl, str); | match = UI_autocomplete_end(data->items.autocpl, str); | ||||
| data->items.autocpl = NULL; | data->items.autocpl = NULL; | ||||
| } | } | ||||
| return match; | return match; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 123 Lines • ▼ Show 20 Lines | static void ui_searchbox_region_free_cb(ARegion *region) | ||||
| MEM_freeN(data->items.pointers); | MEM_freeN(data->items.pointers); | ||||
| MEM_freeN(data->items.icons); | MEM_freeN(data->items.icons); | ||||
| MEM_freeN(data->items.states); | MEM_freeN(data->items.states); | ||||
| MEM_freeN(data); | MEM_freeN(data); | ||||
| region->regiondata = NULL; | region->regiondata = NULL; | ||||
| } | } | ||||
| ARegion *ui_searchbox_create_generic(bContext *C, ARegion *butregion, uiBut *but) | ARegion *ui_searchbox_create_generic(bContext *C, ARegion *butregion, uiButSearch *search_but) | ||||
| { | { | ||||
| wmWindow *win = CTX_wm_window(C); | wmWindow *win = CTX_wm_window(C); | ||||
| const uiStyle *style = UI_style_get(); | const uiStyle *style = UI_style_get(); | ||||
| uiBut *but = &search_but->but; | |||||
| static ARegionType type; | static ARegionType type; | ||||
| ARegion *region; | ARegion *region; | ||||
| uiSearchboxData *data; | uiSearchboxData *data; | ||||
| float aspect = but->block->aspect; | float aspect = but->block->aspect; | ||||
| rctf rect_fl; | rctf rect_fl; | ||||
| rcti rect_i; | rcti rect_i; | ||||
| const int margin = UI_POPUP_MARGIN; | const int margin = UI_POPUP_MARGIN; | ||||
| int winx /*, winy */, ofsx, ofsy; | int winx /*, winy */, ofsx, ofsy; | ||||
| Show All 32 Lines | ARegion *ui_searchbox_create_generic(bContext *C, ARegion *butregion, uiButSearch *search_but) | ||||
| /* Only show key shortcuts when needed (checking RNA prop pointer is useless here, a lot of | /* Only show key shortcuts when needed (checking RNA prop pointer is useless here, a lot of | ||||
| * buttons are about data without having that pointer defined, let's rather try with optype!). | * buttons are about data without having that pointer defined, let's rather try with optype!). | ||||
| * One can also enforce that behavior by setting | * One can also enforce that behavior by setting | ||||
| * UI_BUT_HAS_SHORTCUT drawflag of search button. */ | * UI_BUT_HAS_SHORTCUT drawflag of search button. */ | ||||
| if (but->optype != NULL || (but->drawflag & UI_BUT_HAS_SHORTCUT) != 0) { | if (but->optype != NULL || (but->drawflag & UI_BUT_HAS_SHORTCUT) != 0) { | ||||
| data->use_sep = true; | data->use_sep = true; | ||||
| } | } | ||||
| data->sep_string = but->search->sep_string; | data->sep_string = search_but->item_sep_string; | ||||
| /* compute position */ | /* compute position */ | ||||
| if (but->block->flag & UI_BLOCK_SEARCH_MENU) { | if (but->block->flag & UI_BLOCK_SEARCH_MENU) { | ||||
| const int search_but_h = BLI_rctf_size_y(&but->rect) + 10; | const int search_but_h = BLI_rctf_size_y(&but->rect) + 10; | ||||
| /* this case is search menu inside other menu */ | /* this case is search menu inside other menu */ | ||||
| /* we copy region size */ | /* we copy region size */ | ||||
| region->winrct = butregion->winrct; | region->winrct = butregion->winrct; | ||||
| ▲ Show 20 Lines • Show All 202 Lines • ▼ Show 20 Lines | if (data->items.offset) { | ||||
| ui_searchbox_butrect(&rect, data, 0); | ui_searchbox_butrect(&rect, data, 0); | ||||
| GPU_blend(true); | GPU_blend(true); | ||||
| UI_icon_draw((BLI_rcti_size_x(&rect)) / 2, rect.ymax - 7, ICON_TRIA_UP); | UI_icon_draw((BLI_rcti_size_x(&rect)) / 2, rect.ymax - 7, ICON_TRIA_UP); | ||||
| GPU_blend(false); | GPU_blend(false); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| ARegion *ui_searchbox_create_operator(bContext *C, ARegion *butregion, uiBut *but) | ARegion *ui_searchbox_create_operator(bContext *C, ARegion *butregion, uiButSearch *search_but) | ||||
| { | { | ||||
| ARegion *region; | ARegion *region; | ||||
| UI_but_drawflag_enable(but, UI_BUT_HAS_SHORTCUT); | UI_but_drawflag_enable(&search_but->but, UI_BUT_HAS_SHORTCUT); | ||||
| region = ui_searchbox_create_generic(C, butregion, but); | region = ui_searchbox_create_generic(C, butregion, search_but); | ||||
| region->type->draw = ui_searchbox_region_draw_cb__operator; | region->type->draw = ui_searchbox_region_draw_cb__operator; | ||||
| return region; | return region; | ||||
| } | } | ||||
| void ui_searchbox_free(bContext *C, ARegion *region) | void ui_searchbox_free(bContext *C, ARegion *region) | ||||
| { | { | ||||
| ui_region_temp_remove(C, CTX_wm_screen(C), region); | ui_region_temp_remove(C, CTX_wm_screen(C), region); | ||||
| } | } | ||||
| static void ui_searchbox_region_draw_cb__menu(const bContext *UNUSED(C), ARegion *UNUSED(region)) | static void ui_searchbox_region_draw_cb__menu(const bContext *UNUSED(C), ARegion *UNUSED(region)) | ||||
| { | { | ||||
| /* Currently unused. */ | /* Currently unused. */ | ||||
| } | } | ||||
| ARegion *ui_searchbox_create_menu(bContext *C, ARegion *butregion, uiBut *but) | ARegion *ui_searchbox_create_menu(bContext *C, ARegion *butregion, uiButSearch *search_but) | ||||
| { | { | ||||
| ARegion *region; | ARegion *region; | ||||
| UI_but_drawflag_enable(but, UI_BUT_HAS_SHORTCUT); | UI_but_drawflag_enable(&search_but->but, UI_BUT_HAS_SHORTCUT); | ||||
| region = ui_searchbox_create_generic(C, butregion, but); | region = ui_searchbox_create_generic(C, butregion, search_but); | ||||
| if (false) { | if (false) { | ||||
| region->type->draw = ui_searchbox_region_draw_cb__menu; | region->type->draw = ui_searchbox_region_draw_cb__menu; | ||||
| } | } | ||||
| return region; | return region; | ||||
| } | } | ||||
| /* sets red alert if button holds a string it can't find */ | /* sets red alert if button holds a string it can't find */ | ||||
| /* XXX weak: search_func adds all partial matches... */ | /* XXX weak: search_func adds all partial matches... */ | ||||
| void ui_but_search_refresh(uiBut *but) | void ui_but_search_refresh(uiButSearch *search_but) | ||||
| { | { | ||||
| uiBut *but = &search_but->but; | |||||
| uiSearchItems *items; | uiSearchItems *items; | ||||
| int x1; | int x1; | ||||
| /* possibly very large lists (such as ID datablocks) only | /* possibly very large lists (such as ID datablocks) only | ||||
| * only validate string RNA buts (not pointers) */ | * only validate string RNA buts (not pointers) */ | ||||
| if (but->rnaprop && RNA_property_type(but->rnaprop) != PROP_STRING) { | if (but->rnaprop && RNA_property_type(but->rnaprop) != PROP_STRING) { | ||||
| return; | return; | ||||
| } | } | ||||
| items = MEM_callocN(sizeof(uiSearchItems), "search items"); | items = MEM_callocN(sizeof(uiSearchItems), "search items"); | ||||
| /* setup search struct */ | /* setup search struct */ | ||||
| items->maxitem = 10; | items->maxitem = 10; | ||||
| items->maxstrlen = 256; | items->maxstrlen = 256; | ||||
| items->names = MEM_callocN(items->maxitem * sizeof(void *), "search names"); | items->names = MEM_callocN(items->maxitem * sizeof(void *), "search names"); | ||||
| for (x1 = 0; x1 < items->maxitem; x1++) { | for (x1 = 0; x1 < items->maxitem; x1++) { | ||||
| items->names[x1] = MEM_callocN(but->hardmax + 1, "search names"); | items->names[x1] = MEM_callocN(but->hardmax + 1, "search names"); | ||||
| } | } | ||||
| ui_searchbox_update_fn(but->block->evil_C, but, but->drawstr, items); | ui_searchbox_update_fn(but->block->evil_C, search_but, but->drawstr, items); | ||||
| /* only redalert when we are sure of it, this can miss cases when >10 matches */ | /* only redalert when we are sure of it, this can miss cases when >10 matches */ | ||||
| if (items->totitem == 0) { | if (items->totitem == 0) { | ||||
| UI_but_flag_enable(but, UI_BUT_REDALERT); | UI_but_flag_enable(but, UI_BUT_REDALERT); | ||||
| } | } | ||||
| else if (items->more == 0) { | else if (items->more == 0) { | ||||
| if (UI_search_items_find_index(items, but->drawstr) == -1) { | if (UI_search_items_find_index(items, but->drawstr) == -1) { | ||||
| UI_but_flag_enable(but, UI_BUT_REDALERT); | UI_but_flag_enable(but, UI_BUT_REDALERT); | ||||
| Show All 11 Lines | |||||