Outliner: Element mouse hover feedback
Some little UI polish to get familiar with outliner code (but also because it's a useful feature). Committing to blender2.8 branch but can also port to master (2.7) if wanted. This basically causes the mouse hovered element to be highlighted. Contrast of the highlight should be fine, even with a non-default theme. Also did some minor cleanup.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -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 ------------------------------- */
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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 */
|
||||
|
Reference in New Issue
Block a user