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)))
lawlist
  • 18,826
  • 5
  • 37
  • 118

2 Answers2

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?

John Kitchin
  • 11,555
  • 1
  • 19
  • 41
  • 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 01 '18 at 16:07
  • see my new answer above. – John Kitchin Nov 01 '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 01 '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 02 '18 at 00: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)))
John Kitchin
  • 11,555
  • 1
  • 19
  • 41