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

programking
  • 7,064
  • 9
  • 41
  • 62
Davor Cubranic
  • 700
  • 4
  • 9
  • 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 Oct 27 '14 at 21:01
  • Though the question is not a duplIcate, it does have an answer [here](http://emacs.stackexchange.com/a/296/50) – Malabarba Oct 27 '14 at 23:19

1 Answers1

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.

nispio
  • 8,175
  • 2
  • 35
  • 73
  • 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. – Davor Cubranic Dec 17 '14 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: http://emacs.stackexchange.com/a/296/93 – nispio Dec 18 '14 at 04:07