3

Is there a way for the code inside of an :eval form in the modeline format spec to determine whether the modeline will be drawn with the active or inactive face? I'd like part of the modeline to display differently based on whether its active or inactive. I speciafically don't want to use advice, since the code will end up being used in a package.

(eq (get-buffer-window) (selected-window)) always returns true from the code run in mode-line-format, so that doesn't seem to do the trick. I also tried adding a hook to buffer-list-change-hook as suggested by the manual, but that doesn't do; somehow some updates are missed.

Is there a way to check from the :eval portion of a modeline format spec whether the window is selected?`

(Based on the following code I'll bet no, but I'd love to be proven wrong:)

  Lisp_Object old_selected_window = selected_window;
  Lisp_Object old_selected_frame = selected_frame;
  Lisp_Object new_frame = w->frame;
  Lisp_Object old_frame_selected_window = XFRAME (new_frame)->selected_window;
  int n = 0;

  selected_frame = new_frame;
  /* FIXME: If we were to allow the mode-line's computation changing the buffer
     or window's point, then we'd need select_window_1 here as well.  */
  XSETWINDOW (selected_window, w);
  XFRAME (new_frame)->selected_window = selected_window;

  /* These will be set while the mode line specs are processed.  */
  line_number_displayed = false;
  w->column_number_displayed = -1;

  if (WINDOW_WANTS_MODELINE_P (w))
    {
      struct window *sel_w = XWINDOW (old_selected_window);

      /* Select mode line face based on the real selected window.  */
      display_mode_line (w, CURRENT_MODE_LINE_FACE_ID_3 (sel_w, sel_w, w),
             BVAR (current_buffer, mode_line_format));
      ++n;
    }
2

1 Answer 1

1

Here is a rough draft that records the selected-window with a post-command-hook and compares the value to each newly selected-window as redisplay runs its course.

Just evaluate the sample code in a *scratch* buffer and then type M-x text-mode

(defvar modeline-selected-window nil)

(let ((default-background (face-background 'default nil 'default)))
 (set-face-attribute 'mode-line-inactive nil :background default-background :box nil))

(defun modeline-record-selected-window ()
  (setq modeline-selected-window (selected-window)))

(defun modeline-update-function ()
  (cond
    ((minibufferp)
      (let ((default-background (face-background 'default nil 'default)))
        (with-selected-window (minibuffer-window)
          (setq-local face-remapping-alist '(
            (default :foreground "blue")
            (minibuffer-prompt :foreground "red"))))
          (setq-default face-remapping-alist `((mode-line ,'mode-line-inactive)))))
    (t
      (with-selected-window (minibuffer-window)
        (when (local-variable-p 'face-remapping-alist)
          (kill-local-variable 'face-remapping-alist)))
      (setq-default face-remapping-alist nil))))

(defun modeline-set-format ()
  (setq mode-line-format '(
    (:eval
      (if (eq modeline-selected-window (selected-window))
        (propertize "SELECTED WINDOW" 'face 'font-lock-warning-face)
        (propertize "NOT-SELECTED WINDOW" 'face 'font-lock-keyword-face)))))
  ;; next two lines make the affect immediately apparent
  (setq modeline-selected-window (selected-window))      
  (force-mode-line-update))

(define-minor-mode modeline-mode
"This is a minor-mode for `modeline-mode`."
  :init-value nil
  :lighter " ML"
  :keymap nil
  :global t
  :group nil
  (cond
    (modeline-mode
      (add-hook 'post-command-hook 'modeline-record-selected-window)
      (add-hook 'buffer-list-update-hook 'modeline-update-function)
      (add-hook 'text-mode-hook 'modeline-set-format)
      (when (called-interactively-p 'any)
        (message "Globally turned ON `modeline-mode`.")))
    (t
      (remove-hook 'post-command-hook 'modeline-record-selected-window)
      (remove-hook 'buffer-list-update-hook 'modeline-update-function)
      (remove-hook 'text-mode-hook 'modeline-set-format)
      (when (called-interactively-p 'any)
        (message "Globally turned OFF `modeline-mode`.") ))))

(modeline-mode 1) ;; globally turn on minor-mode
4
  • Is there a guarantee that post-command-hook runs between a window focus change and a modeline update? What about frame focus changes? In fact, shouldn't the last selected window be recorded per frame? Jan 12, 2016 at 20:35
  • @Gilles -- How about changing it to the test that Drew used in the related thread, instead of using the post-command-hook?: (add-function :before pre-redisplay-function #'modeline-record-selected-window) I believe that there is only one truly selected window that has current focus, so it shouldn't be recorded on a per frame basis -- at least, I don't think so. But, that is in interesting idea -- i.e., each frame could have some sort of indicator showing the last selected window . . . -- I'll give it some thought.
    – lawlist
    Jan 12, 2016 at 20:39
  • With frames on text terminals, Emacs doesn't even know which frame is focused. Even with GUI frames, there could be multiple focused frames on different displays. On the other hand, Emacs does know which frame has the focus when there are multiple frames on the same X display. I don't know how that works internally. Jan 12, 2016 at 21:09
  • That's nice, thanks, but I don't think it's enough (I'm using a variant thereof already, but it doesn't seem to cover all cases). Advice may work, but I'd really try to avoid it as much as I can; this code will ship in a package.
    – Clément
    Jan 13, 2016 at 4:19

Your Answer

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

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