How to make NSUndoManager(MBS) manage Undos

I have a layout engine in the making; a pure macOS project where I make heavy use of native stuff via declares. Trying to implement NSUndoManger now, I find myself only half successful. Someone here with more luck?

This is from the MouseDragged event handler where you can reposition things.
l As LayoutView is a subclass of NSView (Canvas).

l = LayoutView.FromPtr(LayoutView.SelectedControls.LastPtr)
Var frame As AppkitAddition.nsrect = l.Frame
Self.CSWindow.UndoManager.RegisterUndoWithTargetAndSelector l, FoundationModule.NSSelectorFromString("setFrameOrigin:"), FoundationModule.CSValue.FromPoint(frame.Origin)
Self.CSWindow.UndoManager.SetActionName "Position"

(code continues to reposition l depending on mouse deltas)
So far, that works and I can see a (localised) version of “Undo Position” in the app’s edit menu. However, when I try to undo, the view is repositioned but to 0,0 instead of the saved NSPoint. Shouldn’t points and other stuff be wrapped in NSValues? Do I have to create different Register methods for different datatypes? But NSUndoManager.RegisterUndoWithTarget:selector:object: says I need an id, not a common datatype, so I guess wrapping the data should be ok, or isn’t it?

FoundationModule.CSValue.FromPoint gives a NSValue object containing a point?

Then you can’t use setFrameOrigin: here since undo manager would call it with an object parameter instead of the NSPoint it needs.

Thanks, Christian. You mean I have to use a RegisterUndo… declare with a NSPoint value instead of wrapping it into an object? I thought so because of the id as parameter type.
EDIT: No, that doesn’t work. Looks like I’d have install methods on the subclass via objC that can be called with a wrapped data type, or use a block that does the same, I guess.

You need a method to be called by the undo manager, which takes a pointer to an object (NSValue*) and then unpacks the NSPoint inside and calls setFrameOrigin with it.

1 Like

Thanks a lot for clarifying! I had the idea the system would do that unwrapping automatically for me. Think I’ll try the block Register method, that should give enough flexibility.

Again, thanks a lot, Christian! Helped enormously to figure things out!

Block Register wasn’t suitable because it will not forward a parameter except for the target. I could now solve it this way – should be easy to translate to MBS:

Var manager As FoundationModule.CSUndoManager = FoundationModule.CSUndoManager.FromPtr( Self.CSWindow.UndoManager.Prepare(l))
AppkitAddition.setFrameOrigin, frame.Origin
Self.CSWindow.UndoManager.SetActionName "Position"

FromPtr is a factory that will return nil if fed with nil, otherwise return the respective instance.