aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJuri Linkov2019-10-23 00:17:27 +0300
committerJuri Linkov2019-10-23 00:17:27 +0300
commit095908aec4a6a414c13385c429e559a73d787ae0 (patch)
tree17a647338ae239d643d288b483dabe56809957f2
parentf342f9cd267d39b3ef7d0c2a58718f4ac62f5abd (diff)
downloademacs-095908aec4a6a414c13385c429e559a73d787ae0.tar.gz
emacs-095908aec4a6a414c13385c429e559a73d787ae0.zip
Tab-line horizontal scrolling with UI buttons and commands (bug#37667)
* etc/images/tabs/left-arrow.xpm: * etc/images/tabs/right-arrow.xpm: New images. * lisp/tab-line.el (tab-line-left-map, tab-line-right-map): New keymaps. (tab-line-left-button, tab-line-right-button): New variables. (tab-line-tab-name-function): Turn defvar into defcustom. (tab-line-tab-name-buffer): New function. (tab-line-tab-name-truncated-buffer): Rename from tab-line-tab-name. (tab-line-tabs-limit): Default to nil. (tab-line-tabs): Behavior depends on tab-line-tabs-limit. (tab-line-format): Use window-parameter tab-line-hscroll. Add left/right buttons. (tab-line-hscroll): New function. (tab-line-hscroll-right, tab-line-hscroll-left): New commands bound to mouse-wheel. Rebind tab-switching commands to mouse-wheel with Ctrl-modifier.
-rw-r--r--etc/images/tabs/README2
-rw-r--r--etc/images/tabs/left-arrow.xpm16
-rw-r--r--etc/images/tabs/right-arrow.xpm16
-rw-r--r--lisp/tab-line.el185
4 files changed, 175 insertions, 44 deletions
diff --git a/etc/images/tabs/README b/etc/images/tabs/README
index 1e9f4e5b595..ac549cf4bdf 100644
--- a/etc/images/tabs/README
+++ b/etc/images/tabs/README
@@ -2,7 +2,7 @@ This directory contains icons for the Tabs user interface.
2 2
3COPYRIGHT AND LICENSE INFORMATION FOR IMAGE FILES 3COPYRIGHT AND LICENSE INFORMATION FOR IMAGE FILES
4 4
5Files: close.xpm new.xpm 5Files: close.xpm new.xpm left-arrow.xpm right-arrow.xpm
6Author: Juri Linkov <juri@linkov.net> 6Author: Juri Linkov <juri@linkov.net>
7Copyright (C) 2019 Free Software Foundation, Inc. 7Copyright (C) 2019 Free Software Foundation, Inc.
8License: GNU General Public License version 3 or later (see COPYING) 8License: GNU General Public License version 3 or later (see COPYING)
diff --git a/etc/images/tabs/left-arrow.xpm b/etc/images/tabs/left-arrow.xpm
new file mode 100644
index 00000000000..f133cd62173
--- /dev/null
+++ b/etc/images/tabs/left-arrow.xpm
@@ -0,0 +1,16 @@
1/* XPM */
2static char * left_arrow_xpm[] = {
3"9 9 4 1",
4" c None",
5". c #BFBFBF",
6"+ c #000000",
7"@ c #808080",
8".........",
9".....+@..",
10"....+@...",
11"...+@....",
12"..+@.....",
13"...+@....",
14"....+@...",
15".....+@..",
16"........."};
diff --git a/etc/images/tabs/right-arrow.xpm b/etc/images/tabs/right-arrow.xpm
new file mode 100644
index 00000000000..ab1f1a099f1
--- /dev/null
+++ b/etc/images/tabs/right-arrow.xpm
@@ -0,0 +1,16 @@
1/* XPM */
2static char * right_arrow_xpm[] = {
3"9 9 4 1",
4" c None",
5". c #BFBFBF",
6"+ c #808080",
7"@ c #000000",
8".........",
9"..+@.....",
10"...+@....",
11"....+@...",
12".....+@..",
13"....+@...",
14"...+@....",
15"..+@.....",
16"........."};
diff --git a/lisp/tab-line.el b/lisp/tab-line.el
index 58f648c2827..7dc6e2b6d04 100644
--- a/lisp/tab-line.el
+++ b/lisp/tab-line.el
@@ -113,6 +113,22 @@
113 map) 113 map)
114 "Local keymap to close `tab-line-mode' window tabs.") 114 "Local keymap to close `tab-line-mode' window tabs.")
115 115
116(defvar tab-line-left-map
117 (let ((map (make-sparse-keymap)))
118 (define-key map [tab-line mouse-1] 'tab-line-hscroll-left)
119 (define-key map [tab-line mouse-2] 'tab-line-hscroll-left)
120 (define-key map "\C-m" 'tab-line-new-tab)
121 map)
122 "Local keymap to scroll `tab-line-mode' window tabs to the left.")
123
124(defvar tab-line-right-map
125 (let ((map (make-sparse-keymap)))
126 (define-key map [tab-line mouse-1] 'tab-line-hscroll-right)
127 (define-key map [tab-line mouse-2] 'tab-line-hscroll-right)
128 (define-key map "\C-m" 'tab-line-new-tab)
129 map)
130 "Local keymap to scroll `tab-line-mode' window tabs to the right.")
131
116 132
117(defcustom tab-line-new-tab-choice t 133(defcustom tab-line-new-tab-choice t
118 "Defines what to show in a new tab. 134 "Defines what to show in a new tab.
@@ -164,22 +180,60 @@ If nil, don't show it at all."
164 'help-echo "Click to close tab") 180 'help-echo "Click to close tab")
165 "Button for closing the clicked tab.") 181 "Button for closing the clicked tab.")
166 182
183(defvar tab-line-left-button
184 (propertize " <"
185 'display `(image :type xpm
186 :file "tabs/left-arrow.xpm"
187 :margin (2 . 0)
188 :ascent center)
189 'keymap tab-line-left-map
190 'mouse-face 'tab-line-highlight
191 'help-echo "Click to scroll left")
192 "Button for scrolling horizontally to the left.")
193
194(defvar tab-line-right-button
195 (propertize "> "
196 'display `(image :type xpm
197 :file "tabs/right-arrow.xpm"
198 :margin (2 . 0)
199 :ascent center)
200 'keymap tab-line-right-map
201 'mouse-face 'tab-line-highlight
202 'help-echo "Click to scroll right")
203 "Button for scrolling horizontally to the right.")
204
167(defvar tab-line-separator nil) 205(defvar tab-line-separator nil)
168 206
169(defvar tab-line-tab-name-ellipsis 207(defvar tab-line-tab-name-ellipsis
170 (if (char-displayable-p ?…) "…" "...")) 208 (if (char-displayable-p ?…) "…" "..."))
171 209
172 210
173(defvar tab-line-tab-name-function #'tab-line-tab-name 211(defcustom tab-line-tab-name-function #'tab-line-tab-name-buffer
174 "Function to get a tab name. 212 "Function to get a tab name.
175Function gets two arguments: tab to get name for and a list of tabs 213Function gets two arguments: tab to get name for and a list of tabs
176to display. By default, use function `tab-line-tab-name'.") 214to display. By default, use function `tab-line-tab-name'."
215 :type '(choice (const :tag "Buffer name"
216 tab-line-tab-name-buffer)
217 (const :tag "Truncated buffer name"
218 tab-line-tab-name-truncated-buffer)
219 (function :tag "Function"))
220 :initialize 'custom-initialize-default
221 :set (lambda (sym val)
222 (set-default sym val)
223 (force-mode-line-update))
224 :group 'tab-line
225 :version "27.1")
177 226
178(defun tab-line-tab-name (buffer &optional buffers) 227(defun tab-line-tab-name-buffer (buffer &optional _buffers)
179 "Generate tab name from BUFFER. 228 "Generate tab name from BUFFER.
180Reduce tab width proportionally to space taken by other tabs. 229Reduce tab width proportionally to space taken by other tabs.
181This function can be overridden by changing the default value of the 230This function can be overridden by changing the default value of the
182variable `tab-line-tab-name-function'." 231variable `tab-line-tab-name-function'."
232 (buffer-name buffer))
233
234(defun tab-line-tab-name-truncated-buffer (buffer &optional buffers)
235 "Generate tab name from BUFFER.
236Reduce tab width proportionally to space taken by other tabs."
183 (let ((tab-name (buffer-name buffer)) 237 (let ((tab-name (buffer-name buffer))
184 (limit (when buffers 238 (limit (when buffers
185 (max 1 (- (/ (window-width) (length buffers)) 3))))) 239 (max 1 (- (/ (window-width) (length buffers)) 3)))))
@@ -189,8 +243,9 @@ variable `tab-line-tab-name-function'."
189 tab-line-tab-name-ellipsis) 243 tab-line-tab-name-ellipsis)
190 'help-echo tab-name)))) 244 'help-echo tab-name))))
191 245
192(defvar tab-line-tabs-limit 15 246(defvar tab-line-tabs-limit nil
193 "Maximum number of buffer tabs displayed in the tab line.") 247 "Maximum number of buffer tabs displayed in the tab line.
248If nil, no limit.")
194 249
195(defvar tab-line-tabs-function #'tab-line-tabs 250(defvar tab-line-tabs-function #'tab-line-tabs
196 "Function to get a list of tabs to display in the tab line. 251 "Function to get a list of tabs to display in the tab line.
@@ -213,53 +268,91 @@ variable `tab-line-tabs-function'."
213 (mapcar #'car (window-prev-buffers window)))) 268 (mapcar #'car (window-prev-buffers window))))
214 (prev-buffers (seq-filter #'buffer-live-p prev-buffers)) 269 (prev-buffers (seq-filter #'buffer-live-p prev-buffers))
215 ;; Remove next-buffers from prev-buffers 270 ;; Remove next-buffers from prev-buffers
216 (prev-buffers (seq-difference prev-buffers next-buffers)) 271 (prev-buffers (seq-difference prev-buffers next-buffers)))
217 (half-limit (/ tab-line-tabs-limit 2)) 272 (if (natnump tab-line-tabs-limit)
218 (prev-buffers-limit 273 (let* ((half-limit (/ tab-line-tabs-limit 2))
219 (if (> (length prev-buffers) half-limit) 274 (prev-buffers-limit
220 (if (> (length next-buffers) half-limit) 275 (if (> (length prev-buffers) half-limit)
221 half-limit 276 (if (> (length next-buffers) half-limit)
222 (+ half-limit (- half-limit (length next-buffers)))) 277 half-limit
223 (length prev-buffers))) 278 (+ half-limit (- half-limit (length next-buffers))))
224 (next-buffers-limit 279 (length prev-buffers)))
225 (- tab-line-tabs-limit prev-buffers-limit)) 280 (next-buffers-limit
226 (buffer-tabs 281 (- tab-line-tabs-limit prev-buffers-limit)))
227 (append (reverse (seq-take prev-buffers prev-buffers-limit)) 282 (append (reverse (seq-take prev-buffers prev-buffers-limit))
228 (list buffer) 283 (list buffer)
229 (seq-take next-buffers next-buffers-limit)))) 284 (seq-take next-buffers next-buffers-limit)))
230 buffer-tabs)) 285 (append (reverse prev-buffers)
286 (list buffer)
287 next-buffers))))
231 288
232(defun tab-line-format () 289(defun tab-line-format ()
233 "Template for displaying tab line for selected window." 290 "Template for displaying tab line for selected window."
234 (let* ((window (selected-window)) 291 (let* ((window (selected-window))
235 (selected-buffer (window-buffer window)) 292 (selected-buffer (window-buffer window))
236 (tabs (funcall tab-line-tabs-function)) 293 (tabs (funcall tab-line-tabs-function))
237 (separator (or tab-line-separator (if window-system " " "|")))) 294 (separator (or tab-line-separator (if window-system " " "|")))
295 (hscroll (window-parameter nil 'tab-line-hscroll))
296 (strings
297 (mapcar
298 (lambda (tab)
299 (concat
300 separator
301 (apply 'propertize
302 (concat (propertize
303 (funcall tab-line-tab-name-function tab tabs)
304 'keymap tab-line-tab-map)
305 (or (and tab-line-close-button-show
306 (not (eq tab-line-close-button-show
307 (if (eq tab selected-buffer)
308 'non-selected
309 'selected)))
310 tab-line-close-button) ""))
311 `(
312 tab ,tab
313 face ,(if (eq tab selected-buffer)
314 'tab-line-tab
315 'tab-line-tab-inactive)
316 mouse-face tab-line-highlight))))
317 tabs)))
238 (append 318 (append
239 (mapcar 319 (list separator
240 (lambda (tab) 320 (when (and (natnump hscroll) (> hscroll 0))
241 (concat 321 tab-line-left-button)
242 separator 322 (when (if (natnump hscroll)
243 (apply 'propertize (concat (propertize 323 (< hscroll (1- (length strings)))
244 (funcall tab-line-tab-name-function tab tabs) 324 (> (length strings) 1))
245 'keymap tab-line-tab-map) 325 tab-line-right-button))
246 (or (and tab-line-close-button-show 326 (if hscroll (nthcdr hscroll strings) strings)
247 (not (eq tab-line-close-button-show
248 (if (eq tab selected-buffer)
249 'non-selected
250 'selected)))
251 tab-line-close-button) ""))
252 `(
253 tab ,tab
254 face ,(if (eq tab selected-buffer)
255 'tab-line-tab
256 'tab-line-tab-inactive)
257 mouse-face tab-line-highlight))))
258 tabs)
259 (list (concat separator (when tab-line-new-tab-choice 327 (list (concat separator (when tab-line-new-tab-choice
260 tab-line-new-button)))))) 328 tab-line-new-button))))))
261 329
262 330
331(defun tab-line-hscroll (&optional arg window)
332 (let* ((hscroll (window-parameter window 'tab-line-hscroll))
333 (tabs (if window
334 (with-selected-window window (funcall tab-line-tabs-function))
335 (funcall tab-line-tabs-function))))
336 (set-window-parameter
337 window 'tab-line-hscroll
338 (max 0 (min (+ (or hscroll 0) (or arg 1))
339 (1- (length tabs)))))
340 (when window
341 (force-mode-line-update t))))
342
343(defun tab-line-hscroll-right (&optional arg mouse-event)
344 (interactive (list current-prefix-arg last-nonmenu-event))
345 (let ((window (and (listp mouse-event) (posn-window (event-start mouse-event)))))
346 (tab-line-hscroll arg window)
347 (force-mode-line-update window)))
348
349(defun tab-line-hscroll-left (&optional arg mouse-event)
350 (interactive (list current-prefix-arg last-nonmenu-event))
351 (let ((window (and (listp mouse-event) (posn-window (event-start mouse-event)))))
352 (tab-line-hscroll (- (or arg 1)) window)
353 (force-mode-line-update window)))
354
355
263(defun tab-line-new-tab (&optional mouse-event) 356(defun tab-line-new-tab (&optional mouse-event)
264 "Add a new tab to the tab line. 357 "Add a new tab to the tab line.
265Usually is invoked by clicking on the plus-shaped button. 358Usually is invoked by clicking on the plus-shaped button.
@@ -316,6 +409,7 @@ Its effect is the same as using the `next-buffer' command
316 (switch-to-next-buffer 409 (switch-to-next-buffer
317 (and (listp mouse-event) (posn-window (event-start mouse-event))))) 410 (and (listp mouse-event) (posn-window (event-start mouse-event)))))
318 411
412
319(defcustom tab-line-close-tab-action 'bury-buffer 413(defcustom tab-line-close-tab-action 'bury-buffer
320 "Defines what to do on closing the tab. 414 "Defines what to do on closing the tab.
321If `bury-buffer', put the tab's buffer at the end of the list of all 415If `bury-buffer', put the tab's buffer at the end of the list of all
@@ -359,10 +453,15 @@ from the tab line."
359 '(:eval (tab-line-format))))) 453 '(:eval (tab-line-format)))))
360 454
361 455
362(global-set-key [tab-line mouse-4] 'tab-line-switch-to-prev-tab) 456(global-set-key [tab-line mouse-4] 'tab-line-hscroll-left)
363(global-set-key [tab-line mouse-5] 'tab-line-switch-to-next-tab) 457(global-set-key [tab-line mouse-5] 'tab-line-hscroll-right)
364(global-set-key [tab-line wheel-up] 'tab-line-switch-to-prev-tab) 458(global-set-key [tab-line wheel-up] 'tab-line-hscroll-left)
365(global-set-key [tab-line wheel-down] 'tab-line-switch-to-next-tab) 459(global-set-key [tab-line wheel-down] 'tab-line-hscroll-right)
460
461(global-set-key [tab-line C-mouse-4] 'tab-line-switch-to-prev-tab)
462(global-set-key [tab-line C-mouse-5] 'tab-line-switch-to-next-tab)
463(global-set-key [tab-line C-wheel-up] 'tab-line-switch-to-prev-tab)
464(global-set-key [tab-line C-wheel-down] 'tab-line-switch-to-next-tab)
366 465
367 466
368(provide 'tab-line) 467(provide 'tab-line)