Blender  V2.93
interface_region_search.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2008 Blender Foundation.
17  * All rights reserved.
18  */
19 
26 #include <stdarg.h>
27 #include <stdlib.h>
28 #include <string.h>
29 
30 #include "DNA_ID.h"
31 #include "MEM_guardedalloc.h"
32 
33 #include "DNA_userdef_types.h"
34 
35 #include "BLI_math.h"
36 
37 #include "BLI_listbase.h"
38 #include "BLI_rect.h"
39 #include "BLI_string.h"
40 #include "BLI_utildefines.h"
41 
42 #include "BKE_context.h"
43 #include "BKE_screen.h"
44 
45 #include "WM_api.h"
46 #include "WM_types.h"
47 
48 #include "RNA_access.h"
49 
50 #include "UI_interface.h"
51 #include "UI_interface_icons.h"
52 #include "UI_view2d.h"
53 
54 #include "BLT_translation.h"
55 
56 #include "ED_screen.h"
57 
58 #include "GPU_state.h"
59 #include "interface_intern.h"
61 
62 #define MENU_BORDER (int)(0.3f * U.widget_unit)
63 
64 /* -------------------------------------------------------------------- */
68 struct uiSearchItems {
70 
71  int offset, offset_i; /* offset for inserting in array */
72  int more; /* flag indicating there are more items */
73 
74  char **names;
75  void **pointers;
76  int *icons;
77  int *states;
79 
81  bool has_icon;
82 
84  void *active;
85 };
86 
87 typedef struct uiSearchboxData {
92  int active;
94  bool noback;
96  bool preview;
98  bool use_sep;
104  const char *sep_string;
106 
107 #define SEARCH_ITEMS 10
108 
121  const char *name,
122  void *poin,
123  int iconid,
124  int state,
125  const uint8_t name_prefix_offset)
126 {
127  /* hijack for autocomplete */
128  if (items->autocpl) {
129  UI_autocomplete_update_name(items->autocpl, name);
130  return true;
131  }
132 
133  if (iconid) {
134  items->has_icon = true;
135  }
136 
137  /* hijack for finding active item */
138  if (items->active) {
139  if (poin == items->active) {
140  items->offset_i = items->totitem;
141  }
142  items->totitem++;
143  return true;
144  }
145 
146  if (items->totitem >= items->maxitem) {
147  items->more = 1;
148  return false;
149  }
150 
151  /* skip first items in list */
152  if (items->offset_i > 0) {
153  items->offset_i--;
154  return true;
155  }
156 
157  if (items->names) {
158  BLI_strncpy(items->names[items->totitem], name, items->maxstrlen);
159  }
160  if (items->pointers) {
161  items->pointers[items->totitem] = poin;
162  }
163  if (items->icons) {
164  items->icons[items->totitem] = iconid;
165  }
166 
167  if (name_prefix_offset != 0) {
168  /* Lazy initialize, as this isn't used often. */
169  if (items->name_prefix_offsets == NULL) {
171  items->maxitem * sizeof(*items->name_prefix_offsets), "search name prefix offsets");
172  }
173  items->name_prefix_offsets[items->totitem] = name_prefix_offset;
174  }
175 
176  /* Limit flags that can be set so flags such as 'UI_SELECT' aren't accidentally set
177  * which will cause problems, add others as needed. */
178  BLI_assert(
180  if (items->states) {
181  items->states[items->totitem] = state;
182  }
183 
184  items->totitem++;
185 
186  return true;
187 }
188 
190 {
192 }
193 
195 {
196  return 12 * UI_UNIT_X;
197 }
198 
199 int UI_search_items_find_index(uiSearchItems *items, const char *name)
200 {
201  if (items->name_prefix_offsets != NULL) {
202  for (int i = 0; i < items->totitem; i++) {
203  if (STREQ(name, items->names[i] + items->name_prefix_offsets[i])) {
204  return i;
205  }
206  }
207  }
208  else {
209  for (int i = 0; i < items->totitem; i++) {
210  if (STREQ(name, items->names[i])) {
211  return i;
212  }
213  }
214  }
215  return -1;
216 }
217 
218 /* region is the search box itself */
219 static void ui_searchbox_select(bContext *C, ARegion *region, uiBut *but, int step)
220 {
221  uiSearchboxData *data = region->regiondata;
222 
223  /* apply step */
224  data->active += step;
225 
226  if (data->items.totitem == 0) {
227  data->active = -1;
228  }
229  else if (data->active >= data->items.totitem) {
230  if (data->items.more) {
231  data->items.offset++;
232  data->active = data->items.totitem - 1;
233  ui_searchbox_update(C, region, but, false);
234  }
235  else {
236  data->active = data->items.totitem - 1;
237  }
238  }
239  else if (data->active < 0) {
240  if (data->items.offset) {
241  data->items.offset--;
242  data->active = 0;
243  ui_searchbox_update(C, region, but, false);
244  }
245  else {
246  /* only let users step into an 'unset' state for unlink buttons */
247  data->active = (but->flag & UI_BUT_VALUE_CLEAR) ? -1 : 0;
248  }
249  }
250 
251  ED_region_tag_redraw(region);
252 }
253 
254 static void ui_searchbox_butrect(rcti *r_rect, uiSearchboxData *data, int itemnr)
255 {
256  /* thumbnail preview */
257  if (data->preview) {
258  const int butw = (BLI_rcti_size_x(&data->bbox) - 2 * MENU_BORDER) / data->prv_cols;
259  const int buth = (BLI_rcti_size_y(&data->bbox) - 2 * MENU_BORDER) / data->prv_rows;
260  int row, col;
261 
262  *r_rect = data->bbox;
263 
264  col = itemnr % data->prv_cols;
265  row = itemnr / data->prv_cols;
266 
267  r_rect->xmin += MENU_BORDER + (col * butw);
268  r_rect->xmax = r_rect->xmin + butw;
269 
270  r_rect->ymax -= MENU_BORDER + (row * buth);
271  r_rect->ymin = r_rect->ymax - buth;
272  }
273  /* list view */
274  else {
275  const int buth = (BLI_rcti_size_y(&data->bbox) - 2 * UI_POPUP_MENU_TOP) / SEARCH_ITEMS;
276 
277  *r_rect = data->bbox;
278  r_rect->xmin = data->bbox.xmin + 3.0f;
279  r_rect->xmax = data->bbox.xmax - 3.0f;
280 
281  r_rect->ymax = data->bbox.ymax - UI_POPUP_MENU_TOP - itemnr * buth;
282  r_rect->ymin = r_rect->ymax - buth;
283  }
284 }
285 
286 int ui_searchbox_find_index(ARegion *region, const char *name)
287 {
288  uiSearchboxData *data = region->regiondata;
289  return UI_search_items_find_index(&data->items, name);
290 }
291 
292 /* x and y in screen-coords. */
293 bool ui_searchbox_inside(ARegion *region, int x, int y)
294 {
295  uiSearchboxData *data = region->regiondata;
296 
297  return BLI_rcti_isect_pt(&data->bbox, x - region->winrct.xmin, y - region->winrct.ymin);
298 }
299 
300 /* string validated to be of correct length (but->hardmax) */
301 bool ui_searchbox_apply(uiBut *but, ARegion *region)
302 {
303  uiSearchboxData *data = region->regiondata;
304  uiButSearch *search_but = (uiButSearch *)but;
305 
307 
308  search_but->item_active = NULL;
309 
310  if (data->active != -1) {
311  const char *name = data->items.names[data->active] +
312  /* Never include the prefix in the button. */
313  (data->items.name_prefix_offsets ?
314  data->items.name_prefix_offsets[data->active] :
315  0);
316 
317  const char *name_sep = data->use_sep ? strrchr(name, UI_SEP_CHAR) : NULL;
318 
319  BLI_strncpy(but->editstr, name, name_sep ? (name_sep - name) + 1 : data->items.maxstrlen);
320 
321  search_but->item_active = data->items.pointers[data->active];
322 
323  return true;
324  }
325  if (but->flag & UI_BUT_VALUE_CLEAR) {
326  /* It is valid for _VALUE_CLEAR flavor to have no active element
327  * (it's a valid way to unlink). */
328  but->editstr[0] = '\0';
329 
330  return true;
331  }
332  return false;
333 }
334 
336  struct ARegion *region,
337  int *UNUSED(r_pass),
338  double *UNUSED(pass_delay),
339  bool *r_exit_on_event)
340 {
341  *r_exit_on_event = true;
342 
343  LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
344  LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
345  if (but->type != UI_BTYPE_SEARCH_MENU) {
346  continue;
347  }
348 
349  uiButSearch *search_but = (uiButSearch *)but;
350  if (!search_but->item_tooltip_fn) {
351  continue;
352  }
353 
354  ARegion *searchbox_region = UI_region_searchbox_region_get(region);
355  uiSearchboxData *data = searchbox_region->regiondata;
356 
357  BLI_assert(data->items.pointers[data->active] == search_but->item_active);
358 
359  rcti rect;
360  ui_searchbox_butrect(&rect, data, data->active);
361 
362  return search_but->item_tooltip_fn(
363  C, region, &rect, search_but->arg, search_but->item_active);
364  }
365  }
366  return NULL;
367 }
368 
370  bContext *C, ARegion *region, uiBut *but, ARegion *butregion, const wmEvent *event)
371 {
372  uiSearchboxData *data = region->regiondata;
373  uiButSearch *search_but = (uiButSearch *)but;
374  int type = event->type, val = event->val;
375  bool handled = false;
376  bool tooltip_timer_started = false;
377 
379 
380  if (type == MOUSEPAN) {
381  ui_pan_to_scroll(event, &type, &val);
382  }
383 
384  switch (type) {
385  case WHEELUPMOUSE:
386  case EVT_UPARROWKEY:
387  ui_searchbox_select(C, region, but, -1);
388  handled = true;
389  break;
390  case WHEELDOWNMOUSE:
391  case EVT_DOWNARROWKEY:
392  ui_searchbox_select(C, region, but, 1);
393  handled = true;
394  break;
395  case RIGHTMOUSE:
396  if (val) {
397  if (search_but->item_context_menu_fn) {
398  if (data->active != -1) {
399  /* Check the cursor is over the active element
400  * (a little confusing if this isn't the case, although it does work). */
401  rcti rect;
402  ui_searchbox_butrect(&rect, data, data->active);
403  if (BLI_rcti_isect_pt(
404  &rect, event->x - region->winrct.xmin, event->y - region->winrct.ymin)) {
405 
406  void *active = data->items.pointers[data->active];
407  if (search_but->item_context_menu_fn(C, search_but->arg, active, event)) {
408  handled = true;
409  }
410  }
411  }
412  }
413  }
414  break;
415  case MOUSEMOVE: {
416  bool is_inside = false;
417 
418  if (BLI_rcti_isect_pt(&region->winrct, event->x, event->y)) {
419  rcti rect;
420  int a;
421 
422  for (a = 0; a < data->items.totitem; a++) {
423  ui_searchbox_butrect(&rect, data, a);
424  if (BLI_rcti_isect_pt(
425  &rect, event->x - region->winrct.xmin, event->y - region->winrct.ymin)) {
426  is_inside = true;
427  if (data->active != a) {
428  data->active = a;
429  ui_searchbox_select(C, region, but, 0);
430  handled = true;
431  break;
432  }
433  }
434  }
435  }
436 
437  if (U.flag & USER_TOOLTIPS) {
438  if (is_inside) {
439  if (data->active != -1) {
441  search_but->item_active = data->items.pointers[data->active];
443  tooltip_timer_started = true;
444  }
445  }
446  }
447 
448  break;
449  }
450  }
451 
452  if (handled && (tooltip_timer_started == false)) {
453  wmWindow *win = CTX_wm_window(C);
454  WM_tooltip_clear(C, win);
455  }
456 
457  return handled;
458 }
459 
462  uiButSearch *search_but,
463  const char *str,
464  uiSearchItems *items)
465 {
466  /* While the button is in text editing mode (searchbox open), remove tooltips on every update. */
467  if (search_but->but.editstr) {
468  wmWindow *win = CTX_wm_window(C);
469  WM_tooltip_clear(C, win);
470  }
471  const bool is_first_search = !search_but->but.changed;
472  search_but->items_update_fn(C, search_but->arg, str, items, is_first_search);
473 }
474 
475 /* region is the search box itself */
476 void ui_searchbox_update(bContext *C, ARegion *region, uiBut *but, const bool reset)
477 {
478  uiButSearch *search_but = (uiButSearch *)but;
479  uiSearchboxData *data = region->regiondata;
480 
482 
483  /* reset vars */
484  data->items.totitem = 0;
485  data->items.more = 0;
486  if (reset == false) {
487  data->items.offset_i = data->items.offset;
488  }
489  else {
490  data->items.offset_i = data->items.offset = 0;
491  data->active = -1;
492 
493  /* handle active */
494  if (search_but->items_update_fn && search_but->item_active) {
495  data->items.active = search_but->item_active;
496  ui_searchbox_update_fn(C, search_but, but->editstr, &data->items);
497  data->items.active = NULL;
498 
499  /* found active item, calculate real offset by centering it */
500  if (data->items.totitem) {
501  /* first case, begin of list */
502  if (data->items.offset_i < data->items.maxitem) {
503  data->active = data->items.offset_i;
504  data->items.offset_i = 0;
505  }
506  else {
507  /* second case, end of list */
508  if (data->items.totitem - data->items.offset_i <= data->items.maxitem) {
509  data->active = data->items.offset_i - data->items.totitem + data->items.maxitem;
510  data->items.offset_i = data->items.totitem - data->items.maxitem;
511  }
512  else {
513  /* center active item */
514  data->items.offset_i -= data->items.maxitem / 2;
515  data->active = data->items.maxitem / 2;
516  }
517  }
518  }
519  data->items.offset = data->items.offset_i;
520  data->items.totitem = 0;
521  }
522  }
523 
524  /* callback */
525  if (search_but->items_update_fn) {
526  ui_searchbox_update_fn(C, search_but, but->editstr, &data->items);
527  }
528 
529  /* handle case where editstr is equal to one of items */
530  if (reset && data->active == -1) {
531  int a;
532 
533  for (a = 0; a < data->items.totitem; a++) {
534  const char *name = data->items.names[a] +
535  /* Never include the prefix in the button. */
536  (data->items.name_prefix_offsets ? data->items.name_prefix_offsets[a] :
537  0);
538  const char *name_sep = data->use_sep ? strrchr(name, UI_SEP_CHAR) : NULL;
539  if (STREQLEN(but->editstr, name, name_sep ? (name_sep - name) : data->items.maxstrlen)) {
540  data->active = a;
541  break;
542  }
543  }
544  if (data->items.totitem == 1 && but->editstr[0]) {
545  data->active = 0;
546  }
547  }
548 
549  /* validate selected item */
550  ui_searchbox_select(C, region, but, 0);
551 
552  ED_region_tag_redraw(region);
553 }
554 
556 {
557  uiButSearch *search_but = (uiButSearch *)but;
558  uiSearchboxData *data = region->regiondata;
559  int match = AUTOCOMPLETE_NO_MATCH;
560 
562 
563  if (str[0]) {
565 
566  ui_searchbox_update_fn(C, search_but, but->editstr, &data->items);
567 
568  match = UI_autocomplete_end(data->items.autocpl, str);
569  data->items.autocpl = NULL;
570  }
571 
572  return match;
573 }
574 
575 static void ui_searchbox_region_draw_cb(const bContext *C, ARegion *region)
576 {
577  uiSearchboxData *data = region->regiondata;
578 
579  /* pixel space */
581 
582  if (data->noback == false) {
583  ui_draw_widget_menu_back(&data->bbox, true);
584  }
585 
586  /* draw text */
587  if (data->items.totitem) {
588  rcti rect;
589  int a;
590 
591  if (data->preview) {
592  /* draw items */
593  for (a = 0; a < data->items.totitem; a++) {
594  const int state = ((a == data->active) ? UI_ACTIVE : 0) | data->items.states[a];
595 
596  /* ensure icon is up-to-date */
597  ui_icon_ensure_deferred(C, data->items.icons[a], data->preview);
598 
599  ui_searchbox_butrect(&rect, data, a);
600 
601  /* widget itself */
603  &data->fstyle, &rect, data->items.names[a], data->items.icons[a], state);
604  }
605 
606  /* indicate more */
607  if (data->items.more) {
608  ui_searchbox_butrect(&rect, data, data->items.maxitem - 1);
610  UI_icon_draw(rect.xmax - 18, rect.ymin - 7, ICON_TRIA_DOWN);
612  }
613  if (data->items.offset) {
614  ui_searchbox_butrect(&rect, data, 0);
616  UI_icon_draw(rect.xmin, rect.ymax - 9, ICON_TRIA_UP);
618  }
619  }
620  else {
621  const int search_sep_len = data->sep_string ? strlen(data->sep_string) : 0;
622  /* draw items */
623  for (a = 0; a < data->items.totitem; a++) {
624  const int state = ((a == data->active) ? UI_ACTIVE : 0) | data->items.states[a];
625  char *name = data->items.names[a];
626  int icon = data->items.icons[a];
627  char *name_sep_test = NULL;
628 
630  if (data->use_sep) {
631  separator_type = UI_MENU_ITEM_SEPARATOR_SHORTCUT;
632  }
633  /* Only set for displaying additional hint (e.g. library name of a linked data-block). */
634  else if (state & UI_BUT_HAS_SEP_CHAR) {
635  separator_type = UI_MENU_ITEM_SEPARATOR_HINT;
636  }
637 
638  ui_searchbox_butrect(&rect, data, a);
639 
640  /* widget itself */
641  if ((search_sep_len == 0) ||
642  !(name_sep_test = strstr(data->items.names[a], data->sep_string))) {
643  if (!icon && data->items.has_icon) {
644  /* If there is any icon item, make sure all items line up. */
645  icon = ICON_BLANK1;
646  }
647 
648  /* Simple menu item. */
649  ui_draw_menu_item(&data->fstyle, &rect, name, icon, state, separator_type, NULL);
650  }
651  else {
652  /* Split menu item, faded text before the separator. */
653  char *name_sep = NULL;
654  do {
655  name_sep = name_sep_test;
656  name_sep_test = strstr(name_sep + search_sep_len, data->sep_string);
657  } while (name_sep_test != NULL);
658 
659  name_sep += search_sep_len;
660  const char name_sep_prev = *name_sep;
661  *name_sep = '\0';
662  int name_width = 0;
663  ui_draw_menu_item(&data->fstyle,
664  &rect,
665  name,
666  0,
669  &name_width);
670  *name_sep = name_sep_prev;
671  rect.xmin += name_width;
672  rect.xmin += UI_UNIT_X / 4;
673 
674  if (icon == ICON_BLANK1) {
675  icon = ICON_NONE;
676  rect.xmin -= UI_DPI_ICON_SIZE / 4;
677  }
678 
679  /* The previous menu item draws the active selection. */
680  ui_draw_menu_item(&data->fstyle, &rect, name_sep, icon, state, separator_type, NULL);
681  }
682  }
683  /* indicate more */
684  if (data->items.more) {
685  ui_searchbox_butrect(&rect, data, data->items.maxitem - 1);
687  UI_icon_draw((BLI_rcti_size_x(&rect)) / 2, rect.ymin - 9, ICON_TRIA_DOWN);
689  }
690  if (data->items.offset) {
691  ui_searchbox_butrect(&rect, data, 0);
693  UI_icon_draw((BLI_rcti_size_x(&rect)) / 2, rect.ymax - 7, ICON_TRIA_UP);
695  }
696  }
697  }
698 }
699 
701 {
702  uiSearchboxData *data = region->regiondata;
703  int a;
704 
705  /* free search data */
706  for (a = 0; a < data->items.maxitem; a++) {
707  MEM_freeN(data->items.names[a]);
708  }
709  MEM_freeN(data->items.names);
710  MEM_freeN(data->items.pointers);
711  MEM_freeN(data->items.icons);
712  MEM_freeN(data->items.states);
713 
714  if (data->items.name_prefix_offsets != NULL) {
715  MEM_freeN(data->items.name_prefix_offsets);
716  }
717 
718  MEM_freeN(data);
719  region->regiondata = NULL;
720 }
721 
723 {
724  wmWindow *win = CTX_wm_window(C);
725  const uiStyle *style = UI_style_get();
726  uiBut *but = &search_but->but;
727  const float aspect = but->block->aspect;
728  const int margin = UI_POPUP_MARGIN;
729 
730  /* create area region */
732 
733  static ARegionType type;
734  memset(&type, 0, sizeof(ARegionType));
737  type.regionid = RGN_TYPE_TEMPORARY;
738  region->type = &type;
739 
740  /* create searchbox data */
741  uiSearchboxData *data = MEM_callocN(sizeof(uiSearchboxData), "uiSearchboxData");
742 
743  /* set font, get bb */
744  data->fstyle = style->widget; /* copy struct */
745  ui_fontscale(&data->fstyle.points, aspect);
746  UI_fontstyle_set(&data->fstyle);
747 
748  region->regiondata = data;
749 
750  /* special case, hardcoded feature, not draw backdrop when called from menus,
751  * assume for design that popup already added it */
752  if (but->block->flag & UI_BLOCK_SEARCH_MENU) {
753  data->noback = true;
754  }
755 
756  if (but->a1 > 0 && but->a2 > 0) {
757  data->preview = true;
758  data->prv_rows = but->a1;
759  data->prv_cols = but->a2;
760  }
761 
762  /* Only show key shortcuts when needed (checking RNA prop pointer is useless here, a lot of
763  * buttons are about data without having that pointer defined, let's rather try with optype!).
764  * One can also enforce that behavior by setting
765  * UI_BUT_HAS_SHORTCUT drawflag of search button. */
766  if (but->optype != NULL || (but->drawflag & UI_BUT_HAS_SHORTCUT) != 0) {
767  data->use_sep = true;
768  }
769  data->sep_string = search_but->item_sep_string;
770 
771  /* compute position */
772  if (but->block->flag & UI_BLOCK_SEARCH_MENU) {
773  const int search_but_h = BLI_rctf_size_y(&but->rect) + 10;
774  /* this case is search menu inside other menu */
775  /* we copy region size */
776 
777  region->winrct = butregion->winrct;
778 
779  /* widget rect, in region coords */
780  data->bbox.xmin = margin;
781  data->bbox.xmax = BLI_rcti_size_x(&region->winrct) - margin;
782  data->bbox.ymin = margin;
783  data->bbox.ymax = BLI_rcti_size_y(&region->winrct) - margin;
784 
785  /* check if button is lower half */
786  if (but->rect.ymax < BLI_rctf_cent_y(&but->block->rect)) {
787  data->bbox.ymin += search_but_h;
788  }
789  else {
790  data->bbox.ymax -= search_but_h;
791  }
792  }
793  else {
794  const int searchbox_width = UI_searchbox_size_x();
795 
796  rctf rect_fl;
797  rect_fl.xmin = but->rect.xmin - 5; /* align text with button */
798  rect_fl.xmax = but->rect.xmax + 5; /* symmetrical */
799  rect_fl.ymax = but->rect.ymin;
800  rect_fl.ymin = rect_fl.ymax - UI_searchbox_size_y();
801 
802  const int ofsx = (but->block->panel) ? but->block->panel->ofsx : 0;
803  const int ofsy = (but->block->panel) ? but->block->panel->ofsy : 0;
804 
805  BLI_rctf_translate(&rect_fl, ofsx, ofsy);
806 
807  /* minimal width */
808  if (BLI_rctf_size_x(&rect_fl) < searchbox_width) {
809  rect_fl.xmax = rect_fl.xmin + searchbox_width;
810  }
811 
812  /* copy to int, gets projected if possible too */
813  rcti rect_i;
814  BLI_rcti_rctf_copy(&rect_i, &rect_fl);
815 
816  if (butregion->v2d.cur.xmin != butregion->v2d.cur.xmax) {
817  UI_view2d_view_to_region_rcti(&butregion->v2d, &rect_fl, &rect_i);
818  }
819 
820  BLI_rcti_translate(&rect_i, butregion->winrct.xmin, butregion->winrct.ymin);
821 
822  int winx = WM_window_pixels_x(win);
823  // winy = WM_window_pixels_y(win); /* UNUSED */
824  // wm_window_get_size(win, &winx, &winy);
825 
826  if (rect_i.xmax > winx) {
827  /* super size */
828  if (rect_i.xmax > winx + rect_i.xmin) {
829  rect_i.xmax = winx;
830  rect_i.xmin = 0;
831  }
832  else {
833  rect_i.xmin -= rect_i.xmax - winx;
834  rect_i.xmax = winx;
835  }
836  }
837 
838  if (rect_i.ymin < 0) {
839  int newy1 = but->rect.ymax + ofsy;
840 
841  if (butregion->v2d.cur.xmin != butregion->v2d.cur.xmax) {
842  newy1 = UI_view2d_view_to_region_y(&butregion->v2d, newy1);
843  }
844 
845  newy1 += butregion->winrct.ymin;
846 
847  rect_i.ymax = BLI_rcti_size_y(&rect_i) + newy1;
848  rect_i.ymin = newy1;
849  }
850 
851  /* widget rect, in region coords */
852  data->bbox.xmin = margin;
853  data->bbox.xmax = BLI_rcti_size_x(&rect_i) + margin;
854  data->bbox.ymin = margin;
855  data->bbox.ymax = BLI_rcti_size_y(&rect_i) + margin;
856 
857  /* region bigger for shadow */
858  region->winrct.xmin = rect_i.xmin - margin;
859  region->winrct.xmax = rect_i.xmax + margin;
860  region->winrct.ymin = rect_i.ymin - margin;
861  region->winrct.ymax = rect_i.ymax;
862  }
863 
864  /* adds subwindow */
865  ED_region_floating_init(region);
866 
867  /* notify change and redraw */
868  ED_region_tag_redraw(region);
869 
870  /* prepare search data */
871  if (data->preview) {
872  data->items.maxitem = data->prv_rows * data->prv_cols;
873  }
874  else {
875  data->items.maxitem = SEARCH_ITEMS;
876  }
877  data->items.maxstrlen = but->hardmax;
878  data->items.totitem = 0;
879  data->items.names = MEM_callocN(data->items.maxitem * sizeof(void *), "search names");
880  data->items.pointers = MEM_callocN(data->items.maxitem * sizeof(void *), "search pointers");
881  data->items.icons = MEM_callocN(data->items.maxitem * sizeof(int), "search icons");
882  data->items.states = MEM_callocN(data->items.maxitem * sizeof(int), "search flags");
883  data->items.name_prefix_offsets = NULL; /* Lazy initialized as needed. */
884  for (int i = 0; i < data->items.maxitem; i++) {
885  data->items.names[i] = MEM_callocN(but->hardmax + 1, "search pointers");
886  }
887 
888  return region;
889 }
890 
897 static void str_tolower_titlecaps_ascii(char *str, const size_t len)
898 {
899  size_t i;
900  bool prev_delim = true;
901 
902  for (i = 0; (i < len) && str[i]; i++) {
903  if (str[i] >= 'A' && str[i] <= 'Z') {
904  if (prev_delim == false) {
905  str[i] += 'a' - 'A';
906  }
907  }
908  else if (str[i] == '_') {
909  str[i] = ' ';
910  }
911 
912  prev_delim = ELEM(str[i], ' ') || (str[i] >= '0' && str[i] <= '9');
913  }
914 }
915 
917 {
918  uiSearchboxData *data = region->regiondata;
919 
920  /* pixel space */
922 
923  if (data->noback == false) {
924  ui_draw_widget_menu_back(&data->bbox, true);
925  }
926 
927  /* draw text */
928  if (data->items.totitem) {
929  rcti rect;
930  int a;
931 
932  /* draw items */
933  for (a = 0; a < data->items.totitem; a++) {
934  rcti rect_pre, rect_post;
935  ui_searchbox_butrect(&rect, data, a);
936 
937  rect_pre = rect;
938  rect_post = rect;
939 
940  rect_pre.xmax = rect_post.xmin = rect.xmin + ((rect.xmax - rect.xmin) / 4);
941 
942  /* widget itself */
943  /* NOTE: i18n messages extracting tool does the same, please keep it in sync. */
944  {
945  const int state = ((a == data->active) ? UI_ACTIVE : 0) | data->items.states[a];
946 
947  wmOperatorType *ot = data->items.pointers[a];
948  char text_pre[128];
949  char *text_pre_p = strstr(ot->idname, "_OT_");
950  if (text_pre_p == NULL) {
951  text_pre[0] = '\0';
952  }
953  else {
954  int text_pre_len;
955  text_pre_p += 1;
956  text_pre_len = BLI_strncpy_rlen(
957  text_pre, ot->idname, min_ii(sizeof(text_pre), text_pre_p - ot->idname));
958  text_pre[text_pre_len] = ':';
959  text_pre[text_pre_len + 1] = '\0';
960  str_tolower_titlecaps_ascii(text_pre, sizeof(text_pre));
961  }
962 
963  rect_pre.xmax += 4; /* sneaky, avoid showing ugly margin */
964  ui_draw_menu_item(&data->fstyle,
965  &rect_pre,
967  data->items.icons[a],
968  state,
970  NULL);
971  ui_draw_menu_item(&data->fstyle,
972  &rect_post,
973  data->items.names[a],
974  0,
975  state,
978  NULL);
979  }
980  }
981  /* indicate more */
982  if (data->items.more) {
983  ui_searchbox_butrect(&rect, data, data->items.maxitem - 1);
985  UI_icon_draw((BLI_rcti_size_x(&rect)) / 2, rect.ymin - 9, ICON_TRIA_DOWN);
987  }
988  if (data->items.offset) {
989  ui_searchbox_butrect(&rect, data, 0);
991  UI_icon_draw((BLI_rcti_size_x(&rect)) / 2, rect.ymax - 7, ICON_TRIA_UP);
993  }
994  }
995 }
996 
998 {
1000  ARegion *region = ui_searchbox_create_generic(C, butregion, search_but);
1001 
1003 
1004  return region;
1005 }
1006 
1008 {
1010 }
1011 
1013 {
1014  /* Currently unused. */
1015 }
1016 
1018 {
1020  ARegion *region = ui_searchbox_create_generic(C, butregion, search_but);
1021 
1022  if (false) {
1024  }
1025 
1026  return region;
1027 }
1028 
1029 /* sets red alert if button holds a string it can't find */
1030 /* XXX weak: search_func adds all partial matches... */
1032 {
1033  uiBut *but = &search_but->but;
1034  uiSearchItems *items;
1035  int x1;
1036 
1037  /* possibly very large lists (such as ID datablocks) only
1038  * only validate string RNA buts (not pointers) */
1039  if (but->rnaprop && RNA_property_type(but->rnaprop) != PROP_STRING) {
1040  return;
1041  }
1042 
1043  items = MEM_callocN(sizeof(uiSearchItems), "search items");
1044 
1045  /* setup search struct */
1046  items->maxitem = 10;
1047  items->maxstrlen = 256;
1048  items->names = MEM_callocN(items->maxitem * sizeof(void *), "search names");
1049  for (x1 = 0; x1 < items->maxitem; x1++) {
1050  items->names[x1] = MEM_callocN(but->hardmax + 1, "search names");
1051  }
1052 
1053  ui_searchbox_update_fn(but->block->evil_C, search_but, but->drawstr, items);
1054 
1055  if (!search_but->results_are_suggestions) {
1056  /* Only red-alert when we are sure of it, this can miss cases when >10 matches. */
1057  if (items->totitem == 0) {
1059  }
1060  else if (items->more == 0) {
1061  if (UI_search_items_find_index(items, but->drawstr) == -1) {
1063  }
1064  }
1065  }
1066 
1067  for (x1 = 0; x1 < items->maxitem; x1++) {
1068  MEM_freeN(items->names[x1]);
1069  }
1070  MEM_freeN(items->names);
1071  MEM_freeN(items);
1072 }
1073 
struct ScrArea * CTX_wm_area(const bContext *C)
Definition: context.c:714
struct bScreen * CTX_wm_screen(const bContext *C)
Definition: context.c:709
struct wmWindow * CTX_wm_window(const bContext *C)
Definition: context.c:699
#define BLI_assert(a)
Definition: BLI_assert.h:58
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:172
MINLINE int min_ii(int a, int b)
void BLI_rctf_translate(struct rctf *rect, float x, float y)
Definition: rct.c:604
BLI_INLINE int BLI_rcti_size_y(const struct rcti *rct)
Definition: BLI_rect.h:157
BLI_INLINE float BLI_rctf_cent_y(const struct rctf *rct)
Definition: BLI_rect.h:148
void BLI_rcti_translate(struct rcti *rect, int x, int y)
Definition: rct.c:597
bool BLI_rcti_isect_pt(const struct rcti *rect, const int x, const int y)
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition: BLI_rect.h:153
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition: BLI_rect.h:161
void BLI_rcti_rctf_copy(struct rcti *dst, const struct rctf *src)
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition: BLI_rect.h:165
size_t BLI_strncpy_rlen(char *__restrict dst, const char *__restrict src, const size_t maxncpy) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: string.c:187
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, const size_t maxncpy) ATTR_NONNULL()
Definition: string.c:108
#define STREQLEN(a, b, n)
#define UNUSED(x)
#define ELEM(...)
#define STREQ(a, b)
#define CTX_IFACE_(context, msgid)
#define BLT_I18NCONTEXT_OPERATOR_DEFAULT
ID and Library types, which are fundamental for sdna.
@ RGN_TYPE_TEMPORARY
@ USER_TOOLTIPS
void ED_region_floating_init(struct ARegion *region)
Definition: area.c:2002
void ED_region_tag_redraw(struct ARegion *region)
Definition: area.c:667
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint y
@ GPU_BLEND_NONE
Definition: GPU_state.h:55
@ GPU_BLEND_ALPHA
Definition: GPU_state.h:57
void GPU_blend(eGPUBlend blend)
Definition: gpu_state.cc:55
Read Guarded memory(de)allocation.
@ PROP_STRING
Definition: RNA_types.h:76
#define C
Definition: RandGen.cpp:39
#define UI_UNIT_Y
@ UI_BUT_HAS_SHORTCUT
Definition: UI_interface.h:295
void UI_fontstyle_set(const struct uiFontStyle *fs)
@ UI_BUT_REDALERT
Definition: UI_interface.h:204
@ UI_BUT_DISABLED
Definition: UI_interface.h:199
@ UI_BUT_INACTIVE
Definition: UI_interface.h:206
@ UI_BUT_HAS_SEP_CHAR
Definition: UI_interface.h:225
@ UI_BUT_VALUE_CLEAR
Definition: UI_interface.h:231
#define UI_SEP_CHAR
Definition: UI_interface.h:86
const struct uiStyle * UI_style_get(void)
#define AUTOCOMPLETE_NO_MATCH
void UI_but_drawflag_enable(uiBut *but, int flag)
Definition: interface.c:6092
AutoComplete * UI_autocomplete_begin(const char *startname, size_t maxlen)
Definition: interface.c:4782
int UI_autocomplete_end(AutoComplete *autocpl, char *autoname)
Definition: interface.c:4829
#define UI_DPI_ICON_SIZE
Definition: UI_interface.h:311
@ UI_BLOCK_SEARCH_MENU
Definition: UI_interface.h:152
struct ARegion * UI_region_searchbox_region_get(const struct ARegion *button_region)
#define UI_UNIT_X
@ UI_BTYPE_SEARCH_MENU
Definition: UI_interface.h:376
void UI_autocomplete_update_name(AutoComplete *autocpl, const char *name)
Definition: interface.c:4795
void UI_but_flag_enable(uiBut *but, int flag)
Definition: interface.c:6077
void UI_icon_draw(float x, float y, int icon_id)
void UI_view2d_view_to_region_rcti(const struct View2D *v2d, const struct rctf *rect_src, struct rcti *rect_dst) ATTR_NONNULL()
float UI_view2d_view_to_region_y(const struct View2D *v2d, float y)
unsigned int U
Definition: btGjkEpa3.h:78
void reset()
clear internal cached data and reset random seed
#define str(s)
static bool is_inside(int x, int y, int cols, int rows)
Definition: filesel.c:663
uint col
int ui_but_string_get_max_length(uiBut *but)
Definition: interface.c:2592
void ui_fontscale(short *points, float aspect)
Definition: interface.c:1943
void ui_pan_to_scroll(const wmEvent *event, int *type, int *val)
void ui_icon_ensure_deferred(const bContext *C, const int icon_id, const bool big)
#define UI_POPUP_MENU_TOP
void ui_draw_menu_item(const struct uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, int state, uiMenuItemSeparatorType separator_type, int *r_xmax)
void ui_draw_preview_item(const struct uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, int state)
void ui_draw_widget_menu_back(const rcti *rect, bool use_shadow)
#define UI_POPUP_MARGIN
@ UI_ACTIVE
uiMenuItemSeparatorType
@ UI_MENU_ITEM_SEPARATOR_NONE
@ UI_MENU_ITEM_SEPARATOR_HINT
@ UI_MENU_ITEM_SEPARATOR_SHORTCUT
int UI_searchbox_size_x(void)
int ui_searchbox_find_index(ARegion *region, const char *name)
int UI_searchbox_size_y(void)
void ui_searchbox_update(bContext *C, ARegion *region, uiBut *but, const bool reset)
ARegion * ui_searchbox_create_operator(bContext *C, ARegion *butregion, uiButSearch *search_but)
static void str_tolower_titlecaps_ascii(char *str, const size_t len)
static void ui_searchbox_region_free_cb(ARegion *region)
void ui_but_search_refresh(uiButSearch *search_but)
static void ui_searchbox_region_draw_cb(const bContext *C, ARegion *region)
bool UI_search_item_add(uiSearchItems *items, const char *name, void *poin, int iconid, int state, const uint8_t name_prefix_offset)
#define SEARCH_ITEMS
static void ui_searchbox_butrect(rcti *r_rect, uiSearchboxData *data, int itemnr)
static void ui_searchbox_select(bContext *C, ARegion *region, uiBut *but, int step)
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)
#define MENU_BORDER
void ui_searchbox_free(bContext *C, ARegion *region)
ARegion * ui_searchbox_create_generic(bContext *C, ARegion *butregion, uiButSearch *search_but)
bool ui_searchbox_apply(uiBut *but, ARegion *region)
static void ui_searchbox_update_fn(bContext *C, uiButSearch *search_but, const char *str, uiSearchItems *items)
bool ui_searchbox_inside(ARegion *region, int x, int y)
static void ui_searchbox_region_draw_cb__menu(const bContext *UNUSED(C), ARegion *UNUSED(region))
bool ui_searchbox_event(bContext *C, ARegion *region, uiBut *but, ARegion *butregion, const wmEvent *event)
static void ui_searchbox_region_draw_cb__operator(const bContext *UNUSED(C), ARegion *region)
struct uiSearchboxData uiSearchboxData
ARegion * ui_searchbox_create_menu(bContext *C, ARegion *butregion, uiButSearch *search_but)
int UI_search_items_find_index(uiSearchItems *items, const char *name)
int ui_searchbox_autocomplete(bContext *C, ARegion *region, uiBut *but, char *str)
void ui_region_temp_remove(bContext *C, bScreen *screen, ARegion *region)
ARegion * ui_region_temp_add(bScreen *screen)
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:45
static ulong state[N]
static unsigned a[3]
Definition: RandGen.cpp:92
static void area(int d1, int d2, int e1, int e2, float weights[2])
bool active
all scheduled work for the GPU.
PropertyType RNA_property_type(PropertyRNA *prop)
Definition: rna_access.c:1155
unsigned char uint8_t
Definition: stdint.h:81
void(* draw)(const struct bContext *C, struct ARegion *region)
Definition: BKE_screen.h:169
void * regiondata
struct ARegionType * type
ListBase uiblocks
float xmax
Definition: DNA_vec_types.h:85
float xmin
Definition: DNA_vec_types.h:85
float ymax
Definition: DNA_vec_types.h:86
float ymin
Definition: DNA_vec_types.h:86
int ymin
Definition: DNA_vec_types.h:80
int ymax
Definition: DNA_vec_types.h:80
int xmin
Definition: DNA_vec_types.h:79
int xmax
Definition: DNA_vec_types.h:79
struct Panel * panel
void * evil_C
uiButSearchUpdateFn items_update_fn
bool results_are_suggestions
const char * item_sep_string
uiButSearchTooltipFn item_tooltip_fn
uiButSearchContextMenuFn item_context_menu_fn
char * editstr
eButType type
float hardmax
bool changed
uiBlock * block
struct wmOperatorType * optype
char drawstr[UI_MAX_DRAW_STR]
struct PropertyRNA * rnaprop
AutoComplete * autocpl
uiFontStyle widget
int y
Definition: WM_types.h:581
int x
Definition: WM_types.h:581
const char * idname
Definition: WM_types.h:723
uint len
@ MOUSEPAN
@ RIGHTMOUSE
@ EVT_DOWNARROWKEY
@ WHEELUPMOUSE
@ WHEELDOWNMOUSE
@ MOUSEMOVE
@ EVT_UPARROWKEY
wmOperatorType * ot
Definition: wm_files.c:3156
void wmOrtho2_region_pixelspace(const ARegion *region)
Definition: wm_subwindow.c:120
void WM_tooltip_clear(bContext *C, wmWindow *win)
Definition: wm_tooltip.c:94
void WM_tooltip_timer_init(bContext *C, wmWindow *win, ScrArea *area, ARegion *region, wmTooltipInitFn init)
Definition: wm_tooltip.c:76
int WM_window_pixels_x(const wmWindow *win)
Definition: wm_window.c:2130