Blender V4.5
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.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
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, UI_BTYPE_LABEL, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE)) {
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 looked at to solve this. */
198 if (!title.is_empty()) {
199 pup->block->puphash = ui_popup_menu_hash(title);
200 }
201 pup->layout = UI_block_layout(
202 pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, UI_MENU_PADDING, style);
203
204 /* NOTE: this intentionally differs from the menu & sub-menu default because many operators
205 * use popups like this to select one of their options -
206 * where having invoke doesn't make sense.
207 * When the menu was opened from a button, use invoke still for compatibility. This used to be
208 * the default and changing now could cause issues. */
209 const wmOperatorCallContext opcontext = pup->but ? WM_OP_INVOKE_REGION_WIN :
211
212 uiLayoutSetOperatorContext(pup->layout, opcontext);
213
214 if (pup->but) {
215 if (pup->but->context) {
217 }
218 }
219}
220
221static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, void *arg_pup)
222{
223 uiPopupMenu *pup = static_cast<uiPopupMenu *>(arg_pup);
224
225 int minwidth = 0;
226
227 if (!pup->layout) {
228 ui_popup_menu_create_block(C, pup, pup->title, __func__);
229
230 if (pup->menu_func) {
231 pup->block->handle = handle;
232 pup->menu_func(C, pup->layout);
233 pup->block->handle = nullptr;
234 }
235
236 if (uiLayoutGetUnitsX(pup->layout) != 0.0f) {
237 /* Use the minimum width from the layout if it's set. */
238 minwidth = uiLayoutGetUnitsX(pup->layout) * UI_UNIT_X;
239 }
240
241 pup->layout = nullptr;
242 }
243
244 /* Find block minimum width. */
245 if (minwidth) {
246 /* Skip. */
247 }
248 else if (pup->but) {
249 /* Minimum width to enforce. */
250 if (!pup->but->drawstr.empty()) {
251 minwidth = BLI_rctf_size_x(&pup->but->rect);
252 }
253 else {
254 /* For buttons with no text, use the minimum (typically icon only). */
255 minwidth = UI_MENU_WIDTH_MIN;
256 }
257 }
258 else {
259 minwidth = UI_MENU_WIDTH_MIN;
260 }
261
262 /* Find block direction. */
263 char direction;
264 if (pup->but) {
265 if (pup->block->direction != 0) {
266 /* allow overriding the direction from menu_func */
267 direction = pup->block->direction;
268 }
269 else {
270 direction = UI_DIR_DOWN;
271 }
272 }
273 else {
274 direction = UI_DIR_DOWN;
275 }
276
277 bool flip = (direction == UI_DIR_DOWN);
278
279 uiBlock *block = pup->block;
280
281 /* in some cases we create the block before the region,
282 * so we set it delayed here if necessary */
283 if (BLI_findindex(&handle->region->runtime->uiblocks, block) == -1) {
284 UI_block_region_set(block, handle->region);
285 }
286
287 block->direction = direction;
288
289 int width, height;
290 UI_block_layout_resolve(block, &width, &height);
291
293
294 if (pup->popup) {
295 int offset[2] = {0, 0};
296
297 uiBut *but_activate = nullptr;
300 UI_block_direction_set(block, direction);
301
302 /* offset the mouse position, possibly based on earlier selection */
303 if (!handle->refresh) {
304 uiBut *bt;
305 if ((block->flag & UI_BLOCK_POPUP_MEMORY) && (bt = ui_popup_menu_memory_get(block))) {
306 /* position mouse on last clicked item, at 0.8*width of the
307 * button, so it doesn't overlap the text too much, also note
308 * the offset is negative because we are inverse moving the
309 * block to be under the mouse */
310 offset[0] = -(bt->rect.xmin + 0.8f * BLI_rctf_size_x(&bt->rect));
311 offset[1] = -(bt->rect.ymin + 0.5f * UI_UNIT_Y);
312
313 if (ui_but_is_editable(bt)) {
314 but_activate = bt;
315 }
316 }
317 else {
318 /* position mouse at 0.8*width of the button and below the tile
319 * on the first item */
320 offset[0] = 0;
321 for (const std::unique_ptr<uiBut> &but_iter : block->buttons) {
322 offset[0] = min_ii(offset[0],
323 -(but_iter->rect.xmin + 0.8f * BLI_rctf_size_x(&but_iter->rect)));
324 }
325
326 offset[1] = 2.1 * UI_UNIT_Y;
327
328 for (const std::unique_ptr<uiBut> &but_iter : block->buttons) {
329 if (ui_but_is_editable(but_iter.get())) {
330 but_activate = but_iter.get();
331 break;
332 }
333 }
334 }
335 copy_v2_v2_int(handle->prev_bounds_offset, offset);
336 }
337 else {
338 copy_v2_v2_int(offset, handle->prev_bounds_offset);
339 }
340
341 /* in rare cases this is needed since moving the popup
342 * to be within the window bounds may move it away from the mouse,
343 * This ensures we set an item to be active. */
344 if (but_activate) {
345 ARegion *region = CTX_wm_region(C);
346 if (region && region->regiontype == RGN_TYPE_TOOLS && but_activate->block &&
347 (but_activate->block->flag & UI_BLOCK_POPUP_HOLD))
348 {
349 /* In Toolbars, highlight the button with select color. */
350 but_activate->flag |= UI_SELECT_DRAW;
351 }
352 ui_but_activate_over(C, handle->region, but_activate);
353 }
354
355 block->minbounds = minwidth;
356 UI_block_bounds_set_menu(block, 1, offset);
357 }
358 else {
359 /* for a header menu we set the direction automatic */
360 if (!pup->slideout && flip) {
361 ARegion *region = CTX_wm_region(C);
362 if (region) {
363 if (RGN_TYPE_IS_HEADER_ANY(region->regiontype)) {
366 }
367 }
368 }
369 }
370
371 block->minbounds = minwidth;
372 UI_block_bounds_set_text(block, 3.0f * UI_UNIT_X);
373 }
374
375 /* if menu slides out of other menu, override direction */
376 if (pup->slideout) {
378 }
379
380 return pup->block;
381}
382
383static void ui_block_free_func_POPUP(void *arg_pup)
384{
385 uiPopupMenu *pup = static_cast<uiPopupMenu *>(arg_pup);
386 MEM_delete(pup);
387}
388
390 bContext *C,
391 ARegion *butregion,
392 uiBut *but,
393 const char *title,
394 std::function<void(bContext *, uiLayout *)> menu_func,
395 const bool can_refresh)
396{
397 wmWindow *window = CTX_wm_window(C);
398
399 uiPopupMenu *pup = MEM_new<uiPopupMenu>(__func__);
400 pup->title = title;
401 /* menu is created from a callback */
402 pup->menu_func = menu_func;
403 if (but) {
404 pup->slideout = ui_block_is_menu(but->block);
405 pup->but = but;
406
407 if (but->type == UI_BTYPE_PULLDOWN) {
408 WorkspaceStatus status(C);
409 status.item(IFACE_("Search"), ICON_EVENT_SPACEKEY);
410 }
411 }
412
413 if (!but) {
414 /* no button to start from, means we are a popup */
415 pup->mx = window->eventstate->xy[0];
416 pup->my = window->eventstate->xy[1];
417 pup->popup = true;
418 }
420 C, butregion, but, nullptr, ui_block_func_POPUP, pup, ui_block_free_func_POPUP, can_refresh);
421
422 if (!but) {
423 handle->popup = true;
424
425 UI_popup_handlers_add(C, &window->modalhandlers, handle, 0);
427 }
428
429 return handle;
430}
431
433 bContext *C, ARegion *butregion, uiBut *but, uiMenuCreateFunc menu_func, void *arg)
434{
436 C,
437 butregion,
438 but,
439 nullptr,
440 [menu_func, arg](bContext *C, uiLayout *layout) { menu_func(C, layout, arg); },
441 false);
442}
443
445
446/* -------------------------------------------------------------------- */
449
450static void create_title_button(uiLayout *layout, const char *title, int icon)
451{
452 uiBlock *block = uiLayoutGetBlock(layout);
453 char titlestr[256];
454
455 if (icon) {
456 SNPRINTF(titlestr, " %s", title);
458 block, UI_BTYPE_LABEL, 0, icon, titlestr, 0, 0, 200, UI_UNIT_Y, nullptr, 0.0, 0.0, "");
459 }
460 else {
461 uiBut *but = uiDefBut(
462 block, UI_BTYPE_LABEL, 0, title, 0, 0, 200, UI_UNIT_Y, nullptr, 0.0, 0.0, "");
464 }
465
466 layout->separator();
467}
468
470 const char *title,
471 const char *block_name,
472 int icon)
473{
474 uiPopupMenu *pup = MEM_new<uiPopupMenu>(__func__);
475
476 pup->title = title;
477
478 ui_popup_menu_create_block(C, pup, title, block_name);
479
480 /* create in advance so we can let buttons point to retval already */
481 pup->block->handle = MEM_new<uiPopupBlockHandle>(__func__);
482
483 if (title[0]) {
484 create_title_button(pup->layout, title, icon);
485 }
486
487 return pup;
488}
489
490uiPopupMenu *UI_popup_menu_begin(bContext *C, const char *title, int icon)
491{
492 return UI_popup_menu_begin_ex(C, title, __func__, icon);
493}
494
495void UI_popup_menu_but_set(uiPopupMenu *pup, ARegion *butregion, uiBut *but)
496{
497 pup->but = but;
498 pup->butregion = butregion;
499}
500
502{
503 wmWindow *window = CTX_wm_window(C);
504
505 pup->popup = true;
506 pup->mx = window->eventstate->xy[0];
507 pup->my = window->eventstate->xy[1];
508
509 uiBut *but = nullptr;
510 ARegion *butregion = nullptr;
511 if (pup->but) {
512 but = pup->but;
513 butregion = pup->butregion;
514 }
515
517 C, butregion, but, nullptr, ui_block_func_POPUP, pup, nullptr, false);
518 menu->popup = true;
519
520 UI_popup_handlers_add(C, &window->modalhandlers, menu, 0);
522
523 MEM_delete(pup);
524}
525
527{
528 if (!UI_block_is_empty_ex(pup->block, true)) {
529 UI_popup_menu_end(C, pup);
530 return true;
531 }
532 UI_block_layout_resolve(pup->block, nullptr, nullptr);
533 MEM_delete(pup->block->handle);
534 UI_block_free(C, pup->block);
535 MEM_delete(pup);
536 return false;
537}
538
540{
541 return pup->layout;
542}
543
545
546/* -------------------------------------------------------------------- */
549
551{
552 uiPopupMenu *pup = nullptr;
553 uiLayout *layout;
554
555 if (!CTX_wm_window(C)) {
556 return;
557 }
558
560
561 LISTBASE_FOREACH (Report *, report, &reports->list) {
562 int icon;
563 const char *msg, *msg_next;
564
565 if (report->type < reports->printlevel) {
566 continue;
567 }
568
569 if (pup == nullptr) {
570 char title[UI_MAX_DRAW_STR];
571 SNPRINTF(title, "%s: %s", RPT_("Report"), report->typestr);
572 /* popup_menu stuff does just what we need (but pass meaningful block name) */
573 pup = UI_popup_menu_begin_ex(C, title, __func__, ICON_NONE);
574 layout = UI_popup_menu_layout(pup);
575 }
576 else {
577 layout->separator();
578 }
579
580 /* split each newline into a label */
581 msg = report->message;
582 icon = UI_icon_from_report_type(report->type);
583 do {
584 char buf[UI_MAX_DRAW_STR];
585 msg_next = strchr(msg, '\n');
586 if (msg_next) {
587 msg_next++;
588 BLI_strncpy(buf, msg, std::min(sizeof(buf), size_t(msg_next - msg)));
589 msg = buf;
590 }
591 layout->label(msg, icon);
592 icon = ICON_NONE;
593 } while ((msg = msg_next) && *msg);
594 }
595
597
598 if (pup) {
599 UI_popup_menu_end(C, pup);
600 }
601}
602
604 MenuType *mt,
605 const char *title,
606 const int icon)
607{
609 C,
610 nullptr,
611 nullptr,
612 title,
613 [mt, title, icon](bContext *C, uiLayout *layout) -> void {
614 if (title && title[0]) {
615 create_title_button(layout, title, icon);
616 }
617 ui_item_menutype_func(C, layout, mt);
618 },
619 true);
620
621 STRNCPY(handle->menu_idname, mt->idname);
622
623 WorkspaceStatus status(C);
624 if (bool(mt->flag & MenuTypeFlag::SearchOnKeyPress)) {
625 status.range(IFACE_("Search"), ICON_EVENT_A, ICON_EVENT_Z);
626 }
627 else if (mt->idname[0]) {
628 status.item(IFACE_("Search"), ICON_EVENT_SPACEKEY);
629 }
630}
631
633{
634 MenuType *mt = WM_menutype_find(idname, true);
635
636 if (mt == nullptr) {
637 BKE_reportf(reports, RPT_ERROR, "Menu \"%s\" not found", idname);
638 return OPERATOR_CANCELLED;
639 }
640
641 if (WM_menutype_poll(C, mt) == false) {
642 /* cancel but allow event to pass through, just like operators do */
644 }
645 /* For now always recreate menus on redraw that were invoked with this function. Maybe we want to
646 * make that optional somehow. */
647 const bool allow_refresh = true;
648
649 const char *title = CTX_IFACE_(mt->translation_context, mt->label);
650 if (allow_refresh) {
651 ui_popup_menu_create_from_menutype(C, mt, title, ICON_NONE);
652 }
653 else {
654 /* If no refresh is needed, create the block directly. */
655 uiPopupMenu *pup = UI_popup_menu_begin(C, title, ICON_NONE);
656 uiLayout *layout = UI_popup_menu_layout(pup);
657 UI_menutype_draw(C, mt, layout);
658 UI_popup_menu_end(C, pup);
659 }
660
661 return OPERATOR_INTERFACE;
662}
663
665
666/* -------------------------------------------------------------------- */
669
671 bContext *C, uiBlockCreateFunc func, void *arg, uiFreeArgFunc arg_free, const bool can_refresh)
672{
673 wmWindow *window = CTX_wm_window(C);
674
676 C, nullptr, nullptr, func, nullptr, arg, arg_free, can_refresh);
677 handle->popup = true;
678
679 /* Clear the status bar. */
680 WorkspaceStatus status(C);
681 status.item(" ", ICON_NONE);
682
683 UI_popup_handlers_add(C, &window->modalhandlers, handle, 0);
685 C, handle->region, static_cast<uiBlock *>(handle->region->runtime->uiblocks.first));
687}
688
690{
691 UI_popup_block_invoke_ex(C, func, arg, arg_free, true);
692}
693
696 uiBlockHandleFunc popup_func,
697 uiBlockCancelFunc cancel_func,
698 void *arg,
699 wmOperator *op)
700{
701 wmWindow *window = CTX_wm_window(C);
702
704 C, nullptr, nullptr, func, nullptr, arg, nullptr, true);
705 handle->popup = true;
706 handle->retvalue = 1;
707
708 handle->popup_op = op;
709 handle->popup_arg = arg;
710 handle->popup_func = popup_func;
711 handle->cancel_func = cancel_func;
712 // handle->opcontext = opcontext;
713
714 /* Clear the status bar. */
715 WorkspaceStatus status(C);
716 status.item(" ", ICON_NONE);
717
718 UI_popup_handlers_add(C, &window->modalhandlers, handle, 0);
720 C, handle->region, static_cast<uiBlock *>(handle->region->runtime->uiblocks.first));
722}
723
724static void popup_block_template_close_cb(bContext *C, void *arg1, void * /*arg2*/)
725{
726 uiBlock *block = (uiBlock *)arg1;
727
728 uiPopupBlockHandle *handle = block->handle;
729 if (handle == nullptr) {
730 printf("Error: used outside of a popup!\n");
731 return;
732 }
733
734 wmWindow *win = CTX_wm_window(C);
736
737 if (handle->cancel_func) {
738 handle->cancel_func(C, handle->popup_arg);
739 }
740
741 UI_popup_block_close(C, win, block);
742}
743
745{
746 if (block->flag & (UI_BLOCK_KEEP_OPEN | UI_BLOCK_POPOVER)) {
747 return true;
748 }
749 return false;
750}
751
753 const bool cancel_default,
754 blender::FunctionRef<uiBut *()> confirm_fn,
755 blender::FunctionRef<uiBut *()> cancel_fn)
756{
757#ifdef _WIN32
758 const bool windows_layout = true;
759#else
760 const bool windows_layout = false;
761#endif
762 blender::FunctionRef<uiBut *()> *button_functions[2];
763 if (windows_layout) {
764 ARRAY_SET_ITEMS(button_functions, &confirm_fn, &cancel_fn);
765 }
766 else {
767 ARRAY_SET_ITEMS(button_functions, &cancel_fn, &confirm_fn);
768 }
769
770 for (int i = 0; i < ARRAY_SIZE(button_functions); i++) {
771 blender::FunctionRef<uiBut *()> *but_fn = button_functions[i];
772 if (uiBut *but = (*but_fn)()) {
773 const bool is_cancel = (but_fn == &cancel_fn);
774 if ((block->flag & UI_BLOCK_LOOP) == 0) {
776 }
777 if (is_cancel == cancel_default) {
778 /* An active button shouldn't exist, if it does, never set another. */
781 }
782 }
783 }
784 }
785}
786
789 const std::optional<StringRef> confirm_text_opt,
790 const std::optional<StringRef> cancel_text_opt,
791 const int icon,
792 bool cancel_default,
793 PointerRNA *r_ptr)
794{
795 uiBlock *block = uiLayoutGetBlock(layout);
796
797 const StringRef confirm_text = confirm_text_opt.value_or(IFACE_("OK"));
798 const StringRef cancel_text = cancel_text_opt.value_or(IFACE_("Cancel"));
799
800 /* Use a split so both buttons are the same size. */
801 const bool show_confirm = !confirm_text.is_empty();
802 const bool show_cancel = !cancel_text.is_empty();
803 uiLayout *row = (show_confirm && show_cancel) ? &layout->split(0.5f, false) : layout;
804
805 /* When only one button is shown, make it default. */
806 if (!show_confirm) {
807 cancel_default = true;
808 }
809
810 auto confirm_fn = [&row, &ot, &confirm_text, &icon, &r_ptr, &show_confirm]() -> uiBut * {
811 if (!show_confirm) {
812 return nullptr;
813 }
814 uiBlock *block = uiLayoutGetBlock(row);
815 const uiBut *but_ref = block->last_but();
816 *r_ptr = row->op(ot, confirm_text, icon, uiLayoutGetOperatorContext(row), UI_ITEM_NONE);
817
818 if (block->buttons.is_empty() || but_ref == block->buttons.last().get()) {
819 return nullptr;
820 }
821 return block->buttons.last().get();
822 };
823
824 auto cancel_fn = [&row, &cancel_text, &show_cancel]() -> uiBut * {
825 if (!show_cancel) {
826 return nullptr;
827 }
828 uiBlock *block = uiLayoutGetBlock(row);
829 uiBut *but = uiDefIconTextBut(block,
831 1,
832 ICON_NONE,
833 cancel_text,
834 0,
835 0,
836 UI_UNIT_X, /* Ignored, as a split is used. */
837 UI_UNIT_Y,
838 nullptr,
839 0.0,
840 0.0,
841 "");
842
843 return but;
844 };
845
846 UI_popup_block_template_confirm(block, cancel_default, confirm_fn, cancel_fn);
847}
848
849#if 0 /* UNUSED */
850void uiPupBlockOperator(bContext *C,
852 wmOperator *op,
853 wmOperatorCallContext opcontext)
854{
855 wmWindow *window = CTX_wm_window(C);
856
857 uiPopupBlockHandle *handle = ui_popup_block_create(C, nullptr, nullptr, func, nullptr, op, nullptr, true);
858 handle->popup = 1;
859 handle->retvalue = 1;
860
861 handle->popup_arg = op;
862 handle->popup_func = operator_cb;
863 handle->cancel_func = confirm_cancel_operator;
864 handle->opcontext = opcontext;
865
866 UI_popup_handlers_add(C, &window->modalhandlers, handle, 0);
868}
869#endif
870
872{
873 /* if loading new .blend while popup is open, window will be nullptr */
874 if (block->handle) {
875 if (win) {
876 const bScreen *screen = WM_window_get_active_screen(win);
877
880
881 /* In the case we have nested popups,
882 * closing one may need to redraw another, see: #48874 */
883 LISTBASE_FOREACH (ARegion *, region, &screen->regionbase) {
885 }
886 }
887 }
888
889 ED_workspace_status_text(C, nullptr);
890}
891
893{
894 LISTBASE_FOREACH (const ARegion *, region, &screen->regionbase) {
895 LISTBASE_FOREACH (const uiBlock *, block, &region->runtime->uiblocks) {
896 if (block->name == name) {
897 return true;
898 }
899 }
900 }
901 return false;
902}
903
904void UI_popup_menu_close(const uiBlock *block, const bool is_cancel)
905{
906 UI_popup_menu_retval_set(block, is_cancel ? UI_RETURN_CANCEL : UI_RETURN_OK, true);
907}
908
909void UI_popup_menu_close_from_but(const uiBut *but, const bool is_cancel)
910{
911 UI_popup_menu_close(but->block, is_cancel);
912}
913
wmWindow * CTX_wm_window(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
void BKE_reports_unlock(ReportList *reports)
Definition report.cc:109
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
void BKE_reports_lock(ReportList *reports)
Definition report.cc:104
#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(dst, format,...)
Definition BLI_string.h:599
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:688
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_TYPE_TOOLS
@ RGN_ALIGN_BOTTOM
#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:668
void ED_workspace_status_text(bContext *C, const char *str)
Definition area.cc:1040
Read Guarded memory(de)allocation.
@ PROP_ENUM
Definition RNA_types.hh:154
#define C
Definition RandGen.cpp:29
void UI_but_func_set(uiBut *but, std::function< void(bContext &)> func)
@ UI_BLOCK_THEME_STYLE_POPUP
#define UI_UNIT_Y
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, std::optional< blender::StringRef > tip)
void UI_popup_menu_retval_set(const uiBlock *block, int retval, bool enable)
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_TEXT_LEFT
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, std::optional< blender::StringRef > tip)
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
@ 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:655
@ UI_RETURN_CANCEL
@ UI_RETURN_OK
bool UI_block_is_empty_ex(const uiBlock *block, bool skip_title)
void UI_block_bounds_set_text(uiBlock *block, int addval)
Definition interface.cc:635
void UI_popup_handlers_add(bContext *C, ListBase *handlers, uiPopupBlockHandle *popup, char flag)
@ 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_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 UI_popup_handlers_remove(ListBase *handlers, uiPopupBlockHandle *popup)
@ UI_BUT_ACTIVE_DEFAULT
@ UI_BUT_HAS_SEP_CHAR
void UI_but_flag_enable(uiBut *but, int flag)
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)
float uiLayoutGetUnitsX(uiLayout *layout)
@ UI_LAYOUT_VERTICAL
@ UI_LAYOUT_MENU
wmOperatorCallContext uiLayoutGetOperatorContext(uiLayout *layout)
#define UI_ITEM_NONE
void UI_menutype_draw(bContext *C, MenuType *mt, uiLayout *layout)
void UI_block_layout_resolve(uiBlock *block, int *r_x, int *r_y)
void uiLayoutSetOperatorContext(uiLayout *layout, wmOperatorCallContext opcontext)
void uiLayoutContextCopy(uiLayout *layout, const bContextStore *context)
void(*)(void *arg) uiFreeArgFunc
void(*)(bContext *C, uiLayout *layout, void *arg1) uiMenuCreateFunc
#define UI_MAX_DRAW_STR
ReportList * reports
Definition WM_types.hh:1025
wmOperatorCallContext
Definition WM_types.hh:236
@ WM_OP_INVOKE_REGION_WIN
Definition WM_types.hh:239
@ WM_OP_EXEC_REGION_WIN
Definition WM_types.hh:246
const T & last(const int64_t n=0) const
bool is_empty() const
void range(std::string text, int icon1, int icon2)
Definition area.cc:986
void item(std::string text, int icon1, int icon2=0)
Definition area.cc:979
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)
@ UI_SELECT_DRAW
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
#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)
uint64_t get_default_hash(const T &v, const Args &...args)
Definition BLI_hash.hh:233
#define hash
Definition noise_c.cc: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)
ARegionRuntimeHandle * runtime
MenuTypeFlag flag
char label[BKE_ST_MAXNAME]
char idname[BKE_ST_MAXNAME]
char translation_context[BKE_ST_MAXNAME]
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:297
PropertyRNA * rnaprop
eButType type
uiBlock * block
const bContextStore * context
std::string drawstr
std::string str
PointerRNA rnapoin
uiMenuStepFunc menu_step_func
PointerRNA op(wmOperatorType *ot, std::optional< blender::StringRef > name, int icon, wmOperatorCallContext context, eUI_Item_Flag flag)
void label(blender::StringRef name, int icon)
void separator(float factor=1.0f, LayoutSeparatorType type=LayoutSeparatorType::Auto)
uiLayout & split(float percentage, bool align)
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:758
struct wmEvent * eventstate
i
Definition text_draw.cc:230
void WM_event_add_mousemove(wmWindow *win)
wmOperatorType * ot
Definition wm_files.cc:4225
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)