Subject: Re: bug#79046: (WIP) [PATCH] tab-line: Change close button appearance for modified tabs

From: Elijah Gabe Pérez <[email-masked]>
Date: Wed, 30 Jul 2025 22:20:03 -0600

Juri Linkov <[email-masked]> writes:

[ Sorry i forgot to reply. ]

>> The patch is not yet finished, this feature is currently "hardcoded"
>> (the only way to disable this is removing `tab-line-tab-face-modified`
>> from `tab-line-tab-face-functions`).
>>
>> I would like any suggestion, if should this have an option for disable
>> this.

> There is an option `tab-line-tab-name-format-function`,
> but it would be too cumbersome for users to override the
> whole `tab-line-tab-name-format-default` to be able to
> disable only the close button.

Indeed.  I've added an option for allow disable this.

> Maybe it would be ok for users to customize the icon with
>
>   M-x customize-icon RET tab-line-close-modified RET
>
> and copy attributes from `tab-line-close-button`.

AFAIK the icon should be customizable.

I'm sending here the finished version of the patch:

>From 7d09ad27594bf5d1c5df42a5f7af3ea5f664674f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?El=C3=ADas=20Gabriel=20P=C3=A9rez?= <[email-masked]>
Date: Sat, 12 Jul 2025 13:16:50 -0600
Subject: [PATCH] tab-line: Add close button for modified tabs (bug#79046)

* etc/NEWS: Announce changes.
* etc/images/tabs/close-modified.xpm: New image.
* etc/images/tabs/README (Files): Add button icon name to README.
* lisp/tab-line.el (tab-line-close-modified-button-show): New
user option.
(tab-line-close-modified): Define new icon.
(tab-line-close-modified-button): New variable.
(tab-line-tab-modified-p): New function.
(tab-line-tab-name-format-default): Rework.
(tab-line-tab-face-modified): Use 'tab-line-tab-modified-p'.
---
 etc/NEWS                           |  5 ++++
 etc/images/tabs/README             |  4 +++
 etc/images/tabs/close-modified.xpm | 16 +++++++++++
 lisp/tab-line.el                   | 46 +++++++++++++++++++++++++++---
 4 files changed, 67 insertions(+), 4 deletions(-)
 create mode 100644 etc/images/tabs/close-modified.xpm

diff --git a/etc/NEWS b/etc/NEWS
index b5349d39000..957ec9f875d 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -418,6 +418,11 @@ This user option controls where 'tab-line-mode' should not be enabled in
 a buffer.  The value must be a condition which is passed to
 'buffer-match-p'.
 
+---
+*** New user option 'tab-line-close-modified-button-show'
+This user option, if non-nil (the default), the tab close button will
+change its appearance whether the tab buffer has been modified.
+
 ** Project
 
 ---
diff --git a/etc/images/tabs/README b/etc/images/tabs/README
index c3bab00d56c..2a68577951f 100644
--- a/etc/images/tabs/README
+++ b/etc/images/tabs/README
@@ -4,5 +4,9 @@ COPYRIGHT AND LICENSE INFORMATION FOR IMAGE FILES
 
 Files: close.xpm new.xpm left-arrow.xpm right-arrow.xpm
 Author: Juri Linkov <[email-masked]>
+
+Files: close-modified.xpm
+Author: Elías Gabriel Pérez <[email-masked]>
+
 Copyright (C) 2019-2025 Free Software Foundation, Inc.
 License: GNU General Public License version 3 or later (see COPYING)
diff --git a/etc/images/tabs/close-modified.xpm b/etc/images/tabs/close-modified.xpm
new file mode 100644
index 00000000000..44b45d49011
--- /dev/null
+++ b/etc/images/tabs/close-modified.xpm
@@ -0,0 +1,16 @@
+/* XPM */
+static char *dummy[]={
+"9 9 4 1",
+". c None",
+"b c #000000",
+"a c #808080",
+"# c #bfbfbf",
+"..#####..",
+".#######.",
+"###aaa###",
+"##abbba##",
+"##abbba##",
+"##abbba##",
+"###aaa###",
+".#######.",
+"..#####.."};
diff --git a/lisp/tab-line.el b/lisp/tab-line.el
index b93aef2dd53..c2d051835fb 100644
--- a/lisp/tab-line.el
+++ b/lisp/tab-line.el
@@ -251,6 +251,36 @@ tab-line-close-button
               'help-echo "Click to close tab")
   "Button for closing the clicked tab.")
 
