Blender  V3.3
interface_region_popover.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 
27 #include "MEM_guardedalloc.h"
28 
29 #include "DNA_userdef_types.h"
30 
31 #include "BLI_listbase.h"
32 
33 #include "BLI_math_vector.h"
34 #include "BLI_rect.h"
35 #include "BLI_utildefines.h"
36 
37 #include "BKE_context.h"
38 #include "BKE_report.h"
39 #include "BKE_screen.h"
40 
41 #include "ED_screen.h"
42 
43 #include "WM_api.h"
44 #include "WM_types.h"
45 
46 #include "UI_interface.h"
47 
48 #include "interface_intern.h"
50 
51 /* -------------------------------------------------------------------- */
55 struct uiPopover {
60 
61  /* Needed for keymap removal. */
65 
67  void *menu_arg;
68 
69  /* Size in pixels (ui scale applied). */
70  int ui_size_x;
71 
72 #ifdef USE_UI_POPOVER_ONCE
73  bool is_once;
74 #endif
75 };
76 
78 {
79  BLI_assert(pup->ui_size_x != 0);
80 
81  const uiStyle *style = UI_style_get_dpi();
82 
83  pup->block = UI_block_begin(C, nullptr, __func__, UI_EMBOSS);
85 #ifdef USE_UI_POPOVER_ONCE
86  if (pup->is_once) {
88  }
89 #endif
90 
91  pup->layout = UI_block_layout(
92  pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, pup->ui_size_x, 0, 0, style);
93 
94  uiLayoutSetOperatorContext(pup->layout, opcontext);
95 
96  if (pup->but) {
97  if (pup->but->context) {
99  }
100  }
101 
102  pup->block->flag |= UI_BLOCK_NO_FLIP;
103 }
104 
105 static uiBlock *ui_block_func_POPOVER(bContext *C, uiPopupBlockHandle *handle, void *arg_pup)
106 {
107  uiPopover *pup = static_cast<uiPopover *>(arg_pup);
108 
109  /* Create UI block and layout now if it wasn't done between begin/end. */
110  if (!pup->layout) {
112 
113  if (pup->menu_func) {
114  pup->block->handle = handle;
115  pup->menu_func(C, pup->layout, pup->menu_arg);
116  pup->block->handle = nullptr;
117  }
118 
119  pup->layout = nullptr;
120  }
121 
122  /* Setup and resolve UI layout for block. */
123  uiBlock *block = pup->block;
124  int width, height;
125 
126  UI_block_region_set(block, handle->region);
129 
130  const int block_margin = U.widget_unit / 2;
131 
132  if (pup->but) {
133  /* For a header menu we set the direction automatic. */
134  block->minbounds = BLI_rctf_size_x(&pup->but->rect);
135  UI_block_bounds_set_normal(block, block_margin);
136 
137  /* If menu slides out of other menu, override direction. */
138  const bool slideout = ui_block_is_menu(pup->but->block);
139  if (slideout) {
141  }
142 
143  /* Store the button location for positioning the popover arrow hint. */
144  if (!handle->refresh) {
145  float center[2] = {BLI_rctf_cent_x(&pup->but->rect), BLI_rctf_cent_y(&pup->but->rect)};
146  ui_block_to_window_fl(handle->ctx_region, pup->but->block, &center[0], &center[1]);
147  /* These variables aren't used for popovers,
148  * we could add new variables if there is a conflict. */
149  block->bounds_offset[0] = (int)center[0];
150  block->bounds_offset[1] = (int)center[1];
152  }
153  else {
155  }
156 
157  if (!slideout) {
158  ARegion *region = CTX_wm_region(C);
159 
160  if (region && region->panels.first) {
161  /* For regions with panels, prefer to open to top so we can
162  * see the values of the buttons below changing. */
164  }
165  /* Prefer popover from header to be positioned into the editor. */
166  else if (region) {
167  if (RGN_TYPE_IS_HEADER_ANY(region->regiontype)) {
170  }
171  }
172  }
173  }
174 
175  /* Estimated a maximum size so we don't go off-screen for low height
176  * areas near the bottom of the window on refreshes. */
177  handle->max_size_y = UI_UNIT_Y * 16.0f;
178  }
179  else {
180  /* Not attached to a button. */
181  int bounds_offset[2] = {0, 0};
184  UI_block_direction_set(block, block->direction);
185  block->minbounds = UI_MENU_WIDTH_MIN;
186 
187  if (!handle->refresh) {
188  uiBut *but = nullptr;
189  uiBut *but_first = nullptr;
190  LISTBASE_FOREACH (uiBut *, but_iter, &block->buttons) {
191  if ((but_first == nullptr) && ui_but_is_editable(but_iter)) {
192  but_first = but_iter;
193  }
194  if (but_iter->flag & (UI_SELECT | UI_SELECT_DRAW)) {
195  but = but_iter;
196  break;
197  }
198  }
199 
200  if (but) {
201  bounds_offset[0] = -(but->rect.xmin + 0.8f * BLI_rctf_size_x(&but->rect));
202  bounds_offset[1] = -BLI_rctf_cent_y(&but->rect);
203  }
204  else {
205  bounds_offset[0] = -(pup->ui_size_x / 2);
206  bounds_offset[1] = but_first ? -BLI_rctf_cent_y(&but_first->rect) : (UI_UNIT_Y / 2);
207  }
208  copy_v2_v2_int(handle->prev_bounds_offset, bounds_offset);
209  }
210  else {
211  copy_v2_v2_int(bounds_offset, handle->prev_bounds_offset);
212  }
213 
214  UI_block_bounds_set_popup(block, block_margin, bounds_offset);
215  }
216 
217  return block;
218 }
219 
220 static void ui_block_free_func_POPOVER(void *arg_pup)
221 {
222  uiPopover *pup = static_cast<uiPopover *>(arg_pup);
223  if (pup->keymap != nullptr) {
224  wmWindow *window = pup->window;
226  }
227  MEM_freeN(pup);
228 }
229 
231  bContext *C, ARegion *butregion, uiBut *but, uiMenuCreateFunc menu_func, void *arg)
232 {
233  wmWindow *window = CTX_wm_window(C);
234  const uiStyle *style = UI_style_get_dpi();
235  const PanelType *panel_type = (PanelType *)arg;
236 
237  /* Create popover, buttons are created from callback. */
238  uiPopover *pup = MEM_cnew<uiPopover>(__func__);
239  pup->but = but;
240 
241  /* FIXME: maybe one day we want non panel popovers? */
242  {
243  const int ui_units_x = (panel_type->ui_units_x == 0) ? UI_POPOVER_WIDTH_UNITS :
244  panel_type->ui_units_x;
245  /* Scale width by changes to Text Style point size. */
246  const int text_points_max = MAX2(style->widget.points, style->widgetlabel.points);
247  pup->ui_size_x = ui_units_x * U.widget_unit *
248  (text_points_max / (float)UI_DEFAULT_TEXT_POINTS);
249  }
250 
251  pup->menu_func = menu_func;
252  pup->menu_arg = arg;
253 
254 #ifdef USE_UI_POPOVER_ONCE
255  {
256  /* Ideally this would be passed in. */
257  const wmEvent *event = window->eventstate;
258  pup->is_once = (event->type == LEFTMOUSE) && (event->val == KM_PRESS);
259  }
260 #endif
261 
262  /* Create popup block. */
263  uiPopupBlockHandle *handle;
264  handle = ui_popup_block_create(
265  C, butregion, but, nullptr, ui_block_func_POPOVER, pup, ui_block_free_func_POPOVER);
266  handle->can_refresh = true;
267 
268  /* Add handlers. If attached to a button, the button will already
269  * add a modal handler and pass on events. */
270  if (!but) {
271  UI_popup_handlers_add(C, &window->modalhandlers, handle, 0);
272  WM_event_add_mousemove(window);
273  handle->popup = true;
274  }
275 
276  return handle;
277 }
278 
281 /* -------------------------------------------------------------------- */
285 int UI_popover_panel_invoke(bContext *C, const char *idname, bool keep_open, ReportList *reports)
286 {
287  uiLayout *layout;
288  PanelType *pt = WM_paneltype_find(idname, true);
289  if (pt == nullptr) {
290  BKE_reportf(reports, RPT_ERROR, "Panel \"%s\" not found", idname);
291  return OPERATOR_CANCELLED;
292  }
293 
294  if (pt->poll && (pt->poll(C, pt) == false)) {
295  /* cancel but allow event to pass through, just like operators do */
297  }
298 
299  uiBlock *block = nullptr;
300  if (keep_open) {
302  C, nullptr, nullptr, ui_item_paneltype_func, pt);
303  uiPopover *pup = static_cast<uiPopover *>(handle->popup_create_vars.arg);
304  block = pup->block;
305  }
306  else {
307  uiPopover *pup = UI_popover_begin(C, U.widget_unit * pt->ui_units_x, false);
308  layout = UI_popover_layout(pup);
309  UI_paneltype_draw(C, pt, layout);
310  UI_popover_end(C, pup, nullptr);
311  block = pup->block;
312  }
313 
314  if (block) {
315  uiPopupBlockHandle *handle = static_cast<uiPopupBlockHandle *>(block->handle);
317  }
318  return OPERATOR_INTERFACE;
319 }
320 
323 /* -------------------------------------------------------------------- */
327 uiPopover *UI_popover_begin(bContext *C, int ui_menu_width, bool from_active_button)
328 {
329  uiPopover *pup = MEM_cnew<uiPopover>(__func__);
330  if (ui_menu_width == 0) {
331  ui_menu_width = U.widget_unit * UI_POPOVER_WIDTH_UNITS;
332  }
333  pup->ui_size_x = ui_menu_width;
334 
335  ARegion *butregion = nullptr;
336  uiBut *but = nullptr;
337 
338  if (from_active_button) {
339  butregion = CTX_wm_region(C);
340  but = UI_region_active_but_get(butregion);
341  if (but == nullptr) {
342  butregion = nullptr;
343  }
344  }
345 
346  pup->but = but;
347  pup->butregion = butregion;
348 
349  /* Operator context default same as menus, change if needed. */
351 
352  /* create in advance so we can let buttons point to retval already */
353  pup->block->handle = MEM_cnew<uiPopupBlockHandle>(__func__);
354 
355  return pup;
356 }
357 
359 {
360  uiPopover *pup = static_cast<uiPopover *>(user_data);
362 }
363 
365 {
366  wmWindow *window = CTX_wm_window(C);
367  /* Create popup block. No refresh support since the buttons were created
368  * between begin/end and we have no callback to recreate them. */
369  uiPopupBlockHandle *handle;
370 
371  if (keymap) {
372  /* Add so we get keymaps shown in the buttons. */
374  pup->keymap = keymap;
377  }
378 
379  handle = ui_popup_block_create(C,
380  pup->butregion,
381  pup->but,
382  nullptr,
384  pup,
386 
387  /* Add handlers. */
388  UI_popup_handlers_add(C, &window->modalhandlers, handle, 0);
389  WM_event_add_mousemove(window);
390  handle->popup = true;
391 
392  /* Re-add so it gets priority. */
393  if (keymap) {
394  BLI_remlink(&window->modalhandlers, pup->keymap_handler);
395  BLI_addhead(&window->modalhandlers, pup->keymap_handler);
396  }
397 
398  pup->window = window;
399 
400  /* TODO(campbell): we may want to make this configurable.
401  * The begin/end stype of calling popups doesn't allow 'can_refresh' to be set.
402  * For now close this style of popovers when accessed. */
404 
405  /* Panels are created flipped (from event handling POV). */
406  pup->block->flag ^= UI_BLOCK_IS_FLIP;
407 }
408 
410 {
411  return pup->layout;
412 }
413 
414 #ifdef USE_UI_POPOVER_ONCE
416 {
417  pup->is_once = false;
418 }
419 #endif
420 
typedef float(TangentPoint)[2]
struct ARegion * CTX_wm_region(const bContext *C)
Definition: context.c:749
struct wmWindow * CTX_wm_window(const bContext *C)
Definition: context.c:723
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
#define BLI_assert(a)
Definition: BLI_assert.h:46
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 BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:100
MINLINE void copy_v2_v2_int(int r[2], const int a[2])
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
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition: BLI_rect.h:194
#define UNUSED(x)
#define MAX2(a, b)
#define RGN_ALIGN_ENUM_FROM_MASK(align)
@ RGN_ALIGN_BOTTOM
#define RGN_TYPE_IS_HEADER_ANY(regiontype)
@ OPERATOR_CANCELLED
@ OPERATOR_INTERFACE
@ OPERATOR_PASS_THROUGH
NSNotificationCenter * center
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei height
_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 width
Read Guarded memory(de)allocation.
#define C
Definition: RandGen.cpp:25
@ UI_LAYOUT_VERTICAL
#define UI_UNIT_Y
void UI_paneltype_draw(struct bContext *C, struct PanelType *pt, struct uiLayout *layout)
@ UI_EMBOSS
Definition: UI_interface.h:108
const struct uiStyle * UI_style_get_dpi(void)
void UI_block_theme_style_set(uiBlock *block, char theme_style)
Definition: interface.cc:3634
@ UI_RETURN_OK
Definition: UI_interface.h:175
void UI_block_bounds_set_normal(struct uiBlock *block, int addval)
Definition: interface.cc:582
void UI_block_bounds_set_popup(uiBlock *block, int addval, const int bounds_offset[2])
Definition: interface.cc:598
@ UI_BLOCK_THEME_STYLE_POPUP
Definition: UI_interface.h:770
@ UI_LAYOUT_PANEL
void UI_block_flag_disable(uiBlock *block, int flag)
Definition: interface.cc:5853
void uiLayoutContextCopy(uiLayout *layout, struct bContextStore *context)
@ UI_DIR_CENTER_X
Definition: UI_interface.h:127
@ UI_DIR_DOWN
Definition: UI_interface.h:124
@ UI_DIR_RIGHT
Definition: UI_interface.h:126
@ UI_DIR_UP
Definition: UI_interface.h:123
void UI_popup_handlers_add(struct bContext *C, struct ListBase *handlers, uiPopupBlockHandle *popup, char flag)
uiLayout * UI_block_layout(uiBlock *block, int dir, int type, int x, int y, int size, int em, int padding, const struct uiStyle *style)
uiBut * UI_region_active_but_get(const struct ARegion *region)
bool UI_block_active_only_flagged_buttons(const struct bContext *C, struct ARegion *region, struct uiBlock *block)
uiBlock * UI_block_begin(const struct bContext *C, struct ARegion *region, const char *name, eUIEmbossType emboss)
void UI_block_layout_resolve(uiBlock *block, int *r_x, int *r_y)
void(* uiMenuCreateFunc)(struct bContext *C, struct uiLayout *layout, void *arg1)
Definition: UI_interface.h:592
void UI_block_direction_set(uiBlock *block, char direction)
Definition: interface.cc:5810
#define UI_DEFAULT_TEXT_POINTS
Definition: UI_interface.h:235
void UI_block_flag_enable(uiBlock *block, int flag)
Definition: interface.cc:5848
void uiLayoutSetOperatorContext(uiLayout *layout, wmOperatorCallContext opcontext)
void UI_block_region_set(uiBlock *block, struct ARegion *region)
Definition: interface.cc:3551
@ UI_BLOCK_POPOVER_ONCE
Definition: UI_interface.h:158
@ UI_BLOCK_LOOP
Definition: UI_interface.h:135
@ UI_BLOCK_KEEP_OPEN
Definition: UI_interface.h:144
@ UI_BLOCK_IS_FLIP
Definition: UI_interface.h:136
@ UI_BLOCK_SHOW_SHORTCUT_ALWAYS
Definition: UI_interface.h:160
@ UI_BLOCK_NO_FLIP
Definition: UI_interface.h:137
@ UI_BLOCK_POPOVER
Definition: UI_interface.h:157
@ KM_PRESS
Definition: WM_types.h:267
wmOperatorCallContext
Definition: WM_types.h:199
@ WM_OP_INVOKE_REGION_WIN
Definition: WM_types.h:202
@ WM_OP_EXEC_REGION_WIN
Definition: WM_types.h:209
unsigned int U
Definition: btGjkEpa3.h:78
void * user_data
void ui_block_to_window_fl(const ARegion *region, uiBlock *block, float *r_x, float *r_y)
Definition: interface.cc:142
uiPopupBlockHandle * ui_popup_block_create(struct bContext *C, struct ARegion *butregion, uiBut *but, uiBlockCreateFunc create_func, uiBlockHandleCreateFunc handle_create_func, void *arg, uiFreeArgFunc arg_free)
#define UI_POPOVER_WIDTH_UNITS
bool ui_block_is_menu(const uiBlock *block) ATTR_WARN_UNUSED_RESULT
void ui_item_paneltype_func(struct bContext *C, struct uiLayout *layout, void *arg_pt)
bool ui_but_is_editable(const uiBut *but) ATTR_WARN_UNUSED_RESULT
#define UI_MENU_WIDTH_MIN
@ UI_SELECT_DRAW
@ UI_SELECT
void UI_popover_end(bContext *C, uiPopover *pup, wmKeyMap *keymap)
uiPopupBlockHandle * ui_popover_panel_create(bContext *C, ARegion *butregion, uiBut *but, uiMenuCreateFunc menu_func, void *arg)
static void ui_block_free_func_POPOVER(void *arg_pup)
void UI_popover_once_clear(uiPopover *pup)
uiLayout * UI_popover_layout(uiPopover *pup)
static void popover_keymap_fn(wmKeyMap *UNUSED(keymap), wmKeyMapItem *UNUSED(kmi), void *user_data)
int UI_popover_panel_invoke(bContext *C, const char *idname, bool keep_open, ReportList *reports)
static void ui_popover_create_block(bContext *C, uiPopover *pup, wmOperatorCallContext opcontext)
static uiBlock * ui_block_func_POPOVER(bContext *C, uiPopupBlockHandle *handle, void *arg_pup)
uiPopover * UI_popover_begin(bContext *C, int ui_menu_width, bool from_active_button)
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
ListBase panels
short alignment
short regiontype
void * first
Definition: DNA_listBase.h:31
bool(* poll)(const struct bContext *C, struct PanelType *pt)
Definition: BKE_screen.h:242
int ui_units_x
Definition: BKE_screen.h:236
float xmin
Definition: DNA_vec_types.h:69
uiPopupBlockHandle * handle
int bounds_offset[2]
ListBase buttons
struct bContextStore * context
uiBlock * block
uiMenuCreateFunc menu_func
struct wmEventHandler_Keymap * keymap_handler
struct ARegion * region
struct ARegion * ctx_region
struct uiPopupBlockCreate popup_create_vars
uiFontStyle widget
uiFontStyle widgetlabel
struct wmEvent * eventstate
void WM_event_remove_keymap_handler(ListBase *handlers, wmKeyMap *keymap)
wmEventHandler_Keymap * WM_event_add_keymap_handler_priority(ListBase *handlers, wmKeyMap *keymap, int UNUSED(priority))
void WM_event_set_keymap_handler_post_callback(wmEventHandler_Keymap *handler, void(keymap_tag)(wmKeyMap *keymap, wmKeyMapItem *kmi, void *user_data), void *user_data)
void WM_event_add_mousemove(wmWindow *win)
@ LEFTMOUSE
PanelType * WM_paneltype_find(const char *idname, bool quiet)
Definition: wm_panel_type.c:28