TIP 658: Attach identifiers to Tk menu entries

Login
Author:		Schelte Bron <[email protected]>
State:		Final
Type:		Project
Vote:		Done
Vote-Summary:	Accepted 4/0/0
Votes-For:	BG, SL, FV, KW
Votes-Present:	none
Votes-Against:	none
Created:	16-Mar-2023
Tcl-Version:	8.7
Tk-Branch:	tip-658
Keywords:	tk menu

Abstract

This tip proposes to enhance the Tk menu widget by attaching an identifier to each menu entry.

Rationale

The Tk menu system currently provides a few methods to refer to a menu entry. To reconfigure an entry after creation, scripts generally use one of two methods. Both of which have their pitfalls:

Index numbers
Index numbers change when entries are added or deleted, and even when the -tearoff option of the menu is changed.

Patterns
The wording or formatting of a menu entry label may change during the lifetime of an application. The developer then has to go through the code to find all references and change them too. Labels may also change during the runtime of an application, for example in a bookmarks menu. Problems also arise when an application uses internationalization. While using the localized string as the pattern will work most of the time, not every string matches itself as a string match pattern. And updating a label when the locale changes is very cumbersome.

Addressing menu entries would be much easier if they can be referenced using a fixed identifier. This would also benefit the tooltip package from tklib.

Specification

Similar to canvas items and ttk::treeview items, whenever a menu entry is created, a unique id is assigned to it. If no id is specified by the developer, an id is automatically generated. This id stays with the entry until it is deleted. The id may be used as the index for all widget commands that take an index argument.

The following widget commands have gained an optional id argument:

Both of these commands now return the id assigned to the entry. If an id is specified that already exists, the command throws an error. If one of the reserved indices (active, end, last, or none) is specified as the id, or an id of the form number or @number is used, no error is thrown. But the entry will not be accessible by that id, because the special meaning of such indexes will take precedence.

A new widget command is introduced to obtain the id of a menu entry:

The ids of entries are mirrored in cloned menus.

Alternative consideration

The presence of the id argument is being detected based on the fact that option value pairs always have to be given using an even number of arguments. This is similar to how an optional name is passed to the image create photo command. An alternative method would be what ttk::treeview does: Via a read-only -id option. This would avoid the need for the id widget command. However, the use of read-only options is uncommon in plain Tk widgets. Making the -id option writable would unnecessarily complicate the implementation. Either way, the add and especially the insert method will have to return the assigned id. It would be hard to reliably obtain it otherwise.

The use of tags instead of, or in addition to ids could also be a possibility. This would allow multiple entries with the same tag to be addressed together. A use case would be the Cut, Copy, and Delete entries in an Edit menu, which would all have to be enabled or disabled based on whether something is selected. The few situations where this could be useful don't justify the complexity and memory overhead of implementing such a feature.

User provided ids are not checked against the list of reserved indices (active, end, last, none), or if they match any of the other forms for specifying an entry index (number, @number). Assigning the ids will be accepted, but the special meaning of these strings when used as index will take precedence. This means that entries with such ids will just not be addressable by their id. An exception can be implemented, if deemed desirable.

After an index is compared against the special forms mentioned above without finding a match, a check is made to see if the index equals the id of any entry. Only if all of that fails, a pattern match against the labels is done. This order was chosen because doing a pattern match is slow. The id check is a quick hash table lookup. The situation that an index would match the id of one entry and the label of a different entry is expected to be extremely rare to merely hypothetical in a real-life application. New applications are most likely to start using the id, rather than pattern matching, which is another reason to favor the id over a pattern.

In the current implementation, specifying an id that is already used throws an error. Another option would be to remove the id from the old entry and assign it to the new entry. This makes ids less reliable, in my opinion. The whole point of this tip is to have a fixed reference to a menu entry. Allowing ids to be moved defeats that purpose. Throwing an error is also how ttk::treeview deals with duplicate ids.

Implementation

Implementation is available in the tip-658 branch of the Tk repository.

Copyright

This document has been placed in the public domain.