Implementing Context-based Undo/Redo Functionality

I’m currently implementing an undo/redo mechanism using the Command Pattern. Everything works so far. My problem now is to implement the undo/redo functionality in a way that it is bound to a given context.

Assume the following situation:
You have win1 and win2. In win1 you execute action1; then you switch to win2 and you execute action2 and action3. You “undo-stack” would look as follows

  • action3
  • action2
  • action1

Now what happens if win1 gets closed/removed and you start performing undos. At action1 the program would probably crash as the context is has been executed against previously (i.e. win1) doesn’t exist any more. As such, when win1 gets closed, all the corresponding actions should be removed from the “undo-stack”.

My question is on whether there exist already implementations/best practices for implementing such scenario properly? Or do you have any ideas?

What happens if win1 gets closed/removed? To answer this question, ask you another one: is the action reversible?

1. Case where the closing/removal action is reversible:

The closing/removal event is recorded in the undo stack, and when the user is performing undos, it un-does the closing/removal.

For example, when a user removes, through your application, a file win1, the app should not remove the file, but only mark it as a candidate for removal; then, a potential undo will un-mark the file.

2. Case of irreversible actions:

If you’re talking about irreversible actions, given that the reversibility is not depending of your application, then deal with it, for example by disabling the corresponding undo elements.

For example, you’re writing a tool which manages server farms, and at a given moment, the server win1 is materially unplugged from the network. In this case, your application can’t replug the server, since it requires human intervention.

The application should then adapt its behavior in a more intuitive way: cancel or undos anterior to the irreversible one, skip the irreversible action, or something radically different and innovative. It’s up to your interaction designer to come with the most intuitive way.

This is not much different from a case such as when your application tries to access a database, but the database is offline: you can’t do anything about it, and the way you behave depends on the precise context.

Example:

Let’s take an example from your comment: the removal of an Excel sheet. The successive actions are:

  1. Change the cell A1 in sheet 1,
  2. Change the cell A1 in sheet 2,
  3. Remove the sheet 1.

Two possible behaviors would be:

  1. The reversibility of sheet removal. Strangely, Excel team have chosen to not make it reversible. While this is probably motivated by some technical aspects I ignore, this behavior is totally wrong UX-wise. As a user, when by mistake I destroy a sheet, I expect to be able to get it back by pressing Ctrl+Z.

    Not being able to do that makes it risky to use Excel, since I can never know what could be reverted, and what could result in a loss of work. That’s not nice at all.

  2. The impossibility to remove the sheet with the loss of “Change the cell A1 in sheet 1” step in the undo stack after the removal. Technically, it’s not so difficult. Imagine the following structure:

    class undoEntry
    {
        string text; // Text which is displayed to the user in the undo history.
        sheet? correspondingSheet; // The sheet where the action happened, or null.
        undoAction action; // The action to undo.
    }
    

    With correspondingSheet property, you can then walk though the undo stack when a sheet is removed, and delete the entries you shouldn’t display any longer.

3

Apologies for necromancy but I’ve actually encountered teams scratching their heads over this a lot. Usually I find the solution very simple which is just use more than one stack: done, ship it, collect money.

That was a tad crude but that’s usually sufficient and it makes sense from a UX perspective. Take this example of me using StackExchange right now. Say I was simultaneously answering a separate SE question and multi-tasking in a separate tab in my browser.

I don’t want to ctrl-Z undo and start invisibly undoing changes I made in my other tabs (I’d hate to switch back to the other tab and realize I accidentally reverted all my work while working in this one), or have ctrl-Z make my browser switch tabs to on me without me explicitly saying so to make those changes visible. My current tab is my “current universe”, and it has its own local data which is invalidated when I close the tab. So when I undo, I only want to undo changes in my current tab and not the other tabs. And the simple answer to allowing that is to have a separate undo stack for each tab (or even more granular context; in this case I suspect the text control itself has its own context and local undo stack) in the browser, not one undo stack to rule them all with entries that get invalidated. That also keeps the memory management tight and clean and so on without these invalidation concerns.

You’ll have to forgive me if I’m a little bit passionate about this one since the team I worked with before started getting tempted to do all these fancy things to detect invalidation of undo entries and when I pointed out the UX concerns above, they were contemplating having multiple “stack pointers” into the stack which can be moved separately based on which tab/window we were in. I was like, “Nooooo! Just use more than one undo stack! Geeeeeeez!!!” 😀 I think I got some gray hairs just specifically from that one.

Having a separate undo stack for each window should be enough in your case given what you said here:

At action1 the program would probably crash as the context is has been executed against previously (i.e. win1) doesn’t exist any more. As such, when win1 gets closed, all the corresponding actions should be removed from the “undo-stack”.

If that data is invalidated when win1 is closed, that implies the data belongs to the window and doesn’t make sense outside of it, just as with my browser tab analogy. So that should go into a different undo/command stack as I see it — don’t jumble them all up in one stack.

If these windows share state and there are changes to the underlying shared business logic side of the data along with data local and specific only to a particular window being modified simultaneously with one user input action, then that gets a lot more tricky and we have to rethink a lot more. But given what I usually encounter and the specific problem you are facing now, I suspect just using multiple undo stacks should be enough, and the undo stack local to win1 would be tossed away when you close that window.

Context is important from a programming/technical standpoint, as well as, usability. Your Excel comment is a good example. Several documents and then worksheets could be in the same instance of Excel and your undo would go across them (assuming you made recent actions on different ones), but this wouldn’t happen in another instance of Excel or from Excel to Word.

Users may be able to provide the context where they see the line drawn with this functionality. Microsoft could have built this functionality in the context of all open Office applications. I wouldn’t like it as a user eventhough it is technically possible.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật