I'm using hl-mode as a minor mode for deft. How do I make the highlighted line change color (e.g. to gray) when the deft window isn't the current window, and then back to the default highlight color when the deft window becomes the current window again?
I've implemented this for
hl-line-mode using the
Here's the code:
(defface hl-line-inactive '((t nil)) "Inactive variant of `hl-line'." :group 'hl-line) (defun hl-line-update-face (window) "Update the `hl-line' face in WINDOW to indicate whether the window is selected." (with-current-buffer (window-buffer window) (when hl-line-mode (if (eq (current-buffer) (window-buffer (selected-window))) (face-remap-reset-base 'hl-line) (face-remap-set-base 'hl-line (face-all-attributes 'hl-line-inactive)))))) (add-hook 'buffer-list-update-hook (lambda () (walk-windows #'hl-line-update-face nil t)))
What this code is doing:
- Define a new face
hl-line-inactiveto be used in inactive windows. You can use
M-x customize-faceto modify the attributes of this face to your taste.
- Define a function to temporarily remap the highlighting face in inactive windows. A window is considered inactive if it is not displaying the same buffer as the currently selected window.
- Add a hook to
hl-line-update-facefor all the visible windows.
The code above (which I'm using in my own
init file) is much simpler than what I originally posted. Thanks @Drew for the suggestion to use
walk-windows. I also read more about face remapping (see Face Remapping in the Emacs Lisp Manual) and realized I could remove a lot of the code.
For posterity, here's what I originally posted:
;; Define a face for the inactive highlight line. (defface hl-line-inactive '((t nil)) "Inactive variant of `hl-line'." :group 'local) (defun toggle-active-window-highlighting () "Update the `hl-line' face in any visible buffers to indicate which window is active." (let ((dups)) (mapc (lambda (frame) (mapc (lambda (window) (with-current-buffer (window-buffer window) (when hl-line-mode (make-local-variable 'face-remapping-alist) (let ((inactive (rassoc '(hl-line-inactive) face-remapping-alist))) (if (eq window (selected-window)) (progn (setq dups (get-buffer-window-list nil nil 'visible)) (setq face-remapping-alist (delq inactive face-remapping-alist))) (unless (or inactive (memq window dups)) (add-to-list 'face-remapping-alist '(hl-line hl-line-inactive)))))))) (window-list frame))) (visible-frame-list)))) (add-hook 'buffer-list-update-hook #'toggle-active-window-highlighting)
You can use
window-selection-change-functions. By adding a function locally the function is called each time when the window showing that buffer has been selected/deselected (see the docstring for more info).
This can be used to toggle
hl-line-face face for the deft buffer:
(defvar-local deft-hl-line-toggle-cookie+ nil) (defun deft-toggle-hl-line-face+ (w) (cond ((eq (current-buffer) (get-buffer deft-buffer)) (when deft-hl-line-toggle-cookie+ (face-remap-remove-relative deft-hl-line-toggle-cookie+))) (t (with-current-buffer (get-buffer deft-buffer) (setq deft-hl-line-toggle-cookie+ (face-remap-add-relative hl-line-face `(:inherit ,hl-line-face :background "gray"))))))) (defun deft-hl-line-toggle-setup+ () (add-hook 'window-selection-change-functions 'deft-toggle-hl-line-face+ nil t)) (add-hook 'deft-mode-hook 'deft-hl-line-toggle-setup+)