Fix items in mode pie changing position by supporting more than 8 items in operator-enum pies
Now a 'More' item is added to the pie when there are too many items. It opens a sub-pie that contains the remaining items. Note that this only touches operator-enum pies (like the object mode pie is), it is not a complete support for pies with more than 8 items. For this further design and code work would be needed, but this is too urgent to wait for that. This is a better fix for T46973, should definitely be applied for 2.77 release. Patch D1800 by myself with some edits by @campbellbarton, thanks!
This commit is contained in:
parent
8f944789cd
commit
21c88df7c7
Notes:
blender-bot
2023-11-21 05:49:08 +01:00
Referenced by issue #115211, Warnings in Blender 4.0
|
@ -977,7 +977,13 @@ void uiItemEnumR(uiLayout *layout, const char *name, int icon, struct PointerRNA
|
|||
void uiItemEnumR_string(uiLayout *layout, struct PointerRNA *ptr, const char *propname, const char *value, const char *name, int icon);
|
||||
void uiItemsEnumR(uiLayout *layout, struct PointerRNA *ptr, const char *propname);
|
||||
void uiItemPointerR(uiLayout *layout, struct PointerRNA *ptr, const char *propname, struct PointerRNA *searchptr, const char *searchpropname, const char *name, int icon);
|
||||
void uiItemsFullEnumO(uiLayout *layout, const char *opname, const char *propname, struct IDProperty *properties, int context, int flag);
|
||||
void uiItemsFullEnumO(
|
||||
uiLayout *layout, const char *opname, const char *propname,
|
||||
struct IDProperty *properties, int context, int flag);
|
||||
void uiItemsFullEnumO_items(
|
||||
uiLayout *layout, struct wmOperatorType *ot, PointerRNA ptr, PropertyRNA *prop,
|
||||
IDProperty *properties, int context, int flag,
|
||||
const EnumPropertyItem *item_array, int totitem);
|
||||
|
||||
void uiItemL(uiLayout *layout, const char *name, int icon); /* label */
|
||||
void uiItemLDrag(uiLayout *layout, struct PointerRNA *ptr, const char *name, int icon); /* label icon for dragging */
|
||||
|
|
|
@ -182,6 +182,9 @@ enum {
|
|||
|
||||
#define PIE_CLICK_THRESHOLD_SQ 50.0f
|
||||
|
||||
/* max amount of items a radial menu (pie menu) can contain */
|
||||
#define PIE_MAX_ITEMS 8
|
||||
|
||||
typedef struct uiLinkLine { /* only for draw/edit */
|
||||
struct uiLinkLine *next, *prev;
|
||||
struct uiBut *from, *to;
|
||||
|
@ -332,6 +335,10 @@ typedef struct ColorPickerData {
|
|||
} ColorPickerData;
|
||||
|
||||
struct PieMenuData {
|
||||
/* store title and icon to allow access when pie levels are created */
|
||||
const char *title;
|
||||
int icon;
|
||||
|
||||
float pie_dir[2];
|
||||
float pie_center_init[2];
|
||||
float pie_center_spawned[2];
|
||||
|
@ -603,6 +610,10 @@ uiPopupBlockHandle *ui_popup_menu_create(
|
|||
struct bContext *C, struct ARegion *butregion, uiBut *but,
|
||||
uiMenuCreateFunc create_func, void *arg);
|
||||
|
||||
void ui_pie_menu_level_create(
|
||||
uiBlock *block, struct wmOperatorType *ot, const char *propname, IDProperty *properties,
|
||||
const EnumPropertyItem *items, int totitem, int context, int flag);
|
||||
|
||||
void ui_popup_block_free(struct bContext *C, uiPopupBlockHandle *handle);
|
||||
|
||||
int ui_but_menu_step(uiBut *but, int step);
|
||||
|
|
|
@ -720,7 +720,7 @@ void UI_context_active_but_prop_get_filebrowser(
|
|||
/**
|
||||
* Update a buttons tip with an enum's description if possible.
|
||||
*/
|
||||
static void ui_but_tip_from_enum_item(uiBut *but, EnumPropertyItem *item)
|
||||
static void ui_but_tip_from_enum_item(uiBut *but, const EnumPropertyItem *item)
|
||||
{
|
||||
if (but->tip == NULL || but->tip[0] == '\0') {
|
||||
if (item->description && item->description[0]) {
|
||||
|
@ -883,6 +883,125 @@ void uiItemEnumO(uiLayout *layout, const char *opname, const char *name, int ico
|
|||
|
||||
}
|
||||
|
||||
BLI_INLINE bool ui_layout_is_radial(const uiLayout *layout)
|
||||
{
|
||||
return (layout->item.type == ITEM_LAYOUT_RADIAL) ||
|
||||
((layout->item.type == ITEM_LAYOUT_ROOT) && (layout->root->type == UI_LAYOUT_PIEMENU));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create ui items for enum items in \a item_array.
|
||||
*
|
||||
* A version of #uiItemsFullEnumO that takes pre-calculated item array.
|
||||
*/
|
||||
void uiItemsFullEnumO_items(
|
||||
uiLayout *layout, wmOperatorType *ot, PointerRNA ptr, PropertyRNA *prop, IDProperty *properties,
|
||||
int context, int flag,
|
||||
const EnumPropertyItem *item_array, int totitem)
|
||||
{
|
||||
const char *propname = RNA_property_identifier(prop);
|
||||
if (RNA_property_type(prop) != PROP_ENUM) {
|
||||
RNA_warning("%s.%s, not an enum type", RNA_struct_identifier(ptr.type), propname);
|
||||
return;
|
||||
}
|
||||
|
||||
uiLayout *target, *split = NULL;
|
||||
const EnumPropertyItem *item;
|
||||
uiBlock *block = layout->root->block;
|
||||
const bool radial = ui_layout_is_radial(layout);
|
||||
|
||||
if (radial) {
|
||||
target = uiLayoutRadial(layout);
|
||||
}
|
||||
else {
|
||||
split = uiLayoutSplit(layout, 0.0f, false);
|
||||
target = uiLayoutColumn(split, layout->align);
|
||||
}
|
||||
|
||||
|
||||
int i;
|
||||
bool last_iter = false;
|
||||
|
||||
for (i = 1, item = item_array; item->identifier && !last_iter; i++, item++) {
|
||||
/* handle oversized pies */
|
||||
if (radial && (totitem > PIE_MAX_ITEMS) && (i >= PIE_MAX_ITEMS)) {
|
||||
if (item->name) { /* only visible items */
|
||||
const EnumPropertyItem *tmp;
|
||||
|
||||
/* Check if there are more visible items for the next level. If not, we don't
|
||||
* add a new level and add the remaining item instead of the 'more' button. */
|
||||
for (tmp = item + 1; tmp->identifier; tmp++)
|
||||
if (tmp->name)
|
||||
break;
|
||||
|
||||
if (tmp->identifier) { /* only true if loop above found item and did early-exit */
|
||||
ui_pie_menu_level_create(block, ot, propname, properties, item_array, totitem, context, flag);
|
||||
/* break since rest of items is handled in new pie level */
|
||||
break;
|
||||
}
|
||||
else {
|
||||
last_iter = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (item->identifier[0]) {
|
||||
PointerRNA tptr;
|
||||
|
||||
WM_operator_properties_create_ptr(&tptr, ot);
|
||||
if (properties) {
|
||||
if (tptr.data) {
|
||||
IDP_FreeProperty(tptr.data);
|
||||
MEM_freeN(tptr.data);
|
||||
}
|
||||
tptr.data = IDP_CopyProperty(properties);
|
||||
}
|
||||
RNA_property_enum_set(&tptr, prop, item->value);
|
||||
|
||||
uiItemFullO_ptr(target, ot, item->name, item->icon, tptr.data, context, flag);
|
||||
|
||||
ui_but_tip_from_enum_item(block->buttons.last, item);
|
||||
}
|
||||
else {
|
||||
if (item->name) {
|
||||
uiBut *but;
|
||||
|
||||
if (item != item_array && !radial) {
|
||||
target = uiLayoutColumn(split, layout->align);
|
||||
|
||||
/* inconsistent, but menus with labels do not look good flipped */
|
||||
block->flag |= UI_BLOCK_NO_FLIP;
|
||||
}
|
||||
|
||||
if (item->icon || radial) {
|
||||
uiItemL(target, item->name, item->icon);
|
||||
|
||||
but = block->buttons.last;
|
||||
}
|
||||
else {
|
||||
/* Do not use uiItemL here, as our root layout is a menu one, it will add a fake blank icon! */
|
||||
but = uiDefBut(block, UI_BTYPE_LABEL, 0, item->name, 0, 0, UI_UNIT_X * 5, UI_UNIT_Y, NULL,
|
||||
0.0, 0.0, 0, 0, "");
|
||||
}
|
||||
ui_but_tip_from_enum_item(but, item);
|
||||
}
|
||||
else {
|
||||
if (radial) {
|
||||
/* invisible dummy button to ensure all items are always at the same position */
|
||||
uiItemS(target);
|
||||
}
|
||||
else {
|
||||
/* XXX bug here, colums draw bottom item badly */
|
||||
uiItemS(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void uiItemsFullEnumO(
|
||||
uiLayout *layout, const char *opname, const char *propname, IDProperty *properties,
|
||||
int context, int flag)
|
||||
|
@ -892,8 +1011,6 @@ void uiItemsFullEnumO(
|
|||
PointerRNA ptr;
|
||||
PropertyRNA *prop;
|
||||
uiBlock *block = layout->root->block;
|
||||
const bool radial = (layout->item.type == ITEM_LAYOUT_RADIAL) ||
|
||||
((layout->item.type == ITEM_LAYOUT_ROOT) && (layout->root->type == UI_LAYOUT_PIEMENU));
|
||||
|
||||
if (!ot || !ot->srna) {
|
||||
ui_item_disabled(layout, opname);
|
||||
|
@ -910,89 +1027,22 @@ void uiItemsFullEnumO(
|
|||
BLI_assert((prop == NULL) || (RNA_property_type(prop) == PROP_ENUM));
|
||||
|
||||
if (prop && RNA_property_type(prop) == PROP_ENUM) {
|
||||
EnumPropertyItem *item, *item_array = NULL;
|
||||
EnumPropertyItem *item_array = NULL;
|
||||
int totitem;
|
||||
bool free;
|
||||
uiLayout *split = NULL;
|
||||
uiLayout *target;
|
||||
|
||||
if (radial) {
|
||||
target = uiLayoutRadial(layout);
|
||||
if (ui_layout_is_radial(layout)) {
|
||||
RNA_property_enum_items_gettexted_all(block->evil_C, &ptr, prop, &item_array, &totitem, &free);
|
||||
}
|
||||
else {
|
||||
split = uiLayoutSplit(layout, 0.0f, false);
|
||||
target = uiLayoutColumn(split, layout->align);
|
||||
RNA_property_enum_items_gettexted(block->evil_C, &ptr, prop, &item_array, &totitem, &free);
|
||||
}
|
||||
|
||||
if (0 && radial) {
|
||||
/* XXX Disabled as temporary workaround!
|
||||
*
|
||||
* Normally, we always draw a button for all items even if they're unavailable (we draw invisible
|
||||
* dummy buttons then), so items are always at the same position. This causes issues with current
|
||||
* 'Object Mode' pie since more than 8 modes exist now (see T46973).
|
||||
* Disabling this until more than 8 items per pie are supported (or a better solution is found).
|
||||
* We should work on that ASAP though.
|
||||
*
|
||||
* - Julian (Dec 2015)
|
||||
*/
|
||||
RNA_property_enum_items_gettexted_all(block->evil_C, &ptr, prop, &item_array, NULL, &free);
|
||||
}
|
||||
else {
|
||||
RNA_property_enum_items_gettexted(block->evil_C, &ptr, prop, &item_array, NULL, &free);
|
||||
}
|
||||
/* add items */
|
||||
uiItemsFullEnumO_items(
|
||||
layout, ot, ptr, prop, properties, context, flag,
|
||||
item_array, totitem);
|
||||
|
||||
for (item = item_array; item->identifier; item++) {
|
||||
if (item->identifier[0]) {
|
||||
PointerRNA tptr;
|
||||
|
||||
WM_operator_properties_create_ptr(&tptr, ot);
|
||||
if (properties) {
|
||||
if (tptr.data) {
|
||||
IDP_FreeProperty(tptr.data);
|
||||
MEM_freeN(tptr.data);
|
||||
}
|
||||
tptr.data = IDP_CopyProperty(properties);
|
||||
}
|
||||
RNA_property_enum_set(&tptr, prop, item->value);
|
||||
|
||||
uiItemFullO_ptr(target, ot, item->name, item->icon, tptr.data, context, flag);
|
||||
|
||||
ui_but_tip_from_enum_item(block->buttons.last, item);
|
||||
}
|
||||
else {
|
||||
if (item->name) {
|
||||
uiBut *but;
|
||||
|
||||
if (item != item_array && !radial) {
|
||||
target = uiLayoutColumn(split, layout->align);
|
||||
|
||||
/* inconsistent, but menus with labels do not look good flipped */
|
||||
block->flag |= UI_BLOCK_NO_FLIP;
|
||||
}
|
||||
|
||||
if (item->icon || radial) {
|
||||
uiItemL(target, item->name, item->icon);
|
||||
|
||||
but = block->buttons.last;
|
||||
}
|
||||
else {
|
||||
/* Do not use uiItemL here, as our root layout is a menu one, it will add a fake blank icon! */
|
||||
but = uiDefBut(block, UI_BTYPE_LABEL, 0, item->name, 0, 0, UI_UNIT_X * 5, UI_UNIT_Y, NULL,
|
||||
0.0, 0.0, 0, 0, "");
|
||||
}
|
||||
ui_but_tip_from_enum_item(but, item);
|
||||
}
|
||||
else {
|
||||
if (radial) {
|
||||
/* invisible dummy button to ensure all items are always at the same position */
|
||||
uiItemS(target);
|
||||
}
|
||||
else {
|
||||
/* XXX bug here, colums draw bottom item badly */
|
||||
uiItemS(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (free) {
|
||||
MEM_freeN(item_array);
|
||||
}
|
||||
|
@ -2178,9 +2228,9 @@ static RadialDirection ui_get_radialbut_vec(float vec[2], short itemnum)
|
|||
{
|
||||
RadialDirection dir;
|
||||
|
||||
if (itemnum >= 8) {
|
||||
itemnum %= 8;
|
||||
printf("Warning: Pie menus with more than 8 items are currently unsupported\n");
|
||||
if (itemnum >= PIE_MAX_ITEMS) {
|
||||
itemnum %= PIE_MAX_ITEMS;
|
||||
printf("Warning: Pie menus with more than %i items are currently unsupported\n", PIE_MAX_ITEMS);
|
||||
}
|
||||
|
||||
dir = ui_radial_dir_order[itemnum];
|
||||
|
|
|
@ -2840,6 +2840,8 @@ uiPieMenu *UI_pie_menu_begin(struct bContext *C, const char *title, int icon, co
|
|||
}
|
||||
/* do not align left */
|
||||
but->drawflag &= ~UI_BUT_TEXT_LEFT;
|
||||
pie->block_radial->pie_data.title = but->str;
|
||||
pie->block_radial->pie_data.icon = icon;
|
||||
}
|
||||
|
||||
return pie;
|
||||
|
@ -2951,6 +2953,101 @@ int UI_pie_menu_invoke_from_rna_enum(
|
|||
return OPERATOR_INTERFACE;
|
||||
}
|
||||
|
||||
/**
|
||||
* \name Pie Menu Levels
|
||||
*
|
||||
* Pie menus can't contain more than 8 items (yet). When using #uiItemsFullEnumO, a "More" button is created that calls
|
||||
* a new pie menu if the enum has too many items. We call this a new "level".
|
||||
* Indirect recursion is used, so that a theoretically unlimited number of items is supported.
|
||||
*
|
||||
* This is a implementation specifically for operator enums, needed since the object mode pie now has more than 8
|
||||
* items. Ideally we'd have some way of handling this for all kinds of pie items, but that's tricky.
|
||||
*
|
||||
* - Julian (Feb 2016)
|
||||
*
|
||||
* \{ */
|
||||
|
||||
typedef struct PieMenuLevelData {
|
||||
char title[UI_MAX_NAME_STR]; /* parent pie title, copied for level */
|
||||
int icon; /* parent pie icon, copied for level */
|
||||
int totitem; /* total count of *remaining* items */
|
||||
|
||||
/* needed for calling uiItemsFullEnumO_array again for new level */
|
||||
wmOperatorType *ot;
|
||||
const char *propname;
|
||||
IDProperty *properties;
|
||||
int context, flag;
|
||||
} PieMenuLevelData;
|
||||
|
||||
/**
|
||||
* Invokes a new pie menu for a new level.
|
||||
*/
|
||||
static void ui_pie_menu_level_invoke(bContext *C, void *argN, void *arg2)
|
||||
{
|
||||
EnumPropertyItem *item_array = (EnumPropertyItem *)argN;
|
||||
PieMenuLevelData *lvl = (PieMenuLevelData *)arg2;
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
|
||||
uiPieMenu *pie = UI_pie_menu_begin(C, IFACE_(lvl->title), lvl->icon, win->eventstate);
|
||||
uiLayout *layout = UI_pie_menu_layout(pie);
|
||||
|
||||
layout = uiLayoutRadial(layout);
|
||||
|
||||
PointerRNA ptr;
|
||||
|
||||
WM_operator_properties_create_ptr(&ptr, lvl->ot);
|
||||
/* so the context is passed to itemf functions (some need it) */
|
||||
WM_operator_properties_sanitize(&ptr, false);
|
||||
PropertyRNA *prop = RNA_struct_find_property(&ptr, lvl->propname);
|
||||
|
||||
if (prop) {
|
||||
uiItemsFullEnumO_items(
|
||||
layout, lvl->ot, ptr, prop, lvl->properties, lvl->context, lvl->flag,
|
||||
item_array, lvl->totitem);
|
||||
}
|
||||
else {
|
||||
RNA_warning("%s.%s not found", RNA_struct_identifier(ptr.type), lvl->propname);
|
||||
}
|
||||
|
||||
UI_pie_menu_end(C, pie);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up data for defining a new pie menu level and add button that invokes it.
|
||||
*/
|
||||
void ui_pie_menu_level_create(
|
||||
uiBlock *block, wmOperatorType *ot, const char *propname, IDProperty *properties,
|
||||
const EnumPropertyItem *items, int totitem, int context, int flag)
|
||||
{
|
||||
const int totitem_parent = PIE_MAX_ITEMS - 1;
|
||||
const int totitem_remain = totitem - totitem_parent;
|
||||
size_t array_size = sizeof(EnumPropertyItem) * totitem_remain;
|
||||
|
||||
/* used as but->func_argN so freeing is handled elsewhere */
|
||||
EnumPropertyItem *remaining = MEM_mallocN(array_size + sizeof(EnumPropertyItem), "pie_level_item_array");
|
||||
memcpy(remaining, items + totitem_parent, array_size);
|
||||
/* a NULL terminating sentinal element is required */
|
||||
memset(&remaining[totitem_remain], 0, sizeof(EnumPropertyItem));
|
||||
|
||||
|
||||
/* yuk, static... issue is we can't reliably free this without doing dangerous changes */
|
||||
static PieMenuLevelData lvl;
|
||||
BLI_strncpy(lvl.title, block->pie_data.title, UI_MAX_NAME_STR);
|
||||
lvl.totitem = totitem_remain;
|
||||
lvl.ot = ot;
|
||||
lvl.propname = propname;
|
||||
lvl.properties = properties;
|
||||
lvl.context = context;
|
||||
lvl.flag = flag;
|
||||
|
||||
/* add a 'more' menu entry */
|
||||
uiBut *but = uiDefIconTextBut(block, UI_BTYPE_BUT, 0, ICON_PLUS, "More", 0, 0, UI_UNIT_X * 3, UI_UNIT_Y, NULL,
|
||||
0.0f, 0.0f, 0.0f, 0.0f, "Show more items of this menu");
|
||||
UI_but_funcN_set(but, ui_pie_menu_level_invoke, remaining, &lvl);
|
||||
}
|
||||
|
||||
/** \} */ /* Pie Menu Levels */
|
||||
|
||||
|
||||
/*************************** Standard Popup Menus ****************************/
|
||||
|
||||
|
|
Loading…
Reference in New Issue