2022-02-11 09:07:11 +11:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
* Copyright 2008 Blender Foundation. All rights reserved. */
|
2017-11-03 20:26:35 +11:00
|
|
|
|
2019-02-18 08:08:12 +11:00
|
|
|
/** \file
|
|
|
|
* \ingroup edinterface
|
2017-11-03 20:26:35 +11:00
|
|
|
*
|
|
|
|
* Search Box Region & Interaction
|
|
|
|
*/
|
|
|
|
|
2021-11-10 15:49:49 -06:00
|
|
|
#include <cstdarg>
|
|
|
|
#include <cstdlib>
|
|
|
|
#include <cstring>
|
2017-11-03 20:26:35 +11:00
|
|
|
|
2020-04-14 18:46:13 +10:00
|
|
|
#include "DNA_ID.h"
|
2017-11-03 20:26:35 +11:00
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
|
|
|
#include "DNA_userdef_types.h"
|
|
|
|
|
|
|
|
#include "BLI_math.h"
|
|
|
|
|
2020-05-11 18:37:43 +10:00
|
|
|
#include "BLI_listbase.h"
|
2017-11-03 20:26:35 +11:00
|
|
|
#include "BLI_rect.h"
|
2020-03-19 09:33:03 +01:00
|
|
|
#include "BLI_string.h"
|
2017-11-03 20:26:35 +11:00
|
|
|
#include "BLI_utildefines.h"
|
|
|
|
|
|
|
|
#include "BKE_context.h"
|
|
|
|
#include "BKE_screen.h"
|
|
|
|
|
|
|
|
#include "WM_api.h"
|
|
|
|
#include "WM_types.h"
|
|
|
|
|
|
|
|
#include "RNA_access.h"
|
|
|
|
|
|
|
|
#include "UI_interface.h"
|
|
|
|
#include "UI_interface_icons.h"
|
|
|
|
#include "UI_view2d.h"
|
|
|
|
|
|
|
|
#include "BLT_translation.h"
|
|
|
|
|
|
|
|
#include "ED_screen.h"
|
|
|
|
|
2020-03-19 09:33:03 +01:00
|
|
|
#include "GPU_state.h"
|
2017-11-03 20:26:35 +11:00
|
|
|
#include "interface_intern.h"
|
|
|
|
#include "interface_regions_intern.h"
|
|
|
|
|
|
|
|
#define MENU_BORDER (int)(0.3f * U.widget_unit)
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name Search Box Creation
|
|
|
|
* \{ */
|
|
|
|
|
|
|
|
struct uiSearchItems {
|
|
|
|
int maxitem, totitem, maxstrlen;
|
|
|
|
|
|
|
|
int offset, offset_i; /* offset for inserting in array */
|
|
|
|
int more; /* flag indicating there are more items */
|
|
|
|
|
|
|
|
char **names;
|
|
|
|
void **pointers;
|
|
|
|
int *icons;
|
2020-03-24 11:34:18 +11:00
|
|
|
int *states;
|
2020-07-23 17:24:17 +10:00
|
|
|
uint8_t *name_prefix_offsets;
|
2017-11-03 20:26:35 +11:00
|
|
|
|
2020-06-29 17:19:43 +02:00
|
|
|
/** Is there any item with an icon? */
|
|
|
|
bool has_icon;
|
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
AutoComplete *autocpl;
|
|
|
|
void *active;
|
|
|
|
};
|
|
|
|
|
2021-11-10 15:49:49 -06:00
|
|
|
struct uiSearchboxData {
|
2017-11-03 20:26:35 +11:00
|
|
|
rcti bbox;
|
|
|
|
uiFontStyle fstyle;
|
|
|
|
uiSearchItems items;
|
2019-01-15 23:24:20 +11:00
|
|
|
/** index in items array */
|
|
|
|
int active;
|
|
|
|
/** when menu opened with enough space for this */
|
|
|
|
bool noback;
|
|
|
|
/** draw thumbnail previews, rather than list */
|
|
|
|
bool preview;
|
2020-04-14 18:46:13 +10:00
|
|
|
/** Use the #UI_SEP_CHAR char for splitting shortcuts (good for operators, bad for data). */
|
2021-06-15 18:53:32 +02:00
|
|
|
bool use_shortcut_sep;
|
2017-11-03 20:26:35 +11:00
|
|
|
int prv_rows, prv_cols;
|
2020-04-14 18:46:13 +10:00
|
|
|
/**
|
|
|
|
* Show the active icon and text after the last instance of this string.
|
|
|
|
* Used so we can show leading text to menu items less prominently (not related to 'use_sep').
|
|
|
|
*/
|
|
|
|
const char *sep_string;
|
2021-11-10 15:49:49 -06:00
|
|
|
};
|
2017-11-03 20:26:35 +11:00
|
|
|
|
|
|
|
#define SEARCH_ITEMS 10
|
|
|
|
|
2020-07-23 17:24:17 +10:00
|
|
|
bool UI_search_item_add(uiSearchItems *items,
|
|
|
|
const char *name,
|
|
|
|
void *poin,
|
|
|
|
int iconid,
|
|
|
|
int state,
|
|
|
|
const uint8_t name_prefix_offset)
|
2017-11-03 20:26:35 +11:00
|
|
|
{
|
|
|
|
/* hijack for autocomplete */
|
|
|
|
if (items->autocpl) {
|
2021-12-09 11:09:50 +01:00
|
|
|
UI_autocomplete_update_name(items->autocpl, name + name_prefix_offset);
|
2017-11-03 20:26:35 +11:00
|
|
|
return true;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-06-29 17:19:43 +02:00
|
|
|
if (iconid) {
|
|
|
|
items->has_icon = true;
|
|
|
|
}
|
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
/* hijack for finding active item */
|
|
|
|
if (items->active) {
|
2019-03-25 10:15:20 +11:00
|
|
|
if (poin == items->active) {
|
2017-11-03 20:26:35 +11:00
|
|
|
items->offset_i = items->totitem;
|
2019-03-25 10:15:20 +11:00
|
|
|
}
|
2017-11-03 20:26:35 +11:00
|
|
|
items->totitem++;
|
|
|
|
return true;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
if (items->totitem >= items->maxitem) {
|
|
|
|
items->more = 1;
|
|
|
|
return false;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
/* skip first items in list */
|
|
|
|
if (items->offset_i > 0) {
|
|
|
|
items->offset_i--;
|
|
|
|
return true;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-03-25 10:15:20 +11:00
|
|
|
if (items->names) {
|
2017-11-03 20:26:35 +11:00
|
|
|
BLI_strncpy(items->names[items->totitem], name, items->maxstrlen);
|
2019-03-25 10:15:20 +11:00
|
|
|
}
|
|
|
|
if (items->pointers) {
|
2017-11-03 20:26:35 +11:00
|
|
|
items->pointers[items->totitem] = poin;
|
2019-03-25 10:15:20 +11:00
|
|
|
}
|
|
|
|
if (items->icons) {
|
2017-11-03 20:26:35 +11:00
|
|
|
items->icons[items->totitem] = iconid;
|
2019-03-25 10:15:20 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-07-23 17:24:17 +10:00
|
|
|
if (name_prefix_offset != 0) {
|
|
|
|
/* Lazy initialize, as this isn't used often. */
|
2021-11-10 15:49:49 -06:00
|
|
|
if (items->name_prefix_offsets == nullptr) {
|
|
|
|
items->name_prefix_offsets = (uint8_t *)MEM_callocN(
|
|
|
|
items->maxitem * sizeof(*items->name_prefix_offsets), __func__);
|
2020-07-23 17:24:17 +10:00
|
|
|
}
|
|
|
|
items->name_prefix_offsets[items->totitem] = name_prefix_offset;
|
|
|
|
}
|
|
|
|
|
2020-03-24 11:34:18 +11:00
|
|
|
/* Limit flags that can be set so flags such as 'UI_SELECT' aren't accidentally set
|
|
|
|
* which will cause problems, add others as needed. */
|
2020-05-07 23:16:22 +10:00
|
|
|
BLI_assert(
|
|
|
|
(state & ~(UI_BUT_DISABLED | UI_BUT_INACTIVE | UI_BUT_REDALERT | UI_BUT_HAS_SEP_CHAR)) == 0);
|
2020-03-24 11:34:18 +11:00
|
|
|
if (items->states) {
|
|
|
|
items->states[items->totitem] = state;
|
|
|
|
}
|
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
items->totitem++;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-12-08 00:31:20 -05:00
|
|
|
int UI_searchbox_size_y()
|
2017-11-03 20:26:35 +11:00
|
|
|
{
|
|
|
|
return SEARCH_ITEMS * UI_UNIT_Y + 2 * UI_POPUP_MENU_TOP;
|
|
|
|
}
|
|
|
|
|
2021-12-08 00:31:20 -05:00
|
|
|
int UI_searchbox_size_x()
|
2017-11-03 20:26:35 +11:00
|
|
|
{
|
|
|
|
return 12 * UI_UNIT_X;
|
|
|
|
}
|
|
|
|
|
|
|
|
int UI_search_items_find_index(uiSearchItems *items, const char *name)
|
|
|
|
{
|
2021-11-10 15:49:49 -06:00
|
|
|
if (items->name_prefix_offsets != nullptr) {
|
2020-07-23 17:24:17 +10:00
|
|
|
for (int i = 0; i < items->totitem; i++) {
|
|
|
|
if (STREQ(name, items->names[i] + items->name_prefix_offsets[i])) {
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
for (int i = 0; i < items->totitem; i++) {
|
|
|
|
if (STREQ(name, items->names[i])) {
|
|
|
|
return i;
|
|
|
|
}
|
2017-11-03 20:26:35 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-03-06 16:56:42 +01:00
|
|
|
/* region is the search box itself */
|
|
|
|
static void ui_searchbox_select(bContext *C, ARegion *region, uiBut *but, int step)
|
2017-11-03 20:26:35 +11:00
|
|
|
{
|
2021-11-10 15:49:49 -06:00
|
|
|
uiSearchboxData *data = static_cast<uiSearchboxData *>(region->regiondata);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
/* apply step */
|
|
|
|
data->active += step;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
if (data->items.totitem == 0) {
|
|
|
|
data->active = -1;
|
|
|
|
}
|
|
|
|
else if (data->active >= data->items.totitem) {
|
|
|
|
if (data->items.more) {
|
|
|
|
data->items.offset++;
|
|
|
|
data->active = data->items.totitem - 1;
|
2020-03-06 16:56:42 +01:00
|
|
|
ui_searchbox_update(C, region, but, false);
|
2017-11-03 20:26:35 +11:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
data->active = data->items.totitem - 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (data->active < 0) {
|
|
|
|
if (data->items.offset) {
|
|
|
|
data->items.offset--;
|
|
|
|
data->active = 0;
|
2020-03-06 16:56:42 +01:00
|
|
|
ui_searchbox_update(C, region, but, false);
|
2017-11-03 20:26:35 +11:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* only let users step into an 'unset' state for unlink buttons */
|
|
|
|
data->active = (but->flag & UI_BUT_VALUE_CLEAR) ? -1 : 0;
|
|
|
|
}
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-03-06 16:56:42 +01:00
|
|
|
ED_region_tag_redraw(region);
|
2017-11-03 20:26:35 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ui_searchbox_butrect(rcti *r_rect, uiSearchboxData *data, int itemnr)
|
|
|
|
{
|
|
|
|
/* thumbnail preview */
|
|
|
|
if (data->preview) {
|
2020-08-26 10:11:13 +10:00
|
|
|
const int butw = (BLI_rcti_size_x(&data->bbox) - 2 * MENU_BORDER) / data->prv_cols;
|
|
|
|
const int buth = (BLI_rcti_size_y(&data->bbox) - 2 * MENU_BORDER) / data->prv_rows;
|
2017-11-03 20:26:35 +11:00
|
|
|
int row, col;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
*r_rect = data->bbox;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
col = itemnr % data->prv_cols;
|
|
|
|
row = itemnr / data->prv_cols;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
r_rect->xmin += MENU_BORDER + (col * butw);
|
|
|
|
r_rect->xmax = r_rect->xmin + butw;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
r_rect->ymax -= MENU_BORDER + (row * buth);
|
|
|
|
r_rect->ymin = r_rect->ymax - buth;
|
|
|
|
}
|
|
|
|
/* list view */
|
|
|
|
else {
|
2020-08-26 10:11:13 +10:00
|
|
|
const int buth = (BLI_rcti_size_y(&data->bbox) - 2 * UI_POPUP_MENU_TOP) / SEARCH_ITEMS;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
*r_rect = data->bbox;
|
|
|
|
r_rect->xmin = data->bbox.xmin + 3.0f;
|
|
|
|
r_rect->xmax = data->bbox.xmax - 3.0f;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
r_rect->ymax = data->bbox.ymax - UI_POPUP_MENU_TOP - itemnr * buth;
|
|
|
|
r_rect->ymin = r_rect->ymax - buth;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-06 16:56:42 +01:00
|
|
|
int ui_searchbox_find_index(ARegion *region, const char *name)
|
2017-11-03 20:26:35 +11:00
|
|
|
{
|
2021-11-10 15:49:49 -06:00
|
|
|
uiSearchboxData *data = static_cast<uiSearchboxData *>(region->regiondata);
|
2017-11-03 20:26:35 +11:00
|
|
|
return UI_search_items_find_index(&data->items, name);
|
|
|
|
}
|
|
|
|
|
2021-10-20 20:49:02 -03:00
|
|
|
bool ui_searchbox_inside(ARegion *region, const int xy[2])
|
2017-11-03 20:26:35 +11:00
|
|
|
{
|
2021-11-10 15:49:49 -06:00
|
|
|
uiSearchboxData *data = static_cast<uiSearchboxData *>(region->regiondata);
|
2017-11-03 20:26:35 +11:00
|
|
|
|
2021-10-20 20:49:02 -03:00
|
|
|
return BLI_rcti_isect_pt(&data->bbox, xy[0] - region->winrct.xmin, xy[1] - region->winrct.ymin);
|
2017-11-03 20:26:35 +11:00
|
|
|
}
|
|
|
|
|
2020-03-06 16:56:42 +01:00
|
|
|
bool ui_searchbox_apply(uiBut *but, ARegion *region)
|
2017-11-03 20:26:35 +11:00
|
|
|
{
|
2021-11-10 15:49:49 -06:00
|
|
|
uiSearchboxData *data = static_cast<uiSearchboxData *>(region->regiondata);
|
2020-08-07 14:34:11 +02:00
|
|
|
uiButSearch *search_but = (uiButSearch *)but;
|
2017-11-03 20:26:35 +11:00
|
|
|
|
2020-08-07 14:34:11 +02:00
|
|
|
BLI_assert(but->type == UI_BTYPE_SEARCH_MENU);
|
|
|
|
|
2021-11-10 15:49:49 -06:00
|
|
|
search_but->item_active = nullptr;
|
2017-11-03 20:26:35 +11:00
|
|
|
|
|
|
|
if (data->active != -1) {
|
2020-07-23 17:24:17 +10:00
|
|
|
const char *name = data->items.names[data->active] +
|
|
|
|
/* Never include the prefix in the button. */
|
|
|
|
(data->items.name_prefix_offsets ?
|
|
|
|
data->items.name_prefix_offsets[data->active] :
|
|
|
|
0);
|
|
|
|
|
2021-11-10 15:49:49 -06:00
|
|
|
const char *name_sep = data->use_shortcut_sep ? strrchr(name, UI_SEP_CHAR) : nullptr;
|
2017-11-03 20:26:35 +11:00
|
|
|
|
2021-10-21 13:54:48 -05:00
|
|
|
/* Search button with dynamic string properties may have their own method of applying
|
|
|
|
* the search results, so only copy the result if there is a proper space for it. */
|
|
|
|
if (but->hardmax != 0) {
|
|
|
|
BLI_strncpy(but->editstr, name, name_sep ? (name_sep - name) + 1 : data->items.maxstrlen);
|
|
|
|
}
|
2017-11-03 20:26:35 +11:00
|
|
|
|
2020-08-07 14:34:11 +02:00
|
|
|
search_but->item_active = data->items.pointers[data->active];
|
2017-11-03 20:26:35 +11:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2020-07-03 14:20:10 +02:00
|
|
|
return false;
|
2017-11-03 20:26:35 +11:00
|
|
|
}
|
|
|
|
|
2020-05-11 18:37:43 +10:00
|
|
|
static struct ARegion *wm_searchbox_tooltip_init(struct bContext *C,
|
|
|
|
struct ARegion *region,
|
|
|
|
int *UNUSED(r_pass),
|
|
|
|
double *UNUSED(pass_delay),
|
|
|
|
bool *r_exit_on_event)
|
|
|
|
{
|
|
|
|
*r_exit_on_event = true;
|
|
|
|
|
|
|
|
LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) {
|
|
|
|
LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
|
2020-08-07 14:34:11 +02:00
|
|
|
if (but->type != UI_BTYPE_SEARCH_MENU) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
uiButSearch *search_but = (uiButSearch *)but;
|
2021-01-26 22:06:45 +01:00
|
|
|
if (!search_but->item_tooltip_fn) {
|
|
|
|
continue;
|
2020-05-11 18:37:43 +10:00
|
|
|
}
|
2021-01-26 22:06:45 +01:00
|
|
|
|
|
|
|
ARegion *searchbox_region = UI_region_searchbox_region_get(region);
|
2021-11-10 15:49:49 -06:00
|
|
|
uiSearchboxData *data = static_cast<uiSearchboxData *>(searchbox_region->regiondata);
|
2021-01-26 22:06:45 +01:00
|
|
|
|
|
|
|
BLI_assert(data->items.pointers[data->active] == search_but->item_active);
|
|
|
|
|
|
|
|
rcti rect;
|
|
|
|
ui_searchbox_butrect(&rect, data, data->active);
|
|
|
|
|
|
|
|
return search_but->item_tooltip_fn(
|
|
|
|
C, region, &rect, search_but->arg, search_but->item_active);
|
2020-05-11 18:37:43 +10:00
|
|
|
}
|
|
|
|
}
|
2021-11-10 15:49:49 -06:00
|
|
|
return nullptr;
|
2020-05-11 18:37:43 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ui_searchbox_event(
|
|
|
|
bContext *C, ARegion *region, uiBut *but, ARegion *butregion, const wmEvent *event)
|
2017-11-03 20:26:35 +11:00
|
|
|
{
|
2021-11-10 15:49:49 -06:00
|
|
|
uiSearchboxData *data = static_cast<uiSearchboxData *>(region->regiondata);
|
2020-08-07 14:34:11 +02:00
|
|
|
uiButSearch *search_but = (uiButSearch *)but;
|
2017-11-03 20:26:35 +11:00
|
|
|
int type = event->type, val = event->val;
|
2020-05-07 23:16:22 +10:00
|
|
|
bool handled = false;
|
2020-05-11 18:37:43 +10:00
|
|
|
bool tooltip_timer_started = false;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-08-07 14:34:11 +02:00
|
|
|
BLI_assert(but->type == UI_BTYPE_SEARCH_MENU);
|
|
|
|
|
2019-03-25 10:15:20 +11:00
|
|
|
if (type == MOUSEPAN) {
|
2017-11-03 20:26:35 +11:00
|
|
|
ui_pan_to_scroll(event, &type, &val);
|
2019-03-25 10:15:20 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
switch (type) {
|
|
|
|
case WHEELUPMOUSE:
|
2020-03-18 10:38:37 -06:00
|
|
|
case EVT_UPARROWKEY:
|
2020-03-06 16:56:42 +01:00
|
|
|
ui_searchbox_select(C, region, but, -1);
|
2020-05-07 23:16:22 +10:00
|
|
|
handled = true;
|
2017-11-03 20:26:35 +11:00
|
|
|
break;
|
|
|
|
case WHEELDOWNMOUSE:
|
2020-03-18 10:38:37 -06:00
|
|
|
case EVT_DOWNARROWKEY:
|
2020-03-06 16:56:42 +01:00
|
|
|
ui_searchbox_select(C, region, but, 1);
|
2020-05-07 23:16:22 +10:00
|
|
|
handled = true;
|
|
|
|
break;
|
|
|
|
case RIGHTMOUSE:
|
|
|
|
if (val) {
|
2020-08-07 14:34:11 +02:00
|
|
|
if (search_but->item_context_menu_fn) {
|
2020-05-07 23:16:22 +10:00
|
|
|
if (data->active != -1) {
|
|
|
|
/* Check the cursor is over the active element
|
|
|
|
* (a little confusing if this isn't the case, although it does work). */
|
|
|
|
rcti rect;
|
|
|
|
ui_searchbox_butrect(&rect, data, data->active);
|
2021-10-20 23:45:30 +11:00
|
|
|
if (BLI_rcti_isect_pt(&rect,
|
|
|
|
event->xy[0] - region->winrct.xmin,
|
|
|
|
event->xy[1] - region->winrct.ymin)) {
|
2020-05-07 23:16:22 +10:00
|
|
|
|
|
|
|
void *active = data->items.pointers[data->active];
|
2020-08-07 14:34:11 +02:00
|
|
|
if (search_but->item_context_menu_fn(C, search_but->arg, active, event)) {
|
2020-05-07 23:16:22 +10:00
|
|
|
handled = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-11-03 20:26:35 +11:00
|
|
|
break;
|
2020-05-11 18:37:43 +10:00
|
|
|
case MOUSEMOVE: {
|
|
|
|
bool is_inside = false;
|
|
|
|
|
2021-10-20 23:45:30 +11:00
|
|
|
if (BLI_rcti_isect_pt(®ion->winrct, event->xy[0], event->xy[1])) {
|
2017-11-03 20:26:35 +11:00
|
|
|
rcti rect;
|
|
|
|
int a;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
for (a = 0; a < data->items.totitem; a++) {
|
|
|
|
ui_searchbox_butrect(&rect, data, a);
|
2020-03-06 16:56:42 +01:00
|
|
|
if (BLI_rcti_isect_pt(
|
2021-10-20 23:45:30 +11:00
|
|
|
&rect, event->xy[0] - region->winrct.xmin, event->xy[1] - region->winrct.ymin)) {
|
2020-05-11 18:37:43 +10:00
|
|
|
is_inside = true;
|
2017-11-03 20:26:35 +11:00
|
|
|
if (data->active != a) {
|
|
|
|
data->active = a;
|
2020-03-06 16:56:42 +01:00
|
|
|
ui_searchbox_select(C, region, but, 0);
|
2020-05-07 23:16:22 +10:00
|
|
|
handled = true;
|
2017-11-03 20:26:35 +11:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-05-11 18:37:43 +10:00
|
|
|
|
|
|
|
if (U.flag & USER_TOOLTIPS) {
|
|
|
|
if (is_inside) {
|
|
|
|
if (data->active != -1) {
|
|
|
|
ScrArea *area = CTX_wm_area(C);
|
2020-08-07 14:34:11 +02:00
|
|
|
search_but->item_active = data->items.pointers[data->active];
|
2020-05-11 18:37:43 +10:00
|
|
|
WM_tooltip_timer_init(C, CTX_wm_window(C), area, butregion, wm_searchbox_tooltip_init);
|
|
|
|
tooltip_timer_started = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
break;
|
2020-05-11 18:37:43 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (handled && (tooltip_timer_started == false)) {
|
|
|
|
wmWindow *win = CTX_wm_window(C);
|
|
|
|
WM_tooltip_clear(C, win);
|
2017-11-03 20:26:35 +11:00
|
|
|
}
|
2020-05-11 18:37:43 +10:00
|
|
|
|
2020-05-07 23:16:22 +10:00
|
|
|
return handled;
|
2017-11-03 20:26:35 +11:00
|
|
|
}
|
|
|
|
|
2020-05-11 18:37:43 +10:00
|
|
|
/** Wrap #uiButSearchUpdateFn callback. */
|
2020-08-07 14:34:11 +02:00
|
|
|
static void ui_searchbox_update_fn(bContext *C,
|
|
|
|
uiButSearch *search_but,
|
|
|
|
const char *str,
|
|
|
|
uiSearchItems *items)
|
2020-05-11 18:37:43 +10:00
|
|
|
{
|
2021-02-02 13:41:32 +01:00
|
|
|
/* While the button is in text editing mode (searchbox open), remove tooltips on every update. */
|
|
|
|
if (search_but->but.editstr) {
|
|
|
|
wmWindow *win = CTX_wm_window(C);
|
|
|
|
WM_tooltip_clear(C, win);
|
|
|
|
}
|
UI: Expose an "is first search" boolean to search button callbacks
Currently when you open an RNA collection search button, like a
vertex group selector, the search filter isn't applied until you
start typing, in order to display every option at the start.
Otherwise they wouldn't be visible, since the search filter would
run for the current text.
Currently this check happens in one place, but it relies on the
`changed` value of `uiBut`. This is fine in the interface directory,
but anywhere else it would require exposing `uiBut.changed`, which
is probably too low-level to expose.
The solution is adding an `is_first` argument to the search callbacks,
which is nice for a few reasons:
- They work at a higher level of abstraction, meaning they don't
have to worry about how exactly to tell if this is the first
search.
- It makes it easier to do special behavior when the search menu
is first opened.
- Then, obviously, it makes that state accessible without including
`interface_intern.h`.
Needed for attribute search: T85658
Differential Revision: https://developer.blender.org/D10528
2021-03-02 11:42:05 -06:00
|
|
|
const bool is_first_search = !search_but->but.changed;
|
|
|
|
search_but->items_update_fn(C, search_but->arg, str, items, is_first_search);
|
2020-05-11 18:37:43 +10:00
|
|
|
}
|
|
|
|
|
2020-03-06 16:56:42 +01:00
|
|
|
void ui_searchbox_update(bContext *C, ARegion *region, uiBut *but, const bool reset)
|
2017-11-03 20:26:35 +11:00
|
|
|
{
|
2020-08-07 14:34:11 +02:00
|
|
|
uiButSearch *search_but = (uiButSearch *)but;
|
2021-11-10 15:49:49 -06:00
|
|
|
uiSearchboxData *data = static_cast<uiSearchboxData *>(region->regiondata);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-08-07 14:34:11 +02:00
|
|
|
BLI_assert(but->type == UI_BTYPE_SEARCH_MENU);
|
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
/* reset vars */
|
|
|
|
data->items.totitem = 0;
|
|
|
|
data->items.more = 0;
|
|
|
|
if (reset == false) {
|
|
|
|
data->items.offset_i = data->items.offset;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
data->items.offset_i = data->items.offset = 0;
|
|
|
|
data->active = -1;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
/* handle active */
|
2020-08-07 14:34:11 +02:00
|
|
|
if (search_but->items_update_fn && search_but->item_active) {
|
|
|
|
data->items.active = search_but->item_active;
|
|
|
|
ui_searchbox_update_fn(C, search_but, but->editstr, &data->items);
|
2021-11-10 15:49:49 -06:00
|
|
|
data->items.active = nullptr;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
/* found active item, calculate real offset by centering it */
|
|
|
|
if (data->items.totitem) {
|
|
|
|
/* first case, begin of list */
|
|
|
|
if (data->items.offset_i < data->items.maxitem) {
|
|
|
|
data->active = data->items.offset_i;
|
|
|
|
data->items.offset_i = 0;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* second case, end of list */
|
|
|
|
if (data->items.totitem - data->items.offset_i <= data->items.maxitem) {
|
|
|
|
data->active = data->items.offset_i - data->items.totitem + data->items.maxitem;
|
|
|
|
data->items.offset_i = data->items.totitem - data->items.maxitem;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* center active item */
|
|
|
|
data->items.offset_i -= data->items.maxitem / 2;
|
|
|
|
data->active = data->items.maxitem / 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
data->items.offset = data->items.offset_i;
|
|
|
|
data->items.totitem = 0;
|
|
|
|
}
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
/* callback */
|
2020-08-07 14:34:11 +02:00
|
|
|
if (search_but->items_update_fn) {
|
|
|
|
ui_searchbox_update_fn(C, search_but, but->editstr, &data->items);
|
2019-03-25 10:15:20 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
/* handle case where editstr is equal to one of items */
|
|
|
|
if (reset && data->active == -1) {
|
2021-11-05 17:17:37 -05:00
|
|
|
for (int a = 0; a < data->items.totitem; a++) {
|
2020-07-23 17:24:17 +10:00
|
|
|
const char *name = data->items.names[a] +
|
|
|
|
/* Never include the prefix in the button. */
|
|
|
|
(data->items.name_prefix_offsets ? data->items.name_prefix_offsets[a] :
|
|
|
|
0);
|
2021-11-10 15:49:49 -06:00
|
|
|
const char *name_sep = data->use_shortcut_sep ? strrchr(name, UI_SEP_CHAR) : nullptr;
|
2017-11-03 20:26:35 +11:00
|
|
|
if (STREQLEN(but->editstr, name, name_sep ? (name_sep - name) : data->items.maxstrlen)) {
|
|
|
|
data->active = a;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2019-03-25 10:15:20 +11:00
|
|
|
if (data->items.totitem == 1 && but->editstr[0]) {
|
2017-11-03 20:26:35 +11:00
|
|
|
data->active = 0;
|
2019-03-25 10:15:20 +11:00
|
|
|
}
|
2017-11-03 20:26:35 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
/* validate selected item */
|
2020-03-06 16:56:42 +01:00
|
|
|
ui_searchbox_select(C, region, but, 0);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-03-06 16:56:42 +01:00
|
|
|
ED_region_tag_redraw(region);
|
2017-11-03 20:26:35 +11:00
|
|
|
}
|
|
|
|
|
2020-03-06 16:56:42 +01:00
|
|
|
int ui_searchbox_autocomplete(bContext *C, ARegion *region, uiBut *but, char *str)
|
2017-11-03 20:26:35 +11:00
|
|
|
{
|
2020-08-07 14:34:11 +02:00
|
|
|
uiButSearch *search_but = (uiButSearch *)but;
|
2021-11-10 15:49:49 -06:00
|
|
|
uiSearchboxData *data = static_cast<uiSearchboxData *>(region->regiondata);
|
2017-11-03 20:26:35 +11:00
|
|
|
int match = AUTOCOMPLETE_NO_MATCH;
|
|
|
|
|
2020-08-07 14:34:11 +02:00
|
|
|
BLI_assert(but->type == UI_BTYPE_SEARCH_MENU);
|
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
if (str[0]) {
|
|
|
|
data->items.autocpl = UI_autocomplete_begin(str, ui_but_string_get_max_length(but));
|
|
|
|
|
2020-08-07 14:34:11 +02:00
|
|
|
ui_searchbox_update_fn(C, search_but, but->editstr, &data->items);
|
2017-11-03 20:26:35 +11:00
|
|
|
|
|
|
|
match = UI_autocomplete_end(data->items.autocpl, str);
|
2021-11-10 15:49:49 -06:00
|
|
|
data->items.autocpl = nullptr;
|
2017-11-03 20:26:35 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
return match;
|
|
|
|
}
|
|
|
|
|
2021-11-05 17:17:37 -05:00
|
|
|
static void ui_searchbox_region_draw_fn(const bContext *C, ARegion *region)
|
2017-11-03 20:26:35 +11:00
|
|
|
{
|
2021-11-10 15:49:49 -06:00
|
|
|
uiSearchboxData *data = static_cast<uiSearchboxData *>(region->regiondata);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
/* pixel space */
|
2020-03-06 16:56:42 +01:00
|
|
|
wmOrtho2_region_pixelspace(region);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-06-12 08:14:13 +02:00
|
|
|
if (data->noback == false) {
|
2019-03-22 17:56:58 +11:00
|
|
|
ui_draw_widget_menu_back(&data->bbox, true);
|
2018-06-12 08:14:13 +02:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
/* draw text */
|
|
|
|
if (data->items.totitem) {
|
|
|
|
rcti rect;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
if (data->preview) {
|
|
|
|
/* draw items */
|
2021-11-05 17:17:37 -05:00
|
|
|
for (int a = 0; a < data->items.totitem; a++) {
|
2020-03-24 11:34:18 +11:00
|
|
|
const int state = ((a == data->active) ? UI_ACTIVE : 0) | data->items.states[a];
|
|
|
|
|
2017-11-03 21:22:27 +11:00
|
|
|
/* ensure icon is up-to-date */
|
|
|
|
ui_icon_ensure_deferred(C, data->items.icons[a], data->preview);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
ui_searchbox_butrect(&rect, data, a);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
/* widget itself */
|
2021-07-13 17:30:34 +02:00
|
|
|
ui_draw_preview_item(&data->fstyle,
|
|
|
|
&rect,
|
|
|
|
data->items.names[a],
|
|
|
|
data->items.icons[a],
|
|
|
|
state,
|
|
|
|
UI_STYLE_TEXT_LEFT);
|
2017-11-03 20:26:35 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
/* indicate more */
|
|
|
|
if (data->items.more) {
|
|
|
|
ui_searchbox_butrect(&rect, data, data->items.maxitem - 1);
|
2020-08-16 15:38:34 +02:00
|
|
|
GPU_blend(GPU_BLEND_ALPHA);
|
2017-11-03 20:26:35 +11:00
|
|
|
UI_icon_draw(rect.xmax - 18, rect.ymin - 7, ICON_TRIA_DOWN);
|
2020-08-16 15:38:34 +02:00
|
|
|
GPU_blend(GPU_BLEND_NONE);
|
2017-11-03 20:26:35 +11:00
|
|
|
}
|
|
|
|
if (data->items.offset) {
|
|
|
|
ui_searchbox_butrect(&rect, data, 0);
|
2020-08-16 15:38:34 +02:00
|
|
|
GPU_blend(GPU_BLEND_ALPHA);
|
2017-11-03 20:26:35 +11:00
|
|
|
UI_icon_draw(rect.xmin, rect.ymax - 9, ICON_TRIA_UP);
|
2020-08-16 15:38:34 +02:00
|
|
|
GPU_blend(GPU_BLEND_NONE);
|
2017-11-03 20:26:35 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2020-04-14 18:46:13 +10:00
|
|
|
const int search_sep_len = data->sep_string ? strlen(data->sep_string) : 0;
|
2017-11-03 20:26:35 +11:00
|
|
|
/* draw items */
|
2021-11-05 17:17:37 -05:00
|
|
|
for (int a = 0; a < data->items.totitem; a++) {
|
2020-03-24 11:34:18 +11:00
|
|
|
const int state = ((a == data->active) ? UI_ACTIVE : 0) | data->items.states[a];
|
2020-04-14 18:46:13 +10:00
|
|
|
char *name = data->items.names[a];
|
|
|
|
int icon = data->items.icons[a];
|
2021-11-10 15:49:49 -06:00
|
|
|
char *name_sep_test = nullptr;
|
2021-01-25 22:54:00 +01:00
|
|
|
|
|
|
|
uiMenuItemSeparatorType separator_type = UI_MENU_ITEM_SEPARATOR_NONE;
|
2021-06-15 18:53:32 +02:00
|
|
|
if (data->use_shortcut_sep) {
|
2021-01-25 22:54:00 +01:00
|
|
|
separator_type = UI_MENU_ITEM_SEPARATOR_SHORTCUT;
|
|
|
|
}
|
|
|
|
/* Only set for displaying additional hint (e.g. library name of a linked data-block). */
|
|
|
|
else if (state & UI_BUT_HAS_SEP_CHAR) {
|
|
|
|
separator_type = UI_MENU_ITEM_SEPARATOR_HINT;
|
|
|
|
}
|
2020-03-24 11:34:18 +11:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
ui_searchbox_butrect(&rect, data, a);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
/* widget itself */
|
2020-04-14 18:46:13 +10:00
|
|
|
if ((search_sep_len == 0) ||
|
|
|
|
!(name_sep_test = strstr(data->items.names[a], data->sep_string))) {
|
2020-06-29 17:19:43 +02:00
|
|
|
if (!icon && data->items.has_icon) {
|
|
|
|
/* If there is any icon item, make sure all items line up. */
|
|
|
|
icon = ICON_BLANK1;
|
|
|
|
}
|
2020-04-14 18:46:13 +10:00
|
|
|
|
|
|
|
/* Simple menu item. */
|
2021-11-10 15:49:49 -06:00
|
|
|
ui_draw_menu_item(&data->fstyle, &rect, name, icon, state, separator_type, nullptr);
|
2020-04-14 18:46:13 +10:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* Split menu item, faded text before the separator. */
|
2021-11-10 15:49:49 -06:00
|
|
|
char *name_sep = nullptr;
|
2020-04-14 18:46:13 +10:00
|
|
|
do {
|
|
|
|
name_sep = name_sep_test;
|
|
|
|
name_sep_test = strstr(name_sep + search_sep_len, data->sep_string);
|
2021-11-10 15:49:49 -06:00
|
|
|
} while (name_sep_test != nullptr);
|
2020-04-14 18:46:13 +10:00
|
|
|
|
|
|
|
name_sep += search_sep_len;
|
|
|
|
const char name_sep_prev = *name_sep;
|
|
|
|
*name_sep = '\0';
|
|
|
|
int name_width = 0;
|
2021-01-25 22:54:00 +01:00
|
|
|
ui_draw_menu_item(&data->fstyle,
|
|
|
|
&rect,
|
|
|
|
name,
|
|
|
|
0,
|
|
|
|
state | UI_BUT_INACTIVE,
|
|
|
|
UI_MENU_ITEM_SEPARATOR_NONE,
|
|
|
|
&name_width);
|
2020-04-14 18:46:13 +10:00
|
|
|
*name_sep = name_sep_prev;
|
|
|
|
rect.xmin += name_width;
|
|
|
|
rect.xmin += UI_UNIT_X / 4;
|
|
|
|
|
|
|
|
if (icon == ICON_BLANK1) {
|
|
|
|
icon = ICON_NONE;
|
|
|
|
rect.xmin -= UI_DPI_ICON_SIZE / 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The previous menu item draws the active selection. */
|
2021-11-10 15:49:49 -06:00
|
|
|
ui_draw_menu_item(&data->fstyle, &rect, name_sep, icon, state, separator_type, nullptr);
|
2020-04-14 18:46:13 +10:00
|
|
|
}
|
2017-11-03 20:26:35 +11:00
|
|
|
}
|
|
|
|
/* indicate more */
|
|
|
|
if (data->items.more) {
|
|
|
|
ui_searchbox_butrect(&rect, data, data->items.maxitem - 1);
|
2020-08-16 15:38:34 +02:00
|
|
|
GPU_blend(GPU_BLEND_ALPHA);
|
2021-07-16 11:45:52 +10:00
|
|
|
UI_icon_draw(BLI_rcti_size_x(&rect) / 2, rect.ymin - 9, ICON_TRIA_DOWN);
|
2020-08-16 15:38:34 +02:00
|
|
|
GPU_blend(GPU_BLEND_NONE);
|
2017-11-03 20:26:35 +11:00
|
|
|
}
|
|
|
|
if (data->items.offset) {
|
|
|
|
ui_searchbox_butrect(&rect, data, 0);
|
2020-08-16 15:38:34 +02:00
|
|
|
GPU_blend(GPU_BLEND_ALPHA);
|
2021-07-16 11:45:52 +10:00
|
|
|
UI_icon_draw(BLI_rcti_size_x(&rect) / 2, rect.ymax - 7, ICON_TRIA_UP);
|
2020-08-16 15:38:34 +02:00
|
|
|
GPU_blend(GPU_BLEND_NONE);
|
2017-11-03 20:26:35 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-05 17:17:37 -05:00
|
|
|
static void ui_searchbox_region_free_fn(ARegion *region)
|
2017-11-03 20:26:35 +11:00
|
|
|
{
|
2021-11-10 15:49:49 -06:00
|
|
|
uiSearchboxData *data = static_cast<uiSearchboxData *>(region->regiondata);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
/* free search data */
|
2021-11-05 17:17:37 -05:00
|
|
|
for (int a = 0; a < data->items.maxitem; a++) {
|
2017-11-03 20:26:35 +11:00
|
|
|
MEM_freeN(data->items.names[a]);
|
|
|
|
}
|
|
|
|
MEM_freeN(data->items.names);
|
|
|
|
MEM_freeN(data->items.pointers);
|
|
|
|
MEM_freeN(data->items.icons);
|
2020-03-24 11:34:18 +11:00
|
|
|
MEM_freeN(data->items.states);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-11-10 15:49:49 -06:00
|
|
|
if (data->items.name_prefix_offsets != nullptr) {
|
2020-07-23 17:24:17 +10:00
|
|
|
MEM_freeN(data->items.name_prefix_offsets);
|
|
|
|
}
|
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
MEM_freeN(data);
|
2021-11-10 15:49:49 -06:00
|
|
|
region->regiondata = nullptr;
|
2017-11-03 20:26:35 +11:00
|
|
|
}
|
|
|
|
|
2021-06-15 18:53:32 +02:00
|
|
|
static ARegion *ui_searchbox_create_generic_ex(bContext *C,
|
|
|
|
ARegion *butregion,
|
|
|
|
uiButSearch *search_but,
|
|
|
|
const bool use_shortcut_sep)
|
2017-11-03 20:26:35 +11:00
|
|
|
{
|
|
|
|
wmWindow *win = CTX_wm_window(C);
|
2020-03-15 17:32:25 +11:00
|
|
|
const uiStyle *style = UI_style_get();
|
2020-08-07 14:34:11 +02:00
|
|
|
uiBut *but = &search_but->but;
|
2020-08-26 10:11:13 +10:00
|
|
|
const float aspect = but->block->aspect;
|
2017-11-03 20:26:35 +11:00
|
|
|
const int margin = UI_POPUP_MARGIN;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
/* create area region */
|
2021-02-22 09:28:17 -06:00
|
|
|
ARegion *region = ui_region_temp_add(CTX_wm_screen(C));
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-02-22 09:28:17 -06:00
|
|
|
static ARegionType type;
|
2017-11-03 20:26:35 +11:00
|
|
|
memset(&type, 0, sizeof(ARegionType));
|
2021-11-05 17:17:37 -05:00
|
|
|
type.draw = ui_searchbox_region_draw_fn;
|
|
|
|
type.free = ui_searchbox_region_free_fn;
|
2017-11-03 20:26:35 +11:00
|
|
|
type.regionid = RGN_TYPE_TEMPORARY;
|
2020-03-06 16:56:42 +01:00
|
|
|
region->type = &type;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
/* create searchbox data */
|
2021-12-24 22:17:49 -05:00
|
|
|
uiSearchboxData *data = MEM_cnew<uiSearchboxData>(__func__);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
/* set font, get bb */
|
|
|
|
data->fstyle = style->widget; /* copy struct */
|
|
|
|
ui_fontscale(&data->fstyle.points, aspect);
|
|
|
|
UI_fontstyle_set(&data->fstyle);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-03-06 16:56:42 +01:00
|
|
|
region->regiondata = data;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
/* special case, hardcoded feature, not draw backdrop when called from menus,
|
|
|
|
* assume for design that popup already added it */
|
2019-03-25 10:15:20 +11:00
|
|
|
if (but->block->flag & UI_BLOCK_SEARCH_MENU) {
|
2017-11-03 20:26:35 +11:00
|
|
|
data->noback = true;
|
2019-03-25 10:15:20 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
if (but->a1 > 0 && but->a2 > 0) {
|
|
|
|
data->preview = true;
|
|
|
|
data->prv_rows = but->a1;
|
|
|
|
data->prv_cols = but->a2;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-11-10 15:49:49 -06:00
|
|
|
if (but->optype != nullptr || use_shortcut_sep) {
|
2021-06-15 18:53:32 +02:00
|
|
|
data->use_shortcut_sep = true;
|
2017-11-03 20:26:35 +11:00
|
|
|
}
|
2020-08-07 14:34:11 +02:00
|
|
|
data->sep_string = search_but->item_sep_string;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
/* compute position */
|
|
|
|
if (but->block->flag & UI_BLOCK_SEARCH_MENU) {
|
|
|
|
const int search_but_h = BLI_rctf_size_y(&but->rect) + 10;
|
|
|
|
/* this case is search menu inside other menu */
|
|
|
|
/* we copy region size */
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-03-06 16:56:42 +01:00
|
|
|
region->winrct = butregion->winrct;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
/* widget rect, in region coords */
|
|
|
|
data->bbox.xmin = margin;
|
2020-03-06 16:56:42 +01:00
|
|
|
data->bbox.xmax = BLI_rcti_size_x(®ion->winrct) - margin;
|
2017-11-03 20:26:35 +11:00
|
|
|
data->bbox.ymin = margin;
|
2020-03-06 16:56:42 +01:00
|
|
|
data->bbox.ymax = BLI_rcti_size_y(®ion->winrct) - margin;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
/* check if button is lower half */
|
|
|
|
if (but->rect.ymax < BLI_rctf_cent_y(&but->block->rect)) {
|
|
|
|
data->bbox.ymin += search_but_h;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
data->bbox.ymax -= search_but_h;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
const int searchbox_width = UI_searchbox_size_x();
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-02-22 09:28:17 -06:00
|
|
|
rctf rect_fl;
|
2017-11-03 20:26:35 +11:00
|
|
|
rect_fl.xmin = but->rect.xmin - 5; /* align text with button */
|
|
|
|
rect_fl.xmax = but->rect.xmax + 5; /* symmetrical */
|
|
|
|
rect_fl.ymax = but->rect.ymin;
|
|
|
|
rect_fl.ymin = rect_fl.ymax - UI_searchbox_size_y();
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-02-22 09:28:17 -06:00
|
|
|
const int ofsx = (but->block->panel) ? but->block->panel->ofsx : 0;
|
|
|
|
const int ofsy = (but->block->panel) ? but->block->panel->ofsy : 0;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
BLI_rctf_translate(&rect_fl, ofsx, ofsy);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
/* minimal width */
|
|
|
|
if (BLI_rctf_size_x(&rect_fl) < searchbox_width) {
|
|
|
|
rect_fl.xmax = rect_fl.xmin + searchbox_width;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
/* copy to int, gets projected if possible too */
|
2021-02-22 09:28:17 -06:00
|
|
|
rcti rect_i;
|
2017-11-03 20:26:35 +11:00
|
|
|
BLI_rcti_rctf_copy(&rect_i, &rect_fl);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
if (butregion->v2d.cur.xmin != butregion->v2d.cur.xmax) {
|
|
|
|
UI_view2d_view_to_region_rcti(&butregion->v2d, &rect_fl, &rect_i);
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
BLI_rcti_translate(&rect_i, butregion->winrct.xmin, butregion->winrct.ymin);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-02-22 09:28:17 -06:00
|
|
|
int winx = WM_window_pixels_x(win);
|
2017-11-03 20:26:35 +11:00
|
|
|
// winy = WM_window_pixels_y(win); /* UNUSED */
|
2019-05-01 11:09:22 +10:00
|
|
|
// wm_window_get_size(win, &winx, &winy);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
if (rect_i.xmax > winx) {
|
|
|
|
/* super size */
|
|
|
|
if (rect_i.xmax > winx + rect_i.xmin) {
|
|
|
|
rect_i.xmax = winx;
|
|
|
|
rect_i.xmin = 0;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
rect_i.xmin -= rect_i.xmax - winx;
|
|
|
|
rect_i.xmax = winx;
|
|
|
|
}
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
if (rect_i.ymin < 0) {
|
|
|
|
int newy1 = but->rect.ymax + ofsy;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-03-25 10:15:20 +11:00
|
|
|
if (butregion->v2d.cur.xmin != butregion->v2d.cur.xmax) {
|
2017-11-03 20:26:35 +11:00
|
|
|
newy1 = UI_view2d_view_to_region_y(&butregion->v2d, newy1);
|
2019-03-25 10:15:20 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
newy1 += butregion->winrct.ymin;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
rect_i.ymax = BLI_rcti_size_y(&rect_i) + newy1;
|
|
|
|
rect_i.ymin = newy1;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
/* widget rect, in region coords */
|
|
|
|
data->bbox.xmin = margin;
|
|
|
|
data->bbox.xmax = BLI_rcti_size_x(&rect_i) + margin;
|
|
|
|
data->bbox.ymin = margin;
|
|
|
|
data->bbox.ymax = BLI_rcti_size_y(&rect_i) + margin;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
/* region bigger for shadow */
|
2020-03-06 16:56:42 +01:00
|
|
|
region->winrct.xmin = rect_i.xmin - margin;
|
|
|
|
region->winrct.xmax = rect_i.xmax + margin;
|
|
|
|
region->winrct.ymin = rect_i.ymin - margin;
|
|
|
|
region->winrct.ymax = rect_i.ymax;
|
2017-11-03 20:26:35 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
/* adds subwindow */
|
2020-08-01 13:02:21 +10:00
|
|
|
ED_region_floating_init(region);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
/* notify change and redraw */
|
2020-03-06 16:56:42 +01:00
|
|
|
ED_region_tag_redraw(region);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
/* prepare search data */
|
|
|
|
if (data->preview) {
|
|
|
|
data->items.maxitem = data->prv_rows * data->prv_cols;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
data->items.maxitem = SEARCH_ITEMS;
|
|
|
|
}
|
2021-10-21 13:54:48 -05:00
|
|
|
/* In case the button's string is dynamic, make sure there are buffers available. */
|
|
|
|
data->items.maxstrlen = but->hardmax == 0 ? UI_MAX_NAME_STR : but->hardmax;
|
2017-11-03 20:26:35 +11:00
|
|
|
data->items.totitem = 0;
|
2021-11-10 15:49:49 -06:00
|
|
|
data->items.names = (char **)MEM_callocN(data->items.maxitem * sizeof(void *), __func__);
|
|
|
|
data->items.pointers = (void **)MEM_callocN(data->items.maxitem * sizeof(void *), __func__);
|
|
|
|
data->items.icons = (int *)MEM_callocN(data->items.maxitem * sizeof(int), __func__);
|
|
|
|
data->items.states = (int *)MEM_callocN(data->items.maxitem * sizeof(int), __func__);
|
|
|
|
data->items.name_prefix_offsets = nullptr; /* Lazy initialized as needed. */
|
2021-02-22 09:28:17 -06:00
|
|
|
for (int i = 0; i < data->items.maxitem; i++) {
|
2021-11-10 15:49:49 -06:00
|
|
|
data->items.names[i] = (char *)MEM_callocN(data->items.maxstrlen + 1, __func__);
|
2019-03-25 10:15:20 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-03-06 16:56:42 +01:00
|
|
|
return region;
|
2017-11-03 20:26:35 +11:00
|
|
|
}
|
|
|
|
|
2021-06-15 18:53:32 +02:00
|
|
|
ARegion *ui_searchbox_create_generic(bContext *C, ARegion *butregion, uiButSearch *search_but)
|
|
|
|
{
|
|
|
|
return ui_searchbox_create_generic_ex(C, butregion, search_but, false);
|
|
|
|
}
|
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
/**
|
|
|
|
* Similar to Python's `str.title` except...
|
|
|
|
*
|
|
|
|
* - we know words are upper case and ascii only.
|
|
|
|
* - '_' are replaces by spaces.
|
|
|
|
*/
|
|
|
|
static void str_tolower_titlecaps_ascii(char *str, const size_t len)
|
|
|
|
{
|
|
|
|
bool prev_delim = true;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-11-05 17:17:37 -05:00
|
|
|
for (size_t i = 0; (i < len) && str[i]; i++) {
|
2017-11-03 20:26:35 +11:00
|
|
|
if (str[i] >= 'A' && str[i] <= 'Z') {
|
|
|
|
if (prev_delim == false) {
|
|
|
|
str[i] += 'a' - 'A';
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2017-11-03 20:26:35 +11:00
|
|
|
else if (str[i] == '_') {
|
|
|
|
str[i] = ' ';
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
prev_delim = ELEM(str[i], ' ') || (str[i] >= '0' && str[i] <= '9');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-06 16:56:42 +01:00
|
|
|
static void ui_searchbox_region_draw_cb__operator(const bContext *UNUSED(C), ARegion *region)
|
2017-11-03 20:26:35 +11:00
|
|
|
{
|
2021-11-10 15:49:49 -06:00
|
|
|
uiSearchboxData *data = static_cast<uiSearchboxData *>(region->regiondata);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
/* pixel space */
|
2020-03-06 16:56:42 +01:00
|
|
|
wmOrtho2_region_pixelspace(region);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-06-12 08:14:13 +02:00
|
|
|
if (data->noback == false) {
|
2019-03-22 17:56:58 +11:00
|
|
|
ui_draw_widget_menu_back(&data->bbox, true);
|
2018-06-12 08:14:13 +02:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
/* draw text */
|
|
|
|
if (data->items.totitem) {
|
|
|
|
rcti rect;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
/* draw items */
|
2021-11-05 17:17:37 -05:00
|
|
|
for (int a = 0; a < data->items.totitem; a++) {
|
2017-11-03 20:26:35 +11:00
|
|
|
rcti rect_pre, rect_post;
|
|
|
|
ui_searchbox_butrect(&rect, data, a);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
rect_pre = rect;
|
|
|
|
rect_post = rect;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
rect_pre.xmax = rect_post.xmin = rect.xmin + ((rect.xmax - rect.xmin) / 4);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
/* widget itself */
|
|
|
|
/* NOTE: i18n messages extracting tool does the same, please keep it in sync. */
|
|
|
|
{
|
2020-03-24 11:34:18 +11:00
|
|
|
const int state = ((a == data->active) ? UI_ACTIVE : 0) | data->items.states[a];
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-11-10 15:49:49 -06:00
|
|
|
wmOperatorType *ot = static_cast<wmOperatorType *>(data->items.pointers[a]);
|
2017-11-03 20:26:35 +11:00
|
|
|
char text_pre[128];
|
2021-11-10 15:49:49 -06:00
|
|
|
const char *text_pre_p = strstr(ot->idname, "_OT_");
|
|
|
|
if (text_pre_p == nullptr) {
|
2017-11-03 20:26:35 +11:00
|
|
|
text_pre[0] = '\0';
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
int text_pre_len;
|
|
|
|
text_pre_p += 1;
|
|
|
|
text_pre_len = BLI_strncpy_rlen(
|
|
|
|
text_pre, ot->idname, min_ii(sizeof(text_pre), text_pre_p - ot->idname));
|
|
|
|
text_pre[text_pre_len] = ':';
|
|
|
|
text_pre[text_pre_len + 1] = '\0';
|
|
|
|
str_tolower_titlecaps_ascii(text_pre, sizeof(text_pre));
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
rect_pre.xmax += 4; /* sneaky, avoid showing ugly margin */
|
2018-07-01 19:57:31 +02:00
|
|
|
ui_draw_menu_item(&data->fstyle,
|
|
|
|
&rect_pre,
|
|
|
|
CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, text_pre),
|
|
|
|
data->items.icons[a],
|
|
|
|
state,
|
2021-01-25 22:54:00 +01:00
|
|
|
UI_MENU_ITEM_SEPARATOR_NONE,
|
2021-11-10 15:49:49 -06:00
|
|
|
nullptr);
|
2021-01-25 22:54:00 +01:00
|
|
|
ui_draw_menu_item(&data->fstyle,
|
|
|
|
&rect_post,
|
|
|
|
data->items.names[a],
|
|
|
|
0,
|
|
|
|
state,
|
2021-06-15 18:53:32 +02:00
|
|
|
data->use_shortcut_sep ? UI_MENU_ITEM_SEPARATOR_SHORTCUT :
|
|
|
|
UI_MENU_ITEM_SEPARATOR_NONE,
|
2021-11-10 15:49:49 -06:00
|
|
|
nullptr);
|
2017-11-03 20:26:35 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2017-11-03 20:26:35 +11:00
|
|
|
/* indicate more */
|
|
|
|
if (data->items.more) {
|
|
|
|
ui_searchbox_butrect(&rect, data, data->items.maxitem - 1);
|
2020-08-16 15:38:34 +02:00
|
|
|
GPU_blend(GPU_BLEND_ALPHA);
|
2021-07-16 11:45:52 +10:00
|
|
|
UI_icon_draw(BLI_rcti_size_x(&rect) / 2, rect.ymin - 9, ICON_TRIA_DOWN);
|
2020-08-16 15:38:34 +02:00
|
|
|
GPU_blend(GPU_BLEND_NONE);
|
2017-11-03 20:26:35 +11:00
|
|
|
}
|
|
|
|
if (data->items.offset) {
|
|
|
|
ui_searchbox_butrect(&rect, data, 0);
|
2020-08-16 15:38:34 +02:00
|
|
|
GPU_blend(GPU_BLEND_ALPHA);
|
2021-07-16 11:45:52 +10:00
|
|
|
UI_icon_draw(BLI_rcti_size_x(&rect) / 2, rect.ymax - 7, ICON_TRIA_UP);
|
2020-08-16 15:38:34 +02:00
|
|
|
GPU_blend(GPU_BLEND_NONE);
|
2017-11-03 20:26:35 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-07 14:34:11 +02:00
|
|
|
ARegion *ui_searchbox_create_operator(bContext *C, ARegion *butregion, uiButSearch *search_but)
|
2017-11-03 20:26:35 +11:00
|
|
|
{
|
2021-06-15 18:53:32 +02:00
|
|
|
ARegion *region = ui_searchbox_create_generic_ex(C, butregion, search_but, true);
|
2017-11-03 20:26:35 +11:00
|
|
|
|
2020-03-06 16:56:42 +01:00
|
|
|
region->type->draw = ui_searchbox_region_draw_cb__operator;
|
2017-11-03 20:26:35 +11:00
|
|
|
|
2020-03-06 16:56:42 +01:00
|
|
|
return region;
|
2017-11-03 20:26:35 +11:00
|
|
|
}
|
|
|
|
|
2020-03-06 16:56:42 +01:00
|
|
|
void ui_searchbox_free(bContext *C, ARegion *region)
|
2017-11-03 20:26:35 +11:00
|
|
|
{
|
2020-03-06 16:56:42 +01:00
|
|
|
ui_region_temp_remove(C, CTX_wm_screen(C), region);
|
2017-11-03 20:26:35 +11:00
|
|
|
}
|
|
|
|
|
2020-03-24 11:34:18 +11:00
|
|
|
static void ui_searchbox_region_draw_cb__menu(const bContext *UNUSED(C), ARegion *UNUSED(region))
|
|
|
|
{
|
|
|
|
/* Currently unused. */
|
|
|
|
}
|
|
|
|
|
2020-08-07 14:34:11 +02:00
|
|
|
ARegion *ui_searchbox_create_menu(bContext *C, ARegion *butregion, uiButSearch *search_but)
|
2020-03-24 11:34:18 +11:00
|
|
|
{
|
2021-06-15 18:53:32 +02:00
|
|
|
ARegion *region = ui_searchbox_create_generic_ex(C, butregion, search_but, true);
|
2020-03-24 11:34:18 +11:00
|
|
|
|
|
|
|
if (false) {
|
|
|
|
region->type->draw = ui_searchbox_region_draw_cb__menu;
|
|
|
|
}
|
|
|
|
|
|
|
|
return region;
|
|
|
|
}
|
|
|
|
|
2020-08-07 14:34:11 +02:00
|
|
|
void ui_but_search_refresh(uiButSearch *search_but)
|
2017-11-03 20:26:35 +11:00
|
|
|
{
|
2020-08-07 14:34:11 +02:00
|
|
|
uiBut *but = &search_but->but;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
/* possibly very large lists (such as ID datablocks) only
|
|
|
|
* only validate string RNA buts (not pointers) */
|
|
|
|
if (but->rnaprop && RNA_property_type(but->rnaprop) != PROP_STRING) {
|
|
|
|
return;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-12-24 22:17:49 -05:00
|
|
|
uiSearchItems *items = MEM_cnew<uiSearchItems>(__func__);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-03 20:26:35 +11:00
|
|
|
/* setup search struct */
|
|
|
|
items->maxitem = 10;
|
|
|
|
items->maxstrlen = 256;
|
2021-11-10 15:49:49 -06:00
|
|
|
items->names = (char **)MEM_callocN(items->maxitem * sizeof(void *), __func__);
|
2021-11-05 17:17:37 -05:00
|
|
|
for (int i = 0; i < items->maxitem; i++) {
|
2021-11-10 15:49:49 -06:00
|
|
|
items->names[i] = (char *)MEM_callocN(but->hardmax + 1, __func__);
|
2019-03-25 10:15:20 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-11-10 15:49:49 -06:00
|
|
|
ui_searchbox_update_fn((bContext *)but->block->evil_C, search_but, but->drawstr, items);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-03-02 12:43:27 -06:00
|
|
|
if (!search_but->results_are_suggestions) {
|
UI: Expose an "is first search" boolean to search button callbacks
Currently when you open an RNA collection search button, like a
vertex group selector, the search filter isn't applied until you
start typing, in order to display every option at the start.
Otherwise they wouldn't be visible, since the search filter would
run for the current text.
Currently this check happens in one place, but it relies on the
`changed` value of `uiBut`. This is fine in the interface directory,
but anywhere else it would require exposing `uiBut.changed`, which
is probably too low-level to expose.
The solution is adding an `is_first` argument to the search callbacks,
which is nice for a few reasons:
- They work at a higher level of abstraction, meaning they don't
have to worry about how exactly to tell if this is the first
search.
- It makes it easier to do special behavior when the search menu
is first opened.
- Then, obviously, it makes that state accessible without including
`interface_intern.h`.
Needed for attribute search: T85658
Differential Revision: https://developer.blender.org/D10528
2021-03-02 11:42:05 -06:00
|
|
|
/* Only red-alert when we are sure of it, this can miss cases when >10 matches. */
|
|
|
|
if (items->totitem == 0) {
|
2017-11-03 20:26:35 +11:00
|
|
|
UI_but_flag_enable(but, UI_BUT_REDALERT);
|
|
|
|
}
|
UI: Expose an "is first search" boolean to search button callbacks
Currently when you open an RNA collection search button, like a
vertex group selector, the search filter isn't applied until you
start typing, in order to display every option at the start.
Otherwise they wouldn't be visible, since the search filter would
run for the current text.
Currently this check happens in one place, but it relies on the
`changed` value of `uiBut`. This is fine in the interface directory,
but anywhere else it would require exposing `uiBut.changed`, which
is probably too low-level to expose.
The solution is adding an `is_first` argument to the search callbacks,
which is nice for a few reasons:
- They work at a higher level of abstraction, meaning they don't
have to worry about how exactly to tell if this is the first
search.
- It makes it easier to do special behavior when the search menu
is first opened.
- Then, obviously, it makes that state accessible without including
`interface_intern.h`.
Needed for attribute search: T85658
Differential Revision: https://developer.blender.org/D10528
2021-03-02 11:42:05 -06:00
|
|
|
else if (items->more == 0) {
|
|
|
|
if (UI_search_items_find_index(items, but->drawstr) == -1) {
|
|
|
|
UI_but_flag_enable(but, UI_BUT_REDALERT);
|
|
|
|
}
|
|
|
|
}
|
2017-11-03 20:26:35 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-11-05 17:17:37 -05:00
|
|
|
for (int i = 0; i < items->maxitem; i++) {
|
|
|
|
MEM_freeN(items->names[i]);
|
2017-11-03 20:26:35 +11:00
|
|
|
}
|
|
|
|
MEM_freeN(items->names);
|
|
|
|
MEM_freeN(items);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** \} */
|