1 /* 2 Copyright © 2020, Inochi2D Project 3 Distributed under the 2-Clause BSD License, see LICENSE file. 4 5 Authors: Luna Nielsen 6 */ 7 module creator.core.actionstack; 8 import creator.core.settings; 9 import creator.actions; 10 import inochi2d; 11 12 private { 13 Action[] actions; 14 size_t actionPointer; 15 size_t actionIndex; 16 size_t maxUndoHistory; 17 } 18 19 /** 20 Initialize actions system 21 */ 22 void incActionInit() { 23 maxUndoHistory = incSettingsGet!size_t("MaxUndoHistory", 100); 24 } 25 26 /** 27 Pushes a new action to the stack 28 */ 29 void incActionPush(Action action) { 30 31 // Chop away entries outside undo history 32 if (actionPointer+1 > incActionGetUndoHistoryLength()) { 33 size_t toChop = (actionPointer+1)-incActionGetUndoHistoryLength(); 34 actions = actions[toChop..$]; 35 actionPointer -= toChop; 36 } 37 38 if (incActionTop() !is null && incActionTop().canMerge(action)) { 39 incActionTop().merge(action); 40 incActionNotifyTopChanged(); 41 } else { 42 // Add to the history 43 actions = actions[0..actionPointer]~action; 44 actionPointer++; 45 } 46 } 47 48 /** 49 Steps back in the action stack 50 */ 51 void incActionUndo() { 52 actionPointer--; 53 if (cast(ptrdiff_t)actionPointer < 0) { 54 actionPointer = 0; 55 return; 56 } 57 actions[actionPointer].rollback(); 58 } 59 60 /** 61 Steps forward in the action stack 62 */ 63 void incActionRedo() { 64 if (actionPointer >= actions.length) { 65 actionPointer = actions.length; 66 return; 67 } 68 actions[actionPointer].redo(); 69 actionPointer++; 70 } 71 72 /** 73 Gets whether undo is possible 74 */ 75 bool incActionCanUndo() { 76 return actionPointer > 0; 77 } 78 79 /** 80 Gets whether redo is possible 81 */ 82 bool incActionCanRedo() { 83 return actionPointer < actions.length; 84 } 85 86 /** 87 Gets the action history 88 */ 89 Action[] incActionHistory() { 90 return actions; 91 } 92 93 /** 94 Index of the current action 95 */ 96 size_t incActionIndex() { 97 return actionPointer; 98 } 99 100 /** 101 Gets the "top" action 102 */ 103 Action incActionTop() { 104 return actionPointer > 0 && actionPointer <= actions.length ? actions[actionPointer-1] : null; 105 } 106 107 /** 108 Notify that the top action has changed 109 */ 110 void incActionNotifyTopChanged() { 111 actions.length = actionPointer; 112 } 113 114 /** 115 Sets max undo history length 116 */ 117 void incActionSetUndoHistoryLength(size_t length) { 118 length = clamp(length, 0, 1000); 119 maxUndoHistory = length; 120 incSettingsSet("MaxUndoHistory", maxUndoHistory); 121 } 122 123 /** 124 Gets max undo history 125 */ 126 size_t incActionGetUndoHistoryLength() { 127 return maxUndoHistory; 128 } 129 130 /** 131 Sets the action index 132 Indexes start at 1, 0 is reserved for the INTIAL index 133 */ 134 void incActionSetIndex(size_t index) { 135 if (index > actions.length) { 136 index = actions.length; 137 } 138 139 if (index == 0) { 140 141 // Undo till we can't anymore 142 while (incActionCanUndo()) incActionUndo(); 143 } 144 145 if (index < actionPointer) { 146 while (index < actionPointer) incActionUndo(); 147 } else if (cast(ptrdiff_t)index > cast(ptrdiff_t)actionPointer) { 148 while (cast(ptrdiff_t)index > cast(ptrdiff_t)actionPointer) incActionRedo(); 149 } 150 } 151 152 /** 153 Clears action history 154 */ 155 void incActionClearHistory() { 156 actions.length = 0; 157 actionPointer = 0; 158 }