TIP 446: Introspect Undo/Redo Stack Depths

Login
Bounty program for improvements to Tcl and certain Tcl packages.
Tcl 2017 Conference, Houston/TX, US, Oct 16-20
Send your abstracts to tclconference@googlegroups.com
by Aug 21.
Author:         Fran├žois Vogel <fvogelnew1@free.fr>
State:          Final
Type:           Project
Vote:           Done
Created:        05-Apr-2016
Post-History:   
Keywords:       Tk
Tcl-Version:    8.6.6

Abstract

Tk features a generic undo/redo mechanism (see [104]). This is used in practice by the text widget, within the edit command. The present TIP proposes to add two new subcommands to the edit command allowing the user to know whether undo and redo is possible for a text widget.

Rationale

The undo/redo feature of Tk is handy and works very well. In modern GUIs, there is usually a button in a menubar to call the "Undo" command, and likewise for "Redo". It is good practice to enhance the user experience by greying out or otherwise changing the button aspect when there is nothing to undo (or redo). This cannot be achieved currently with the curent Tk implementation because there is no way to know whether the undo and redo stacks are empty or not.

This feature was requested for the text widget in RFE 1273358 https://core.tcl.tk/tk/tktview/1273358

Proposed Change

It is proposed to add the following subcommands to the text widget's edit command:

canundo:

Returns a boolean true if undo is possible, i.e. when the undo stack is not empty. Otherwise returns false.

canredo:

Returns a boolean true if redo is possible, i.e. when the undo stack is not empty. Otherwise returns false.

When the -undo option of the text widget is false both subcommands return false since indeed no undo nor redo action is possible in this case. The undo/redo stacks are however not cleared, so that when -undo becomes eventually true again the two new subcommands will return their value in accordance with the contents of the stacks (this is current behavior of the undo/redo feature - the TIP does not intend to change this).

This new capability will be implemented in the generic code for undo (generic/tkUndo.c), so that any client of the generic undo/redo mechanism can make use of it. Currently, only the text widget is concerned since no other Tk widget is featuring undo/redo.

Besides the two new subcommands, it is also proposed to add a new virtual event <>, that will trigger each time the undo or redo stack becomes empty or unempty. When this condition is met, the event will trigger once for each peer widget.

Despite this is a new feature, this TIP targets Tk 8.6.6 since no change of any existing behavior is proposed: only new, additonal, features are proposed. It is therefore believed there is no risk regarding backwards compatibility in the 8.6.x series.

Example

  package require Tk

  pack [text .t -undo false -autoseparators false]

  set nbUS 0
  bind .t <<UndoStack>> {incr nbUS}

  .t edit canundo    ; # 0
  .t edit canredo    ; # 0

  .t configure -undo true

  .t edit canundo    ; # 0
  .t edit canredo    ; # 0

  .t insert end "ABC\n"
  .t edit separator
  .t insert end "DEF\n"
  .t insert end "DEF again\n"
  .t edit separator

  .t edit canundo    ; # 1
  .t edit canredo    ; # 0

  .t edit undo

  .t edit canundo    ; # 1
  .t edit canredo    ; # 1

  # A quick interactive testing environment...:

  pack [label .l]

  proc showit {} {
    global nbUS
    .l configure -text "Can undo: [.t edit canundo]\t\t\t \
                        Can redo: [.t edit canredo]\t\t\t \
                        <<UndoStack>> triggered: $nbUS"
    after 200 showit
  }
  showit

  proc toggleautosep {} {
    global autosepset
    set autosepset [.t cget -autoseparators]
    if {$autosepset} {
      .t configure -autoseparators false
    } else {
      .t configure -autoseparators true
    }
    set autosepset [.t cget -autoseparators]
  }
  proc toggleundo {} {
    global undoset
    set undoset [.t cget -undo]
    if {$undoset} {
      .t configure -undo false
    } else {
      .t configure -undo true
    }
    set undoset [.t cget -undo]
  }
  button .b1 -text "Insert separator" -command {.t edit separator}
  button .b2 -text "Undo" -command {.t edit undo}
  button .b3 -text "Redo" -command {.t edit redo}
  button .b32 -text "Reset" -command {.t edit reset}
  checkbutton .b4 -text "-autoseparators" -command toggleautosep -variable autosepset
  checkbutton .b5 -text "-undo" -command toggleundo -variable undoset ; .b5 select

  pack .b1 .b2 .b3 .b32 .b4 .b5 -side left -padx 10

Reference Implementation

A reference implementation is available in branch tip-446 of the fossil repository. Credits for this implementation largely go to Neil Hodgson, the author of TkTextPlus (for canundo/canredo) and to Koen Danckaert (for <>).

Copyright

This document has been placed in the public domain.

History