AEditor project
Simon Strandgaard
$Id: pagedown_resize.rb,v 1.10 2003/09/17 11:45:14 neoneye Exp $

User-friendly Pagedown

At first glance pagedown seems to be a trivial operation, but if we dig deeper, an interesting world appears. Undo/Redo of pagedown should attempt to restore the original state. How should one deal with those cases where 100% restoration is NOT possible?  Lets find out!

If the window has been resized, then it is no longer possible to restore the exact original state. In such cases it should do what seems to be most user-friendly. This is what this document is about.

Pagedown

Undo/Redo should attempt to restore the view position, plus the cursor-position-within-view. In certain cases the view position cannot be retored, eg: resize. Therefore we must try to do the best we can and restore it into a state as close as possible to the original one. In order to do this 'nice' restoration, then our memento must also store cursor-position-within-view and view-height.

t0t01t1t12t2t23t3
pagedownundo (pagedown)redo (pagedown)
a typical page down operation. everything will be restored, exactly as it were before. you will not be able to tell the difference, wheter pagedown or redo occured.

As you can see both (t0 == t2) and (t1 == t3) is true. The corresponding test code looks like this:

def test_pagedown_normal1
    i = fake_setup(2, 2, 1, 4)
    assert_equal([2, 2, 1, 4], i.status)
    i.execute :do_move_page_down
    assert_equal([5, 2, 1, 1], i.status)
    i.undo
    assert_equal([2, 2, 1, 4], i.status)
    i.redo
    assert_equal([5, 2, 1, 1], i.status)
end 

Another interesting case is when the we hit the bottom of the file, this look like the following diagram:

t0t01t1t12t2t23t3
pagedownundo (pagedown)redo (pagedown)
We will hit the bottom of the buffer. everything will be restored, exactly as it were before. Obsrve that NO scrolling occurs, eg: the view should not be locked in this case. How does this special case work out if resize is involved? you will not be able to tell the difference, wheter pagedown or redo occured.

Resize

Resize is an operation which cannot be undone, therefore it doesn't store any any memento data.

Observe that Resize let the cursor-position stay untouched! If you resize the view by grapping at the bottom-border, then only the bottom lines is affected. The cursor is frozen and locked on to the same (x, y) position in the display.

t0t01t1t12t2t23t3t34t4
pagedownresizeundo (pagedown)redo (pagedown)
a typical page down operation. By resizing + moving + resizing the window, we will get this output! Preservation of the cursor, so you get the feeling that its the opposite of pagedown(pageup). Preservation of the cursor, so you get the feeling that its pagedown.

A smaller view?

Changing the view-height from 4 lines into 2 lines.

t0t01t1t12t2t23t3t34t4
pagedownresizeundo (pagedown)redo (pagedown)
a typical page down operation. Shrinking the height of the view affects indirectly redo! Undoing behaves quite differently from a ordinary page-up operation. This is because of resize! If we redo, then the page-down operation is no longer a typical behavier. Therefore watch out redoing page-down, when the height has changed!

A bigger view?

Changing the view-height from 4 lines into 7 lines.

t0t01t1t12t2t23t3t34t4
pagedownresizeundo (pagedown)redo (pagedown)
a typical page down operation. Widening the height of the view affects indirectly redo! Preserve the cursors relative position within the view. Preserve the cursors relative position within the view.

At the buffer-top it has serious consequences.

t0t01t1t12t2t23t3t34t4
pagedownresizeundo (pagedown)redo (pagedown)
a typical page down operation. Widening the height of the view affects indirectly redo! Undoing behaves quite differently from a ordinary page-up operation. This is because of resize!

Preservation of the cursor-position-within-view is not possible. Thus observe that the cursor position within the view is changed. This is because we have hit buffer-begin.

I think it would be a bad idea to preserve the cursor position from t2. This just works as an nomal pagedown operation.

At the buffer-bottom there doesn't seems to be any problems, at least there is no problems here.

t0t01t1t12t2t23t3t34t4
pagedownresizeundo (pagedown)redo (pagedown)
a typical page down operation. Widening the height of the view affects indirectly redo! Preservation of the cursor. Preservation of the cursor.

But at the buffer-bottom there IS a minor problem. Compare case t23 above with below, observe that scrolling occurs in the above case and NO scrolling occurs in the below case! Which behaver should one choose to implement?
Hmmm.. I think the case below is bad, because it doesn't behave like pageup, its behavier reminds me more of move_one_line_up. Therefore the above case is much better.

t0t01t1t12t2t23t3
pagedownresizeundo (pagedown)
a typical page down operation. Widening the height of the view! Preserving the cursor position is bad in this paticular case. It is much better to disable lock_view.

Conclusion

I have learned that resize must be considered when dealing with undo/redo. This document is only about consequences from resize. I have seen examples of many other editors which erroneous deals with these situations. It both scares me and makes me more confident!

Is it really worth the efforts, making pagedown user-friendly?
Yes, undo of Pagedown can easily be confusing to the observer when doing pair-programming, this is vital for improving user-frindlyness for the observer!

The solution is simple: In the border-cases (bottom/top) to use move the cursor within the view. In the normal case scrolling is done. During set_memento, you can specify if the view should be locked.

Most of these cases also work for pageup, but not all. This is because up/down are assymetric. At the bottom there is virtual-lines. At the top there is nothing. BTW: The resize concept also applyes to sideway scrolling!

If we use softwrap, then changing the width of the window is equal to changing the height of the window!