Fix T60358: popup clipping within window
[blender.git] / source / blender / editors / interface / interface_region_popup.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2008 Blender Foundation.
19  * All rights reserved.
20  *
21  * Contributor(s): Blender Foundation
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/editors/interface/interface_region_popup.c
27  *  \ingroup edinterface
28  *
29  * PopUp Region (Generic)
30  */
31
32 #include <stdarg.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <assert.h>
36
37 #include "MEM_guardedalloc.h"
38
39 #include "DNA_userdef_types.h"
40
41 #include "BLI_math.h"
42 #include "BLI_listbase.h"
43 #include "BLI_rect.h"
44 #include "BLI_utildefines.h"
45
46 #include "BKE_context.h"
47 #include "BKE_screen.h"
48
49 #include "WM_api.h"
50 #include "WM_types.h"
51 #include "wm_subwindow.h"
52
53 #include "UI_interface.h"
54
55 #include "ED_screen.h"
56
57 #include "interface_intern.h"
58 #include "interface_regions_intern.h"
59
60 /* -------------------------------------------------------------------- */
61 /** \name Utility Functions
62  * \{ */
63
64 /**
65  * Translate any popup regions (so we can drag them).
66  */
67 void ui_popup_translate(bContext *C, ARegion *ar, const int mdiff[2])
68 {
69         uiBlock *block;
70
71         BLI_rcti_translate(&ar->winrct, UNPACK2(mdiff));
72
73         ED_region_update_rect(C, ar);
74
75         ED_region_tag_redraw(ar);
76
77         /* update blocks */
78         for (block = ar->uiblocks.first; block; block = block->next) {
79                 uiSafetyRct *saferct;
80                 for (saferct = block->saferct.first; saferct; saferct = saferct->next) {
81                         BLI_rctf_translate(&saferct->parent, UNPACK2(mdiff));
82                         BLI_rctf_translate(&saferct->safety, UNPACK2(mdiff));
83                 }
84         }
85 }
86
87 /* position block relative to but, result is in window space */
88 static void ui_block_position(wmWindow *window, ARegion *butregion, uiBut *but, uiBlock *block)
89 {
90         uiBut *bt;
91         uiSafetyRct *saferct;
92         rctf butrct;
93         /*float aspect;*/ /*UNUSED*/
94         int size_x, size_y, offset_x = 0, offset_y = 0;
95         short dir1 = 0, dir2 = 0;
96
97         /* transform to window coordinates, using the source button region/block */
98         ui_block_to_window_rctf(butregion, but->block, &butrct, &but->rect);
99
100         /* widget_roundbox_set has this correction too, keep in sync */
101         if (but->type != UI_BTYPE_PULLDOWN) {
102                 if (but->drawflag & UI_BUT_ALIGN_TOP)
103                         butrct.ymax += U.pixelsize;
104                 if (but->drawflag & UI_BUT_ALIGN_LEFT)
105                         butrct.xmin -= U.pixelsize;
106         }
107
108         /* calc block rect */
109         if (block->rect.xmin == 0.0f && block->rect.xmax == 0.0f) {
110                 if (block->buttons.first) {
111                         BLI_rctf_init_minmax(&block->rect);
112
113                         for (bt = block->buttons.first; bt; bt = bt->next) {
114                                 BLI_rctf_union(&block->rect, &bt->rect);
115                         }
116                 }
117                 else {
118                         /* we're nice and allow empty blocks too */
119                         block->rect.xmin = block->rect.ymin = 0;
120                         block->rect.xmax = block->rect.ymax = 20;
121                 }
122         }
123
124         /* aspect = (float)(BLI_rcti_size_x(&block->rect) + 4);*/ /*UNUSED*/
125         ui_block_to_window_rctf(butregion, but->block, &block->rect, &block->rect);
126
127         //block->rect.xmin -= 2.0; block->rect.ymin -= 2.0;
128         //block->rect.xmax += 2.0; block->rect.ymax += 2.0;
129
130         size_x = BLI_rctf_size_x(&block->rect) + 0.2f * UI_UNIT_X;  /* 4 for shadow */
131         size_y = BLI_rctf_size_y(&block->rect) + 0.2f * UI_UNIT_Y;
132         /* aspect /= (float)size_x;*/ /*UNUSED*/
133
134         {
135                 bool left = 0, right = 0, top = 0, down = 0;
136                 // int offscreen;
137
138                 const int win_x = WM_window_pixels_x(window);
139                 const int win_y = WM_window_pixels_y(window);
140                 // wm_window_get_size(window, &win_x, &win_y);
141
142                 const int center_y = (block->direction & UI_DIR_CENTER_Y) ? size_y / 2 : 0;
143
144                 /* check if there's space at all */
145                 if (butrct.xmin - size_x > 0.0f) left = 1;
146                 if (butrct.xmax + size_x < win_x) right = 1;
147                 if (butrct.ymin - size_y + center_y > 0.0f) down = 1;
148                 if (butrct.ymax + size_y - center_y < win_y) top = 1;
149
150                 if (top == 0 && down == 0) {
151                         if (butrct.ymin - size_y < win_y - butrct.ymax - size_y)
152                                 top = 1;
153                         else
154                                 down = 1;
155                 }
156
157                 dir1 = (block->direction & UI_DIR_ALL);
158
159                 /* Secondary directions. */
160                 if (dir1 & (UI_DIR_UP | UI_DIR_DOWN)) {
161                         if      (dir1 & UI_DIR_LEFT)  dir2 = UI_DIR_LEFT;
162                         else if (dir1 & UI_DIR_RIGHT) dir2 = UI_DIR_RIGHT;
163                         dir1 &= (UI_DIR_UP | UI_DIR_DOWN);
164                 }
165
166                 if ((dir2 == 0) && (dir1 == UI_DIR_LEFT || dir1 == UI_DIR_RIGHT)) dir2 = UI_DIR_DOWN;
167                 if ((dir2 == 0) && (dir1 == UI_DIR_UP   || dir1 == UI_DIR_DOWN))  dir2 = UI_DIR_LEFT;
168
169                 /* no space at all? don't change */
170                 if (left || right) {
171                         if (dir1 == UI_DIR_LEFT  && left == 0)  dir1 = UI_DIR_RIGHT;
172                         if (dir1 == UI_DIR_RIGHT && right == 0) dir1 = UI_DIR_LEFT;
173                         /* this is aligning, not append! */
174                         if (dir2 == UI_DIR_LEFT  && right == 0) dir2 = UI_DIR_RIGHT;
175                         if (dir2 == UI_DIR_RIGHT && left == 0)  dir2 = UI_DIR_LEFT;
176                 }
177                 if (down || top) {
178                         if (dir1 == UI_DIR_UP   && top == 0)  dir1 = UI_DIR_DOWN;
179                         if (dir1 == UI_DIR_DOWN && down == 0) dir1 = UI_DIR_UP;
180                         BLI_assert(dir2 != UI_DIR_UP);
181 //                      if (dir2 == UI_DIR_UP   && top == 0)  dir2 = UI_DIR_DOWN;
182                         if (dir2 == UI_DIR_DOWN && down == 0) dir2 = UI_DIR_UP;
183                 }
184
185                 if (dir1 == UI_DIR_LEFT) {
186                         offset_x = butrct.xmin - block->rect.xmax;
187                         if (dir2 == UI_DIR_UP) offset_y = butrct.ymin - block->rect.ymin - center_y - MENU_PADDING;
188                         else                   offset_y = butrct.ymax - block->rect.ymax + center_y + MENU_PADDING;
189                 }
190                 else if (dir1 == UI_DIR_RIGHT) {
191                         offset_x = butrct.xmax - block->rect.xmin;
192                         if (dir2 == UI_DIR_UP) offset_y = butrct.ymin - block->rect.ymin - center_y - MENU_PADDING;
193                         else                   offset_y = butrct.ymax - block->rect.ymax + center_y + MENU_PADDING;
194                 }
195                 else if (dir1 == UI_DIR_UP) {
196                         offset_y = butrct.ymax - block->rect.ymin;
197                         if (dir2 == UI_DIR_RIGHT) offset_x = butrct.xmax - block->rect.xmax;
198                         else                      offset_x = butrct.xmin - block->rect.xmin;
199                         /* changed direction? */
200                         if ((dir1 & block->direction) == 0) {
201                                 UI_block_order_flip(block);
202                         }
203                 }
204                 else if (dir1 == UI_DIR_DOWN) {
205                         offset_y = butrct.ymin - block->rect.ymax;
206                         if (dir2 == UI_DIR_RIGHT) offset_x = butrct.xmax - block->rect.xmax;
207                         else                      offset_x = butrct.xmin - block->rect.xmin;
208                         /* changed direction? */
209                         if ((dir1 & block->direction) == 0) {
210                                 UI_block_order_flip(block);
211                         }
212                 }
213
214                 /* and now we handle the exception; no space below or to top */
215                 if (top == 0 && down == 0) {
216                         if (dir1 == UI_DIR_LEFT || dir1 == UI_DIR_RIGHT) {
217                                 /* align with bottom of screen */
218                                 // offset_y = size_y; (not with menu scrolls)
219                         }
220                 }
221
222 #if 0 /* seems redundant and causes issues with blocks inside big regions */
223                 /* or no space left or right */
224                 if (left == 0 && right == 0) {
225                         if (dir1 == UI_DIR_UP || dir1 == UI_DIR_DOWN) {
226                                 /* align with left size of screen */
227                                 offset_x = -block->rect.xmin + 5;
228                         }
229                 }
230 #endif
231
232 #if 0
233                 /* clamp to window bounds, could be made into an option if its ever annoying */
234                 if (     (offscreen = (block->rect.ymin + offset_y)) < 0) offset_y -= offscreen;   /* bottom */
235                 else if ((offscreen = (block->rect.ymax + offset_y) - winy) > 0) offset_y -= offscreen;  /* top */
236                 if (     (offscreen = (block->rect.xmin + offset_x)) < 0) offset_x -= offscreen;   /* left */
237                 else if ((offscreen = (block->rect.xmax + offset_x) - winx) > 0) offset_x -= offscreen;  /* right */
238 #endif
239         }
240
241         /* apply offset, buttons in window coords */
242
243         for (bt = block->buttons.first; bt; bt = bt->next) {
244                 ui_block_to_window_rctf(butregion, but->block, &bt->rect, &bt->rect);
245
246                 BLI_rctf_translate(&bt->rect, offset_x, offset_y);
247
248                 /* ui_but_update recalculates drawstring size in pixels */
249                 ui_but_update(bt);
250         }
251
252         BLI_rctf_translate(&block->rect, offset_x, offset_y);
253
254         /* safety calculus */
255         {
256                 const float midx = BLI_rctf_cent_x(&butrct);
257                 const float midy = BLI_rctf_cent_y(&butrct);
258
259                 /* when you are outside parent button, safety there should be smaller */
260
261                 /* parent button to left */
262                 if (midx < block->rect.xmin) block->safety.xmin = block->rect.xmin - 3;
263                 else block->safety.xmin = block->rect.xmin - 40;
264                 /* parent button to right */
265                 if (midx > block->rect.xmax) block->safety.xmax = block->rect.xmax + 3;
266                 else block->safety.xmax = block->rect.xmax + 40;
267
268                 /* parent button on bottom */
269                 if (midy < block->rect.ymin) block->safety.ymin = block->rect.ymin - 3;
270                 else block->safety.ymin = block->rect.ymin - 40;
271                 /* parent button on top */
272                 if (midy > block->rect.ymax) block->safety.ymax = block->rect.ymax + 3;
273                 else block->safety.ymax = block->rect.ymax + 40;
274
275                 /* exception for switched pulldowns... */
276                 if (dir1 && (dir1 & block->direction) == 0) {
277                         if (dir2 == UI_DIR_RIGHT) block->safety.xmax = block->rect.xmax + 3;
278                         if (dir2 == UI_DIR_LEFT)  block->safety.xmin = block->rect.xmin - 3;
279                 }
280                 block->direction = dir1;
281         }
282
283         /* keep a list of these, needed for pulldown menus */
284         saferct = MEM_callocN(sizeof(uiSafetyRct), "uiSafetyRct");
285         saferct->parent = butrct;
286         saferct->safety = block->safety;
287         BLI_freelistN(&block->saferct);
288         BLI_duplicatelist(&block->saferct, &but->block->saferct);
289         BLI_addhead(&block->saferct, saferct);
290 }
291
292 /** \} */
293
294 /* -------------------------------------------------------------------- */
295 /** \name Menu Block Creation
296  * \{ */
297
298 static void ui_block_region_draw(const bContext *C, ARegion *ar)
299 {
300         ScrArea *ctx_area = CTX_wm_area(C);
301         ARegion *ctx_region = CTX_wm_region(C);
302         uiBlock *block;
303
304         if (ar->do_draw & RGN_DRAW_REFRESH_UI) {
305                 ScrArea *handle_ctx_area;
306                 ARegion *handle_ctx_region;
307                 uiBlock *block_next;
308
309                 ar->do_draw &= ~RGN_DRAW_REFRESH_UI;
310                 for (block = ar->uiblocks.first; block; block = block_next) {
311                         block_next = block->next;
312                         if (block->handle->can_refresh) {
313                                 handle_ctx_area = block->handle->ctx_area;
314                                 handle_ctx_region = block->handle->ctx_region;
315
316                                 if (handle_ctx_area) {
317                                         CTX_wm_area_set((bContext *)C, handle_ctx_area);
318                                 }
319                                 if (handle_ctx_region) {
320                                         CTX_wm_region_set((bContext *)C, handle_ctx_region);
321                                 }
322                                 ui_popup_block_refresh((bContext *)C, block->handle, NULL, NULL);
323                         }
324                 }
325         }
326
327         CTX_wm_area_set((bContext *)C, ctx_area);
328         CTX_wm_region_set((bContext *)C, ctx_region);
329
330         for (block = ar->uiblocks.first; block; block = block->next)
331                 UI_block_draw(C, block);
332 }
333
334 /**
335  * Use to refresh centered popups on screen resizing (for splash).
336  */
337 static void ui_block_region_popup_window_listener(
338         bScreen *UNUSED(sc), ScrArea *UNUSED(sa), ARegion *ar, wmNotifier *wmn)
339 {
340         switch (wmn->category) {
341                 case NC_WINDOW:
342                 {
343                         switch (wmn->action) {
344                                 case NA_EDITED:
345                                 {
346                                         /* window resize */
347                                         ED_region_tag_refresh_ui(ar);
348                                         break;
349                                 }
350                         }
351                         break;
352                 }
353         }
354 }
355
356 static void ui_popup_block_clip(wmWindow *window, uiBlock *block)
357 {
358         uiBut *bt;
359         const float xmin_orig = block->rect.xmin;
360         const int margin = UI_SCREEN_MARGIN;
361         int winx, winy;
362
363         if (block->flag & UI_BLOCK_NO_WIN_CLIP) {
364                 return;
365         }
366
367         winx = WM_window_pixels_x(window);
368         winy = WM_window_pixels_y(window);
369
370         /* shift to left if outside of view */
371         if (block->rect.xmax > winx - margin) {
372                 const float xofs = winx - margin - block->rect.xmax;
373                 block->rect.xmin += xofs;
374                 block->rect.xmax += xofs;
375         }
376         /* shift menus to right if outside of view */
377         if (block->rect.xmin < margin) {
378                 const float xofs = (margin - block->rect.xmin);
379                 block->rect.xmin += xofs;
380                 block->rect.xmax += xofs;
381         }
382
383         if (block->rect.ymin < margin) {
384                 block->rect.ymin = margin;
385         }
386         if (block->rect.ymax > winy - UI_POPUP_MENU_TOP) {
387                 block->rect.ymax = winy - UI_POPUP_MENU_TOP;
388         }
389
390         /* ensure menu items draw inside left/right boundary */
391         const float xofs = block->rect.xmin - xmin_orig;
392         for (bt = block->buttons.first; bt; bt = bt->next) {
393                 bt->rect.xmin += xofs;
394                 bt->rect.xmax += xofs;
395         }
396 }
397
398 void ui_popup_block_scrolltest(uiBlock *block)
399 {
400         uiBut *bt;
401
402         block->flag &= ~(UI_BLOCK_CLIPBOTTOM | UI_BLOCK_CLIPTOP);
403
404         for (bt = block->buttons.first; bt; bt = bt->next)
405                 bt->flag &= ~UI_SCROLLED;
406
407         if (block->buttons.first == block->buttons.last)
408                 return;
409
410         /* mark buttons that are outside boundary */
411         for (bt = block->buttons.first; bt; bt = bt->next) {
412                 if (bt->rect.ymin < block->rect.ymin) {
413                         bt->flag |= UI_SCROLLED;
414                         block->flag |= UI_BLOCK_CLIPBOTTOM;
415                 }
416                 if (bt->rect.ymax > block->rect.ymax) {
417                         bt->flag |= UI_SCROLLED;
418                         block->flag |= UI_BLOCK_CLIPTOP;
419                 }
420         }
421
422         /* mark buttons overlapping arrows, if we have them */
423         for (bt = block->buttons.first; bt; bt = bt->next) {
424                 if (block->flag & UI_BLOCK_CLIPBOTTOM) {
425                         if (bt->rect.ymin < block->rect.ymin + UI_MENU_SCROLL_ARROW)
426                                 bt->flag |= UI_SCROLLED;
427                 }
428                 if (block->flag & UI_BLOCK_CLIPTOP) {
429                         if (bt->rect.ymax > block->rect.ymax - UI_MENU_SCROLL_ARROW)
430                                 bt->flag |= UI_SCROLLED;
431                 }
432         }
433 }
434
435 static void ui_popup_block_remove(bContext *C, uiPopupBlockHandle *handle)
436 {
437         wmWindow *win = CTX_wm_window(C);
438         bScreen *sc = CTX_wm_screen(C);
439
440         ui_region_temp_remove(C, sc, handle->region);
441
442         /* reset to region cursor (only if there's not another menu open) */
443         if (BLI_listbase_is_empty(&sc->regionbase)) {
444                 ED_region_cursor_set(win, CTX_wm_area(C), CTX_wm_region(C));
445                 /* in case cursor needs to be changed again */
446                 WM_event_add_mousemove(C);
447         }
448
449         if (handle->scrolltimer)
450                 WM_event_remove_timer(CTX_wm_manager(C), win, handle->scrolltimer);
451 }
452
453 /**
454  * Called for creating new popups and refreshing existing ones.
455  */
456 uiBlock *ui_popup_block_refresh(
457         bContext *C, uiPopupBlockHandle *handle,
458         ARegion *butregion, uiBut *but)
459 {
460         BLI_assert(handle->can_refresh == true);
461
462         const int margin = UI_POPUP_MARGIN;
463         wmWindow *window = CTX_wm_window(C);
464         ARegion *ar = handle->region;
465
466         uiBlockCreateFunc create_func = handle->popup_create_vars.create_func;
467         uiBlockHandleCreateFunc handle_create_func = handle->popup_create_vars.handle_create_func;
468         void *arg = handle->popup_create_vars.arg;
469
470         uiBlock *block_old = ar->uiblocks.first;
471         uiBlock *block;
472
473 #ifdef DEBUG
474         wmEvent *event_back = window->eventstate;
475 #endif
476
477         /* create ui block */
478         if (create_func)
479                 block = create_func(C, ar, arg);
480         else
481                 block = handle_create_func(C, handle, arg);
482
483         /* callbacks _must_ leave this for us, otherwise we can't call UI_block_update_from_old */
484         BLI_assert(!block->endblock);
485
486         /* ensure we don't use mouse coords here! */
487 #ifdef DEBUG
488         window->eventstate = NULL;
489 #endif
490
491         if (block->handle) {
492                 memcpy(block->handle, handle, sizeof(uiPopupBlockHandle));
493                 MEM_freeN(handle);
494                 handle = block->handle;
495         }
496         else
497                 block->handle = handle;
498
499         ar->regiondata = handle;
500
501         /* set UI_BLOCK_NUMSELECT before UI_block_end() so we get alphanumeric keys assigned */
502         if (but == NULL) {
503                 block->flag |= UI_BLOCK_POPUP;
504         }
505
506         block->flag |= UI_BLOCK_LOOP;
507         UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
508
509         /* defer this until blocks are translated (below) */
510         block->oldblock = NULL;
511
512         if (!block->endblock) {
513                 UI_block_end_ex(C, block, handle->popup_create_vars.event_xy, handle->popup_create_vars.event_xy);
514         }
515
516         /* if this is being created from a button */
517         if (but) {
518                 block->aspect = but->block->aspect;
519                 ui_block_position(window, butregion, but, block);
520                 handle->direction = block->direction;
521         }
522         else {
523                 uiSafetyRct *saferct;
524                 /* keep a list of these, needed for pulldown menus */
525                 saferct = MEM_callocN(sizeof(uiSafetyRct), "uiSafetyRct");
526                 saferct->safety = block->safety;
527                 BLI_addhead(&block->saferct, saferct);
528         }
529
530         if (block->flag & UI_BLOCK_RADIAL) {
531                 int win_width = UI_SCREEN_MARGIN;
532                 int winx, winy;
533
534                 int x_offset = 0, y_offset = 0;
535
536                 winx = WM_window_pixels_x(window);
537                 winy = WM_window_pixels_y(window);
538
539                 copy_v2_v2(block->pie_data.pie_center_init, block->pie_data.pie_center_spawned);
540
541                 /* only try translation if area is large enough */
542                 if (BLI_rctf_size_x(&block->rect) < winx - (2.0f * win_width)) {
543                         if (block->rect.xmin < win_width )   x_offset += win_width - block->rect.xmin;
544                         if (block->rect.xmax > winx - win_width) x_offset += winx - win_width - block->rect.xmax;
545                 }
546
547                 if (BLI_rctf_size_y(&block->rect) < winy - (2.0f * win_width)) {
548                         if (block->rect.ymin < win_width )   y_offset += win_width - block->rect.ymin;
549                         if (block->rect.ymax > winy - win_width) y_offset += winy - win_width - block->rect.ymax;
550                 }
551                 /* if we are offsetting set up initial data for timeout functionality */
552
553                 if ((x_offset != 0) || (y_offset != 0)) {
554                         block->pie_data.pie_center_spawned[0] += x_offset;
555                         block->pie_data.pie_center_spawned[1] += y_offset;
556
557                         ui_block_translate(block, x_offset, y_offset);
558
559                         if (U.pie_initial_timeout > 0)
560                                 block->pie_data.flags |= UI_PIE_INITIAL_DIRECTION;
561                 }
562
563                 ar->winrct.xmin = 0;
564                 ar->winrct.xmax = winx;
565                 ar->winrct.ymin = 0;
566                 ar->winrct.ymax = winy;
567
568                 ui_block_calc_pie_segment(block, block->pie_data.pie_center_init);
569
570                 /* lastly set the buttons at the center of the pie menu, ready for animation */
571                 if (U.pie_animation_timeout > 0) {
572                         for (uiBut *but_iter = block->buttons.first; but_iter; but_iter = but_iter->next) {
573                                 if (but_iter->pie_dir != UI_RADIAL_NONE) {
574                                         BLI_rctf_recenter(&but_iter->rect, UNPACK2(block->pie_data.pie_center_spawned));
575                                 }
576                         }
577                 }
578         }
579         else {
580                 /* clip block with window boundary */
581                 ui_popup_block_clip(window, block);
582                 /* the block and buttons were positioned in window space as in 2.4x, now
583                  * these menu blocks are regions so we bring it back to region space.
584                  * additionally we add some padding for the menu shadow or rounded menus */
585                 ar->winrct.xmin = block->rect.xmin - margin;
586                 ar->winrct.xmax = block->rect.xmax + margin;
587                 ar->winrct.ymin = block->rect.ymin - margin;
588                 ar->winrct.ymax = block->rect.ymax + UI_POPUP_MENU_TOP;
589
590                 ui_block_translate(block, -ar->winrct.xmin, -ar->winrct.ymin);
591         }
592
593         if (block_old) {
594                 block->oldblock = block_old;
595                 UI_block_update_from_old(C, block);
596                 UI_blocklist_free_inactive(C, &ar->uiblocks);
597         }
598
599         /* checks which buttons are visible, sets flags to prevent draw (do after region init) */
600         ui_popup_block_scrolltest(block);
601
602         /* adds subwindow */
603         ED_region_init(C, ar);
604
605         /* get winmat now that we actually have the subwindow */
606         wmSubWindowSet(window, ar->swinid);
607
608         wm_subwindow_matrix_get(window, ar->swinid, block->winmat);
609
610         /* notify change and redraw */
611         ED_region_tag_redraw(ar);
612
613         ED_region_update_rect(C, ar);
614
615 #ifdef DEBUG
616         window->eventstate = event_back;
617 #endif
618
619         return block;
620 }
621
622 uiPopupBlockHandle *ui_popup_block_create(
623         bContext *C, ARegion *butregion, uiBut *but,
624         uiBlockCreateFunc create_func, uiBlockHandleCreateFunc handle_create_func,
625         void *arg)
626 {
627         wmWindow *window = CTX_wm_window(C);
628         uiBut *activebut = UI_context_active_but_get(C);
629         static ARegionType type;
630         ARegion *ar;
631         uiBlock *block;
632         uiPopupBlockHandle *handle;
633
634         /* disable tooltips from buttons below */
635         if (activebut) {
636                 UI_but_tooltip_timer_remove(C, activebut);
637         }
638         /* standard cursor by default */
639         WM_cursor_set(window, CURSOR_STD);
640
641         /* create handle */
642         handle = MEM_callocN(sizeof(uiPopupBlockHandle), "uiPopupBlockHandle");
643
644         /* store context for operator */
645         handle->ctx_area = CTX_wm_area(C);
646         handle->ctx_region = CTX_wm_region(C);
647
648         /* store vars to refresh popup (RGN_DRAW_REFRESH_UI) */
649         handle->popup_create_vars.create_func = create_func;
650         handle->popup_create_vars.handle_create_func = handle_create_func;
651         handle->popup_create_vars.arg = arg;
652         handle->popup_create_vars.butregion = but ? butregion : NULL;
653         copy_v2_v2_int(handle->popup_create_vars.event_xy, &window->eventstate->x);
654         /* caller may free vars used to create this popup, in that case this variable should be disabled. */
655         handle->can_refresh = true;
656
657         /* create area region */
658         ar = ui_region_temp_add(CTX_wm_screen(C));
659         handle->region = ar;
660
661         memset(&type, 0, sizeof(ARegionType));
662         type.draw = ui_block_region_draw;
663         type.regionid = RGN_TYPE_TEMPORARY;
664         ar->type = &type;
665
666         UI_region_handlers_add(&ar->handlers);
667
668         block = ui_popup_block_refresh(C, handle, butregion, but);
669         handle = block->handle;
670
671         /* keep centered on window resizing */
672         if ((block->bounds_type == UI_BLOCK_BOUNDS_POPUP_CENTER) && handle->can_refresh) {
673                 type.listener = ui_block_region_popup_window_listener;
674         }
675
676         return handle;
677 }
678
679 void ui_popup_block_free(bContext *C, uiPopupBlockHandle *handle)
680 {
681         ui_popup_block_remove(C, handle);
682
683         MEM_freeN(handle);
684 }
685
686 /** \} */