/// <summary> /// Detects an UndoState change as either an undo or redo operation /// A positive return value indicates a redo operation, a negative an undo operation; /// If it is 0, then the adressed stack (undo/redo) was empty /// The absolute value is the number of records that were adressed, means the size of the group /// It is possible that the group size changed due to an anomaly, so count of records added by the anomaly is put into addedRecordsCount /// </summary> private static int DetectStateChange (UndoState prevState, UndoState nextState, out int addedRecordsCount) { addedRecordsCount = 0; int prevUndoCount = prevState.undoRecords.Count, prevRedoCount = prevState.redoRecords.Count; int nextUndoCount = nextState.undoRecords.Count, nextRedoCount = nextState.redoRecords.Count; int undoChange = nextUndoCount-prevUndoCount, redoChange = nextRedoCount-prevRedoCount; // Check if the action is undo or redo bool undoAction = undoChange < 0, redoAction = redoChange < 0; if ((!redoAction && prevUndoCount == 0) || (!undoAction && prevRedoCount == 0)) // Tried to undo/redo with an empty record stack return 0; if (!undoAction && !redoAction) throw new Exception ("Detected neither redo nor undo operation!"); int recordChange = undoAction? Math.Abs (undoChange) : Math.Abs (redoChange); #if UNDO_DEBUG Debug.Log ("Detected " + (undoAction? "UNDO" : "REDO") + " of " + recordChange + " initial records!"); #endif if (redoChange != -undoChange) { // This anomaly happens only for records that trigger other undo/redo operations // -> only known case: Reparent unselected object in hierarchy, each iteration (undo/redo) of the issued record a 'Parenting' record gets added ontop addedRecordsCount = undoAction? (Math.Abs (redoChange)-Math.Abs (undoChange)) : (Math.Abs (undoChange)-Math.Abs (redoChange)); #if UNDO_DEBUG Debug.LogWarning ("Due to an anomaly a difference of " + addedRecordsCount + " records was created during " + (undoAction? "undo" : "redo") + " where undo change was " + undoChange + " and redo change " + redoChange); #endif } return (undoAction? -recordChange : recordChange); // Return the count of initially changed records }
private static UndoState FetchUndoState() { UndoState newUndoState = new UndoState(); getRecordsInternalDelegate.Invoke(newUndoState.undoRecords, newUndoState.redoRecords); return(newUndoState); }
/// <summary> /// Callback recognising the type of record, calling the apropriate callback and handling undo pro records /// </summary> private static void UndoRedoPerformed() { lastFrameUndoRedoPerformed = true; AssureRecords(); // Get new UndoState UndoState prevState = records.undoState; UndoState newState = records.undoState = FetchUndoState(); // Detect undo/redo int addedRecordCount; int change = DetectStateChange(prevState, newState, out addedRecordCount); if (change == 0) // Nothing happend; Only possible if Undo/Redo stack was empty { return; } List <UndoProRecord> operatedRecords = records.PerformOperationInternal(change, addedRecordCount); if (change < 0) { // UNDO operation foreach (UndoProRecord undoRecord in operatedRecords) { // Invoke undo operations if (undoRecord.undo != null) { undoRecord.undo.Invoke(); } } // Callback for whole group if (OnUndoPerformed != null) { OnUndoPerformed.Invoke(operatedRecords.Select((UndoProRecord record) => record.label).ToArray()); } } else { // REDO operation foreach (UndoProRecord redoRecord in operatedRecords) { // Invoke redo operations if (redoRecord.perform != null) { redoRecord.perform.Invoke(); } } // Callback for whole group if (OnRedoPerformed != null) { OnRedoPerformed.Invoke(operatedRecords.Select((UndoProRecord record) => record.label).ToArray()); } } }
/// <summary> /// Check the current undoState for any added undo records and updates the internal records accordingly /// </summary> private static void UpdateUndoRecords () { AssureRecords (); // Get new UndoState UndoState prevState = records.undoState; records.undoState = FetchUndoState (); UndoState newState = records.undoState; // Detect additions to the record through comparision of the old and the new UndoState if (prevState.undoRecords.Count == newState.undoRecords.Count) return; // No undo record was added for sure // Fetch new undo records int addedUndoCount = newState.undoRecords.Count-prevState.undoRecords.Count; #if UNDO_DEBUG Debug.Log ("Checking for update... change detected! Added Undo's: " + addedUndoCount); #endif if (addedUndoCount < 0) { // This happens only when the undo was erased, for example after switching the scene if (newState.undoRecords.Count != 0) { records.ClearRedo (); Debug.LogWarning ("Cleared Redo because some undos were removed!"); } CreateRecords (); return; } // Update internals records.UndoRecordsAdded (addedUndoCount); // Callback string[] undosAdded = newState.undoRecords.GetRange (newState.undoRecords.Count-addedUndoCount, addedUndoCount).ToArray (); if (OnAddUndoRecord != null) OnAddUndoRecord.Invoke (undosAdded, newState.redoRecords.Count == 0); #if UNDO_DEBUG // Debug added undo records string undoLog = undosAdded.Length + " undo records added: "; for (int undoCnt = 0; undoCnt < undosAdded.Length; undoCnt++) undoLog += undosAdded[undoCnt] + "; "; Debug.Log (undoLog); #endif }