Page MenuHome

Multi-Object Mode User Interface
Closed, ArchivedPublic

Description

In Blender 2.8, we support having multiple objects in Edit Mode, Pose Mode and Sculpt Mode.

In order for this to work well, we want to make it more clear which objects are in the mode session, and also make it easy to both add & remove items from the current session.

We want to solve this as follows:

When in either Edit Mode, Sculpt Mode or Pose Mode, the objects in the mode stand out, like so:

In the Outliner, we will add an additional column with an icon corresponding to the active mode. When you click it, you can toggle that item in and out of the current mode:

Also, we make the items in the mode prominently stand out with a differently colored row.

In object mode this column wont be displayed.

Details

Type
Design

Event Timeline

William Reynish (billreynish) triaged this task as Normal priority.

Not sure color should be used here since the outliner has it's own selection already.

Further, there are already multiple states being communicated here:

  • Outliner selection.
  • Object Selection.
  • Active object.

Having icons which are clearly activated in the right column could be enough.

Or we could dim all text for objects not sharing in the current mode.

Testing this as a patch, and found some issues.

The extra column feels much more heavy than expected (even though the column is small, there is already not much room in the outliner)

This looks more exaggerated by there being no use for the 3 columns to the right which are empty space.

Default layout:

One option could be to re-use the existing columns.


Talked this over with @venomgfx and he thinks this doesn't need its own column (useful but not something you do all the time).

So we've agreed to add this to the right click menu, for eg:

  • Add to Edit Mode
  • Remove from Edit Mode

Users can still toggle individual objects using the icons as before.

This doesn't prevent having a dedicated column for this in the future as the design task proposes.

Patch on 91c0f17a475793de7bc6016598ef13221392dbf5

diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c
index e4f551b0004..b23ba83408c 100644
--- a/source/blender/editors/space_outliner/outliner_draw.c
+++ b/source/blender/editors/space_outliner/outliner_draw.c
@@ -264,6 +264,24 @@ static void restrictbutton_gp_layer_flag_cb(bContext *C, void *UNUSED(poin), voi
 	WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
 }
 
+static void restrictbutton_object_mode_cb(bContext *C, void *poin, void *poin2)
+{
+	eObjectMode object_mode_toggle = (eObjectMode)(intptr_t)poin;
+	Object *ob = poin2;
+	/* WEAK, restore original value! Switch is an operator. */
+	ob->mode = object_mode_toggle;
+
+	printf("Object: %s\n", ob->id.name);
+	ViewLayer *view_layer = CTX_data_view_layer(C);
+	Base *base = BKE_view_layer_base_find(view_layer, ob);
+	if (base == NULL) {
+		return;
+	}
+
+	Scene *scene = CTX_data_scene(C);
+	outliner_object_mode_toggle(C, scene, view_layer, base);
+}
+
 static void restrictbutton_id_user_toggle(bContext *UNUSED(C), void *poin, void *UNUSED(poin2))
 {
 	ID *id = (ID *)poin;
@@ -435,7 +453,8 @@ static void namebutton_cb(bContext *C, void *tsep, char *oldname)
 }
 
 static void outliner_draw_restrictbuts(
-        uiBlock *block, Scene *scene, ARegion *ar, SpaceOops *soops, ListBase *lb)
+        uiBlock *block, Scene *scene, const Object *obact, const eObjectMode object_mode_toggle,
+        ARegion *ar, SpaceOops *soops, ListBase *lb)
 {
 	uiBut *bt;
 
@@ -544,6 +563,20 @@ static void outliner_draw_restrictbuts(
 
 				/* TODO: visibility in renders */
 			}
+			else if ((tselem->type == 0) && (te->idcode == ID_OB)) {
+				if (object_mode_toggle != OB_MODE_OBJECT) {
+					Object *ob = (Object *)tselem->id;
+					if (ob->type == obact->type) {
+						bt = uiDefIconButBitI(
+						        block, UI_BTYPE_ICON_TOGGLE, object_mode_toggle, 0, ICON_CHECKBOX_DEHLT,
+						        (int)(ar->v2d.cur.xmax - OL_TOG_OBJECT_MODE), te->ys, UI_UNIT_X,
+						        UI_UNIT_Y, &ob->mode, 0, 0, 0, 0,
+						        TIP_("Restrict/Allow editing of strokes and keyframes in this layer"));
+						UI_but_func_set(bt, restrictbutton_object_mode_cb, (void *)(intptr_t)ob->mode, ob);
+						UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
+					}
+				}
+			}
 			else if (outliner_is_collection_tree_element(te)) {
 				LayerCollection *lc = (tselem->type == TSE_LAYER_COLLECTION) ? te->directdata : NULL;
 				Collection *collection = outliner_collection_from_tree_element(te);
@@ -576,7 +609,7 @@ static void outliner_draw_restrictbuts(
 		}
 
 		if (TSELEM_OPEN(tselem, soops)) {
-			outliner_draw_restrictbuts(block, scene, ar, soops, &te->subtree);
+			outliner_draw_restrictbuts(block, scene, obact, object_mode_toggle, ar, soops, &te->subtree);
 		}
 	}
 }
