2008년 10월 21일

Switch Emacs Frames Quickly

Quick answer: Function other-frame is bound to C-x 5 o. But I'm not talking about it.

I don't like to lose focus on my keyboard when I write code. I'm not effective enough to work well while switching between keyboard and mouse.

So I try not to use mouse if possible, not because I hate mice but I can write code easier with keyboard only. Someone may prefer to write code without keyboard though. (But has anybody here watched the video about writing perl code with voice recognition system?)

Anyway, I open about 6 emacs frames–which are windows of X– in a single GNOME workspace. And it's very hard to switch between frames with Alt-Tabs. I don't know how many Tabs with Alt key pressed I need for switching to my desired frame. I saw someone open more than 10 frames at a time.

I searched and found Joseph Perla's solution. His solution was very interesting, but I'd rather not change source code whenever I move window positions.

I thought it would be great if I could switch to the frame on some direction. I could research X Window system more, but I chose ELisp for that, because every window on the screen was Emacs frame and I enjoyed learning ELisp.

Well, this is ELisp code to make it possible. I bound super-left key to switch to the nearest frame on the left side of the current frame, and so on.

ELisp Code

You can copy and paste the following code and save it to your ELisp place.

;; directed-switch-to-frame.el -- Switching to frames with direction.

;; Keywords: frame, direction

;; Move 4 direction to switch emacs frames.

(defun cartesian-to-polar (cartesian)
  "Convert cartesian coordinate to polar coordinate."
  (let ((x (float (car cartesian))) (y (float (cdr cartesian))))
    (cons
     (sqrt (+ (* x x) (* y y)))
     (cond ((and (= x 0) (> y 0)) (* pi 0.5))
           ((and (= x 0) (< y 0)) (* pi 1.5))
           ((< x 0) (+ (atan (/ y x)) pi))
           ((and (> x 0) (< y 0)) (+ (atan (/ y x)) (* pi 2)))
           ((> x 0) (atan (/ y x)))
           (t nil)))))

(defun switch-to-frame-with-direction (dir-in-deg var-in-deg next)
  "Switch to frame with direction with variation."
  (let ((f (sort (delq nil
                       (let ((list-of-frames (mapcar (lambda (each-frame)
                                                       (cons each-frame
                                                             (cons (+ (frame-parameter each-frame 'left)
                                                                      (/ (frame-pixel-width each-frame) 2))
                                                                   (+ (frame-parameter each-frame 'top)
                                                                      (/ (frame-pixel-height each-frame) 2)))))
                                                     (cons (selected-frame) 
                                                           (delete-if (lambda (item)
                                                                        (or (eq (selected-frame) item)
                                                                            (eq nil (frame-parameter item 'window-id))))
                                                                        (visible-frame-list)))))
                             (dir-in-rad (degrees-to-radians (mod dir-in-deg 360)))
                             (var-in-rad (degrees-to-radians (mod var-in-deg 360))))
                         (mapcar
                          (lambda (each-frame)
                            (let ((polar (cartesian-to-polar (cons (- (cadr each-frame) (cadr (car list-of-frames)))
                                                                   (- (cddr each-frame) (cddr (car list-of-frames)))))))
                              (when (or (not (numberp (cdr polar)))
                                        (< (- (* 2 pi) (abs (- (cdr polar) dir-in-rad))) var-in-rad)
                                        (< (abs (- (cdr polar) dir-in-rad)) var-in-rad))
                                (cons (car each-frame) polar))))
                          (cdr list-of-frames))))
                 (lambda (a b)
                   (or (not (numberp (cddr b)))
                       (and (numberp (cddr a))
                            (< (cadr a) (cadr b))))))))
    (cond ((not f) nil)
          ((cddar f) (x-focus-frame (caar f)))
          (t (while (not (memq (selected-frame) (mapcar 'car f)))
               (other-frame next))))))

(defun switch-to-right-frame ()
  (interactive)
  (switch-to-frame-with-direction 0 60 -1))

(defun switch-to-down-frame ()
  (interactive)
  (switch-to-frame-with-direction 90 60 -1))

(defun switch-to-left-frame ()
  (interactive)
  (switch-to-frame-with-direction 180 60 1))

(defun switch-to-up-frame ()
  (interactive)
  (switch-to-frame-with-direction 270 60 1))

(provide 'directed-switch-to-frame)

Sorry, I'm not good at writing ELisp code yet.

Key binding

I put the following code to my .emacs file.

;; Moving between frames
(when (load "directed-switch-to-frame")
  (global-set-key [s-left] 'switch-to-left-frame)
  (global-set-key [s-right] 'switch-to-right-frame)
  (global-set-key [s-up] 'switch-to-up-frame)
  (global-set-key [s-down] 'switch-to-down-frame))

Enjoy

Restart your emacs and open multiple frames with C-x 5 2. Usually the super key is bound to the Windows key. So try Win-Left, Win-Up, Win-Right, Win-Down to switch frames. Does it work?

It doesn't move to windows other than Emacs frames. And it doesn't move to Emacs frames of other emacs instances.

If center coords of frames are same, the focus cycles if there are no other frames on that direction. This is useful if you opened multiple full screen frames. You can switch more quickly than Alt-Tab. Opposite direction will cycle the opposite way. Isn't that cool? :)

댓글 없음: