Popup Minibuffer at the Center
Here’s a way to do exactly what you asked: display the minibuffer at the center of the screen.
- Have a separate frame for the minibuffer
- Position it at the center
- Raise this frame whenever the minibuffer gains focus.
That is relatively easy to achieve, and is what you’ll get with the
oneonone.el
package (as @Drew points out). The problem with this approach, is
that the minibuffer is also used for messages, so keeping it hidden is
not something very convenient. Not to worry! I came up with another
solution.
- Have a second minibuffer displayed on a separate frame.
- Position it at the center.
- Use the second frame for interactive commands, while the original (first) minibuffer is used for echoing messages.
Implementation
To implement this, we need to create the minibuffer frame at emacs
startup.
(defvar endless/popup-frame-parameters
'((name . "MINIBUFFER")
(minibuffer . only)
(height . 1)
;; Ajust this one to your preference.
(top . 200))
"Parameters for the minibuffer popup frame.")
(defvar endless/minibuffer-frame
(let ((mf (make-frame endless/popup-frame-parameters)))
(iconify-frame mf) mf)
"Frame holding the extra minibuffer.")
(defvar endless/minibuffer-window
(car (window-list endless/minibuffer-frame t))
"")
Then we patch read-from-minibuffer
to use this second minibuffer,
instead of the original frame’s minibuffer.
(defmacro with-popup-minibuffer (&rest body)
"Execute BODY using a popup minibuffer."
(let ((frame-symbol (make-symbol "selected-frame")))
`(let* ((,frame-symbol (selected-frame)))
(unwind-protect
(progn
(make-frame-visible endless/minibuffer-frame)
(when (fboundp 'point-screen-height)
(set-frame-parameter
endless/minibuffer-frame
'top (point-screen-height)))
(select-frame-set-input-focus endless/minibuffer-frame 'norecord)
,@body)
(select-frame-set-input-focus ,frame-symbol)))))
(defun use-popup-minibuffer (function)
"Rebind FUNCTION so that it uses a popup minibuffer."
(let* ((back-symb (intern (format "endless/backup-%s" function)))
(func-symb (intern (format "endless/%s-with-popup-minibuffer"
function)))
(defs `(progn
(defvar ,back-symb (symbol-function ',function))
(defun ,func-symb (&rest rest)
,(format "Call `%s' with a poupup minibuffer." function)
,@(list (interactive-form function))
(with-popup-minibuffer
(apply ,back-symb rest))))))
(message "%s" defs)
(when (and (boundp back-symb) (eval back-symb))
(error "`%s' already defined! Can't override twice" back-symb))
(eval defs)
(setf (symbol-function function) func-symb)))
;;; Try at own risk.
(use-popup-minibuffer 'read-from-minibuffer)
;;; This will revert the effect.
;; (setf (symbol-function #'read-from-minibuffer) endless/backup-read-from-minibuffer)
;; (setq endless/backup-read-from-minibuffer nil)
This might not work with absolutely everything, but it worked on
everything I tried so far---find-file
, ido-switch-buffer
,
eval-expression
. If you do find any exceptions, you can patch
these functions on a case-by-case basis by calling
use-popup-minibuffer
on them.
Position Near Point
To position this minibuffer frame near the height of the point, simply
define something like the following function. It’s not perfect (in fact, it's awful at a lot of cases), but it does a decent
job of estimating point height.
(defun point-screen-height ()
(* (/ (face-attribute 'default :height) 10) 2
(- (line-number-at-pos (point))
(line-number-at-pos (window-start)))))