diff options
| author | Juri Linkov | 2022-11-04 09:47:06 +0200 |
|---|---|---|
| committer | Juri Linkov | 2022-11-04 09:47:59 +0200 |
| commit | ca3763af5cc2758ec71700029558e6ecc4379ea9 (patch) | |
| tree | abf1cebb10119b01b266c152e422219d803240ae | |
| parent | 4fa8f57cc627166f4f7f1a915bb24923f413a3d0 (diff) | |
| download | emacs-ca3763af5cc2758ec71700029558e6ecc4379ea9.tar.gz emacs-ca3763af5cc2758ec71700029558e6ecc4379ea9.zip | |
* lisp/tab-bar.el (tab-bar-fixed-width): New user option.
(tab-bar-fixed-width-max): New user option.
(tab-bar-fixed-width-min): New variable.
(tab-bar-fixed-width-faces): New variable.
(tab-bar--fixed-width-hash): New function.
(tab-bar-make-keymap-1): Use 'tab-bar-fixed-width'.
https://lists.gnu.org/archive/html/emacs-devel/2022-10/msg02067.html
| -rw-r--r-- | etc/NEWS | 5 | ||||
| -rw-r--r-- | lisp/tab-bar.el | 112 |
2 files changed, 116 insertions, 1 deletions
| @@ -1077,6 +1077,11 @@ the corresponding deleted frame. | |||
| 1077 | ** Tab Bars and Tab Lines | 1077 | ** Tab Bars and Tab Lines |
| 1078 | 1078 | ||
| 1079 | --- | 1079 | --- |
| 1080 | *** New user option 'tab-bar-fixed-width' to automatically resize tabs. | ||
| 1081 | Another option 'tab-bar-fixed-width-max' defines the maximum tab width | ||
| 1082 | that by default is 220 pixels on GUI and 20 characters on a tty. | ||
| 1083 | |||
| 1084 | --- | ||
| 1080 | *** 'C-x t RET' creates a new tab when the provided tab name doesn't exist. | 1085 | *** 'C-x t RET' creates a new tab when the provided tab name doesn't exist. |
| 1081 | 1086 | ||
| 1082 | --- | 1087 | --- |
diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el index 2032689c65d..810cb4edd7f 100644 --- a/lisp/tab-bar.el +++ b/lisp/tab-bar.el | |||
| @@ -963,7 +963,117 @@ on the tab bar instead." | |||
| 963 | 963 | ||
| 964 | (defun tab-bar-make-keymap-1 () | 964 | (defun tab-bar-make-keymap-1 () |
| 965 | "Generate an actual keymap from `tab-bar-map', without caching." | 965 | "Generate an actual keymap from `tab-bar-map', without caching." |
| 966 | (append tab-bar-map (tab-bar-format-list tab-bar-format))) | 966 | (let ((items (tab-bar-format-list tab-bar-format))) |
| 967 | (when tab-bar-fixed-width | ||
| 968 | (setq items (tab-bar-fixed-width items))) | ||
| 969 | (append tab-bar-map items))) | ||
| 970 | |||
| 971 | |||
| 972 | (defcustom tab-bar-fixed-width t | ||
| 973 | "Automatically resize tabs on the tab bar to the fixed width. | ||
| 974 | This variable is intended to solve two problems. When switching buffers | ||
| 975 | on the current tab, the tab changes its name to buffer names of | ||
| 976 | various lengths, thus resizing the tab and shifting the tab positions | ||
| 977 | on the tab bar. But with the fixed width, the size of the tab name | ||
| 978 | doesn't change when the tab name changes, thus keeping the fixed | ||
| 979 | tab bar layout. The second problem solved by this variable is to prevent | ||
| 980 | wrapping the long tab bar to the second line, thus keeping the height of | ||
| 981 | the tab bar always fixed to one line. | ||
| 982 | |||
| 983 | The maximum tab width is defined by the variable `tab-bar-fixed-width-max'." | ||
| 984 | :type 'boolean | ||
| 985 | :group 'tab-bar | ||
| 986 | :version "29.1") | ||
| 987 | |||
| 988 | (defcustom tab-bar-fixed-width-max '(220 . 20) | ||
| 989 | "Maximum number of pixels or characters allowed for the tab name width. | ||
| 990 | The car of the cons cell is the maximum number of pixels when used on | ||
| 991 | a GUI session. The cdr of the cons cell defines the maximum number of | ||
| 992 | characters when used on a tty. When set to nil, there is no limit | ||
| 993 | on maximum width, and tabs are resized evenly to the whole width | ||
| 994 | of the tab bar when `tab-bar-fixed-width' is non-nil." | ||
| 995 | :type '(choice | ||
| 996 | (const :tag "No limit" nil) | ||
| 997 | (cons (integer :tag "Max width (pixels)" :value 220) | ||
| 998 | (integer :tag "Max width (chars)" :value 20))) | ||
| 999 | :group 'tab-bar | ||
| 1000 | :version "29.1") | ||
| 1001 | |||
| 1002 | (defvar tab-bar-fixed-width-min '(20 . 2) | ||
| 1003 | "Minimum number of pixels or characters allowed for the tab name width. | ||
| 1004 | It's not recommended to change this value since with a bigger value, the | ||
| 1005 | tab bar might wrap to the second line.") | ||
| 1006 | |||
| 1007 | (defvar tab-bar-fixed-width-faces | ||
| 1008 | '( tab-bar-tab tab-bar-tab-inactive | ||
| 1009 | tab-bar-tab-ungrouped | ||
| 1010 | tab-bar-tab-group-inactive) | ||
| 1011 | "Resize tabs only with these faces.") | ||
| 1012 | |||
| 1013 | (defvar tab-bar--fixed-width-hash nil | ||
| 1014 | "Memoization table for `tab-bar-fixed-width'.") | ||
| 1015 | |||
| 1016 | (defun tab-bar-fixed-width (items) | ||
| 1017 | "Return tab-bar items with resized tab names." | ||
| 1018 | (unless tab-bar--fixed-width-hash | ||
| 1019 | (define-hash-table-test 'tab-bar--fixed-width-hash-test | ||
| 1020 | #'equal-including-properties | ||
| 1021 | #'sxhash-equal-including-properties) | ||
| 1022 | (setq tab-bar--fixed-width-hash | ||
| 1023 | (make-hash-table :test 'tab-bar--fixed-width-hash-test))) | ||
| 1024 | (let ((tabs nil) ;; list of resizable tabs | ||
| 1025 | (non-tabs "") ;; concatenated names of non-resizable tabs | ||
| 1026 | (width 0)) ;; resize tab names to this width | ||
| 1027 | (dolist (item items) | ||
| 1028 | (when (and (eq (nth 1 item) 'menu-item) (stringp (nth 2 item))) | ||
| 1029 | (if (memq (get-text-property 0 'face (nth 2 item)) | ||
| 1030 | tab-bar-fixed-width-faces) | ||
| 1031 | (push item tabs) | ||
| 1032 | (unless (eq (nth 0 item) 'align-right) | ||
| 1033 | (setq non-tabs (concat non-tabs (nth 2 item))))))) | ||
| 1034 | (when tabs | ||
| 1035 | (setq width (/ (- (frame-pixel-width) | ||
| 1036 | (string-pixel-width | ||
| 1037 | (propertize non-tabs 'face 'tab-bar))) | ||
| 1038 | (length tabs))) | ||
| 1039 | (when tab-bar-fixed-width-min | ||
| 1040 | (setq width (max width (if window-system | ||
| 1041 | (car tab-bar-fixed-width-min) | ||
| 1042 | (cdr tab-bar-fixed-width-min))))) | ||
| 1043 | (when tab-bar-fixed-width-max | ||
| 1044 | (setq width (min width (if window-system | ||
| 1045 | (car tab-bar-fixed-width-max) | ||
| 1046 | (cdr tab-bar-fixed-width-max))))) | ||
| 1047 | (dolist (item tabs) | ||
| 1048 | (setf (nth 2 item) | ||
| 1049 | (with-memoization (gethash (cons width (nth 2 item)) | ||
| 1050 | tab-bar--fixed-width-hash) | ||
| 1051 | (let* ((name (nth 2 item)) | ||
| 1052 | (len (length name)) | ||
| 1053 | (close-p (get-text-property (1- len) 'close-tab name)) | ||
| 1054 | (pixel-width (string-pixel-width | ||
| 1055 | (propertize name 'face 'tab-bar-tab)))) | ||
| 1056 | (cond | ||
| 1057 | ((< pixel-width width) | ||
| 1058 | (let ((space (apply 'propertize " " (text-properties-at 0 name))) | ||
| 1059 | (ins-pos (- len (if close-p 1 0)))) | ||
| 1060 | (while (< pixel-width width) | ||
| 1061 | (setf (substring name ins-pos ins-pos) space) | ||
| 1062 | (setq pixel-width (string-pixel-width | ||
| 1063 | (propertize name 'face 'tab-bar-tab)))))) | ||
| 1064 | ((> pixel-width width) | ||
| 1065 | (let (del-pos) | ||
| 1066 | (while (> pixel-width width) | ||
| 1067 | (setq len (length name) | ||
| 1068 | del-pos (- len (if close-p 1 0))) | ||
| 1069 | (setf (substring name (1- del-pos) del-pos) "") | ||
| 1070 | (setq pixel-width (string-pixel-width | ||
| 1071 | (propertize name 'face 'tab-bar-tab)))) | ||
| 1072 | (add-face-text-property (max (- del-pos 3) 1) | ||
| 1073 | (1- del-pos) | ||
| 1074 | 'shadow nil name)))) | ||
| 1075 | name))))) | ||
| 1076 | items)) | ||
| 967 | 1077 | ||
| 968 | 1078 | ||
| 969 | ;; Some window-configuration parameters don't need to be persistent. | 1079 | ;; Some window-configuration parameters don't need to be persistent. |