Blender V5.0
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_utf8.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
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;
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
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
137 0,
138 0,
139 200,
140 0,
141 0,
142 style);
143
144 /* NOTE: #wmEvent.xy is where we started dragging in case of #KM_PRESS_DRAG. */
145 pie->mx = event->xy[0];
146 pie->my = event->xy[1];
147
148 /* create title button */
149 if (title[0]) {
150 uiBut *but;
151 char titlestr[256];
152 int w;
153 if (icon) {
154 SNPRINTF_UTF8(titlestr, " %s", title);
155 w = ui_pie_menu_title_width(titlestr, icon);
156 but = uiDefIconTextBut(
157 pie->pie_block, ButType::Label, 0, icon, titlestr, 0, 0, w, UI_UNIT_Y, nullptr, "");
158 }
159 else {
160 w = ui_pie_menu_title_width(title, 0);
161 but = uiDefBut(
162 pie->pie_block, ButType::Label, 0, title, 0, 0, w, UI_UNIT_Y, nullptr, 0.0, 0.0, "");
163 }
164 /* do not align left */
166 pie->pie_block->pie_data.title = but->str.c_str();
167 pie->pie_block->pie_data.icon = icon;
168 }
169
170 return pie;
171}
172
174{
175 wmWindow *window = CTX_wm_window(C);
176
178 C, nullptr, nullptr, nullptr, ui_block_func_PIE, pie, nullptr, false);
179 menu->popup = true;
181
184
185 MEM_freeN(pie);
186}
187
189{
190 return pie->layout;
191}
192
193wmOperatorStatus UI_pie_menu_invoke(bContext *C, const char *idname, const wmEvent *event)
194{
195 uiPieMenu *pie;
196 uiLayout *layout;
197 MenuType *mt = WM_menutype_find(idname, true);
198
199 if (mt == nullptr) {
200 printf("%s: named menu \"%s\" not found\n", __func__, idname);
201 return OPERATOR_CANCELLED;
202 }
203
204 if (WM_menutype_poll(C, mt) == false) {
205 /* cancel but allow event to pass through, just like operators do */
207 }
208
209 pie = UI_pie_menu_begin(C, CTX_IFACE_(mt->translation_context, mt->label), ICON_NONE, event);
210 layout = UI_pie_menu_layout(pie);
211
212 UI_menutype_draw(C, mt, layout);
213
214 UI_pie_menu_end(C, pie);
215
216 return OPERATOR_INTERFACE;
217}
218
220
221/* -------------------------------------------------------------------- */
235
237 char title[UI_MAX_NAME_STR]; /* parent pie title, copied for level */
238 int icon; /* parent pie icon, copied for level */
239 int totitem; /* total count of *remaining* items */
240
241 /* needed for calling #uiLayout::operator_enum_items again for new level */
247};
248
252static void ui_pie_menu_level_invoke(bContext *C, void *argN, void *arg2)
253{
254 EnumPropertyItem *item_array = (EnumPropertyItem *)argN;
255 PieMenuLevelData *lvl = (PieMenuLevelData *)arg2;
256 wmWindow *win = CTX_wm_window(C);
257
258 uiPieMenu *pie = UI_pie_menu_begin(C, IFACE_(lvl->title), lvl->icon, win->eventstate);
259 uiLayout *layout = UI_pie_menu_layout(pie);
260
261 layout = &layout->menu_pie();
262
264
266 /* So the context is passed to `itemf` functions (some need it). */
269
270 if (prop) {
271 layout->op_enum_items(
272 lvl->ot, ptr, prop, lvl->properties, lvl->context, lvl->flag, item_array, lvl->totitem);
273 }
274 else {
275 RNA_warning("%s.%s not found", RNA_struct_identifier(ptr.type), lvl->propname.c_str());
276 }
277
278 UI_pie_menu_end(C, pie);
279}
280
283 const StringRefNull propname,
284 IDProperty *properties,
285 const EnumPropertyItem *items,
286 int totitem,
287 const blender::wm::OpCallContext context,
288 const eUI_Item_Flag flag)
289{
290 const int totitem_parent = PIE_MAX_ITEMS - 1;
291 const int totitem_remain = totitem - totitem_parent;
292 const size_t array_size = sizeof(EnumPropertyItem) * totitem_remain;
293
294 /* used as but->func_argN so freeing is handled elsewhere */
295 EnumPropertyItem *remaining = static_cast<EnumPropertyItem *>(
296 MEM_mallocN(array_size + sizeof(EnumPropertyItem), "pie_level_item_array"));
297 memcpy(remaining, items + totitem_parent, array_size);
298 /* A null terminating sentinel element is required. */
299 memset(&remaining[totitem_remain], 0, sizeof(EnumPropertyItem));
300
301 /* yuk, static... issue is we can't reliably free this without doing dangerous changes */
302 static PieMenuLevelData lvl;
303 STRNCPY_UTF8(lvl.title, block->pie_data.title);
304 lvl.totitem = totitem_remain;
305 lvl.ot = ot;
306 lvl.propname = propname;
307 lvl.properties = properties;
308 lvl.context = context;
309 lvl.flag = flag;
310
311 /* add a 'more' menu entry */
312 uiBut *but = uiDefIconTextBut(block,
314 0,
315 ICON_PLUS,
316 "More",
317 0,
318 0,
319 UI_UNIT_X * 3,
320 UI_UNIT_Y,
321 nullptr,
322 "Show more items of this menu");
323 UI_but_funcN_set(but, ui_pie_menu_level_invoke, remaining, &lvl);
324}
325 /* Pie Menu Levels */
wmWindow * CTX_wm_window(const bContext *C)
int BLI_findindex(const ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:586
#define SNPRINTF_UTF8(dst, format,...)
#define STRNCPY_UTF8(dst, src)
Platform independent time functions.
double BLI_time_now_seconds(void)
Definition time.cc:113
#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,...)
#define C
Definition RandGen.cpp:29
uiBut * uiDefIconTextBut(uiBlock *block, uiButTypeWithPointerType but_and_ptr_type, int retval, int icon, blender::StringRef str, int x, int y, short width, short height, void *poin, std::optional< blender::StringRef > tip)
#define UI_UNIT_Y
@ UI_BLOCK_NUMSELECT
@ UI_BLOCK_LOOP
@ UI_BLOCK_PIE_MENU
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)
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)
void UI_block_region_set(uiBlock *block, ARegion *region)
@ UI_BLOCK_THEME_STYLE_POPUP
#define UI_FSTYLE_WIDGET
#define UI_UNIT_X
void UI_block_flag_enable(uiBlock *block, int flag)
@ UI_BUT_TEXT_LEFT
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)
uiBut * uiDefBut(uiBlock *block, uiButTypeWithPointerType but_and_ptr_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)
void UI_menutype_draw(bContext *C, MenuType *mt, uiLayout *layout)
#define UI_MAX_NAME_STR
@ WM_HANDLER_ACCEPT_DBL_CLICK
Definition WM_api.hh:581
@ KM_RELEASE
Definition WM_types.hh:312
@ KM_CLICK
Definition WM_types.hh:313
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
constexpr const char * c_str() const
#define printf(...)
@ UI_PIE_CLICK_STYLE
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
#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)
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 blender::wm::OpCallContext context, const eUI_Item_Flag flag)
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
int2 block_layout_resolve(uiBlock *block)
uiLayout & block_layout(uiBlock *block, LayoutDirection direction, LayoutType type, int x, int y, int size, int em, int padding, const uiStyle *style)
const char * name
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
const char * RNA_struct_identifier(const StructRNA *type)
ARegionRuntimeHandle * runtime
char label[BKE_ST_MAXNAME]
char translation_context[BKE_ST_MAXNAME]
float pie_center_spawned[2]
const char * title
blender::StringRefNull propname
blender::wm::OpCallContext context
char title[UI_MAX_NAME_STR]
PieMenuData pie_data
int bounds_offset[2]
eBlockBoundsCalc bounds_type
std::string str
void op_enum_items(wmOperatorType *ot, const PointerRNA &ptr, PropertyRNA *prop, IDProperty *properties, blender::wm::OpCallContext context, eUI_Item_Flag flag, const EnumPropertyItem *item_array, int totitem, int active=-1)
uiLayout & menu_pie()
wmEventType type
Definition WM_types.hh:757
short val
Definition WM_types.hh:759
struct wmEvent * eventstate
void WM_event_add_mousemove(wmWindow *win)
@ EVENT_NONE
@ LEFTMOUSE
PointerRNA * ptr
Definition wm_files.cc:4238
wmOperatorType * ot
Definition wm_files.cc:4237
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:145