Blender V4.5
interface_region_menu_pie.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2008 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
10
11#include <cstdarg>
12#include <cstdlib>
13#include <cstring>
14
15#include "MEM_guardedalloc.h"
16
17#include "DNA_userdef_types.h"
18
19#include "BLI_listbase.h"
20#include "BLI_string.h"
21#include "BLI_time.h"
22#include "BLI_utildefines.h"
23
24#include "BKE_context.hh"
25#include "BKE_screen.hh"
26
27#include "WM_api.hh"
28#include "WM_types.hh"
29
30#include "RNA_access.hh"
31#include "RNA_path.hh"
32#include "RNA_prototypes.hh"
33
34#include "UI_interface.hh"
35
36#include "BLT_translation.hh"
37
38#include "interface_intern.hh"
40
43
44/* -------------------------------------------------------------------- */
47
48struct uiPieMenu {
49 uiBlock *pie_block; /* radial block of the pie menu (more could be added later) */
51 int mx, my;
52};
53
54static uiBlock *ui_block_func_PIE(bContext * /*C*/, uiPopupBlockHandle *handle, void *arg_pie)
55{
56 uiBlock *block;
57 uiPieMenu *pie = static_cast<uiPieMenu *>(arg_pie);
58 int minwidth, width, height;
59
60 minwidth = UI_MENU_WIDTH_MIN;
61 block = pie->pie_block;
62
63 /* in some cases we create the block before the region,
64 * so we set it delayed here if necessary */
65 if (BLI_findindex(&handle->region->runtime->uiblocks, block) == -1) {
66 UI_block_region_set(block, handle->region);
67 }
68
69 UI_block_layout_resolve(block, &width, &height);
70
73
74 block->minbounds = minwidth;
75 block->bounds = 1;
76 block->bounds_offset[0] = 0;
77 block->bounds_offset[1] = 0;
79
80 block->pie_data.pie_center_spawned[0] = pie->mx;
81 block->pie_data.pie_center_spawned[1] = pie->my;
82
83 return pie->pie_block;
84}
85
86static float ui_pie_menu_title_width(const char *name, int icon)
87{
88 const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
89 return (UI_fontstyle_string_width(fstyle, name) + (UI_UNIT_X * (1.50f + (icon ? 0.25f : 0.0f))));
90}
91
92uiPieMenu *UI_pie_menu_begin(bContext *C, const char *title, int icon, const wmEvent *event)
93{
94 const uiStyle *style = UI_style_get_dpi();
95 short event_type;
96
97 wmWindow *win = CTX_wm_window(C);
98
99 uiPieMenu *pie = MEM_callocN<uiPieMenu>(__func__);
100
101 pie->pie_block = UI_block_begin(C, nullptr, __func__, blender::ui::EmbossType::Emboss);
102 /* may be useful later to allow spawning pies
103 * from old positions */
104 // pie->pie_block->flag |= UI_BLOCK_POPUP_MEMORY;
105 pie->pie_block->puphash = ui_popup_menu_hash(title);
107
108 /* if pie is spawned by a left click, release or click event,
109 * it is always assumed to be click style */
110 if (event->type == LEFTMOUSE || ELEM(event->val, KM_RELEASE, KM_CLICK)) {
114 }
115 else {
116 if (win->pie_event_type_last != EVENT_NONE) {
117 /* original pie key has been released, so don't propagate the event */
118 if (win->pie_event_type_lock == EVENT_NONE) {
119 event_type = EVENT_NONE;
121 }
122 else {
123 event_type = win->pie_event_type_last;
124 }
125 }
126 else {
127 event_type = event->type;
128 }
129
130 pie->pie_block->pie_data.event_type = event_type;
131 win->pie_event_type_lock = event_type;
132 }
133
134 pie->layout = UI_block_layout(
135 pie->pie_block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PIEMENU, 0, 0, 200, 0, 0, style);
136
137 /* NOTE: #wmEvent.xy is where we started dragging in case of #KM_CLICK_DRAG. */
138 pie->mx = event->xy[0];
139 pie->my = event->xy[1];
140
141 /* create title button */
142 if (title[0]) {
143 uiBut *but;
144 char titlestr[256];
145 int w;
146 if (icon) {
147 SNPRINTF(titlestr, " %s", title);
148 w = ui_pie_menu_title_width(titlestr, icon);
149 but = uiDefIconTextBut(pie->pie_block,
151 0,
152 icon,
153 titlestr,
154 0,
155 0,
156 w,
157 UI_UNIT_Y,
158 nullptr,
159 0.0,
160 0.0,
161 "");
162 }
163 else {
164 w = ui_pie_menu_title_width(title, 0);
165 but = uiDefBut(
166 pie->pie_block, UI_BTYPE_LABEL, 0, title, 0, 0, w, UI_UNIT_Y, nullptr, 0.0, 0.0, "");
167 }
168 /* do not align left */
170 pie->pie_block->pie_data.title = but->str.c_str();
171 pie->pie_block->pie_data.icon = icon;
172 }
173
174 return pie;
175}
176
178{
179 wmWindow *window = CTX_wm_window(C);
180
182 C, nullptr, nullptr, nullptr, ui_block_func_PIE, pie, nullptr, false);
183 menu->popup = true;
185
188
189 MEM_freeN(pie);
190}
191
193{
194 return pie->layout;
195}
196
197wmOperatorStatus UI_pie_menu_invoke(bContext *C, const char *idname, const wmEvent *event)
198{
199 uiPieMenu *pie;
200 uiLayout *layout;
201 MenuType *mt = WM_menutype_find(idname, true);
202
203 if (mt == nullptr) {
204 printf("%s: named menu \"%s\" not found\n", __func__, idname);
205 return OPERATOR_CANCELLED;
206 }
207
208 if (WM_menutype_poll(C, mt) == false) {
209 /* cancel but allow event to pass through, just like operators do */
211 }
212
213 pie = UI_pie_menu_begin(C, CTX_IFACE_(mt->translation_context, mt->label), ICON_NONE, event);
214 layout = UI_pie_menu_layout(pie);
215
216 UI_menutype_draw(C, mt, layout);
217
218 UI_pie_menu_end(C, pie);
219
220 return OPERATOR_INTERFACE;
221}
222
224 const StringRefNull title,
225 const StringRefNull opname,
226 const StringRefNull propname,
227 const wmEvent *event)
228{
229 uiPieMenu *pie;
230 uiLayout *layout;
231
232 pie = UI_pie_menu_begin(C, IFACE_(title.c_str()), ICON_NONE, event);
233 layout = UI_pie_menu_layout(pie);
234
235 layout = &layout->menu_pie();
236 uiItemsEnumO(layout, opname, propname);
237
238 UI_pie_menu_end(C, pie);
239
240 return OPERATOR_INTERFACE;
241}
242
244 const char *title,
245 const char *path,
246 const wmEvent *event)
247{
248 PointerRNA r_ptr;
249 PropertyRNA *r_prop;
250 uiPieMenu *pie;
251 uiLayout *layout;
252
253 PointerRNA ctx_ptr = RNA_pointer_create_discrete(nullptr, &RNA_Context, C);
254
255 if (!RNA_path_resolve(&ctx_ptr, path, &r_ptr, &r_prop)) {
256 return OPERATOR_CANCELLED;
257 }
258
259 /* invalid property, only accept enums */
260 if (RNA_property_type(r_prop) != PROP_ENUM) {
261 BLI_assert(0);
262 return OPERATOR_CANCELLED;
263 }
264
265 pie = UI_pie_menu_begin(C, IFACE_(title), ICON_NONE, event);
266
267 layout = UI_pie_menu_layout(pie);
268
269 layout = &layout->menu_pie();
270 layout->prop(&r_ptr, r_prop, RNA_NO_INDEX, 0, UI_ITEM_R_EXPAND, std::nullopt, ICON_NONE);
271
272 UI_pie_menu_end(C, pie);
273
274 return OPERATOR_INTERFACE;
275}
276
278
279/* -------------------------------------------------------------------- */
293
295 char title[UI_MAX_NAME_STR]; /* parent pie title, copied for level */
296 int icon; /* parent pie icon, copied for level */
297 int totitem; /* total count of *remaining* items */
298
299 /* needed for calling uiItemsFullEnumO_array again for new level */
305};
306
310static void ui_pie_menu_level_invoke(bContext *C, void *argN, void *arg2)
311{
312 EnumPropertyItem *item_array = (EnumPropertyItem *)argN;
313 PieMenuLevelData *lvl = (PieMenuLevelData *)arg2;
314 wmWindow *win = CTX_wm_window(C);
315
316 uiPieMenu *pie = UI_pie_menu_begin(C, IFACE_(lvl->title), lvl->icon, win->eventstate);
317 uiLayout *layout = UI_pie_menu_layout(pie);
318
319 layout = &layout->menu_pie();
320
322
324 /* So the context is passed to `itemf` functions (some need it). */
327
328 if (prop) {
330 lvl->ot,
331 ptr,
332 prop,
333 lvl->properties,
334 lvl->context,
335 lvl->flag,
336 item_array,
337 lvl->totitem);
338 }
339 else {
340 RNA_warning("%s.%s not found", RNA_struct_identifier(ptr.type), lvl->propname.c_str());
341 }
342
343 UI_pie_menu_end(C, pie);
344}
345
348 const StringRefNull propname,
349 IDProperty *properties,
350 const EnumPropertyItem *items,
351 int totitem,
352 const wmOperatorCallContext context,
353 const eUI_Item_Flag flag)
354{
355 const int totitem_parent = PIE_MAX_ITEMS - 1;
356 const int totitem_remain = totitem - totitem_parent;
357 const size_t array_size = sizeof(EnumPropertyItem) * totitem_remain;
358
359 /* used as but->func_argN so freeing is handled elsewhere */
360 EnumPropertyItem *remaining = static_cast<EnumPropertyItem *>(
361 MEM_mallocN(array_size + sizeof(EnumPropertyItem), "pie_level_item_array"));
362 memcpy(remaining, items + totitem_parent, array_size);
363 /* A null terminating sentinel element is required. */
364 memset(&remaining[totitem_remain], 0, sizeof(EnumPropertyItem));
365
366 /* yuk, static... issue is we can't reliably free this without doing dangerous changes */
367 static PieMenuLevelData lvl;
368 STRNCPY(lvl.title, block->pie_data.title);
369 lvl.totitem = totitem_remain;
370 lvl.ot = ot;
371 lvl.propname = propname;
372 lvl.properties = properties;
373 lvl.context = context;
374 lvl.flag = flag;
375
376 /* add a 'more' menu entry */
377 uiBut *but = uiDefIconTextBut(block,
379 0,
380 ICON_PLUS,
381 "More",
382 0,
383 0,
384 UI_UNIT_X * 3,
385 UI_UNIT_Y,
386 nullptr,
387 0.0f,
388 0.0f,
389 "Show more items of this menu");
390 UI_but_funcN_set(but, ui_pie_menu_level_invoke, remaining, &lvl);
391}
392 /* Pie Menu Levels */
wmWindow * CTX_wm_window(const bContext *C)
#define BLI_assert(a)
Definition BLI_assert.h:46
int BLI_findindex(const ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:586
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:599
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:688
Platform independent time functions.
double BLI_time_now_seconds(void)
Definition time.cc:65
#define ELEM(...)
#define CTX_IFACE_(context, msgid)
#define IFACE_(msgid)
@ OPERATOR_CANCELLED
@ OPERATOR_INTERFACE
@ OPERATOR_PASS_THROUGH
Read Guarded memory(de)allocation.
#define RNA_warning(format,...)
@ PROP_ENUM
Definition RNA_types.hh:154
#define C
Definition RandGen.cpp:29
@ UI_BLOCK_THEME_STYLE_POPUP
#define UI_UNIT_Y
uiBut * uiDefIconTextBut(uiBlock *block, int type, int retval, int icon, blender::StringRef str, int x, int y, short width, short height, void *poin, float min, float max, std::optional< blender::StringRef > tip)
void UI_block_theme_style_set(uiBlock *block, char theme_style)
uiBlock * UI_block_begin(const bContext *C, ARegion *region, std::string name, blender::ui::EmbossType emboss)
@ UI_BUT_TEXT_LEFT
uiBut * uiDefBut(uiBlock *block, int type, int retval, blender::StringRef str, int x, int y, short width, short height, void *poin, float min, float max, std::optional< blender::StringRef > tip)
const uiStyle * UI_style_get_dpi()
@ UI_BLOCK_BOUNDS_PIE_CENTER
int UI_fontstyle_string_width(const uiFontStyle *fs, const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
void UI_popup_handlers_add(bContext *C, ListBase *handlers, uiPopupBlockHandle *popup, char flag)
@ UI_BLOCK_NUMSELECT
@ UI_BLOCK_LOOP
@ UI_BLOCK_PIE_MENU
void UI_block_region_set(uiBlock *block, ARegion *region)
#define UI_FSTYLE_WIDGET
#define UI_UNIT_X
void UI_block_flag_enable(uiBlock *block, int flag)
@ UI_BTYPE_BUT
@ UI_BTYPE_LABEL
void UI_but_funcN_set(uiBut *but, uiButHandleNFunc funcN, void *argN, void *arg2, uiButArgNFree func_argN_free_fn=MEM_freeN, uiButArgNCopy func_argN_copy_fn=MEM_dupallocN)
@ UI_ITEM_R_EXPAND
void uiItemsFullEnumO_items(uiLayout *layout, wmOperatorType *ot, const PointerRNA &ptr, PropertyRNA *prop, IDProperty *properties, wmOperatorCallContext context, eUI_Item_Flag flag, const EnumPropertyItem *item_array, int totitem, int active=-1)
uiLayout * UI_block_layout(uiBlock *block, int dir, int type, int x, int y, int size, int em, int padding, const uiStyle *style)
@ UI_LAYOUT_VERTICAL
@ UI_LAYOUT_PIEMENU
void UI_menutype_draw(bContext *C, MenuType *mt, uiLayout *layout)
void uiItemsEnumO(uiLayout *layout, blender::StringRefNull opname, blender::StringRefNull propname)
void UI_block_layout_resolve(uiBlock *block, int *r_x, int *r_y)
#define UI_MAX_NAME_STR
@ WM_HANDLER_ACCEPT_DBL_CLICK
Definition WM_api.hh:545
wmOperatorCallContext
Definition WM_types.hh:236
@ KM_RELEASE
Definition WM_types.hh:309
@ KM_CLICK
Definition WM_types.hh:310
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
constexpr const char * c_str() const
#define printf(...)
#define RNA_NO_INDEX
uiPopupBlockHandle * ui_popup_block_create(bContext *C, ARegion *butregion, uiBut *but, uiBlockCreateFunc create_func, uiBlockHandleCreateFunc handle_create_func, void *arg, uiFreeArgFunc arg_free, bool can_refresh)
#define PIE_MAX_ITEMS
@ UI_PIE_CLICK_STYLE
#define UI_MENU_WIDTH_MIN
static float ui_pie_menu_title_width(const char *name, int icon)
uiLayout * UI_pie_menu_layout(uiPieMenu *pie)
wmOperatorStatus UI_pie_menu_invoke(bContext *C, const char *idname, const wmEvent *event)
wmOperatorStatus UI_pie_menu_invoke_from_rna_enum(bContext *C, const char *title, const char *path, const wmEvent *event)
void UI_pie_menu_end(bContext *C, uiPieMenu *pie)
static uiBlock * ui_block_func_PIE(bContext *, uiPopupBlockHandle *handle, void *arg_pie)
void ui_pie_menu_level_create(uiBlock *block, wmOperatorType *ot, const StringRefNull propname, IDProperty *properties, const EnumPropertyItem *items, int totitem, const wmOperatorCallContext context, const eUI_Item_Flag flag)
wmOperatorStatus UI_pie_menu_invoke_from_operator_enum(bContext *C, const StringRefNull title, const StringRefNull opname, const StringRefNull propname, const wmEvent *event)
uiPieMenu * UI_pie_menu_begin(bContext *C, const char *title, int icon, const wmEvent *event)
static void ui_pie_menu_level_invoke(bContext *C, void *argN, void *arg2)
uint ui_popup_menu_hash(const StringRef str)
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
PropertyType RNA_property_type(PropertyRNA *prop)
const char * RNA_struct_identifier(const StructRNA *type)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
bool RNA_path_resolve(const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop)
Definition rna_path.cc:532
ARegionRuntimeHandle * runtime
char label[BKE_ST_MAXNAME]
char translation_context[BKE_ST_MAXNAME]
float pie_center_spawned[2]
const char * title
wmOperatorCallContext context
blender::StringRefNull propname
char title[UI_MAX_NAME_STR]
PieMenuData pie_data
int bounds_offset[2]
eBlockBoundsCalc bounds_type
std::string str
uiLayout & menu_pie()
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
wmEventType type
Definition WM_types.hh:754
short val
Definition WM_types.hh:756
struct wmEvent * eventstate
void WM_event_add_mousemove(wmWindow *win)
@ EVENT_NONE
@ LEFTMOUSE
PointerRNA * ptr
Definition wm_files.cc:4226
wmOperatorType * ot
Definition wm_files.cc:4225
MenuType * WM_menutype_find(const StringRef idname, bool quiet)
bool WM_menutype_poll(bContext *C, MenuType *mt)
void WM_operator_properties_create_ptr(PointerRNA *ptr, wmOperatorType *ot)
void WM_operator_properties_sanitize(PointerRNA *ptr, const bool no_context)
uint8_t flag
Definition wm_window.cc:139