3

Q: How do I programmatically instruct Emacs to change a displayed image (XPM) on mouse-over in the header-line-format?

For example, the following image has a black circle with a white letter X. On mouse-over, I would like to modify the header-line-format and substitute the existing XPM image with one using different colors.

(setq header-line-format (propertize " " 'display
  '(image :type xpm :data "/* XPM */
          static char * close_tab_xpm[] = {
          \"14 11 3 1\",
          \"       c None\",
          \".      c #000000\",
          \"+      c #FFFFFF\",
          \"     .....    \",
          \"    .......   \",
          \"   .........  \",
          \"  ...+...+... \",
          \"  ....+.+.... \",
          \"  .....+..... \",
          \"  ....+.+.... \",
          \"  ...+...+... \",
          \"   .........  \",
          \"    .......   \",
          \"     .....    \"};" :ascent center :mask (heuristic t) :margin 0)))
1

Here is a new idea. It seems to work for me, but it relies on the mouse-position and detecting when it goes away from the header-mode-line. If you mouse over to change it, but slide right then down it doesn't change. Maybe you can make it more robust by checking for x and y.

(setq
 img1 '(image :type xpm :data "/* XPM */
          static char * close_tab_xpm[] = {
          \"14 11 3 1\",
          \"       c None\",
          \".      c #000000\",
          \"+      c #FFFFFF\",
          \"     .....    \",
          \"    .......   \",
          \"   .........  \",
          \"  ...+...+... \",
          \"  ....+.+.... \",
          \"  .....+..... \",
          \"  ....+.+.... \",
          \"  ...+...+... \",
          \"   .........  \",
          \"    .......   \",
          \"     .....    \"};"
          :ascent center :mask (heuristic t) :margin 0)
 img2 '(image :type xpm :data "/* XPM */
          static char * close_tab_xpm[] = {
          \"14 11 3 1\",
          \"       c None\",
          \".      c #000000\",
          \"+      c #FFFFFF\",
          \"     .....    \",
          \"    .......   \",
          \"   .........  \",
          \"  ........... \",
          \"  ........... \",
          \"  +++++++++++ \",
          \"  ........... \",
          \"  ........... \",
          \"   .........  \",
          \"    .......   \",
          \"     .....    \"};"
          :ascent center :mask (heuristic t) :margin 0))

(setq header-line-format
      (propertize
       " "
       'help-echo (lambda (win obj pos)
            (destructuring-bind (frame x . y) (mouse-position)
              (setq header-line-format (propertize header-line-format
                               'display
                               (if (> y 0)
                                   img1
                                 img2)))

              (force-mode-line-update)))
       'display
       img1))

What do you think?

  • Thank you for taking the time to post this helpful answer. If we change redraw-frame to force-mode-line-update (without the optional argument), that will limit the redisplay to just the modeline/headline of this particular window). Using your example, can you think of way to have the image/XPM change back to its original state once the mouse ceases to hover over it? – lawlist Nov 1 '18 at 16:07
  • see my new answer above. – John Kitchin Nov 1 '18 at 16:25
  • Thank you for the updated draft. I tried with Emacs 26.1 for OSX [GNU Emacs 26.1 (build 1, x86_64-apple-darwin10.8.0, NS appkit-1038.36 Version 10.6.8 (Build 10K549)) of 2018-05-28]. However, the latest draft does not appear to do anything on mouse-over. I'll have some time this weekend to experiment, but have a large project in the office to complete .... – lawlist Nov 1 '18 at 20:54
  • It only works for me when the mouse is over the image in the header. It doesn't work everywhere in the header. – John Kitchin Nov 2 '18 at 0:03
1

If you can use unicode characters and mouse clicks, here is another approach.

(require 's)
(setq header-line-format
      (propertize "♥                                                                       "
          'help-echo "Click to toggle."
          'keymap (let ((map (make-sparse-keymap)))
                (define-key map [header-line mouse-1]
                  (lambda ()
                (interactive)
                (if (s-starts-with? "♥" header-line-format)
                    (setf (substring header-line-format 0 1) "♣")
                  (setf (substring header-line-format 0 1) "♥"))
                (force-mode-line-update)))
                map)))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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