7

The usual behavior of Alt-Tab in window managers is that as long as Alt is pressed, the user can Tab or Shift-Tab through the list of windows, and then when Alt is released the currently selected window is made active.

Can this be replicated with Emacs? AFAIK, keybindings are activated on key-down, and there is no way to say: enter a mode where additional switch through a list and exit (and activate) when Alt is released. (Replace <alt> and <tab> with a modifier + key of choice, I understand that Alt-Tab may be fully under the WM's control.)

2
  • You could achieve a very similar behavior with a transient map. It would not deactivate on key release, but it would deactivate as soon as you pressed any key besides those specified in your transient map.
    – nispio
    Commented Oct 27, 2014 at 21:01
  • Though the question is not a duplIcate, it does have an answer here
    – Malabarba
    Commented Oct 27, 2014 at 23:19

1 Answer 1

6

As has been discussed before, you cannot bind commands to key-up events. One alternative that comes to mind to achieve similar workflow is to use a transient map like this:

(defvar my-cycling-var 0 "Some variable that I am interested in modifying")

(defvar my-transient-map
  (let ((map (make-sparse-keymap)))
     (define-key map (kbd "<tab>") 'my-forward-command)
     (define-key map (kbd "<C-tab>") 'my-forward-command)
     (define-key map (kbd "<S-tab>") 'my-backward-command)
     (define-key map (kbd "<C-S-tab>") 'my-backward-command)
     map)
  "A key map used for a demonstration of transient maps")

(defun my-cycling-command ()
"Starts the cycling command and sets the transient key map"
  (interactive)
  (my-forward-command)
  (set-transient-map my-transient-map 'my-keep-keymap))

(defun my-keep-keymap ()
  "This function is called each time a key is pressed while the
transient map is active. If this function returns non-nil, the
keymap will stay active."
  (let* ((key (this-command-keys))
         (binding (lookup-key my-transient-map key)))
    (unless binding
      (my-end-repeatable-command))
    binding))

(defun my-forward-command ()
  "Increment my-cycling-var by one"
  (interactive)
  (setq my-cycling-var (+ 1 my-cycling-var))
  (message "my-cycling-var is %d" my-cycling-var))

(defun my-backward-command ()
  "Decrement my-cycling-var by one"
  (interactive)
  (setq my-cycling-var (+ -1 my-cycling-var))
  (message "my-cycling-var is %d" my-cycling-var))

(defun my-end-repeatable-command ()
  "Take any necessary action after cycling"
  (message "my-cycling-var was set to %d" my-cycling-var))

;; Bind my-repeatable-command to Ctrl+TAB
(global-set-key (kbd "<C-tab>") 'my-cycling-command)

This binds my-cycling-command to C-TAB. Subsequent presses of TAB and S-TAB or C-TAB and C-S-TAB will have a cycling effect. As soon as you press any key that is not TAB, S-TAB, or RET, the overlay map disappears and you resume normal editing.

Important Note: Prior to Emacs 24.4, set-transient-map was named set-temporary-overlay-map. To be compatible with earlier versions, you will have to use that function name instead.

2
  • This is close enough, thank you. For the remaining piece, could I have an idle function that's active during the transient map and checks active modifiers? For instance, OS X has [NSEvent modifierFlags] method, I wonder if there is anything similar available in elisp. Commented Dec 17, 2014 at 20:00
  • 1
    I don't think you have access to the raw modifiers from elisp. If you are really set on using a key-up behavior you could check out @JordonBiondo's hack here: emacs.stackexchange.com/a/296/93
    – nispio
    Commented Dec 18, 2014 at 4:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.