diff options
author | Juri Linkov <juri@linkov.net> | 2019-08-31 23:40:07 +0300 |
---|---|---|
committer | Juri Linkov <juri@linkov.net> | 2019-08-31 23:40:07 +0300 |
commit | 3e0ad29a607c8c085de3b74c7505e417ad7f9062 (patch) | |
tree | feff537233e67c705d67b280023c623192c3d95b /lisp/tab-line.el | |
parent | 7791005544836f93542e8277ad5897f8f5920f05 (diff) | |
download | emacs-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.el | 264 |
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 |