Undo/Redo in MacRuby


As I’m working on my upcoming O’Reilly MacRuby book, I’m writing quite a lot of example code. I have spent the last few weeks digging through most of the Foundation framework classes to hopefully make Cocoa more accessible to Ruby developers.

In some instances things might look quite weird to someone new to Cocoa in some cases, things seem almost too easy. Here is an example implementing a undo/redo functionality using Foundations’ NSUndoManager.

framework 'Foundation'
class Player
  attr_accessor :x, :y
 
  def initialize
    @x = @y = 0
  end
 
  def undo_manager
    @manager ||= NSUndoManager.alloc.init
  end
 
  def left
    undo_manager.prepareWithInvocationTarget(self).right
    @x -= 1
  end
 
  def right
    undo_manager.prepareWithInvocationTarget(self).left
    @x += 1
  end
end

Which you can use as such:

 
>> lara = Player.new
=> <Player:0x200267c80 @y=0 @x=0>
>> lara.undo_manager.canUndo
=> false # normal since we did not do anything yet
>> lara.left
=> -1
>> lara.x # -1
=> -1
>> lara.undo_manager.canUndo
=> true # now we can undo, so let's try
>> lara.undo_manager.undo # undo back to initial position
=> #<NSUndoManager:0x200257560>
>> lara.x
=> 0
>> lara.undo_manager.canUndo
=> false # we can't anymore which makes sense 
>> lara.undo_manager.canRedo
=> true # however we can redo what we just undone
>> lara.undo_manager.redo # redo to before we called undo
=> #<NSUndoManager:0x200257560>
>> lara.x
=> -1

The above example was tested in macirb but as you can see, actions can be undone and redone very very easily. This is just a quick preview of what you can do using Ruby + Cocoa and hopefully it will give you some cool ideas to implement.


Similar Posts

, , , ,

  1. #1 by Ryan Bates - February 2nd, 2010 at 10:49

    Looks really great! Have you considered how blocks can play a part in undo/redo as well? I was thinking something like:

    undo_manager.push { right }

    Maybe for HotCocoa. :)

    • #2 by Matt Aimonetti - February 2nd, 2010 at 10:57

      That’s a good idea Ryan, someone else on Twitter was also suggesting creating a Undoable module. Maybe merging both ideas and offering that as a module in HotCocoa (or as a separate lib) would be really nice.

      But to be honest, right now I’m just documenting the Cocoa API for my book and unfortunately, I don’t really have time to think about all the cool stuff I could do with this cool technology :(

      Any volunteers?

  2. #4 by Daniel Lopes - February 2nd, 2010 at 12:33

    NSUndoManager is really cool part of Foundation Framework.

  3. #5 by ecin - February 4th, 2010 at 18:48

    Could you point me in the direction of the twitter fellow that suggested Undoable? I want to know if s/he has already started on it.

    • #6 by Matt Aimonetti - February 4th, 2010 at 20:02

      Here is the tweet in question: http://twitter.com/mathie/status/8553033611 I don’t think he started implementing his module, shouldn’t be hard tho.

      • #7 by ecin - February 7th, 2010 at 11:08

        Well, gave it a short shot, but all I’m getting are segfaults and type-errors.

        http://pastie.org/813681

        I’ll have to come back to that later on.

      • #8 by ecin - February 7th, 2010 at 11:40

        Whoops, actually got it working by yielding the proxy object that [NSUndoManager prepareWithInvocationTarget] returns.

        http://pastie.org/813714

        Now to find better names for these methods.

  4. #9 by victor - February 15th, 2010 at 02:06

    You writing a book with ORA? Cool! Let us know when the drafts are available as a Rough Cut (if you go that way)

  1. No trackbacks yet.

Comments are closed.