Support mouse events clicked on the tab bar but outside of any tab (bug#41343)

* lisp/tab-bar.el (tab-bar--key-to-number): Return non-nil non-numeric t
when no tab is used.  Return nil for current-tab.
(tab-bar-mouse-select-tab, tab-bar-mouse-close-tab): Do nothing
when tab-bar--key-to-number returns non-nil non-numeric t
for click events outside of any tab.
(tab-bar-mouse-context-menu): Add context menu when mouse is clicked
outside of tabs.  Add "Duplicate" alongside with "Close" to the menu
used when mouse is clicked on a tab.
(toggle-tab-bar-mode-from-frame, toggle-frame-tab-bar): Move code
closer to 'tab-bar-show'.

* src/xdisp.c (handle_tab_bar_click): Return Qtab_bar with empty list
when mouse is clicked on the tab bar but outside of any tab.
This commit is contained in:
Juri Linkov 2021-09-13 11:14:32 +03:00
parent 4ee8b4d225
commit fd1379a85a
2 changed files with 72 additions and 46 deletions

View file

@ -113,7 +113,6 @@ For easier selection of tabs by their numbers, consider customizing
:group 'tab-bar
:version "27.1")
(defun tab-bar--define-keys ()
"Install key bindings for switching between tabs if the user has configured them."
(when tab-bar-select-tab-modifiers
@ -224,10 +223,15 @@ a list of frames to update."
(tab-bar--define-keys)
(tab-bar--undefine-keys)))
;;; Key bindings
(defun tab-bar--key-to-number (key)
(let ((key-name (format "%S" key)))
(when (string-prefix-p "tab-" key-name)
(string-to-number (string-replace "tab-" "" key-name)))))
(unless (eq key 'current-tab)
(let ((key-name (format "%S" key)))
(if (string-prefix-p "tab-" key-name)
(string-to-number (string-replace "tab-" "" key-name))
t))))
(defun tab-bar--event-to-item (posn)
(if (posn-window posn)
@ -246,37 +250,59 @@ a list of frames to update."
(lambda (key binding)
(when (eq (car-safe binding) 'menu-item)
(when (> (+ column (length (nth 1 binding))) x-position)
(throw 'done (list
key (nth 2 binding)
(get-text-property
(- x-position column) 'close-tab (nth 1 binding)))))
(throw 'done (list key (nth 2 binding)
(get-text-property
(- x-position column)
'close-tab (nth 1 binding)))))
(setq column (+ column (length (nth 1 binding))))))
keymap))))))
(defun tab-bar-mouse-select-tab (event)
(interactive "e")
(let ((item (tab-bar--event-to-item (event-start event))))
(let* ((item (tab-bar--event-to-item (event-start event)))
(tab-number (tab-bar--key-to-number (nth 0 item))))
(if (nth 2 item)
(tab-bar-close-tab (tab-bar--key-to-number (nth 0 item)))
(unless (eq tab-number t)
(tab-bar-close-tab tab-number))
(if (functionp (nth 1 item))
(call-interactively (nth 1 item))
(tab-bar-select-tab (tab-bar--key-to-number (nth 0 item)))))))
(unless (eq tab-number t)
(tab-bar-select-tab tab-number))))))
(defun tab-bar-mouse-close-tab (event)
(interactive "e")
(let ((item (tab-bar--event-to-item (event-start event))))
(tab-bar-close-tab (tab-bar--key-to-number (nth 0 item)))))
(let* ((item (tab-bar--event-to-item (event-start event)))
(tab-number (tab-bar--key-to-number (nth 0 item))))
(unless (eq tab-number t)
(tab-bar-close-tab tab-number))))
(defun tab-bar-mouse-context-menu (event)
(interactive "e")
(let* ((item (tab-bar--event-to-item (event-start event)))
(tab-number (tab-bar--key-to-number (nth 0 item)))
(menu (make-sparse-keymap "Context Menu")))
(menu (make-sparse-keymap (propertize "Context Menu" 'hide t))))
(define-key-after menu [close]
`(menu-item "Close" (lambda () (interactive)
(tab-bar-close-tab ,tab-number))
:help "Close the tab"))
(cond
((eq tab-number t)
(define-key-after menu [new-tab]
'(menu-item "New tab" tab-bar-new-tab
:help "Create a new tab"))
(when tab-bar-closed-tabs
(define-key-after menu [undo-close]
'(menu-item "Reopen closed tab" tab-bar-undo-close-tab
:help "Undo closing the tab"))))
(t
(define-key-after menu [duplicate-tab]
`(menu-item "Duplicate" (lambda () (interactive)
(tab-bar-duplicate-tab
nil ;; TODO: add ,tab-number
))
:help "Duplicate the tab"))
(define-key-after menu [close]
`(menu-item "Close" (lambda () (interactive)
(tab-bar-close-tab ,tab-number))
:help "Close the tab"))))
(popup-menu menu event)))
@ -290,31 +316,6 @@ a list of frames to update."
(event-end event))))))
(tab-bar-move-tab-to to from)))
(defun toggle-tab-bar-mode-from-frame (&optional arg)
"Toggle tab bar on or off, based on the status of the current frame.
Used in the Show/Hide menu, to have the toggle reflect the current frame.
See `tab-bar-mode' for more information."
(interactive (list (or current-prefix-arg 'toggle)))
(if (eq arg 'toggle)
(tab-bar-mode (if (> (frame-parameter nil 'tab-bar-lines) 0) 0 1))
(tab-bar-mode arg)))
(defun toggle-frame-tab-bar (&optional frame)
"Toggle tab bar of the selected frame.
When calling from Lisp, use the optional argument FRAME to toggle
the tab bar on that frame.
This is useful if you want to enable the tab bar individually
on each new frame when the global `tab-bar-mode' is disabled,
or if you want to disable the tab bar individually on each
new frame when the global `tab-bar-mode' is enabled, by using
(add-hook 'after-make-frame-functions 'toggle-frame-tab-bar)"
(interactive)
(set-frame-parameter frame 'tab-bar-lines
(if (> (frame-parameter frame 'tab-bar-lines) 0) 0 1))
(set-frame-parameter frame 'tab-bar-lines-keep-state
(not (frame-parameter frame 'tab-bar-lines-keep-state))))
(defvar tab-bar-map
(let ((map (make-sparse-keymap)))
(define-key map [down-mouse-1] 'tab-bar-mouse-select-tab)
@ -351,6 +352,32 @@ Its main job is to show tabs in the tab bar
and to bind mouse events to the commands."
(tab-bar-make-keymap-1))
(defun toggle-tab-bar-mode-from-frame (&optional arg)
"Toggle tab bar on or off, based on the status of the current frame.
Used in the Show/Hide menu, to have the toggle reflect the current frame.
See `tab-bar-mode' for more information."
(interactive (list (or current-prefix-arg 'toggle)))
(if (eq arg 'toggle)
(tab-bar-mode (if (> (frame-parameter nil 'tab-bar-lines) 0) 0 1))
(tab-bar-mode arg)))
(defun toggle-frame-tab-bar (&optional frame)
"Toggle tab bar of the selected frame.
When calling from Lisp, use the optional argument FRAME to toggle
the tab bar on that frame.
This is useful if you want to enable the tab bar individually
on each new frame when the global `tab-bar-mode' is disabled,
or if you want to disable the tab bar individually on each
new frame when the global `tab-bar-mode' is enabled, by using
(add-hook 'after-make-frame-functions 'toggle-frame-tab-bar)"
(interactive)
(set-frame-parameter frame 'tab-bar-lines
(if (> (frame-parameter frame 'tab-bar-lines) 0) 0 1))
(set-frame-parameter frame 'tab-bar-lines-keep-state
(not (frame-parameter frame 'tab-bar-lines-keep-state))))
(defcustom tab-bar-show t
"Defines when to show the tab bar.
@ -1224,8 +1251,7 @@ where argument addressing is absolute."
(defun tab-bar-duplicate-tab (&optional arg)
"Duplicate the current tab to ARG positions to the right.
If a negative ARG, duplicate the tab to ARG positions to the left.
If ARG is zero, duplicate the tab in place of the current tab."
ARG has the same meaning as in `tab-bar-new-tab'."
(interactive "P")
(let ((tab-bar-new-tab-choice nil)
(tab-bar-new-tab-group t))

View file

@ -13774,7 +13774,7 @@ handle_tab_bar_click (struct frame *f, int x, int y, bool down_p,
frame_to_window_pixel_xy (w, &x, &y);
ts = get_tab_bar_item (f, x, y, &glyph, &hpos, &vpos, &prop_idx, &close_p);
if (ts == -1)
return Qnil;
return Fcons (Qtab_bar, Qnil);
/* If item is disabled, do nothing. */
enabled_p = AREF (f->tab_bar_items, prop_idx + TAB_BAR_ITEM_ENABLED_P);