+(defcustom tab-line-close-modified-button-show t
+  "If non-nil, the close button appearance will change when its buffer is modified."
+  :type 'boolean
+  :initialize 'custom-initialize-default
+  :set (lambda (sym val)
+         (set-default sym val)
+         (force-mode-line-update t))
+  :group 'tab-line
+  :version "31.1")
+
+(define-icon tab-line-close-modified nil
+  `((image "symbols/dot_medium_16.svg" "tabs/close-modified.xpm"
+           :face shadow
+           :height (1 . em)
+           :margin (2 . 0)
+           :ascent center)
+    (symbol ,(concat " " [#x2022])) ; bullet
+    (text " *"))
+  "Icon for closing the clicked tab when tab is modified."
+  :version "31.1"
+  :help-echo "Click to close tab")
+
+(defvar tab-line-close-modified-button
+  (propertize (icon-string 'tab-line-close-modified)
+              'rear-nonsticky nil
+              'keymap tab-line-tab-close-map
+              'mouse-face 'tab-line-close-highlight
+              'help-echo "Click to close tab")
+  "Button for closing the clicked tab when tab is modified.")
+
 (define-icon tab-line-left nil
   `((image "symbols/chevron_left_16.svg" "tabs/left-arrow.xpm"
            :face shadow
@@ -387,6 +417,12 @@ tab-line-tabs-mode-buffers
                                            (derived-mode-p mode)))
                              (funcall tab-line-tabs-buffer-list-function)))))
 
+(defun tab-line-tab-modified-p (tab buffer-p)
+  "Return t if TAB is modified."
+  (let ((buffer (if buffer-p tab (cdr (assq 'buffer tab)))))
+    (when (and buffer (buffer-file-name buffer) (buffer-modified-p buffer))
+      t)))
+
 (defcustom tab-line-tabs-buffer-group-function
   #'tab-line-tabs-buffer-group-by-mode
   "Function to add a buffer to the appropriate group of tabs.
@@ -619,7 +655,10 @@ tab-line-tab-name-format-default
                                          (not (eq tab-line-close-button-show
                                                   (if selected-p 'non-selected
                                                     'selected)))
-                                         tab-line-close-button)
+                                         (if (and tab-line-close-modified-button-show
+                                                  (tab-line-tab-modified-p tab buffer-p))
+                                             tab-line-close-modified-button
+                                           tab-line-close-button))
                                     "")))
                      (setq close (copy-sequence close))
                      ;; Don't overwrite the icon face
@@ -687,9 +726,8 @@ tab-line-tab-face-modified
 When TAB is a modified, file-backed buffer, make FACE inherit
 from `tab-line-tab-modified'.  For use in
 `tab-line-tab-face-functions'."
-  (let ((buffer (if buffer-p tab (cdr (assq 'buffer tab)))))
-    (when (and buffer (buffer-file-name buffer) (buffer-modified-p buffer))
-      (setf face `(:inherit (tab-line-tab-modified ,face)))))
+  (when (tab-line-tab-modified-p tab buffer-p)
+    (setf face `(:inherit (tab-line-tab-modified ,face))))
   face)
 
 (defun tab-line-tab-face-group (tab _tabs face _buffer-p _selected-p)
-- 
2.50.1

-- 
- E.G via Gnus and Org.