diff options
author | Campbell Barton <ideasman42@gmail.com> | 2014-02-10 05:52:35 +0400 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2014-02-10 06:04:43 +0400 |
commit | 35f62bdced41b8960c90ad20a2f90809c4b677e9 (patch) | |
tree | d8839ac4babb896ac1b1afa7251cbf93b1660cc2 /source | |
parent | 21b60ea7e16c825a05eab910d13337dd079b8bbf (diff) |
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.
Diffstat (limited to 'source')
-rw-r--r-- | source/blender/editors/interface/interface.c | 197 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_handlers.c | 19 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_intern.h | 3 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_regions.c | 355 |
4 files changed, 202 insertions, 372 deletions
diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index fb0b0351453..4034ad46520 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -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); } diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 5c5db59fcd9..2f47b70e644 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -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; } diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 80190675a0a..958e0d979aa 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -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; diff --git a/source/blender/editors/interface/interface_regions.c b/source/blender/editors/interface/interface_regions.c index 2770e657ea0..7d78be3558a 100644 --- a/source/blender/editors/interface/interface_regions.c +++ b/source/blender/editors/interface/interface_regions.c @@ -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; - } - - 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++; -} + } while ((i != i_init) && (step_tot != direction)); -static void menudata_free(MenuData *md) -{ - MEM_freeN((void *)md->instr); - if (md->items) { - MEM_freeN(md->items); + if (i != i_init) { + value = item_array[i].value; } - 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++; + if (free) { + MEM_freeN(item_array); } - - 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); + return value; } -int ui_step_name_menu(uiBut *but, int step) +int ui_step_name_menu(uiBut *but, int direction) { - 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; - } - } + /* 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; } - 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; -} + 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); |