diff options
5 files changed, 160 insertions, 52 deletions
diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index 7cf40ca07f6..7f45784c341 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -1448,23 +1448,8 @@ static void outliner_draw_tree_element( glEnable(GL_BLEND); - /* start by highlighting search matches - * we don't expand items when searching in the datablocks but we - * still want to highlight any filter matches. - */ - if ((SEARCHING_OUTLINER(soops) || (soops->outlinevis == SO_DATABLOCKS && soops->search_string[0] != 0)) && - (tselem->flag & TSE_SEARCHMATCH)) - { - char col[4]; - UI_GetThemeColorType4ubv(TH_MATCH, SPACE_OUTLINER, col); - col[3] = alpha; - glColor4ubv((GLubyte *)col); - glRecti(startx, *starty + 1, ar->v2d.cur.xmax, *starty + UI_UNIT_Y - 1); - } - /* colors for active/selected data */ if (tselem->type == 0) { - if (te->idcode == ID_SCE) { if (tselem->id == (ID *)scene) { glColor4ub(255, 255, 255, alpha); @@ -1650,7 +1635,7 @@ static void outliner_draw_tree_element( } } -static void outliner_draw_hierarchy(SpaceOops *soops, ListBase *lb, int startx, int *starty) +static void outliner_draw_hierarchy_lines(SpaceOops *soops, ListBase *lb, int startx, int *starty) { TreeElement *te; TreeStoreElem *tselem; @@ -1670,7 +1655,7 @@ static void outliner_draw_hierarchy(SpaceOops *soops, ListBase *lb, int startx, *starty -= UI_UNIT_Y; if (TSELEM_OPEN(tselem, soops)) - outliner_draw_hierarchy(soops, &te->subtree, startx + UI_UNIT_X, starty); + outliner_draw_hierarchy_lines(soops, &te->subtree, startx + UI_UNIT_X, starty); } /* vertical line */ @@ -1706,34 +1691,71 @@ static void outliner_draw_struct_marks(ARegion *ar, SpaceOops *soops, ListBase * } } -static void outliner_draw_selection(ARegion *ar, SpaceOops *soops, ListBase *lb, int *starty) +static void outliner_draw_highlights_recursive( + const ARegion *ar, const SpaceOops *soops, const ListBase *lb, + const float col_selection[4], const float col_highlight[4], const float col_searchmatch[4], + int start_x, int *io_start_y) { - TreeElement *te; - TreeStoreElem *tselem; - - for (te = lb->first; te; te = te->next) { - tselem = TREESTORE(te); - + const bool is_searching = SEARCHING_OUTLINER(soops) || + (soops->outlinevis == SO_DATABLOCKS && soops->search_string[0] != 0); + + for (TreeElement *te = lb->first; te; te = te->next) { + const TreeStoreElem *tselem = TREESTORE(te); + const int start_y = *io_start_y; + /* selection status */ if (tselem->flag & TSE_SELECTED) { - glRecti(0, *starty + 1, (int)ar->v2d.cur.xmax, *starty + UI_UNIT_Y - 1); + glColor4fv(col_selection); + glRecti(0, start_y + 1, (int)ar->v2d.cur.xmax, start_y + UI_UNIT_Y - 1); + } + + /* search match highlights + * we don't expand items when searching in the datablocks but we + * still want to highlight any filter matches. */ + if (is_searching && (tselem->flag & TSE_SEARCHMATCH)) { + glColor4fv(col_searchmatch); + glRecti(start_x, start_y + 1, ar->v2d.cur.xmax, start_y + UI_UNIT_Y - 1); + } + + /* mouse hover highlights */ + if (tselem->flag & TSE_HIGHLIGHTED) { + glColor4fv(col_highlight); + glRecti(0, start_y + 1, (int)ar->v2d.cur.xmax, start_y + UI_UNIT_Y - 1); + } + + *io_start_y -= UI_UNIT_Y; + if (TSELEM_OPEN(tselem, soops)) { + outliner_draw_highlights_recursive( + ar, soops, &te->subtree, col_selection, col_highlight, col_searchmatch, + start_x, io_start_y); } - *starty -= UI_UNIT_Y; - if (TSELEM_OPEN(tselem, soops)) outliner_draw_selection(ar, soops, &te->subtree, starty); } } +static void outliner_draw_highlights(ARegion *ar, SpaceOops *soops, int startx, int *starty) +{ + const float col_highlight[4] = {1.0f, 1.0f, 1.0f, 0.13f}; + float col_selection[4], col_searchmatch[4]; + + UI_GetThemeColor3fv(TH_SELECT_HIGHLIGHT, col_selection); + col_selection[3] = 1.0f; /* no alpha */ + UI_GetThemeColor4fv(TH_MATCH, col_searchmatch); + col_searchmatch[3] = 0.5f; + + glEnable(GL_BLEND); + outliner_draw_highlights_recursive(ar, soops, &soops->tree, col_selection, col_highlight, col_searchmatch, + startx, starty); + glDisable(GL_BLEND); +} static void outliner_draw_tree(bContext *C, uiBlock *block, Scene *scene, ARegion *ar, SpaceOops *soops, TreeElement **te_edit) { const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; - TreeElement *te; int starty, startx; - float col[3]; - + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // only once - + if (ELEM(soops->outlinevis, SO_DATABLOCKS, SO_USERDEF)) { /* struct marks */ UI_ThemeColorShadeAlpha(TH_BACK, -15, -200); @@ -1741,23 +1763,22 @@ static void outliner_draw_tree(bContext *C, uiBlock *block, Scene *scene, ARegio starty = (int)ar->v2d.tot.ymax - UI_UNIT_Y - OL_Y_OFFSET; outliner_draw_struct_marks(ar, soops, &soops->tree, &starty); } - - /* always draw selection fill before hierarchy */ - UI_GetThemeColor3fv(TH_SELECT_HIGHLIGHT, col); - glColor3fv(col); + + /* draw highlights before hierarchy */ starty = (int)ar->v2d.tot.ymax - UI_UNIT_Y - OL_Y_OFFSET; - outliner_draw_selection(ar, soops, &soops->tree, &starty); + startx = 0; + outliner_draw_highlights(ar, soops, startx, &starty); // gray hierarchy lines UI_ThemeColorBlend(TH_BACK, TH_TEXT, 0.4f); starty = (int)ar->v2d.tot.ymax - UI_UNIT_Y / 2 - OL_Y_OFFSET; startx = 6; - outliner_draw_hierarchy(soops, &soops->tree, startx, &starty); - + outliner_draw_hierarchy_lines(soops, &soops->tree, startx, &starty); + // items themselves starty = (int)ar->v2d.tot.ymax - UI_UNIT_Y - OL_Y_OFFSET; startx = 0; - for (te = soops->tree.first; te; te = te->next) { + for (TreeElement *te = soops->tree.first; te; te = te->next) { outliner_draw_tree_element(C, block, fstyle, scene, ar, soops, te, startx, &starty, te_edit); } } diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c index 8eb53d0d7b9..bfb87b6cd7b 100644 --- a/source/blender/editors/space_outliner/outliner_edit.c +++ b/source/blender/editors/space_outliner/outliner_edit.c @@ -151,7 +151,69 @@ TreeElement *outliner_dropzone_find(const SpaceOops *soops, const float fmval[2] } /* ************************************************************** */ -/* Click Activated */ + +/* Highlight --------------------------------------------------- */ + +/** + * Try to find an item under y-coordinate \a view_co_y (view-space). + * \note Recursive + */ +static TreeElement *outliner_find_item_at_y( + const SpaceOops *soops, const ListBase *tree, float view_co_y) +{ + for (TreeElement *te_iter = tree->first; te_iter; te_iter = te_iter->next) { + if (view_co_y < (te_iter->ys + UI_UNIT_Y)) { + if (view_co_y > te_iter->ys) { + /* co_y is inside this element */ + return te_iter; + } + else if (TSELEM_OPEN(te_iter->store_elem, soops)) { + /* co_y is lower than current element, possibly inside children */ + TreeElement *te_sub = outliner_find_item_at_y(soops, &te_iter->subtree, view_co_y); + if (te_sub) { + return te_sub; + } + } + } + } + + return NULL; +} + +static int outliner_highlight_update(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) +{ + ARegion *ar = CTX_wm_region(C); + SpaceOops *soops = CTX_wm_space_outliner(C); + const float my = UI_view2d_region_to_view_y(&ar->v2d, event->mval[1]); + + TreeElement *hovered_te = outliner_find_item_at_y(soops, &soops->tree, my); + bool changed; + + if (!hovered_te || !(hovered_te->store_elem->flag & TSE_HIGHLIGHTED)) { + changed = outliner_set_flag(soops, &soops->tree, TSE_HIGHLIGHTED, false); + if (hovered_te) { + hovered_te->store_elem->flag |= TSE_HIGHLIGHTED; + changed = true; + } + } + + if (changed) { + ED_region_tag_redraw(ar); + } + + return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH); +} + +void OUTLINER_OT_highlight_update(wmOperatorType *ot) +{ + ot->name = "Update Highlight"; + ot->idname = "OUTLINER_OT_highlight_update"; + ot->description = "Update the item highlight based on the current mouse position"; + + ot->invoke = outliner_highlight_update; + + ot->poll = ED_operator_outliner_active; +} /* Toggle Open/Closed ------------------------------------------- */ @@ -742,17 +804,34 @@ int outliner_has_one_flag(SpaceOops *soops, ListBase *lb, short flag, const int return 0; } -void outliner_set_flag(SpaceOops *soops, ListBase *lb, short flag, short set) +/** + * Set or unset \a flag for all outliner elements in \a lb and sub-trees. + * \return if any flag was modified. + */ +bool outliner_set_flag(SpaceOops *soops, ListBase *lb, short flag, short set) { TreeElement *te; TreeStoreElem *tselem; - + bool changed = false; + bool has_flag; + for (te = lb->first; te; te = te->next) { tselem = TREESTORE(te); - if (set == 0) tselem->flag &= ~flag; - else tselem->flag |= flag; - outliner_set_flag(soops, &te->subtree, flag, set); + has_flag = (tselem->flag & flag); + if (set == 0) { + if (has_flag) { + tselem->flag &= ~flag; + changed = true; + } + } + else if (!has_flag){ + tselem->flag |= flag; + changed = true; + } + changed |= outliner_set_flag(soops, &te->subtree, flag, set); } + + return changed; } /* Restriction Columns ------------------------------- */ diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h index e95ca32048d..74c59fb4533 100644 --- a/source/blender/editors/space_outliner/outliner_intern.h +++ b/source/blender/editors/space_outliner/outliner_intern.h @@ -167,7 +167,7 @@ void outliner_do_object_operation( int common_restrict_check(struct bContext *C, struct Object *ob); int outliner_has_one_flag(struct SpaceOops *soops, ListBase *lb, short flag, const int curlevel); -void outliner_set_flag(struct SpaceOops *soops, ListBase *lb, short flag, short set); +bool outliner_set_flag(struct SpaceOops *soops, ListBase *lb, short flag, short set); void object_toggle_visibility_cb( struct bContext *C, struct ReportList *reports, struct Scene *scene, @@ -210,6 +210,8 @@ void id_remap_cb( TreeElement *outliner_dropzone_find(const struct SpaceOops *soops, const float fmval[2], const bool children); /* ...................................................... */ +void OUTLINER_OT_highlight_update(struct wmOperatorType *ot); + void OUTLINER_OT_item_activate(struct wmOperatorType *ot); void OUTLINER_OT_item_openclose(struct wmOperatorType *ot); void OUTLINER_OT_item_rename(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_outliner/outliner_ops.c b/source/blender/editors/space_outliner/outliner_ops.c index 776717c8443..f0c2d848f7a 100644 --- a/source/blender/editors/space_outliner/outliner_ops.c +++ b/source/blender/editors/space_outliner/outliner_ops.c @@ -43,6 +43,7 @@ void outliner_operatortypes(void) { + WM_operatortype_append(OUTLINER_OT_highlight_update); WM_operatortype_append(OUTLINER_OT_item_activate); WM_operatortype_append(OUTLINER_OT_select_border); WM_operatortype_append(OUTLINER_OT_item_openclose); @@ -93,7 +94,9 @@ void outliner_keymap(wmKeyConfig *keyconf) { wmKeyMap *keymap = WM_keymap_find(keyconf, "Outliner", SPACE_OUTLINER, 0); wmKeyMapItem *kmi; - + + WM_keymap_add_item(keymap, "OUTLINER_OT_highlight_update", MOUSEMOVE, KM_ANY, KM_ANY, 0); + WM_keymap_add_item(keymap, "OUTLINER_OT_item_rename", LEFTMOUSE, KM_DBL_CLICK, 0, 0); kmi = WM_keymap_add_item(keymap, "OUTLINER_OT_item_activate", LEFTMOUSE, KM_CLICK, 0, 0); diff --git a/source/blender/makesdna/DNA_outliner_types.h b/source/blender/makesdna/DNA_outliner_types.h index 51bd30cdf14..c26c236b978 100644 --- a/source/blender/makesdna/DNA_outliner_types.h +++ b/source/blender/makesdna/DNA_outliner_types.h @@ -50,11 +50,14 @@ typedef struct TreeStore { } TreeStore; /* TreeStoreElem->flag */ -#define TSE_CLOSED 1 -#define TSE_SELECTED 2 -#define TSE_TEXTBUT 4 -#define TSE_CHILDSEARCH 8 -#define TSE_SEARCHMATCH 16 +enum { + TSE_CLOSED = (1 << 0), + TSE_SELECTED = (1 << 1), + TSE_TEXTBUT = (1 << 2), + TSE_CHILDSEARCH = (1 << 3), + TSE_SEARCHMATCH = (1 << 4), + TSE_HIGHLIGHTED = (1 << 5), +}; /* TreeStoreElem->types */ #define TSE_NLA 1 /* NO ID */ |