diff options
6 files changed, 211 insertions, 28 deletions
diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index a2fe131198d..a3b1c03a1b8 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -2590,7 +2590,11 @@ static void tselem_draw_icon(uiBlock *block, return; } + /* Icon is covered by restrict buttons */ if (!is_clickable || x >= xmax) { + /* Reduce alpha to match icon buttons */ + alpha *= 0.8f; + /* placement of icons, copied from interface_widgets.c */ float aspect = (0.8f * UI_UNIT_Y) / ICON_DEFAULT_HEIGHT; x += 2.0f * aspect; @@ -2698,7 +2702,6 @@ static void outliner_draw_iconrow_doit(uiBlock *block, float ufac = UI_UNIT_X / 20.0f; float icon_color[4], icon_border[4]; outliner_icon_background_colors(icon_color, icon_border); - icon_color[3] *= alpha_fac; if (active == OL_DRAWSEL_ACTIVE) { UI_GetThemeColor4fv(TH_EDITED_OBJECT, icon_color); icon_border[3] = 0.3f; @@ -2723,6 +2726,9 @@ static void outliner_draw_iconrow_doit(uiBlock *block, GPU_blend(true); /* Roundbox disables. */ } + if (tselem->flag & TSE_HIGHLIGHTED) { + alpha_fac += 0.5; + } tselem_draw_icon(block, xmax, (float)*offsx, (float)ys, tselem, te, alpha_fac, false); te->xs = *offsx; te->ys = ys; @@ -2730,7 +2736,12 @@ static void outliner_draw_iconrow_doit(uiBlock *block, if (num_elements > 1) { outliner_draw_iconrow_number(fstyle, *offsx, ys, num_elements); + te->flag |= TE_ICONROW_MERGED; } + else { + te->flag |= TE_ICONROW; + } + (*offsx) += UI_UNIT_X; } @@ -2740,7 +2751,7 @@ static void outliner_draw_iconrow_doit(uiBlock *block, * We use a continuum of indices until we get to the object data-blocks * and we then make room for the object types. */ -static int tree_element_id_type_to_index(TreeElement *te) +int tree_element_id_type_to_index(TreeElement *te) { TreeStoreElem *tselem = TREESTORE(te); @@ -2870,7 +2881,7 @@ static void outliner_set_coord_tree_element(TreeElement *te, int startx, int sta TreeElement *ten; /* closed items may be displayed in row of parent, don't change their coordinate! */ - if ((te->flag & TE_ICONROW) == 0) { + if ((te->flag & TE_ICONROW) == 0 && (te->flag & TE_ICONROW_MERGED) == 0) { /* store coord and continue, we need coordinates for elements outside view too */ te->xs = startx; te->ys = starty; diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c index 57fea544c77..842502ec82f 100644 --- a/source/blender/editors/space_outliner/outliner_edit.c +++ b/source/blender/editors/space_outliner/outliner_edit.c @@ -101,9 +101,15 @@ static int outliner_highlight_update(bContext *C, wmOperator *UNUSED(op), const ARegion *ar = CTX_wm_region(C); SpaceOutliner *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); + float view_mval[2]; + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &view_mval[0], &view_mval[1]); + + TreeElement *hovered_te = outliner_find_item_at_y(soops, &soops->tree, view_mval[1]); + + if (hovered_te) { + hovered_te = outliner_find_item_at_x_in_row(soops, hovered_te, view_mval[0], NULL); + } bool changed = false; if (!hovered_te || !(hovered_te->store_elem->flag & TSE_HIGHLIGHTED)) { diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h index c12fdcc6d1f..72cd8dc6424 100644 --- a/source/blender/editors/space_outliner/outliner_intern.h +++ b/source/blender/editors/space_outliner/outliner_intern.h @@ -131,6 +131,9 @@ enum { TE_DISABLED = (1 << 4), TE_DRAGGING = (1 << 5), TE_CHILD_NOT_IN_COLLECTION = (1 << 6), + /* Child elements of the same type in the iconrow are drawn merged as one icon. + * TE_ICONROW_MERGED is set for an element that is part of these merged child icons. */ + TE_ICONROW_MERGED = (1 << 7), }; /* button events */ @@ -223,6 +226,8 @@ void outliner_collection_isolate_flag(struct Scene *scene, const char *propname, const bool value); +int tree_element_id_type_to_index(TreeElement *te); + /* outliner_select.c -------------------------------------------- */ eOLDrawState tree_element_type_active(struct bContext *C, struct Scene *scene, @@ -255,6 +260,8 @@ void outliner_object_mode_toggle(struct bContext *C, void outliner_element_activate(struct SpaceOutliner *soops, struct TreeStoreElem *tselem); +bool outliner_item_is_co_within_close_toggle(TreeElement *te, float view_co_x); + /* outliner_edit.c ---------------------------------------------- */ typedef void (*outliner_operation_cb)(struct bContext *C, struct ReportList *, @@ -382,6 +389,10 @@ void OUTLINER_OT_orphans_purge(struct wmOperatorType *ot); /* outliner_tools.c ---------------------------------------------- */ +void merged_element_search_menu_invoke(struct bContext *C, + TreeElement *parent_te, + TreeElement *activate_te); + void OUTLINER_OT_operation(struct wmOperatorType *ot); void OUTLINER_OT_scene_operation(struct wmOperatorType *ot); void OUTLINER_OT_object_operation(struct wmOperatorType *ot); @@ -441,7 +452,8 @@ TreeElement *outliner_find_item_at_y(const SpaceOutliner *soops, float view_co_y); TreeElement *outliner_find_item_at_x_in_row(const SpaceOutliner *soops, const TreeElement *parent_te, - float view_co_x); + float view_co_x, + bool *multiple_objects); TreeElement *outliner_find_tse(struct SpaceOutliner *soops, const TreeStoreElem *tse); TreeElement *outliner_find_tree_element(ListBase *lb, const TreeStoreElem *store_elem); TreeElement *outliner_find_parent_element(ListBase *lb, diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c index 38dfe439b9c..ddb943d6c66 100644 --- a/source/blender/editors/space_outliner/outliner_select.c +++ b/source/blender/editors/space_outliner/outliner_select.c @@ -1246,12 +1246,6 @@ static void outliner_item_toggle_closed(TreeElement *te, const bool toggle_child } } -static bool outliner_item_is_co_within_close_toggle(TreeElement *te, float view_co_x) -{ - return ((te->flag & TE_ICONROW) == 0) && (view_co_x > te->xs) && - (view_co_x < te->xs + UI_UNIT_X); -} - static bool outliner_is_co_within_restrict_columns(const SpaceOutliner *soops, const ARegion *ar, float view_co_x) @@ -1313,8 +1307,18 @@ static int outliner_item_do_activate_from_cursor(bContext *C, else { Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); - /* the row may also contain children, if one is hovered we want this instead of current te */ - TreeElement *activate_te = outliner_find_item_at_x_in_row(soops, te, view_mval[0]); + + /* The row may also contain children, if one is hovered we want this instead of current te */ + bool merged_elements = false; + TreeElement *activate_te = outliner_find_item_at_x_in_row( + soops, te, view_mval[0], &merged_elements); + + /* If the selected icon was an aggregate of multiple elements, run the search popup */ + if (merged_elements) { + merged_element_search_menu_invoke(C, te, activate_te); + return OPERATOR_CANCELLED; + } + TreeStoreElem *activate_tselem = TREESTORE(activate_te); outliner_item_select(soops, activate_te, extend, extend); diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index f9905cc4fcd..582a9cad312 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -63,6 +63,7 @@ #include "ED_armature.h" #include "ED_object.h" +#include "ED_outliner.h" #include "ED_scene.h" #include "ED_screen.h" #include "ED_sequencer.h" @@ -478,6 +479,127 @@ void OUTLINER_OT_scene_operation(wmOperatorType *ot) } /* ******************************************** */ +/* Stores the parent and a child element of a merged iconrow icon for + * the merged select popup menu. The subtree of the parent is searched and + * the child is needed to only show elements of the same type in the popup. */ +typedef struct MergedSearchData { + TreeElement *parent_element; + TreeElement *select_element; +} MergedSearchData; + +static void merged_element_search_cb_recursive( + const ListBase *tree, short tselem_type, short type, const char *str, uiSearchItems *items) +{ + char name[64]; + int iconid; + + for (TreeElement *te = tree->first; te; te = te->next) { + TreeStoreElem *tselem = TREESTORE(te); + + if (tree_element_id_type_to_index(te) == type && tselem_type == tselem->type) { + if (BLI_strcasestr(te->name, str)) { + BLI_strncpy(name, te->name, 64); + + iconid = tree_element_get_icon(tselem, te).icon; + + /* Don't allow duplicate named items */ + if (UI_search_items_find_index(items, name) == -1) { + if (!UI_search_item_add(items, name, te, iconid)) { + break; + } + } + } + } + + merged_element_search_cb_recursive(&te->subtree, tselem_type, type, str, items); + } +} + +/* Get a list of elements that match the search string */ +static void merged_element_search_cb(const bContext *UNUSED(C), + void *data, + const char *str, + uiSearchItems *items) +{ + MergedSearchData *search_data = (MergedSearchData *)data; + TreeElement *parent = search_data->parent_element; + TreeElement *te = search_data->select_element; + + int type = tree_element_id_type_to_index(te); + + merged_element_search_cb_recursive(&parent->subtree, TREESTORE(te)->type, type, str, items); +} + +/* Activate an element from the merged element search menu */ +static void merged_element_search_call_cb(struct bContext *C, void *UNUSED(arg1), void *element) +{ + SpaceOutliner *soops = CTX_wm_space_outliner(C); + TreeElement *te = (TreeElement *)element; + + outliner_item_select(soops, te, false, false); + outliner_item_do_activate_from_tree_element(C, te, te->store_elem, false, false); + + if (soops->flag & SO_SYNC_SELECT) { + ED_outliner_select_sync_from_outliner(C, soops); + } +} + +/** Merged element search menu + * Created on activation of a merged or aggregated iconrow icon. + */ +static uiBlock *merged_element_search_menu(bContext *C, ARegion *ar, void *data) +{ + static char search[64] = ""; + uiBlock *block; + uiBut *but; + + /* Clear search on each menu creation */ + *search = '\0'; + + block = UI_block_begin(C, ar, __func__, UI_EMBOSS); + UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_SEARCH_MENU); + UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP); + + short menu_width = 10 * UI_UNIT_X; + but = uiDefSearchBut( + block, search, 0, ICON_VIEWZOOM, sizeof(search), 10, 10, menu_width, UI_UNIT_Y, 0, 0, ""); + UI_but_func_search_set( + but, NULL, merged_element_search_cb, data, false, merged_element_search_call_cb, NULL); + UI_but_flag_enable(but, UI_BUT_ACTIVATE_ON_INIT); + + /* Fake button to hold space for search items */ + uiDefBut(block, + UI_BTYPE_LABEL, + 0, + "", + 10, + 10 - UI_searchbox_size_y(), + menu_width, + UI_searchbox_size_y(), + NULL, + 0, + 0, + 0, + 0, + NULL); + + /* Center the menu on the cursor */ + UI_block_bounds_set_popup(block, 6, (const int[2]){-(menu_width / 2), 0}); + + return block; +} + +void merged_element_search_menu_invoke(bContext *C, + TreeElement *parent_te, + TreeElement *activate_te) +{ + MergedSearchData *select_data = MEM_callocN(sizeof(MergedSearchData), "merge_search_data"); + select_data->parent_element = parent_te; + select_data->select_element = activate_te; + + UI_popup_block_invoke(C, merged_element_search_menu, select_data, MEM_freeN); +} + static void object_select_cb(bContext *C, ReportList *UNUSED(reports), Scene *UNUSED(scene), diff --git a/source/blender/editors/space_outliner/outliner_utils.c b/source/blender/editors/space_outliner/outliner_utils.c index 727d9866793..d39a9387a6a 100644 --- a/source/blender/editors/space_outliner/outliner_utils.c +++ b/source/blender/editors/space_outliner/outliner_utils.c @@ -66,6 +66,38 @@ TreeElement *outliner_find_item_at_y(const SpaceOutliner *soops, return NULL; } +static TreeElement *outliner_find_item_at_x_in_row_recursive(const TreeElement *parent_te, + float view_co_x, + bool *r_merged) +{ + TreeElement *child_te = parent_te->subtree.first; + + bool over_element = false; + + while (child_te) { + over_element = (view_co_x > child_te->xs) && (view_co_x < child_te->xend); + if ((child_te->flag & TE_ICONROW) && over_element) { + return child_te; + } + else if ((child_te->flag & TE_ICONROW_MERGED) && over_element) { + if (r_merged) { + *r_merged = true; + } + return child_te; + } + + TreeElement *te = outliner_find_item_at_x_in_row_recursive(child_te, view_co_x, r_merged); + if (te != child_te) { + return te; + } + + child_te = child_te->next; + } + + /* return parent if no child is hovered */ + return (TreeElement *)parent_te; +} + /** * Collapsed items can show their children as click-able icons. This function tries to find * such an icon that represents the child item at x-coordinate \a view_co_x (view-space). @@ -74,24 +106,14 @@ TreeElement *outliner_find_item_at_y(const SpaceOutliner *soops, */ TreeElement *outliner_find_item_at_x_in_row(const SpaceOutliner *soops, const TreeElement *parent_te, - float view_co_x) + float view_co_x, + bool *r_merged) { - /* if parent_te is opened, it doesn't show childs in row */ + /* if parent_te is opened, it doesn't show children in row */ if (!TSELEM_OPEN(TREESTORE(parent_te), soops)) { - /* no recursion, items can only display their direct children in the row */ - for (TreeElement *child_te = parent_te->subtree.first; - /* don't look further if co_x is smaller than child position*/ - child_te && view_co_x >= child_te->xs; - - child_te = child_te->next) { - if ((child_te->flag & TE_ICONROW) && (view_co_x > child_te->xs) && - (view_co_x < child_te->xend)) { - return child_te; - } - } + return outliner_find_item_at_x_in_row_recursive(parent_te, view_co_x, r_merged); } - /* return parent if no child is hovered */ return (TreeElement *)parent_te; } @@ -305,6 +327,12 @@ float outliner_restrict_columns_width(const SpaceOutliner *soops) return (num_columns * UI_UNIT_X + V2D_SCROLL_WIDTH); } +/* Find if x coordinate is over element disclosure toggle */ +bool outliner_item_is_co_within_close_toggle(TreeElement *te, float view_co_x) +{ + return (view_co_x > te->xs) && (view_co_x < te->xs + UI_UNIT_X); +} + /* Get base of object under cursor. Used for eyedropper tool */ Base *ED_outliner_give_base_under_cursor(bContext *C, const int mval[2]) { |