From 0384600a21fb363e623afa658d1e66a54f79d2fb Mon Sep 17 00:00:00 2001 From: Juri Linkov Date: Tue, 14 Apr 2026 20:14:15 +0300 Subject: [PATCH] New faces 'tab-line-active' and 'tab-line-inactive' (bug#80742) This is intended to parallel the 'mode-line-active/inactive' and 'header-line-active/inactive' distinction. * doc/emacs/display.texi (Standard Faces): Document the new faces. * lisp/faces.el (tab-line-active, tab-line-inactive): Add new faces. * lisp/tab-line.el (tab-line-faces): Add new faces to MEMBERS arg of 'defgroup'. (tab-line-auto-hscroll): Use one of the faces depending on 'mode-line-window-selected-p'. * src/dispextern.h (CURRENT_TAB_LINE_ACTIVE_FACE_ID_3) (CURRENT_TAB_LINE_ACTIVE_FACE_ID): New macros based on header-line equivalents. (CURRENT_TAB_LINE_HEIGHT): Use CURRENT_TAB_LINE_ACTIVE_FACE_ID. (face_id): Use TAB_LINE_ACTIVE_FACE_ID and TAB_LINE_INACTIVE_FACE_ID instead of TAB_LINE_FACE_ID. * src/xdisp.c (window_box_height, pos_visible_p, init_iterator) (window_text_pixel_size, display_mode_lines, display_mode_line) (format-mode-line): Replace all uses of TAB_LINE_FACE_ID with either a new macro or the new face IDs. * src/xfaces.c (lookup_basic_face, realize_basic_faces): Map new face IDs to their lisp symbols. (syms_of_xfaces): New lisp symbols. --- doc/emacs/display.texi | 16 ++++++++++++++++ etc/NEWS | 6 ++++++ lisp/faces.el | 17 ++++++++++++++++- lisp/tab-line.el | 17 ++++++++++++----- src/dispextern.h | 26 ++++++++++++++++++++++++-- src/xdisp.c | 28 ++++++++++++++++------------ src/xfaces.c | 8 ++++++-- 7 files changed, 96 insertions(+), 22 deletions(-) diff --git a/doc/emacs/display.texi b/doc/emacs/display.texi index dde6cc4f1b6..a64c009f2d9 100644 --- a/doc/emacs/display.texi +++ b/doc/emacs/display.texi @@ -811,6 +811,22 @@ Similar to @code{mode-line} for a window's tab line, which appears at the top of a window with tabs representing window buffers. @xref{Tab Line}. +The @code{tab-line-active} and @code{tab-line-inactive} faces (which +are the ones actually used on the tab lines) inherit from this face. + +@cindex faces for tab lines +@item tab-line-active +Like @code{tab-line}, but used for the tab line of the currently +selected window. This face inherits from @code{tab-line}, so changes +in that face affect tab lines in all windows. + +@cindex @code{tab-line-inactive} face +@item tab-line-inactive +Like @code{tab-line}, but used for tab lines of the windows other +than the selected one (if those windows have a tab line). This face +inherits from @code{tab-line}, so changes in that face affect tab +lines in all windows. + @cindex @code{vertical-border} face @item vertical-border This face is used for the vertical divider between windows on text diff --git a/etc/NEWS b/etc/NEWS index 3639d1b47e8..3c02002d0ff 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -658,6 +658,12 @@ will change its appearance if the tab buffer has been modified. This user option controls which buffers should appear in the tab line. By default, this is set to not filter the buffers. ++++ +*** New faces 'tab-line-active' and 'tab-line-inactive'. +These inherit from the 'tab-line' face, but the faces actually used +on the tab lines are now these two: the selected window uses +'tab-line-active', non-selected windows use 'tab-line-inactive'. + ** Help +++ diff --git a/lisp/faces.el b/lisp/faces.el index 6fe94927290..97620822b88 100644 --- a/lisp/faces.el +++ b/lisp/faces.el @@ -3005,7 +3005,22 @@ Note: Other faces cannot inherit from the cursor face." :background "grey") (t :inverse-video t)) - "Tab line face." + "Basic tab line face. +See `tab-line-active' and `tab-line-inactive' for the faces +used on tab lines." + :version "31.1" + :group 'basic-faces) + +(defface tab-line-active + '((t :inherit tab-line)) + "Face for the selected tab line. +This inherits from the `tab-line' face." + :version "31.1" + :group 'basic-faces) + +(defface tab-line-inactive + '((t :inherit tab-line)) + "Basic tab line face for non-selected windows." :version "31.1" :group 'basic-faces) diff --git a/lisp/tab-line.el b/lisp/tab-line.el index 832b2b74437..524799e445d 100644 --- a/lisp/tab-line.el +++ b/lisp/tab-line.el @@ -54,7 +54,11 @@ is selected." :group 'tab-line :version "28.1") -(defgroup tab-line-faces '((tab-line custom-face)) ; tab-line is defined in faces.el +(defgroup tab-line-faces + ;; These faces are defined in faces.el + '((tab-line custom-face) + (tab-line-active custom-face) + (tab-line-inactive custom-face)) "Faces used in the tab line." :group 'tab-line :group 'faces @@ -860,13 +864,16 @@ the selected tab visible." (with-current-buffer tab-line-auto-hscroll-buffer (let ((truncate-partial-width-windows nil) (inhibit-modification-hooks t) + (face (if (mode-line-window-selected-p) + 'tab-line-active + 'tab-line-inactive)) show-arrows) (setq truncate-lines nil word-wrap nil) (erase-buffer) (apply 'insert strings) (goto-char (point-min)) - (add-face-text-property (point-min) (point-max) 'tab-line t) + (add-face-text-property (point-min) (point-max) face t) ;; Continuation means tab-line doesn't fit completely, ;; thus scroll arrows are needed for scrolling. (setq show-arrows (> (vertical-motion 1) 0)) @@ -888,7 +895,7 @@ the selected tab visible." (erase-buffer) (apply 'insert (reverse (seq-subseq strings 0 (1+ selected)))) (goto-char (point-min)) - (add-face-text-property (point-min) (point-max) 'tab-line) + (add-face-text-property (point-min) (point-max) face) (if (> (vertical-motion 1) 0) (let* ((point (previous-single-property-change (point) 'tab)) (tab-prop (when point @@ -909,13 +916,13 @@ the selected tab visible." (erase-buffer) (apply 'insert (seq-subseq strings (truncate hscroll) (1+ selected))) (goto-char (point-min)) - (add-face-text-property (point-min) (point-max) 'tab-line) + (add-face-text-property (point-min) (point-max) face) (when (> (vertical-motion 1) 0) ;; Not visible already (erase-buffer) (apply 'insert (reverse (seq-subseq strings 0 (1+ selected)))) (goto-char (point-min)) - (add-face-text-property (point-min) (point-max) 'tab-line) + (add-face-text-property (point-min) (point-max) face) (when (> (vertical-motion 1) 0) (let* ((point (previous-single-property-change (point) 'tab)) (tab-prop (when point diff --git a/src/dispextern.h b/src/dispextern.h index 55f4118d676..16565b8ad18 100644 --- a/src/dispextern.h +++ b/src/dispextern.h @@ -1610,6 +1610,27 @@ struct glyph_string : estimate_mode_line_height \ (XFRAME ((W)->frame), CURRENT_HEADER_LINE_ACTIVE_FACE_ID (W))))) +/* Return the desired face id for the tab line of a window, depending + on whether the window is selected or not, or if the window is the + scrolling window for the currently active minibuffer window. */ + +#define CURRENT_TAB_LINE_ACTIVE_FACE_ID_3(SELW, MBW, SCRW) \ + ((!mode_line_in_non_selected_windows \ + || (SELW) == XWINDOW (selected_window) \ + || (minibuf_level > 0 \ + && !NILP (minibuf_selected_window) \ + && (MBW) == XWINDOW (minibuf_window) \ + && (SCRW) == XWINDOW (minibuf_selected_window))) \ + ? TAB_LINE_ACTIVE_FACE_ID \ + : TAB_LINE_INACTIVE_FACE_ID) + +/* Return the desired face id for the tab line of window W. */ + +#define CURRENT_TAB_LINE_ACTIVE_FACE_ID(W) \ + CURRENT_TAB_LINE_ACTIVE_FACE_ID_3(W, \ + XWINDOW (selected_window), \ + W) + /* Return the current height of the tab line of window W. If not known from W->tab_line_height, look at W's current glyph matrix, or return an estimation based on the height of the font of the face `tab-line'. */ @@ -1621,7 +1642,7 @@ struct glyph_string = (MATRIX_TAB_LINE_HEIGHT ((W)->current_matrix) \ ? MATRIX_TAB_LINE_HEIGHT ((W)->current_matrix) \ : estimate_mode_line_height \ - (XFRAME ((W)->frame), TAB_LINE_FACE_ID)))) + (XFRAME ((W)->frame), CURRENT_TAB_LINE_ACTIVE_FACE_ID (W))))) /* Return the height of the desired mode line of window W. */ @@ -1943,7 +1964,8 @@ enum face_id INTERNAL_BORDER_FACE_ID, CHILD_FRAME_BORDER_FACE_ID, TAB_BAR_FACE_ID, - TAB_LINE_FACE_ID, + TAB_LINE_ACTIVE_FACE_ID, + TAB_LINE_INACTIVE_FACE_ID, BASIC_FACE_ID_SENTINEL }; diff --git a/src/xdisp.c b/src/xdisp.c index 370c3ec30c1..281f04e4132 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -1343,7 +1343,7 @@ window_box_height (struct window *w) if (tl_row && tl_row->mode_line_p) height -= tl_row->height; else - height -= estimate_mode_line_height (f, TAB_LINE_FACE_ID); + height -= estimate_mode_line_height (f, CURRENT_TAB_LINE_ACTIVE_FACE_ID (w)); } } @@ -1743,7 +1743,7 @@ pos_visible_p (struct window *w, ptrdiff_t charpos, int *x, int *y, = window_parameter (w, Qtab_line_format); w->tab_line_height - = display_mode_line (w, TAB_LINE_FACE_ID, + = display_mode_line (w, CURRENT_TAB_LINE_ACTIVE_FACE_ID (w), NILP (window_tab_line_format) ? BVAR (current_buffer, tab_line_format) : window_tab_line_format); @@ -3285,7 +3285,8 @@ init_iterator (struct it *it, struct window *w, if (base_face_id == MODE_LINE_ACTIVE_FACE_ID || base_face_id == MODE_LINE_INACTIVE_FACE_ID) row = MATRIX_MODE_LINE_ROW (w->desired_matrix); - else if (base_face_id == TAB_LINE_FACE_ID) + else if (base_face_id == TAB_LINE_ACTIVE_FACE_ID + || base_face_id == TAB_LINE_INACTIVE_FACE_ID) row = MATRIX_TAB_LINE_ROW (w->desired_matrix); else if (base_face_id == HEADER_LINE_ACTIVE_FACE_ID || base_face_id == HEADER_LINE_INACTIVE_FACE_ID) @@ -11975,7 +11976,7 @@ window_text_pixel_size (Lisp_Object window, Lisp_Object from, Lisp_Object to, Lisp_Object window_tab_line_format = window_parameter (w, Qtab_line_format); - y = y + display_mode_line (w, TAB_LINE_FACE_ID, + y = y + display_mode_line (w, CURRENT_TAB_LINE_ACTIVE_FACE_ID (w), NILP (window_tab_line_format) ? BVAR (current_buffer, tab_line_format) : window_tab_line_format); @@ -27946,7 +27947,7 @@ display_mode_lines (struct window *w) Lisp_Object window_tab_line_format = window_parameter (w, Qtab_line_format); - display_mode_line (w, TAB_LINE_FACE_ID, + display_mode_line (w, CURRENT_TAB_LINE_ACTIVE_FACE_ID_3 (sel_w, sel_w, w), NILP (window_tab_line_format) ? BVAR (current_buffer, tab_line_format) : window_tab_line_format); @@ -27975,10 +27976,10 @@ display_mode_lines (struct window *w) /* Display mode or header/tab line of window W. FACE_ID specifies which line to display; it is either MODE_LINE_ACTIVE_FACE_ID, - HEADER_LINE_ACTIVE_FACE_ID, HEADER_LINE_INACTIVE_FACE_ID, or - TAB_LINE_FACE_ID. FORMAT is the mode/header/tab line format to - display. Value is the pixel height of the mode/header/tab line - displayed. */ + HEADER_LINE_ACTIVE_FACE_ID, HEADER_LINE_INACTIVE_FACE_ID, + TAB_LINE_ACTIVE_FACE_ID or TAB_LINE_INACTIVE_FACE_ID. + FORMAT is the mode/header/tab line format to display. + Value is the pixel height of the mode/header/tab line displayed. */ static int display_mode_line (struct window *w, enum face_id face_id, Lisp_Object format) @@ -27994,7 +27995,8 @@ display_mode_line (struct window *w, enum face_id face_id, Lisp_Object format) prepare_desired_row (w, it.glyph_row, true); it.glyph_row->mode_line_p = true; - if (face_id == TAB_LINE_FACE_ID) + if (face_id == TAB_LINE_ACTIVE_FACE_ID + || face_id == TAB_LINE_INACTIVE_FACE_ID) { it.glyph_row->tab_line_p = true; w->desired_matrix->tab_line_p = true; @@ -28021,7 +28023,8 @@ display_mode_line (struct window *w, enum face_id face_id, Lisp_Object format) if (NILP (Vmode_line_compact) || face_id == HEADER_LINE_ACTIVE_FACE_ID || face_id == HEADER_LINE_INACTIVE_FACE_ID - || face_id == TAB_LINE_FACE_ID) + || face_id == TAB_LINE_ACTIVE_FACE_ID + || face_id == TAB_LINE_INACTIVE_FACE_ID) { mode_line_target = MODE_LINE_DISPLAY; display_mode_element (&it, 0, 0, 0, format, Qnil, false); @@ -28879,7 +28882,8 @@ are the selected window and the WINDOW's buffer). */) : EQ (face, Qmode_line_inactive) ? MODE_LINE_INACTIVE_FACE_ID : EQ (face, Qheader_line_active) ? HEADER_LINE_ACTIVE_FACE_ID : EQ (face, Qheader_line_inactive) ? HEADER_LINE_INACTIVE_FACE_ID - : EQ (face, Qtab_line) ? TAB_LINE_FACE_ID + : EQ (face, Qtab_line_active) ? TAB_LINE_ACTIVE_FACE_ID + : EQ (face, Qtab_line_inactive) ? TAB_LINE_INACTIVE_FACE_ID : EQ (face, Qtab_bar) ? TAB_BAR_FACE_ID : EQ (face, Qtool_bar) ? TOOL_BAR_FACE_ID : DEFAULT_FACE_ID; diff --git a/src/xfaces.c b/src/xfaces.c index ab0934abde2..6c4267b8afa 100644 --- a/src/xfaces.c +++ b/src/xfaces.c @@ -5195,7 +5195,8 @@ lookup_basic_face (struct window *w, struct frame *f, int face_id) case MODE_LINE_INACTIVE_FACE_ID: name = Qmode_line_inactive; break; case HEADER_LINE_ACTIVE_FACE_ID: name = Qheader_line_active; break; case HEADER_LINE_INACTIVE_FACE_ID: name = Qheader_line_inactive; break; - case TAB_LINE_FACE_ID: name = Qtab_line; break; + case TAB_LINE_ACTIVE_FACE_ID: name = Qtab_line_active; break; + case TAB_LINE_INACTIVE_FACE_ID: name = Qtab_line_inactive; break; case TAB_BAR_FACE_ID: name = Qtab_bar; break; case TOOL_BAR_FACE_ID: name = Qtool_bar; break; case FRINGE_FACE_ID: name = Qfringe; break; @@ -5975,7 +5976,8 @@ realize_basic_faces (struct frame *f) realize_named_face (f, Qinternal_border, INTERNAL_BORDER_FACE_ID); realize_named_face (f, Qchild_frame_border, CHILD_FRAME_BORDER_FACE_ID); realize_named_face (f, Qtab_bar, TAB_BAR_FACE_ID); - realize_named_face (f, Qtab_line, TAB_LINE_FACE_ID); + realize_named_face (f, Qtab_line_active, TAB_LINE_ACTIVE_FACE_ID); + realize_named_face (f, Qtab_line_inactive, TAB_LINE_INACTIVE_FACE_ID); unbind_to (count, Qnil); /* Reflect changes in the `menu' face in menu bars. */ @@ -7534,6 +7536,8 @@ syms_of_xfaces (void) DEFSYM (Qtab_bar, "tab-bar"); DEFSYM (Qfringe, "fringe"); DEFSYM (Qtab_line, "tab-line"); + DEFSYM (Qtab_line_inactive, "tab-line-inactive"); + DEFSYM (Qtab_line_active, "tab-line-active"); DEFSYM (Qheader_line, "header-line"); DEFSYM (Qheader_line_inactive, "header-line-inactive"); DEFSYM (Qheader_line_active, "header-line-active");