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)))))