Fix T61272: Undo fails to track multi-edit mode enter/exit
authorCampbell Barton <ideasman42@gmail.com>
Thu, 7 Feb 2019 09:27:11 +0000 (20:27 +1100)
committerCampbell Barton <ideasman42@gmail.com>
Thu, 7 Feb 2019 09:28:33 +0000 (20:28 +1100)
Objects leaving edit-mode weren't restored by edit-mode undo steps.

source/blender/editors/armature/editarmature_undo.c
source/blender/editors/curve/editcurve_undo.c
source/blender/editors/include/ED_undo.h
source/blender/editors/lattice/editlattice_undo.c
source/blender/editors/mesh/editmesh_undo.c
source/blender/editors/metaball/editmball_undo.c
source/blender/editors/undo/ed_undo.c

index f88054f20c86e7a17a4cedd8f49acfbfaaf1f356..3e76a66e891fb3dffd2eca51cf986316a52da60d 100644 (file)
@@ -146,9 +146,11 @@ static bool armature_undosys_step_encode(struct bContext *C, struct Main *UNUSED
 {
        ArmatureUndoStep *us = (ArmatureUndoStep *)us_p;
 
+       /* Important not to use the 3D view when getting objects because all objects
+        * outside of this list will be moved out of edit-mode when reading back undo steps. */
        ViewLayer *view_layer = CTX_data_view_layer(C);
        uint objects_len = 0;
-       Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, CTX_wm_view3d(C), &objects_len);
+       Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, NULL, &objects_len);
 
        us->elems = MEM_callocN(sizeof(*us->elems) * objects_len, __func__);
        us->elems_len = objects_len;
