Blender  V3.3
interface_region_popup.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2008 Blender Foundation. All rights reserved. */
3 
10 #include <cstdarg>
11 #include <cstdlib>
12 #include <cstring>
13 
14 #include "MEM_guardedalloc.h"
15 
16 #include "DNA_userdef_types.h"
17 
18 #include "BLI_listbase.h"
19 #include "BLI_math.h"
20 #include "BLI_rect.h"
21 #include "BLI_utildefines.h"
22 
23 #include "BKE_context.h"
24 #include "BKE_screen.h"
25 
26 #include "WM_api.h"
27 #include "WM_types.h"
28 
29 #include "UI_interface.h"
30 
31 #include "ED_screen.h"
32 
33 #include "interface_intern.h"
35 
36 /* -------------------------------------------------------------------- */
40 void ui_popup_translate(ARegion *region, const int mdiff[2])
41 {
42  BLI_rcti_translate(&region->winrct, UNPACK2(mdiff));
43 
44  ED_region_update_rect(region);
45 
46  ED_region_tag_redraw(region);
47 
48  /* update blocks */
49  LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
50  uiPopupBlockHandle *handle = block->handle;
51  /* Make empty, will be initialized on next use, see T60608. */
52  BLI_rctf_init(&handle->prev_block_rect, 0, 0, 0, 0);
53 
54  LISTBASE_FOREACH (uiSafetyRct *, saferct, &block->saferct) {
55  BLI_rctf_translate(&saferct->parent, UNPACK2(mdiff));
56  BLI_rctf_translate(&saferct->safety, UNPACK2(mdiff));
57  }
58  }
59 }
60 
61 /* position block relative to but, result is in window space */
62 static void ui_popup_block_position(wmWindow *window,
63  ARegion *butregion,
64  uiBut *but,
65  uiBlock *block)
66 {
67  uiPopupBlockHandle *handle = block->handle;
68 
69  /* Compute button position in window coordinates using the source
70  * button region/block, to position the popup attached to it. */
71  rctf butrct;
72 
73  if (!handle->refresh) {
74  ui_block_to_window_rctf(butregion, but->block, &butrct, &but->rect);
75 
76  /* widget_roundbox_set has this correction too, keep in sync */
77  if (but->type != UI_BTYPE_PULLDOWN) {
78  if (but->drawflag & UI_BUT_ALIGN_TOP) {
79  butrct.ymax += U.pixelsize;
80  }
81  if (but->drawflag & UI_BUT_ALIGN_LEFT) {
82  butrct.xmin -= U.pixelsize;
83  }
84  }
85 
86  handle->prev_butrct = butrct;
87  }
88  else {
89  /* For refreshes, keep same button position so popup doesn't move. */
90  butrct = handle->prev_butrct;
91  }
92 
93  /* Compute block size in window space, based on buttons contained in it. */
94  if (block->rect.xmin == 0.0f && block->rect.xmax == 0.0f) {
95  if (block->buttons.first) {
96  BLI_rctf_init_minmax(&block->rect);
97 
98  LISTBASE_FOREACH (uiBut *, bt, &block->buttons) {
100  bt->rect.xmax += UI_MENU_SUBMENU_PADDING;
101  }
102  BLI_rctf_union(&block->rect, &bt->rect);
103  }
104  }
105  else {
106  /* we're nice and allow empty blocks too */
107  block->rect.xmin = block->rect.ymin = 0;
108  block->rect.xmax = block->rect.ymax = 20;
109  }
110  }
111 
112  ui_block_to_window_rctf(butregion, but->block, &block->rect, &block->rect);
113 
114  /* Compute direction relative to button, based on available space. */
115  const int size_x = BLI_rctf_size_x(&block->rect) + 0.2f * UI_UNIT_X; /* 4 for shadow */
116  const int size_y = BLI_rctf_size_y(&block->rect) + 0.2f * UI_UNIT_Y;
117  const int center_x = (block->direction & UI_DIR_CENTER_X) ? size_x / 2 : 0;
118  const int center_y = (block->direction & UI_DIR_CENTER_Y) ? size_y / 2 : 0;
119 
120  short dir1 = 0, dir2 = 0;
121 
122  if (!handle->refresh) {
123  bool left = false, right = false, top = false, down = false;
124 
125  const int win_x = WM_window_pixels_x(window);
126  const int win_y = WM_window_pixels_y(window);
127 
128  /* Take into account maximum size so we don't have to flip on refresh. */
129  const float max_size_x = max_ff(size_x, handle->max_size_x);
130  const float max_size_y = max_ff(size_y, handle->max_size_y);
131 
132  /* check if there's space at all */
133  if (butrct.xmin - max_size_x + center_x > 0.0f) {
134  left = true;
135  }
136  if (butrct.xmax + max_size_x - center_x < win_x) {
137  right = true;
138  }
139  if (butrct.ymin - max_size_y + center_y > 0.0f) {
140  down = true;
141  }
142  if (butrct.ymax + max_size_y - center_y < win_y) {
143  top = true;
144  }
145 
146  if (top == 0 && down == 0) {
147  if (butrct.ymin - max_size_y < win_y - butrct.ymax - max_size_y) {
148  top = true;
149  }
150  else {
151  down = true;
152  }
153  }
154 
155  dir1 = (block->direction & UI_DIR_ALL);
156 
157  /* Secondary directions. */
158  if (dir1 & (UI_DIR_UP | UI_DIR_DOWN)) {
159  if (dir1 & UI_DIR_LEFT) {
160  dir2 = UI_DIR_LEFT;
161  }
162  else if (dir1 & UI_DIR_RIGHT) {
163  dir2 = UI_DIR_RIGHT;
164  }
165  dir1 &= (UI_DIR_UP | UI_DIR_DOWN);
166  }
167 
168  if ((dir2 == 0) && (ELEM(dir1, UI_DIR_LEFT, UI_DIR_RIGHT))) {
169  dir2 = UI_DIR_DOWN;
170  }
171  if ((dir2 == 0) && (ELEM(dir1, UI_DIR_UP, UI_DIR_DOWN))) {
172  dir2 = UI_DIR_LEFT;
173  }
174 
175  /* no space at all? don't change */
176  if (left || right) {
177  if (dir1 == UI_DIR_LEFT && left == 0) {
178  dir1 = UI_DIR_RIGHT;
179  }
180  if (dir1 == UI_DIR_RIGHT && right == 0) {
181  dir1 = UI_DIR_LEFT;
182  }
183  /* this is aligning, not append! */
184  if (dir2 == UI_DIR_LEFT && right == 0) {
185  dir2 = UI_DIR_RIGHT;
186  }
187  if (dir2 == UI_DIR_RIGHT && left == 0) {
188  dir2 = UI_DIR_LEFT;
189  }
190  }
191  if (down || top) {
192  if (dir1 == UI_DIR_UP && top == 0) {
193  dir1 = UI_DIR_DOWN;
194  }
195  if (dir1 == UI_DIR_DOWN && down == 0) {
196  dir1 = UI_DIR_UP;
197  }
198  BLI_assert(dir2 != UI_DIR_UP);
199  // if (dir2 == UI_DIR_UP && top == 0) { dir2 = UI_DIR_DOWN; }
200  if (dir2 == UI_DIR_DOWN && down == 0) {
201  dir2 = UI_DIR_UP;
202  }
203  }
204 
205  handle->prev_dir1 = dir1;
206  handle->prev_dir2 = dir2;
207  }
208  else {
209  /* For refreshes, keep same popup direct so popup doesn't move
210  * to a totally different position while editing in it. */
211  dir1 = handle->prev_dir1;
212  dir2 = handle->prev_dir2;
213  }
214 
215  /* Compute offset based on direction. */
216  float offset_x = 0, offset_y = 0;
217 
218  /* Ensure buttons don't come between the parent button and the popup, see: T63566. */
219  const float offset_overlap = max_ff(U.pixelsize, 1.0f);
220 
221  if (dir1 == UI_DIR_LEFT) {
222  offset_x = (butrct.xmin - block->rect.xmax) + offset_overlap;
223  if (dir2 == UI_DIR_UP) {
224  offset_y = butrct.ymin - block->rect.ymin - center_y - UI_MENU_PADDING;
225  }
226  else {
227  offset_y = butrct.ymax - block->rect.ymax + center_y + UI_MENU_PADDING;
228  }
229  }
230  else if (dir1 == UI_DIR_RIGHT) {
231  offset_x = (butrct.xmax - block->rect.xmin) - offset_overlap;
232  if (dir2 == UI_DIR_UP) {
233  offset_y = butrct.ymin - block->rect.ymin - center_y - UI_MENU_PADDING;
234  }
235  else {
236  offset_y = butrct.ymax - block->rect.ymax + center_y + UI_MENU_PADDING;
237  }
238  }
239  else if (dir1 == UI_DIR_UP) {
240  offset_y = (butrct.ymax - block->rect.ymin) - offset_overlap;
241  if (dir2 == UI_DIR_RIGHT) {
242  offset_x = butrct.xmax - block->rect.xmax + center_x;
243  }
244  else {
245  offset_x = butrct.xmin - block->rect.xmin - center_x;
246  }
247  /* changed direction? */
248  if ((dir1 & block->direction) == 0) {
249  /* TODO: still do */
250  UI_block_order_flip(block);
251  }
252  }
253  else if (dir1 == UI_DIR_DOWN) {
254  offset_y = (butrct.ymin - block->rect.ymax) + offset_overlap;
255  if (dir2 == UI_DIR_RIGHT) {
256  offset_x = butrct.xmax - block->rect.xmax + center_x;
257  }
258  else {
259  offset_x = butrct.xmin - block->rect.xmin - center_x;
260  }
261  /* changed direction? */
262  if ((dir1 & block->direction) == 0) {
263  /* TODO: still do */
264  UI_block_order_flip(block);
265  }
266  }
267 
268  /* Center over popovers for eg. */
269  if (block->direction & UI_DIR_CENTER_X) {
270  offset_x += BLI_rctf_size_x(&butrct) / ((dir2 == UI_DIR_LEFT) ? 2 : -2);
271  }
272 
273  /* Apply offset, buttons in window coords. */
274  LISTBASE_FOREACH (uiBut *, bt, &block->buttons) {
275  ui_block_to_window_rctf(butregion, but->block, &bt->rect, &bt->rect);
276 
277  BLI_rctf_translate(&bt->rect, offset_x, offset_y);
278 
279  /* ui_but_update recalculates drawstring size in pixels */
280  ui_but_update(bt);
281  }
282 
283  BLI_rctf_translate(&block->rect, offset_x, offset_y);
284 
285  /* Safety calculus. */
286  {
287  const float midx = BLI_rctf_cent_x(&butrct);
288  const float midy = BLI_rctf_cent_y(&butrct);
289 
290  /* when you are outside parent button, safety there should be smaller */
291 
292  const int s1 = 40 * U.dpi_fac;
293  const int s2 = 3 * U.dpi_fac;
294 
295  /* parent button to left */
296  if (midx < block->rect.xmin) {
297  block->safety.xmin = block->rect.xmin - s2;
298  }
299  else {
300  block->safety.xmin = block->rect.xmin - s1;
301  }
302  /* parent button to right */
303  if (midx > block->rect.xmax) {
304  block->safety.xmax = block->rect.xmax + s2;
305  }
306  else {
307  block->safety.xmax = block->rect.xmax + s1;
308  }
309 
310  /* parent button on bottom */
311  if (midy < block->rect.ymin) {
312  block->safety.ymin = block->rect.ymin - s2;
313  }
314  else {
315  block->safety.ymin = block->rect.ymin - s1;
316  }
317  /* parent button on top */
318  if (midy > block->rect.ymax) {
319  block->safety.ymax = block->rect.ymax + s2;
320  }
321  else {
322  block->safety.ymax = block->rect.ymax + s1;
323  }
324 
325  /* Exception for switched pull-downs. */
326  if (dir1 && (dir1 & block->direction) == 0) {
327  if (dir2 == UI_DIR_RIGHT) {
328  block->safety.xmax = block->rect.xmax + s2;
329  }
330  if (dir2 == UI_DIR_LEFT) {
331  block->safety.xmin = block->rect.xmin - s2;
332  }
333  }
334  block->direction = dir1;
335  }
336 
337  /* Keep a list of these, needed for pull-down menus. */
338  uiSafetyRct *saferct = MEM_cnew<uiSafetyRct>(__func__);
339  saferct->parent = butrct;
340  saferct->safety = block->safety;
341  BLI_freelistN(&block->saferct);
342  BLI_duplicatelist(&block->saferct, &but->block->saferct);
343  BLI_addhead(&block->saferct, saferct);
344 }
345 
348 /* -------------------------------------------------------------------- */
352 static void ui_block_region_refresh(const bContext *C, ARegion *region)
353 {
354  ScrArea *ctx_area = CTX_wm_area(C);
355  ARegion *ctx_region = CTX_wm_region(C);
356 
357  if (region->do_draw & RGN_REFRESH_UI) {
358  ScrArea *handle_ctx_area;
359  ARegion *handle_ctx_region;
360 
361  region->do_draw &= ~RGN_REFRESH_UI;
362  LISTBASE_FOREACH_MUTABLE (uiBlock *, block, &region->uiblocks) {
363  uiPopupBlockHandle *handle = block->handle;
364 
365  if (handle->can_refresh) {
366  handle_ctx_area = handle->ctx_area;
367  handle_ctx_region = handle->ctx_region;
368 
369  if (handle_ctx_area) {
370  CTX_wm_area_set((bContext *)C, handle_ctx_area);
371  }
372  if (handle_ctx_region) {
373  CTX_wm_region_set((bContext *)C, handle_ctx_region);
374  }
375 
376  uiBut *but = handle->popup_create_vars.but;
377  ARegion *butregion = handle->popup_create_vars.butregion;
378  ui_popup_block_refresh((bContext *)C, handle, butregion, but);
379  }
380  }
381  }
382 
383  CTX_wm_area_set((bContext *)C, ctx_area);
384  CTX_wm_region_set((bContext *)C, ctx_region);
385 }
386 
387 static void ui_block_region_draw(const bContext *C, ARegion *region)
388 {
389  LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
390  UI_block_draw(C, block);
391  }
392 }
393 
398 {
399  ARegion *region = params->region;
400  wmNotifier *wmn = params->notifier;
401 
402  switch (wmn->category) {
403  case NC_WINDOW: {
404  switch (wmn->action) {
405  case NA_EDITED: {
406  /* window resize */
407  ED_region_tag_refresh_ui(region);
408  break;
409  }
410  }
411  break;
412  }
413  }
414 }
415 
416 static void ui_popup_block_clip(wmWindow *window, uiBlock *block)
417 {
418  const float xmin_orig = block->rect.xmin;
419  const int margin = UI_SCREEN_MARGIN;
420  int winx, winy;
421 
422  if (block->flag & UI_BLOCK_NO_WIN_CLIP) {
423  return;
424  }
425 
426  winx = WM_window_pixels_x(window);
427  winy = WM_window_pixels_y(window);
428 
429  /* shift to left if outside of view */
430  if (block->rect.xmax > winx - margin) {
431  const float xofs = winx - margin - block->rect.xmax;
432  block->rect.xmin += xofs;
433  block->rect.xmax += xofs;
434  }
435  /* shift menus to right if outside of view */
436  if (block->rect.xmin < margin) {
437  const float xofs = (margin - block->rect.xmin);
438  block->rect.xmin += xofs;
439  block->rect.xmax += xofs;
440  }
441 
442  if (block->rect.ymin < margin) {
443  block->rect.ymin = margin;
444  }
445  if (block->rect.ymax > winy - UI_POPUP_MENU_TOP) {
446  block->rect.ymax = winy - UI_POPUP_MENU_TOP;
447  }
448 
449  /* ensure menu items draw inside left/right boundary */
450  const float xofs = block->rect.xmin - xmin_orig;
451  LISTBASE_FOREACH (uiBut *, bt, &block->buttons) {
452  bt->rect.xmin += xofs;
453  bt->rect.xmax += xofs;
454  }
455 }
456 
458 {
460 
461  LISTBASE_FOREACH (uiBut *, bt, &block->buttons) {
462  bt->flag &= ~UI_SCROLLED;
463  }
464 
465  if (block->buttons.first == block->buttons.last) {
466  return;
467  }
468 
469  /* mark buttons that are outside boundary */
470  LISTBASE_FOREACH (uiBut *, bt, &block->buttons) {
471  if (bt->rect.ymin < block->rect.ymin) {
472  bt->flag |= UI_SCROLLED;
473  block->flag |= UI_BLOCK_CLIPBOTTOM;
474  }
475  if (bt->rect.ymax > block->rect.ymax) {
476  bt->flag |= UI_SCROLLED;
477  block->flag |= UI_BLOCK_CLIPTOP;
478  }
479  }
480 
481  /* mark buttons overlapping arrows, if we have them */
482  LISTBASE_FOREACH (uiBut *, bt, &block->buttons) {
483  if (block->flag & UI_BLOCK_CLIPBOTTOM) {
484  if (bt->rect.ymin < block->rect.ymin + UI_MENU_SCROLL_ARROW) {
485  bt->flag |= UI_SCROLLED;
486  }
487  }
488  if (block->flag & UI_BLOCK_CLIPTOP) {
489  if (bt->rect.ymax > block->rect.ymax - UI_MENU_SCROLL_ARROW) {
490  bt->flag |= UI_SCROLLED;
491  }
492  }
493  }
494 }
495 
497 {
498  wmWindow *ctx_win = CTX_wm_window(C);
499  ScrArea *ctx_area = CTX_wm_area(C);
500  ARegion *ctx_region = CTX_wm_region(C);
501 
503  wmWindow *win = ctx_win;
504  bScreen *screen = CTX_wm_screen(C);
505 
506  /* There may actually be a different window active than the one showing the popup, so lookup real
507  * one. */
508  if (BLI_findindex(&screen->regionbase, handle->region) == -1) {
509  LISTBASE_FOREACH (wmWindow *, win_iter, &wm->windows) {
510  screen = WM_window_get_active_screen(win_iter);
511  if (BLI_findindex(&screen->regionbase, handle->region) != -1) {
512  win = win_iter;
513  break;
514  }
515  }
516  }
517 
518  BLI_assert(win && screen);
519 
520  CTX_wm_window_set(C, win);
521  ui_region_temp_remove(C, screen, handle->region);
522 
523  /* Reset context (area and region were nullptr'ed when changing context window). */
524  CTX_wm_window_set(C, ctx_win);
525  CTX_wm_area_set(C, ctx_area);
526  CTX_wm_region_set(C, ctx_region);
527 
528  /* reset to region cursor (only if there's not another menu open) */
529  if (BLI_listbase_is_empty(&screen->regionbase)) {
530  win->tag_cursor_refresh = true;
531  }
532 
533  if (handle->scrolltimer) {
534  WM_event_remove_timer(wm, win, handle->scrolltimer);
535  }
536 }
537 
539  uiPopupBlockHandle *handle,
540  ARegion *butregion,
541  uiBut *but)
542 {
543  const int margin = UI_POPUP_MARGIN;
544  wmWindow *window = CTX_wm_window(C);
545  ARegion *region = handle->region;
546 
548  const uiBlockHandleCreateFunc handle_create_func = handle->popup_create_vars.handle_create_func;
549  void *arg = handle->popup_create_vars.arg;
550 
551  uiBlock *block_old = static_cast<uiBlock *>(region->uiblocks.first);
552  uiBlock *block;
553 
554  handle->refresh = (block_old != nullptr);
555 
556  BLI_assert(!handle->refresh || handle->can_refresh);
557 
558 #ifdef DEBUG
559  wmEvent *event_back = window->eventstate;
560  wmEvent *event_last_back = window->event_last_handled;
561 #endif
562 
563  /* create ui block */
564  if (create_func) {
565  block = create_func(C, region, arg);
566  }
567  else {
568  block = handle_create_func(C, handle, arg);
569  }
570 
571  /* callbacks _must_ leave this for us, otherwise we can't call UI_block_update_from_old */
572  BLI_assert(!block->endblock);
573 
574  /* ensure we don't use mouse coords here! */
575 #ifdef DEBUG
576  window->eventstate = nullptr;
577 #endif
578 
579  if (block->handle) {
580  memcpy(block->handle, handle, sizeof(uiPopupBlockHandle));
581  MEM_freeN(handle);
582  handle = block->handle;
583  }
584  else {
585  block->handle = handle;
586  }
587 
588  region->regiondata = handle;
589 
590  /* set UI_BLOCK_NUMSELECT before UI_block_end() so we get alphanumeric keys assigned */
591  if (but == nullptr) {
592  block->flag |= UI_BLOCK_POPUP;
593  }
594 
595  block->flag |= UI_BLOCK_LOOP;
597 
598  /* defer this until blocks are translated (below) */
599  block->oldblock = nullptr;
600 
601  if (!block->endblock) {
603  C, block, handle->popup_create_vars.event_xy, handle->popup_create_vars.event_xy);
604  }
605 
606  /* if this is being created from a button */
607  if (but) {
608  block->aspect = but->block->aspect;
609  ui_popup_block_position(window, butregion, but, block);
610  handle->direction = block->direction;
611  }
612  else {
613  /* Keep a list of these, needed for pull-down menus. */
614  uiSafetyRct *saferct = MEM_cnew<uiSafetyRct>(__func__);
615  saferct->safety = block->safety;
616  BLI_addhead(&block->saferct, saferct);
617  }
618 
619  if (block->flag & UI_BLOCK_RADIAL) {
620  const int win_width = UI_SCREEN_MARGIN;
621  int winx, winy;
622 
623  int x_offset = 0, y_offset = 0;
624 
625  winx = WM_window_pixels_x(window);
626  winy = WM_window_pixels_y(window);
627 
629 
630  /* only try translation if area is large enough */
631  if (BLI_rctf_size_x(&block->rect) < winx - (2.0f * win_width)) {
632  if (block->rect.xmin < win_width) {
633  x_offset += win_width - block->rect.xmin;
634  }
635  if (block->rect.xmax > winx - win_width) {
636  x_offset += winx - win_width - block->rect.xmax;
637  }
638  }
639 
640  if (BLI_rctf_size_y(&block->rect) < winy - (2.0f * win_width)) {
641  if (block->rect.ymin < win_width) {
642  y_offset += win_width - block->rect.ymin;
643  }
644  if (block->rect.ymax > winy - win_width) {
645  y_offset += winy - win_width - block->rect.ymax;
646  }
647  }
648  /* if we are offsetting set up initial data for timeout functionality */
649 
650  if ((x_offset != 0) || (y_offset != 0)) {
651  block->pie_data.pie_center_spawned[0] += x_offset;
652  block->pie_data.pie_center_spawned[1] += y_offset;
653 
654  UI_block_translate(block, x_offset, y_offset);
655 
656  if (U.pie_initial_timeout > 0) {
658  }
659  }
660 
661  region->winrct.xmin = 0;
662  region->winrct.xmax = winx;
663  region->winrct.ymin = 0;
664  region->winrct.ymax = winy;
665 
667 
668  /* lastly set the buttons at the center of the pie menu, ready for animation */
669  if (U.pie_animation_timeout > 0) {
670  LISTBASE_FOREACH (uiBut *, but_iter, &block->buttons) {
671  if (but_iter->pie_dir != UI_RADIAL_NONE) {
672  BLI_rctf_recenter(&but_iter->rect, UNPACK2(block->pie_data.pie_center_spawned));
673  }
674  }
675  }
676  }
677  else {
678  /* Add an offset to draw the popover arrow. */
679  if ((block->flag & UI_BLOCK_POPOVER) && ELEM(block->direction, UI_DIR_UP, UI_DIR_DOWN)) {
680  /* Keep sync with 'ui_draw_popover_back_impl'. */
681  const float unit_size = U.widget_unit / block->aspect;
682  const float unit_half = unit_size * (block->direction == UI_DIR_DOWN ? 0.5 : -0.5);
683 
684  UI_block_translate(block, 0, -unit_half);
685  }
686 
687  /* clip block with window boundary */
688  ui_popup_block_clip(window, block);
689 
690  /* Avoid menu moving down and losing cursor focus by keeping it at
691  * the same height. */
692  if (handle->refresh && handle->prev_block_rect.ymax > block->rect.ymax) {
694  const float offset = handle->prev_block_rect.ymax - block->rect.ymax;
695  UI_block_translate(block, 0, offset);
696  block->rect.ymin = handle->prev_block_rect.ymin;
697  }
698  }
699 
700  handle->prev_block_rect = block->rect;
701 
702  /* the block and buttons were positioned in window space as in 2.4x, now
703  * these menu blocks are regions so we bring it back to region space.
704  * additionally we add some padding for the menu shadow or rounded menus */
705  region->winrct.xmin = block->rect.xmin - margin;
706  region->winrct.xmax = block->rect.xmax + margin;
707  region->winrct.ymin = block->rect.ymin - margin;
708  region->winrct.ymax = block->rect.ymax + UI_POPUP_MENU_TOP;
709 
710  UI_block_translate(block, -region->winrct.xmin, -region->winrct.ymin);
711 
712  /* apply scroll offset */
713  if (handle->scrolloffset != 0.0f) {
714  LISTBASE_FOREACH (uiBut *, bt, &block->buttons) {
715  bt->rect.ymin += handle->scrolloffset;
716  bt->rect.ymax += handle->scrolloffset;
717  }
718  }
719  }
720 
721  if (block_old) {
722  block->oldblock = block_old;
723  UI_block_update_from_old(C, block);
724  UI_blocklist_free_inactive(C, region);
725  }
726 
727  /* checks which buttons are visible, sets flags to prevent draw (do after region init) */
729 
730  /* adds subwindow */
731  ED_region_floating_init(region);
732 
733  /* get winmat now that we actually have the subwindow */
734  wmGetProjectionMatrix(block->winmat, &region->winrct);
735 
736  /* notify change and redraw */
737  ED_region_tag_redraw(region);
738 
739  ED_region_update_rect(region);
740 
741 #ifdef DEBUG
742  window->eventstate = event_back;
743  window->event_last_handled = event_last_back;
744 #endif
745 
746  return block;
747 }
748 
750  ARegion *butregion,
751  uiBut *but,
753  uiBlockHandleCreateFunc handle_create_func,
754  void *arg,
755  uiFreeArgFunc arg_free)
756 {
757  wmWindow *window = CTX_wm_window(C);
758  uiBut *activebut = UI_context_active_but_get(C);
759  static ARegionType type;
760  ARegion *region;
761  uiBlock *block;
762 
763  /* disable tooltips from buttons below */
764  if (activebut) {
765  UI_but_tooltip_timer_remove(C, activebut);
766  }
767  /* standard cursor by default */
769 
770  /* create handle */
771  uiPopupBlockHandle *handle = MEM_cnew<uiPopupBlockHandle>(__func__);
772 
773  /* store context for operator */
774  handle->ctx_area = CTX_wm_area(C);
775  handle->ctx_region = CTX_wm_region(C);
776 
777  /* store vars to refresh popup (RGN_REFRESH_UI) */
779  handle->popup_create_vars.handle_create_func = handle_create_func;
780  handle->popup_create_vars.arg = arg;
781  handle->popup_create_vars.arg_free = arg_free;
782  handle->popup_create_vars.but = but;
783  handle->popup_create_vars.butregion = but ? butregion : nullptr;
785 
786  /* don't allow by default, only if popup type explicitly supports it */
787  handle->can_refresh = false;
788 
789  /* create area region */
791  handle->region = region;
792 
793  memset(&type, 0, sizeof(ARegionType));
794  type.draw = ui_block_region_draw;
795  type.layout = ui_block_region_refresh;
796  type.regionid = RGN_TYPE_TEMPORARY;
797  region->type = &type;
798 
800 
801  block = ui_popup_block_refresh(C, handle, butregion, but);
802  handle = block->handle;
803 
804  /* keep centered on window resizing */
807  }
808 
809  return handle;
810 }
811 
813 {
814  /* If this popup is created from a popover which does NOT have keep-open flag set,
815  * then close the popover too. We could extend this to other popup types too. */
816  ARegion *region = handle->popup_create_vars.butregion;
817  if (region != nullptr) {
818  LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
819  if (block->handle && (block->flag & UI_BLOCK_POPOVER) &&
820  (block->flag & UI_BLOCK_KEEP_OPEN) == 0) {
821  uiPopupBlockHandle *menu = block->handle;
822  menu->menuretval = UI_RETURN_OK;
823  }
824  }
825  }
826 
827  if (handle->popup_create_vars.arg_free) {
829  }
830 
831  ui_popup_block_remove(C, handle);
832 
833  MEM_freeN(handle);
834 }
835 
struct ScrArea * CTX_wm_area(const bContext *C)
Definition: context.c:738
void CTX_wm_region_set(bContext *C, struct ARegion *region)
Definition: context.c:1009
struct wmWindowManager * CTX_wm_manager(const bContext *C)
Definition: context.c:713
struct bScreen * CTX_wm_screen(const bContext *C)
Definition: context.c:733
void CTX_wm_window_set(bContext *C, struct wmWindow *win)
Definition: context.c:966
struct ARegion * CTX_wm_region(const bContext *C)
Definition: context.c:749
void CTX_wm_area_set(bContext *C, struct ScrArea *area)
Definition: context.c:997
struct wmWindow * CTX_wm_window(const bContext *C)
Definition: context.c:723
#define BLI_assert(a)
Definition: BLI_assert.h:46
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
Definition: BLI_listbase.h:269
void BLI_addhead(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:60
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:336
void void void void void BLI_duplicatelist(struct ListBase *dst, const struct ListBase *src) ATTR_NONNULL(1
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
Definition: BLI_listbase.h:354
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition: listbase.c:466
int BLI_findindex(const struct ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
MINLINE float max_ff(float a, float b)
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void copy_v2_v2_int(int r[2], const int a[2])
void BLI_rctf_translate(struct rctf *rect, float x, float y)
Definition: rct.c:566
void BLI_rctf_union(struct rctf *rct_a, const struct rctf *rct_b)
BLI_INLINE float BLI_rctf_cent_y(const struct rctf *rct)
Definition: BLI_rect.h:181
BLI_INLINE float BLI_rctf_cent_x(const struct rctf *rct)
Definition: BLI_rect.h:177
void BLI_rcti_translate(struct rcti *rect, int x, int y)
Definition: rct.c:559
void BLI_rctf_init(struct rctf *rect, float xmin, float xmax, float ymin, float ymax)
Definition: rct.c:407
void BLI_rctf_recenter(struct rctf *rect, float x, float y)
Definition: rct.c:580
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition: BLI_rect.h:194
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition: BLI_rect.h:198
void BLI_rctf_init_minmax(struct rctf *rect)
Definition: rct.c:483
#define UNPACK2(a)
#define ELEM(...)
@ RGN_TYPE_TEMPORARY
@ RGN_REFRESH_UI
void ED_region_tag_refresh_ui(struct ARegion *region)
Definition: area.c:683
void ED_region_floating_init(struct ARegion *region)
Definition: area.c:2057
void ED_region_tag_redraw(struct ARegion *region)
Definition: area.c:655
void ED_region_update_rect(struct ARegion *region)
Definition: area.c:2052
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble right
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble top
Read Guarded memory(de)allocation.
#define C
Definition: RandGen.cpp:25
@ UI_BUT_ALIGN_TOP
Definition: UI_interface.h:269
@ UI_BUT_ALIGN_LEFT
Definition: UI_interface.h:270
#define UI_UNIT_Y
void UI_block_theme_style_set(uiBlock *block, char theme_style)
Definition: interface.cc:3634
@ UI_RETURN_OK
Definition: UI_interface.h:175
@ UI_BLOCK_THEME_STYLE_POPUP
Definition: UI_interface.h:770
uiBut * UI_context_active_but_get(const struct bContext *C)
void UI_blocklist_free_inactive(const struct bContext *C, struct ARegion *region)
@ UI_DIR_CENTER_X
Definition: UI_interface.h:127
@ UI_DIR_CENTER_Y
Definition: UI_interface.h:128
@ UI_DIR_ALL
Definition: UI_interface.h:130
@ UI_DIR_DOWN
Definition: UI_interface.h:124
@ UI_DIR_RIGHT
Definition: UI_interface.h:126
@ UI_DIR_LEFT
Definition: UI_interface.h:125
@ UI_DIR_UP
Definition: UI_interface.h:123
void UI_but_tooltip_timer_remove(struct bContext *C, uiBut *but)
void(* uiFreeArgFunc)(void *arg)
Definition: UI_interface.h:603
@ UI_BLOCK_BOUNDS_POPUP_CENTER
Definition: UI_interface.h:816
void UI_block_draw(const struct bContext *C, struct uiBlock *block)
void UI_block_order_flip(uiBlock *block)
Definition: interface.cc:5815
void UI_region_handlers_add(struct ListBase *handlers)
#define UI_SCREEN_MARGIN
Definition: UI_interface.h:104
#define UI_UNIT_X
void UI_block_end_ex(const struct bContext *C, uiBlock *block, const int xy[2], int r_xy[2])
@ UI_BTYPE_PULLDOWN
Definition: UI_interface.h:358
void UI_block_translate(uiBlock *block, int x, int y)
Definition: interface.cc:356
void UI_block_update_from_old(const struct bContext *C, struct uiBlock *block)
uiBlock *(* uiBlockCreateFunc)(struct bContext *C, struct ARegion *region, void *arg1)
Definition: UI_interface.h:714
@ UI_BLOCK_CLIPBOTTOM
Definition: UI_interface.h:141
@ UI_BLOCK_RADIAL
Definition: UI_interface.h:156
@ UI_BLOCK_LOOP
Definition: UI_interface.h:135
@ UI_BLOCK_KEEP_OPEN
Definition: UI_interface.h:144
@ UI_BLOCK_POPUP
Definition: UI_interface.h:145
@ UI_BLOCK_CLIPTOP
Definition: UI_interface.h:142
@ UI_BLOCK_POPOVER
Definition: UI_interface.h:157
@ UI_BLOCK_NO_WIN_CLIP
Definition: UI_interface.h:140
#define NC_WINDOW
Definition: WM_types.h:325
#define NA_EDITED
Definition: WM_types.h:523
unsigned int U
Definition: btGjkEpa3.h:78
void ui_but_update(uiBut *but)
Definition: interface.cc:3900
void ui_block_to_window_rctf(const ARegion *region, uiBlock *block, rctf *rct_dst, const rctf *rct_src)
Definition: interface.cc:170
float ui_block_calc_pie_segment(uiBlock *block, const float event_xy[2])
#define UI_POPUP_MENU_TOP
@ UI_RADIAL_NONE
#define UI_MENU_SUBMENU_PADDING
uiBlock *(* uiBlockHandleCreateFunc)(struct bContext *C, struct uiPopupBlockHandle *handle, void *arg1)
#define UI_POPUP_MARGIN
#define UI_MENU_PADDING
@ UI_PIE_INITIAL_DIRECTION
#define UI_MENU_SCROLL_ARROW
@ UI_BLOCK_CONTAINS_SUBMENU_BUT
@ UI_SCROLLED
static void ui_popup_block_position(wmWindow *window, ARegion *butregion, uiBut *but, uiBlock *block)
static void ui_block_region_refresh(const bContext *C, ARegion *region)
uiBlock * ui_popup_block_refresh(bContext *C, uiPopupBlockHandle *handle, ARegion *butregion, uiBut *but)
static void ui_block_region_popup_window_listener(const wmRegionListenerParams *params)
static void ui_popup_block_clip(wmWindow *window, uiBlock *block)
void ui_popup_block_scrolltest(uiBlock *block)
void ui_popup_block_free(bContext *C, uiPopupBlockHandle *handle)
void ui_popup_translate(ARegion *region, const int mdiff[2])
static void ui_block_region_draw(const bContext *C, ARegion *region)
static void ui_popup_block_remove(bContext *C, uiPopupBlockHandle *handle)
uiPopupBlockHandle * ui_popup_block_create(bContext *C, ARegion *butregion, uiBut *but, uiBlockCreateFunc create_func, uiBlockHandleCreateFunc handle_create_func, void *arg, uiFreeArgFunc arg_free)
void ui_region_temp_remove(bContext *C, bScreen *screen, ARegion *region)
ARegion * ui_region_temp_add(bScreen *screen)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_gpu_kernel_postfix ccl_global float int int int int float bool int offset
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
static int left
static PyObject * create_func(PyObject *, PyObject *args)
Definition: python.cpp:156
void * regiondata
ListBase handlers
struct ARegionType * type
ListBase uiblocks
void * last
Definition: DNA_listBase.h:31
void * first
Definition: DNA_listBase.h:31
float pie_center_spawned[2]
float pie_center_init[2]
ListBase regionbase
float xmax
Definition: DNA_vec_types.h:69
float xmin
Definition: DNA_vec_types.h:69
float ymax
Definition: DNA_vec_types.h:70
float ymin
Definition: DNA_vec_types.h:70
int ymin
Definition: DNA_vec_types.h:64
int ymax
Definition: DNA_vec_types.h:64
int xmin
Definition: DNA_vec_types.h:63
int xmax
Definition: DNA_vec_types.h:63
float winmat[4][4]
ListBase saferct
uiBlock * oldblock
uiPopupBlockHandle * handle
struct PieMenuData pie_data
ListBase buttons
eBlockBoundsCalc bounds_type
short content_hints
eButType type
uiBlock * block
uiBlockHandleCreateFunc handle_create_func
struct ARegion * butregion
uiBlockCreateFunc create_func
uiFreeArgFunc arg_free
struct ARegion * region
struct wmTimer * scrolltimer
struct ARegion * ctx_region
struct ScrArea * ctx_area
struct uiPopupBlockCreate popup_create_vars
int xy[2]
Definition: WM_types.h:682
unsigned int action
Definition: WM_types.h:308
unsigned int category
Definition: WM_types.h:308
struct wmEvent * eventstate
struct wmEvent * event_last_handled
void WM_cursor_set(wmWindow *win, int curs)
Definition: wm_cursors.c:126
@ WM_CURSOR_DEFAULT
Definition: wm_cursors.h:18
void wmGetProjectionMatrix(float mat[4][4], const rcti *winrct)
Definition: wm_subwindow.c:113
void WM_event_remove_timer(wmWindowManager *wm, wmWindow *UNUSED(win), wmTimer *timer)
Definition: wm_window.c:1682
int WM_window_pixels_y(const wmWindow *win)
Definition: wm_window.c:2082
bScreen * WM_window_get_active_screen(const wmWindow *win)
Definition: wm_window.c:2300
int WM_window_pixels_x(const wmWindow *win)
Definition: wm_window.c:2076