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:
Diffstat (limited to 'source/blender/editors/space_outliner/outliner_select.c')
-rw-r--r--source/blender/editors/space_outliner/outliner_select.c254
1 files changed, 233 insertions, 21 deletions
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);
+}
+
+/* ****************************************************** */