From: shipmints Date: Sat, 22 Feb 2025 10:13:05 +0000 (-0500) Subject: mouse-face properties on tab-bar tab captions (bug#76394) X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=a2ae0e04cbdd8ebd554c2115af102551a2134be5;p=emacs.git mouse-face properties on tab-bar tab captions (bug#76394) * etc/NEWS: Announce 'tab-bar' 'mouse-face' support. * src/xdisp.c (note_tab_bar_highlight): Handle mouse-face property. * lisp/tab-bar.el (tab-bar-tab-highlight): New face. (tab-bar-tab-name-format-mouse-face): New function adds the 'mouse-face' 'tab-bar-tab-highlight' to the tab name. (tab-bar-tab-name-format-functions): Add 'tab-bar-tab-name-format-mouse-face'. (cherry picked from commit efd483cf0ecde3b0545a9eb39cc3fa9483fad76c) --- diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el index d479b0c9f09..a53b33686c1 100644 --- a/lisp/tab-bar.el +++ b/lisp/tab-bar.el @@ -85,6 +85,16 @@ :version "28.1" :group 'tab-bar-faces) +(defface tab-bar-tab-highlight + '((((class color) (min-colors 88)) + :box (:line-width 1 :style released-button) + :background "grey85" + :foreground "black") + (t :inverse-video nil)) + "Tab bar face for highlighting." + :version "31.1" + :group 'tab-bar-faces) + (defvar tab-bar-mode-map (make-sparse-keymap) @@ -886,10 +896,15 @@ It uses the function `tab-bar-tab-face-function'." 0 (length name) (funcall tab-bar-tab-face-function tab) t name) name) +(defun tab-bar-tab-name-format-mouse-face (name _tab _i) + "Apply the `mouse-face' `tab-bar-tab-highlight' to the tab name." + (propertize name 'mouse-face 'tab-bar-tab-highlight)) + (defcustom tab-bar-tab-name-format-functions '(tab-bar-tab-name-format-hints tab-bar-tab-name-format-close-button - tab-bar-tab-name-format-face) + tab-bar-tab-name-format-face + tab-bar-tab-name-format-mouse-face) "Functions called to modify the tab name. Each function is called with three arguments: the name returned by the previously called modifier, the tab and its number. @@ -899,6 +914,7 @@ It should return the formatted tab name to display in the tab bar." (function-item tab-bar-tab-name-format-hints) (function-item tab-bar-tab-name-format-close-button) (function-item tab-bar-tab-name-format-face) + (function-item tab-bar-tab-name-format-mouse-face) (function :tag "Custom function"))) :group 'tab-bar :version "30.1") diff --git a/src/xdisp.c b/src/xdisp.c index 2ecfeec215f..89e8841d821 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -15084,7 +15084,6 @@ handle_tab_bar_click (struct frame *f, int x, int y, bool down_p, return Fcons (Qtab_bar, Fcons (caption, make_fixnum (0))); } - /* Possibly highlight a tab-bar item on frame F when mouse moves to tab-bar window-relative coordinates X/Y. Called from note_mouse_highlight. */ @@ -15096,8 +15095,7 @@ note_tab_bar_highlight (struct frame *f, int x, int y) struct window *w = XWINDOW (window); Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f); int hpos, vpos; - struct glyph *glyph; - struct glyph_row *row; + struct glyph *glyph = NULL; int i; Lisp_Object enabled_p; int prop_idx; @@ -15143,25 +15141,124 @@ note_tab_bar_highlight (struct frame *f, int x, int y) /* If tab-bar item is not enabled, don't highlight it. */ enabled_p = AREF (f->tab_bar_items, prop_idx + TAB_BAR_ITEM_ENABLED_P); + if (!NILP (enabled_p) && !NILP (Vmouse_highlight)) { - /* Compute the x-position of the glyph. In front and past the - image is a space. We include this in the highlighted area. */ + struct glyph_row *row = NULL; + struct glyph *row_start_glyph = NULL; + struct glyph *tmp_glyph; + int total_pixel_width; + Lisp_Object string; + Lisp_Object mouse_face; + int mouse_face_id = -1; + int hpos0, hpos_caption; + row = MATRIX_ROW (w->current_matrix, vpos); - for (i = x = 0; i < hpos; ++i) - x += row->glyphs[TEXT_AREA][i].pixel_width; + /* display_tab_bar does not yet support R2L. */ + eassert (!row->reversed_p); + row_start_glyph = row->glyphs[TEXT_AREA]; + + string = AREF (f->tab_bar_items, prop_idx + TAB_BAR_ITEM_CAPTION); + if (STRINGP (string)) + { + /* Compute starting column of the tab-bar-item to adjust col + of the mouse face relative to row_start_glyph. + + tab_bar_item_info does not contain the absolute starting + offset of the item. We compute it by looking backwards + until we find a glyph that belongs to a previous tab bar + item, or if this is the first item. */ + hpos0 = hpos + 1; + int tmp_prop_idx; + bool tmp_bool; + for (tmp_glyph = glyph; + tmp_glyph >= row_start_glyph; + tmp_glyph--) + { + if (!tab_bar_item_info (f, tmp_glyph, &tmp_prop_idx, &tmp_bool) + || tmp_prop_idx != prop_idx) + break; /* Just before the beginning of this item. */ + else + --hpos0; + } - /* Record this as the current active region. */ - hlinfo->mouse_face_beg_col = hpos; - hlinfo->mouse_face_beg_row = vpos; - hlinfo->mouse_face_beg_x = x; - hlinfo->mouse_face_past_end = false; + /* Offset into the caption vs. the row. */ + hpos_caption = hpos - hpos0; - hlinfo->mouse_face_end_col = hpos + 1; - hlinfo->mouse_face_end_row = vpos; - hlinfo->mouse_face_end_x = x + glyph->pixel_width; - hlinfo->mouse_face_window = window; - hlinfo->mouse_face_face_id = TAB_BAR_FACE_ID; + mouse_face = Fget_text_property (make_fixnum (hpos_caption), + Qmouse_face, string); + if (!NILP (mouse_face)) + { + mouse_face_id = lookup_named_face (w, f, mouse_face, false); + if (mouse_face_id < 0) + mouse_face_id = compute_char_face (f, ' ', mouse_face); + draw = DRAW_MOUSE_FACE; + } + } + + if (draw == DRAW_MOUSE_FACE) + { + Lisp_Object b, e; + ptrdiff_t begpos, endpos; + int beg_x, end_x; + + /* Search for mouse-face boundaries. */ + b = Fprevious_single_property_change (make_fixnum (hpos_caption + 1), + Qmouse_face, string, Qnil); + if (NILP (b)) + begpos = 0; + else + begpos = XFIXNUM (b); + e = Fnext_single_property_change (make_fixnum (begpos), Qmouse_face, string, Qnil); + if (NILP (e)) + endpos = SCHARS (string); + else + endpos = XFIXNUM (e); + + /* Compute the starting and ending pixel coordinates */ + for (i = beg_x = 0; + i < hpos0 + begpos; ++i) + beg_x += row->glyphs[TEXT_AREA][i].pixel_width; + for (end_x = 0, + i = hpos0 + begpos; + i < hpos0 + endpos; ++i) + end_x += row->glyphs[TEXT_AREA][i].pixel_width; + + if ( EQ (window, hlinfo->mouse_face_window) + && (hlinfo->mouse_face_beg_col <= hpos + && hpos < hlinfo->mouse_face_end_col) + && hlinfo->mouse_face_beg_row == vpos ) + return; + + hlinfo->mouse_face_window = window; + hlinfo->mouse_face_face_id = mouse_face_id; + hlinfo->mouse_face_beg_row = vpos; + hlinfo->mouse_face_end_row = vpos; + hlinfo->mouse_face_past_end = false; + hlinfo->mouse_face_beg_col = hpos0 + begpos; + hlinfo->mouse_face_end_col = hpos0 + endpos; + hlinfo->mouse_face_beg_x = beg_x; + hlinfo->mouse_face_end_x = end_x; + } + else + { + /* Compute the x-position of the glyph. In front and past the + image is a space. We include this in the highlighted area. */ + for (i = x = 0; i < hpos; ++i) + x += row->glyphs[TEXT_AREA][i].pixel_width; + total_pixel_width = glyph->pixel_width; + + hlinfo->mouse_face_face_id = TAB_BAR_FACE_ID; + hlinfo->mouse_face_beg_col = hpos; + hlinfo->mouse_face_beg_row = vpos; + hlinfo->mouse_face_beg_x = x; + hlinfo->mouse_face_past_end = false; + + hlinfo->mouse_face_end_col = hpos + 1; + hlinfo->mouse_face_end_row = vpos; + hlinfo->mouse_face_end_x = x + total_pixel_width; + hlinfo->mouse_face_window = window; + } /* Display it as active. */ show_mouse_face (hlinfo, draw, true);