summaryrefslogtreecommitdiff
path: root/lisp/tab-line.el
diff options
context:
space:
mode:
authorJuri Linkov <juri@linkov.net>2019-08-31 23:40:07 +0300
committerJuri Linkov <juri@linkov.net>2019-08-31 23:40:07 +0300
commit3e0ad29a607c8c085de3b74c7505e417ad7f9062 (patch)
treefeff537233e67c705d67b280023c623192c3d95b /lisp/tab-line.el
parent7791005544836f93542e8277ad5897f8f5920f05 (diff)
downloademacs-3e0ad29a607c8c085de3b74c7505e417ad7f9062.tar.gz
Frame-local tab-bar and window-local tab-line.
* etc/NEWS: Add 'tab-bar-mode' and 'global-tab-line-mode'. * etc/TODO: Remove tab-related items. * lisp/cus-start.el: Add tab-bar-mode, tab-bar-max-label-size. * lisp/frame.el (frame-notice-user-settings): handle tab-bar-lines. * lisp/loadup.el: Load "tab-bar". * lisp/menu-bar.el (menu-bar-options-save): Add tab-bar-mode. (menu-bar-showhide-menu): Define showhide-tab-bar. * lisp/startup.el (tab-bar-images-pixel-height): New defconst. (command-line): Reset tab-bar-mode. (x-apply-session-resources): Add "tabBar", "TabBar". * lisp/subr.el (read-key): Add tab-bar. * lisp/tab-bar.el: New file. * lisp/tab-line.el: New file. * lisp/window.el (window--dump-frame): Add tab-bar-height. * src/dispextern.h (enum window_part): Add ON_TAB_LINE. (struct glyph_matrix): Add tab_line_p. (struct glyph_row): Add tab_line_p. (MATRIX_TAB_LINE_ROW): New macro. (MATRIX_FIRST_TEXT_ROW): Handle more mode lines. (MR_PARTIALLY_VISIBLE_AT_TOP): Add WINDOW_TAB_LINE_HEIGHT. (MATRIX_TAB_LINE_HEIGHT, CURRENT_TAB_LINE_HEIGHT) (DESIRED_TAB_LINE_HEIGHT): New macros. (enum face_id): Add TAB_BAR_FACE_ID and TAB_LINE_FACE_ID. (struct it): Add tab_line_p. (tab_bar_item_idx, tab_bar_item_image): New enums. (DEFAULT_TAB_BAR_LABEL_SIZE, DEFAULT_TAB_BAR_BUTTON_MARGIN) (DEFAULT_TAB_BAR_BUTTON_RELIEF, DEFAULT_TAB_BAR_IMAGE_HEIGHT): New constants. * src/dispnew.c (adjust_glyph_matrix): Use window_wants_tab_line. (shift_glyph_matrix): Add WINDOW_TAB_LINE_HEIGHT. (clear_current_matrices, clear_desired_matrices): Call clear_glyph_matrix on tab_bar_window. (blank_row): Add WINDOW_TAB_LINE_HEIGHT. (required_matrix_height): Change 2 to 3. (fake_current_matrices): Reset tab_line_p. (adjust_frame_glyphs_for_window_redisplay): Handle tab_bar_window. Add FRAME_TAB_BAR_HEIGHT and FRAME_TAB_BAR_LINES. (free_glyphs): Handle tab_bar_window. (update_frame): Handle tab_bar_window. (update_window): Handle row->tab_line_p. (scrolling_window): Change arg type from bool to int. Change header_line_p to tab_line_p. (buffer_posn_from_coords): Add window_wants_tab_line. (mode_line_string): Use MATRIX_TAB_LINE_ROW for part ON_TAB_LINE. * src/frame.c (frame_default_tab_bar_height): New internal variable. (adjust_frame_size): Handle tab_bar_window. (make_frame): Reset tab_bar_redisplayed, tab_bar_resized and last_tab_bar_item. (Ftab_bar_pixel_width): New function. (frame_parms): Add tab-bar-lines. (gui_figure_window_size): Add new arg tabbar_p. (syms_of_frame): Add Qtab_bar_size, Qupdate_frame_tab_bar, Qfree_frame_tab_bar, Qtab_bar_lines, Stab_bar_pixel_width. Add Qtab_bar_lines to frame_inhibit_implied_resize. (tab-bar-mode): New variable. * src/frame.h (GCALIGNED_STRUCT): Add tab_bar_window, desired_tab_bar_string, current_tab_bar_string. (GCALIGNED_STRUCT): Add tab_bar_items, last_tab_bar_item, minimize_tab_bar_window_p, tab_bar_redisplayed, tab_bar_resized, tab_bar_lines, tab_bar_height, n_tab_bar_rows, n_tab_bar_items. (fset_tab_bar_items, fset_tab_bar_window) (fset_current_tab_bar_string, fset_desired_tab_bar_string): New inlines. (FRAME_TAB_BAR_LINES, FRAME_TAB_BAR_HEIGHT): New macros. (FRAME_TOP_MARGIN, FRAME_TOP_MARGIN_HEIGHT): Use FRAME_TAB_BAR_LINES. * src/fringe.c (draw_fringe_bitmap_1, update_window_fringes): Add WINDOW_TAB_LINE_HEIGHT. * src/gtkutil.c (xg_frame_set_char_size): Add FRAME_TABBAR_WIDTH. (x_wm_set_size_hint): Add FRAME_TABBAR_WIDTH. * src/keyboard.c (read_char): Handle Qtab_bar. (kbd_buffer_get_event): Handle TAB_BAR_EVENT. (make_lispy_position): Add WINDOW_TAB_LINE_HEIGHT. Handle TAB_BAR_EVENT. (tab_bar_items_vector, tab_bar_item_properties, ntab_bar_items): New internal variables. (tab_bar_items, process_tab_bar_item, set_prop_tab_bar) (parse_tab_bar_item, init_tab_bar_items, append_tab_bar_item): New functions. (read_char_x_menu_prompt, read_key_sequence): Handle Qtab_bar. (tab-bar-separator-image-expression): New variable. * src/keymap.c (syms_of_keymap): Add Qtab_bar and Qtab_line. * src/menu.c (x_popup_menu_1, Fx_popup_dialog): Handle Qtab_bar. * src/termhooks.h (enum event_kind): Add TAB_BAR_EVENT. (GCALIGNED_STRUCT): Add change_tab_bar_height_hook. * src/w32fns.c (w32_frame_parm_handlers): Add w32_set_tab_bar_lines. * src/w32term.c (w32_draw_window_cursor): Add WINDOW_TAB_LINE_HEIGHT. * src/window.c (window_body_height): Add WINDOW_TAB_LINE_HEIGHT. (Fwindow_tab_line_height): New function. (coordinates_in_window): Use window_wants_tab_line with CURRENT_TAB_LINE_HEIGHT. (window_relative_x_coord): Add ON_TAB_LINE. (Fcoordinates_in_window_p): Add ON_TAB_LINE. (window_from_coordinates): Add new arg tab_bar_p. (Fwindow_line_height): Use window_wants_tab_line with WINDOW_TAB_LINE_HEIGHT. (Fwindow_lines_pixel_dimensions): Add WINDOW_TAB_LINE_HEIGHT. (make_window): Set tab_line_height to -1. (window_wants_tab_line): New function. (window_internal_height): Use window_wants_tab_line. (window_scroll_pixel_based): Add WINDOW_TAB_LINE_HEIGHT. (Frecenter): Set minimize_tab_bar_window_p to 1. (GCALIGNED_STRUCT): Add frame_tab_bar_lines and frame_tab_bar_height. (Fcurrent_window_configuration): Set frame_tab_bar_lines and frame_tab_bar_height. (set_window_scroll_bars): Add WINDOW_TAB_LINE_HEIGHT. (syms_of_window): Add Qtab_line_format and Swindow_tab_line_height. * src/window.h (GCALIGNED_STRUCT): Add tab_line_height. (WINDOW_TAB_BAR_P, WINDOW_TAB_LINE_HEIGHT, WINDOW_TAB_LINE_LINES): New macros. (WINDOW_TOP_EDGE_Y, WINDOW_BOTTOM_EDGE_Y, WINDOW_TAB_LINE_HEIGHT): Add WINDOW_TAB_BAR_P. * src/xdisp.c (window_box_height): Add window_wants_tab_line with MATRIX_TAB_LINE_ROW and CURRENT_TAB_LINE_HEIGHT. (pos_visible_p): Use window_wants_tab_line. Add WINDOW_TAB_LINE_HEIGHT. (get_glyph_string_clip_rects): Add WINDOW_TAB_LINE_HEIGHT. (get_phys_cursor_geometry): Add WINDOW_TAB_LINE_HEIGHT. (remember_mouse_glyph): Use MATRIX_TAB_LINE_ROW for part ON_TAB_LINE. (init_iterator): Use MATRIX_TAB_LINE_ROW for TAB_LINE_FACE_ID. Add WINDOW_TAB_LINE_HEIGHT. Add window_wants_tab_line. (Fwindow_text_pixel_size): Add WINDOW_TAB_LINE_HEIGHT. (prepare_menu_bars): Call update_tab_bar. (update_tab_bar, build_desired_tab_bar_string) (display_tab_bar_line, tab_bar_height, Ftab_bar_height) (redisplay_tab_bar, tab_bar_item_info, get_tab_bar_item) (handle_tab_bar_click, note_tab_bar_highlight): New functions. (compute_window_start_on_continuation_line): Use window_wants_tab_line. (try_cursor_movement): Use window_wants_tab_line with CURRENT_TAB_LINE_HEIGHT. (redisplay_window): Use window_wants_tab_line with CURRENT_TAB_LINE_HEIGHT. (try_window_reusing_current_matrix): Use window_wants_tab_line with WINDOW_TAB_LINE_HEIGHT. (Fdump_tab_bar_row): New function. (compute_line_metrics): Add WINDOW_TAB_LINE_HEIGHT. (display_line): Use window_wants_tab_line. (display_mode_line): Set tab_line_p to true if face_id is TAB_LINE_FACE_ID. (Fformat_mode_line): Handle Qtab_line and Qtab_bar. (gui_clear_end_of_line): Add WINDOW_TAB_LINE_HEIGHT. (erase_phys_cursor): Use WINDOW_TAB_LINE_HEIGHT. (show_mouse_face): Use tab_bar_window. (note_mode_line_or_margin_highlight): Use MATRIX_TAB_LINE_ROW for area ON_TAB_LINE. (note_mouse_highlight): Call note_tab_bar_highlight, (expose_frame): Handle tab_bar_window. (syms_of_xdisp): Add Sdump_tab_bar_row and Stab_bar_height. (auto-resize-tab-bars, auto-raise-tab-bar-buttons) (tab-bar-border, tab-bar-button-margin, tab-bar-button-relief) (tab-bar-max-label-size): New variables. * src/xfaces.c (lookup_basic_face): Add TAB_LINE_FACE_ID and TAB_BAR_FACE_ID. (syms_of_xfaces): Define Qtab_bar and Qtab_line. * src/xfns.c (x_set_tab_bar_lines, x_change_tab_bar_height): New functions. (xic_set_statusarea): Add FRAME_TABBAR_TOP_HEIGHT. (frame_geometry): Add FRAME_TAB_BAR_HEIGHT and Qtab_bar_size. * src/xterm.c (x_draw_image_relief): Use tab_bar_button_relief. (x_draw_image_relief): Use TAB_BAR_FACE_ID. (handle_one_xevent): Handle tab_bar_window. (x_set_window_size_1): Add FRAME_TABBAR_WIDTH. (x_create_terminal): Set change_tab_bar_height_hook. * src/xterm.h (struct x_output): Add tabbar_top_height, tabbar_bottom_height, tabbar_left_width, tabbar_right_width tabbar_widget, tabbar_in_hbox, tabbar_is_packed. (FRAME_TABBAR_TOP_HEIGHT): Add FRAME_TABBAR_TOP_HEIGHT, FRAME_TABBAR_BOTTOM_HEIGHT, FRAME_TABBAR_HEIGHT, FRAME_TABBAR_LEFT_WIDTH, FRAME_TABBAR_RIGHT_WIDTH, FRAME_TABBAR_WIDTH.
Diffstat (limited to 'lisp/tab-line.el')
-rw-r--r--lisp/tab-line.el264
1 files changed, 264 insertions, 0 deletions
diff --git a/lisp/tab-line.el b/lisp/tab-line.el
new file mode 100644
index 00000000000..addd0459c95
--- /dev/null
+++ b/lisp/tab-line.el
@@ -0,0 +1,264 @@
+;;; tab-line.el --- window-local tab line with window buffers -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2019 Free Software Foundation, Inc.
+
+;; Author: Juri Linkov <juri@linkov.net>
+;; Keywords: windows tabs
+;; Maintainer: emacs-devel@gnu.org
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; To enable this mode, run `M-x global-tab-line-mode'.
+
+;;; Code:
+
+(require 'seq) ; tab-line.el is not pre-loaded so it's safe to use it here
+
+
+(defgroup tab-line nil
+ "Window-local tab line."
+ :group 'convenience
+ :version "27.1")
+
+(defgroup tab-line-faces nil
+ "Faces used in the tab line."
+ :group 'tab-line
+ :group 'faces
+ :version "27.1")
+
+(defface tab-line
+ '((default :inherit header-line))
+ "Tab line face."
+ :version "27.1"
+ :group 'tab-line-faces)
+
+(defface tab-line-highlight
+ '((default :inherit tab-line-tab))
+ "Tab line face for highlighting."
+ :version "27.1"
+ :group 'tab-line-faces)
+
+(defface tab-line-close-highlight
+ '((t :foreground "red"))
+ "Tab line face for highlighting."
+ :version "27.1"
+ :group 'tab-line-faces)
+
+(defface tab-line-tab
+ '((((class color) (min-colors 88))
+ :box (:line-width -1 :style pressed-button)
+ :background "white" :foreground "black")
+ (t
+ :inverse-video t))
+ "Tab line face for selected tab."
+ :version "27.1"
+ :group 'tab-line-faces)
+
+(defface tab-line-tab-inactive
+ '((default
+ :inherit tab-line)
+ (((class color) (min-colors 88) (background light))
+ :weight light
+ :box (:line-width -1 :color "grey75" :style released-button)
+ :foreground "grey20" :background "grey90")
+ (((class color) (min-colors 88) (background dark) )
+ :weight light
+ :box (:line-width -1 :color "grey40" :style released-button)
+ :foreground "grey80" :background "grey30"))
+ "Tab line face for non-selected tabs."
+ :version "27.1"
+ :group 'tab-line-faces)
+
+(defvar tab-line-tab-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map [tab-line mouse-1] 'tab-line-select-tab)
+ (define-key map [tab-line mouse-2] 'tab-line-select-tab)
+ (define-key map [tab-line mouse-4] 'tab-line-switch-to-prev-tab)
+ (define-key map [tab-line mouse-5] 'tab-line-switch-to-next-tab)
+ (define-key map "\C-m" 'tab-line-select-tab)
+ (define-key map [follow-link] 'mouse-face)
+ map)
+ "Local keymap for `tab-line-mode' window tabs.")
+
+(defvar tab-line-add-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map [tab-line mouse-1] 'tab-line-add-tab)
+ (define-key map [tab-line mouse-2] 'tab-line-add-tab)
+ (define-key map "\C-m" 'tab-line-add-tab)
+ (define-key map [follow-link] 'mouse-face)
+ map)
+ "Local keymap to add `tab-line-mode' window tabs.")
+
+(defvar tab-line-tab-close-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map [tab-line mouse-1] 'tab-line-close-tab)
+ (define-key map [tab-line mouse-2] 'tab-line-close-tab)
+ (define-key map [follow-link] 'mouse-face)
+ map)
+ "Local keymap to close `tab-line-mode' window tabs.")
+
+(defvar tab-line-separator " ")
+(defvar tab-line-tab-name-ellipsis (if (char-displayable-p ?…) "…" "..."))
+(defvar tab-line-tab-name-add (if (char-displayable-p ?➕) "➕" "[+]"))
+(defvar tab-line-tab-name-close (if (char-displayable-p ?⮿) "⮿" "[x]"))
+
+
+(defun tab-line-tab-name (buffer &optional buffers)
+ "Generate tab name from BUFFER.
+Reduce tab width proportionally to space taken by other tabs."
+ (let ((tab-name (buffer-name buffer))
+ (limit (when buffers
+ (max 1 (- (/ (window-width) (length buffers)) 3)))))
+ (if (or (not limit) (< (length tab-name) limit))
+ tab-name
+ (concat tab-line-tab-name-ellipsis (substring tab-name (- limit))))))
+
+(defun tab-line-format ()
+ "Template for displaying tab line for selected window."
+ (let* ((window (selected-window))
+ (buffer (window-buffer window))
+ (next-buffers (seq-remove (lambda (b) (eq b buffer))
+ (window-next-buffers window)))
+ (prev-buffers (seq-remove (lambda (b) (eq b buffer))
+ (mapcar #'car (window-prev-buffers window))))
+ ;; Remove next-buffers from prev-buffers
+ (prev-buffers (seq-difference prev-buffers next-buffers))
+ (buffers (append (reverse prev-buffers)
+ (list buffer)
+ next-buffers))
+ (buffers (seq-filter #'buffer-live-p buffers)))
+ (append
+ (mapcar
+ (lambda (b)
+ (format "%s%s%s"
+ tab-line-separator
+ (apply 'propertize (tab-line-tab-name b buffers)
+ `(
+ help-echo "Click to visit tab"
+ buffer ,b
+ face ,(if (eq b buffer)
+ 'tab-line-tab
+ 'tab-line-tab-inactive)
+ mouse-face tab-line-highlight
+ keymap ,tab-line-tab-map))
+ (apply 'propertize tab-line-tab-name-close
+ `(
+ help-echo "Click to close tab"
+ buffer ,b
+ face ,(if (eq b buffer)
+ 'tab-line-tab
+ 'tab-line-tab-inactive)
+ mouse-face tab-line-close-highlight
+ keymap ,tab-line-tab-close-map))))
+ buffers)
+ (list (format "%s%s"
+ tab-line-separator
+ (apply 'propertize tab-line-tab-name-add
+ `(
+ help-echo "Click to add tab"
+ face tab-line-tab-inactive
+ mouse-face tab-line-highlight
+ keymap ,tab-line-add-map)))))))
+
+
+(defun tab-line-add-tab (&optional e)
+ (interactive "e")
+ ;; Maybe (buffer-menu-open)
+ (mouse-buffer-menu e))
+
+(defun tab-line-select-tab (&optional e)
+ "Switch to the selected tab.
+This command maintains the original order of prev/next buffers.
+So for example, switching to a previous tab is equivalent to
+using the `previous-buffer' command."
+ (interactive "e")
+ (let* ((posnp (event-start e))
+ (window (posn-window posnp))
+ (buffer (get-pos-property 1 'buffer (car (posn-string posnp))))
+ (window-buffer (window-buffer window))
+ (next-buffers (seq-remove (lambda (b) (eq b window-buffer))
+ (window-next-buffers window)))
+ (prev-buffers (seq-remove (lambda (b) (eq b window-buffer))
+ (mapcar #'car (window-prev-buffers window))))
+ ;; Remove next-buffers from prev-buffers
+ (prev-buffers (seq-difference prev-buffers next-buffers)))
+ (cond
+ ((memq buffer next-buffers)
+ (dotimes (_ (1+ (seq-position next-buffers buffer)))
+ (switch-to-next-buffer window)))
+ ((memq buffer prev-buffers)
+ (dotimes (_ (1+ (seq-position prev-buffers buffer)))
+ (switch-to-prev-buffer window)))
+ (t
+ (switch-to-buffer buffer)))))
+
+(defun tab-line-switch-to-prev-tab (&optional e)
+ "Switch to the previous tab."
+ (interactive "e")
+ (switch-to-prev-buffer (posn-window (event-start e))))
+
+(defun tab-line-switch-to-next-tab (&optional e)
+ "Switch to the next tab."
+ (interactive "e")
+ (switch-to-next-buffer (posn-window (event-start e))))
+
+(defun tab-line-close-tab (&optional e)
+ "Close the selected tab."
+ (interactive "e")
+ (let* ((posnp (event-start e))
+ (window (posn-window posnp))
+ (buffer (get-pos-property 1 'buffer (car (posn-string posnp)))))
+ (with-selected-window window
+ (if (eq buffer (current-buffer))
+ (bury-buffer)
+ (set-window-prev-buffers nil (assq-delete-all buffer (window-prev-buffers)))
+ (set-window-next-buffers nil (delq buffer (window-next-buffers))))
+ (force-mode-line-update))))
+
+
+(defvar tab-line-format '(:eval (tab-line-format)))
+
+;;;###autoload
+(define-minor-mode global-tab-line-mode
+ "Display window-local tab line."
+ :group 'tab-line
+ :type 'boolean
+ :global t
+ :init-value nil
+ :initialize (lambda (sym val)
+ (custom-initialize-default sym val)
+ (when global-tab-line-mode
+ (add-hook 'pre-redisplay-functions #'tab-line-update-window-parameter)))
+ (if global-tab-line-mode
+ (progn
+ (add-hook 'pre-redisplay-functions #'tab-line-update-window-parameter)
+ (force-mode-line-update))
+ (remove-hook 'pre-redisplay-functions #'tab-line-update-window-parameter)
+ (walk-windows (lambda (w) (tab-line-update-window-parameter w)) t)))
+
+(defun tab-line-update-window-parameter (window)
+ (let* ((name 'tab-line-format)
+ (value (window-parameter window name))
+ (active global-tab-line-mode))
+ (when (xor value active)
+ (set-window-parameter
+ window name (unless value tab-line-format)))))
+
+
+(provide 'tab-line)
+;;; tab-line.el ends here