UI: refactor menus to remove menus encoded in strings
On every redraw a single unopened dropdown boxe would translate and convert every EnumPropertyItem into a string, then decode every item, and search those items to find the name of the button to draw. Replace this with a custom menu callback for RNA enums, tooltips for enums now show too.
This commit is contained in:
parent
21b60ea7e1
commit
35f62bdced
Notes:
blender-bot
2023-02-14 11:12:14 +01:00
Referenced by issue #38588, Strange button labels in Render View in latest build
|
@ -48,11 +48,9 @@
|
|||
#include "BLI_path_util.h"
|
||||
#include "BLI_rect.h"
|
||||
|
||||
#include "BLI_dynstr.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_library.h"
|
||||
#include "BKE_unit.h"
|
||||
#include "BKE_screen.h"
|
||||
#include "BKE_idprop.h"
|
||||
|
@ -82,6 +80,8 @@
|
|||
#define UI_BUT_VALUE_UNSET DBL_MAX
|
||||
#define UI_GET_BUT_VALUE_INIT(_but, _value) if (_value == DBL_MAX) { (_value) = ui_get_but_val(_but); } (void)0
|
||||
|
||||
#define B_NOP -1
|
||||
|
||||
/*
|
||||
* a full doc with API notes can be found in bf-blender/trunk/blender/doc/guides/interface_API.txt
|
||||
*
|
||||
|
@ -670,6 +670,25 @@ static bool ui_but_update_from_old_block(const bContext *C, uiBlock *block, uiBu
|
|||
|
||||
ui_but_update_linklines(block, oldbut, but);
|
||||
|
||||
/* move/copy string from the new button to the old */
|
||||
/* needed for alt+mouse wheel over enums */
|
||||
if (but->str != but->strdata) {
|
||||
if (oldbut->str != oldbut->strdata) {
|
||||
SWAP(char *, but->str, oldbut->str);
|
||||
}
|
||||
else {
|
||||
oldbut->str = but->str;
|
||||
but->str = but->strdata;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (oldbut->str != oldbut->strdata) {
|
||||
MEM_freeN(oldbut->str);
|
||||
oldbut->str = oldbut->strdata;
|
||||
}
|
||||
BLI_strncpy(oldbut->strdata, but->strdata, sizeof(oldbut->strdata));
|
||||
}
|
||||
|
||||
BLI_remlink(&block->buttons, but);
|
||||
ui_free_but(C, but);
|
||||
|
||||
|
@ -2469,15 +2488,6 @@ void ui_check_but(uiBut *but)
|
|||
|
||||
/* name: */
|
||||
switch (but->type) {
|
||||
|
||||
case MENU:
|
||||
|
||||
if (BLI_rctf_size_x(&but->rect) > 24.0f) {
|
||||
UI_GET_BUT_VALUE_INIT(but, value);
|
||||
ui_set_name_menu(but, (int)value);
|
||||
}
|
||||
break;
|
||||
|
||||
case NUM:
|
||||
case NUMSLI:
|
||||
|
||||
|
@ -2966,6 +2976,127 @@ static void ui_def_but_rna__disable(uiBut *but)
|
|||
but->lockstr = "";
|
||||
}
|
||||
|
||||
static void ui_def_but_rna__menu(bContext *UNUSED(C), uiLayout *layout, void *but_p)
|
||||
{
|
||||
uiBlock *block = uiLayoutGetBlock(layout);
|
||||
uiPopupBlockHandle *handle = block->handle;
|
||||
uiBut *but = (uiBut *)but_p;
|
||||
|
||||
/* see comment in ui_item_enum_expand, re: uiname */
|
||||
EnumPropertyItem *item, *item_array;
|
||||
bool free;
|
||||
|
||||
uiLayout *split, *column = NULL;
|
||||
|
||||
int totitems = 0;
|
||||
int columns, rows, a, b;
|
||||
int column_start = 0, column_end = 0;
|
||||
int nbr_entries_nosepr = 0;
|
||||
|
||||
uiBlockSetFlag(block, UI_BLOCK_MOVEMOUSE_QUIT);
|
||||
|
||||
RNA_property_enum_items_gettexted(block->evil_C, &but->rnapoin, but->rnaprop, &item_array, NULL, &free);
|
||||
|
||||
|
||||
/* we dont want nested rows, cols in menus */
|
||||
uiBlockSetCurLayout(block, layout);
|
||||
|
||||
for (item = item_array; item->identifier; item++, totitems++) {
|
||||
if (!item->identifier[0]) {
|
||||
/* inconsistent, but menus with labels do not look good flipped */
|
||||
if (item->name) {
|
||||
block->flag |= UI_BLOCK_NO_FLIP;
|
||||
nbr_entries_nosepr++;
|
||||
}
|
||||
/* We do not want simple separators in nbr_entries_nosepr count */
|
||||
continue;
|
||||
}
|
||||
nbr_entries_nosepr++;
|
||||
}
|
||||
|
||||
/* Columns and row estimation. Ignore simple separators here. */
|
||||
columns = (nbr_entries_nosepr + 20) / 20;
|
||||
if (columns < 1)
|
||||
columns = 1;
|
||||
if (columns > 8)
|
||||
columns = (nbr_entries_nosepr + 25) / 25;
|
||||
|
||||
rows = totitems / columns;
|
||||
if (rows < 1)
|
||||
rows = 1;
|
||||
while (rows * columns < totitems)
|
||||
rows++;
|
||||
|
||||
/* Title */
|
||||
uiDefBut(block, LABEL, 0, RNA_property_ui_name(but->rnaprop),
|
||||
0, 0, UI_UNIT_X * 5, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
|
||||
uiItemS(layout);
|
||||
|
||||
/* note, item_array[...] is reversed on access */
|
||||
|
||||
/* create items */
|
||||
split = uiLayoutSplit(layout, 0.0f, false);
|
||||
|
||||
for (a = 0; a < totitems; a++) {
|
||||
if (a == column_end) {
|
||||
/* start new column, and find out where it ends in advance, so we
|
||||
* can flip the order of items properly per column */
|
||||
column_start = a;
|
||||
column_end = totitems;
|
||||
|
||||
for (b = a + 1; b < totitems; b++) {
|
||||
item = &item_array[ b];
|
||||
|
||||
/* new column on N rows or on separation label */
|
||||
if (((b - a) % rows == 0) || (!item->identifier[0] && item->name)) {
|
||||
column_end = b;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
column = uiLayoutColumn(split, false);
|
||||
}
|
||||
|
||||
if (block->flag & UI_BLOCK_NO_FLIP) {
|
||||
item = &item_array[a];
|
||||
}
|
||||
else {
|
||||
item = &item_array[(column_start + column_end - 1 - a)];
|
||||
}
|
||||
|
||||
if (!item->identifier[0]) {
|
||||
if (item->name) {
|
||||
if (item->icon) {
|
||||
uiItemL(column, item->name, item->icon);
|
||||
}
|
||||
else {
|
||||
/* Do not use uiItemL here, as our root layout is a menu one, it will add a fake blank icon! */
|
||||
uiDefBut(block, LABEL, 0, item->name, 0, 0, UI_UNIT_X * 5, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
|
||||
}
|
||||
}
|
||||
else {
|
||||
uiItemS(column);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (item->icon) {
|
||||
uiDefIconTextButF(block, BUTM, B_NOP, item->icon, item->name, 0, 0,
|
||||
UI_UNIT_X * 5, UI_UNIT_Y, &handle->retvalue, (float) item->value, 0.0, 0, -1, item->description);
|
||||
}
|
||||
else {
|
||||
uiDefButF(block, BUTM, B_NOP, item->name, 0, 0,
|
||||
UI_UNIT_X * 5, UI_UNIT_X, &handle->retvalue, (float) item->value, 0.0, 0, -1, item->description);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uiBlockSetCurLayout(block, layout);
|
||||
|
||||
if (free) {
|
||||
MEM_freeN(item_array);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ui_def_but_rna_propname and ui_def_but_rna
|
||||
* both take the same args except for propname vs prop, this is done so we can
|
||||
|
@ -2982,6 +3113,7 @@ static uiBut *ui_def_but_rna(uiBlock *block, int type, int retval, const char *s
|
|||
const PropertyType proptype = RNA_property_type(prop);
|
||||
uiBut *but;
|
||||
int freestr = 0, icon = 0;
|
||||
uiMenuCreateFunc func = NULL;
|
||||
|
||||
if (ELEM3(type, COLOR, HSVCIRCLE, HSVCUBE)) {
|
||||
BLI_assert(index == -1);
|
||||
|
@ -2991,38 +3123,30 @@ static uiBut *ui_def_but_rna(uiBlock *block, int type, int retval, const char *s
|
|||
if (!str) {
|
||||
if (type == MENU && proptype == PROP_ENUM) {
|
||||
EnumPropertyItem *item;
|
||||
DynStr *dynstr;
|
||||
int i, totitem, value;
|
||||
int totitem, value;
|
||||
bool free;
|
||||
int i;
|
||||
|
||||
RNA_property_enum_items_gettexted(block->evil_C, ptr, prop, &item, &totitem, &free);
|
||||
RNA_property_enum_items(block->evil_C, ptr, prop, &item, &totitem, &free);
|
||||
value = RNA_property_enum_get(ptr, prop);
|
||||
|
||||
dynstr = BLI_dynstr_new();
|
||||
BLI_dynstr_appendf(dynstr, "%s%%t", RNA_property_ui_name(prop));
|
||||
for (i = 0; i < totitem; i++) {
|
||||
if (!item[i].identifier[0]) {
|
||||
if (item[i].name)
|
||||
BLI_dynstr_appendf(dynstr, "|%s%%l", item[i].name);
|
||||
else
|
||||
BLI_dynstr_append(dynstr, "|%l");
|
||||
}
|
||||
else if (item[i].icon)
|
||||
BLI_dynstr_appendf(dynstr, "|%s %%i%d %%x%d", item[i].name, item[i].icon, item[i].value);
|
||||
else
|
||||
BLI_dynstr_appendf(dynstr, "|%s %%x%d", item[i].name, item[i].value);
|
||||
|
||||
if (value == item[i].value)
|
||||
icon = item[i].icon;
|
||||
i = RNA_enum_from_value(item, value);
|
||||
if (i != -1) {
|
||||
str = item[i].name;
|
||||
icon = item[i].icon;
|
||||
}
|
||||
else {
|
||||
str = "";
|
||||
}
|
||||
str = BLI_dynstr_get_cstring(dynstr);
|
||||
BLI_dynstr_free(dynstr);
|
||||
|
||||
if (free) {
|
||||
MEM_freeN(item);
|
||||
}
|
||||
|
||||
freestr = 1;
|
||||
#ifdef WITH_INTERNATIONAL
|
||||
str = CTX_IFACE_(RNA_property_translation_context(prop), str);
|
||||
#endif
|
||||
|
||||
func = ui_def_but_rna__menu;
|
||||
}
|
||||
else if (ELEM(type, ROW, LISTROW) && proptype == PROP_ENUM) {
|
||||
EnumPropertyItem *item, *item_array = NULL;
|
||||
|
@ -3123,6 +3247,11 @@ static uiBut *ui_def_but_rna(uiBlock *block, int type, int retval, const char *s
|
|||
but->a1 = ui_get_but_step_unit(but, but->a1);
|
||||
}
|
||||
|
||||
if (func) {
|
||||
but->menu_create_func = func;
|
||||
but->poin = (char *)but;
|
||||
}
|
||||
|
||||
if (freestr) {
|
||||
MEM_freeN((void *)str);
|
||||
}
|
||||
|
|
|
@ -2821,7 +2821,6 @@ static void ui_blockopen_begin(bContext *C, uiBut *but, uiHandleButtonData *data
|
|||
uiBlockCreateFunc func = NULL;
|
||||
uiBlockHandleCreateFunc handlefunc = NULL;
|
||||
uiMenuCreateFunc menufunc = NULL;
|
||||
char *menustr = NULL;
|
||||
void *arg = NULL;
|
||||
|
||||
switch (but->type) {
|
||||
|
@ -2837,17 +2836,9 @@ static void ui_blockopen_begin(bContext *C, uiBut *but, uiHandleButtonData *data
|
|||
}
|
||||
break;
|
||||
case MENU:
|
||||
if (but->menu_create_func) {
|
||||
menufunc = but->menu_create_func;
|
||||
arg = but->poin;
|
||||
}
|
||||
else {
|
||||
data->origvalue = ui_get_but_val(but);
|
||||
data->value = data->origvalue;
|
||||
but->editval = &data->value;
|
||||
|
||||
menustr = but->str;
|
||||
}
|
||||
BLI_assert(but->menu_create_func);
|
||||
menufunc = but->menu_create_func;
|
||||
arg = but->poin;
|
||||
break;
|
||||
case COLOR:
|
||||
ui_get_but_vectorf(but, data->origvec);
|
||||
|
@ -2868,8 +2859,8 @@ static void ui_blockopen_begin(bContext *C, uiBut *but, uiHandleButtonData *data
|
|||
if (but->block->handle)
|
||||
data->menu->popup = but->block->handle->popup;
|
||||
}
|
||||
else if (menufunc || menustr) {
|
||||
data->menu = ui_popup_menu_create(C, data->region, but, menufunc, arg, menustr);
|
||||
else if (menufunc) {
|
||||
data->menu = ui_popup_menu_create(C, data->region, but, menufunc, arg);
|
||||
if (but->block->handle)
|
||||
data->menu->popup = but->block->handle->popup;
|
||||
}
|
||||
|
|
|
@ -490,11 +490,10 @@ uiPopupBlockHandle *ui_popup_block_create(struct bContext *C, struct ARegion *bu
|
|||
uiBlockCreateFunc create_func, uiBlockHandleCreateFunc handle_create_func,
|
||||
void *arg);
|
||||
uiPopupBlockHandle *ui_popup_menu_create(struct bContext *C, struct ARegion *butregion, uiBut *but,
|
||||
uiMenuCreateFunc create_func, void *arg, char *str);
|
||||
uiMenuCreateFunc create_func, void *arg);
|
||||
|
||||
void ui_popup_block_free(struct bContext *C, uiPopupBlockHandle *handle);
|
||||
|
||||
void ui_set_name_menu(uiBut *but, int value);
|
||||
int ui_step_name_menu(uiBut *but, int step);
|
||||
|
||||
struct AutoComplete;
|
||||
|
|
|
@ -72,221 +72,52 @@
|
|||
|
||||
#include "interface_intern.h"
|
||||
|
||||
#define B_NOP -1
|
||||
#define MENU_TOP 8
|
||||
#define MENU_PADDING (int)(0.2f * UI_UNIT_Y)
|
||||
|
||||
/*********************** Menu Data Parsing ********************* */
|
||||
|
||||
typedef struct MenuEntry {
|
||||
const char *str;
|
||||
int retval;
|
||||
int icon;
|
||||
int sepr;
|
||||
} MenuEntry;
|
||||
|
||||
typedef struct MenuData {
|
||||
const char *instr;
|
||||
const char *title;
|
||||
int titleicon;
|
||||
|
||||
MenuEntry *items;
|
||||
int nitems, itemssize;
|
||||
} MenuData;
|
||||
|
||||
static MenuData *menudata_new(const char *instr)
|
||||
static int rna_property_enum_step(const bContext *C, PointerRNA *ptr, PropertyRNA *prop, int direction)
|
||||
{
|
||||
MenuData *md = MEM_mallocN(sizeof(*md), "MenuData");
|
||||
EnumPropertyItem *item_array;
|
||||
int totitem;
|
||||
bool free;
|
||||
int value;
|
||||
int i, i_init;
|
||||
int step = (direction < 0) ? -1 : 1;
|
||||
int step_tot = 0;
|
||||
|
||||
md->instr = instr;
|
||||
md->title = NULL;
|
||||
md->titleicon = 0;
|
||||
md->items = NULL;
|
||||
md->nitems = md->itemssize = 0;
|
||||
|
||||
return md;
|
||||
}
|
||||
RNA_property_enum_items((bContext *)C, ptr, prop, &item_array, &totitem, &free);
|
||||
value = RNA_property_enum_get(ptr, prop);
|
||||
i = RNA_enum_from_value(item_array, value);
|
||||
i_init = i;
|
||||
|
||||
static void menudata_set_title(MenuData *md, const char *title, int titleicon)
|
||||
{
|
||||
if (!md->title)
|
||||
md->title = title;
|
||||
if (!md->titleicon)
|
||||
md->titleicon = titleicon;
|
||||
}
|
||||
|
||||
static void menudata_add_item(MenuData *md, const char *str, int retval, int icon, int sepr)
|
||||
{
|
||||
if (md->nitems == md->itemssize) {
|
||||
int nsize = md->itemssize ? (md->itemssize << 1) : 1;
|
||||
MenuEntry *oitems = md->items;
|
||||
|
||||
md->items = MEM_mallocN(nsize * sizeof(*md->items), "md->items");
|
||||
if (oitems) {
|
||||
memcpy(md->items, oitems, md->nitems * sizeof(*md->items));
|
||||
MEM_freeN(oitems);
|
||||
do {
|
||||
i = mod_i(i + step, totitem);
|
||||
if (item_array[i].identifier[0]) {
|
||||
step_tot += step;
|
||||
}
|
||||
|
||||
md->itemssize = nsize;
|
||||
} while ((i != i_init) && (step_tot != direction));
|
||||
|
||||
if (i != i_init) {
|
||||
value = item_array[i].value;
|
||||
}
|
||||
|
||||
md->items[md->nitems].str = str;
|
||||
md->items[md->nitems].retval = retval;
|
||||
md->items[md->nitems].icon = icon;
|
||||
md->items[md->nitems].sepr = sepr;
|
||||
md->nitems++;
|
||||
}
|
||||
|
||||
static void menudata_free(MenuData *md)
|
||||
{
|
||||
MEM_freeN((void *)md->instr);
|
||||
if (md->items) {
|
||||
MEM_freeN(md->items);
|
||||
if (free) {
|
||||
MEM_freeN(item_array);
|
||||
}
|
||||
MEM_freeN(md);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse menu description strings, string is of the
|
||||
* form "[sss%t|]{(sss[%xNN]|), (%l|), (sss%l|)}", ssss%t indicates the
|
||||
* menu title, sss or sss%xNN indicates an option,
|
||||
* if %xNN is given then NN is the return value if
|
||||
* that option is selected otherwise the return value
|
||||
* is the index of the option (starting with 1). %l
|
||||
* indicates a separator, sss%l indicates a label and
|
||||
* new column.
|
||||
*
|
||||
* \param str String to be parsed.
|
||||
* \retval new menudata structure, free with menudata_free()
|
||||
*/
|
||||
static MenuData *decompose_menu_string(const char *str)
|
||||
{
|
||||
char *instr = BLI_strdup(str);
|
||||
MenuData *md = menudata_new(instr);
|
||||
const char *nitem = NULL;
|
||||
char *s = instr;
|
||||
int nicon = 0, nretval = 1, nitem_is_title = 0, nitem_is_sepr = 0;
|
||||
|
||||
while (1) {
|
||||
char c = *s;
|
||||
|
||||
if (c == '%') {
|
||||
if (s[1] == 'x') {
|
||||
nretval = atoi(s + 2);
|
||||
|
||||
*s = '\0';
|
||||
s++;
|
||||
}
|
||||
else if (s[1] == 't') {
|
||||
nitem_is_title = (s != instr); /* check for empty title */
|
||||
|
||||
*s = '\0';
|
||||
s++;
|
||||
}
|
||||
else if (s[1] == 'l') {
|
||||
nitem_is_sepr = 1;
|
||||
if (!nitem) nitem = "";
|
||||
|
||||
*s = '\0';
|
||||
s++;
|
||||
}
|
||||
else if (s[1] == 'i') {
|
||||
nicon = atoi(s + 2);
|
||||
|
||||
*s = '\0';
|
||||
s++;
|
||||
}
|
||||
}
|
||||
else if (c == UI_SEP_CHAR || c == '\n' || c == '\0') {
|
||||
if (nitem) {
|
||||
*s = '\0';
|
||||
|
||||
if (nitem_is_title) {
|
||||
menudata_set_title(md, nitem, nicon);
|
||||
nitem_is_title = 0;
|
||||
}
|
||||
else if (nitem_is_sepr) {
|
||||
/* prevent separator to get a value */
|
||||
menudata_add_item(md, nitem, -1, nicon, 1);
|
||||
nretval = md->nitems + 1;
|
||||
nitem_is_sepr = 0;
|
||||
}
|
||||
else {
|
||||
menudata_add_item(md, nitem, nretval, nicon, 0);
|
||||
nretval = md->nitems + 1;
|
||||
}
|
||||
|
||||
nitem = NULL;
|
||||
nicon = 0;
|
||||
}
|
||||
|
||||
if (c == '\0') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (!nitem) {
|
||||
nitem = s;
|
||||
}
|
||||
|
||||
s++;
|
||||
}
|
||||
|
||||
return md;
|
||||
}
|
||||
|
||||
void ui_set_name_menu(uiBut *but, int value)
|
||||
{
|
||||
MenuData *md;
|
||||
int i;
|
||||
|
||||
md = decompose_menu_string(but->str);
|
||||
for (i = 0; i < md->nitems; i++) {
|
||||
if (md->items[i].retval == value) {
|
||||
BLI_strncpy(but->drawstr, md->items[i].str, sizeof(but->drawstr));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
menudata_free(md);
|
||||
}
|
||||
|
||||
int ui_step_name_menu(uiBut *but, int step)
|
||||
{
|
||||
MenuData *md;
|
||||
int value = ui_get_but_val(but);
|
||||
int i;
|
||||
|
||||
md = decompose_menu_string(but->str);
|
||||
for (i = 0; i < md->nitems; i++)
|
||||
if (md->items[i].retval == value)
|
||||
break;
|
||||
|
||||
if (step == 1) {
|
||||
/* skip separators */
|
||||
for (; i < md->nitems - 1; i++) {
|
||||
if (md->items[i + 1].retval != -1) {
|
||||
value = md->items[i + 1].retval;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (i > 0) {
|
||||
/* skip separators */
|
||||
for (; i > 0; i--) {
|
||||
if (md->items[i - 1].retval != -1) {
|
||||
value = md->items[i - 1].retval;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
menudata_free(md);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
int ui_step_name_menu(uiBut *but, int direction)
|
||||
{
|
||||
/* currenly only RNA buttons */
|
||||
if ((but->rnaprop == NULL) || (RNA_property_type(but->rnaprop) != PROP_ENUM)) {
|
||||
printf("%s: cannot cycle button '%s'", __func__, but->str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return rna_property_enum_step(but->block->evil_C, &but->rnapoin, but->rnaprop, direction);
|
||||
}
|
||||
|
||||
/******************** Creating Temporary regions ******************/
|
||||
|
||||
|
@ -1793,119 +1624,6 @@ void ui_popup_block_free(bContext *C, uiPopupBlockHandle *handle)
|
|||
|
||||
/***************************** Menu Button ***************************/
|
||||
|
||||
static void ui_block_func_MENUSTR(bContext *UNUSED(C), uiLayout *layout, void *arg_str)
|
||||
{
|
||||
uiBlock *block = uiLayoutGetBlock(layout);
|
||||
uiPopupBlockHandle *handle = block->handle;
|
||||
uiLayout *split, *column = NULL;
|
||||
MenuData *md;
|
||||
MenuEntry *entry;
|
||||
const char *instr = arg_str;
|
||||
int columns, rows, a, b;
|
||||
int column_start = 0, column_end = 0;
|
||||
int nbr_entries_nosepr = 0;
|
||||
|
||||
uiBlockSetFlag(block, UI_BLOCK_MOVEMOUSE_QUIT);
|
||||
|
||||
/* compute menu data */
|
||||
md = decompose_menu_string(instr);
|
||||
|
||||
/* Run some "tweaking" checks. */
|
||||
entry = md->items;
|
||||
for (a = 0; a < md->nitems; a++, entry++) {
|
||||
if (entry->sepr) {
|
||||
/* inconsistent, but menus with labels do not look good flipped */
|
||||
if (entry->str[0]) {
|
||||
block->flag |= UI_BLOCK_NO_FLIP;
|
||||
nbr_entries_nosepr++;
|
||||
}
|
||||
/* We do not want simple separators in nbr_entries_nosepr count */
|
||||
continue;
|
||||
}
|
||||
nbr_entries_nosepr++;
|
||||
}
|
||||
|
||||
/* Columns and row estimation. Ignore simple separators here. */
|
||||
columns = (nbr_entries_nosepr + 20) / 20;
|
||||
if (columns < 1)
|
||||
columns = 1;
|
||||
if (columns > 8)
|
||||
columns = (nbr_entries_nosepr + 25) / 25;
|
||||
|
||||
rows = md->nitems / columns;
|
||||
if (rows < 1)
|
||||
rows = 1;
|
||||
while (rows * columns < md->nitems)
|
||||
rows++;
|
||||
|
||||
/* create title */
|
||||
if (md->title) {
|
||||
if (md->titleicon) {
|
||||
uiItemL(layout, md->title, md->titleicon);
|
||||
}
|
||||
else {
|
||||
/* Do not use uiItemL here, as our root layout is a menu one, it will add a fake blank icon! */
|
||||
uiDefBut(block, LABEL, 0, md->title, 0, 0, UI_UNIT_X * 5, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
|
||||
}
|
||||
|
||||
uiItemS(layout);
|
||||
}
|
||||
|
||||
/* create items */
|
||||
split = uiLayoutSplit(layout, 0.0f, false);
|
||||
|
||||
for (a = 0; a < md->nitems; a++) {
|
||||
if (a == column_end) {
|
||||
/* start new column, and find out where it ends in advance, so we
|
||||
* can flip the order of items properly per column */
|
||||
column_start = a;
|
||||
column_end = md->nitems;
|
||||
|
||||
for (b = a + 1; b < md->nitems; b++) {
|
||||
entry = &md->items[b];
|
||||
|
||||
/* new column on N rows or on separation label */
|
||||
if (((b - a) % rows == 0) || (entry->sepr && entry->str[0])) {
|
||||
column_end = b;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
column = uiLayoutColumn(split, false);
|
||||
}
|
||||
|
||||
if (block->flag & UI_BLOCK_NO_FLIP)
|
||||
entry = &md->items[a];
|
||||
else
|
||||
entry = &md->items[column_start + column_end - 1 - a];
|
||||
|
||||
if (entry->sepr) {
|
||||
if (entry->str[0]) {
|
||||
if (entry->icon) {
|
||||
uiItemL(column, entry->str, entry->icon);
|
||||
}
|
||||
else {
|
||||
/* Do not use uiItemL here, as our root layout is a menu one, it will add a fake blank icon! */
|
||||
uiDefBut(block, LABEL, 0, entry->str, 0, 0, UI_UNIT_X * 5, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
|
||||
}
|
||||
}
|
||||
else {
|
||||
uiItemS(column);
|
||||
}
|
||||
}
|
||||
else if (entry->icon) {
|
||||
uiDefIconTextButF(block, BUTM, B_NOP, entry->icon, entry->str, 0, 0,
|
||||
UI_UNIT_X * 5, UI_UNIT_Y, &handle->retvalue, (float) entry->retval, 0.0, 0, -1, "");
|
||||
}
|
||||
else {
|
||||
uiDefButF(block, BUTM, B_NOP, entry->str, 0, 0,
|
||||
UI_UNIT_X * 5, UI_UNIT_X, &handle->retvalue, (float) entry->retval, 0.0, 0, -1, "");
|
||||
}
|
||||
}
|
||||
|
||||
menudata_free(md);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void ui_warp_pointer(int x, int y)
|
||||
{
|
||||
|
@ -2478,7 +2196,7 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi
|
|||
}
|
||||
|
||||
uiPopupBlockHandle *ui_popup_menu_create(bContext *C, ARegion *butregion, uiBut *but,
|
||||
uiMenuCreateFunc menu_func, void *arg, char *str)
|
||||
uiMenuCreateFunc menu_func, void *arg)
|
||||
{
|
||||
wmWindow *window = CTX_wm_window(C);
|
||||
uiStyle *style = UI_GetStyleDraw();
|
||||
|
@ -2516,16 +2234,9 @@ uiPopupBlockHandle *ui_popup_menu_create(bContext *C, ARegion *butregion, uiBut
|
|||
uiLayoutContextCopy(pup->layout, but->context);
|
||||
}
|
||||
|
||||
if (str) {
|
||||
/* menu is created from a string */
|
||||
pup->menu_func = ui_block_func_MENUSTR;
|
||||
pup->menu_arg = str;
|
||||
}
|
||||
else {
|
||||
/* menu is created from a callback */
|
||||
pup->menu_func = menu_func;
|
||||
pup->menu_arg = arg;
|
||||
}
|
||||
/* menu is created from a callback */
|
||||
pup->menu_func = menu_func;
|
||||
pup->menu_arg = arg;
|
||||
|
||||
handle = ui_popup_block_create(C, butregion, but, NULL, ui_block_func_POPUP, pup);
|
||||
|
||||
|
|
Loading…
Reference in New Issue