diff options
Diffstat (limited to 'lisp/tab-bar.el')
-rw-r--r-- | lisp/tab-bar.el | 476 |
1 files changed, 354 insertions, 122 deletions
diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el index 9383baf810a..fa22500a04e 100644 --- a/lisp/tab-bar.el +++ b/lisp/tab-bar.el @@ -31,13 +31,8 @@ ;;; Code: -(eval-when-compile - (require 'cl-lib) - (require 'seq) - (require 'icons)) - -(autoload 'cl--set-substring "cl-lib") - +(eval-when-compile (require 'icons)) +(eval-when-compile (require 'cl-lib)) (defgroup tab-bar nil "Frame-local tabs." @@ -107,7 +102,7 @@ For easier selection of tabs by their numbers, consider customizing (const hyper) (const super) (const alt)) - :initialize 'custom-initialize-default + :initialize #'custom-initialize-default :set (lambda (sym val) (set-default sym val) ;; Reenable the tab-bar with new keybindings @@ -118,23 +113,23 @@ For easier selection of tabs by their numbers, consider customizing :version "27.1") (defun tab-bar--define-keys () - "Install key bindings for switching between tabs if the user has configured them." + "Install key bindings to switch between tabs if so configured." (when tab-bar-select-tab-modifiers (global-set-key (vector (append tab-bar-select-tab-modifiers (list ?0))) - 'tab-recent) + #'tab-recent) (dotimes (i 8) (global-set-key (vector (append tab-bar-select-tab-modifiers (list (+ i 1 ?0)))) - 'tab-bar-select-tab)) + #'tab-bar-select-tab)) (global-set-key (vector (append tab-bar-select-tab-modifiers (list ?9))) - 'tab-last)) + #'tab-last)) ;; Don't override user customized key bindings (unless (global-key-binding [(control tab)]) - (global-set-key [(control tab)] 'tab-next)) + (global-set-key [(control tab)] #'tab-next)) (unless (global-key-binding [(control shift tab)]) - (global-set-key [(control shift tab)] 'tab-previous)) + (global-set-key [(control shift tab)] #'tab-previous)) (unless (global-key-binding [(control shift iso-lefttab)]) - (global-set-key [(control shift iso-lefttab)] 'tab-previous)) + (global-set-key [(control shift iso-lefttab)] #'tab-previous)) ;; Replace default value with a condition that supports displaying ;; global-mode-string in the tab bar instead of the mode line. @@ -159,9 +154,14 @@ For easier selection of tabs by their numbers, consider customizing (defun tab-bar--load-buttons () "Load the icons for the tab buttons." (require 'icons) + (declare-function icon-string "icons" (name)) + (declare-function iconp "icons" (object)) + (declare-function icons--register "icons") (unless (iconp 'tab-bar-new) (define-icon tab-bar-new nil - `((image "tabs/new.xpm" + `((image "symbols/plus_16.svg" "tabs/new.xpm" + :face shadow + :height (1 . em) :margin ,tab-bar-button-margin :ascent center) ;; (emoji "➕") @@ -174,7 +174,9 @@ For easier selection of tabs by their numbers, consider customizing (unless (iconp 'tab-bar-close) (define-icon tab-bar-close nil - `((image "tabs/close.xpm" + `((image "symbols/cross_16.svg" "tabs/close.xpm" + :face shadow + :height (1 . em) :margin ,tab-bar-button-margin :ascent center) ;; (emoji " ❌") @@ -188,7 +190,11 @@ For easier selection of tabs by their numbers, consider customizing (unless (iconp 'tab-bar-menu-bar) (define-icon tab-bar-menu-bar nil - '(;; (emoji "🍔") + `((image "symbols/menu_16.svg" + :height (1 . em) + :margin ,tab-bar-button-margin + :ascent center) + ;; (emoji "🍔") (symbol "☰") (text "Menu" :face tab-bar-tab-inactive)) "Icon for the menu bar." @@ -229,7 +235,8 @@ a list of frames to update." ;; Update `default-frame-alist' (when (eq frames t) (setq default-frame-alist - (cons (cons 'tab-bar-lines (if (and tab-bar-mode (eq tab-bar-show t)) 1 0)) + (cons (cons 'tab-bar-lines + (if (and tab-bar-mode (eq tab-bar-show t)) 1 0)) (assq-delete-all 'tab-bar-lines default-frame-alist))))) (define-minor-mode tab-bar-mode @@ -281,7 +288,8 @@ It returns a list of the form (KEY KEY-BINDING CLOSE-P), where: ;; This code is used when you click the mouse in the tab bar ;; on a console which has no window system but does have a mouse. (let* ((x-position (car (posn-x-y posn))) - (keymap (lookup-key (cons 'keymap (nreverse (current-active-maps))) [tab-bar])) + (keymap (lookup-key (cons 'keymap (nreverse (current-active-maps))) + [tab-bar])) (column 0)) (when x-position (catch 'done @@ -338,10 +346,12 @@ only when you click on its \"x\" close button." (unless (eq tab-number t) (tab-bar-close-tab tab-number)))) -(defun tab-bar-mouse-context-menu (event) - "Pop up the context menu for the tab on which you click." +(defun tab-bar-mouse-context-menu (event &optional posn) + "Pop up the context menu for the tab on which you click. +EVENT is a mouse or touch screen event. POSN is nil or the +position of EVENT." (interactive "e") - (let* ((item (tab-bar--event-to-item (event-start event))) + (let* ((item (tab-bar--event-to-item (or posn (event-start event)))) (tab-number (tab-bar--key-to-number (nth 0 item))) (menu (make-sparse-keymap (propertize "Context Menu" 'hide t)))) @@ -394,6 +404,80 @@ at the mouse-down event to the position at mouse-up event." (tab-bar-move-tab-to (if (null to) (1+ (tab-bar--current-tab-index)) to) from)))) + + +;;; Tab bar touchscreen support. + +(declare-function touch-screen-track-tap "touch-screen.el") + +(defun tab-bar-handle-timeout () + "Handle a touch-screen timeout on the tab bar. +Beep, then throw to `context-menu' and return." + (beep) + (throw 'context-menu 'context-menu)) + +(defvar touch-screen-delay) + +(defun tab-bar-touchscreen-begin (event) + "Handle a touchscreen begin EVENT on the tab bar. + +Determine where the touch was made. If it was made on a tab +itself, start a timer set to go off after a certain amount of +time, and wait for the touch point to be released, and either +display a context menu or select a tab as appropriate. + +Otherwise, if it was made on a button, close or create a tab as +appropriate." + (interactive "e") + (let* ((posn (cdadr event)) + (item (tab-bar--event-to-item posn)) + (number (tab-bar--key-to-number (car item))) + timer) + (when (eq (catch 'context-menu + (cond ((integerp number) + ;; The touch began on a tab. Start a context + ;; menu timer and start tracking the tap. + (unwind-protect + (progn + (setq timer (run-at-time touch-screen-delay nil + #'tab-bar-handle-timeout)) + ;; Now wait for the tap to complete. + (when (touch-screen-track-tap event) + ;; And select the tab, or close it, + ;; depending on whether or not the + ;; close button was pressed. + (if (caddr item) + (tab-bar-close-tab number) + (tab-bar-select-tab number)))) + ;; Cancel the timer. + (cancel-timer timer))) + ((and (memq (car item) '(add-tab history-back + history-forward)) + (functionp (cadr item))) + ;; This is some kind of button. Wait for the + ;; tap to complete and press it. + (when (touch-screen-track-tap event) + (call-interactively (cadr item)))) + (t + ;; The touch began on the tab bar itself. + ;; Start a context menu timer and start + ;; tracking the tap, but don't do anything + ;; afterwards. + (unwind-protect + (progn + (setq timer (run-at-time touch-screen-delay nil + #'tab-bar-handle-timeout)) + ;; Now wait for the tap to complete. + (touch-screen-track-tap event)) + ;; Cancel the timer. + (cancel-timer timer))))) + 'context-menu) + ;; Display the context menu in response to a time out waiting + ;; for the tap to complete. + (tab-bar-mouse-context-menu event posn)))) + + + (defvar-keymap tab-bar-map :doc "Keymap for the commands used on the tab bar." "<down-mouse-1>" #'tab-bar-mouse-down-1 @@ -415,7 +499,8 @@ at the mouse-down event to the position at mouse-up event." "S-<wheel-up>" #'tab-bar-move-tab-backward "S-<wheel-down>" #'tab-bar-move-tab "S-<wheel-left>" #'tab-bar-move-tab-backward - "S-<wheel-right>" #'tab-bar-move-tab) + "S-<wheel-right>" #'tab-bar-move-tab + "<touchscreen-begin>" #'tab-bar-touchscreen-begin) (global-set-key [tab-bar] `(menu-item ,(purecopy "tab bar") ,(make-sparse-keymap) @@ -480,7 +565,7 @@ you can use the command `toggle-frame-tab-bar'." :type '(choice (const :tag "Always" t) (const :tag "When more than one tab" 1) (const :tag "Never" nil)) - :initialize 'custom-initialize-default + :initialize #'custom-initialize-default :set (lambda (sym val) (set-default sym val) (if val @@ -531,7 +616,7 @@ to get the group name." "If non-nil, show the \"New tab\" button in the tab bar. When this is nil, you can create new tabs with \\[tab-new]." :type 'boolean - :initialize 'custom-initialize-default + :initialize #'custom-initialize-default :set (lambda (sym val) (set-default sym val) (force-mode-line-update)) @@ -552,7 +637,7 @@ If nil, don't show it at all." (const :tag "On selected tab" selected) (const :tag "On non-selected tabs" non-selected) (const :tag "None" nil)) - :initialize 'custom-initialize-default + :initialize #'custom-initialize-default :set (lambda (sym val) (set-default sym val) (force-mode-line-update)) @@ -576,7 +661,7 @@ If nil, don't show it at all." This helps to select the tab by its number using `tab-bar-select-tab' and `tab-bar-select-tab-modifiers'." :type 'boolean - :initialize 'custom-initialize-default + :initialize #'custom-initialize-default :set (lambda (sym val) (set-default sym val) (force-mode-line-update)) @@ -606,7 +691,7 @@ from all windows in the window configuration." (const :tag "All window buffers" tab-bar-tab-name-all) (function :tag "Function")) - :initialize 'custom-initialize-default + :initialize #'custom-initialize-default :set (lambda (sym val) (set-default sym val) (force-mode-line-update)) @@ -615,13 +700,18 @@ from all windows in the window configuration." (defun tab-bar-tab-name-current () "Generate tab name from the buffer of the selected window." - (buffer-name (window-buffer (minibuffer-selected-window)))) + ;; `minibuffer-selected-window' loses its original window + ;; after switching to another tab while the minibuffer was active, + ;; so get the most recently used non-minibuffer window. + (buffer-name (window-buffer (or (minibuffer-selected-window) + (and (window-minibuffer-p) + (get-mru-window)))))) (defun tab-bar-tab-name-current-with-count () "Generate tab name from the buffer of the selected window. Also add the number of windows in the window configuration." (let ((count (length (window-list-1 nil 'nomini))) - (name (window-buffer (minibuffer-selected-window)))) + (name (tab-bar-tab-name-current))) (if (> count 1) (format "%s (%d)" name count) (format "%s" name)))) @@ -648,7 +738,7 @@ to `tab-bar-tab-name-truncated'." "Generate tab name from the buffer of the selected window. Truncate it to the length specified by `tab-bar-tab-name-truncated-max'. Append ellipsis `tab-bar-tab-name-ellipsis' in this case." - (let ((tab-name (buffer-name (window-buffer (minibuffer-selected-window))))) + (let ((tab-name (tab-bar-tab-name-current))) (if (< (length tab-name) tab-bar-tab-name-truncated-max) tab-name (propertize (truncate-string-to-width @@ -691,6 +781,31 @@ Return its existing value or a new value." (set-frame-parameter frame 'tabs tabs)) +(defun tab-bar-tab-name-format-truncated (name _tab _i) + "Truncate the tab name. +The maximal length is specified by `tab-bar-tab-name-truncated-max'. +Append ellipsis `tab-bar-tab-name-ellipsis' at the end." + (if (< (length name) tab-bar-tab-name-truncated-max) + name + (truncate-string-to-width + name tab-bar-tab-name-truncated-max nil nil + tab-bar-tab-name-ellipsis))) + +(defun tab-bar-tab-name-format-hints (name _tab i) + "Show absolute numbers on tabs in the tab bar before the tab name. +It has effect when `tab-bar-tab-hints' is non-nil." + (if tab-bar-tab-hints (concat (format "%d " i) name) name)) + +(defun tab-bar-tab-name-format-close-button (name tab _i) + "Show the tab close button. +The variable `tab-bar-close-button-show' defines when to show it." + (if (and tab-bar-close-button-show + (not (eq tab-bar-close-button-show + (if (eq (car tab) 'current-tab) 'non-selected 'selected))) + tab-bar-close-button) + (concat name tab-bar-close-button) + name)) + (defcustom tab-bar-tab-face-function #'tab-bar-tab-face-default "Function to define a tab face. Function gets one argument: a tab." @@ -701,30 +816,50 @@ Function gets one argument: a tab." (defun tab-bar-tab-face-default (tab) (if (eq (car tab) 'current-tab) 'tab-bar-tab 'tab-bar-tab-inactive)) +(defun tab-bar-tab-name-format-face (name tab _i) + "Apply the face to the tab name. +It uses the function `tab-bar-tab-face-function'." + (add-face-text-property + 0 (length name) (funcall tab-bar-tab-face-function tab) t name) + name) + +(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) + "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. +It should return the formatted tab name to display in the tab bar." + :type '(repeat + (choice (function-item tab-bar-tab-name-format-truncated) + (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 :tag "Custom function"))) + :group 'tab-bar + :version "30.1") + +(defun tab-bar-tab-name-format-default (tab i) + (let ((name (copy-sequence (alist-get 'name tab)))) + (run-hook-wrapped 'tab-bar-tab-name-format-functions + (lambda (fun) + (setq name (funcall fun name tab i)) + nil)) + name)) + (defcustom tab-bar-tab-name-format-function #'tab-bar-tab-name-format-default "Function to format a tab name. Function gets two arguments, the tab and its number, and should return the formatted tab name to display in the tab bar." :type 'function - :initialize 'custom-initialize-default + :initialize #'custom-initialize-default :set (lambda (sym val) (set-default sym val) (force-mode-line-update)) :group 'tab-bar :version "28.1") -(defun tab-bar-tab-name-format-default (tab i) - (let ((current-p (eq (car tab) 'current-tab))) - (propertize - (concat (if tab-bar-tab-hints (format "%d " i) "") - (alist-get 'name tab) - (or (and tab-bar-close-button-show - (not (eq tab-bar-close-button-show - (if current-p 'non-selected 'selected))) - tab-bar-close-button) - "")) - 'face (funcall tab-bar-tab-face-function tab)))) - (defcustom tab-bar-format '(tab-bar-format-history tab-bar-format-tabs tab-bar-separator @@ -755,7 +890,7 @@ of the mode line. Replacing `tab-bar-format-tabs' with tab-bar-format-add-tab tab-bar-format-align-right tab-bar-format-global) - :initialize 'custom-initialize-default + :initialize #'custom-initialize-default :set (lambda (sym val) (set-default sym val) (force-mode-line-update)) @@ -782,7 +917,7 @@ Used by `tab-bar-format-menu-bar'." (defun tab-bar-format-menu-bar () "Produce the Menu button for the tab bar that shows the menu bar." `((menu-bar menu-item ,tab-bar-menu-bar-button - tab-bar-menu-bar :help "Menu Bar"))) + tab-bar-menu-bar :help "Menu bar"))) (defun tab-bar-format-history () "Produce back and forward buttons for the tab bar. @@ -809,15 +944,16 @@ You can hide these buttons by customizing `tab-bar-format' and removing menu-item ,(funcall tab-bar-tab-name-format-function tab i) ignore - :help "Current tab"))) + :help ,(alist-get 'name tab)))) (t `((,(intern (format "tab-%i" i)) menu-item ,(funcall tab-bar-tab-name-format-function tab i) ,(alist-get 'binding tab) - :help "Click to visit tab")))) + :help ,(alist-get 'name tab))))) (when (alist-get 'close-binding tab) - `((,(if (eq (car tab) 'current-tab) 'C-current-tab (intern (format "C-tab-%i" i))) + `((,(if (eq (car tab) 'current-tab) 'C-current-tab + (intern (format "C-tab-%i" i))) menu-item "" ,(alist-get 'close-binding tab)))))) @@ -834,7 +970,7 @@ You can hide these buttons by customizing `tab-bar-format' and removing "Function to get a tab group name. Function gets one argument: a tab." :type 'function - :initialize 'custom-initialize-default + :initialize #'custom-initialize-default :set (lambda (sym val) (set-default sym val) (force-mode-line-update)) @@ -850,7 +986,7 @@ Function gets three arguments, a tab with a group name, its number, and an optional value that is non-nil when the tab is from the current group. It should return the formatted tab group name to display in the tab bar." :type 'function - :initialize 'custom-initialize-default + :initialize #'custom-initialize-default :set (lambda (sym val) (set-default sym val) (force-mode-line-update)) @@ -921,7 +1057,8 @@ when the tab is current. Return the result as a keymap." (when (and (not (equal previous-group tab-group)) tab-group) (tab-bar--format-tab-group tab i t)) ;; Override default tab faces to use group faces - (let ((tab-bar-tab-face-function tab-bar-tab-group-face-function)) + (let ((tab-bar-tab-face-function + tab-bar-tab-group-face-function)) (tab-bar--format-tab tab i)))) ;; Show first tab of other groups with a group name ((not (equal previous-group tab-group)) @@ -950,7 +1087,8 @@ when the tab is current. Return the result as a keymap." ;; when windows are split horizontally (bug#59620) (if (window-system) `(space :align-to (- right (,hpos))) - `(space :align-to (,(- (frame-inner-width) hpos))))))) + `(space :align-to (,(- (frame-inner-width) + hpos))))))) `((align-right menu-item ,str ignore)))) (defun tab-bar-format-global () @@ -1020,7 +1158,7 @@ This variable has effect only when `tab-bar-auto-width' is non-nil." (const :tag "No limit" nil) (list (integer :tag "Max width (pixels)" :value 220) (integer :tag "Max width (chars)" :value 20))) - :initialize 'custom-initialize-default + :initialize #'custom-initialize-default :set (lambda (sym val) (set-default sym val) (setq tab-bar--auto-width-hash nil)) @@ -1089,17 +1227,18 @@ tab bar might wrap to the second line when it shouldn't.") curr-width) (cond ((< prev-width width) - (let* ((space (apply 'propertize " " + (let* ((space (apply #'propertize " " (text-properties-at 0 name))) (ins-pos (- len (if close-p (length tab-bar-close-button) 0))) (prev-name name)) (while continue - (setf (substring name ins-pos ins-pos) space) + (setq name (concat (substring name 0 ins-pos) + space + (substring name ins-pos))) (setq curr-width (string-pixel-width name)) - (if (and (< curr-width width) - (> curr-width prev-width)) + (if (< curr-width width) (setq prev-width curr-width prev-name name) ;; Set back a shorter name @@ -1109,10 +1248,11 @@ tab bar might wrap to the second line when it shouldn't.") (let ((del-pos1 (if close-p -2 -1)) (del-pos2 (if close-p -1 nil))) (while continue - (setf (substring name del-pos1 del-pos2) "") + (setq name (concat (substring name 0 del-pos1) + (and del-pos2 + (substring name del-pos2)))) (setq curr-width (string-pixel-width name)) - (if (and (> curr-width width) - (< curr-width prev-width)) + (if (> curr-width width) (setq prev-width curr-width) (setq continue nil))) (let* ((len (length name)) @@ -1162,7 +1302,7 @@ tab bar might wrap to the second line when it shouldn't.") (ws . ,(window-state-get (frame-root-window (or frame (selected-frame))) 'writable)) (wc . ,(current-window-configuration)) - (wc-point . ,(point-marker)) + (wc-point . ,(copy-marker (window-point) window-point-insertion-type)) (wc-bl . ,bl) (wc-bbl . ,bbl) ,@(when tab-bar-history-mode @@ -1245,6 +1385,74 @@ inherits the current tab's `explicit-name' parameter." tabs)))) +(defcustom tab-bar-tab-post-select-functions nil + "List of functions to call after selecting a tab. +Two arguments are supplied: the previous tab that was selected before, +and the newly selected tab." + :type '(repeat function) + :group 'tab-bar + :version "30.1") + +(defcustom tab-bar-select-restore-windows #'tab-bar-select-restore-windows + "Function called when selecting a tab to handle windows whose buffer was killed. +When a tab-bar tab displays a window whose buffer was killed since +this tab was last selected, this function determines what to do with +that window. By default, either a random buffer is displayed instead of +the killed buffer, or the window gets deleted. However, with the help +of `window-restore-killed-buffer-windows' it's possible to handle such +situations better by displaying an information about the killed buffer." + :type '(choice (const :tag "No special handling" nil) + (const :tag "Show placeholder buffers" + tab-bar-select-restore-windows) + (function :tag "Function")) + :group 'tab-bar + :version "30.1") + +(defun tab-bar-select-restore-windows (_frame windows _type) + "Display a placeholder buffer in the window whose buffer was killed. +A button in the window allows to restore the killed buffer, +if it was visiting a file." + (dolist (quad windows) + (when (window-live-p (nth 0 quad)) + (let* ((window (nth 0 quad)) + (old-buffer (nth 1 quad)) + (file (when (bufferp old-buffer) + (buffer-file-name old-buffer))) + (name (or file + (and (bufferp old-buffer) + (fboundp 'buffer-last-name) + (buffer-last-name old-buffer)) + old-buffer)) + (new-buffer (generate-new-buffer + (format "*Old buffer %s*" name)))) + (with-current-buffer new-buffer + (set-auto-mode) + (insert (format-message "This window displayed the %s `%s'.\n" + (if file "file" "buffer") + name)) + (when file + (insert-button + "[Restore]" 'action + (lambda (_button) + (set-window-buffer window (find-file-noselect file)) + (set-window-start window (nth 2 quad) t) + (set-window-point window (nth 3 quad)))) + (insert "\n")) + (goto-char (point-min)) + (setq buffer-read-only t) + (set-window-buffer window new-buffer)))))) + +(defvar tab-bar-minibuffer-restore-tab nil + "Tab number for `tab-bar-minibuffer-restore-tab'.") + +(defun tab-bar-minibuffer-restore-tab () + "Switch back to the tab where the minibuffer was activated. +This is necessary to prepare the same window configuration where +original windows were saved and will be restored. This function +is used only when `read-minibuffer-restore-windows' is non-nil." + (when tab-bar-minibuffer-restore-tab + (tab-bar-select-tab tab-bar-minibuffer-restore-tab))) + (defun tab-bar-select-tab (&optional tab-number) "Switch to the tab by its absolute position TAB-NUMBER in the tab bar. When this command is bound to a numeric key (with a key prefix or modifier key @@ -1270,11 +1478,19 @@ Negative TAB-NUMBER counts tabs from the end of the tab bar." (to-index (1- (max 1 (min to-number (length tabs))))) (minibuffer-was-active (minibuffer-window-active-p (selected-window)))) + (when (and read-minibuffer-restore-windows minibuffer-was-active + (not tab-bar-minibuffer-restore-tab)) + (setq-local tab-bar-minibuffer-restore-tab (1+ from-index)) + (add-hook 'minibuffer-exit-hook 'tab-bar-minibuffer-restore-tab nil t)) + (unless (eq from-index to-index) (let* ((from-tab (tab-bar--tab)) (to-tab (nth to-index tabs)) (wc (alist-get 'wc to-tab)) - (ws (alist-get 'ws to-tab))) + (ws (alist-get 'ws to-tab)) + (window-restore-killed-buffer-windows + (or tab-bar-select-restore-windows + window-restore-killed-buffer-windows))) ;; During the same session, use window-configuration to switch ;; tabs, because window-configurations are more reliable @@ -1299,13 +1515,7 @@ Negative TAB-NUMBER counts tabs from the end of the tab bar." ;; set-window-configuration does not restore the value of ;; point in the current buffer, so restore it separately. (when (and (markerp wc-point) - (marker-buffer wc-point) - ;; FIXME: After dired-revert, marker relocates to 1. - ;; window-configuration restores point to global point - ;; in this dired buffer, not to its window point, - ;; but this is slightly better than 1. - ;; Maybe better to save dired-filename in each window? - (not (eq 1 (marker-position wc-point)))) + (marker-buffer wc-point)) (goto-char wc-point)) (when wc-bl (set-frame-parameter nil 'buffer-list wc-bl)) @@ -1313,11 +1523,13 @@ Negative TAB-NUMBER counts tabs from the end of the tab bar." (when tab-bar-history-mode (puthash (selected-frame) - (and (window-configuration-p (alist-get 'wc (car wc-history-back))) + (and (window-configuration-p + (alist-get 'wc (car wc-history-back))) wc-history-back) tab-bar-history-back) (puthash (selected-frame) - (and (window-configuration-p (alist-get 'wc (car wc-history-forward))) + (and (window-configuration-p + (alist-get 'wc (car wc-history-forward))) wc-history-forward) tab-bar-history-forward)))) @@ -1343,10 +1555,14 @@ Negative TAB-NUMBER counts tabs from the end of the tab bar." (when from-index (setf (nth from-index tabs) from-tab)) - (setf (nth to-index tabs) (tab-bar--current-tab-make (nth to-index tabs))) + (setf (nth to-index tabs) + (tab-bar--current-tab-make (nth to-index tabs))) (unless tab-bar-mode - (message "Selected tab '%s'" (alist-get 'name to-tab)))) + (message "Selected tab '%s'" (alist-get 'name to-tab))) + + (run-hook-with-args 'tab-bar-tab-post-select-functions + from-tab to-tab)) (force-mode-line-update)))) @@ -1410,7 +1626,7 @@ and rename it to NAME." (tab-bar-new-tab) (tab-bar-rename-tab name)))) -(defalias 'tab-bar-select-tab-by-name 'tab-bar-switch-to-tab) +(defalias 'tab-bar-select-tab-by-name #'tab-bar-switch-to-tab) (defun tab-bar-move-tab-to (to-number &optional from-number) @@ -1425,7 +1641,8 @@ where argument addressing is relative." (from-number (or from-number (1+ (tab-bar--current-tab-index tabs)))) (from-tab (nth (1- from-number) tabs)) (to-number (if to-number (prefix-numeric-value to-number) 1)) - (to-number (if (< to-number 0) (+ (length tabs) (1+ to-number)) to-number)) + (to-number (if (< to-number 0) (+ (length tabs) (1+ to-number)) + to-number)) (to-index (max 0 (min (1- to-number) (1- (length tabs)))))) (setq tabs (delq from-tab tabs)) (cl-pushnew from-tab (nthcdr to-index tabs)) @@ -1451,7 +1668,8 @@ Like `tab-bar-move-tab', but moves in the opposite direction." (interactive "p") (tab-bar-move-tab (- (or arg 1)))) -(defun tab-bar-move-tab-to-frame (arg &optional from-frame from-number to-frame to-number) +(defun tab-bar-move-tab-to-frame (arg &optional from-frame from-number + to-frame to-number) "Move tab from FROM-NUMBER position to new position at TO-NUMBER. FROM-NUMBER defaults to the current tab number. FROM-NUMBER and TO-NUMBER count from 1. @@ -1467,7 +1685,8 @@ to which to move the tab; ARG defaults to 1." (setq to-frame (next-frame to-frame)))) (unless (eq from-frame to-frame) (let* ((from-tabs (funcall tab-bar-tabs-function from-frame)) - (from-number (or from-number (1+ (tab-bar--current-tab-index from-tabs)))) + (from-number (or from-number + (1+ (tab-bar--current-tab-index from-tabs)))) (from-tab (nth (1- from-number) from-tabs)) (to-tabs (funcall tab-bar-tabs-function to-frame)) (to-index (max 0 (min (1- (or to-number 1)) (1- (length to-tabs)))))) @@ -1489,7 +1708,8 @@ to which to move the tab; ARG defaults to 1." FROM-NUMBER defaults to the current tab (which happens interactively)." (interactive (list (1+ (tab-bar--current-tab-index)))) (let* ((tabs (funcall tab-bar-tabs-function)) - (tab-index (1- (or from-number (1+ (tab-bar--current-tab-index tabs))))) + (tab-index (1- (or from-number + (1+ (tab-bar--current-tab-index tabs))))) (tab-name (alist-get 'name (nth tab-index tabs))) ;; On some window managers, `make-frame' selects the new frame, ;; so previously selected frame is saved to `from-frame'. @@ -1754,7 +1974,8 @@ for the last tab on a frame is determined by ;; Select another tab before deleting the current tab (let ((to-index (or (if to-number (1- to-number)) (pcase tab-bar-close-tab-select - ('left (1- (if (< current-index 1) 2 current-index))) + ('left (1- (if (< current-index 1) 2 + current-index))) ('right (if (> (length tabs) (1+ current-index)) (1+ current-index) (1- current-index))) @@ -1779,7 +2000,8 @@ for the last tab on a frame is determined by (force-mode-line-update) (unless tab-bar-mode - (message "Deleted tab and switched to %s" tab-bar-close-tab-select)))))) + (message "Deleted tab and switched to %s" + tab-bar-close-tab-select)))))) (defun tab-bar-close-tab-by-name (name) "Close the tab given its NAME. @@ -1870,7 +2092,8 @@ If NAME is the empty string, then use the automatic name function `tab-bar-tab-name-function'." (interactive (let* ((tabs (funcall tab-bar-tabs-function)) - (tab-number (or current-prefix-arg (1+ (tab-bar--current-tab-index tabs)))) + (tab-number (or current-prefix-arg + (1+ (tab-bar--current-tab-index tabs)))) (tab-name (alist-get 'name (nth (1- tab-number) tabs)))) (list (read-from-minibuffer "New name for tab (leave blank for automatic naming): " @@ -2117,7 +2340,8 @@ and can restore them." (unless (iconp 'tab-bar-back) (define-icon tab-bar-back nil - `((image "tabs/left-arrow.xpm" + `((image "symbols/chevron_left_16.svg" "tabs/left-arrow.xpm" + :height (1 . em) :margin ,tab-bar-button-margin :ascent center) (text " < ")) @@ -2127,7 +2351,8 @@ and can restore them." (unless (iconp 'tab-bar-forward) (define-icon tab-bar-forward nil - `((image "tabs/right-arrow.xpm" + `((image "symbols/chevron_right_16.svg" "tabs/right-arrow.xpm" + :height (1 . em) :margin ,tab-bar-button-margin :ascent center) (text " > ")) @@ -2135,10 +2360,10 @@ and can restore them." :version "29.1")) (setq tab-bar-forward-button (icon-string 'tab-bar-forward)) - (add-hook 'pre-command-hook 'tab-bar--history-pre-change) - (add-hook 'window-configuration-change-hook 'tab-bar--history-change)) - (remove-hook 'pre-command-hook 'tab-bar--history-pre-change) - (remove-hook 'window-configuration-change-hook 'tab-bar--history-change))) + (add-hook 'pre-command-hook #'tab-bar--history-pre-change) + (add-hook 'window-configuration-change-hook #'tab-bar--history-change)) + (remove-hook 'pre-command-hook #'tab-bar--history-pre-change) + (remove-hook 'window-configuration-change-hook #'tab-bar--history-change))) ;;; Non-graphical access to frame-local tabs (named window configurations) @@ -2178,8 +2403,9 @@ For more information, see the function `tab-switcher'." (tabs (sort tabs (lambda (a b) (< (alist-get 'time b) (alist-get 'time a)))))) (with-current-buffer (get-buffer-create - (format " *Tabs*<%s>" (or (frame-parameter nil 'window-id) - (frame-parameter nil 'name)))) + (format " *Tabs*<%s>" + (or (frame-parameter nil 'window-id) + (frame-parameter nil 'name)))) (setq buffer-read-only nil) (erase-buffer) (tab-switcher-mode) @@ -2194,7 +2420,8 @@ For more information, see the function `tab-switcher'." (propertize (alist-get 'name tab) 'mouse-face 'highlight - 'help-echo "mouse-2: select this window configuration")) + 'help-echo + "mouse-2: select this window configuration")) 'tab tab))) (goto-char (point-min)) (goto-char (or (next-single-property-change (point) 'tab) (point-min))) @@ -2270,8 +2497,8 @@ Interactively, ARG is the prefix numeric argument and defaults to 1." (move-to-column tab-switcher-column)) (defun tab-switcher-unmark (&optional backup) - "Cancel requested operations on window configuration on this line and move down. -With prefix arg, move up instead." + "Cancel operations on window configuration on this line and move down. +With prefix arg BACKUP, move up instead." (interactive "P") (beginning-of-line) (move-to-column tab-switcher-column) @@ -2282,7 +2509,7 @@ With prefix arg, move up instead." (move-to-column tab-switcher-column)) (defun tab-switcher-backup-unmark () - "Move up one line and cancel requested operations on window configuration there." + "Move up one line and cancel operations on window configuration there." (interactive) (forward-line -1) (tab-switcher-unmark) @@ -2290,9 +2517,10 @@ With prefix arg, move up instead." (move-to-column tab-switcher-column)) (defun tab-switcher-delete (&optional arg) - "Mark window configuration on this line to be deleted by \\<tab-switcher-mode-map>\\[tab-switcher-execute] command. + "Mark window configuration on this line to be deleted. Prefix arg says how many window configurations to delete. -Negative arg means delete backwards." +Negative arg means delete backwards. +The deletion will be done by the \\<tab-switcher-mode-map>\\[tab-switcher-execute] command." (interactive "p") (let ((buffer-read-only nil)) (if (or (null arg) (= arg 0)) @@ -2310,8 +2538,9 @@ Negative arg means delete backwards." (move-to-column tab-switcher-column))) (defun tab-switcher-delete-backwards (&optional arg) - "Mark window configuration on this line to be deleted by \\<tab-switcher-mode-map>\\[tab-switcher-execute] command. -Then move up one line. Prefix arg means move that many lines." + "Mark window configuration on this line to be deleted. +Then move up one line. Prefix arg means move that many lines. +The deletion will be done by the \\<tab-switcher-mode-map>\\[tab-switcher-execute] command." (interactive "p") (tab-switcher-delete (- (or arg 1)))) @@ -2324,7 +2553,9 @@ Then move up one line. Prefix arg means move that many lines." (tab-bar-tabs-set (delq tab (funcall tab-bar-tabs-function)))) (defun tab-switcher-execute () - "Delete window configurations marked with \\<tab-switcher-mode-map>\\[tab-switcher-delete] commands." + "Delete the marked window configurations. +Use the \\<tab-switcher-mode-map>\\[tab-switcher-delete] commands +to set those marks." (interactive) (save-excursion (goto-char (point-min)) @@ -2370,7 +2601,8 @@ with those specified by the selected window configuration." ((framep all-frames) (list all-frames)) (t (list (selected-frame))))) -(defun tab-bar-get-buffer-tab (buffer-or-name &optional all-frames ignore-current-tab all-tabs) +(defun tab-bar-get-buffer-tab (buffer-or-name + &optional all-frames ignore-current-tab all-tabs) "Return the tab that owns the window whose buffer is BUFFER-OR-NAME. BUFFER-OR-NAME may be a buffer or a buffer name, and defaults to the current buffer. @@ -2546,7 +2778,7 @@ files will be visited." (progn (setq value (nreverse value)) (switch-to-buffer-other-tab (car value)) - (mapc 'switch-to-buffer (cdr value)) + (mapc #'switch-to-buffer (cdr value)) value) (switch-to-buffer-other-tab value)))) @@ -2588,26 +2820,26 @@ When `switch-to-buffer-obey-display-actions' is non-nil, ;;; Short aliases and keybindings -(defalias 'tab-new 'tab-bar-new-tab) -(defalias 'tab-new-to 'tab-bar-new-tab-to) -(defalias 'tab-duplicate 'tab-bar-duplicate-tab) -(defalias 'tab-detach 'tab-bar-detach-tab) -(defalias 'tab-window-detach 'tab-bar-move-window-to-tab) -(defalias 'tab-close 'tab-bar-close-tab) -(defalias 'tab-close-other 'tab-bar-close-other-tabs) -(defalias 'tab-close-group 'tab-bar-close-group-tabs) -(defalias 'tab-undo 'tab-bar-undo-close-tab) -(defalias 'tab-select 'tab-bar-select-tab) -(defalias 'tab-switch 'tab-bar-switch-to-tab) -(defalias 'tab-next 'tab-bar-switch-to-next-tab) -(defalias 'tab-previous 'tab-bar-switch-to-prev-tab) -(defalias 'tab-last 'tab-bar-switch-to-last-tab) -(defalias 'tab-recent 'tab-bar-switch-to-recent-tab) -(defalias 'tab-move 'tab-bar-move-tab) -(defalias 'tab-move-to 'tab-bar-move-tab-to) -(defalias 'tab-rename 'tab-bar-rename-tab) -(defalias 'tab-group 'tab-bar-change-tab-group) -(defalias 'tab-list 'tab-switcher) +(defalias 'tab-new #'tab-bar-new-tab) +(defalias 'tab-new-to #'tab-bar-new-tab-to) +(defalias 'tab-duplicate #'tab-bar-duplicate-tab) +(defalias 'tab-detach #'tab-bar-detach-tab) +(defalias 'tab-window-detach #'tab-bar-move-window-to-tab) +(defalias 'tab-close #'tab-bar-close-tab) +(defalias 'tab-close-other #'tab-bar-close-other-tabs) +(defalias 'tab-close-group #'tab-bar-close-group-tabs) +(defalias 'tab-undo #'tab-bar-undo-close-tab) +(defalias 'tab-select #'tab-bar-select-tab) +(defalias 'tab-switch #'tab-bar-switch-to-tab) +(defalias 'tab-next #'tab-bar-switch-to-next-tab) +(defalias 'tab-previous #'tab-bar-switch-to-prev-tab) +(defalias 'tab-last #'tab-bar-switch-to-last-tab) +(defalias 'tab-recent #'tab-bar-switch-to-recent-tab) +(defalias 'tab-move #'tab-bar-move-tab) +(defalias 'tab-move-to #'tab-bar-move-tab-to) +(defalias 'tab-rename #'tab-bar-rename-tab) +(defalias 'tab-group #'tab-bar-change-tab-group) +(defalias 'tab-list #'tab-switcher) (keymap-set tab-prefix-map "n" #'tab-duplicate) (keymap-set tab-prefix-map "N" #'tab-new-to) |