Search code examples
emacselisp

Switch between frames by number or letter


I'd like to create a function that offers me numbered or lettered choices (1, 2, 3, or a, b, c) of available frames to switch to, instead of manually typing the name. Aspell would be the closest example I can think of.

Could someone please share an example of how this might be done? Lines 6 to 14 of the following function creates a list of all available frame names on the fly. Additional functions related to frame switching can be found here

(defun switch-frame (frame-to)
  (interactive  (list (read-string (format "From: (%s) => To: %s.  Select: "
    ;;  From:
    (frame-parameter nil 'name)
    ;;  To:
    (mapcar
      (lambda (frame) "print frame"
        (reduce 'concat
          (mapcar (lambda (s) (format "%s" s))
            (list "|" (frame-parameter frame 'name) "|" )
          )
        )
      )
    (frame-list) )
   )))) ;; end of interactive statement
  (setq frame-from (frame-parameter nil 'name))
  (let ((frames (frame-list)))
    (catch 'break
      (while frames
        (let ((frame (car frames)))
          (if (equal (frame-parameter frame 'name) frame-to)
              (throw 'break (select-frame-set-input-focus frame))
            (setq frames (cdr frames)))))) )
  (message "Switched -- From: \"%s\"  To: \"%s\"." frame-from frame-to) )

EDIT (November 13, 2014):  Here is a revised function using ido-completing-read:

(defun ido-switch-frame ()
(interactive)
  (when (not (minibufferp))
    (let* (
        (frames (frame-list))
        (frame-to (ido-completing-read "Select Frame:  "
          (mapcar (lambda (frame) (frame-parameter frame 'name)) frames))))
      (catch 'break
        (while frames
          (let ((frame (car frames)))
            (if (equal (frame-parameter frame 'name) frame-to)
              (throw 'break (select-frame-set-input-focus frame))
              (setq frames (cdr frames)))))))))

Solution

  • Depending upon the operating system, it may be necessary to use (select-frame-set-input-focus chosen-frame) instead of select-frame / raise-frame towards the end of the function.

    (defface frame-number-face
      '((t (:background "black" :foreground "red" )))
      "Face for `frame-number-face`."
      :group 'frame-fn)
    
    (defface frame-name-face
      '((t ( :background "black" :foreground "ForestGreen")))
      "Face for `frame-name-face`."
      :group 'frame-fn)
    
    (defun select-frame-number ()
    "Select a frame by number -- a maximum of 9 frames are supported."
    (interactive)
      (let* (
          choice
          chosen-frame
          (n 0)
          (frame-list (frame-list))
          (total-frames (safe-length frame-list))
          (frame-name-list
            (mapcar
              (lambda (frame) (cons frame (frame-parameter frame 'name)))
              frame-list))
          (frame-name-list-sorted
            (sort
              frame-name-list
              #'(lambda (x y) (string< (cdr x) (cdr y)))))
          (frame-number-list
            (mapcar
              (lambda (frame)
                (setq n (1+ n))
                (cons n (cdr frame)))
              frame-name-list-sorted))
          (pretty-list
            (mapconcat 'identity
              (mapcar
                (lambda (x) (concat
                  "["
                  (propertize (format "%s" (car x)) 'face 'frame-number-face)
                  "] "
                  (propertize (format "%s" (cdr x)) 'face 'frame-name-face)))
                frame-number-list)
              " | "))  )
        (message "%s" pretty-list)
        (setq choice (read-char-exclusive))
        (cond
          ((eq choice ?1)
            (setq choice 1))
          ((eq choice ?2)
            (setq choice 2))
          ((eq choice ?3)
            (setq choice 3))
          ((eq choice ?4)
            (setq choice 4))
          ((eq choice ?5)
            (setq choice 5))
          ((eq choice ?6)
            (setq choice 6))
          ((eq choice ?7)
            (setq choice 7))
          ((eq choice ?8)
            (setq choice 8))
          ((eq choice ?9)
            (setq choice 9))
          (t
            (setq choice 10)))
        (setq chosen-frame (car (nth (1- choice) frame-name-list-sorted)))
        (when (> choice total-frames)
          (let* (
              (debug-on-quit nil)
              (quit-message
                (format "You must select a number between 1 and %s." total-frames)))
            (signal 'quit `(,quit-message ))))
        (select-frame chosen-frame)
        (raise-frame chosen-frame)
        chosen-frame))
    

    Example