Blender V5.0
interface_region_menu_popup.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2008 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
10
11#include <algorithm>
12#include <cstdarg>
13#include <cstdlib>
14#include <cstring>
15#include <functional>
16
17#include "MEM_guardedalloc.h"
18
19#include "DNA_userdef_types.h"
20
21#include "BLI_hash.hh"
22#include "BLI_listbase.h"
23#include "BLI_math_vector.h"
24#include "BLI_rect.h"
25#include "BLI_string_utf8.h"
26#include "BLI_utildefines.h"
27
28#include "BKE_context.hh"
29#include "BKE_report.hh"
30#include "BKE_screen.hh"
31
32#include "WM_api.hh"
33#include "WM_types.hh"
34
35#include "RNA_access.hh"
36
38
39#include "BLT_translation.hh"
40
41#include "ED_screen.hh"
42
43#include "interface_intern.hh"
45
48
49/* -------------------------------------------------------------------- */
52
54{
56
57 /* currently only RNA buttons */
58 return ((but->menu_step_func != nullptr) ||
59 (but->rnaprop && RNA_property_type(but->rnaprop) == PROP_ENUM));
60}
61
62int ui_but_menu_step(uiBut *but, int direction)
63{
64 if (ui_but_menu_step_poll(but)) {
65 if (but->menu_step_func) {
66 return but->menu_step_func(
67 static_cast<bContext *>(but->block->evil_C), direction, but->poin);
68 }
69
70 const int curval = RNA_property_enum_get(&but->rnapoin, but->rnaprop);
71 return RNA_property_enum_step(static_cast<bContext *>(but->block->evil_C),
72 &but->rnapoin,
73 but->rnaprop,
74 curval,
75 direction);
76 }
77
78 printf("%s: cannot cycle button '%s'\n", __func__, but->str.c_str());
79 return 0;
80}
81
83
84/* -------------------------------------------------------------------- */
92
93static uint ui_popup_string_hash(const StringRef str, const bool use_sep)
94{
95 /* sometimes button contains hotkey, sometimes not, strip for proper compare */
96 const size_t sep_index = use_sep ? str.find_first_of(UI_SEP_CHAR) : StringRef::not_found;
97 const StringRef before_hotkey = sep_index == StringRef::not_found ? str :
98 str.substr(0, sep_index);
99
100 return blender::get_default_hash(before_hotkey);
101}
102
107
108/* but == nullptr read, otherwise set */
110{
111 static uint mem[256];
112 static bool first = true;
113
114 const uint hash = block->puphash;
115 const uint hash_mod = hash & 255;
116
117 if (first) {
118 /* init */
119 memset(mem, -1, sizeof(mem));
120 first = false;
121 }
122
123 if (but) {
124 /* set */
125 mem[hash_mod] = ui_popup_string_hash(but->str, but->flag & UI_BUT_HAS_SEP_CHAR);
126 return nullptr;
127 }
128
129 /* get */
130 for (const std::unique_ptr<uiBut> &but_iter : block->buttons) {
131 /* Prevent labels (typically headings), from being returned in the case the text
132 * happens to matches one of the menu items.
133 * Skip separators too as checking them is redundant. */
134 if (ELEM(but_iter->type, ButType::Label, ButType::Sepr, ButType::SeprLine)) {
135 continue;
136 }
137 if (mem[hash_mod] == ui_popup_string_hash(but_iter->str, but_iter->flag & UI_BUT_HAS_SEP_CHAR))
138 {
139 return but_iter.get();
140 }
141 }
142
143 return nullptr;
144}
145
147{
148 return ui_popup_menu_memory__internal(block, nullptr);
149}
150
152{
154}
155
157
158/* -------------------------------------------------------------------- */
161
167
168 /* Menu hash is created from this, to keep a memory of recently opened menus. */
170
171 int mx, my;
173
174 std::function<void(bContext *C, uiLayout *layout)> menu_func;
175};
176
182 uiPopupMenu *pup,
183 const StringRef title,
184 const StringRef block_name)
185{
186 const uiStyle *style = UI_style_get_dpi();
187
188 pup->block = UI_block_begin(C, nullptr, block_name, blender::ui::EmbossType::Pulldown);
189
190 /* A title is only provided when a Menu has a label, this is not always the case, see e.g.
191 * `VIEW3D_MT_edit_mesh_context_menu` -- this specifies its own label inside the draw function
192 * depending on vertex/edge/face mode. We still want to flag the uiBlock (but only insert into
193 * the `puphash` if we have a title provided). Choosing an entry in a menu will still handle
194 * `puphash` later (see `button_activate_exit`) though multiple menus without a label might fight
195 * for the same storage of the menu memory. Using `idname` instead (or in combination with the
196 * label) for the hash could be investigated to solve this. */
198 if (!title.is_empty()) {
199 pup->block->puphash = ui_popup_menu_hash(title);
200 }
204 0,
205 0,
206 200,
207 0,
209 style);
210
211 /* NOTE: this intentionally differs from the menu & sub-menu default because many operators
212 * use popups like this to select one of their options -
213 * where having invoke doesn't make sense.
214 * When the menu was opened from a button, use invoke still for compatibility. This used to be
215 * the default and changing now could cause issues. */
216 const blender::wm::OpCallContext opcontext = pup->but ?
219
220 pup->layout->operator_context_set(opcontext);
221
222 if (pup->but) {
223 if (pup->but->context) {
224 pup->layout->context_copy(pup->but->context);
225 }
226 }
227}
228
229static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, void *arg_pup)
230{
231 uiPopupMenu *pup = static_cast<uiPopupMenu *>(arg_pup);
232
233 int minwidth = 0;
234
235 if (!pup->layout) {
236 ui_popup_menu_create_block(C, pup, pup->title, __func__);
237
238 if (pup->menu_func) {
239 pup->block->handle = handle;
240 pup->menu_func(C, pup->layout);
241 pup->block->handle = nullptr;
242 }
243
244 if (pup->layout->ui_units_x() != 0.0f) {
245 /* Use the minimum width from the layout if it's set. */
246 minwidth = pup->layout->ui_units_x() * UI_UNIT_X;
247 }
248
249 pup->layout = nullptr;
250 }
251
252 /* Find block minimum width. */
253 if (minwidth) {
254 /* Skip. */
255 }
256 else if (pup->but) {
257 /* Minimum width to enforce. */
258 if (!pup->but->drawstr.empty()) {
259 minwidth = BLI_rctf_size_x(&pup->but->rect);
260 }
261 else {
262 /* For buttons with no text, use the minimum (typically icon only). */
263 minwidth = UI_MENU_WIDTH_MIN;
264 }
265 }
266 else {
267 minwidth = UI_MENU_WIDTH_MIN;
268 }
269
270 /* Find block direction. */
271 char direction;
272 if (pup->but) {
273 if (pup->block->direction != 0) {
274 /* allow overriding the direction from menu_func */
275 direction = pup->block->direction;
276 }
277 else {
278 direction = UI_DIR_DOWN;
279 }
280 }
281 else {
282 direction = UI_DIR_DOWN;
283 }
284
285 bool flip = (direction == UI_DIR_DOWN);
286
287 uiBlock *block = pup->block;
288
289 /* in some cases we create the block before the region,
290 * so we set it delayed here if necessary */
291 if (BLI_findindex(&handle->region->runtime->uiblocks, block) == -1) {
292 UI_block_region_set(block, handle->region);
293 }
294
295 block->direction = direction;
296
298
300
301 if (pup->popup) {
302 int offset[2] = {0, 0};
303
304 uiBut *but_activate = nullptr;
307 UI_block_direction_set(block, direction);
308
309 /* offset the mouse position, possibly based on earlier selection */
310 if (!handle->refresh) {
311 uiBut *bt;
312 if ((block->flag & UI_BLOCK_POPUP_MEMORY) && (bt = ui_popup_menu_memory_get(block))) {
313 /* position mouse on last clicked item, at 0.8*width of the
314 * button, so it doesn't overlap the text too much, also note
315 * the offset is negative because we are inverse moving the
316 * block to be under the mouse */
317 offset[0] = -(bt->rect.xmin + 0.8f * BLI_rctf_size_x(&bt->rect));
318 offset[1] = -(bt->rect.ymin + 0.5f * UI_UNIT_Y);
319
320 if (ui_but_is_editable(bt)) {
321 but_activate = bt;
322 }
323 }
324 else {
325 /* position mouse at 0.8*width of the button and below the tile
326 * on the first item */
327 offset[0] = 0;
328 for (const std::unique_ptr<uiBut> &but_iter : block->buttons) {
329 offset[0] = min_ii(offset[0],
330 -(but_iter->rect.xmin + 0.8f * BLI_rctf_size_x(&but_iter->rect)));
331 }
332
333 offset[1] = 2.1 * UI_UNIT_Y;
334
335 for (const std::unique_ptr<uiBut> &but_iter : block->buttons) {
336 if (ui_but_is_editable(but_iter.get())) {
337 but_activate = but_iter.get();
338 break;
339 }
340 }
341 }
342 copy_v2_v2_int(handle->prev_bounds_offset, offset);
343 }
344 else {
345 copy_v2_v2_int(offset, handle->prev_bounds_offset);
346 }
347
348 /* in rare cases this is needed since moving the popup
349 * to be within the window bounds may move it away from the mouse,
350 * This ensures we set an item to be active. */
351 if (but_activate) {
352 ARegion *region = CTX_wm_region(C);
353 if (region && region->regiontype == RGN_TYPE_TOOLS && but_activate->block &&
354 (but_activate->block->flag & UI_BLOCK_POPUP_HOLD))
355 {
356 /* In Toolbars, highlight the button with select color. */
357 but_activate->flag |= UI_SELECT_DRAW;
358 }
359 ui_but_activate_over(C, handle->region, but_activate);
360 }
361
362 block->minbounds = minwidth;
363 UI_block_bounds_set_menu(block, 1, offset);
364 }
365 else {
366 /* for a header menu we set the direction automatic */
367 if (!pup->slideout && flip) {
368 ARegion *region = CTX_wm_region(C);
369 if (region) {
370 if (RGN_TYPE_IS_HEADER_ANY(region->regiontype)) {
373 }
374 }
375 }
376 }
377
378 block->minbounds = minwidth;
379 UI_block_bounds_set_text(block, 3.0f * UI_UNIT_X);
380 }
381
382 /* if menu slides out of other menu, override direction */
383 if (pup->slideout) {
385 }
386
387 return pup->block;
388}
389
390static void ui_block_free_func_POPUP(void *arg_pup)
391{
392 uiPopupMenu *pup = static_cast<uiPopupMenu *>(arg_pup);
393 MEM_delete(pup);
394}
395
397 bContext *C,
398 ARegion *butregion,
399 uiBut *but,
400 const char *title,
401 std::function<void(bContext *, uiLayout *)> menu_func,
402 const bool can_refresh)
403{
404 wmWindow *window = CTX_wm_window(C);
405
406 uiPopupMenu *pup = MEM_new<uiPopupMenu>(__func__);
407 pup->title = title;
408 /* menu is created from a callback */
409 pup->menu_func = menu_func;
410 if (but) {
411 pup->slideout = ui_block_is_menu(but->block);
412 pup->but = but;
413
414 if (but->type == ButType::Pulldown) {
416 status.item(IFACE_("Search"), ICON_EVENT_SPACEKEY);
417 }
418 }
419
420 if (!but) {
421 /* no button to start from, means we are a popup */
422 pup->mx = window->eventstate->xy[0];
423 pup->my = window->eventstate->xy[1];
424 pup->popup = true;
425 }
427 C, butregion, but, nullptr, ui_block_func_POPUP, pup, ui_block_free_func_POPUP, can_refresh);
428
429 if (!but) {
430 handle->popup = true;
431
432 UI_popup_handlers_add(C, &window->modalhandlers, handle, 0);
434 }
435
436 return handle;
437}
438
440 bContext *C, ARegion *butregion, uiBut *but, uiMenuCreateFunc menu_func, void *arg)
441{
443 C,
444 butregion,
445 but,
446 nullptr,
447 [menu_func, arg](bContext *C, uiLayout *layout) { menu_func(C, layout, arg); },
448 false);
449}
450
452
453/* -------------------------------------------------------------------- */
456
457static void create_title_button(uiLayout *layout, const char *title, int icon)
458{
459 uiBlock *block = layout->block();
460 char titlestr[256];
461
462 if (icon) {
463 SNPRINTF_UTF8(titlestr, " %s", title);
464 uiDefIconTextBut(block, ButType::Label, 0, icon, titlestr, 0, 0, 200, UI_UNIT_Y, nullptr, "");
465 }
466 else {
467 uiBut *but = uiDefBut(
468 block, ButType::Label, 0, title, 0, 0, 200, UI_UNIT_Y, nullptr, 0.0, 0.0, "");
470 }
471
472 layout->separator();
473}
474
476 const char *title,
477 const char *block_name,
478 int icon)
479{
480 uiPopupMenu *pup = MEM_new<uiPopupMenu>(__func__);
481
482 pup->title = title;
483
484 ui_popup_menu_create_block(C, pup, title, block_name);
485
486 /* create in advance so we can let buttons point to retval already */
487 pup->block->handle = MEM_new<uiPopupBlockHandle>(__func__);
488
489 if (title[0]) {
490 create_title_button(pup->layout, title, icon);
491 }
492
493 return pup;
494}
495
496uiPopupMenu *UI_popup_menu_begin(bContext *C, const char *title, int icon)
497{
498 return UI_popup_menu_begin_ex(C, title, __func__, icon);
499}
500
501void UI_popup_menu_but_set(uiPopupMenu *pup, ARegion *butregion, uiBut *but)
502{
503 pup->but = but;
504 pup->butregion = butregion;
505}
506
508{
509 wmWindow *window = CTX_wm_window(C);
510
511 pup->popup = true;
512 pup->mx = window->eventstate->xy[0];
513 pup->my = window->eventstate->xy[1];
514
515 uiBut *but = nullptr;
516 ARegion *butregion = nullptr;
517 if (pup->but) {
518 but = pup->but;
519 butregion = pup->butregion;
520 }
521
523 C, butregion, but, nullptr, ui_block_func_POPUP, pup, nullptr, false);
524 menu->popup = true;
525
526 UI_popup_handlers_add(C, &window->modalhandlers, menu, 0);
528
529 MEM_delete(pup);
530}
531
533{
534 if (!UI_block_is_empty_ex(pup->block, true)) {
535 UI_popup_menu_end(C, pup);
536 return true;
537 }
539 MEM_delete(pup->block->handle);
540 UI_block_free(C, pup->block);
541 MEM_delete(pup);
542 return false;
543}
544
546{
547 return pup->layout;
548}
549
551
552/* -------------------------------------------------------------------- */
555
557{
558 uiPopupMenu *pup = nullptr;
559 uiLayout *layout;
560
561 if (!CTX_wm_window(C)) {
562 return;
563 }
564
565 BKE_reports_lock(reports);
566
567 LISTBASE_FOREACH (Report *, report, &reports->list) {
568 int icon;
569 const char *msg, *msg_next;
570
571 if (report->type < reports->printlevel) {
572 continue;
573 }
574
575 if (pup == nullptr) {
576 char title[UI_MAX_DRAW_STR];
577 SNPRINTF_UTF8(title, "%s: %s", RPT_("Report"), report->typestr);
578 /* popup_menu stuff does just what we need (but pass meaningful block name) */
579 pup = UI_popup_menu_begin_ex(C, title, __func__, ICON_NONE);
580 layout = UI_popup_menu_layout(pup);
581 }
582 else {
583 layout->separator();
584 }
585
586 /* split each newline into a label */
587 msg = report->message;
588 icon = UI_icon_from_report_type(report->type);
589 do {
590 char buf[UI_MAX_DRAW_STR];
591 msg_next = strchr(msg, '\n');
592 if (msg_next) {
593 msg_next++;
594 BLI_strncpy_utf8(buf, msg, std::min(sizeof(buf), size_t(msg_next - msg)));
595 msg = buf;
596 }
597 layout->label(msg, icon);
598 icon = ICON_NONE;
599 } while ((msg = msg_next) && *msg);
600 }
601
602 BKE_reports_unlock(reports);
603
604 if (pup) {
605 UI_popup_menu_end(C, pup);
606 }
607}
608
610 MenuType *mt,
611 const char *title,
612 const int icon)
613{
615 C,
616 nullptr,
617 nullptr,
618 title,
619 [mt, title, icon](bContext *C, uiLayout *layout) -> void {
620 if (title && title[0]) {
621 create_title_button(layout, title, icon);
622 }
623 ui_item_menutype_func(C, layout, mt);
624 },
625 true);
626
627 STRNCPY_UTF8(handle->menu_idname, mt->idname);
628
630 if (bool(mt->flag & MenuTypeFlag::SearchOnKeyPress)) {
631 status.range(IFACE_("Search"), ICON_EVENT_A, ICON_EVENT_Z);
632 }
633 else if (mt->idname[0]) {
634 status.item(IFACE_("Search"), ICON_EVENT_SPACEKEY);
635 }
636}
637
639{
640 MenuType *mt = WM_menutype_find(idname, true);
641
642 if (mt == nullptr) {
643 BKE_reportf(reports, RPT_ERROR, "Menu \"%s\" not found", idname);
644 return OPERATOR_CANCELLED;
645 }
646
647 if (WM_menutype_poll(C, mt) == false) {
648 /* cancel but allow event to pass through, just like operators do */
650 }
651 /* For now always recreate menus on redraw that were invoked with this function. Maybe we want to
652 * make that optional somehow. */
653 const bool allow_refresh = true;
654
655 const char *title = CTX_IFACE_(mt->translation_context, mt->label);
656 if (allow_refresh) {
657 ui_popup_menu_create_from_menutype(C, mt, title, ICON_NONE);
658 }
659 else {
660 /* If no refresh is needed, create the block directly. */
661 uiPopupMenu *pup = UI_popup_menu_begin(C, title, ICON_NONE);
662 uiLayout *layout = UI_popup_menu_layout(pup);
663 UI_menutype_draw(C, mt, layout);
664 UI_popup_menu_end(C, pup);
665 }
666
667 return OPERATOR_INTERFACE;
668}
669
671
672/* -------------------------------------------------------------------- */
675
677 bContext *C, uiBlockCreateFunc func, void *arg, uiFreeArgFunc arg_free, const bool can_refresh)
678{
679 wmWindow *window = CTX_wm_window(C);
680
682 C, nullptr, nullptr, func, nullptr, arg, arg_free, can_refresh);
683 handle->popup = true;
684
685 /* Clear the status bar. */
687 status.item(" ", ICON_NONE);
688
689 UI_popup_handlers_add(C, &window->modalhandlers, handle, 0);
691 C, handle->region, static_cast<uiBlock *>(handle->region->runtime->uiblocks.first));
693}
694
696{
697 UI_popup_block_invoke_ex(C, func, arg, arg_free, true);
698}
699
702 uiBlockHandleFunc popup_func,
703 uiBlockCancelFunc cancel_func,
704 void *arg,
705 wmOperator *op)
706{
707 wmWindow *window = CTX_wm_window(C);
708
710 C, nullptr, nullptr, func, nullptr, arg, nullptr, true);
711 handle->popup = true;
712 handle->retvalue = 1;
713
714 handle->popup_op = op;
715 handle->popup_arg = arg;
716 handle->popup_func = popup_func;
717 handle->cancel_func = cancel_func;
718 // handle->opcontext = opcontext;
719
720 /* Clear the status bar. */
722 status.item(" ", ICON_NONE);
723
724 UI_popup_handlers_add(C, &window->modalhandlers, handle, 0);
726 C, handle->region, static_cast<uiBlock *>(handle->region->runtime->uiblocks.first));
728}
729
730static void popup_block_template_close_cb(bContext *C, void *arg1, void * /*arg2*/)
731{
732 uiBlock *block = (uiBlock *)arg1;
733
734 uiPopupBlockHandle *handle = block->handle;
735 if (handle == nullptr) {
736 printf("Error: used outside of a popup!\n");
737 return;
738 }
739
740 wmWindow *win = CTX_wm_window(C);
742
743 if (handle->cancel_func) {
744 handle->cancel_func(C, handle->popup_arg);
745 }
746
747 UI_popup_block_close(C, win, block);
748}
749
751{
752 if (block->flag & (UI_BLOCK_KEEP_OPEN | UI_BLOCK_POPOVER)) {
753 return true;
754 }
755 return false;
756}
757
759 const bool cancel_default,
760 blender::FunctionRef<uiBut *()> confirm_fn,
761 blender::FunctionRef<uiBut *()> cancel_fn)
762{
763#ifdef _WIN32
764 const bool windows_layout = true;
765#else
766 const bool windows_layout = false;
767#endif
768 blender::FunctionRef<uiBut *()> *button_functions[2];
769 if (windows_layout) {
770 ARRAY_SET_ITEMS(button_functions, &confirm_fn, &cancel_fn);
771 }
772 else {
773 ARRAY_SET_ITEMS(button_functions, &cancel_fn, &confirm_fn);
774 }
775
776 for (int i = 0; i < ARRAY_SIZE(button_functions); i++) {
777 blender::FunctionRef<uiBut *()> *but_fn = button_functions[i];
778 if (uiBut *but = (*but_fn)()) {
779 const bool is_cancel = (but_fn == &cancel_fn);
780 if ((block->flag & UI_BLOCK_LOOP) == 0) {
782 }
783 if (is_cancel == cancel_default) {
784 /* An active button shouldn't exist, if it does, never set another. */
787 }
788 }
789 }
790 }
791}
792
795 const std::optional<StringRef> confirm_text_opt,
796 const std::optional<StringRef> cancel_text_opt,
797 const int icon,
798 bool cancel_default,
799 PointerRNA *r_ptr)
800{
801 uiBlock *block = layout->block();
802
803 const StringRef confirm_text = confirm_text_opt.value_or(IFACE_("OK"));
804 const StringRef cancel_text = cancel_text_opt.value_or(IFACE_("Cancel"));
805
806 /* Use a split so both buttons are the same size. */
807 const bool show_confirm = !confirm_text.is_empty();
808 const bool show_cancel = !cancel_text.is_empty();
809 uiLayout *row = (show_confirm && show_cancel) ? &layout->split(0.5f, false) : layout;
810
811 /* When only one button is shown, make it default. */
812 if (!show_confirm) {
813 cancel_default = true;
814 }
815
816 auto confirm_fn = [&row, &ot, &confirm_text, &icon, &r_ptr, &show_confirm]() -> uiBut * {
817 if (!show_confirm) {
818 return nullptr;
819 }
820 uiBlock *block = row->block();
821 const uiBut *but_ref = block->last_but();
822 *r_ptr = row->op(ot, confirm_text, icon, row->operator_context(), UI_ITEM_NONE);
823
824 if (block->buttons.is_empty() || but_ref == block->buttons.last().get()) {
825 return nullptr;
826 }
827 return block->buttons.last().get();
828 };
829
830 auto cancel_fn = [&row, &cancel_text, &show_cancel]() -> uiBut * {
831 if (!show_cancel) {
832 return nullptr;
833 }
834 uiBlock *block = row->block();
835 uiBut *but = uiDefIconTextBut(block,
837 1,
838 ICON_NONE,
839 cancel_text,
840 0,
841 0,
842 UI_UNIT_X, /* Ignored, as a split is used. */
843 UI_UNIT_Y,
844 nullptr,
845 "");
846
847 return but;
848 };
849
850 UI_popup_block_template_confirm(block, cancel_default, confirm_fn, cancel_fn);
851}
852
853#if 0 /* UNUSED */
854void uiPupBlockOperator(bContext *C,
856 wmOperator *op,
858{
859 wmWindow *window = CTX_wm_window(C);
860
861 uiPopupBlockHandle *handle = ui_popup_block_create(C, nullptr, nullptr, func, nullptr, op, nullptr, true);
862 handle->popup = 1;
863 handle->retvalue = 1;
864
865 handle->popup_arg = op;
866 handle->popup_func = operator_cb;
867 handle->cancel_func = confirm_cancel_operator;
868 handle->opcontext = opcontext;
869
870 UI_popup_handlers_add(C, &window->modalhandlers, handle, 0);
872}
873#endif
874
876{
877 /* if loading new .blend while popup is open, window will be nullptr */
878 if (block->handle) {
879 if (win) {
880 const bScreen *screen = WM_window_get_active_screen(win);
881
884
885 /* In the case we have nested popups,
886 * closing one may need to redraw another, see: #48874 */
887 LISTBASE_FOREACH (ARegion *, region, &screen->regionbase) {
889 }
890 }
891 }
892
893 ED_workspace_status_text(C, nullptr);
894}
895
897{
898 LISTBASE_FOREACH (const ARegion *, region, &screen->regionbase) {
899 LISTBASE_FOREACH (const uiBlock *, block, &region->runtime->uiblocks) {
900 if (block->name == name) {
901 return true;
902 }
903 }
904 }
905 return false;
906}
907
908void UI_popup_menu_close(const uiBlock *block, const bool is_cancel)
909{
910 UI_popup_menu_retval_set(block, is_cancel ? UI_RETURN_CANCEL : UI_RETURN_OK, true);
911}
912
913void UI_popup_menu_close_from_but(const uiBut *but, const bool is_cancel)
914{
915 UI_popup_menu_close(but->block, is_cancel);
916}
917
wmWindow * CTX_wm_window(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
void BKE_reports_unlock(ReportList *reports)
Definition report.cc:136
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
@ RPT_ERROR
Definition BKE_report.hh:39
void BKE_reports_lock(ReportList *reports)
Definition report.cc:131
#define BLI_assert(a)
Definition BLI_assert.h:46
int BLI_findindex(const ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:586
#define LISTBASE_FOREACH(type, var, list)
MINLINE int min_ii(int a, int b)
MINLINE void copy_v2_v2_int(int r[2], const int a[2])
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition BLI_rect.h:202
#define SNPRINTF_UTF8(dst, format,...)
char * BLI_strncpy_utf8(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
#define STRNCPY_UTF8(dst, src)
unsigned int uint
#define ARRAY_SIZE(arr)
#define ARRAY_SET_ITEMS(...)
#define ELEM(...)
#define RPT_(msgid)
#define CTX_IFACE_(context, msgid)
#define IFACE_(msgid)
#define RGN_ALIGN_ENUM_FROM_MASK(align)
@ RGN_ALIGN_BOTTOM
@ RGN_TYPE_TOOLS
#define RGN_TYPE_IS_HEADER_ANY(regiontype)
@ OPERATOR_CANCELLED
@ OPERATOR_INTERFACE
@ OPERATOR_PASS_THROUGH
void ED_region_tag_refresh_ui(ARegion *region)
Definition area.cc:647
void ED_workspace_status_text(bContext *C, const char *str)
Definition area.cc:1024
Read Guarded memory(de)allocation.
@ PROP_ENUM
Definition RNA_types.hh:166
#define C
Definition RandGen.cpp:29
void UI_but_func_set(uiBut *but, std::function< void(bContext &)> func)
uiBut * uiDefIconTextBut(uiBlock *block, uiButTypeWithPointerType but_and_ptr_type, int retval, int icon, blender::StringRef str, int x, int y, short width, short height, void *poin, std::optional< blender::StringRef > tip)
#define UI_UNIT_Y
void UI_popup_menu_retval_set(const uiBlock *block, int retval, bool enable)
@ UI_BLOCK_NUMSELECT
@ UI_BLOCK_LOOP
@ UI_BLOCK_POPUP_MEMORY
@ UI_BLOCK_MOVEMOUSE_QUIT
@ UI_BLOCK_KEEP_OPEN
@ UI_BLOCK_POPOVER
@ UI_BLOCK_POPUP_HOLD
void UI_block_theme_style_set(uiBlock *block, char theme_style)
uiBlock * UI_block_begin(const bContext *C, ARegion *region, std::string name, blender::ui::EmbossType emboss)
int UI_icon_from_report_type(int type)
#define UI_SEP_CHAR
@ UI_BUT_ACTIVE_DEFAULT
@ UI_BUT_HAS_SEP_CHAR
const uiStyle * UI_style_get_dpi()
void(*)(bContext *C, void *arg1) uiBlockCancelFunc
bool UI_block_active_only_flagged_buttons(const bContext *C, ARegion *region, uiBlock *block)
bool UI_block_has_active_default_button(const uiBlock *block)
void(*)(bContext *C, void *arg, int event) uiBlockHandleFunc
uiBlock *(*)(bContext *C, ARegion *region, void *arg1) uiBlockCreateFunc
void UI_block_bounds_set_menu(uiBlock *block, int addval, const int bounds_offset[2])
Definition interface.cc:667
bool UI_block_is_empty_ex(const uiBlock *block, bool skip_title)
@ UI_RETURN_CANCEL
@ UI_RETURN_OK
void UI_block_bounds_set_text(uiBlock *block, int addval)
Definition interface.cc:647
void UI_popup_handlers_add(bContext *C, ListBase *handlers, uiPopupBlockHandle *popup, char flag)
void UI_block_free(const bContext *C, uiBlock *block)
void UI_block_region_set(uiBlock *block, ARegion *region)
void UI_block_direction_set(uiBlock *block, char direction)
@ UI_BLOCK_THEME_STYLE_POPUP
#define UI_UNIT_X
void UI_block_flag_enable(uiBlock *block, int flag)
@ UI_BUT_TEXT_LEFT
void UI_popup_handlers_remove(ListBase *handlers, uiPopupBlockHandle *popup)
uiBut * uiDefBut(uiBlock *block, uiButTypeWithPointerType but_and_ptr_type, int retval, blender::StringRef str, int x, int y, short width, short height, void *poin, float min, float max, std::optional< blender::StringRef > tip)
void UI_but_flag_enable(uiBut *but, int flag)
@ UI_DIR_DOWN
@ UI_DIR_RIGHT
@ UI_DIR_UP
#define UI_ITEM_NONE
void UI_menutype_draw(bContext *C, MenuType *mt, uiLayout *layout)
void(*)(void *arg) uiFreeArgFunc
void(*)(bContext *C, uiLayout *layout, void *arg1) uiMenuCreateFunc
#define UI_MAX_DRAW_STR
const T & last(const int64_t n=0) const
bool is_empty() const
static constexpr int64_t not_found
constexpr bool is_empty() const
#define str(s)
#define printf(...)
void ui_but_activate_over(bContext *C, ARegion *region, uiBut *but)
uiBut * ui_popup_menu_memory_get(uiBlock *block)
void ui_item_menutype_func(bContext *C, uiLayout *layout, void *arg_mt)
uiPopupBlockHandle * ui_popup_block_create(bContext *C, ARegion *butregion, uiBut *but, uiBlockCreateFunc create_func, uiBlockHandleCreateFunc handle_create_func, void *arg, uiFreeArgFunc arg_free, bool can_refresh)
void ui_popup_block_free(bContext *C, uiPopupBlockHandle *handle)
bool ui_block_is_menu(const uiBlock *block) ATTR_WARN_UNUSED_RESULT
#define UI_MENU_PADDING
bool ui_but_is_editable(const uiBut *but) ATTR_WARN_UNUSED_RESULT
@ UI_SELECT_DRAW
#define UI_MENU_WIDTH_MIN
bool UI_popup_block_name_exists(const bScreen *screen, const blender::StringRef name)
void UI_popup_block_template_confirm_op(uiLayout *layout, wmOperatorType *ot, const std::optional< StringRef > confirm_text_opt, const std::optional< StringRef > cancel_text_opt, const int icon, bool cancel_default, PointerRNA *r_ptr)
void UI_popup_block_template_confirm(uiBlock *block, const bool cancel_default, blender::FunctionRef< uiBut *()> confirm_fn, blender::FunctionRef< uiBut *()> cancel_fn)
uiBut * ui_popup_menu_memory_get(uiBlock *block)
bool ui_but_menu_step_poll(const uiBut *but)
void UI_popup_menu_but_set(uiPopupMenu *pup, ARegion *butregion, uiBut *but)
static uiBlock * ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, void *arg_pup)
uiPopupMenu * UI_popup_menu_begin(bContext *C, const char *title, int icon)
void UI_popup_block_invoke(bContext *C, uiBlockCreateFunc func, void *arg, uiFreeArgFunc arg_free)
void UI_popup_menu_close(const uiBlock *block, const bool is_cancel)
void UI_popup_block_ex(bContext *C, uiBlockCreateFunc func, uiBlockHandleFunc popup_func, uiBlockCancelFunc cancel_func, void *arg, wmOperator *op)
void UI_popup_menu_end(bContext *C, uiPopupMenu *pup)
static void ui_popup_menu_create_from_menutype(bContext *C, MenuType *mt, const char *title, const int icon)
uiPopupBlockHandle * ui_popup_menu_create(bContext *C, ARegion *butregion, uiBut *but, uiMenuCreateFunc menu_func, void *arg)
void UI_popup_block_close(bContext *C, wmWindow *win, uiBlock *block)
int ui_but_menu_step(uiBut *but, int direction)
void UI_popup_block_invoke_ex(bContext *C, uiBlockCreateFunc func, void *arg, uiFreeArgFunc arg_free, const bool can_refresh)
void UI_popup_menu_reports(bContext *C, ReportList *reports)
static void popup_block_template_close_cb(bContext *C, void *arg1, void *)
uiLayout * UI_popup_menu_layout(uiPopupMenu *pup)
uint ui_popup_menu_hash(const StringRef str)
static void create_title_button(uiLayout *layout, const char *title, int icon)
bool UI_popup_menu_end_or_cancel(bContext *C, uiPopupMenu *pup)
static void ui_popup_menu_create_block(bContext *C, uiPopupMenu *pup, const StringRef title, const StringRef block_name)
static uint ui_popup_string_hash(const StringRef str, const bool use_sep)
bool UI_popup_block_template_confirm_is_supported(const uiBlock *block)
void ui_popup_menu_memory_set(uiBlock *block, uiBut *but)
void UI_popup_menu_close_from_but(const uiBut *but, const bool is_cancel)
static uiBut * ui_popup_menu_memory__internal(uiBlock *block, uiBut *but)
wmOperatorStatus UI_popup_menu_invoke(bContext *C, const char *idname, ReportList *reports)
uiPopupMenu * UI_popup_menu_begin_ex(bContext *C, const char *title, const char *block_name, int icon)
static uiPopupBlockHandle * ui_popup_menu_create_impl(bContext *C, ARegion *butregion, uiBut *but, const char *title, std::function< void(bContext *, uiLayout *)> menu_func, const bool can_refresh)
static void ui_block_free_func_POPUP(void *arg_pup)
int2 block_layout_resolve(uiBlock *block)
uiLayout & block_layout(uiBlock *block, LayoutDirection direction, LayoutType type, int x, int y, int size, int em, int padding, const uiStyle *style)
uint64_t get_default_hash(const T &v, const Args &...args)
Definition BLI_hash.hh:233
#define hash
Definition noise_c.cc:154
const char * name
const int status
PropertyType RNA_property_type(PropertyRNA *prop)
int RNA_property_enum_step(const bContext *C, PointerRNA *ptr, PropertyRNA *prop, int from_value, int step)
int RNA_property_enum_get(PointerRNA *ptr, PropertyRNA *prop)
ARegionRuntimeHandle * runtime
MenuTypeFlag flag
char label[BKE_ST_MAXNAME]
char idname[BKE_ST_MAXNAME]
char translation_context[BKE_ST_MAXNAME]
ListBase list
Definition BKE_report.hh:75
ListBase regionbase
float xmin
float ymin
blender::Vector< std::unique_ptr< uiBut > > buttons
uiPopupBlockHandle * handle
std::string name
uiBut * last_but() const
Definition interface.cc:309
PropertyRNA * rnaprop
ButType type
uiBlock * block
const bContextStore * context
std::string drawstr
std::string str
PointerRNA rnapoin
uiMenuStepFunc menu_step_func
blender::wm::OpCallContext operator_context() const
uiBlock * block() const
void operator_context_set(blender::wm::OpCallContext opcontext)
void label(blender::StringRef name, int icon)
void separator(float factor=1.0f, LayoutSeparatorType type=LayoutSeparatorType::Auto)
uiLayout & split(float percentage, bool align)
void context_copy(const bContextStore *context)
PointerRNA op(wmOperatorType *ot, std::optional< blender::StringRef > name, int icon, blender::wm::OpCallContext context, eUI_Item_Flag flag)
float ui_units_x() const
void(* popup_func)(bContext *C, void *arg, int event)
void(* cancel_func)(bContext *C, void *arg)
std::function< void(bContext *C, uiLayout *layout)> menu_func
int xy[2]
Definition WM_types.hh:761
struct wmEvent * eventstate
i
Definition text_draw.cc:230
void WM_event_add_mousemove(wmWindow *win)
wmOperatorType * ot
Definition wm_files.cc:4237
MenuType * WM_menutype_find(const StringRef idname, bool quiet)
bool WM_menutype_poll(bContext *C, MenuType *mt)
bScreen * WM_window_get_active_screen(const wmWindow *win)