Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/space_outliner/outliner_draw.cc
| Show First 20 Lines • Show All 2,871 Lines • ▼ Show 20 Lines | |||||
| */ | */ | ||||
| static bool tselem_draw_icon(uiBlock *block, | static bool tselem_draw_icon(uiBlock *block, | ||||
| int xmax, | int xmax, | ||||
| float x, | float x, | ||||
| float y, | float y, | ||||
| TreeStoreElem *tselem, | TreeStoreElem *tselem, | ||||
| TreeElement *te, | TreeElement *te, | ||||
| float alpha, | float alpha, | ||||
| const bool is_clickable) | const bool is_clickable, | ||||
| const int num_elements) | |||||
| { | { | ||||
| TreeElementIcon data = tree_element_get_icon(tselem, te); | TreeElementIcon data = tree_element_get_icon(tselem, te); | ||||
| if (data.icon == 0) { | if (data.icon == 0) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| const bool is_collection = outliner_is_collection_tree_element(te); | const bool is_collection = outliner_is_collection_tree_element(te); | ||||
| IconTextOverlay text_overlay; | |||||
| UI_icon_text_overlay_init_from_count(&text_overlay, num_elements); | |||||
| /* Collection colors and icons covered by restrict buttons. */ | /* Collection colors and icons covered by restrict buttons. */ | ||||
| if (!is_clickable || x >= xmax || is_collection) { | if (!is_clickable || x >= xmax || is_collection) { | ||||
| /* Placement of icons, copied from interface_widgets.c */ | /* Placement of icons, copied from interface_widgets.c */ | ||||
| float aspect = (0.8f * UI_UNIT_Y) / ICON_DEFAULT_HEIGHT; | float aspect = (0.8f * UI_UNIT_Y) / ICON_DEFAULT_HEIGHT; | ||||
| x += 2.0f * aspect; | x += 2.0f * aspect; | ||||
| y += 2.0f * aspect; | y += 2.0f * aspect; | ||||
| if (is_collection) { | if (is_collection) { | ||||
| Collection *collection = outliner_collection_from_tree_element(te); | Collection *collection = outliner_collection_from_tree_element(te); | ||||
| if (collection->color_tag != COLLECTION_COLOR_NONE) { | if (collection->color_tag != COLLECTION_COLOR_NONE) { | ||||
| bTheme *btheme = UI_GetTheme(); | bTheme *btheme = UI_GetTheme(); | ||||
| UI_icon_draw_ex(x, | UI_icon_draw_ex(x, | ||||
| y, | y, | ||||
| data.icon, | data.icon, | ||||
| U.inv_dpi_fac, | U.inv_dpi_fac, | ||||
| alpha, | alpha, | ||||
| 0.0f, | 0.0f, | ||||
| btheme->collection_color[collection->color_tag].color, | btheme->collection_color[collection->color_tag].color, | ||||
| true); | true, | ||||
| &text_overlay); | |||||
| return true; | return true; | ||||
| } | } | ||||
| } | } | ||||
| /* Reduce alpha to match icon buttons */ | /* Reduce alpha to match icon buttons */ | ||||
| alpha *= 0.8f; | alpha *= 0.8f; | ||||
| /* Restrict column clip. it has been coded by simply overdrawing, doesn't work for buttons. */ | /* Restrict column clip. it has been coded by simply overdrawing, doesn't work for buttons. */ | ||||
| uchar color[4]; | uchar color[4]; | ||||
| if (UI_icon_get_theme_color(data.icon, color)) { | if (UI_icon_get_theme_color(data.icon, color)) { | ||||
| UI_icon_draw_ex(x, y, data.icon, U.inv_dpi_fac, alpha, 0.0f, color, true); | UI_icon_draw_ex(x, y, data.icon, U.inv_dpi_fac, alpha, 0.0f, color, true, &text_overlay); | ||||
| } | } | ||||
| else { | else { | ||||
| UI_icon_draw_ex(x, y, data.icon, U.inv_dpi_fac, alpha, 0.0f, nullptr, false); | UI_icon_draw_ex(x, y, data.icon, U.inv_dpi_fac, alpha, 0.0f, nullptr, false, &text_overlay); | ||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| uiDefIconBut(block, | uiDefIconBut(block, | ||||
| UI_BTYPE_LABEL, | UI_BTYPE_LABEL, | ||||
| 0, | 0, | ||||
| data.icon, | data.icon, | ||||
| x, | x, | ||||
| y, | y, | ||||
| UI_UNIT_X, | UI_UNIT_X, | ||||
| UI_UNIT_Y, | UI_UNIT_Y, | ||||
| nullptr, | nullptr, | ||||
| 0.0, | 0.0, | ||||
| 0.0, | 0.0, | ||||
| 1.0, | 1.0, | ||||
| alpha, | alpha, | ||||
| (data.drag_id && ID_IS_LINKED(data.drag_id)) ? data.drag_id->lib->filepath : ""); | (data.drag_id && ID_IS_LINKED(data.drag_id)) ? data.drag_id->lib->filepath : ""); | ||||
| } | } | ||||
| return true; | return true; | ||||
| } | } | ||||
| static bool outliner_is_main_row(const ARegion *region, const int ys) | |||||
| { | |||||
| int ystart; | |||||
| ystart = int(region->v2d.tot.ymax); | |||||
| ystart = UI_UNIT_Y * (ystart / (UI_UNIT_Y)) - OL_Y_OFFSET; | |||||
| return ((ys - ystart) / UI_UNIT_Y) % 2; | |||||
| } | |||||
| /** | |||||
| * Get the expected row background color to use for the data-block counter | |||||
| * | |||||
| * This reproduces some of the logic of outliner_draw_highlights. | |||||
| * At the moment it doesn't implement the search match color since | |||||
| * we don't draw the data-block counter in those cases. | |||||
| */ | |||||
| static void outliner_get_row_color(const ARegion *region, | |||||
| const TreeElement *te, | |||||
| int ys, | |||||
| float r_color[4]) | |||||
| { | |||||
| const TreeStoreElem *tselem = TREESTORE(te); | |||||
| if ((tselem->flag & TSE_ACTIVE) && (tselem->flag & TSE_SELECTED)) { | |||||
| UI_GetThemeColor3fv(TH_ACTIVE, r_color); | |||||
| } | |||||
| else if (tselem->flag & TSE_SELECTED) { | |||||
| UI_GetThemeColor3fv(TH_SELECT_HIGHLIGHT, r_color); | |||||
| } | |||||
| else if (outliner_is_main_row(region, ys)) { | |||||
| UI_GetThemeColor3fv(TH_BACK, r_color); | |||||
| } | |||||
| else { | |||||
| float color_alternating[4]; | |||||
| UI_GetThemeColor4fv(TH_ROW_ALTERNATE, color_alternating); | |||||
| UI_GetThemeColorBlend3f(TH_BACK, TH_ROW_ALTERNATE, color_alternating[3], r_color); | |||||
| } | |||||
| if (tselem->flag & TSE_HIGHLIGHTED) { | |||||
| const float color_highlight[4] = {1.0f, 1.0f, 1.0f, 0.13f}; | |||||
| interp_v3_v3v3(r_color, r_color, color_highlight, color_highlight[3]); | |||||
| } | |||||
| r_color[3] = 1.0f; | |||||
| } | |||||
| /** | |||||
| * For icon-only children of a collapsed tree, | |||||
| * Draw small number over the icon to show how many items of this type are displayed. | |||||
| */ | |||||
| static void outliner_draw_iconrow_number(const ARegion *region, | |||||
| const uiFontStyle *fstyle, | |||||
| int offsx, | |||||
| int ys, | |||||
| const TreeElement *te_visible, | |||||
| const int num_elements) | |||||
| { | |||||
| float color[4]; | |||||
| outliner_get_row_color(region, te_visible, ys, color); | |||||
| float ufac = 0.25f * UI_UNIT_X; | |||||
| float offset_x = float(offsx) + UI_UNIT_X * 0.35f; | |||||
| rctf rect{}; | |||||
| BLI_rctf_init(&rect, | |||||
| offset_x + ufac, | |||||
| offset_x + UI_UNIT_X - ufac, | |||||
| float(ys) - UI_UNIT_Y * 0.2f + ufac, | |||||
| float(ys) - UI_UNIT_Y * 0.2f + UI_UNIT_Y - ufac); | |||||
| UI_draw_roundbox_corner_set(UI_CNR_ALL); | |||||
| UI_draw_roundbox_4fv_ex( | |||||
| &rect, color, NULL, 1.0f, color, U.pixelsize, float(UI_UNIT_Y) / 2.0f - ufac); | |||||
| /* Now the numbers. */ | |||||
| uchar text_col[4]; | |||||
| UI_GetThemeColor3ubv(TH_TEXT, text_col); | |||||
| text_col[3] = 255; | |||||
| uiFontStyle fstyle_small = *fstyle; | |||||
| fstyle_small.points *= 0.8f; | |||||
| /* We treat +99 as 4 digits to make sure the (eyeballed) alignment looks nice. */ | |||||
| int num_digits = 4; | |||||
| char number_text[4] = "+99"; | |||||
| if (num_elements < 100) { | |||||
| BLI_snprintf(number_text, sizeof(number_text), "%d", num_elements); | |||||
| num_digits = num_elements < 10 ? 1 : 2; | |||||
| } | |||||
| UI_fontstyle_draw_simple(&fstyle_small, | |||||
| (offset_x + ufac + UI_UNIT_X * (2 - num_digits) * 0.12f), | |||||
| float(ys) - UI_UNIT_Y * 0.095f + ufac, | |||||
| number_text, | |||||
| text_col); | |||||
| UI_fontstyle_set(fstyle); | |||||
| GPU_blend(GPU_BLEND_ALPHA); /* Round-box and text drawing disables. */ | |||||
| } | |||||
| static void outliner_icon_background_colors(float icon_color[4], float icon_border[4]) | static void outliner_icon_background_colors(float icon_color[4], float icon_border[4]) | ||||
| { | { | ||||
| float text[4]; | float text[4]; | ||||
| UI_GetThemeColor4fv(TH_TEXT, text); | UI_GetThemeColor4fv(TH_TEXT, text); | ||||
| copy_v3_v3(icon_color, text); | copy_v3_v3(icon_color, text); | ||||
| icon_color[3] = 0.4f; | icon_color[3] = 0.4f; | ||||
| copy_v3_v3(icon_border, text); | copy_v3_v3(icon_border, text); | ||||
| Show All 14 Lines | static void outliner_draw_active_indicator(const float minx, | ||||
| BLI_rctf_init(&rect, minx, maxx, miny + ufac, maxy - ufac); | BLI_rctf_init(&rect, minx, maxx, miny + ufac, maxy - ufac); | ||||
| UI_draw_roundbox_corner_set(UI_CNR_ALL); | UI_draw_roundbox_corner_set(UI_CNR_ALL); | ||||
| UI_draw_roundbox_aa(&rect, true, radius, icon_color); | UI_draw_roundbox_aa(&rect, true, radius, icon_color); | ||||
| UI_draw_roundbox_aa(&rect, false, radius, icon_border); | UI_draw_roundbox_aa(&rect, false, radius, icon_border); | ||||
| GPU_blend(GPU_BLEND_ALPHA); /* Round-box disables. */ | GPU_blend(GPU_BLEND_ALPHA); /* Round-box disables. */ | ||||
| } | } | ||||
| static void outliner_draw_iconrow_doit(const ARegion *region, | static void outliner_draw_iconrow_doit(uiBlock *block, | ||||
| uiBlock *block, | |||||
| TreeElement *te_visible, | |||||
| TreeElement *te, | TreeElement *te, | ||||
| const uiFontStyle *fstyle, | |||||
| int xmax, | int xmax, | ||||
| int *offsx, | int *offsx, | ||||
| int ys, | int ys, | ||||
| float alpha_fac, | float alpha_fac, | ||||
| const eOLDrawState active, | const eOLDrawState active, | ||||
| const int num_elements) | const int num_elements) | ||||
| { | { | ||||
| TreeStoreElem *tselem = TREESTORE(te); | TreeStoreElem *tselem = TREESTORE(te); | ||||
| Show All 12 Lines | outliner_draw_active_indicator(float(*offsx), | ||||
| float(ys) + UI_UNIT_Y, | float(ys) + UI_UNIT_Y, | ||||
| icon_color, | icon_color, | ||||
| icon_border); | icon_border); | ||||
| } | } | ||||
| if (tselem->flag & TSE_HIGHLIGHTED_ICON) { | if (tselem->flag & TSE_HIGHLIGHTED_ICON) { | ||||
| alpha_fac += 0.5; | alpha_fac += 0.5; | ||||
| } | } | ||||
| tselem_draw_icon(block, xmax, float(*offsx), float(ys), tselem, te, alpha_fac, false); | tselem_draw_icon( | ||||
| block, xmax, float(*offsx), float(ys), tselem, te, alpha_fac, false, num_elements); | |||||
| te->xs = *offsx; | te->xs = *offsx; | ||||
| te->ys = ys; | te->ys = ys; | ||||
| te->xend = short(*offsx) + UI_UNIT_X; | te->xend = short(*offsx) + UI_UNIT_X; | ||||
| if (num_elements > 1) { | if (num_elements > 1) { | ||||
| outliner_draw_iconrow_number(region, fstyle, *offsx, ys, te_visible, num_elements); | |||||
| te->flag |= TE_ICONROW_MERGED; | te->flag |= TE_ICONROW_MERGED; | ||||
| } | } | ||||
| else { | else { | ||||
| te->flag |= TE_ICONROW; | te->flag |= TE_ICONROW; | ||||
| } | } | ||||
| (*offsx) += UI_UNIT_X; | (*offsx) += UI_UNIT_X; | ||||
| } | } | ||||
| Show All 20 Lines | struct MergedIconRow { | ||||
| TreeElement *tree_element[INDEX_ID_MAX + OB_TYPE_MAX]; | TreeElement *tree_element[INDEX_ID_MAX + OB_TYPE_MAX]; | ||||
| }; | }; | ||||
| static void outliner_draw_iconrow(bContext *C, | static void outliner_draw_iconrow(bContext *C, | ||||
| uiBlock *block, | uiBlock *block, | ||||
| const uiFontStyle *fstyle, | const uiFontStyle *fstyle, | ||||
| const TreeViewContext *tvc, | const TreeViewContext *tvc, | ||||
| SpaceOutliner *space_outliner, | SpaceOutliner *space_outliner, | ||||
| TreeElement *te_visible, | |||||
| ListBase *lb, | ListBase *lb, | ||||
| int level, | int level, | ||||
| int xmax, | int xmax, | ||||
| int *offsx, | int *offsx, | ||||
| int ys, | int ys, | ||||
| float alpha_fac, | float alpha_fac, | ||||
| MergedIconRow *merged) | MergedIconRow *merged) | ||||
| { | { | ||||
| const ARegion *region = CTX_wm_region(C); | |||||
| eOLDrawState active = OL_DRAWSEL_NONE; | eOLDrawState active = OL_DRAWSEL_NONE; | ||||
| LISTBASE_FOREACH (TreeElement *, te, lb) { | LISTBASE_FOREACH (TreeElement *, te, lb) { | ||||
| TreeStoreElem *tselem = TREESTORE(te); | TreeStoreElem *tselem = TREESTORE(te); | ||||
| te->flag &= ~(TE_ICONROW | TE_ICONROW_MERGED); | te->flag &= ~(TE_ICONROW | TE_ICONROW_MERGED); | ||||
| /* object hierarchy always, further constrained on level */ | /* object hierarchy always, further constrained on level */ | ||||
| if ((level < 1) || ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_OB)) || | if ((level < 1) || ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_OB)) || | ||||
| Show All 23 Lines | if ((level < 1) || ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_OB)) || | ||||
| TSE_LIBRARY_OVERRIDE_BASE, | TSE_LIBRARY_OVERRIDE_BASE, | ||||
| TSE_LIBRARY_OVERRIDE, | TSE_LIBRARY_OVERRIDE, | ||||
| TSE_LIBRARY_OVERRIDE_OPERATION, | TSE_LIBRARY_OVERRIDE_OPERATION, | ||||
| TSE_BONE, | TSE_BONE, | ||||
| TSE_EBONE, | TSE_EBONE, | ||||
| TSE_POSE_CHANNEL, | TSE_POSE_CHANNEL, | ||||
| TSE_POSEGRP, | TSE_POSEGRP, | ||||
| TSE_DEFGROUP)) { | TSE_DEFGROUP)) { | ||||
| outliner_draw_iconrow_doit( | outliner_draw_iconrow_doit(block, te, xmax, offsx, ys, alpha_fac, active, 1); | ||||
| region, block, te_visible, te, fstyle, xmax, offsx, ys, alpha_fac, active, 1); | |||||
| } | } | ||||
| else { | else { | ||||
| const int index = tree_element_id_type_to_index(te); | const int index = tree_element_id_type_to_index(te); | ||||
| merged->num_elements[index]++; | merged->num_elements[index]++; | ||||
| if ((merged->tree_element[index] == nullptr) || (active > merged->active[index])) { | if ((merged->tree_element[index] == nullptr) || (active > merged->active[index])) { | ||||
| merged->tree_element[index] = te; | merged->tree_element[index] = te; | ||||
| } | } | ||||
| merged->active[index] = MAX2(active, merged->active[index]); | merged->active[index] = MAX2(active, merged->active[index]); | ||||
| } | } | ||||
| } | } | ||||
| /* this tree element always has same amount of branches, so don't draw */ | /* this tree element always has same amount of branches, so don't draw */ | ||||
| if (tselem->type != TSE_R_LAYER) { | if (tselem->type != TSE_R_LAYER) { | ||||
| outliner_draw_iconrow(C, | outliner_draw_iconrow(C, | ||||
| block, | block, | ||||
| fstyle, | fstyle, | ||||
| tvc, | tvc, | ||||
| space_outliner, | space_outliner, | ||||
| te, | |||||
| &te->subtree, | &te->subtree, | ||||
| level + 1, | level + 1, | ||||
| xmax, | xmax, | ||||
| offsx, | offsx, | ||||
| ys, | ys, | ||||
| alpha_fac, | alpha_fac, | ||||
| merged); | merged); | ||||
| } | } | ||||
| } | } | ||||
| if (level == 0) { | if (level == 0) { | ||||
| for (int i = 0; i < INDEX_ID_MAX; i++) { | for (int i = 0; i < INDEX_ID_MAX; i++) { | ||||
| const int num_subtypes = (i == INDEX_ID_OB) ? OB_TYPE_MAX : 1; | const int num_subtypes = (i == INDEX_ID_OB) ? OB_TYPE_MAX : 1; | ||||
| /* See tree_element_id_type_to_index for the index logic. */ | /* See tree_element_id_type_to_index for the index logic. */ | ||||
| int index_base = i; | int index_base = i; | ||||
| if (i > INDEX_ID_OB) { | if (i > INDEX_ID_OB) { | ||||
| index_base += OB_TYPE_MAX; | index_base += OB_TYPE_MAX; | ||||
| } | } | ||||
| for (int j = 0; j < num_subtypes; j++) { | for (int j = 0; j < num_subtypes; j++) { | ||||
| const int index = index_base + j; | const int index = index_base + j; | ||||
| if (merged->num_elements[index] != 0) { | if (merged->num_elements[index] != 0) { | ||||
| outliner_draw_iconrow_doit(region, | outliner_draw_iconrow_doit(block, | ||||
| block, | |||||
| te_visible, | |||||
| merged->tree_element[index], | merged->tree_element[index], | ||||
| fstyle, | |||||
| xmax, | xmax, | ||||
| offsx, | offsx, | ||||
| ys, | ys, | ||||
| alpha_fac, | alpha_fac, | ||||
| merged->active[index], | merged->active[index], | ||||
| merged->num_elements[index]); | merged->num_elements[index]); | ||||
| } | } | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 172 Lines • ▼ Show 20 Lines | if (*starty + 2 * UI_UNIT_Y >= region->v2d.cur.ymin && *starty <= region->v2d.cur.ymax) { | ||||
| if (!ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM, TSE_ID_BASE) && | if (!ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM, TSE_ID_BASE) && | ||||
| tselem_draw_icon(block, | tselem_draw_icon(block, | ||||
| xmax, | xmax, | ||||
| float(startx) + offsx, | float(startx) + offsx, | ||||
| float(*starty), | float(*starty), | ||||
| tselem, | tselem, | ||||
| te, | te, | ||||
| (tselem->flag & TSE_HIGHLIGHTED_ICON) ? alpha_fac + 0.5f : alpha_fac, | (tselem->flag & TSE_HIGHLIGHTED_ICON) ? alpha_fac + 0.5f : alpha_fac, | ||||
| true)) { | true, | ||||
| 1)) { | |||||
| offsx += UI_UNIT_X + 4 * ufac; | offsx += UI_UNIT_X + 4 * ufac; | ||||
| } | } | ||||
| else { | else { | ||||
| offsx += 2 * ufac; | offsx += 2 * ufac; | ||||
| } | } | ||||
| const TreeElementRNAStruct *te_rna_struct = tree_element_cast<TreeElementRNAStruct>(te); | const TreeElementRNAStruct *te_rna_struct = tree_element_cast<TreeElementRNAStruct>(te); | ||||
| if (ELEM(tselem->type, TSE_SOME_ID, TSE_LAYER_COLLECTION) || | if (ELEM(tselem->type, TSE_SOME_ID, TSE_LAYER_COLLECTION) || | ||||
| Show All 32 Lines | if (!TSELEM_OPEN(tselem, space_outliner)) { | ||||
| GPU_blend(GPU_BLEND_ALPHA); | GPU_blend(GPU_BLEND_ALPHA); | ||||
| MergedIconRow merged{}; | MergedIconRow merged{}; | ||||
| outliner_draw_iconrow(C, | outliner_draw_iconrow(C, | ||||
| block, | block, | ||||
| fstyle, | fstyle, | ||||
| tvc, | tvc, | ||||
| space_outliner, | space_outliner, | ||||
| te, | |||||
| &te->subtree, | &te->subtree, | ||||
| 0, | 0, | ||||
| xmax, | xmax, | ||||
| &tempx, | &tempx, | ||||
| *starty, | *starty, | ||||
| alpha_fac, | alpha_fac, | ||||
| &merged); | &merged); | ||||
| ▲ Show 20 Lines • Show All 574 Lines • Show Last 20 Lines | |||||