Blender V4.3
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
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.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
37#include "UI_interface.hh"
38
39#include "BLT_translation.hh"
40
41#include "ED_screen.hh"
42
43#include "interface_intern.hh"
45
47
48/* -------------------------------------------------------------------- */
53{
55
56 /* currently only RNA buttons */
57 return ((but->menu_step_func != nullptr) ||
58 (but->rnaprop && RNA_property_type(but->rnaprop) == PROP_ENUM));
59}
60
61int ui_but_menu_step(uiBut *but, int direction)
62{
63 if (ui_but_menu_step_poll(but)) {
64 if (but->menu_step_func) {
65 return but->menu_step_func(
66 static_cast<bContext *>(but->block->evil_C), direction, but->poin);
67 }
68
69 const int curval = RNA_property_enum_get(&but->rnapoin, but->rnaprop);
70 return RNA_property_enum_step(static_cast<bContext *>(but->block->evil_C),
71 &but->rnapoin,
72 but->rnaprop,
73 curval,
74 direction);
75 }
76
77 printf("%s: cannot cycle button '%s'\n", __func__, but->str.c_str());
78 return 0;
79}
80
83/* -------------------------------------------------------------------- */
92static uint ui_popup_string_hash(const StringRef str, const bool use_sep)
93{
94 /* sometimes button contains hotkey, sometimes not, strip for proper compare */
95 const size_t sep_index = use_sep ? str.find_first_of(UI_SEP_CHAR) : StringRef::not_found;
96 const StringRef before_hotkey = sep_index == StringRef::not_found ? str :
97 str.substr(0, sep_index);
98
99 return blender::get_default_hash(before_hotkey);
100}
101
106
107/* but == nullptr read, otherwise set */
109{
110 static uint mem[256];
111 static bool first = true;
112
113 const uint hash = block->puphash;
114 const uint hash_mod = hash & 255;
115
116 if (first) {
117 /* init */
118 memset(mem, -1, sizeof(mem));
119 first = false;
120 }
121
122 if (but) {
123 /* set */
124 mem[hash_mod] = ui_popup_string_hash(but->str, but->flag & UI_BUT_HAS_SEP_CHAR);
125 return nullptr;
126 }
127
128 /* get */
129 LISTBASE_FOREACH (uiBut *, but_iter, &block->buttons) {
130 /* Prevent labels (typically headings), from being returned in the case the text
131 * happens to matches one of the menu items.
132 * Skip separators too as checking them is redundant. */
133 if (ELEM(but_iter->type, UI_BTYPE_LABEL, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE)) {
134 continue;
135 }
136 if (mem[hash_mod] == ui_popup_string_hash(but_iter->str, but_iter->flag & UI_BUT_HAS_SEP_CHAR))
137 {
138 return but_iter;
139 }
140 }
141
142 return nullptr;
143}
144
146{
147 return ui_popup_menu_memory__internal(block, nullptr);
148}
149
151{
153}
154
157/* -------------------------------------------------------------------- */
166
167 /* Menu hash is created from this, to keep a memory of recently opened menus. */
169
170 int mx, my;
172
173 std::function<void(bContext *C, uiLayout *layout)> menu_func;
174};
175
181 uiPopupMenu *pup,
182 const StringRef title,
183 const StringRef block_name)
184{
185 const uiStyle *style = UI_style_get_dpi();
186
187 pup->block = UI_block_begin(C, nullptr, block_name, UI_EMBOSS_PULLDOWN);
188
189 /* A title is only provided when a Menu has a label, this is not always the case, see e.g.
190 * `VIEW3D_MT_edit_mesh_context_menu` -- this specifies its own label inside the draw function
191 * depending on vertex/edge/face mode. We still want to flag the uiBlock (but only insert into
192 * the `puphash` if we have a title provided). Choosing an entry in a menu will still handle
193 * `puphash` later (see `button_activate_exit`) though multiple menus without a label might fight
194 * for the same storage of the menu memory. Using idname instead (or in combination with the
195 * label) for the hash could be looked at to solve this. */
197 if (!title.is_empty()) {
198 pup->block->puphash = ui_popup_menu_hash(title);
199 }
200 pup->layout = UI_block_layout(
201 pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, UI_MENU_PADDING, style);
202
203 /* NOTE: this intentionally differs from the menu & sub-menu default because many operators
204 * use popups like this to select one of their options -
205 * where having invoke doesn't make sense.
206 * When the menu was opened from a button, use invoke still for compatibility. This used to be
207 * the default and changing now could cause issues. */
208 const wmOperatorCallContext opcontext = pup->but ? WM_OP_INVOKE_REGION_WIN :
210
211 uiLayoutSetOperatorContext(pup->layout, opcontext);
212
213 if (pup->but) {
214 if (pup->but->context) {
216 }
217 }
218}
219
220static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, void *arg_pup)
221{
222 uiPopupMenu *pup = static_cast<uiPopupMenu *>(arg_pup);
223
224 int minwidth = 0;
225
226 if (!pup->layout) {
227 ui_popup_menu_create_block(C, pup, pup->title, __func__);
228
229 if (pup->menu_func) {
230 pup->block->handle = handle;
231 pup->menu_func(C, pup->layout);
232 pup->block->handle = nullptr;
233 }
234
235 if (uiLayoutGetUnitsX(pup->layout) != 0.0f) {
236 /* Use the minimum width from the layout if it's set. */
237 minwidth = uiLayoutGetUnitsX(pup->layout) * UI_UNIT_X;
238 }
239
240 pup->layout = nullptr;
241 }
242
243 /* Find block minimum width. */
244 if (minwidth) {
245 /* Skip. */
246 }
247 else if (pup->but) {
248 /* Minimum width to enforce. */
249 if (!pup->but->drawstr.empty()) {
250 minwidth = BLI_rctf_size_x(&pup->but->rect);
251 }
252 else {
253 /* For buttons with no text, use the minimum (typically icon only). */
254 minwidth = UI_MENU_WIDTH_MIN;
255 }
256 }
257 else {
258 minwidth = UI_MENU_WIDTH_MIN;
259 }
260
261 /* Find block direction. */
262 char direction;
263 if (pup->but) {
264 if (pup->block->direction != 0) {
265 /* allow overriding the direction from menu_func */
266 direction = pup->block->direction;
267 }
268 else {
269 direction = UI_DIR_DOWN;
270 }
271 }
272 else {
273 direction = UI_DIR_DOWN;
274 }
275
276 bool flip = (direction == UI_DIR_DOWN);
277
278 uiBlock *block = pup->block;
279
280 /* in some cases we create the block before the region,
281 * so we set it delayed here if necessary */
282 if (BLI_findindex(&handle->region->uiblocks, block) == -1) {
283 UI_block_region_set(block, handle->region);
284 }
285
286 block->direction = direction;
287
288 int width, height;
289 UI_block_layout_resolve(block, &width, &height);
290
292
293 if (pup->popup) {
294 int offset[2] = {0, 0};
295
296 uiBut *but_activate = nullptr;
299 UI_block_direction_set(block, direction);
300
301 /* offset the mouse position, possibly based on earlier selection */
302 if (!handle->refresh) {
303 uiBut *bt;
304 if ((block->flag & UI_BLOCK_POPUP_MEMORY) && (bt = ui_popup_menu_memory_get(block))) {
305 /* position mouse on last clicked item, at 0.8*width of the
306 * button, so it doesn't overlap the text too much, also note
307 * the offset is negative because we are inverse moving the
308 * block to be under the mouse */
309 offset[0] = -(bt->rect.xmin + 0.8f * BLI_rctf_size_x(&bt->rect));
310 offset[1] = -(bt->rect.ymin + 0.5f * UI_UNIT_Y);
311
312 if (ui_but_is_editable(bt)) {
313 but_activate = bt;
314 }
315 }
316 else {
317 /* position mouse at 0.8*width of the button and below the tile
318 * on the first item */
319 offset[0] = 0;
320 LISTBASE_FOREACH (uiBut *, but_iter, &block->buttons) {
321 offset[0] = min_ii(offset[0],
322 -(but_iter->rect.xmin + 0.8f * BLI_rctf_size_x(&but_iter->rect)));
323 }
324
325 offset[1] = 2.1 * UI_UNIT_Y;
326
327 LISTBASE_FOREACH (uiBut *, but_iter, &block->buttons) {
328 if (ui_but_is_editable(but_iter)) {
329 but_activate = but_iter;
330 break;
331 }
332 }
333 }
334 copy_v2_v2_int(handle->prev_bounds_offset, offset);
335 }
336 else {
337 copy_v2_v2_int(offset, handle->prev_bounds_offset);
338 }
339
340 /* in rare cases this is needed since moving the popup
341 * to be within the window bounds may move it away from the mouse,
342 * This ensures we set an item to be active. */
343 if (but_activate) {
344 ARegion *region = CTX_wm_region(C);
345 if (region && region->regiontype == RGN_TYPE_TOOLS && but_activate->block &&
346 (but_activate->block->flag & UI_BLOCK_POPUP_HOLD))
347 {
348 /* In Toolbars, highlight the button with select color. */
349 but_activate->flag |= UI_SELECT_DRAW;
350 }
351 ui_but_activate_over(C, handle->region, but_activate);
352 }
353
354 block->minbounds = minwidth;
355 UI_block_bounds_set_menu(block, 1, offset);
356 }
357 else {
358 /* for a header menu we set the direction automatic */
359 if (!pup->slideout && flip) {
360 ARegion *region = CTX_wm_region(C);
361 if (region) {
362 if (RGN_TYPE_IS_HEADER_ANY(region->regiontype)) {
363 if (RGN_ALIGN_ENUM_FROM_MASK(region->alignment) == RGN_ALIGN_BOTTOM) {
365 }
366 }
367 }
368 }
369
370 block->minbounds = minwidth;
371 UI_block_bounds_set_text(block, 3.0f * UI_UNIT_X);
372 }
373
374 /* if menu slides out of other menu, override direction */
375 if (pup->slideout) {
377 }
378
379 return pup->block;
380}
381
382static void ui_block_free_func_POPUP(void *arg_pup)
383{
384 uiPopupMenu *pup = static_cast<uiPopupMenu *>(arg_pup);
385 MEM_delete(pup);
386}
387
389 bContext *C,
390 ARegion *butregion,
391 uiBut *but,
392 const char *title,
393 std::function<void(bContext *, uiLayout *)> menu_func,
394 const bool can_refresh)
395{
396 wmWindow *window = CTX_wm_window(C);
397
398 uiPopupMenu *pup = MEM_new<uiPopupMenu>(__func__);
399 pup->title = title;
400 /* menu is created from a callback */
401 pup->menu_func = menu_func;
402 if (but) {
403 pup->slideout = ui_block_is_menu(but->block);
404 pup->but = but;
405
406 if (but->type == UI_BTYPE_PULLDOWN) {
407 WorkspaceStatus status(C);
408 status.item(IFACE_("Search"), ICON_EVENT_SPACEKEY);
409 }
410 }
411
412 if (!but) {
413 /* no button to start from, means we are a popup */
414 pup->mx = window->eventstate->xy[0];
415 pup->my = window->eventstate->xy[1];
416 pup->popup = true;
417 }
419 C, butregion, but, nullptr, ui_block_func_POPUP, pup, ui_block_free_func_POPUP, can_refresh);
420
421 if (!but) {
422 handle->popup = true;
423
424 UI_popup_handlers_add(C, &window->modalhandlers, handle, 0);
426 }
427
428 return handle;
429}
430
432 bContext *C, ARegion *butregion, uiBut *but, uiMenuCreateFunc menu_func, void *arg)
433{
435 C,
436 butregion,
437 but,
438 nullptr,
439 [menu_func, arg](bContext *C, uiLayout *layout) { menu_func(C, layout, arg); },
440 false);
441}
442
445/* -------------------------------------------------------------------- */
449static void create_title_button(uiLayout *layout, const char *title, int icon)
450{
451 uiBlock *block = uiLayoutGetBlock(layout);
452 char titlestr[256];
453
454 if (icon) {
455 SNPRINTF(titlestr, " %s", title);
457 block, UI_BTYPE_LABEL, 0, icon, titlestr, 0, 0, 200, UI_UNIT_Y, nullptr, 0.0, 0.0, "");
458 }
459 else {
460 uiBut *but = uiDefBut(
461 block, UI_BTYPE_LABEL, 0, title, 0, 0, 200, UI_UNIT_Y, nullptr, 0.0, 0.0, "");
463 }
464
465 uiItemS(layout);
466}
467
469 const char *title,
470 const char *block_name,
471 int icon)
472{
473 uiPopupMenu *pup = MEM_new<uiPopupMenu>(__func__);
474
475 pup->title = title;
476
477 ui_popup_menu_create_block(C, pup, title, block_name);
478
479 /* create in advance so we can let buttons point to retval already */
480 pup->block->handle = MEM_cnew<uiPopupBlockHandle>(__func__);
481
482 if (title[0]) {
483 create_title_button(pup->layout, title, icon);
484 }
485
486 return pup;
487}
488
489uiPopupMenu *UI_popup_menu_begin(bContext *C, const char *title, int icon)
490{
491 return UI_popup_menu_begin_ex(C, title, __func__, icon);
492}
493
494void UI_popup_menu_but_set(uiPopupMenu *pup, ARegion *butregion, uiBut *but)
495{
496 pup->but = but;
497 pup->butregion = butregion;
498}
499
501{
502 wmWindow *window = CTX_wm_window(C);
503
504 pup->popup = true;
505 pup->mx = window->eventstate->xy[0];
506 pup->my = window->eventstate->xy[1];
507
508 uiBut *but = nullptr;
509 ARegion *butregion = nullptr;
510 if (pup->but) {
511 but = pup->but;
512 butregion = pup->butregion;
513 }
514
516 C, butregion, but, nullptr, ui_block_func_POPUP, pup, nullptr, false);
517 menu->popup = true;
518
519 UI_popup_handlers_add(C, &window->modalhandlers, menu, 0);
521
522 MEM_delete(pup);
523}
524
526{
527 if (!UI_block_is_empty_ex(pup->block, true)) {
528 UI_popup_menu_end(C, pup);
529 return true;
530 }
531 UI_block_layout_resolve(pup->block, nullptr, nullptr);
532 MEM_freeN(pup->block->handle);
533 UI_block_free(C, pup->block);
534 MEM_delete(pup);
535 return false;
536}
537
539{
540 return pup->layout;
541}
542
545/* -------------------------------------------------------------------- */
550{
551 uiPopupMenu *pup = nullptr;
552 uiLayout *layout;
553
554 if (!CTX_wm_window(C)) {
555 return;
556 }
557
558 BKE_reports_lock(reports);
559
560 LISTBASE_FOREACH (Report *, report, &reports->list) {
561 int icon;
562 const char *msg, *msg_next;
563
564 if (report->type < reports->printlevel) {
565 continue;
566 }
567
568 if (pup == nullptr) {
569 char title[UI_MAX_DRAW_STR];
570 SNPRINTF(title, "%s: %s", RPT_("Report"), report->typestr);
571 /* popup_menu stuff does just what we need (but pass meaningful block name) */
572 pup = UI_popup_menu_begin_ex(C, title, __func__, ICON_NONE);
573 layout = UI_popup_menu_layout(pup);
574 }
575 else {
576 uiItemS(layout);
577 }
578
579 /* split each newline into a label */
580 msg = report->message;
581 icon = UI_icon_from_report_type(report->type);
582 do {
583 char buf[UI_MAX_DRAW_STR];
584 msg_next = strchr(msg, '\n');
585 if (msg_next) {
586 msg_next++;
587 BLI_strncpy(buf, msg, std::min(sizeof(buf), size_t(msg_next - msg)));
588 msg = buf;
589 }
590 uiItemL(layout, msg, icon);
591 icon = ICON_NONE;
592 } while ((msg = msg_next) && *msg);
593 }
594
595 BKE_reports_unlock(reports);
596
597 if (pup) {
598 UI_popup_menu_end(C, pup);
599 }
600}
601
603 MenuType *mt,
604 const char *title,
605 const int icon)
606{
608 C,
609 nullptr,
610 nullptr,
611 title,
612 [mt, title, icon](bContext *C, uiLayout *layout) -> void {
613 if (title && title[0]) {
614 create_title_button(layout, title, icon);
615 }
616 ui_item_menutype_func(C, layout, mt);
617 },
618 true);
619
620 STRNCPY(handle->menu_idname, mt->idname);
621
622 WorkspaceStatus status(C);
623 if (bool(mt->flag & MenuTypeFlag::SearchOnKeyPress)) {
624 status.range(IFACE_("Search"), ICON_EVENT_A, ICON_EVENT_Z);
625 }
626 else if (mt->idname[0]) {
627 status.item(IFACE_("Search"), ICON_EVENT_SPACEKEY);
628 }
629}
630
631int UI_popup_menu_invoke(bContext *C, const char *idname, ReportList *reports)
632{
633 MenuType *mt = WM_menutype_find(idname, true);
634
635 if (mt == nullptr) {
636 BKE_reportf(reports, RPT_ERROR, "Menu \"%s\" not found", idname);
637 return OPERATOR_CANCELLED;
638 }
639
640 if (WM_menutype_poll(C, mt) == false) {
641 /* cancel but allow event to pass through, just like operators do */
643 }
644 /* For now always recreate menus on redraw that were invoked with this function. Maybe we want to
645 * make that optional somehow. */
646 const bool allow_refresh = true;
647
648 const char *title = CTX_IFACE_(mt->translation_context, mt->label);
649 if (allow_refresh) {
650 ui_popup_menu_create_from_menutype(C, mt, title, ICON_NONE);
651 }
652 else {
653 /* If no refresh is needed, create the block directly. */
654 uiPopupMenu *pup = UI_popup_menu_begin(C, title, ICON_NONE);
655 uiLayout *layout = UI_popup_menu_layout(pup);
656 UI_menutype_draw(C, mt, layout);
657 UI_popup_menu_end(C, pup);
658 }
659
660 return OPERATOR_INTERFACE;
661}
662
665/* -------------------------------------------------------------------- */
670 bContext *C, uiBlockCreateFunc func, void *arg, uiFreeArgFunc arg_free, const bool can_refresh)
671{
672 wmWindow *window = CTX_wm_window(C);
673
675 C, nullptr, nullptr, func, nullptr, arg, arg_free, can_refresh);
676 handle->popup = true;
677
678 /* Clear the status bar. */
679 WorkspaceStatus status(C);
680 status.item(" ", ICON_NONE);
681
682 UI_popup_handlers_add(C, &window->modalhandlers, handle, 0);
684 C, handle->region, static_cast<uiBlock *>(handle->region->uiblocks.first));
686}
687
689{
690 UI_popup_block_invoke_ex(C, func, arg, arg_free, true);
691}
692
695 uiBlockHandleFunc popup_func,
696 uiBlockCancelFunc cancel_func,
697 void *arg,
698 wmOperator *op)
699{
700 wmWindow *window = CTX_wm_window(C);
701
703 C, nullptr, nullptr, func, nullptr, arg, nullptr, true);
704 handle->popup = true;
705 handle->retvalue = 1;
706
707 handle->popup_op = op;
708 handle->popup_arg = arg;
709 handle->popup_func = popup_func;
710 handle->cancel_func = cancel_func;
711 // handle->opcontext = opcontext;
712
713 /* Clear the status bar. */
714 WorkspaceStatus status(C);
715 status.item(" ", ICON_NONE);
716
717 UI_popup_handlers_add(C, &window->modalhandlers, handle, 0);
719 C, handle->region, static_cast<uiBlock *>(handle->region->uiblocks.first));
721}
722
723static void popup_block_template_close_cb(bContext *C, void *arg1, void * /*arg2*/)
724{
725 uiBlock *block = (uiBlock *)arg1;
726
727 uiPopupBlockHandle *handle = block->handle;
728 if (handle == nullptr) {
729 printf("Error: used outside of a popup!\n");
730 return;
731 }
732
733 wmWindow *win = CTX_wm_window(C);
735
736 if (handle->cancel_func) {
737 handle->cancel_func(C, handle->popup_arg);
738 }
739
740 UI_popup_block_close(C, win, block);
741}
742
744{
745 if (block->flag & (UI_BLOCK_KEEP_OPEN | UI_BLOCK_POPOVER)) {
746 return true;
747 }
748 return false;
749}
750
752 const bool cancel_default,
753 blender::FunctionRef<uiBut *()> confirm_fn,
754 blender::FunctionRef<uiBut *()> cancel_fn)
755{
756#ifdef _WIN32
757 const bool windows_layout = true;
758#else
759 const bool windows_layout = false;
760#endif
761 blender::FunctionRef<uiBut *()> *button_functions[2];
762 if (windows_layout) {
763 ARRAY_SET_ITEMS(button_functions, &confirm_fn, &cancel_fn);
764 }
765 else {
766 ARRAY_SET_ITEMS(button_functions, &cancel_fn, &confirm_fn);
767 }
768
769 for (int i = 0; i < ARRAY_SIZE(button_functions); i++) {
770 blender::FunctionRef<uiBut *()> *but_fn = button_functions[i];
771 if (uiBut *but = (*but_fn)()) {
772 const bool is_cancel = (but_fn == &cancel_fn);
773 if ((block->flag & UI_BLOCK_LOOP) == 0) {
775 }
776 if (is_cancel == cancel_default) {
777 /* An active button shouldn't exist, if it does, never set another. */
780 }
781 }
782 }
783 }
784}
785
788 const char *confirm_text,
789 const char *cancel_text,
790 const int icon,
791 bool cancel_default,
792 PointerRNA *r_ptr)
793{
794 uiBlock *block = uiLayoutGetBlock(layout);
795
796 if (confirm_text == nullptr) {
797 confirm_text = IFACE_("OK");
798 }
799 if (cancel_text == nullptr) {
800 cancel_text = IFACE_("Cancel");
801 }
802
803 /* Use a split so both buttons are the same size. */
804 const bool show_confirm = confirm_text[0] != '\0';
805 const bool show_cancel = cancel_text[0] != '\0';
806 uiLayout *row = (show_confirm && show_cancel) ? uiLayoutSplit(layout, 0.5f, false) : layout;
807
808 /* When only one button is shown, make it default. */
809 if (!show_confirm) {
810 cancel_default = true;
811 }
812
813 auto confirm_fn = [&row, &ot, &confirm_text, &icon, &r_ptr, &show_confirm]() -> uiBut * {
814 if (!show_confirm) {
815 return nullptr;
816 }
817 uiBlock *block = uiLayoutGetBlock(row);
818 const uiBut *but_ref = (uiBut *)block->buttons.last;
819 uiItemFullO_ptr(row,
820 ot,
821 confirm_text,
822 icon,
823 nullptr,
826 r_ptr);
827
828 if (but_ref == block->buttons.last) {
829 return nullptr;
830 }
831 return static_cast<uiBut *>(block->buttons.last);
832 };
833
834 auto cancel_fn = [&row, &cancel_text, &show_cancel]() -> uiBut * {
835 if (!show_cancel) {
836 return nullptr;
837 }
838 uiBlock *block = uiLayoutGetBlock(row);
839 uiBut *but = uiDefIconTextBut(block,
841 1,
842 ICON_NONE,
843 cancel_text,
844 0,
845 0,
846 UI_UNIT_X, /* Ignored, as a split is used. */
847 UI_UNIT_Y,
848 nullptr,
849 0.0,
850 0.0,
851 "");
852
853 return but;
854 };
855
856 UI_popup_block_template_confirm(block, cancel_default, confirm_fn, cancel_fn);
857}
858
859#if 0 /* UNUSED */
860void uiPupBlockOperator(bContext *C,
862 wmOperator *op,
863 wmOperatorCallContext opcontext)
864{
865 wmWindow *window = CTX_wm_window(C);
866
867 uiPopupBlockHandle *handle = ui_popup_block_create(C, nullptr, nullptr, func, nullptr, op, nullptr, true);
868 handle->popup = 1;
869 handle->retvalue = 1;
870
871 handle->popup_arg = op;
872 handle->popup_func = operator_cb;
873 handle->cancel_func = confirm_cancel_operator;
874 handle->opcontext = opcontext;
875
876 UI_popup_handlers_add(C, &window->modalhandlers, handle, 0);
878}
879#endif
880
882{
883 /* if loading new .blend while popup is open, window will be nullptr */
884 if (block->handle) {
885 if (win) {
886 const bScreen *screen = WM_window_get_active_screen(win);
887
889 ui_popup_block_free(C, block->handle);
890
891 /* In the case we have nested popups,
892 * closing one may need to redraw another, see: #48874 */
893 LISTBASE_FOREACH (ARegion *, region, &screen->regionbase) {
895 }
896 }
897 }
898
899 ED_workspace_status_text(C, nullptr);
900}
901
903{
904 LISTBASE_FOREACH (const ARegion *, region, &screen->regionbase) {
905 LISTBASE_FOREACH (const uiBlock *, block, &region->uiblocks) {
906 if (block->name == name) {
907 return true;
908 }
909 }
910 }
911 return false;
912}
913
914void UI_popup_menu_close(const uiBlock *block, const bool is_cancel)
915{
916 UI_popup_menu_retval_set(block, is_cancel ? UI_RETURN_CANCEL : UI_RETURN_OK, true);
917}
918
919void UI_popup_menu_close_from_but(const uiBut *but, const bool is_cancel)
920{
921 UI_popup_menu_close(but->block, is_cancel);
922}
923
wmWindow * CTX_wm_window(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
void BKE_reports_unlock(ReportList *reports)
Definition report.cc:108
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
void BKE_reports_lock(ReportList *reports)
Definition report.cc:103
#define BLI_assert(a)
Definition BLI_assert.h:50
#define LISTBASE_FOREACH(type, var, list)
int BLI_findindex(const struct ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
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:197
#define STRNCPY(dst, src)
Definition BLI_string.h:593
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:597
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
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_PASS_THROUGH
void ED_region_tag_refresh_ui(ARegion *region)
Definition area.cc:662
void ED_workspace_status_text(bContext *C, const char *str)
Definition area.cc:966
Read Guarded memory(de)allocation.
@ PROP_ENUM
Definition RNA_types.hh:69
void UI_but_func_set(uiBut *but, std::function< void(bContext &)> func)
@ UI_RETURN_CANCEL
@ UI_RETURN_OK
void(*)(void *arg) uiFreeArgFunc
#define UI_UNIT_Y
@ UI_EMBOSS_PULLDOWN
void UI_popup_menu_retval_set(const uiBlock *block, int retval, bool enable)
void UI_block_theme_style_set(uiBlock *block, char theme_style)
@ 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
int UI_icon_from_report_type(int type)
#define UI_SEP_CHAR
void(*)(bContext *C, uiLayout *layout, void *arg1) uiMenuCreateFunc
uiBut * uiDefBut(uiBlock *block, int type, int retval, blender::StringRef str, int x, int y, short width, short height, void *poin, float min, float max, const char *tip)
void uiItemL(uiLayout *layout, const char *name, int icon)
uiBlock * uiLayoutGetBlock(uiLayout *layout)
uiLayout * UI_block_layout(uiBlock *block, int dir, int type, int x, int y, int size, int em, int padding, const uiStyle *style)
const uiStyle * UI_style_get_dpi()
float uiLayoutGetUnitsX(uiLayout *layout)
void(*)(bContext *C, void *arg1) uiBlockCancelFunc
uiBlock * UI_block_begin(const bContext *C, ARegion *region, std::string name, eUIEmbossType emboss)
void uiItemS(uiLayout *layout)
uiBut * uiDefIconTextBut(uiBlock *block, int type, int retval, int icon, blender::StringRef str, int x, int y, short width, short height, void *poin, float min, float max, const char *tip)
bool UI_block_active_only_flagged_buttons(const bContext *C, ARegion *region, uiBlock *block)
wmOperatorCallContext uiLayoutGetOperatorContext(uiLayout *layout)
bool UI_block_has_active_default_button(const uiBlock *block)
void uiItemFullO_ptr(uiLayout *layout, wmOperatorType *ot, const char *name, int icon, IDProperty *properties, wmOperatorCallContext context, eUI_Item_Flag flag, PointerRNA *r_opptr)
void(*)(bContext *C, void *arg, int event) uiBlockHandleFunc
#define UI_ITEM_NONE
#define UI_MAX_DRAW_STR
void UI_menutype_draw(bContext *C, MenuType *mt, uiLayout *layout)
@ UI_DIR_DOWN
@ UI_DIR_RIGHT
@ UI_DIR_UP
void UI_block_bounds_set_menu(uiBlock *block, int addval, const int bounds_offset[2])
Definition interface.cc:604
bool UI_block_is_empty_ex(const uiBlock *block, bool skip_title)
@ UI_LAYOUT_VERTICAL
void UI_block_bounds_set_text(uiBlock *block, int addval)
Definition interface.cc:584
@ UI_LAYOUT_MENU
@ UI_BLOCK_THEME_STYLE_POPUP
void UI_popup_handlers_add(bContext *C, ListBase *handlers, uiPopupBlockHandle *popup, char flag)
uiLayout * uiLayoutSplit(uiLayout *layout, float percentage, bool align)
void UI_block_layout_resolve(uiBlock *block, int *r_x, int *r_y)
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)
#define UI_UNIT_X
uiBlock *(*)(bContext *C, ARegion *region, void *arg1) uiBlockCreateFunc
void UI_block_flag_enable(uiBlock *block, int flag)
@ UI_BTYPE_BUT
@ UI_BTYPE_LABEL
@ UI_BTYPE_SEPR_LINE
@ UI_BTYPE_SEPR
@ UI_BTYPE_PULLDOWN
@ UI_BTYPE_MENU
void uiLayoutSetOperatorContext(uiLayout *layout, wmOperatorCallContext opcontext)
void UI_popup_handlers_remove(ListBase *handlers, uiPopupBlockHandle *popup)
void uiLayoutContextCopy(uiLayout *layout, const bContextStore *context)
void UI_but_flag_enable(uiBut *but, int flag)
@ UI_BUT_TEXT_LEFT
@ UI_BUT_ACTIVE_DEFAULT
@ UI_BUT_HAS_SEP_CHAR
wmOperatorCallContext
Definition WM_types.hh:216
@ WM_OP_INVOKE_REGION_WIN
Definition WM_types.hh:219
@ WM_OP_EXEC_REGION_WIN
Definition WM_types.hh:226
void range(std::string text, int icon1, int icon2)
Definition area.cc:915
void item(std::string text, int icon1, int icon2=0)
Definition area.cc:908
static constexpr int64_t not_found
constexpr bool is_empty() const
constexpr StringRef substr(int64_t start, int64_t size) const
#define printf
#define str(s)
void ui_but_activate_over(bContext *C, ARegion *region, uiBut *but)
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(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_template_confirm_op(uiLayout *layout, wmOperatorType *ot, const char *confirm_text, const char *cancel_text, const int icon, bool cancel_default, PointerRNA *r_ptr)
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 *)
int UI_popup_menu_invoke(bContext *C, const char *idname, ReportList *reports)
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)
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)
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
uint64_t get_default_hash(const T &v)
Definition BLI_hash.hh:219
#define hash
Definition noise.c:154
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)
void * last
MenuTypeFlag flag
char label[BKE_ST_MAXNAME]
char idname[BKE_ST_MAXNAME]
char translation_context[BKE_ST_MAXNAME]
float xmin
float ymin
uiPopupBlockHandle * handle
ListBase buttons
std::string name
PropertyRNA * rnaprop
eButType type
uiBlock * block
const bContextStore * context
std::string drawstr
std::string str
PointerRNA rnapoin
uiMenuStepFunc menu_step_func
std::function< void(bContext *C, uiLayout *layout)> menu_func
int xy[2]
Definition WM_types.hh:726
struct wmEvent * eventstate
void WM_event_add_mousemove(wmWindow *win)
wmOperatorType * ot
Definition wm_files.cc:4125
MenuType * WM_menutype_find(const char *idname, bool quiet)
bool WM_menutype_poll(bContext *C, MenuType *mt)
bScreen * WM_window_get_active_screen(const wmWindow *win)