Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNathan Craddock <nzcraddock@gmail.com>2019-08-09 07:00:57 +0300
committerNathan Craddock <nzcraddock@gmail.com>2019-08-16 21:30:54 +0300
commit252fb4899735ca154cae9147be819f2362f2ba51 (patch)
treefd8f9ef96ebb5b21528657d3a1c527221b2e2027
parenteb92ac05d6f850ccdbc0ed636763669f5d1e765e (diff)
Outliner: walk navigation operator and openclose fixes
Adds a keyboard walk navigation and selection operator to the outliner. Up and down arrow keys walk up and down the list of elements, and left and right will open and close elements if the elements are closed or opened respectively. Holding shift while walking up and down the tree expands the selection. Holding shift while clicking or pressing left and right arrows will expand or collapse all children elements recursively. Pressing enter to openclose the hovered element is removed. Also allows click+drag for openclose of element subtrees. This moves openclose toggling to the openclose operator to remove duplicate code. The outliner tree building is tweaked slightly to set the proper parents in scene display mode for walk select to walk to parents without errors.
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/blender_default.py19
-rw-r--r--source/blender/editors/space_outliner/outliner_edit.c122
-rw-r--r--source/blender/editors/space_outliner/outliner_intern.h14
-rw-r--r--source/blender/editors/space_outliner/outliner_ops.c1
-rw-r--r--source/blender/editors/space_outliner/outliner_select.c254
-rw-r--r--source/blender/editors/space_outliner/outliner_tree.c4
-rw-r--r--source/blender/editors/space_outliner/outliner_utils.c56
-rw-r--r--source/blender/editors/space_outliner/space_outliner.c4
-rw-r--r--source/blender/makesdna/DNA_outliner_types.h2
9 files changed, 412 insertions, 64 deletions
diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
index 3f6a5f0a514..798bdc1fe01 100644
--- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py
+++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
@@ -709,11 +709,24 @@ def km_outliner(params):
("outliner.item_activate", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True, "ctrl": True},
{"properties": [("extend", True), ("recursive", True)]}),
("outliner.select_box", {"type": 'B', "value": 'PRESS'}, None),
- ("outliner.item_openclose", {"type": 'RET', "value": 'PRESS'},
+ ("outliner.select_walk", {"type": 'UP_ARROW', "value": 'PRESS'}, {"properties": [("direction", 'UP')]}),
+ ("outliner.select_walk", {"type": 'UP_ARROW', "value": 'PRESS', "shift": True},
+ {"properties": [("direction", 'UP'), ("extend", True)]}),
+ ("outliner.select_walk", {"type": 'DOWN_ARROW', "value": 'PRESS'}, {"properties": [("direction", 'DOWN')]}),
+ ("outliner.select_walk", {"type": 'DOWN_ARROW', "value": 'PRESS', "shift": True},
+ {"properties": [("direction", 'DOWN'), ("extend", True)]}),
+ ("outliner.select_walk", {"type": 'LEFT_ARROW', "value": 'PRESS'}, {"properties": [("direction", 'LEFT')]}),
+ ("outliner.select_walk", {"type": 'LEFT_ARROW', "value": 'PRESS', "shift": True},
+ {"properties": [("direction", 'LEFT'), ("toggle_all", True)]}),
+ ("outliner.select_walk", {"type": 'RIGHT_ARROW', "value": 'PRESS'}, {"properties": [("direction", 'RIGHT')]}),
+ ("outliner.select_walk", {"type": 'RIGHT_ARROW', "value": 'PRESS', "shift": True},
+ {"properties": [("direction", 'RIGHT'), ("toggle_all", True)]}),
+ ("outliner.item_openclose", {"type": 'LEFTMOUSE', "value": 'CLICK'},
{"properties": [("all", False)]}),
- ("outliner.item_openclose", {"type": 'RET', "value": 'PRESS', "shift": True},
+ ("outliner.item_openclose", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True},
{"properties": [("all", True)]}),
- ("outliner.item_rename", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True}, None),
+ ("outliner.item_openclose", {"type": 'EVT_TWEAK_L', "value": 'ANY'},
+ {"properties": [("all", False)]}),
("outliner.operation", {"type": 'RIGHTMOUSE', "value": 'PRESS'}, None),
("outliner.item_drag_drop", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
("outliner.item_drag_drop", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True}, None),
diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c
index 842502ec82f..60b5dbdfc50 100644
--- a/source/blender/editors/space_outliner/outliner_edit.c
+++ b/source/blender/editors/space_outliner/outliner_edit.c
@@ -140,59 +140,108 @@ void OUTLINER_OT_highlight_update(wmOperatorType *ot)
/* Toggle Open/Closed ------------------------------------------- */
-static int do_outliner_item_openclose(
- bContext *C, SpaceOutliner *soops, TreeElement *te, const bool all, const float mval[2])
+/* Open or close a tree element, optionally toggling all children recursively */
+void outliner_item_openclose(TreeElement *te, bool open, bool toggle_all)
{
+ TreeStoreElem *tselem = TREESTORE(te);
- if (mval[1] > te->ys && mval[1] < te->ys + UI_UNIT_Y) {
- TreeStoreElem *tselem = TREESTORE(te);
+ if (open) {
+ tselem->flag &= ~TSE_CLOSED;
+ }
+ else {
+ tselem->flag |= TSE_CLOSED;
+ }
- /* all below close/open? */
- if (all) {
- tselem->flag &= ~TSE_CLOSED;
- outliner_flag_set(
- &te->subtree, TSE_CLOSED, !outliner_flag_is_any_test(&te->subtree, TSE_CLOSED, 1));
- }
- else {
- if (tselem->flag & TSE_CLOSED) {
- tselem->flag &= ~TSE_CLOSED;
- }
- else {
- tselem->flag |= TSE_CLOSED;
+ if (toggle_all) {
+ outliner_flag_set(&te->subtree, TSE_CLOSED, !open);
+ }
+}
+
+typedef struct OpenCloseData {
+ TreeStoreElem *prev_tselem;
+ bool open;
+ int x_location;
+} OpenCloseData;
+
+static int outliner_item_openclose_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ ARegion *ar = CTX_wm_region(C);
+ SpaceOutliner *soops = CTX_wm_space_outliner(C);
+
+ float view_mval[2];
+ UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &view_mval[0], &view_mval[1]);
+
+ if (event->type == MOUSEMOVE) {
+ TreeElement *te = outliner_find_item_at_y(soops, &soops->tree, view_mval[1]);
+
+ OpenCloseData *data = (OpenCloseData *)op->customdata;
+
+ /* Only openclose if mouse is not over the previously toggled element */
+ if (te && TREESTORE(te) != data->prev_tselem) {
+
+ /* Only toggle openclose on the same level as the first clicked element */
+ if (te->xs == data->x_location) {
+ outliner_item_openclose(te, data->open, false);
+ ED_region_tag_redraw(ar);
}
}
- return 1;
+ if (te) {
+ data->prev_tselem = TREESTORE(te);
+ }
+ else {
+ data->prev_tselem = NULL;
+ }
}
+ else if (event->val == KM_RELEASE) {
+ MEM_freeN(op->customdata);
- for (te = te->subtree.first; te; te = te->next) {
- if (do_outliner_item_openclose(C, soops, te, all, mval)) {
- return 1;
- }
+ return OPERATOR_FINISHED;
}
- return 0;
+
+ return OPERATOR_RUNNING_MODAL;
}
-/* event can enterkey, then it opens/closes */
-static int outliner_item_openclose(bContext *C, wmOperator *op, const wmEvent *event)
+static int outliner_item_openclose_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
ARegion *ar = CTX_wm_region(C);
SpaceOutliner *soops = CTX_wm_space_outliner(C);
- TreeElement *te;
- float fmval[2];
- const bool all = RNA_boolean_get(op->ptr, "all");
- UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
+ const bool toggle_all = RNA_boolean_get(op->ptr, "all");
- for (te = soops->tree.first; te; te = te->next) {
- if (do_outliner_item_openclose(C, soops, te, all, fmval)) {
- break;
+ float view_mval[2];
+ UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &view_mval[0], &view_mval[1]);
+
+ TreeElement *te = outliner_find_item_at_y(soops, &soops->tree, view_mval[1]);
+
+ if (te && outliner_item_is_co_within_close_toggle(te, view_mval[0])) {
+ TreeStoreElem *tselem = TREESTORE(te);
+
+ const bool open = (tselem->flag & TSE_CLOSED) ||
+ (toggle_all && (outliner_flag_is_any_test(&te->subtree, TSE_CLOSED, 1)));
+
+ outliner_item_openclose(te, open, toggle_all);
+ ED_region_tag_redraw(ar);
+
+ /* Only toggle once for single click toggling */
+ if (event->type == LEFTMOUSE) {
+ return OPERATOR_FINISHED;
}
- }
- ED_region_tag_redraw(ar);
+ /* Store last expanded tselem and x coordinate of disclosure triangle */
+ OpenCloseData *toggle_data = MEM_callocN(sizeof(OpenCloseData), "open_close_data");
+ toggle_data->prev_tselem = tselem;
+ toggle_data->open = open;
+ toggle_data->x_location = te->xs;
- return OPERATOR_FINISHED;
+ /* Store the first clicked on element */
+ op->customdata = toggle_data;
+
+ WM_event_add_modal_handler(C, op);
+ return OPERATOR_RUNNING_MODAL;
+ }
+
+ return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
}
void OUTLINER_OT_item_openclose(wmOperatorType *ot)
@@ -201,11 +250,12 @@ void OUTLINER_OT_item_openclose(wmOperatorType *ot)
ot->idname = "OUTLINER_OT_item_openclose";
ot->description = "Toggle whether item under cursor is enabled or closed";
- ot->invoke = outliner_item_openclose;
+ ot->invoke = outliner_item_openclose_invoke;
+ ot->modal = outliner_item_openclose_modal;
ot->poll = ED_operator_outliner_active;
- RNA_def_boolean(ot->srna, "all", 1, "All", "Close or open all items");
+ RNA_def_boolean(ot->srna, "all", false, "All", "Close or open all items");
}
/* -------------------------------------------------------------------- */
diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h
index 72cd8dc6424..e37f3519653 100644
--- a/source/blender/editors/space_outliner/outliner_intern.h
+++ b/source/blender/editors/space_outliner/outliner_intern.h
@@ -50,6 +50,14 @@ typedef enum TreeElementInsertType {
TE_INSERT_INTO,
} TreeElementInsertType;
+/* Use generic walk select after D4771 is committed */
+typedef enum WalkSelectDirection {
+ OUTLINER_SELECT_WALK_UP,
+ OUTLINER_SELECT_WALK_DOWN,
+ OUTLINER_SELECT_WALK_LEFT,
+ OUTLINER_SELECT_WALK_RIGHT,
+} WalkSelectDirection;
+
typedef enum TreeTraversalAction {
/* Continue traversal regularly, don't skip children. */
TRAVERSE_CONTINUE = 0,
@@ -346,6 +354,8 @@ void item_object_mode_exit_cb(struct bContext *C,
void outliner_set_coordinates(struct ARegion *ar, struct SpaceOutliner *soops);
+void outliner_item_openclose(TreeElement *te, bool open, bool toggle_all);
+
/* outliner_dragdrop.c */
void outliner_dropboxes(void);
@@ -373,6 +383,7 @@ void OUTLINER_OT_show_active(struct wmOperatorType *ot);
void OUTLINER_OT_show_hierarchy(struct wmOperatorType *ot);
void OUTLINER_OT_select_box(struct wmOperatorType *ot);
+void OUTLINER_OT_select_walk(struct wmOperatorType *ot);
void OUTLINER_OT_select_all(struct wmOperatorType *ot);
void OUTLINER_OT_expanded_toggle(struct wmOperatorType *ot);
@@ -470,6 +481,9 @@ bool outliner_tree_traverse(const SpaceOutliner *soops,
TreeTraversalFunc func,
void *customdata);
float outliner_restrict_columns_width(const struct SpaceOutliner *soops);
+TreeElement *outliner_find_element_with_flag(const ListBase *lb, short flag);
+bool outliner_is_element_visible(const TreeElement *te);
+void outliner_scroll_view(struct ARegion *ar, int delta_y);
/* outliner_sync.c ---------------------------------------------- */
diff --git a/source/blender/editors/space_outliner/outliner_ops.c b/source/blender/editors/space_outliner/outliner_ops.c
index f155a2d5f89..4b57d4ad771 100644
--- a/source/blender/editors/space_outliner/outliner_ops.c
+++ b/source/blender/editors/space_outliner/outliner_ops.c
@@ -50,6 +50,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_box);
+ WM_operatortype_append(OUTLINER_OT_select_walk);
WM_operatortype_append(OUTLINER_OT_item_openclose);
WM_operatortype_append(OUTLINER_OT_item_rename);
WM_operatortype_append(OUTLINER_OT_item_drag_drop);
diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c
index ddb943d6c66..6e70cdcddcd 100644
--- a/source/blender/editors/space_outliner/outliner_select.c
+++ b/source/blender/editors/space_outliner/outliner_select.c
@@ -1087,8 +1087,8 @@ eOLDrawState tree_element_type_active(bContext *C,
/* Activate a tree store element and set the walk navigation start element */
void outliner_element_activate(SpaceOutliner *soops, TreeStoreElem *tselem)
{
- outliner_flag_set(&soops->tree, TSE_ACTIVE, false);
- tselem->flag |= TSE_ACTIVE;
+ outliner_flag_set(&soops->tree, TSE_ACTIVE | TSE_ACTIVE_WALK, false);
+ tselem->flag |= TSE_ACTIVE | TSE_ACTIVE_WALK;
}
/**
@@ -1232,20 +1232,6 @@ void outliner_item_select(SpaceOutliner *soops,
tselem->flag = new_flag;
}
-static void outliner_item_toggle_closed(TreeElement *te, const bool toggle_children)
-{
- TreeStoreElem *tselem = TREESTORE(te);
- if (toggle_children) {
- tselem->flag &= ~TSE_CLOSED;
-
- const bool all_opened = !outliner_flag_is_any_test(&te->subtree, TSE_CLOSED, 1);
- outliner_flag_set(&te->subtree, TSE_CLOSED, all_opened);
- }
- else {
- tselem->flag ^= TSE_CLOSED;
- }
-}
-
static bool outliner_is_co_within_restrict_columns(const SpaceOutliner *soops,
const ARegion *ar,
float view_co_x)
@@ -1254,7 +1240,7 @@ static bool outliner_is_co_within_restrict_columns(const SpaceOutliner *soops,
}
/**
- * A version of #outliner_item_do_acticate_from_cursor that takes the tree element directly.
+ * A version of #outliner_item_do_activate_from_cursor that takes the tree element directly.
* and doesn't depend on the pointer position.
*
* This allows us to simulate clicking on an item without dealing with the mouse cursor.
@@ -1299,10 +1285,10 @@ static int outliner_item_do_activate_from_cursor(bContext *C,
changed = true;
}
}
- else if (outliner_item_is_co_within_close_toggle(te, view_mval[0])) {
- outliner_item_toggle_closed(te, extend);
- changed = true;
- rebuild_tree = true;
+ /* Don't allow selection on disclosure triangles */
+ else if ((TREESTORE(te)->type != TSE_VIEW_COLLECTION_BASE) &&
+ outliner_item_is_co_within_close_toggle(te, view_mval[0])) {
+ return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
}
else {
Scene *scene = CTX_data_scene(C);
@@ -1454,3 +1440,229 @@ void OUTLINER_OT_select_box(wmOperatorType *ot)
}
/* ****************************************************** */
+
+/* **************** Walk Select Tool ****************** */
+
+/* Given a tree element return the rightmost child that is visible in the outliner */
+static TreeElement *outliner_find_rightmost_visible_child(SpaceOutliner *soops, TreeElement *te)
+{
+ while (te->subtree.last) {
+ if (TSELEM_OPEN(TREESTORE(te), soops)) {
+ te = te->subtree.last;
+ }
+ else {
+ break;
+ }
+ }
+ return te;
+}
+
+/* Find previous visible element in the tree */
+static TreeElement *outliner_find_previous_element(SpaceOutliner *soops, TreeElement *walk_element)
+{
+ if (walk_element->prev) {
+ walk_element = outliner_find_rightmost_visible_child(soops, walk_element->prev);
+ }
+ else if (walk_element->parent) {
+ /* Use parent if at beginning of list */
+ walk_element = walk_element->parent;
+ }
+
+ return walk_element;
+}
+
+/* Recursively search up the tree until a successor to a given element is found */
+static TreeElement *outliner_element_find_successor_in_parents(TreeElement *te)
+{
+ TreeElement *successor = te;
+ while (successor->parent) {
+ if (successor->parent->next) {
+ te = successor->parent->next;
+ break;
+ }
+ else {
+ successor = successor->parent;
+ }
+ }
+
+ return te;
+}
+
+/* Find next visible element in the tree */
+static TreeElement *outliner_find_next_element(SpaceOutliner *soops, TreeElement *walk_element)
+{
+ TreeStoreElem *tselem = TREESTORE(walk_element);
+
+ if (TSELEM_OPEN(tselem, soops) && walk_element->subtree.first) {
+ walk_element = walk_element->subtree.first;
+ }
+ else if (walk_element->next) {
+ walk_element = walk_element->next;
+ }
+ else {
+ walk_element = outliner_element_find_successor_in_parents(walk_element);
+ }
+
+ return walk_element;
+}
+
+static TreeElement *do_outliner_select_walk(SpaceOutliner *soops,
+ TreeElement *walk_element,
+ const int direction,
+ const bool extend,
+ const bool toggle_all)
+{
+ TreeStoreElem *tselem = TREESTORE(walk_element);
+
+ if (!extend) {
+ outliner_flag_set(&soops->tree, TSE_SELECTED, false);
+ }
+ tselem->flag &= ~TSE_ACTIVE_WALK;
+
+ switch (direction) {
+ case OUTLINER_SELECT_WALK_UP:
+ walk_element = outliner_find_previous_element(soops, walk_element);
+ break;
+ case OUTLINER_SELECT_WALK_DOWN:
+ walk_element = outliner_find_next_element(soops, walk_element);
+ break;
+ case OUTLINER_SELECT_WALK_LEFT:
+ outliner_item_openclose(walk_element, false, toggle_all);
+ break;
+ case OUTLINER_SELECT_WALK_RIGHT:
+ outliner_item_openclose(walk_element, true, toggle_all);
+ break;
+ }
+
+ TreeStoreElem *tselem_new = TREESTORE(walk_element);
+
+ /* If new element is already selected, deselect the previous element */
+ if (extend) {
+ tselem->flag = (tselem_new->flag & TSE_SELECTED) ? (tselem->flag & ~TSE_SELECTED) :
+ (tselem->flag | TSE_SELECTED);
+ }
+
+ tselem_new->flag |= TSE_SELECTED | TSE_ACTIVE_WALK;
+
+ return walk_element;
+}
+
+/* Find walk select element, or set it if it does not exist.
+ * Changed is set to true if walk element is found, false if it was set */
+static TreeElement *find_walk_select_start_element(SpaceOutliner *soops, bool *changed)
+{
+ TreeElement *walk_element = outliner_find_element_with_flag(&soops->tree, TSE_ACTIVE_WALK);
+
+ *changed = false;
+
+ /* If no walk element exists, start from active */
+ if (!walk_element) {
+ TreeElement *active_element = outliner_find_element_with_flag(&soops->tree, TSE_ACTIVE);
+
+ /* If no active element exists, use the first element in the tree */
+ if (!active_element) {
+ walk_element = soops->tree.first;
+ }
+ else {
+ walk_element = active_element;
+ }
+
+ *changed = true;
+ }
+
+ /* If walk element is not visible, set that element's first visible parent as walk element */
+ if (!outliner_is_element_visible(walk_element)) {
+ TREESTORE(walk_element)->flag &= ~TSE_ACTIVE_WALK;
+
+ while (!outliner_is_element_visible(walk_element)) {
+ walk_element = walk_element->parent;
+ }
+ *changed = true;
+ }
+
+ return walk_element;
+}
+
+/* Scroll the outliner when the walk element reaches the top or bottom boundary */
+static void outliner_walk_scroll(ARegion *ar, TreeElement *te)
+{
+ /* Account for the header height */
+ int y_max = ar->v2d.cur.ymax - UI_UNIT_Y;
+ int y_min = ar->v2d.cur.ymin;
+
+ /* Scroll if walked position is beyond the border */
+ if (te->ys > y_max) {
+ outliner_scroll_view(ar, te->ys - y_max);
+ }
+ else if (te->ys < y_min) {
+ outliner_scroll_view(ar, -(y_min - te->ys));
+ }
+}
+
+static int outliner_walk_select_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ SpaceOutliner *soops = CTX_wm_space_outliner(C);
+ ARegion *ar = CTX_wm_region(C);
+
+ const short direction = RNA_enum_get(op->ptr, "direction");
+ const bool extend = RNA_boolean_get(op->ptr, "extend");
+ const bool toggle_all = RNA_boolean_get(op->ptr, "toggle_all");
+
+ bool changed;
+ TreeElement *walk_element = find_walk_select_start_element(soops, &changed);
+
+ /* If finding the starting walk select element did not move the element, proceed to walk */
+ if (!changed) {
+ walk_element = do_outliner_select_walk(soops, walk_element, direction, extend, toggle_all);
+ }
+ else {
+ TREESTORE(walk_element)->flag |= TSE_SELECTED | TSE_ACTIVE_WALK;
+ }
+
+ /* Scroll outliner to focus on walk element */
+ outliner_walk_scroll(ar, walk_element);
+
+ if (soops->flag & SO_SYNC_SELECT) {
+ ED_outliner_select_sync_from_outliner(C, soops);
+ }
+ ED_region_tag_redraw(ar);
+
+ return OPERATOR_FINISHED;
+}
+
+void OUTLINER_OT_select_walk(wmOperatorType *ot)
+{
+ static const EnumPropertyItem direction_items[] = {
+ {OUTLINER_SELECT_WALK_UP, "UP", 0, "Up", ""},
+ {OUTLINER_SELECT_WALK_DOWN, "DOWN", 0, "Down", ""},
+ {OUTLINER_SELECT_WALK_LEFT, "LEFT", 0, "Left", ""},
+ {OUTLINER_SELECT_WALK_RIGHT, "RIGHT", 0, "Right", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ /* identifiers */
+ ot->name = "Walk Select";
+ ot->idname = "OUTLINER_OT_select_walk";
+ ot->description = "Use walk navigation to select tree elements";
+
+ /* api callbacks */
+ ot->invoke = outliner_walk_select_invoke;
+ ot->poll = ED_operator_outliner_active;
+
+ /* properties */
+ PropertyRNA *prop;
+ prop = RNA_def_enum(ot->srna,
+ "direction",
+ direction_items,
+ 0,
+ "Walk Direction",
+ "Select element in this direction");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend selection on walk");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_boolean(
+ ot->srna, "toggle_all", false, "Toggle All", "Toggle open/close hierarchy");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+}
+
+/* ****************************************************** */
diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c
index 7107b5539c8..605b5383aec 100644
--- a/source/blender/editors/space_outliner/outliner_tree.c
+++ b/source/blender/editors/space_outliner/outliner_tree.c
@@ -297,7 +297,7 @@ static void outliner_add_scene_contents(SpaceOutliner *soops,
ViewLayer *view_layer;
for (view_layer = sce->view_layers.first; view_layer; view_layer = view_layer->next) {
- TreeElement *tenlay = outliner_add_element(soops, &ten->subtree, sce, te, TSE_R_LAYER, 0);
+ TreeElement *tenlay = outliner_add_element(soops, &ten->subtree, sce, ten, TSE_R_LAYER, 0);
tenlay->name = view_layer->name;
tenlay->directdata = view_layer;
}
@@ -314,7 +314,7 @@ static void outliner_add_scene_contents(SpaceOutliner *soops,
ten = outliner_add_element(soops, lb, sce, te, TSE_SCENE_OBJECTS_BASE, 0);
ten->name = IFACE_("Objects");
FOREACH_SCENE_OBJECT_BEGIN (sce, ob) {
- outliner_add_element(soops, &ten->subtree, ob, NULL, 0, 0);
+ outliner_add_element(soops, &ten->subtree, ob, ten, 0, 0);
}
FOREACH_SCENE_OBJECT_END;
outliner_make_object_parent_hierarchy(&ten->subtree);
diff --git a/source/blender/editors/space_outliner/outliner_utils.c b/source/blender/editors/space_outliner/outliner_utils.c
index d39a9387a6a..5dfdf6f129b 100644
--- a/source/blender/editors/space_outliner/outliner_utils.c
+++ b/source/blender/editors/space_outliner/outliner_utils.c
@@ -327,12 +327,68 @@ float outliner_restrict_columns_width(const SpaceOutliner *soops)
return (num_columns * UI_UNIT_X + V2D_SCROLL_WIDTH);
}
+/* Find first tree element in tree with matching treestore flag */
+TreeElement *outliner_find_element_with_flag(const ListBase *lb, short flag)
+{
+ for (TreeElement *te = lb->first; te; te = te->next) {
+ if ((TREESTORE(te)->flag & flag) == flag) {
+ return te;
+ }
+ TreeElement *active_element = outliner_find_element_with_flag(&te->subtree, flag);
+ if (active_element) {
+ return active_element;
+ }
+ }
+ return NULL;
+}
+
+/* Find if element is visible in the outliner tree */
+bool outliner_is_element_visible(const TreeElement *te)
+{
+ TreeStoreElem *tselem;
+
+ while (te->parent) {
+ tselem = TREESTORE(te->parent);
+
+ if (tselem->flag & TSE_CLOSED) {
+ return false;
+ }
+ else {
+ te = te->parent;
+ }
+ }
+
+ return true;
+}
+
/* 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);
}
+/* Scroll view vertically while keeping within total bounds */
+void outliner_scroll_view(ARegion *ar, int delta_y)
+{
+ int y_min = MIN2(ar->v2d.cur.ymin, ar->v2d.tot.ymin);
+
+ ar->v2d.cur.ymax += delta_y;
+ ar->v2d.cur.ymin += delta_y;
+
+ /* Adjust view if delta placed view outside total area */
+ int offset;
+ if (ar->v2d.cur.ymax > -UI_UNIT_Y) {
+ offset = ar->v2d.cur.ymax;
+ ar->v2d.cur.ymax -= offset;
+ ar->v2d.cur.ymin -= offset;
+ }
+ else if (ar->v2d.cur.ymin < y_min) {
+ offset = y_min - ar->v2d.cur.ymin;
+ ar->v2d.cur.ymax += offset;
+ ar->v2d.cur.ymin += offset;
+ }
+}
+
/* Get base of object under cursor. Used for eyedropper tool */
Base *ED_outliner_give_base_under_cursor(bContext *C, const int mval[2])
{
diff --git a/source/blender/editors/space_outliner/space_outliner.c b/source/blender/editors/space_outliner/space_outliner.c
index 2455d953053..3ac09b8bcf8 100644
--- a/source/blender/editors/space_outliner/space_outliner.c
+++ b/source/blender/editors/space_outliner/space_outliner.c
@@ -415,7 +415,7 @@ void ED_spacetype_outliner(void)
/* regions: main window */
art = MEM_callocN(sizeof(ARegionType), "spacetype outliner region");
art->regionid = RGN_TYPE_WINDOW;
- art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES;
+ art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D;
art->init = outliner_main_region_init;
art->draw = outliner_main_region_draw;
@@ -428,7 +428,7 @@ void ED_spacetype_outliner(void)
art = MEM_callocN(sizeof(ARegionType), "spacetype outliner header region");
art->regionid = RGN_TYPE_HEADER;
art->prefsizey = HEADERY;
- art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | ED_KEYMAP_HEADER;
+ art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_HEADER;
art->init = outliner_header_region_init;
art->draw = outliner_header_region_draw;
diff --git a/source/blender/makesdna/DNA_outliner_types.h b/source/blender/makesdna/DNA_outliner_types.h
index 6e830987725..9776063f220 100644
--- a/source/blender/makesdna/DNA_outliner_types.h
+++ b/source/blender/makesdna/DNA_outliner_types.h
@@ -62,6 +62,8 @@ enum {
TSE_DRAG_AFTER = (1 << 8),
/* Needed because outliner-only elements can be active */
TSE_ACTIVE = (1 << 9),
+ /* Needed because walk selection should not activate */
+ TSE_ACTIVE_WALK = (1 << 10),
TSE_DRAG_ANY = (TSE_DRAG_INTO | TSE_DRAG_BEFORE | TSE_DRAG_AFTER),
};