Based on @Drew's answer (Icicles), with modifications to separate menu implementation from menu data - so multiple menu's could be defined, each with their own: (content, prompt, default).
Generic popup.
(defun custom-popup (menu-prompt menu-index menu-content) "
Pop up menu
Takes args: menu-prompt, default-index, menu-content).
Where the content is any number of (string, function) pairs, each representing a menu item.
User can hit just the first char of a menu item to choose it.
Or click it with `mouse-1' or `mouse-2' to select it.
Or hit RET immediately to select the default item.
"
(interactive)
(let*
(
(icicle-Completions-max-columns 1)
(icicle-show-Completions-initially-flag t)
(icicle-incremental-completion-delay 0.01)
(icicle-top-level-when-sole-completion-flag t)
(icicle-top-level-when-sole-completion-delay 0.01)
(icicle-default-value t)
(icicle-show-Completions-help-flag nil)
(choice
(completing-read
;; menu prompt & content
menu-prompt menu-content nil t nil nil
;; default index
(nth menu-index menu-content)))
(action (cdr (assoc choice menu-content))))
(funcall action)))
Example use, assign some actions to the f12 key:
(defvar
my-global-utility-menu-def
;; (content, prompt, default_index)
'(("Emacs REPL" . ielm)
("Spell Check" . ispell-buffer)
("Delete Trailing Space In Buffer" . delete-trailing-whitespace)
("Emacs Edit Init" . (lambda () (find-file user-init-file))))
(defun my-global-utility-popup ()
"Pop up my menu. Hit RET immediately to select the default item."
(interactive)
(custom-popup my-global-utility-menu-def "Select: ", 1))
(global-set-key (kbd "<f12>") 'my-global-utility-popup)