Blender  V2.93
interface_region_popover.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2008 Blender Foundation.
17  * All rights reserved.
18  */
19 
43 #include "MEM_guardedalloc.h"
44 
45 #include "DNA_userdef_types.h"
46 
47 #include "BLI_listbase.h"
48 
49 #include "BLI_math_vector.h"
50 #include "BLI_rect.h"
51 #include "BLI_utildefines.h"
52 
53 #include "BKE_context.h"
54 #include "BKE_report.h"
55 #include "BKE_screen.h"
56 
57 #include "ED_screen.h"
58 
59 #include "WM_api.h"
60 #include "WM_types.h"
61 
62 #include "UI_interface.h"
63 
64 #include "interface_intern.h"
66 
67 /* -------------------------------------------------------------------- */
71 struct uiPopover {
76 
77  /* Needed for keymap removal. */
81 
83  void *menu_arg;
84 
85  /* Size in pixels (ui scale applied). */
86  int ui_size_x;
87 
88 #ifdef USE_UI_POPOVER_ONCE
89  bool is_once;
90 #endif
91 };
92 
93 static void ui_popover_create_block(bContext *C, uiPopover *pup, int opcontext)
94 {
95  BLI_assert(pup->ui_size_x != 0);
96 
97  const uiStyle *style = UI_style_get_dpi();
98 
99  pup->block = UI_block_begin(C, NULL, __func__, UI_EMBOSS);
101 #ifdef USE_UI_POPOVER_ONCE
102  if (pup->is_once) {
104  }
105 #endif
106 
107  pup->layout = UI_block_layout(
108  pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, pup->ui_size_x, 0, 0, style);
109 
110  uiLayoutSetOperatorContext(pup->layout, opcontext);
111 
112  if (pup->but) {
113  if (pup->but->context) {
114  uiLayoutContextCopy(pup->layout, pup->but->context);
115  }
116  }
117 
118  pup->block->flag |= UI_BLOCK_NO_FLIP;
119 }
120 
121 static uiBlock *ui_block_func_POPOVER(bContext *C, uiPopupBlockHandle *handle, void *arg_pup)
122 {
123  uiPopover *pup = arg_pup;
124 
125  /* Create UI block and layout now if it wasn't done between begin/end. */
126  if (!pup->layout) {
128 
129  if (pup->menu_func) {
130  pup->block->handle = handle;
131  pup->menu_func(C, pup->layout, pup->menu_arg);
132  pup->block->handle = NULL;
133  }
134 
135  pup->layout = NULL;
136  }
137 
138  /* Setup and resolve UI layout for block. */
139  uiBlock *block = pup->block;
140  int width, height;
141 
142  UI_block_region_set(block, handle->region);
145 
146  const int block_margin = U.widget_unit / 2;
147 
148  if (pup->but) {
149  /* For a header menu we set the direction automatic. */
150  block->minbounds = BLI_rctf_size_x(&pup->but->rect);
151  UI_block_bounds_set_normal(block, block_margin);
152 
153  /* If menu slides out of other menu, override direction. */
154  const bool slideout = ui_block_is_menu(pup->but->block);
155  if (slideout) {
157  }
158 
159  /* Store the button location for positioning the popover arrow hint. */
160  if (!handle->refresh) {
161  float center[2] = {BLI_rctf_cent_x(&pup->but->rect), BLI_rctf_cent_y(&pup->but->rect)};
162  ui_block_to_window_fl(handle->ctx_region, pup->but->block, &center[0], &center[1]);
163  /* These variables aren't used for popovers,
164  * we could add new variables if there is a conflict. */
165  block->bounds_offset[0] = (int)center[0];
166  block->bounds_offset[1] = (int)center[1];
168  }
169  else {
171  }
172 
173  if (!slideout) {
174  ARegion *region = CTX_wm_region(C);
175 
176  if (region && region->panels.first) {
177  /* For regions with panels, prefer to open to top so we can
178  * see the values of the buttons below changing. */
180  }
181  /* Prefer popover from header to be positioned into the editor. */
182  else if (region) {
183  if (RGN_TYPE_IS_HEADER_ANY(region->regiontype)) {
186  }
187  }
188  }
189  }
190 
191  /* Estimated a maximum size so we don't go offscreen for low height
192  * areas near the bottom of the window on refreshes. */
193  handle->max_size_y = UI_UNIT_Y * 16.0f;
194  }
195  else {
196  /* Not attached to a button. */
197  int bounds_offset[2] = {0, 0};
200  UI_block_direction_set(block, block->direction);
201  block->minbounds = UI_MENU_WIDTH_MIN;
202 
203  if (!handle->refresh) {
204  uiBut *but = NULL;
205  uiBut *but_first = NULL;
206  LISTBASE_FOREACH (uiBut *, but_iter, &block->buttons) {
207  if ((but_first == NULL) && ui_but_is_editable(but_iter)) {
208  but_first = but_iter;
209  }
210  if (but_iter->flag & (UI_SELECT | UI_SELECT_DRAW)) {
211  but = but_iter;
212  break;
213  }
214  }
215 
216  if (but) {
217  bounds_offset[0] = -(but->rect.xmin + 0.8f * BLI_rctf_size_x(&but->rect));
218  bounds_offset[1] = -BLI_rctf_cent_y(&but->rect);
219  }
220  else {
221  bounds_offset[0] = -(pup->ui_size_x / 2);
222  bounds_offset[1] = but_first ? -BLI_rctf_cent_y(&but_first->rect) : (UI_UNIT_Y / 2);
223  }
224  copy_v2_v2_int(handle->prev_bounds_offset, bounds_offset);
225  }
226  else {
227  copy_v2_v2_int(bounds_offset, handle->prev_bounds_offset);
228  }
229 
230  UI_block_bounds_set_popup(block, block_margin, bounds_offset);
231  }
232 
233  return block;
234 }
235 
236 static void ui_block_free_func_POPOVER(void *arg_pup)
237 {
238  uiPopover *pup = arg_pup;
239  if (pup->keymap != NULL) {
240  wmWindow *window = pup->window;
242  }
243  MEM_freeN(pup);
244 }
245 
247  bContext *C, ARegion *butregion, uiBut *but, uiMenuCreateFunc menu_func, void *arg)
248 {
249  wmWindow *window = CTX_wm_window(C);
250  const uiStyle *style = UI_style_get_dpi();
251  const PanelType *panel_type = (PanelType *)arg;
252 
253  /* Create popover, buttons are created from callback. */
254  uiPopover *pup = MEM_callocN(sizeof(uiPopover), __func__);
255  pup->but = but;
256 
257  /* FIXME: maybe one day we want non panel popovers? */
258  {
259  const int ui_units_x = (panel_type->ui_units_x == 0) ? UI_POPOVER_WIDTH_UNITS :
260  panel_type->ui_units_x;
261  /* Scale width by changes to Text Style point size. */
262  const int text_points_max = MAX2(style->widget.points, style->widgetlabel.points);
263  pup->ui_size_x = ui_units_x * U.widget_unit *
264  (text_points_max / (float)UI_DEFAULT_TEXT_POINTS);
265  }
266 
267  pup->menu_func = menu_func;
268  pup->menu_arg = arg;
269 
270 #ifdef USE_UI_POPOVER_ONCE
271  {
272  /* Ideally this would be passed in. */
273  const wmEvent *event = window->eventstate;
274  pup->is_once = (event->type == LEFTMOUSE) && (event->val == KM_PRESS);
275  }
276 #endif
277 
278  /* Create popup block. */
279  uiPopupBlockHandle *handle;
280  handle = ui_popup_block_create(
281  C, butregion, but, NULL, ui_block_func_POPOVER, pup, ui_block_free_func_POPOVER);
282  handle->can_refresh = true;
283 
284  /* Add handlers. If attached to a button, the button will already
285  * add a modal handler and pass on events. */
286  if (!but) {
287  UI_popup_handlers_add(C, &window->modalhandlers, handle, 0);
288  WM_event_add_mousemove(window);
289  handle->popup = true;
290  }
291 
292  return handle;
293 }
294 
297 /* -------------------------------------------------------------------- */
301 int UI_popover_panel_invoke(bContext *C, const char *idname, bool keep_open, ReportList *reports)
302 {
303  uiLayout *layout;
304  PanelType *pt = WM_paneltype_find(idname, true);
305  if (pt == NULL) {
306  BKE_reportf(reports, RPT_ERROR, "Panel \"%s\" not found", idname);
307  return OPERATOR_CANCELLED;
308  }
309 
310  if (pt->poll && (pt->poll(C, pt) == false)) {
311  /* cancel but allow event to pass through, just like operators do */
313  }
314 
315  uiBlock *block = NULL;
316  if (keep_open) {
319  uiPopover *pup = handle->popup_create_vars.arg;
320  block = pup->block;
321  }
322  else {
323  uiPopover *pup = UI_popover_begin(C, U.widget_unit * pt->ui_units_x, false);
324  layout = UI_popover_layout(pup);
325  UI_paneltype_draw(C, pt, layout);
326  UI_popover_end(C, pup, NULL);
327  block = pup->block;
328  }
329 
330  if (block) {
331  uiPopupBlockHandle *handle = block->handle;
333  }
334  return OPERATOR_INTERFACE;
335 }
336 
339 /* -------------------------------------------------------------------- */
349 uiPopover *UI_popover_begin(bContext *C, int ui_menu_width, bool from_active_button)
350 {
351  uiPopover *pup = MEM_callocN(sizeof(uiPopover), "popover menu");
352  if (ui_menu_width == 0) {
353  ui_menu_width = U.widget_unit * UI_POPOVER_WIDTH_UNITS;
354  }
355  pup->ui_size_x = ui_menu_width;
356 
357  ARegion *butregion = NULL;
358  uiBut *but = NULL;
359 
360  if (from_active_button) {
361  butregion = CTX_wm_region(C);
362  but = UI_region_active_but_get(butregion);
363  if (but == NULL) {
364  butregion = NULL;
365  }
366  }
367 
368  pup->but = but;
369  pup->butregion = butregion;
370 
371  /* Operator context default same as menus, change if needed. */
373 
374  /* create in advance so we can let buttons point to retval already */
375  pup->block->handle = MEM_callocN(sizeof(uiPopupBlockHandle), "uiPopupBlockHandle");
376 
377  return pup;
378 }
379 
381 {
382  uiPopover *pup = user_data;
384 }
385 
386 /* set the whole structure to work */
388 {
389  wmWindow *window = CTX_wm_window(C);
390  /* Create popup block. No refresh support since the buttons were created
391  * between begin/end and we have no callback to recreate them. */
392  uiPopupBlockHandle *handle;
393 
394  if (keymap) {
395  /* Add so we get keymaps shown in the buttons. */
397  pup->keymap = keymap;
400  }
401 
402  handle = ui_popup_block_create(
404 
405  /* Add handlers. */
406  UI_popup_handlers_add(C, &window->modalhandlers, handle, 0);
407  WM_event_add_mousemove(window);
408  handle->popup = true;
409 
410  /* Re-add so it gets priority. */
411  if (keymap) {
412  BLI_remlink(&window->modalhandlers, pup->keymap_handler);
413  BLI_addhead(&window->modalhandlers, pup->keymap_handler);
414  }
415 
416  pup->window = window;
417 
418  /* TODO(campbell): we may want to make this configurable.
419  * The begin/end stype of calling popups doesn't allow 'can_refresh' to be set.
420  * For now close this style of popovers when accessed. */
422 
423  /* panels are created flipped (from event handling pov) */
424  pup->block->flag ^= UI_BLOCK_IS_FLIP;
425 }
426 
428 {
429  return pup->layout;
430 }
431 
432 #ifdef USE_UI_POPOVER_ONCE
434 {
435  pup->is_once = false;
436 }
437 #endif
438 
typedef float(TangentPoint)[2]
struct ARegion * CTX_wm_region(const bContext *C)
Definition: context.c:725
struct wmWindow * CTX_wm_window(const bContext *C)
Definition: context.c:699
void BKE_reportf(ReportList *reports, ReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
#define BLI_assert(a)
Definition: BLI_assert.h:58
void BLI_addhead(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:87
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:172
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:133
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:148
BLI_INLINE float BLI_rctf_cent_x(const struct rctf *rct)
Definition: BLI_rect.h:144
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition: BLI_rect.h:161
#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 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
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei height
Read Guarded memory(de)allocation.
#define C
Definition: RandGen.cpp:39
#define UI_UNIT_Y
void UI_paneltype_draw(struct bContext *C, struct PanelType *pt, struct uiLayout *layout)
@ UI_EMBOSS
Definition: UI_interface.h:107
const struct uiStyle * UI_style_get_dpi(void)
void UI_block_theme_style_set(uiBlock *block, char theme_style)
Definition: interface.c:3547
void uiLayoutSetOperatorContext(uiLayout *layout, int opcontext)
@ UI_LAYOUT_PANEL
void UI_block_bounds_set_normal(struct uiBlock *block, int addval)
Definition: interface.c:580
void UI_block_bounds_set_popup(uiBlock *block, int addval, const int bounds_offset[2])
Definition: interface.c:598
void UI_block_flag_disable(uiBlock *block, int flag)
Definition: interface.c:6072
void uiLayoutContextCopy(uiLayout *layout, struct bContextStore *context)
void UI_popup_handlers_add(struct bContext *C, struct ListBase *handlers, uiPopupBlockHandle *popup, const char flag)
@ UI_BLOCK_POPOVER_ONCE
Definition: UI_interface.h:163
@ UI_BLOCK_LOOP
Definition: UI_interface.h:140
@ UI_BLOCK_KEEP_OPEN
Definition: UI_interface.h:149
@ UI_BLOCK_IS_FLIP
Definition: UI_interface.h:141
@ UI_BLOCK_SHOW_SHORTCUT_ALWAYS
Definition: UI_interface.h:165
@ UI_BLOCK_NO_FLIP
Definition: UI_interface.h:142
@ UI_BLOCK_POPOVER
Definition: UI_interface.h:162
uiLayout * UI_block_layout(uiBlock *block, int dir, int type, int x, int y, int size, int em, int padding, const struct uiStyle *style)
@ UI_RETURN_OK
Definition: UI_interface.h:178
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:529
void UI_block_direction_set(uiBlock *block, char direction)
Definition: interface.c:6028
@ UI_DIR_CENTER_X
Definition: UI_interface.h:126
@ UI_DIR_DOWN
Definition: UI_interface.h:123
@ UI_DIR_RIGHT
Definition: UI_interface.h:125
@ UI_DIR_UP
Definition: UI_interface.h:122
#define UI_DEFAULT_TEXT_POINTS
Definition: UI_interface.h:238
@ UI_LAYOUT_VERTICAL
void UI_block_flag_enable(uiBlock *block, int flag)
Definition: interface.c:6067
@ UI_BLOCK_THEME_STYLE_POPUP
Definition: UI_interface.h:671
void UI_block_region_set(uiBlock *block, struct ARegion *region)
Definition: interface.c:3468
@ WM_OP_INVOKE_REGION_WIN
Definition: WM_types.h:198
@ WM_OP_EXEC_REGION_WIN
Definition: WM_types.h:205
#define KM_PRESS
Definition: WM_types.h:242
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.c:134
#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
uiPopupBlockHandle * ui_popup_block_create(struct bContext *C, struct ARegion *butregion, uiBut *but, uiBlockCreateFunc create_func, uiBlockHandleCreateFunc handle_create_func, void *arg, void(*arg_free)(void *arg))
#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)
static void ui_popover_create_block(bContext *C, uiPopover *pup, int opcontext)
int UI_popover_panel_invoke(bContext *C, const char *idname, bool keep_open, ReportList *reports)
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:41
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:45
ListBase panels
short alignment
short regiontype
void * first
Definition: DNA_listBase.h:47
bool(* poll)(const struct bContext *C, struct PanelType *pt)
Definition: BKE_screen.h:260
int ui_units_x
Definition: BKE_screen.h:254
float xmin
Definition: DNA_vec_types.h:85
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:42