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'.
This commit is contained in:
shipmints 2025-02-22 05:13:05 -05:00 committed by Juri Linkov
parent 63cc542b94
commit efd483cf0e
3 changed files with 137 additions and 18 deletions

View file

@ -289,6 +289,12 @@ specified by external means.
*** New abnormal hook 'tab-bar-auto-width-functions'.
This hook allows you to control which tab-bar tabs are auto-resized.
---
*** 'mouse-face' properties are now supported on the 'tab-bar'.
'tab-bar' tab "buttons" are now highlighted when the mouse pointer
hovers over them. You can customize the new face
'tab-bar-tab-highlight'.
---
*** New abnormal hook 'tab-bar-post-undo-close-tab-functions'.
This hook allows you to operate on a reopened tab.

View file

@ -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")

View file

@ -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];
/* 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;
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.
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;
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;
}
/* Offset into the caption vs. the row. */
hpos_caption = hpos - hpos0;
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);