The Complete Computing Environment

Floating Minibuffer via Ivy/PosFrame


,#+ARROYOEMACSMODULE: ivy-posframe

(provide 'cce/ivy-posframe)

Ivy/PosFrame is an Emacs Glue Library which allows me to take the Ivy minibuffer and display it in a temporary Emacs frame, which in Modern Interface Terms means that it appears in a dedicated window above my Emacs window, rather than taking a section of it. In EXWM this has the added benefit of not forcing windows to constantly re-size themselves whenever I hit M-x or go to swap buffers.

Configuration is more or less taken from the repository's file, except that I tell it to display most things at frame center, where my eyes are likely going to find it most easily. Swiper, however, gets to stay pinned to its parent frame, since it needs to stay out of the way when I am looking at text. Of note here as well, I set parent-frame to nil in the temporary frame created by posframe, which allows them to float above EXWM-managed windows, based on advice provided in this GitHub issue.

(use-package ivy-posframe
  (setq ivy-posframe-parameters '((left-fringe 10)
                                  (right-fringe 10)
                                  (z-group . above)
                                  (parent-frame nil))
       ivy-posframe-display-functions-alist '((swiper           . ivy-display-function-fallback)
                                              (complete-symbol  . ivy-posframe-display-at-point)
                                              (t                . ivy-posframe-display-exwm)))

EXWM and Ivy/PosFrame don't interact well, as hinted above, and in the case of having multiple monitors, Ivy/PosFrame simply doesn't know where to put the window, causing it to appear off-screen at a logical 0,0 coordinate. Ivy/PosFrame lets users define our own display-function, and someone in the thread above implemented one which uses EXWM's workspace metadata to construction a frame location. I haven't tried it, and need to integrate it properly the next time I am on my desktop. I implemented something similar but this is more clear.

(defun ivy-posframe-display-exwm (str)
  (if (memq #'exwm-init after-make-frame-functions) 
      (ivy-posframe--display str
                             (lambda (info)
                               (let* ((workarea (elt exwm-workspace--workareas exwm-workspace-current-index))
                                      (x (aref workarea 0))
                                      (y (aref workarea 1))

                                      (fw (aref workarea 2))
                                      (fh (aref workarea 3))

                                      (pw (plist-get info :posframe-width))
                                      (ph (plist-get info :posframe-height)))

                                 (cons (+ x (/ (- fw pw) 2)) (+ y (/ (- fh ph) 2))))))
    (ivy-posframe-display-at-frame-center str)))