FunctionPackage: cgToCDocOverviewCGDocRelNotesFAQIndexPermutedIndex
Allegro CL version 10.1
Unrevised from 10.0 to 10.1.
10.0 version

pop-up-menus-for-many-sorted-choices

Arguments: choices &key (on-print (quote princ-to-string)) (stream (screen *system*)) position

Arguments: choices &key (on-print (quote princ-to-string)) (stream (screen *system*)) position case-insensitive on-help

This function takes a list of pre-sorted choices and presents them to the user as a series of pop-up menus (by calling pop-up-menu one or more times). The main purpose of this function is to make it easy to select one of many choices.

This function is challenging to describe. We will start with a simple example where we do not use the keyword arguments, then we will describe the arguments in more detail, then we have a more complex example.

choices must be list of objects arranged so their printnames (as created by the function which is the value of the on-print argument) are in alphanumeric order. Just to use strings (assuming default on-print), ("Jones" "Smith" "Smits" "Watson" "Williams" "Wilson") is a suitable value. ("Smith" "Smits" "Watson" "Williams" "Jones" "Wilson") is not. (The menu presenting algorithm may get confused if the printnames are out of order.) So this call:

(pop-up-menus-for-many-sorted-choices '("Jones" "Smith" "Smits"
                                        "Watson" "Williams" "Wilson"))

causes this menu to be displayed:

Note that, for reasons having to do with how multiprocessing in the IDE is done, clicks away from the menu and keystrokes are not seen by the menu, so this example menu cannot be dismissed -- you must follow it through to a choice. In the example below, things are set up so the menu can be dismissed and keystrokes can be used to choose items. Here we are only interested in showing what the choices are in the cascade of menus displayed.

In our choices, we have three initial characters, J, S, and W, so there are three choices in the initial menu. Each new menu will have as many choices as there are different characters in the position of the remaining choices at the position where they start to differ.

The choices are Jones, Smit..., and W.... Jones has no ellipsis (...) because there is only one choice beginning with J so if you select it, you get it. the other two have ellipses because there are multiple choices. The ellipsis starts where the characters diverge (after Smit or after W). We select W... and this new menu is displayed:

Now the choices are Watson and Wil.... If you choose Watson, that object is returned. If you choose Wil..., you get a final menu with choices Williams and Wilson (not illustrated).

So, a pop-up menu will be shown for each character position for which there remain multiple choices that have different characters at that position. When there is a single remaining choice for a character at the current character position, then that choice will appear on the menu and the user may select it directly. When there are multiple remaining choices for a particular next character, then the menu-item for that character will show just the character followed by an ellipsis, and selecting that menu-item will show a further pop-up menu for the remaining choices that have the selected character at that position.

The keyboard shortcut on each menu will be the unique character at the current character position that either selects a final choice or presents a further menu for that character. This means that a choice can be made quickly by typing the characters of the desired choice wherever there is an ambiguity, until the choice has been fully disambiguated. Note that using the keyboard shortcuts requires a proper stream argument, as detailed below (In our simple example above, no stream argument specified so shortcuts did not work.)

choices is a list of arbitrary values from which the user may select a choice. The choices must already be sorted alphanumerically by the strings that are returned by the on-print function; otherwise the menu may be confused. One of the elements of this list is returned if the user makes a selection, or nil is returned immediately if the user cancels from any menu.

on-print is a function that returns strings that will appear in the menu. It should take a single argument, which is one of the elements of the choices list, and should return a string to represent it. The default value is princ-to-string. This function will be called many times when there are many choices, and so it is most efficient if it returns an existing string rather than consing a new one on each call. This argument could be identity if the choices are already strings.

stream is either the screen or else a window that will serve as the owner of the pop-up menus. On the Windows platform (and perhaps elsewhere in the future), the keyboard shortcuts for the menu items will work only if this argument is a window that was created in the process that is calling pop-up-menus-for-many-sorted-choices. The default is the screen, and so the keyboard shortcuts would never work unless this argument is passed.

position is the position relative to the upper-left interior corner of the window (or screen) at which the upper-left corner of the pop-up menus will appear. The default is the current position of the mouse cursor.x Successive menus will always appear at the same position as the first one, rather than defaulting again to the new mouse cursor position.

case-insensitive should be a boolean value that indicates whether the choices (after the on-print function has converted them to the strings to be presented) are sorted in a case-insensitve way. The choices will then be separated into multiple menus accordingly. The default is nil, meaning a case-sensitive order.

on-help, if specified, should be a funcallable object (a function object or function name) or nil. The default is nil. A function may be used for displaying status bar messages as the user highlights individual menu items. The function should take a single argument, which will be one of the values in the choices list, and it should return a string that can be displayed in a status bar. The strings will be displayed in the status bar of the specified stream, if it has a status bar, or else in the status bar of the first ancestor window of that stream that has a status bar, if any.

An Example

Below is a complete example that uses an owner window so that keyboard shortcuts will work. The user's most recent choice is displayed in the window. Methods are defined on a window subclass so that the initial menu may be shown either by right-clicking the window or by pressing the spacebar when the window is selected. The mouse or keyboard may then be used to select choices.

The short list of choices is (:apple :blueberry :cherry :chocolate :cranberry). There is only one choice that begins with "a", so "apple" appears on the initial menu and may be chosen immediately. Likewise with "blueberry" for "b". But there are multiple choices beginning with "c", and so the third menu item will show "c ...", and choosing that item will show a second menu of all choices that being with "c". If "c" is chosen, then the second menu will show "ch ..." for the two remaining choices that begin with "ch", followed by "cranberry" since it is the only remaining choice that begins with "cr".

When using the keyboard to select choices most quickly, :apple could be selected by pressing the spacebar to show the first menu, and then pressing the "a" key. :cranberry can be selected by typing space, c, r, while :chocolate requires space, c, h, o.

(defclass menu-test-window (frame-window)
  ((current-food :accessor current-food
                 :initform nil)))

(defun select-a-food (window cursor-position)
  (let* ((choice (pop-up-menus-for-many-sorted-choices
                  '(:apple :blueberry :cherry :chocolate :cranberry)
                  :stream window
                  :position cursor-position)))
    (when choice
      (setf (current-food window) choice)
      (invalidate window))))

(defmethod redisplay-window ((window menu-test-window) &optional box)
  (declare (ignore box))
  (clear-page window)
  (let* ((food (current-food window)))
    (when food
      (with-font (window (make-font-ex nil "Arial" 24 '(:bold)))
        (draw-string-in-box window (princ-to-string food) nil nil
                            (visible-box window) :center :center)))))

(defmethod mouse-right-down ((window menu-test-window)
                             mouse-buttons cursor-position )
  (declare (ignore mouse-buttons))
  (select-a-food window cursor-position))

(defmethod virtual-key-down ((window menu-test-window)
                             mouse-buttons key-code)
  (declare (ignore mouse-buttons))
  (case key-code
    (#.vk-space (select-a-food window #.(make-position 0 0)))
    (t (call-next-method))))

(make-window :menu-sequence-test
  :class 'menu-test-window
  :title "Right-Click or Space"
  :exterior (make-box-relative 200 200 300 200))

Copyright (c) 1998-2022, Franz Inc. Lafayette, CA., USA. All rights reserved.
This page was not revised from the 10.0 page.
Created 2019.8.20.

ToCDocOverviewCGDocRelNotesFAQIndexPermutedIndex
Allegro CL version 10.1
Unrevised from 10.0 to 10.1.
10.0 version