@@ -1893,7 +1926,7 @@ static void outliner_draw_restrictcols(ARegion *ar)
 	unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_I32, 2, GWN_FETCH_INT_TO_FLOAT);
 	immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
 	immUniformThemeColorShadeAlpha(TH_BACK, -15, -200);
-	immBegin(GWN_PRIM_LINES, 6);
+	immBegin(GWN_PRIM_LINES, 8);
 
 	immVertex2i(pos, (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), (int)ar->v2d.cur.ymax);
 	immVertex2i(pos, (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), (int)ar->v2d.cur.ymin);
@@ -1904,6 +1937,9 @@ static void outliner_draw_restrictcols(ARegion *ar)
 	immVertex2i(pos, (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_RENDERX), (int)ar->v2d.cur.ymax);
 	immVertex2i(pos, (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_RENDERX), (int)ar->v2d.cur.ymin);
 
+	immVertex2i(pos, (int)(ar->v2d.cur.xmax - OL_TOG_OBJECT_MODE), (int)ar->v2d.cur.ymax);
+	immVertex2i(pos, (int)(ar->v2d.cur.xmax - OL_TOG_OBJECT_MODE), (int)ar->v2d.cur.ymin);
+
 	immEnd();
 	immUnbindProgram();
 }
@@ -1996,10 +2032,11 @@ void draw_outliner(const bContext *C)
 		outliner_draw_userbuts(block, ar, soops, &soops->tree);
 	}
 	else if (has_restrict_icons) {
+		const Object *obact = OBACT(view_layer);
+		const eObjectMode object_mode_toggle = obact ? (obact->mode & OB_MODE_ALL_MULTI) : OB_MODE_OBJECT;
 		/* draw restriction columns */
 		outliner_draw_restrictcols(ar);
-
-		outliner_draw_restrictbuts(block, scene, ar, soops, &soops->tree);
+		outliner_draw_restrictbuts(block, scene, obact, object_mode_toggle, ar, soops, &soops->tree);
 	}
 
 	/* draw edit buttons if nessecery */
diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h
index 8ac09648d60..27ea5f14afa 100644
--- a/source/blender/editors/space_outliner/outliner_intern.h
+++ b/source/blender/editors/space_outliner/outliner_intern.h
@@ -151,11 +151,12 @@ typedef enum {
 /* size constants */
 #define OL_Y_OFFSET 2
 
+#define OL_TOG_OBJECT_MODE      (UI_UNIT_X * 4.0f)
 #define OL_TOG_RESTRICT_SELECTX (UI_UNIT_X * 3.0f)
 #define OL_TOG_RESTRICT_VIEWX   (UI_UNIT_X * 2.0f)
 #define OL_TOG_RESTRICT_RENDERX UI_UNIT_X
 
-#define OL_TOGW OL_TOG_RESTRICT_SELECTX
+#define OL_TOGW OL_TOG_OBJECT_MODE
 
 #define OL_RNA_COLX         (UI_UNIT_X * 15)
 #define OL_RNA_COL_SIZEX    (UI_UNIT_X * 7.5f)
@@ -226,6 +227,10 @@ void outliner_item_select(
         struct SpaceOops *soops, const struct TreeElement *te,
         const bool extend, const bool toggle);
 
+void outliner_object_mode_toggle(
+        struct bContext *C, Scene *scene, ViewLayer *view_layer,
+        Base *base);
+
 /* outliner_edit.c ---------------------------------------------- */
 typedef void (*outliner_operation_cb)(
         struct bContext *C, struct ReportList *, struct Scene *scene,
diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c
index 6a6a392e4a6..38ae0683d4b 100644
--- a/source/blender/editors/space_outliner/outliner_select.c
+++ b/source/blender/editors/space_outliner/outliner_select.c
@@ -155,6 +155,20 @@ static void do_outliner_activate_pose(bContext *C, ViewLayer *view_layer, Base *
 	}
 }
 
+/* For draw callback to run mode switching */
+void outliner_object_mode_toggle(
+        bContext *C, Scene *scene, ViewLayer *view_layer,
+        Base *base)
+{
+	Object *obact = OBACT(view_layer);
+	if (obact->mode & OB_MODE_EDIT) {
+		do_outliner_activate_obdata(C, scene, view_layer, base);
+	}
+	else if (obact->mode & OB_MODE_POSE) {
+		do_outliner_activate_pose(C, view_layer, base);
+	}
+}
+
 /* ****************************************************** */
 /* Outliner Element Selection/Activation on Click */
 
diff --git a/source/blender/makesdna/DNA_object_enums.h b/source/blender/makesdna/DNA_object_enums.h
index 802ca6c7d0d..4462c939032 100644
--- a/source/blender/makesdna/DNA_object_enums.h
+++ b/source/blender/makesdna/DNA_object_enums.h
@@ -50,4 +50,8 @@ typedef enum eObjectMode {
 #define OB_MODE_ALL_MODE_DATA \
 	(OB_MODE_EDIT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_SCULPT | OB_MODE_POSE)
 
+/* Any mode that supports multiple objects at once. */
+#define OB_MODE_ALL_MULTI \
+	(OB_MODE_EDIT | OB_MODE_POSE)
+
 #endif  /* __DNA_OBJECT_ENUMS_H__ */