diff options
author | Antony Riakiotakis <kalast@gmail.com> | 2014-08-11 12:39:59 +0400 |
---|---|---|
committer | Antony Riakiotakis <kalast@gmail.com> | 2014-08-11 13:02:26 +0400 |
commit | 028fd29eeb092b6ed0625ed4d59b8100ae69596f (patch) | |
tree | 0cb15b15f17e6d9f16fef74631e3838e8962fafe /source/blender/editors/interface/interface_layout.c | |
parent | 5d1d23d5bdb0e5937520f16063f8d16fb049850c (diff) |
Pie Menus C code backend.
This commit merges the code in the pie-menu branch.
As per decisions taken the last few days, there are no pie menus
included and there will be an official add-on including overrides of
some keys with pie menus. However, people will now be able to use the
new code in python.
Full Documentation is in http://wiki.blender.org/index.php/Dev:Ref/
Thanks:
Campbell Barton, Dalai Felinto and Ton Roosendaal for the code review
and design comments
Jonathan Williamson, Pawel Lyczkowski, Pablo Vazquez among others for
suggestions during the development.
Special Thanks to Sean Olson, for his support, suggestions, testing and
merciless bugging so that I would finish the pie menu code. Without him
we wouldn't be here. Also to the rest of the developers of the original
python add-on, Patrick Moore and Dan Eicher and finally to Matt Ebb, who
did the research and first implementation and whose code I used to get
started.
Diffstat (limited to 'source/blender/editors/interface/interface_layout.c')
-rw-r--r-- | source/blender/editors/interface/interface_layout.c | 227 |
1 files changed, 212 insertions, 15 deletions
diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index 645eb607031..a2d8ce06e5f 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -106,6 +106,7 @@ typedef enum uiItemType { ITEM_LAYOUT_ABSOLUTE, ITEM_LAYOUT_SPLIT, ITEM_LAYOUT_OVERLAP, + ITEM_LAYOUT_RADIAL, ITEM_LAYOUT_ROOT #if 0 @@ -218,7 +219,9 @@ static int ui_item_fit(int item, int pos, int all, int available, int last, int static int ui_layout_vary_direction(uiLayout *layout) { - return (layout->root->type == UI_LAYOUT_HEADER || layout->alignment != UI_LAYOUT_ALIGN_EXPAND) ? UI_ITEM_VARY_X : UI_ITEM_VARY_Y; + return ((ELEM(layout->root->type, UI_LAYOUT_HEADER, UI_LAYOUT_PIEMENU) || + (layout->alignment != UI_LAYOUT_ALIGN_EXPAND)) ? + UI_ITEM_VARY_X : UI_ITEM_VARY_Y); } /* estimated size of text + icon */ @@ -553,15 +556,24 @@ static void ui_item_enum_expand(uiLayout *layout, uiBlock *block, PointerRNA *pt */ uiBut *but; + uiLayout *layout_radial = NULL; EnumPropertyItem *item, *item_array; const char *name; int itemw, icon, value; bool free; + bool radial = (layout->root->type == UI_LAYOUT_PIEMENU); - RNA_property_enum_items_gettexted(block->evil_C, ptr, prop, &item_array, NULL, &free); + if (radial) + 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); /* we dont want nested rows, cols in menus */ - if (layout->root->type != UI_LAYOUT_MENU) { + if (radial) { + layout_radial = uiLayoutRadial(layout); + uiBlockSetCurLayout(block, layout_radial); + } + else if (layout->root->type != UI_LAYOUT_MENU) { uiBlockSetCurLayout(block, ui_item_local_sublayout(layout, layout, 1)); } else { @@ -569,8 +581,11 @@ static void ui_item_enum_expand(uiLayout *layout, uiBlock *block, PointerRNA *pt } for (item = item_array; item->identifier; item++) { - if (!item->identifier[0]) + if (!item->identifier[0]) { + if (radial) + uiItemS(layout_radial); continue; + } name = (!uiname || uiname[0]) ? item->name : ""; icon = item->icon; @@ -869,6 +884,8 @@ void uiItemsFullEnumO(uiLayout *layout, const char *opname, const char *propname 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); @@ -887,10 +904,24 @@ void uiItemsFullEnumO(uiLayout *layout, const char *opname, const char *propname if (prop && RNA_property_type(prop) == PROP_ENUM) { EnumPropertyItem *item, *item_array = NULL; bool free; - uiLayout *split = uiLayoutSplit(layout, 0.0f, false); - uiLayout *column = uiLayoutColumn(split, layout->align); + uiLayout *split; + uiLayout *target; + + if (radial) { + target = uiLayoutRadial(layout); + } + else { + split = uiLayoutSplit(layout, 0.0f, false); + target = uiLayoutColumn(split, layout->align); + } + + if (radial) { + 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); + } - RNA_property_enum_items_gettexted(block->evil_C, &ptr, prop, &item_array, NULL, &free); for (item = item_array; item->identifier; item++) { if (item->identifier[0]) { PointerRNA tptr; @@ -905,20 +936,24 @@ void uiItemsFullEnumO(uiLayout *layout, const char *opname, const char *propname } RNA_property_enum_set(&tptr, prop, item->value); - uiItemFullO_ptr(column, ot, item->name, item->icon, tptr.data, context, flag); + 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) { - column = uiLayoutColumn(split, layout->align); + + 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) { - uiItemL(column, item->name, item->icon); + if (item->icon || radial) { + uiItemL(target, item->name, item->icon); + but = block->buttons.last; } else { @@ -928,8 +963,14 @@ void uiItemsFullEnumO(uiLayout *layout, const char *opname, const char *propname } ui_but_tip_from_enum_item(but, item); } - else { /* XXX bug here, colums draw bottom item badly */ - uiItemS(column); + else { + if (radial) { + uiItemS(target); + } + else { + /* XXX bug here, colums draw bottom item badly */ + uiItemS(target); + } } } } @@ -2072,16 +2113,135 @@ static void ui_litem_layout_column(uiLayout *litem) litem->y = y; } +/* calculates the angle of a specified button in a radial menu, + * stores a float vector in unit circle */ +static RadialDirection ui_get_radialbut_vec(float vec[2], short itemnum) +{ + RadialDirection dir; + BLI_assert(itemnum < 8); + + dir = ui_radial_dir_order[itemnum]; + ui_but_pie_dir_visual(dir, vec); + + return dir; +} + +static bool ui_item_is_radial_displayable(uiItem *item) +{ + + if ((item->type == ITEM_BUTTON) && (((uiButtonItem *)item)->but->type == LABEL)) + return false; + + return true; +} + +static bool ui_item_is_radial_drawable(uiButtonItem *bitem) +{ + + if (ELEM(bitem->but->type, SEPR, SEPRLINE)) + return false; + + return true; +} + +static void ui_litem_layout_radial(uiLayout *litem) +{ + uiItem *item; + int itemh, itemw, x, y; + int itemnum = 0; + int totitems = 0; + + int minx, miny, maxx, maxy; + /* For the radial layout we will use Matt Ebb's design + * for radiation, see http://mattebb.com/weblog/radiation/ + * also the old code at http://developer.blender.org/T5103 + */ + + int pie_radius = U.pie_menu_radius * UI_DPI_FAC; + + x = litem->x; + y = litem->y; + + minx = x, miny = y, maxx = x, maxy = y; + + /* first count total items */ + for (item = litem->items.first; item; item = item->next) + totitems++; + + if (totitems < 5) + litem->root->block->pie_data.flags |= UI_PIE_DEGREES_RANGE_LARGE; + + if (totitems == 3) + litem->root->block->pie_data.flags |= UI_PIE_3_ITEMS; + + for (item = litem->items.first; item; item = item->next) { + /* not all button types are drawn in a radial menu, do filtering here */ + if (ui_item_is_radial_displayable(item)) { + RadialDirection dir; + float vec[2]; + + dir = ui_get_radialbut_vec(vec, itemnum); + + itemnum++; + + if (item->type == ITEM_BUTTON) { + uiButtonItem *bitem = (uiButtonItem *) item; + + bitem->but->pie_dir = dir; + /* scale the buttons */ + bitem->but->rect.ymax *= 1.5f; + /* add a little bit more here to include number */ + bitem->but->rect.xmax += 1.5f * UI_UNIT_X; + /* enable drawing as pie item if supported by widget */ + if (ui_item_is_radial_drawable(bitem)) + bitem->but->dt = UI_EMBOSSR; + } + + ui_item_size(item, &itemw, &itemh); + + ui_item_position(item, x + vec[0] * pie_radius - itemw / 2, y + vec[1] * pie_radius - itemh / 2, itemw, itemh); + + minx = min_ii(minx, x + vec[0] * pie_radius - itemw / 2); + maxx = max_ii(maxx, x + vec[0] * pie_radius + itemw / 2); + miny = min_ii(miny, y + vec[1] * pie_radius - itemh / 2); + maxy = max_ii(maxy, y + vec[1] * pie_radius + itemh / 2); + } + } + + litem->x = minx; + litem->y = miny; + litem->w = maxx - minx; + litem->h = maxy - miny; +} + /* root layout */ static void ui_litem_estimate_root(uiLayout *UNUSED(litem)) { /* nothing to do */ } +static void ui_litem_layout_root_radial(uiLayout *litem) +{ + /* first item is pie menu title, align on center of menu */ + uiItem *item = litem->items.first; + + if (item->type == ITEM_BUTTON) { + int itemh, itemw, x, y; + x = litem->x; + y = litem->y; + + ui_item_size(item, &itemw, &itemh); + + ui_item_position(item, x - itemw / 2, y + 2 * UI_UNIT_Y, itemw, itemh); + } +} + static void ui_litem_layout_root(uiLayout *litem) { if (litem->root->type == UI_LAYOUT_HEADER) ui_litem_layout_row(litem); + else if (litem->root->type == UI_LAYOUT_PIEMENU) + ui_litem_layout_root_radial(litem); else ui_litem_layout_column(litem); } @@ -2497,6 +2657,40 @@ static uiLayoutItemBx *ui_layout_box(uiLayout *layout, int type) return box; } +uiLayout *uiLayoutRadial(uiLayout *layout) +{ + uiLayout *litem; + uiItem *item; + + /* radial layouts are only valid for radial menus */ + if (layout->root->type != UI_LAYOUT_PIEMENU) + return ui_item_local_sublayout(layout, layout, 0); + + /* only one radial wheel per root layout is allowed, so check and return that, if it exists */ + for (item = layout->root->layout->items.first; item; item = item->next) { + litem = (uiLayout *)item; + if (litem->item.type == ITEM_LAYOUT_RADIAL) { + uiBlockSetCurLayout(layout->root->block, litem); + return litem; + } + } + + litem = MEM_callocN(sizeof(uiLayout), "uiLayoutRadial"); + litem->item.type = ITEM_LAYOUT_RADIAL; + litem->root = layout->root; + litem->active = true; + litem->enabled = true; + litem->context = layout->context; + litem->redalert = layout->redalert; + litem->w = layout->w; + BLI_addtail(&layout->root->layout->items, litem); + + uiBlockSetCurLayout(layout->root->block, litem); + + return litem; +} + + uiLayout *uiLayoutBox(uiLayout *layout) { return (uiLayout *)ui_layout_box(layout, ROUNDBOX); @@ -2843,6 +3037,9 @@ static void ui_item_layout(uiItem *item) case ITEM_LAYOUT_OVERLAP: ui_litem_layout_overlap(litem); break; + case ITEM_LAYOUT_RADIAL: + ui_litem_layout_radial(litem); + break; default: break; } @@ -2916,7 +3113,7 @@ uiLayout *uiBlockLayout(uiBlock *block, int dir, int type, int x, int y, int siz layout->enabled = 1; layout->context = NULL; - if (type == UI_LAYOUT_MENU) + if (type == UI_LAYOUT_MENU || type == UI_LAYOUT_PIEMENU) layout->space = 0; if (dir == UI_LAYOUT_HORIZONTAL) { |