@@ -168,12 +170,13 @@ static bool armature_undosys_step_encode(struct bContext *C, struct Main *UNUSED
 
 static void armature_undosys_step_decode(struct bContext *C, struct Main *UNUSED(bmain), UndoStep *us_p, int UNUSED(dir))
 {
-       /* TODO(campbell): undo_system: use low-level API to set mode. */
-       ED_object_mode_set(C, OB_MODE_EDIT);
-       BLI_assert(armature_undosys_poll(C));
-
        ArmatureUndoStep *us = (ArmatureUndoStep *)us_p;
 
+       /* Load all our objects  into edit-mode, clear everything else. */
+       ED_undo_object_editmode_restore_helper(C, &us->elems[0].obedit_ref.ptr, us->elems_len, sizeof(*us->elems));
+
+       BLI_assert(armature_undosys_poll(C));
+
        for (uint i = 0; i < us->elems_len; i++) {
                ArmatureUndoStep_Elem *elem = &us->elems[i];
                Object *obedit = elem->obedit_ref.ptr;
index a586e94a6f9c619cd54533cc54893e79f951f96b..e9586594274a71727c89a241699d505690b8c07e 100644 (file)
@@ -213,9 +213,11 @@ static bool curve_undosys_step_encode(struct bContext *C, struct Main *UNUSED(bm
 {
        CurveUndoStep *us = (CurveUndoStep *)us_p;
 
+       /* Important not to use the 3D view when getting objects because all objects
+        * outside of this list will be moved out of edit-mode when reading back undo steps. */
        ViewLayer *view_layer = CTX_data_view_layer(C);
        uint objects_len = 0;
-       Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, CTX_wm_view3d(C), &objects_len);
+       Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, NULL, &objects_len);
 
        us->elems = MEM_callocN(sizeof(*us->elems) * objects_len, __func__);
        us->elems_len = objects_len;
@@ -234,12 +236,14 @@ static bool curve_undosys_step_encode(struct bContext *C, struct Main *UNUSED(bm
 
 static void curve_undosys_step_decode(struct bContext *C, struct Main *UNUSED(bmain), UndoStep *us_p, int UNUSED(dir))
 {
-       /* TODO(campbell): undo_system: use low-level API to set mode. */
-       ED_object_mode_set(C, OB_MODE_EDIT);
-       BLI_assert(curve_undosys_poll(C));
-
        CurveUndoStep *us = (CurveUndoStep *)us_p;
 
+       /* Load all our objects  into edit-mode, clear everything else. */
+       ED_undo_object_editmode_restore_helper(
+               C, &us->elems[0].obedit_ref.ptr, us->elems_len, sizeof(*us->elems));
+
+       BLI_assert(curve_undosys_poll(C));
+
        for (uint i = 0; i < us->elems_len; i++) {
                CurveUndoStep_Elem *elem = &us->elems[i];
                Object *obedit = elem->obedit_ref.ptr;
index 3486761c19fa9ac7b5d86854ac8f8158ed12908b..3a45e2c3b687a81310eacb7127577b5440175aaa 100644 (file)
@@ -23,7 +23,9 @@
 #include "BLI_compiler_attrs.h"
 
 struct CLG_LogRef;
+struct Object;
 struct UndoStack;
+struct ViewLayer;
 struct bContext;
 struct wmOperator;
 struct wmOperatorType;
@@ -51,6 +53,10 @@ bool    ED_undo_is_valid(const struct bContext *C, const char *undoname);
 
 bool    ED_undo_is_memfile_compatible(const struct bContext *C);
 
+void    ED_undo_object_editmode_restore_helper(
+        struct bContext *C,
+        struct Object **object_array, uint object_array_len, uint object_array_stride);
+
 struct UndoStack *ED_undo_stack_get(void);
 
 /* helpers */
index c6c0ad542d1bcd15f808a56fc9ebe8d6e5580b73..8dddec3ed6daefdf7b8df8bee0ff1d5bf39f65a3 100644 (file)
@@ -148,9 +148,11 @@ static bool lattice_undosys_step_encode(struct bContext *C, struct Main *UNUSED(
 {
        LatticeUndoStep *us = (LatticeUndoStep *)us_p;
 
+       /* Important not to use the 3D view when getting objects because all objects
+        * outside of this list will be moved out of edit-mode when reading back undo steps. */
        ViewLayer *view_layer = CTX_data_view_layer(C);
        uint objects_len = 0;
-       Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, CTX_wm_view3d(C), &objects_len);
+       Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, NULL, &objects_len);
 
        us->elems = MEM_callocN(sizeof(*us->elems) * objects_len, __func__);
        us->elems_len = objects_len;
@@ -170,12 +172,13 @@ static bool lattice_undosys_step_encode(struct bContext *C, struct Main *UNUSED(
 
 static void lattice_undosys_step_decode(struct bContext *C, struct Main *UNUSED(bmain), UndoStep *us_p, int UNUSED(dir))
 {
-       /* TODO(campbell): undo_system: use low-level API to set mode. */
-       ED_object_mode_set(C, OB_MODE_EDIT);
-       BLI_assert(lattice_undosys_poll(C));
-
        LatticeUndoStep *us = (LatticeUndoStep *)us_p;
 
+       /* Load all our objects  into edit-mode, clear everything else. */
+       ED_undo_object_editmode_restore_helper(C, &us->elems[0].obedit_ref.ptr, us->elems_len, sizeof(*us->elems));
+
+       BLI_assert(lattice_undosys_poll(C));
+
        for (uint i = 0; i < us->elems_len; i++) {
                LatticeUndoStep_Elem *elem = &us->elems[i];
                Object *obedit = elem->obedit_ref.ptr;
index 7af7204deabaf0b04721829f0ad1640b5da30d38..5ed4395a740f666ab55c7dd61f07a5822fb19cf3 100644 (file)
@@ -698,9 +698,11 @@ static bool mesh_undosys_step_encode(struct bContext *C, struct Main *UNUSED(bma
 {
        MeshUndoStep *us = (MeshUndoStep *)us_p;
 
+       /* Important not to use the 3D view when getting objects because all objects
+        * outside of this list will be moved out of edit-mode when reading back undo steps. */
        ViewLayer *view_layer = CTX_data_view_layer(C);
        uint objects_len = 0;
-       Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, CTX_wm_view3d(C), &objects_len);
+       Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, NULL, &objects_len);
 
        us->elems = MEM_callocN(sizeof(*us->elems) * objects_len, __func__);
        us->elems_len = objects_len;
@@ -718,16 +720,12 @@ static bool mesh_undosys_step_encode(struct bContext *C, struct Main *UNUSED(bma
        return true;
 }
 
-static void mesh_undosys_step_decode(struct bContext *C, struct Main *bmain, UndoStep *us_p, int UNUSED(dir))
+static void mesh_undosys_step_decode(struct bContext *C, struct Main *UNUSED(bmain), UndoStep *us_p, int UNUSED(dir))
 {
        MeshUndoStep *us = (MeshUndoStep *)us_p;
 
-       Scene *scene = CTX_data_scene(C);
-       for (uint i = 0; i < us->elems_len; i++) {
-               MeshUndoStep_Elem *elem = &us->elems[i];
-               Object *obedit = elem->obedit_ref.ptr;
-               ED_object_editmode_enter_ex(bmain, scene, obedit, EM_NO_CONTEXT);
-       }
+       /* Load all our objects  into edit-mode, clear everything else. */
+       ED_undo_object_editmode_restore_helper(C, &us->elems[0].obedit_ref.ptr, us->elems_len, sizeof(*us->elems));
 
        BLI_assert(mesh_undosys_poll(C));
 
index c526edd7bd2925bd6bd57eb19bb94a19cad158ac..160b43b70659738656e09ab35e75c104df055e23 100644 (file)
@@ -156,9 +156,11 @@ static bool mball_undosys_step_encode(struct bContext *C, struct Main *UNUSED(bm
 {
        MBallUndoStep *us = (MBallUndoStep *)us_p;
 
+       /* Important not to use the 3D view when getting objects because all objects
+        * outside of this list will be moved out of edit-mode when reading back undo steps. */
        ViewLayer *view_layer = CTX_data_view_layer(C);
        uint objects_len = 0;
-       Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, CTX_wm_view3d(C), &objects_len);
+       Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, NULL, &objects_len);
 
        us->elems = MEM_callocN(sizeof(*us->elems) * objects_len, __func__);
        us->elems_len = objects_len;
@@ -178,12 +180,13 @@ static bool mball_undosys_step_encode(struct bContext *C, struct Main *UNUSED(bm
 
 static void mball_undosys_step_decode(struct bContext *C, struct Main *UNUSED(bmain), UndoStep *us_p, int UNUSED(dir))
 {
-       /* TODO(campbell): undo_system: use low-level API to set mode. */
-       ED_object_mode_set(C, OB_MODE_EDIT);
-       BLI_assert(mball_undosys_poll(C));
-
        MBallUndoStep *us = (MBallUndoStep *)us_p;
 
+       /* Load all our objects  into edit-mode, clear everything else. */
+       ED_undo_object_editmode_restore_helper(C, &us->elems[0].obedit_ref.ptr, us->elems_len, sizeof(*us->elems));
+
+       BLI_assert(mball_undosys_poll(C));
+
        for (uint i = 0; i < us->elems_len; i++) {
                MBallUndoStep_Elem *elem = &us->elems[i];
                Object *obedit = elem->obedit_ref.ptr;
index 69e02148ba5076b422092a470a7bb3c5877187c7..ffe752e11b18afdc84a230814011751128814d4e 100644 (file)
@@ -653,4 +653,37 @@ void ED_undo_object_set_active_or_warn(ViewLayer *view_layer, Object *ob, const
        }
 }
 
+void ED_undo_object_editmode_restore_helper(
+        struct bContext *C, Object **object_array, uint object_array_len, uint object_array_stride)
+{
+       Main *bmain = CTX_data_main(C);
+       ViewLayer *view_layer = CTX_data_view_layer(C);
+       uint bases_len = 0;
+       /* Don't request unique data because we wan't to de-select objects when exiting edit-mode
+        * for that to be done on all objects we can't skip ones that share data. */
+       Base **bases = BKE_view_layer_array_from_bases_in_edit_mode(
+               view_layer, NULL, &bases_len);
+       for (uint i = 0; i < bases_len; i++) {
+               ((ID *)bases[i]->object->data)->tag |= LIB_TAG_DOIT;
+       }
+       Scene *scene = CTX_data_scene(C);
+       Object **ob_p = object_array;
+       for (uint i = 0; i < object_array_len; i++, ob_p = POINTER_OFFSET(ob_p, object_array_stride)) {
+               Object *obedit = *ob_p;
+               ED_object_editmode_enter_ex(bmain, scene, obedit, EM_NO_CONTEXT);
+               ((ID *)obedit->data)->tag &= ~LIB_TAG_DOIT;
+       }
+       for (uint i = 0; i < bases_len; i++) {
+               ID *id = bases[i]->object->data;
+               if (id->tag & LIB_TAG_DOIT) {
+                       ED_object_editmode_exit_ex(bmain, scene, bases[i]->object, EM_FREEDATA);
+                       /* Ideally we would know the selection state it was before entering edit-mode,
+                        * for now follow the convention of having them unselected when exiting the mode. */
+                       ED_object_base_select(bases[i], BA_DESELECT);
+
+               }
+       }
+       MEM_freeN(bases);
+}
+
 /** \} */