Alright, it works Thanks @barmac, your help was on point
It took me a long time to figure it out and a lot of trial & error but I’ve managed to apply your first solution, the one with CustomModeling. The results were a bit surprising but work. Here’s my code for anyone who wants to do this, with a few comments to explain the results :
in a CustomModeling.js file :
import inherits from 'inherits'
import Modeling from 'bpmn-js/lib/features/modeling/Modeling.js'
import CommandStack from 'diagram-js/lib/command/CommandStack.js'
export var CustomCommandStackModule = {
__init__: [ 'customCommandStack' ],
customCommandStack: [ 'type', CommandStack ]
}
function CustomModeling(eventBus, elementFactory, commandStack, bpmnRules) {
Modeling.call(this, eventBus, elementFactory, commandStack, bpmnRules)
}
inherits(CustomModeling, Modeling)
Modeling.$inject = [
'eventBus',
'elementFactory',
'customCommandStack',
'bpmnRules'
]
export default {
__init__: [ 'customModeling' ],
customModeling: [ 'type', CustomModeling ]
}
in my code using modeler :
import BpmnModeler from 'bpmn-js/lib/Modeler'
import CustomModelingModule, { CustomCommandStackModule } from '...some/path/CustomModeling.js'
var modeler = new BpmnModeler({
additionalModules: [
CustomModelingModule,
CustomCommandStackModule
]
})
// And that's it. Now for using it properly :
var modeling = modeler.get('modeling') // for user actions
var customCommandStack = modeler.get('customCommandStack') // user's command stack (for Ctrl+Z, Y)
var customModeling = modeler.get('customModeling') // for programmed actions
var commandStack = modeler.get('commandStack') // programmed actions command stack
// To my surprise, any action performed on modeling will get pushed to customCommandStack,
// and vice versa.
// --------- example :
customModeling.setColor(someshapes, {
fill: 'orange',
stroke: 'red'
})
// I have a listener bound to my modeler canvas for undo/redo :
onKeyDown = (evt) => {
if (evt.ctrlKey) {
if (evt.keyCode === 90) // Ctrl + Z
customCommandStack.undo()
if (evt.keyCode === 89) // Ctrl + Y
customCommandStack.redo()
}
}
This seems to work for me so far One thing I should have done was rename the instances so that it made more sense, but I guess everyone will have their own vision about it.
Another thing just for the sake of the topic : I have to mention that the second solution you told me about kind of works, but not really. In my OP, I mentioned that I had already tried that :
- Popping commandStack._stack manually every time I perform a non-user action in the code. [This] breaks the undo/redo chain. That’s because the action is pushed to the stack and then deleted, instead of not being added to it at all.
If you pop the stack while your stack index is at the top of the stack (i.e you currently have nothing to redo in your command stack) it works fine. However if you have previously undone a few actions before popping the stack, then at best you lose the actions you had stored for redo. At worst, I’m not sure but I think you may just break it.
If anyone still prefers that admittedly lighter solution, then there’s one last problem, you can’t simply do “commandStack._stack.pop()” and call it a day. The following code does it correctly for any action, I have written it during my experimentations so here you go, in case you’re in a rush :
let lastCommand = commandStack._stack[commandStack._stack.length - 1]
if (lastCommand && lastCommand.id) {
let lastOperations = commandStack._stack.filter(com => com.id === lastCommand.id)
let numberOps = lastOperations.length
let firstIndexInStack = commandStack._stack.indexOf(firstOf(lastOperations))
commandStack._stack.splice(firstIndexInStack, numberOps)
commandStack._stackIdx -= numberOps
}
tl;dr don’t do it unless you don’t care about Ctrl+Y.